I have been watching the shared algorithms that are posted. It is fun and interesting to see the ideas people come up with.
However I'm also starting to see a trend where the comment replies inevitably say something like "the algorithm borrows wildly, many times beyond the starting cash". These sort of comments are useful feedback but also distract from the main idea being shared.
So I started to investigate the reason behind the massive over-spending by algorithms.
The documentation lists two properties of the portfolio object that should help determine how much cash has been used versus how much is available for spending: context.portfolio.cash and context.portfolio.capital_used.
I have tried to understand how to use these variables but I simply cannot make them work for me. They seem to only be useful in a strictly long portfolio. Adding in short positions causes their usefulness to evaporate.
In the attached backtest I show a portfolio composed of equally balanced long and short positions. Each day $10k is added to both the long and short side. Each month the overall position is closed out and started all over.
I plotted context.portfolio.cash and capital_used and found that neither one changed meaningfully over the period of the backtest. Even though the algorithm was investing $20k per day there was negligible change.
I now believe that the main culprit is context.portfolio.capital_used. The formula for calculating the amount of capital used must be incorrect. Once it is fixed then context.portfolio.cash should also start working (since it likely uses capital_used as an input).
I came to this conclusion by calculating my own version of context.portfolio.capital_used which you will see in the backtest as abs_cap_used. If you look at the custom recorded variables graph the only useful information is from abs_cap_used and abs_cash.
If anyone has the patience and willingness to review the source code I would appreciate any feedback or corrections.
If the Quantopian team sees this please consider changing the formula for context.portfolio.capital_used (or adding a new property that reflects the modified calculation).
|Returns||1 Month||3 Month||6 Month||12 Month|
|Alpha||1 Month||3 Month||6 Month||12 Month|
|Beta||1 Month||3 Month||6 Month||12 Month|
|Sharpe||1 Month||3 Month||6 Month||12 Month|
|Sortino||1 Month||3 Month||6 Month||12 Month|
|Volatility||1 Month||3 Month||6 Month||12 Month|
|Max Drawdown||1 Month||3 Month||6 Month||12 Month|
def initialize(context): set_commission(commission.PerTrade(cost=0)) # ignore commission costs for clarity context.daycounter = 0 def handle_data(context, data): # use virtually identical securities for net zero position spy = sid(8554) # SPY, S&P 500 ivv = sid(21513) # IVV, S&P 500 # go long and short the same amount order(spy, -10000 / data[spy].price) # short $10k of SPY order(ivv, +10000 / data[ivv].price) # long $10k of IVV # record built-in portfolio variables: cash and capital_used record(cash = context.portfolio.cash) record(capital_used = context.portfolio.capital_used) # calculate capital_used the Quantopian way: -amount*cost_basis pos = context.portfolio.positions q_capital_used = sum(-pos[s].amount * pos[s].cost_basis for s in pos) record(q_cap_used = q_capital_used) # calculate capital_used using absolute amount: abs(amount)*cost_basis pos = context.portfolio.positions abs_capital_used = sum(abs(pos[s].amount) * pos[s].cost_basis for s in pos) record(abs_cap_used = abs_capital_used) # calculate free cash: starting_cash + pnl - capital_used port = context.portfolio abs_cash = port.starting_cash + port.pnl - abs_capital_used record(abs_cash = abs_cash) # close all positions once per month context.daycounter += 1 if context.daycounter > 20: for stock in pos: order(stock, -pos[stock].amount) context.daycounter = 0