Open ch3st3rt0n opened 3 years ago
I'm having the exact same issue.
I'm trying to replicate the SMA crossover strategy from one of your examples using one stock (link below as well as code) and get the same error with different stocks / periods). Here are the error messages:
/root/miniconda3/lib/python3.8/site-packages/ffn/core.py:2299: RuntimeWarning: divide by zero encountered in true_divide res = np.divide(er.mean(), std)
ExceptionTraceback (most recent call last) in 7 bah_test = bt.Backtest(bah_strat, data) 8 # Run backtest ----> 9 bt_res = bt.run(sma20_test, sma50_test, sma100_test, ma_co_test, bah_test)
~/miniconda3/lib/python3.8/site-packages/bt/backtest.py in run(backtests) 26 # run each backtest 27 for bkt in backtests: ---> 28 bkt.run() 29 30 return Result(backtests)
~/miniconda3/lib/python3.8/site-packages/bt/backtest.py in run(self) 238 239 if not self.strategy.bankrupt: --> 240 self.strategy.run() 241 # need update after to save weights, values and such 242 self.strategy.update(dt)
~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.Strategy.run()
~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.AlgoStack.call()
~/miniconda3/lib/python3.8/site-packages/bt/algos.py in call(self, target) 1810 # Turn off updating while we rebalance each child 1811 for item in iteritems(targets): -> 1812 target.rebalance(item[1], child=item[0], base=base, update=False) 1813 1814 # Now update
~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.StrategyBase.rebalance()
~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.SecurityBase.allocate()
Exception: Cannot allocate capital to 0 because price is 0 as of 2019-04-26 00:00:00
----------- References ----------- === Link to example === https://pmorissette.github.io/bt/examples.html
=== Code snippet: ===
Calculate indicators
EMA_short = talib.EMA(data[ticker], timeperiod=10).to_frame() EMA_long = talib.EMA(data[ticker], timeperiod=40).to_frame() EMA_long.head()
Create signal
signal = EMA_long.copy() signal[EMA_short > EMA_long] = 1.0 signal[EMA_short < EMA_long] = -1.0 signal[EMA_long.isnull()] = 0.0
Define the strategy
ma_co_strat = bt.Strategy('EMA_crossover', [bt.algos.WeighTarget(signal), bt.algos.Rebalance()])
Run bt
ma_co_test = bt.Backtest(ma_co_strat, data) bt_res = bt.run(ma_co_test)
=== Installed packages === bt 0.2.9 ffn 0.3.6 decorator 5.0.9
Hey..Did you resolve tuple out of range error during bt.get?
I'm trying to replicate the SMA crossover strategy from one of your examples using one stock (link below as well as code) and get the same error with different stocks / periods). Here are the error messages: /root/miniconda3/lib/python3.8/site-packages/ffn/core.py:2299: RuntimeWarning: divide by zero encountered in true_divide res = np.divide(er.mean(), std) ExceptionTraceback (most recent call last) in 7 bah_test = bt.Backtest(bah_strat, data) 8 # Run backtest ----> 9 bt_res = bt.run(sma20_test, sma50_test, sma100_test, ma_co_test, bah_test) ~/miniconda3/lib/python3.8/site-packages/bt/backtest.py in run(backtests) 26 # run each backtest 27 for bkt in backtests: ---> 28 bkt.run() 29 30 return Result(backtests) ~/miniconda3/lib/python3.8/site-packages/bt/backtest.py in run(self) 238 239 if not self.strategy.bankrupt: --> 240 self.strategy.run() 241 # need update after to save weights, values and such 242 self.strategy.update(dt) ~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.Strategy.run() ~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.AlgoStack.call() ~/miniconda3/lib/python3.8/site-packages/bt/algos.py in call(self, target) 1810 # Turn off updating while we rebalance each child 1811 for item in iteritems(targets): -> 1812 target.rebalance(item[1], child=item[0], base=base, update=False) 1813 1814 # Now update ~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.StrategyBase.rebalance() ~/miniconda3/lib/python3.8/site-packages/bt/core.cpython-38-x86_64-linux-gnu.so in bt.core.SecurityBase.allocate() Exception: Cannot allocate capital to 0 because price is 0 as of 2019-04-26 00:00:00 ----------- References ----------- === Link to example === https://pmorissette.github.io/bt/examples.html === Code snippet: ===
Calculate indicators
EMA_short = talib.EMA(data[ticker], timeperiod=10).to_frame() EMA_long = talib.EMA(data[ticker], timeperiod=40).to_frame() EMA_long.head()
Create signal
signal = EMA_long.copy() signal[EMA_short > EMA_long] = 1.0 signal[EMA_short < EMA_long] = -1.0 signal[EMA_long.isnull()] = 0.0
Define the strategy
ma_co_strat = bt.Strategy('EMA_crossover', [bt.algos.WeighTarget(signal), bt.algos.Rebalance()])
Run bt
ma_co_test = bt.Backtest(ma_co_strat, data) bt_res = bt.run(ma_co_test) === Installed packages === bt 0.2.9 ffn 0.3.6 decorator 5.0.9
Hey..Did you resolve tuple out of range error during bt.get?
No, I got this error too and wasn't able to get this function working. I ended up with a workaround pulling the data myself from yahoo and passing it to bt directly.
I can't seem to get bt.algos.WeighTarget() to work in any scenario when backtesting, unless if I set the signal series to all zeroes (which of course doesn't do anything).
ma_co_strat = bt.Strategy('EMA_crossover', [bt.algos.WeighTarget(signal), bt.algos.Rebalance()])
Does it work if you add bt.algos.SelectAll()
? This is how I use it:
s1 = bt.Strategy('s1', [bt.algos.SelectAll(),
bt.algos.WeighTarget(strat),
bt.algos.Rebalance()])
b1 = bt.Backtest(s1, data,
initial_capital=capital,
commissions=comm,
integer_positions=integer_positions)
res = bt.run(b1)
res.display()
res.plot()
res.plot_weights('s1')
Here strat
has the same shape as data
, but it could have less rows (only when you need a rebalance).
ma_co_strat = bt.Strategy('EMA_crossover', [bt.algos.WeighTarget(signal), bt.algos.Rebalance()])
Does it work if you add
bt.algos.SelectAll()
? This is how I use it:s1 = bt.Strategy('s1', [bt.algos.SelectAll(), bt.algos.WeighTarget(strat), bt.algos.Rebalance()]) b1 = bt.Backtest(s1, data, initial_capital=capital, commissions=comm, integer_positions=integer_positions) res = bt.run(b1) res.display() res.plot() res.plot_weights('s1')
Here
strat
has the same shape asdata
, but it could have less rows (only when you need a rebalance).
Thanks for the tip, but it didn't help to include SelectAll(). Same error. I made sure the data and signal shapes were the same too. Any other ideas?
I'm running the master branch.
I tried running the master branch too. It looks like the error occurs when the signal flips to a non-zero value for the first time.
The columns of the DataFrame you pass into WeighTarget
must be the same as the securities of your backtest. When you are generating the EMAs in your code, you are using to_frame()
to convert the Series into DataFrames. The issue is that because these Series have no name, the resulting DataFrame's column name becomes '0' (check the output when you run EMA_long.head()
).
The easiest solution would be to rename the column in signal
. Rename the column so that it matches the security name in data
and your backtest should work.
signal = EMA_long.copy()
signal[EMA_short > EMA_long] = 1.0
signal[EMA_short < EMA_long] = -1.0
signal[EMA_long.isnull()] = 0.0
signal.columns = [ticker]
The columns of the DataFrame you pass into
WeighTarget
must be the same as the securities of your backtest. When you are generating the EMAs in your code, you are usingto_frame()
to convert the Series into DataFrames. The issue is that because these Series have no name, the resulting DataFrame's column name becomes '0' (check the output when you runEMA_long.head()
).The easiest solution would be to rename the column in
signal
. Rename the column so that it matches the security name indata
and your backtest should work.signal = EMA_long.copy() signal[EMA_short > EMA_long] = 1.0 signal[EMA_short < EMA_long] = -1.0 signal[EMA_long.isnull()] = 0.0 signal.columns = [ticker]
Thanks very much - this fixed it! I was using for backtesting 1 stock and didn't realize that the column names were missing.
Price=data1["Adj Close"].to_frame() Signal=data1["Signals"].to_frame() Signal.columns=[Price]
bt_strategy=bt.Strategy("Strategye", [bt.algos.WeighTarget(Signal), bt.algos.Rebalance()]) bt_backtest=bt.Backtest(bt_strategy, Price) bt_result=bt.run(bt_backtest) bt_result.plot(title='Backtest result')
Traceback (most recent call last): File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\main.py", line 79, in
bt_result=bt.run(btbacktest) File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\backtest.py", line 28, in run bkt.run() File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\backtest.py", line 240, in run self.strategy.run() File "bt\core.py", line 2101, in bt.core.Strategy.run File "bt\core.py", line 2040, in bt.core.AlgoStack.call File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\algos.py", line 1812, in call target.rebalance(item[1], child=item[0], base=base, update=False) File "bt\core.py", line 1016, in bt.core.StrategyBase.rebalance File "bt\core.py", line 1508, in bt.core.SecurityBase.allocate Exception: Cannot allocate capital to ('Adj Close',) because price is 0 as of 2017-01-11 00:00:00
Im new in this programing world and im kinda lost.
@EduardoLoz12 in your third line Signal.columns=[Price]
you set the name of the column as the DataFrame Price
. That doesn't seem right. See that, in the post above yours, the line signal.columns = [ticker]
sets the column name to a string stored in the variable ticker
.
From the previous posts, you should set the same column names in the Price and in the Signal frames, such as:
Price = data1["Adj Close"].to_frame()
Price.columns = [ticker]
Signal = data1["Signals"].to_frame()
Signal.columns = [ticker]
or:
Price = data1["Adj Close"].to_frame(name=ticker)
Signal = data1["Signals"].to_frame(name=ticker)
where ticker
is a string variable that you defined beforehand (the name of your stock/ETF/whatever) that will become the name of the column.
@danilogalisteu Thank you for the fast reply!
Alright, so i did this. I put "ticker"
as a string name for both columns (not sure if that's right though). Both are <class 'pandas.core.frame.DataFrame'>
and has the same column names when i print
those df
.
Price = data1["Adj Close"].to_frame(name="ticker") Signal = data1["Signals"].to_frame(name="ticker")
bt_strategy=bt.Strategy("Strategye",[bt.algos.WeighTarget(Signal), bt.algos.Rebalance()]) bt_backtest=bt.Backtest(bt_strategy, Price) bt_result=bt.run(bt_backtest)
But now i have a different error:
Traceback (most recent call last): File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\main.py", line 99, in
bt_result=bt.run(bt_backtest) File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\backtest.py", line 28, in run bkt.run() File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\backtest.py", line 240, in run self.strategy.run() File "bt\core.py", line 2101, in bt.core.Strategy.run File "bt\core.py", line 2040, in bt.core.AlgoStack.call File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\bt\algos.py", line 1812, in call target.rebalance(item[1], child=item[0], base=base, update=False) File "bt\core.py", line 955, in bt.core.StrategyBase.rebalance File "C:\Users\E.Lozada\PycharmProjects\CryptoRobot\venv\lib\site-packages\pandas\core\series.py", line 185, in wrapper raise TypeError(f"cannot convert the series to {converter}") TypeError: cannot convert the series to <class 'float'>
Also, the only way to make bt
work is if you get your data from bt.get()
? Because I've got my data from web.DataReader()
to get "high","low","Adj Close", etc to build some other indicators. But at the end in my second line Signal = data1["Signals"].to_frame(name=ticker)
is a dataframe with 1,-1,0. Just as the example from the beginning.
Sorry if I can not explain myself better, I'm just trying to build my first code.
Also, the only way to make
bt
work is if you get your data frombt.get()
?
No, any numerical series/dataframe with a datetime index should work. bt.get()
just gets the adjusted close prices from Yahoo by default.
It's hard to help you at all without being able to look at your code and run it or at least look at the data you are using. The last error tells you that something should have float
type but doesn't. Can you at least show data1
or the Price
and Signal
dataframes?
Thanks, sure I can share with you the data of data1
.
data1Excel.xlsx.
I still cant fix this problem.
When i run this code without the bt.algos.Rebalance()
it works but give me all the data spots of bt.display() = 0. I dont know if that means something??
bt_strategy=bt.Strategy("Strategye",[bt.algos.WeighTarget(Signal), bt.algos.Rebalance()]) bt_backtest=bt.Backtest(bt_strategy, Price) bt_result=bt.run(bt_backtest)
Stat Strategye
Start 2016-12-31 End 2021-11-10 Risk-free rate 0.00%
Total Return 0.00% Daily Sharpe - Daily Sortino - CAGR 0.00% Max Drawdown 0.00% Calmar Ratio -
Before I run the code, this is what I see in the relevant columns of your data:
Date | Adj Close | Signals |
---|---|---|
2016-12-29 00:00:00 | 973.497 | 0 |
2016-12-30 00:00:00 | 961.238 | 0 |
2016-12-31 00:00:00 | 963.743 | 0 |
2017-01-01 00:00:00 | 998.325 | 0 |
2017-01-02 00:00:00 | 1021.75 | 0 |
2017-01-03 00:00:00 | 1043.84 | 0 |
2017-01-04 00:00:00 | 1154.73 | 0 |
2017-01-05 00:00:00 | 1013.38 | 0 |
2017-01-06 00:00:00 | 902.201 | 1 |
2017-01-07 00:00:00 | 908.585 | 0 |
2017-01-08 00:00:00 | 911.199 | 0 |
2017-01-09 00:00:00 | 902.828 | 0 |
Remember that Signal
is actually a weight, and not a signal to buy or sell (bt.algos.WeighTarget(Signal)
). At the moment, you are buying the asset as your whole portfolio on 2017-01-06 at the price of 902.201 and selling it all on the next day at the price of 908.585. Is this what you want?
@danilogalisteu No, actually. I was trying to code that Signal=1
= Buy, Signal=-1
= Sell. Am i using the wrong algos
? Should i change the Signal data to weigh type?
You should change your signal to stay at 1 when you want to be long and go to 0 when you want to be flat, if that's what you want.
Regarding the data, your dataframe has two lines for the date 2017-03-26 and that's where it goes down for some reason. You should make sure your data has only one line for each date. I ran the code like this (there is a comment for the fix):
import pandas as pd
import bt
data1 = pd.read_excel(r"C:\Users\danil\Downloads\data1Excel.xlsx", sheet_name='Data', index_col='Date', parse_dates=['Date'])
# this line removes rows with duplicated index values, keeping the last one
data1 = data1[~data1.index.duplicated(keep='last')]
Price = data1["Adj Close"].to_frame(name="ticker")
Signal = data1["Signals"].to_frame(name="ticker")
bt_strategy = bt.Strategy("Strategye", [bt.algos.WeighTarget(Signal), bt.algos.Rebalance()])
bt_backtest = bt.Backtest(bt_strategy, Price)
bt_result = bt.run(bt_backtest)
bt_result.display()
bt_result.plot()
This would be the weight calculation from the signal if you want to go long at signal=1 and go flat at signal=-1.
import numpy as np
import pandas as pd
import bt
from matplotlib import use
use('Qt5Agg')
data1 = pd.read_excel(r"C:\Users\danil\Downloads\data1Excel.xlsx", sheet_name='Data', index_col='Date', parse_dates=['Date'])
# this line removes rows with duplicated index values, keeping the last one
data1 = data1[~data1.index.duplicated(keep='last')]
Price = data1["Adj Close"].to_frame(name="ticker")
Signal = data1["Signals"].to_frame(name="ticker")
# go long at signal=1 and go flat at signal=-1
Weight = Signal.replace(0, np.nan).replace(-1, 0).ffill().replace(np.nan, 0)
bt_strategy = bt.Strategy("Strategye", [bt.algos.WeighTarget(Weight), bt.algos.Rebalance()])
bt_backtest = bt.Backtest(bt_strategy, Price)
bt_result = bt.run(bt_backtest)
bt_result.display()
bt_result.plot()
The equity curve is something:
The results are below.
Stat Strategye
------------------- -----------
Start 2016-12-28
End 2021-11-08
Risk-free rate 0.00%
Total Return 1419.91%
Daily Sharpe 1.02
Daily Sortino 1.71
CAGR 75.01%
Max Drawdown -71.35%
Calmar Ratio 1.05
MTD 7.50%
3m 15.84%
6m 15.84%
YTD 130.48%
1Y 289.28%
3Y (ann.) 118.14%
5Y (ann.) 75.01%
10Y (ann.) -
Since Incep. (ann.) 75.01%
Daily Sharpe 1.02
Daily Sortino 1.71
Daily Mean (ann.) 51.37%
Daily Vol (ann.) 50.39%
Daily Skew 0.81
Daily Kurt 11.03
Best Day 25.21%
Worst Day -18.73%
Monthly Sharpe 1.20
Monthly Sortino 3.28
Monthly Mean (ann.) 72.10%
Monthly Vol (ann.) 59.90%
Monthly Skew 1.13
Monthly Kurt 2.15
Best Month 58.43%
Worst Month -31.28%
Yearly Sharpe 1.00
Yearly Sortino 4.24
Yearly Mean 108.43%
Yearly Vol 108.21%
Yearly Skew -0.71
Yearly Kurt 1.73
Best Year 241.61%
Worst Year -57.16%
Avg. Drawdown -11.71%
Avg. Drawdown Days 55.80
Avg. Up Month 18.25%
Avg. Down Month -5.05%
Win Year % 80.00%
Win 12m % 83.67%
@danilogalisteu Thank you very much! I used everything you coded and it worked! Will keep working on my trading bot now.
I'm trying to replicate the SMA crossover strategy from one of your examples using one stock (link below as well as code) and get the same error with different stocks / periods). Here are the error messages:
/root/miniconda3/lib/python3.8/site-packages/ffn/core.py:2299: RuntimeWarning: divide by zero encountered in true_divide res = np.divide(er.mean(), std)
ExceptionTraceback (most recent call last)