diff --git a/pyfolio/capacity.py b/pyfolio/capacity.py index 00864741..bce1ba2e 100644 --- a/pyfolio/capacity.py +++ b/pyfolio/capacity.py @@ -18,10 +18,9 @@ def daily_txns_with_bar_data(transactions, market_data): transactions : pd.DataFrame Prices and amounts of executed trades. One row per trade. - See full explanation in tears.create_full_tear_sheet - market_data : pd.DataFrame - Daily market_data - - DataFrame has a multi-index index, one level is dates and another is - market_data contains volume & price, equities as columns + market_data : pd.Panel + Contains "volume" and "price" DataFrames for the tickers + in the passed positions DataFrames Returns ------- @@ -35,8 +34,9 @@ def daily_txns_with_bar_data(transactions, market_data): txn_daily = pd.DataFrame(transactions.assign( amount=abs(transactions.amount)).groupby( ['symbol', pd.Grouper(freq='D')]).sum()['amount']) - txn_daily['price'] = market_data.xs('price', level=1).unstack() - txn_daily['volume'] = market_data.xs('volume', level=1).unstack() + txn_daily['price'] = market_data.xs('price', level='market_data').unstack() + txn_daily['volume'] = market_data.xs('volume', + level='market_data').unstack() txn_daily = txn_daily.reset_index().set_index('date') @@ -64,10 +64,10 @@ def days_to_liquidate_positions(positions, market_data, positions: pd.DataFrame Contains daily position values including cash - See full explanation in tears.create_full_tear_sheet - market_data : pd.DataFrame - Daily market_data - - DataFrame has a multi-index index, one level is dates and another is - market_data contains volume & price, equities as columns + market_data : pd.Panel + Panel with items axis of 'price' and 'volume' DataFrames. + The major and minor axes should match those of the + the passed positions DataFrame (same dates and symbols). max_bar_consumption : float Max proportion of a daily bar that can be consumed in the process of liquidating a position. @@ -84,8 +84,10 @@ def days_to_liquidate_positions(positions, market_data, Datetime index, symbols as columns. """ - DV = market_data.xs('volume', level=1) * market_data.xs('price', level=1) - roll_mean_dv = DV.rolling(window=mean_volume_window, + dv = market_data.xs('volume', level='market_data') * \ + market_data.xs('price', level='market_data') + + roll_mean_dv = dv.rolling(window=mean_volume_window, center=False).mean().shift() roll_mean_dv = roll_mean_dv.replace(0, np.nan) @@ -93,7 +95,7 @@ def days_to_liquidate_positions(positions, market_data, positions_alloc = positions_alloc.drop('cash', axis=1) days_to_liquidate = (positions_alloc * capital_base) / \ - (max_bar_consumption * roll_mean_dv) + (max_bar_consumption * roll_mean_dv) return days_to_liquidate.iloc[mean_volume_window:] @@ -112,10 +114,10 @@ def get_max_days_to_liquidate_by_ticker(positions, market_data, positions: pd.DataFrame Contains daily position values including cash - See full explanation in tears.create_full_tear_sheet - market_data : pd.DataFrame - Daily market_data - - DataFrame has a multi-index index, one level is dates and another is - market_data contains volume & price, equities as columns + market_data : pd.Panel + Panel with items axis of 'price' and 'volume' DataFrames. + The major and minor axes should match those of the + the passed positions DataFrame (same dates and symbols). max_bar_consumption : float Max proportion of a daily bar that can be consumed in the process of liquidating a position. @@ -169,10 +171,10 @@ def get_low_liquidity_transactions(transactions, market_data, transactions : pd.DataFrame Prices and amounts of executed trades. One row per trade. - See full explanation in create_full_tear_sheet. - market_data : pd.DataFrame - Daily market_data - - DataFrame has a multi-index index, one level is dates and another is - market_data contains volume & price, equities as columns + market_data : pd.Panel + Panel with items axis of 'price' and 'volume' DataFrames. + The major and minor axes should match those of the + the passed positions DataFrame (same dates and symbols). last_n_days : integer Compute for only the last n days of the passed backtest data. """ @@ -186,8 +188,8 @@ def get_low_liquidity_transactions(transactions, market_data, txn_daily_w_bar = txn_daily_w_bar[txn_daily_w_bar.date > md] bar_consumption = txn_daily_w_bar.assign( - max_pct_bar_consumed=( - txn_daily_w_bar.amount/txn_daily_w_bar.volume)*100 + max_pct_bar_consumed=(txn_daily_w_bar.amount / + txn_daily_w_bar.volume) * 100 ).sort_values('max_pct_bar_consumed', ascending=False) max_bar_consumption = bar_consumption.groupby('symbol').first() @@ -228,8 +230,8 @@ def apply_slippage_penalty(returns, txn_daily, simulate_starting_capital, simulate_traded_dollars = txn_daily.price * simulate_traded_shares simulate_pct_volume_used = simulate_traded_shares / txn_daily.volume - penalties = simulate_pct_volume_used**2 \ - * impact * simulate_traded_dollars + penalties = (simulate_pct_volume_used ** 2 * + impact * simulate_traded_dollars) daily_penalty = penalties.resample('D').sum() daily_penalty = daily_penalty.reindex(returns.index).fillna(0)