API¶
ffn Package¶
core Module¶
- class ffn.core.GroupStats(*prices, annualization_factor=None)[source]¶
Bases:
dictGroupStats enables one to compare multiple series side by side. It is a wrapper around a dict of {price.name: PerformanceStats} and provides many convenience methods.
The order of the series passed in will be preserved. Individual PerformanceStats objects can be accessed via index position or name via the [] accessor. An integer matching a stored name is treated as a name; other integers use positional lookup.
Each series’ stats are computed from that series’ own available observations, matching what PerformanceStats returns for the series on its own. Cross-sectional views (prices, correlations) use the merged calendar, keeping only dates where every series has data.
- Parameters:
prices (*) – Multiple price series to be compared. Each DatetimeIndex must be monotonic increasing.
annualization_factor (*) – Annualization factor used for each series.
- Raises:
* ValueError – If any price series contains no usable value.
- \* stats
Dataframe containing stats for each series provided. Stats in rows, series in columns.
- Type:
DataFrame
- \* lookback_returns
Returns for diffrent lookback periods (1m, 3m, 6m, ytd…) Period in rows, series in columns.
- Type:
DataFrame
- \* prices
The merged prices at their original levels, keeping only dates where every series has data. These prices are not rebased;
plot()rebases each series separately for display.- Type:
DataFrame
- plot(freq=None, figsize=(15, 5), title=None, logy=False, **kwargs)[source]¶
Helper function for plotting the series.
- Parameters:
freq (*) – Data frequency used for display purposes. Refer to pandas docs for valid freq strings.
figsize (*) – figure size
title (*) – Title if default not appropriate
logy (*) – log-scale for y axis
kwargs (*) – passed to pandas’ plot method
- plot_correlation(freq=None, title=None, figsize=(12, 6), **kwargs)[source]¶
Utility function to plot correlations.
- Parameters:
freq (*) – Pandas data frequency alias string
title (*) – Plot title
figsize (*) – figure size
kwargs (*) – passed to Pandas’ plot_corr_heatmap function
- plot_histograms(freq=None, title=None, figsize=(10, 10), **kwargs)[source]¶
Wrapper around pandas’ hist.
- Parameters:
freq (*) – Data frequency used for display purposes. Refer to pandas docs for valid freq strings.
figsize (*) – figure size
title (*) – Title if default not appropriate
kwargs (*) – passed to pandas’ hist method
- plot_scatter_matrix(freq=None, title=None, figsize=(10, 10), **kwargs)[source]¶
Wrapper around pandas’ scatter_matrix.
- Parameters:
freq (*) – Data frequency used for display purposes. Refer to pandas docs for valid freq strings.
figsize (*) – figure size
title (*) – Title if default not appropriate
kwargs (*) – passed to pandas’ scatter_matrix method
- set_date_range(start=None, end=None)[source]¶
Update date range of stats, charts, etc. If None then the original date range is used. So to reset to the original range, just call with no args. A failed recalculation preserves the group and its existing children.
- Parameters:
start (*) – start date
end (*) – end date
- Raises:
* ValueError – If any series has no usable price in the selected range.
- set_riskfree_rate(rf)[source]¶
Set annual risk-free rate property and calculate properly annualized monthly and daily rates. Then performance stats are recalculated. Affects only those instances of PerformanceStats that are children of this GroupStats object.
- Parameters:
rf (*) – Annual risk-free rate or risk-free rate price series (not returns)
- to_csv(sep=',', path=None)[source]¶
Returns a CSV string with appropriate formatting. Strategy names, including numeric labels, are serialized as text. Names containing separators, quotes or line breaks are CSV-quoted. If path is not None, the string will be saved to file at path.
- Parameters:
sep (*) – Separator
path (*) – If None, CSV string returned. Else file written to specified path.
- class ffn.core.PerformanceStats(prices, rf=0.0, annualization_factor=None)[source]¶
Bases:
objectPerformanceStats is a convenience class used for the performance evaluation of a price series. It contains various helper functions to help with plotting and contains a large amount of descriptive statistics.
- Parameters:
prices (*) – A price series. A DatetimeIndex must be monotonic increasing. Unavailable outer observations are excluded from endpoint statistics when total return is available. MTD and YTD use the last observed daily price’s month and year, even when later dates contain only unavailable prices.
rf (*) –
Risk-free rate used in various calculation. Should be expressed as a yearly (annualized) return if it is a floating scalar. Otherwise rf should be a price series — it is converted internally with to_returns(). Note this differs from calc_sharpe and calc_sortino_ratio, which take rf as a return series. Passing a return series here treats its values as price levels rather than returns, so it does not generally behave like rf=0. Daily statistics use risk-free prices on the asset’s own observed daily endpoints; an interval missing either risk-free price is excluded, not filled. Monthly and yearly risk-free prices are sampled in the asset’s timezone, excluding observations after the asset’s last usable price, before their returns are aligned with the asset’s calendar-period returns.
- Raises:
* ValueError – If the price series contains no usable value.
- \* name
Name, derived from price series name
- \* return_table
A table of monthly returns with YTD figures as well.
- Type:
DataFrame
- \* lookback_returns
Returns for different lookback periods (1m, 3m, 6m, ytd…)
- Type:
Series
- \* stats
A series that contains all the stats
- Type:
Series
- \* annualization_factor
Annualization factor used in various calculations; aka nperiods, 252
- Type:
float
- display_monthly_returns()[source]¶
Display a table containing monthly returns and ytd returns for every year in range.
- plot(freq=None, figsize=(15, 5), title=None, logy=False, **kwargs)[source]¶
Helper function for plotting the series.
- Parameters:
freq (*) – Data frequency used for display purposes. Refer to pandas docs for valid freq strings.
figsize (*) – figure size
title (*) – Title if default not appropriate
logy (*) – log-scale for y axis
kwargs (*) – passed to pandas’ plot method
- plot_histogram(freq=None, figsize=(15, 5), title=None, bins=20, **kwargs)[source]¶
Plots a histogram of returns given a return frequency.
- Parameters:
freq (*) – Data frequency used for display purposes. This will dictate the type of returns (daily returns, monthly, …) Refer to pandas docs for valid period strings.
figsize (*) – figure size
title (*) – Title if default not appropriate
bins (*) – number of bins for the histogram
kwargs (*) – passed to pandas’ hist method
- set_date_range(start=None, end=None)[source]¶
Update date range of stats, charts, etc. If None then the original date is used. So to reset to the original range, just call with no args. A failed recalculation leaves the current statistics unchanged.
- Parameters:
start (*) – start date
end (*) – end date
- Raises:
* ValueError – If the selected range has no usable price.
- set_riskfree_rate(rf)[source]¶
Set annual risk-free rate property and calculate properly annualized monthly and daily rates. Then performance stats are recalculated. Affects only this instance of the PerformanceStats. The selected date range is preserved; call
set_date_range()to restore the original range.- Parameters:
rf (*) –
Annual risk-free rate, or a risk-free price series (not returns)
- to_csv(sep=',', path=None)[source]¶
Returns a CSV string with appropriate formatting. Strategy names, including numeric labels, are serialized as text. Names containing separators, quotes or line breaks are CSV-quoted. If path is not None, the string will be saved to file at path.
- Parameters:
sep (*) – Separator
path (*) – If None, CSV string returned. Else file written to specified path.
- ffn.core.annualize(returns, durations, one_year=365.0)[source]¶
Annualize returns using their respective durations.
- Formula used is:
(1 + returns) ** (1 / (durations / one_year)) - 1
- ffn.core.asfreq_actual(series, freq, method='ffill', how='end', normalize=False)[source]¶
Similar to pandas’ asfreq but keeps the actual dates. For example, if last data point in Jan is on the 29th, that date will be used instead of the 31st.
- ffn.core.calc_cagr(prices)[source]¶
Calculates the CAGR (compound annual growth rate) for given prices.
Each Series or DataFrame column uses its first and last observed prices and their dates when at least two observations are available.
- Parameters:
prices (*) – Prices.
- Returns:
float or pandas.Series – cagr.
- ffn.core.calc_calmar_ratio(prices)[source]¶
Calculates the Calmar ratio given a series of prices
- Parameters:
prices (*) – Price series
- ffn.core.calc_clusters(returns, n=None, plot=False)[source]¶
Calculates the clusters based on k-means clustering.
With automatic cluster selection, a single asset with a defined correlation forms cluster 0 without fitting a model.
- Parameters:
returns (*) – DataFrame of returns
n (*) – Specify # of clusters. If None, this will be automatically determined
plot (*) – Show plot?
- Returns:
{cluster# : [col names]}
- Return type:
dict with structure
- Raises:
* ValueError – If correlation calculation omits any requested asset.
- ffn.core.calc_deflated_sharpe_ratio(returns, trial_sharpe_ratios, rf=0.0, nperiods=None, annualized_trials=True)[source]¶
Calculates the deflated Sharpe ratio of a strategy selected as the best of several trials: the probability that its true Sharpe ratio exceeds zero, after correcting for the multiple testing of the selection and for the non-normality of the returns.
Use it on the winner of a strategy search or a parameter optimization.
trial_sharpe_ratiosmust cover all trials that were evaluated, not only the ones that were kept: their count and dispersion set the hurdle (seecalc_expected_max_sharpe()) that the winner is measured against. Values close to 1 mean the winner clears the bar its own search sets by chance; values below ~0.95 suggest the result may be an artifact of having tried many candidates.Where the trials are strongly correlated (e.g. a dense grid of similar parameters), the effective number of independent trials is lower than their count and the result is accordingly conservative.
The sample size, Sharpe ratio, skew and kurtosis are all taken over the excess returns, skipping missing observations.
Source: Bailey, D. and Lopez de Prado, M. (2014), “The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality”, Journal of Portfolio Management, 40(5), 94-107.
- Parameters:
returns (*) – Return series of the selected (best) trial.
trial_sharpe_ratios (*) – Sharpe ratios of all evaluated trials, e.g. as returned by
calc_sharpe(). All values must be available and finite; a masked, missing or nonfinite score makes the resultNaN.rf (*) –
Risk-free rate expressed in yearly (annualized) terms or return series.
nperiods (*) – Frequency of returns (252 for daily, 12 for monthly, etc.). Inferred from returns if not provided.
annualized_trials (*) – Whether trial_sharpe_ratios are annualized, as
calc_sharpe()returns them by default.
- Returns:
float – probability [0, 1] that the selected trial’s Sharpe ratio is greater than zero, or
NaNwhen the statistic is undefined.
- ffn.core.calc_erc_weights(returns, initial_weights=None, risk_weights=None, covar_method='ledoit-wolf', risk_parity_method='ccd', maximum_iterations=100, tolerance=1e-08)[source]¶
Calculates the equal risk contribution / risk parity weights given a DataFrame of returns.
- Parameters:
returns (*) – Returns for multiple securities with unique column labels.
initial_weights (*) – Starting asset weights [default inverse vol].
risk_weights (*) – Finite, nonnegative relative risk targets, one per return column and with at least one positive value [default equal weight]. Masked entries are invalid risk targets.
covar_method (*) – Covariance matrix estimation method: ledoit-wolf (default) or
standard.risk_parity_method (*) – Risk parity estimation method:
ccd(cyclical coordinate descent, default) orslsqp(scipy’s implementation of sequential least squares programming).maximum_iterations (*) – Maximum iterations in iterative solutions.
tolerance (*) – Tolerance level in iterative solutions.
- Returns:
weight}
- Return type:
Series {col_name
- Raises:
* ValueError – If the standard covariance matrix is not positive semidefinite.
* ValueError – If return columns are duplicated or risk targets are invalid.
- ffn.core.calc_expected_max_sharpe(n_trials, sr_std)[source]¶
Calculates the expected maximum Sharpe ratio of
n_trialsskill-less strategies, i.e. the hurdle the best of that many trials is expected to clear by chance alone.Searching over many strategies (or many parameter sets of one strategy) and keeping the best is a multiple testing problem: even with no skill, the maximum Sharpe ratio of the search grows with the number and the dispersion of the trials.
sr_stdand the returned value are expressed in the same terms, so passing an annualized dispersion returns an annualized hurdle.- Parameters:
n_trials (*) – Number of trials (strategies or parameter sets) evaluated.
sr_std (*) – Standard deviation of the Sharpe ratios across those trials.
- Returns:
float – expected maximum Sharpe ratio under the null of no skill.
- ffn.core.calc_ftca(returns, threshold=0.5)[source]¶
Implementation of David Varadi’s Fast Threshold Clustering Algorithm (FTCA).
http://cssanalytics.wordpress.com/2013/11/26/fast-threshold-clustering-algorithm-ftca/ # NOQA
More stable than k-means for clustering purposes. If you want more clusters, use a higher threshold.
- Parameters:
where (* returns - expects a pandas dataframe of returns) – each column is the name of a given security.
threshold (*) – Threshold parameter - use higher value for more clusters. Basically controls how similar (correlated) series have to be.
- Returns:
dict of cluster name (a number) and list of securities in cluster
- ffn.core.calc_information_ratio(returns, benchmark_returns)[source]¶
Calculates the Information ratio (or from Wikipedia).
- ffn.core.calc_inv_vol_weights(returns)[source]¶
Calculates weights proportional to inverse volatility of each column.
Returns weights that are inversely proportional to the column’s volatility resulting in a set of portfolio weights where each position has the same level of volatility.
Assets with no non-missing returns or only one distinct non-missing return are excluded from the portfolio (their weight is set to NaN).
- Returns:
weight}
- Return type:
Series {col_name
- ffn.core.calc_max_drawdown(prices)[source]¶
Calculates the max drawdown of a price series. If you want the actual drawdown series, please use to_drawdown_series.
- ffn.core.calc_mean_var_weights(returns, weight_bounds=(0.0, 1.0), rf=0.0, covar_method='ledoit-wolf', options=None)[source]¶
Calculates the mean-variance weights given a DataFrame of returns.
- Parameters:
returns (*) – Returns for multiple securities with unique column labels.
weight_bounds (*) – Weigh limits for optimization.
rf (*) –
Risk-free rate used in utility calculation. Expressed per observation period, matching
returns; it is not automatically deannualized. For daily returns on a 252-period annual basis, convert a 3% annual rate withffn.deannualize(0.03, 252)before passing it asrf.covar_method (*) –
Covariance matrix estimation method: ledoit-wolf or
standard.options (*) – options for minimizing, e.g. {‘maxiter’: 10000 }
- Returns:
weight}
- Return type:
Series {col_name
- Raises:
* ValueError – If the returns have duplicate column labels.
* ValueError – If the standard covariance matrix is not positive semidefinite.
- ffn.core.calc_mtd(daily_prices, monthly_prices)[source]¶
Calculates mtd return of a price series. Use daily_prices if prices are only available from the same month. With older periods present, use current-month prices when the prior month is unavailable and the current month has at least two usable observations; else use monthly_prices.
- ffn.core.calc_perf_stats(prices, risk_free_rate=0.0, annualization_factor=252)[source]¶
Calculates the performance statistics given an object. The object should be a Series of prices.
A PerformanceStats object will be returned containing all the stats.
- Parameters:
prices (*) – Series of prices. A DatetimeIndex must be monotonic increasing.
risk_free_rate (*) – Annual risk-free rate or risk-free rate price series
annualization_factor (*) – Annualizing factor. Default is 252 (trading days)
- Raises:
* ValueError – If the price series contains no usable value.
- ffn.core.calc_prob_backtest_overfitting(trial_returns, n_blocks=16, metric=None, full_output=False)[source]¶
Calculates the probability of backtest overfitting (PBO) of a strategy search via combinatorially symmetric cross validation (CSCV).
Use it on the full matrix of trials a strategy search evaluated, with one column per trial. The sample is split into
n_blockscontiguous blocks and, for every balanced combination of half the blocks used in-sample, the in-sample winner’s performance rank is observed out-of-sample. If selecting the in-sample best carried no information, that rank is uniform; PBO is the fraction of combinations in which the in-sample winner performs at or below the out-of-sample median (rank logit <= 0). Out-of-sample ties receive their average rank; in-sample ties select the first column.This is the companion diagnostic to
calc_deflated_sharpe_ratio(), and they answer different questions: the deflated Sharpe ratio asks whether the winner’s performance clears the hurdle its own search sets by chance, whilst PBO asks whether the act of selection transferred out of sample at all. A strategy family with genuine common alpha can pass the first and still show a PBO near one half because the parameter choice within the family was arbitrary.Source: Bailey, D., Borwein, J., Lopez de Prado, M. and Zhu, Q. (2017), “The Probability of Backtest Overfitting”, Journal of Computational Finance, 20(4), 39-69.
- Parameters:
trial_returns (*) – Return series of ALL evaluated trials, one column per trial (configuration), in chronological row order.
n_blocks (*) – Number of contiguous blocks for CSCV. Must be even and at least 2; observations beyond a multiple of
n_blocksare truncated from the end. 16 gives 12,870 combinations; 12 or 8 run faster on long samples.metric (*) – Performance measure mapping a return Series to a scalar, with larger values indicating better performance, used both to pick the in-sample winner and to rank out of sample. The Series retains its original index and column name. Defaults to the per-period Sharpe ratio (mean over sample standard deviation), skipping missing observations. Constant or insufficient data has an undefined Sharpe ratio.
full_output (*) – If True, also return the per-combination rank logits and the mean out-of-sample rank of the in-sample winner.
- Raises:
* ValueError – If a DatetimeIndex is not monotonic increasing.
- Returns:
float – probability [0, 1] of backtest overfitting, or a dict with keys
pbo,logitsandmean_oos_rankwhenfull_outputis True. If any trial has a non-finite metric in either half of a combination, that combination’s logit is NaN and bothpboandmean_oos_rankare NaN. Undefined combinations are not dropped.
- ffn.core.calc_prob_mom(returns, other_returns)[source]¶
Probabilistic momentum (see momentum investing)
Basically the “probability or confidence that one asset is going to outperform the other”.
- ffn.core.calc_risk_return_ratio(returns, nperiods=None, annualize=True)[source]¶
Calculates the return / risk ratio. Basically the Sharpe ratio without factoring in the risk-free rate.
Supply nperiods when the return frequency cannot be inferred, or set annualize=False for a per-period ratio. See
calc_sharpe().
- ffn.core.calc_sharpe(returns, rf=0.0, nperiods=None, annualize=True)[source]¶
Calculates the Sharpe ratio (see Sharpe vs. Sortino).
If rf is a non-zero floating scalar, nperiods must be supplied or inferable. In this case, rf is assumed to be expressed in yearly (annualized) terms, even when annualize=False.
Returns NaN when the aligned excess returns have no dispersion, independently for each DataFrame column.
- Parameters:
returns (*) – Input return series
rf (*) –
Risk-free rate expressed as a yearly (annualized) return or return series (unlike PerformanceStats, which takes rf as a price series)
nperiods (*) – Frequency of returns (252 for daily, 12 for monthly, etc.). Inferred from the index when omitted.
annualize (*) – Annualize the result. Defaults to True.
- Raises:
* ValueError – If nperiods cannot be inferred and is needed for annualization or a non-zero scalar risk-free rate. Supply nperiods explicitly, or use annualize=False for a per-period ratio with no scalar rate conversion.
- ffn.core.calc_sortino_ratio(returns, rf=0.0, nperiods=None, annualize=True)[source]¶
Calculates the Sortino ratio given a series of returns (see Sharpe vs. Sortino).
- Parameters:
returns (*) – Returns
rf (*) –
Risk-free rate expressed in yearly (annualized) terms or return series.
nperiods (*) – Number of periods used for annualization. Must be provided or inferable if annualize=True or rf is a non-zero scalar.
annualize (*) – Annualize the result. Defaults to True. A non-zero scalar risk-free rate still needs nperiods when annualize=False.
- Raises:
* ValueError – If nperiods cannot be inferred and is needed for annualization or a non-zero scalar risk-free rate. Supply nperiods explicitly, or use annualize=False for a per-period ratio with no scalar rate conversion.
- ffn.core.calc_stats(prices, annualization_factor=None)[source]¶
Calculates performance stats of a given object.
If object is Series, a PerformanceStats object is returned. If object is DataFrame, a GroupStats object is returned.
- Parameters:
prices (*) – Set of prices. A DatetimeIndex must be monotonic increasing.
annualization_factor (*) – Annualization factor used in calculations
- Raises:
* ValueError – If any input price series contains no usable value.
- ffn.core.calc_total_return(prices)[source]¶
Calculates the total return of given prices.
Each Series or DataFrame column uses its first and last observed prices when at least two observations are available.
last / first - 1
- ffn.core.calc_ytd(daily_prices, yearly_prices)[source]¶
Calculates ytd return of a price series. Use daily_prices if prices are only available from the same year. With older periods present, use current-year prices when the prior year is unavailable and the current year has at least two usable observations; else use yearly_prices.
- ffn.core.deannualize(returns, nperiods)[source]¶
Convert return expressed in annual terms on a different basis.
- Parameters:
returns (*) – Return(s)
nperiods (*) – Target basis, typically 252 for daily, 12 for monthly, etc.
- ffn.core.drawdown_details(drawdown, index_type=<class 'pandas.DatetimeIndex'>)[source]¶
Returns a data frame with start, end, days (duration) and drawdown for each drawdown in a drawdown series.
Note
days are actual calendar days, not trading days
- Parameters:
drawdown (*) – A drawdown Series (can be obtained w/ drawdown(prices).
- Returns:
- pandas.DataFrame – A data frame with the following
columns: start, end, days, drawdown.
- ffn.core.drop_duplicate_cols(df)[source]¶
Remove duplicate columns without modifying the input DataFrame.
For each label, keep the first column with the most non-missing values. Retained labels remain in first-occurrence order.
- ffn.core.extend_pandas()[source]¶
Extends pandas’ PandasObject (Series, Series, DataFrame) with some functions defined in this file.
This facilitates common functional composition used in quant finance.
- Ex:
prices.to_returns().dropna().calc_clusters() (where prices would be a DataFrame)
- ffn.core.get_num_days_required(offset, period='d', perc_required=0.9, annualization_factor=252)[source]¶
Estimates the number of days required to assume that data is OK.
Helper function used to determine if there are enough “good” data days over a given period.
- Parameters:
offset (*) – Offset (lookback) period.
period (*) – Period string.
perc_required (*) – percentage of number of days expected required.
- ffn.core.infer_freq(data)[source]¶
Infer the most likely frequency given the input index. If the frequency is uncertain or index is not DateTime like, just return None
- Parameters:
data (*) – Any timeseries dataframe or series
- ffn.core.limit_weights(weights, limit=0.1)[source]¶
Limits weights and redistributes excedent amount proportionally.
Integer weights are converted to floating point when capping is needed, so redistribution can retain fractional allocations.
- ex:
weights are {a: 0.7, b: 0.2, c: 0.1}
call with limit=0.5
- excess 0.2 in a is ditributed to b and c
proportionally. - result is {a: 0.5, b: 0.33, c: 0.167}
- Parameters:
weights (*) – A series describing finite weights
limit (*) – Maximum weight allowed
- ffn.core.plot_corr_heatmap(data, **kwargs)[source]¶
Plots the correlation heatmap for a given DataFrame.
- ffn.core.plot_heatmap(data, title='Heatmap', show_legend=True, show_labels=True, label_fmt='.2f', vmin=None, vmax=None, figsize=None, label_color='w', cmap='RdBu', **kwargs)[source]¶
Plot a heatmap using matplotlib’s pcolor.
- Parameters:
data (*) – DataFrame to plot. Usually small matrix (ex. correlation matrix).
title (*) – Plot title
show_legend (*) – Show color legend
show_labels (*) – Show value labels
label_fmt (*) – Label format string
vmin (*) – Min value for scale
vmax (*) – Max value for scale
cmap (*) – Color map
kwargs (*) – Passed to matplotlib’s pcolor
- ffn.core.random_weights(n, bounds=(0.0, 1.0), total=1.0)[source]¶
Generate pseudo-random weights.
Returns a list of random weights that is of length n, where each weight is in the range bounds, and where the weights sum up to total.
Useful for creating random portfolios when benchmarking.
- Parameters:
n (*) – number of random weights
bounds (*) – bounds for each weight
total (*) – total sum of the weights
- ffn.core.rebase(prices, value=100)[source]¶
Rebase each price series to a given initial value.
Each series uses its first non-missing price as the baseline. Missing prices remain missing in the returned object.
- Parameters:
prices (*) – Expects a price Series or DataFrame
value (*) – starting value for all series.
- ffn.core.resample_returns(returns, func, seed=0, num_trials=100)[source]¶
Resample the returns and calculate any statistic on every new sample.
https://en.wikipedia.org/wiki/Resampling_(statistics)
Sampled dates are shuffled and may repeat, so annualized ratio functions need an explicit frequency, for example
lambda sample: calc_sharpe(sample, nperiods=252). Useannualize=Falseinstead for per-period ratios.- Parameters:
DataFrame) (returns (Series,) – Returns
func – Given the resampled returns calculate a statistic
seed – Integer seed for random number generator; trial i adds i
num_trials – Number of times to resample and run the experiment
- Returns:
Series of resampled statistics
- ffn.core.rescale(x, min=0.0, max=1.0, axis=0)[source]¶
Rescale values to fit a certain range [min, max]
- ffn.core.rollapply(data, window, fn)[source]¶
Apply a function fn over a rolling window of size window.
- Parameters:
data (*) – Series or DataFrame
window (*) – Window size
fn (*) – Function to apply over the rolling window. For a series, the return value is expected to be a single number. For a DataFrame, it should return a new row. Complete uniquely labeled Series rows are aligned to unique DataFrame columns. Other row results retain positional assignment.
- Returns:
Object of same dimensions as data
- ffn.core.to_drawdown_series(prices)[source]¶
Calculates the drawdown series.
This returns a series representing a drawdown. When the price is at all time highs, the drawdown is 0. However, when prices are below high water marks, the drawdown series = current / hwm - 1
The max drawdown can be obtained by simply calling .min() on the result (since the drawdown series is negative)
Method ignores all gaps of NaN’s in the price series.
- Parameters:
prices (*) – Series of prices.
- ffn.core.to_excess_returns(returns, rf, nperiods=None)[source]¶
Given a series of returns, it will return the excess returns over rf.
A Series risk-free return is aligned to the return index. For DataFrame returns, it is subtracted from every column by date.
- Parameters:
returns (*) – Returns
rf (*) – Risk-Free rate(s) expressed in annualized term or return series
nperiods (*) – Optional. If provided, will convert rf to different frequency using deannualize only if rf is a floating scalar
- Returns:
Returns - rf
- Return type:
excess_returns (Series, DataFrame)
- ffn.core.to_log_returns(prices)[source]¶
Calculates the log returns of a price series.
Formula is: ln(p1/p0)
- Parameters:
prices (*) – Expects a price series
- ffn.core.to_monthly(series, method='ffill', how='end')[source]¶
Convenience method that wraps asfreq_actual with ‘M’ param (method=’ffill’, how=’end’).
- ffn.core.to_price_index(returns, start=100)[source]¶
Returns a price index given a series of returns.
- Parameters:
returns (*) – Expects a return series
start (*) – Starting level
Assumes arithmetic returns.
The first value of the returned price index is always
start. When a Series begins with NaN, or every column of a DataFrame begins with NaN (as produced byto_returns()), the NaN position is replaced bystartand the output length equals the input length. Otherwisestartis prepended so that every non-missing return is reflected in the prices. Missing returns are treated as zero, and the round-tripto_returns(to_price_index(r))recovers the non-missing returns.Prepending
startrequires a DatetimeIndex or RangeIndex to derive a baseline label. Other index types raise TypeError in this case; they remain supported when the first row is entirely missing.Formula is: start, start * cumprod(1+r)
- ffn.core.to_returns(prices)[source]¶
Calculates the simple arithmetic returns of a price series.
Formula is: (t1 / t0) - 1
- Parameters:
prices (*) – Expects a price series
- ffn.core.to_ulcer_index(prices)[source]¶
Calculates the Ulcer Index from price levels.
Converts from prices -> Ulcer index
See https://en.wikipedia.org/wiki/Ulcer_index
Method ignores all gaps of NaN’s in the price series.
- Parameters:
prices (*) – Prices
- Returns:
float (Series if prices has more than one column) – The Ulcer Index in percentage points.
- ffn.core.to_ulcer_performance_index(prices, rf=0.0, nperiods=None)[source]¶
Converts from prices -> ulcer performance index.
See https://www.tangotools.com/ui/ui.htm
The numerator is the investment’s compound annual return minus the annual risk-free return. The Ulcer Index can still vary with sampling frequency. Price gaps are forward-filled. Fewer than two prices, zero elapsed time, or missing risk-free returns within the holding period produce NaN.
- Parameters:
prices (*) – Prices
rf (*) –
Annual risk-free rate when scalar, or per-period return Series aligned to the price index.
nperiods (*) – Positive periods per year, required for prices without a datetime or timedelta index. Dated prices use elapsed calendar time.
data Module¶
- class ffn.data.DataProvider(*args, **kwargs)[source]¶
Bases:
ProtocolCallable accepting ticker/field keywords and returning one price Series.
Additional request options, including start/end, are passed unchanged. The response must have a unique, increasing DatetimeIndex without NaT, and real numeric values. Missing values and timezone-aware indexes are supported. Providers own authentication, transport, and optional caching.
- ffn.data.csv(ticker: str, path='data.csv', field='', mrefresh=False, start=None, end=None, **kwargs) Series[source]¶
Data provider wrapper around pandas’ read_csv. Provides memoization. The date window includes start and excludes end. Duplicate dates keep the last observation, and the result is sorted by date.
- ffn.data.get(tickers: Sequence[str], provider: DataProvider | str | None = None, common_dates=True, forward_fill=False, clean_tickers=True, column_names=None, ticker_field_sep=':', mrefresh=False, existing=None, **kwargs) DataFrame[source]¶
Helper function for retrieving data as a DataFrame.
- Parameters:
tickers (*) – Tickers to download.
provider (*) – A DataProvider or an
ffn.providersentry-point name. Defaults to ffn.data.DEFAULT_PROVIDER. An unset default falls back to Yahoo with a deprecation warning.common_dates (*) – Keep common dates only? Drop na’s.
forward_fill (*) – forward fill values if missing. Only works if common_dates is False, since common_dates will remove all nan’s, so no filling forward necessary.
clean_tickers (*) – Should the tickers be ‘cleaned’ using ffn.utils.clean_tickers? Basically remove non-standard characters (^VIX -> vix) and standardize to lower case. Raises ValueError if distinct columns would clean to the same name; disable cleaning or provide column_names to preserve their identities.
column_names (*) – List of column names if clean_tickers is not satisfactory.
ticker_field_sep (*) – separator used to determine the ticker and field. This is in case we want to specify particular, non-default fields. For example, we might want: AAPL:Low,AAPL:High,AAPL:Close. ‘:’ is the separator.
mrefresh (*) – Request a refresh from providers that explicitly declare an mrefresh keyword. Legacy providers without it are called normally. This function does not cache requests or results.
existing (*) – Existing DataFrame to append returns to - used when we download from multiple sources
kwargs (*) – passed to provider
- ffn.data.get_provider(name: str) DataProvider[source]¶
Load one callable registered in the
ffn.providersentry-point group.Raises ValueError for unknown or duplicate names, and TypeError if the selected entry point is not callable. Unselected providers are not loaded.
yahoo Module¶
Optional Yahoo Finance provider, registered through ffn.providers.
- ffn.yahoo.download(ticker: str, field=None, start=None, end=None, **kwargs) Series[source]¶
Fetch one Yahoo series using the optional yfinance package.
Defaults to Adj Close with auto_adjust=False. Start is inclusive and end exclusive. Extra options are forwarded to yfinance.download. Responses are sorted by date, with the last observation kept for duplicate dates. This adapter does not cache results or require ffn-specific utilities.
utils Module¶
- ffn.utils.as_format(item: DataFrame | Series, format_str='.2f') DataFrame | Series[source]¶
Map a format string over a pandas object.
- ffn.utils.clean_ticker(ticker: str) str[source]¶
Cleans a ticker for easier use throughout MoneyTree
Splits by space and only keeps first bit. Also removes any characters that are not letters. Returns as lowercase.
>>> clean_ticker('^VIX') 'vix' >>> clean_ticker('SPX Index') 'spx'
- ffn.utils.memoize(f, refresh_keyword='mrefresh')[source]¶
Memoize decorator. The refresh keyword is the keyword used to bypass the cache (in the function call). If pickle rejects arguments with PicklingError, AttributeError, or TypeError, the call runs without caching. Other serialization errors propagate. Exceptions raised by the wrapped function are propagated unchanged. Pandas Series and DataFrame results are copied at the cache boundary so assignments through returned containers do not change cached data. Axes and attrs are copied independently. As with pandas deep copies, mutable Python objects stored in cells are not recursively copied.