Closed nyobna closed 9 years ago
Hey @nyobna,
Just ran your code and it works fine on my machine. What versions do you have installed for bt and pandas?
Cheers, Phil
Hi Phil,
Let me say great work btw!
bt.version (0,1,9) pd.version '0.14.1'
Thanks! I appreciate it!
I think this issue might be related to your pandas version. I am running 0.16 (most recent version) and not getting this issue. I don't know what your setup is like but if you can update pandas, give that a try.
Cheers, Phil
Perfect that worked!
One other question - trying to run the plot_correlation I get the following:
AttributeError Traceback (most recent call last)
Hey @nyobna,
The period parameter should be one of the pandas 'offset aliases'. I will add this to the docstring to make it clearer.
Here is the reference from the pandas docs:
http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
Basically, you would use 'd' for daily, 'm' for monthly, etc...
Hope this helps, Phil
It does indeed! I am able to plot a correlation matrix on the strategy returns.
Can I use plot_correlation function on a DataFrame or on a bt.get? Can you provide an example of how to add commission to the backtest? And what exactly it does (bps, absolute $, etc?)
Apologies as I am still learning my way around Python and/or this isn't the correct venue to ask these types of questions.
I am really having fun with your library!
I've been putting away all weekend on this and having a lot of fun with it! Unfortunately my python skills might still not be up to par to take it and run with it and I still need some assistance.
data = bt.get('spy, mdy, efa, eem, tlt', start='2000-01-01')
s = bt.Strategy('s1', [bt.algos.RunMonthly(), bt.algos.SelectAll(), bt.algos.WeighSpecified({'spy':1.0}), bt.algos.SelectMomentum(n=1, lookback = pd.DateOffset(months=1)), bt.algos.Rebalance()])
test = bt.Backtest(s, data) res = bt.run(test)
res.plot()
1) SelectMomentum - I'm having issues with the lookback, DateOffset, what is the proper syntax to use? I don't get an error with compiling but the lookback window does not change. 2) WeighSpecified - I originally tried WeighRandomly however the only bounds it recognized was 0 and 1. I tried bounds = (0.5, 2.0) and would get a flat line for equity curve. Similarly above I'm trying to use 1 security in a momentum strategy and weigh it 100% (it can pick from 5 securities) 3) Commission - do you have an example of usage?
Some other questions related to the backtest itself: 1) When do the execution occurs? (In order to ensure no look-ahead bias)
Thanks again!
Hey @nyobna,
Glad to hear you are enjoying bt!
I was actually just writing a reply to your last post.
Anyways, the basic structure for a backtest is Select -> Weigh -> Rebalance. Assuming you want to pick the best performer over the last month and allocate capital to it on a monthly basis, you should do this:
data = bt.get('spy, mdy, efa, eem, tlt', start='2000-01-01')
s = bt.Strategy('s1', [bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.SelectMomentum(n=1, lookback = pd.DateOffset(months=1)),
bt.algos.WeighEqually(),
bt.algos.Rebalance()])
test = bt.Backtest(s, data)
res = bt.run(test)
res.plot()
Basically, you are selecting the top performer over the last month, giving it a weight of one (equal weight with 1 asset = 100% in that 1 asset) and rebalancing your portfolio.
For commissions, there is a basic commission function that is applied by default. Basically, it's $0.01 per share with a minimum of $1 a transaction.
You can change this by providing the commission argument to bt.Backtest. All you need to provide is a function that takes 2 arguments: quantity and price and returns the commission. For example:
def my_comm(q, p):
return abs(q) * 1
data = bt.get('spy, mdy, efa, eem, tlt', start='2000-01-01')
s = bt.Strategy('s1', [bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.SelectMomentum(n=1, lookback = pd.DateOffset(months=1)),
bt.algos.WeighEqually(),
bt.algos.Rebalance()])
test = bt.Backtest(s, data, commissions=my_comm)
res = bt.run(test)
res.plot()
I made the commission quite extreme here so that you would see the difference.
As for the execution, it occurs at the price of that day (for a daily backtest). So you have all the data available including the current day. Now, there is a implicit assumption that you can make a decision and trade at that same price. I find this to be acceptable - basically it's as if you were to run the numbers a few minutes before the close and trade at the close price. In my opinion, you shouldn't have any systematic bias this way, at least not for the example mentioned.
If this is not something you are willing to live with, then you can use the lag parameter in SelectMomentum for example, which lags the total return calculation window by a given amount.
Anyways, I hope this clarifies things. Let me know if you have any other questions.
Cheers, Phil
Thanks so much for the quick response!
One other question that came up, can you apply leverage to strategies? I've been using meanVar function but when I pass it bounds = (0,2) the results from plotting security weights max out at 1 and -1.
I've passed on your library to a few friends, you might see some more hits soon :)
Hey @nyobna,
Thanks for passing along the library!
The WeighMeanVar algo uses ffn's calc_mean_var_weights function under the hood. It might not be clear from the docstring, but the bounds parameter is used to restrict the maximum/minimum weights a single security can have. However, as it currently stands, the optimizer always finds a set of weights that adds up to 1. So basically you could pass in bounds (0.05, 0.25) if you wanted to make sure that a single security had at least 5% but no more than 25% allocation.
Therefore, the WeighMeanVar algo will always return weights that add up to 1. It may be a good idea to add a parameter so that this can changed, but I still have to think about it. Not sure how the optimizer would react to different target sums.
In the meantime, you could do something like this:
class ApplyLeverage(bt.Algo):
def __init__(self, leverage):
super(ApplyLeverage, self).__init__()
self.leverage = leverage
def __call__(self, target):
target.temp['weights'] = target.temp['weights'] * self.leverage
return True
s = bt.Strategy('s1', [bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.WeighMeanVar(),
ApplyLeverage(2.),
bt.algos.Rebalance()])
test = bt.Backtest(s, data)
res = bt.run(test)
res.plot()
Basically, you are creating a new custom Algo
that will scale up the target weights by a factor of 2. I don't know if this is exactly what you want but the general idea applies. You can create your own algos and modify the target weights according to your needs.
Hope this helps, Phil
Hi Phil: First off, I'd like to thank you for all the time you have put into the bt package, I'm really looking forward to learning how to use this powerful backtesting package. In order to familiarize myself with the package, I tried to replicate the SMA strategy in Anaconda. When I run the sample, I receive an "NotImplementedError". I've entered the sample code as it is displayed on github and was wondering why this might be happening.
Thank you for any help you can provide me with on this error and for putting together a great package!
Archie
Hey @Archieus,
Thanks for the kind words!
Could you post the code that you are running and the error message?
Cheers, Phil
Hi Phil:
I think I typed the code in correctly, here it is:
import bt data = bt.get('aapl,msft,c,gs,ge', start = '2010-01-01') import pandas as pd sma = pd.rolling_mean(data, 50) plot = bt.merge(data,sma).plot(figsize=(15, 5)) class SelectWhere(bt.Algo): def init(self, signal): self.signal = signal
def _call_(self, target):
if target.now in self.signal.index:
sig = self.signal.ix[target.now]
selected = list(sig.index[sig])
target.temp['selected'] = selected
return True
signal = data > sma s = bt.Strategy('above50sma', [SelectWhere(signal), bt.algos.WeighEqually(), bt.algos.Rebalance()])
t = bt.Backtest(s, data) res = bt.run(t) res.plot('d') res.display()
The error occurs where the backtest is run: res = bt.run(t) from what I can tell by running it line by line.
Thank you for your help.
Archie
Hi Phil: I realized I forgot to include the error that was generated when running this sample, so I've cut and pasted it here along with a section of the data it printed.
line 57, in
File "build\bdist.win-amd64\egg\bt\backtest.py", line 25, in run bkt.run()
File "build\bdist.win-amd64\egg\bt\backtest.py", line 164, in run self.strategy.run()
File "bt/core.py", line 1157, in bt.core.Strategy.run (bt/core.c:14847)
File "bt/core.py", line 1102, in bt.core.AlgoStack.call (bt/core.c:14249)
File "bt/core.py", line 1075, in bt.core.Algo.call (bt/core.c:13674)
NotImplementedError: aapl msft c gs ge
Date
2010-01-04 False False False False False
2010-01-05 False False False False False
2010-01-06 False False False False False
2010-01-07 False False False False False
2015-04-24 True True True True True 2015-04-27 True True True True True 2015-04-28 True True True True True
[1338 rows x 5 columns] not implemented!
Hey @Archieus,
Could you try running the following code:
data = bt.get('aapl,msft,c,gs,ge', start = '2010-01-01')
sma = pd.rolling_mean(data, 50)
s = bt.Strategy('above50sma', [bt.algos.SelectWhere(data > sma),
bt.algos.WeighEqually(),
bt.algos.Rebalance()])
t = bt.Backtest(s, data)
res = bt.run(t)
The SelectWhere algo is already implemented in the library so you don't have to re-write it. I was just showing how you could implement a basic version of it in the docs.
Anyways, this code works without errors on my machine. Let me know if it works for you.
Cheers, Phil
Hi Phil:
That did it! Thanks again for all of your help! I am very excited to start testing other ideas and strategies using your package!
Best, Archie
No Problem just thanks. Thanks for your BT program. I've been experimenting for a couple of days and it works great. I particularly like your Tree Structure concept. Here is an example I did with 6 equities chosen by momentum and volitility. data = bt.get('^GSPC,VBMFX,VUG,VGK,VNQ,DBC', start='1997-01-03') #S&P and Investment grade bonds print data.head(3) sm = bt.Strategy('sm', [bt.algos.RunMonthly(), bt.algos.SelectAll(), bt.algos.SelectMomentum(n = 2,lookback=pd.DateOffset(months=4)), bt.algos.WeighInvVol(lookback=pd.DateOffset(months=4)), bt.algos.Rebalance()]) testm = bt.Backtest(sm, data) resm = bt.run(testm) resm.plot_security_weights() resm.plot() resm.display() Start 2006-02-06 End 2015-04-24 Risk-free rate 0.00%
Total Return 226.30% Daily Sharpe 0.99 CAGR 13.70% Max Drawdown -19.71% So far haven't attempted my own algos but would love one with layered momentum (weighted 3m + weighted 6m). Thanks again.
Hey @GatorByte,
Glad you like it! Always nice to have user feedback. Let me know if you run into any issues!
Cheers, Phil
Hi,
@pmorissette Thanks for the great work!
I'm using the below code. But I would like to set thresh hold of 6% for rebalancing. Could you advice me which line needs to be changed. Sorry that I'm an amateur in python. Thanks for your help.
import bt
data = bt.get('VTI, BND',start='2007,01,11',end='2014,01,11') print (data.head())
class OrderedWeights(bt.Algo): def init(self, weights): self.target_weights = weights
def call(self, target): target.temp['weights'] = dict(zip(target.temp['selected'], self.target_weights)) return True
def my_comm(q, p): return abs(q) * 0.5 # I have inputted 0.5, hope this space denotes that my commision per trade is $0.5
s = bt.Strategy('Equal Weighted Portfolio', [bt.algos.RunMonthly(), bt.algos.SelectAll(), OrderedWeights([0.5, 0.5]), bt.algos.Rebalance()])
test = bt.Backtest(s, data, initial_capital=10000, commissions=my_comm) res = bt.run(test)
res.plot()
res.display()
Hi,
@pmorissette Thanks for this huge works !
I try to use the following code:
import bt
data = bt.get('AAPL, MSFT', start= '2010-01-01', end= '2010-04-01') data.info() print(data.head())
w = {'AAPL': 0.5, 'MSFT': 0.5}
s = bt.Strategy('s1', [bt.algos.RunWeekly(), bt.algos.SelectAll(), bt.algos.WeighSpecified({'AAPL': 0.5, 'MSFT': 0.5}), bt.algos.Rebalance()])
test = bt.Backtest(s, data, name='first_test', initial_capital= 1000, progress_bar= True) res = bt.run(test)
Unfortunately, on the bt.algos.WeighSpecified line, i have a type error: init() takes 1 positional argument but 2 were given. I try to understand why but i don't find the solution.
Thanks
I am getting a keyerror on 'selected' in ~/opt/anaconda3/lib/python3.9/site-packages/bt/algos.py in call(self, target) 992 993 def call(self, target): --> 994 selected = target.temp["selected"] 995 n = len(selected) 996
KeyError: 'selected' I tried to implement my own SelectWhere as follows: class SelectWhere(bt.Algo):
"""
Selects securities where the value is True on the current date (target.now).
Args:
* signal: DataFrame containing the signal (boolean DataFrame)
"""
def __init__(self, signal):
self.signal = signal
# - - - - - - - - - -
def __call__(self, target):
# get signal on target.now
if target.now in self.signal.index:
sig = self.signal.loc[target.now]
#print("print",sig)
#print(type(sig))
#print("print2",sig.)
# get indices where true as list
#selected = list(sig.index[sig]) #orig code
selected = list(sig[sig == True].index) # your code
#print(selected)
# save in temp - this will be used by the weighing algo
#print(target.temp)
target.temp = {"selected": selected} #you added this
#target.temp['selected'] = selected
# return True because we want to keep on moving down the stack
return True
Code: import bt
data = bt.get('spy, agg', start='2010-01-01')
s = bt.Strategy('s1', [bt.algos.RunMonthly(), bt.algos.SelectAll(), bt.algos.WeighEqually(), bt.algos.Rebalance()])
test = bt.Backtest(s, data) res = bt.run(test)
AttributeError Traceback (most recent call last)