Fractional Shares / Currencies not supported
realfishsam opened this issue · 1 comments
realfishsam commented
Expected Behavior
Fractional shares are supported by numerous brokers.
If you are trading currencies, it makes sense to allow for a dollar and a quarter. So, why isn't it allowed?
Hence, the expected behavior when calling self.buy(size=1.1) is not an error, but rather the absence of any error. Printing output['_trades'] should result in this output:
Size EntryBar ExitBar EntryPrice ExitPrice PnL ReturnPct \
0 -1.1 63 75 168.68196 179.13 -11.492844 -0.061939
1 1.1 75 85 179.48826 182.00 2.762914 0.013994
2 -1.1 85 88 181.63600 187.45 -6.395400 -0.032009
3 1.1 88 110 187.82490 179.27 -9.410390 -0.045547
4 -1.1 110 119 178.91146 196.96 -19.853394 -0.100880
.. ... ... ... ... ... ... ...
89 1.1 1947 1960 611.57070 588.72 -25.135770 -0.037364
90 -1.1 1960 1983 587.54256 580.01 8.285816 0.012820
91 1.1 1983 2059 581.17002 705.58 136.850978 0.214068
92 -1.1 2059 2087 704.16884 702.24 2.121724 0.002739
93 1.1 2087 2147 703.64448 797.80 103.571072 0.133811
EntryTime ExitTime Duration
0 2004-11-17 2004-12-06 19 days
1 2004-12-06 2004-12-20 14 days
2 2004-12-20 2004-12-23 3 days
3 2004-12-23 2005-01-26 34 days
4 2005-01-26 2005-02-08 13 days
.. ... ... ...
89 2012-05-11 2012-05-31 20 days
90 2012-05-31 2012-07-03 33 days
91 2012-07-03 2012-10-19 108 days
92 2012-10-19 2012-12-03 45 days
93 2012-12-03 2013-03-01 88 days
[94 rows x 10 columns]
Given that the size of a buy or sell order is a fraction.
Actual Behavior
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Cell In[2], [line 27](vscode-notebook-cell:?execution_count=2&line=27)
[20](vscode-notebook-cell:?execution_count=2&line=20) self.sell(size=1.1) # changed to fractional
[23](vscode-notebook-cell:?execution_count=2&line=23) bt = Backtest(GOOG, SmaCross,
[24](vscode-notebook-cell:?execution_count=2&line=24) cash=10000, commission=.002,
[25](vscode-notebook-cell:?execution_count=2&line=25) exclusive_orders=True)
---> [27](vscode-notebook-cell:?execution_count=2&line=27) output = bt.run()
[28](vscode-notebook-cell:?execution_count=2&line=28) bt.plot()
File /opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1170, in Backtest.run(self, **kwargs)
[1167](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1167) break
[1169](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1169) # Next tick, a moment before bar close
-> [1170](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1170) strategy.next()
[1171](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1171) else:
[1172](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1172) # Close any remaining open trades so they produce some stats
[1173](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:1173) for trade in broker.trades:
Cell In[2], [line 20](vscode-notebook-cell:?execution_count=2&line=20)
[18](vscode-notebook-cell:?execution_count=2&line=18) self.buy(size=1.1) # changed to fractional
[19](vscode-notebook-cell:?execution_count=2&line=19) elif crossover(self.sma2, self.sma1):
---> [20](vscode-notebook-cell:?execution_count=2&line=20) self.sell(size=1.1)
File /opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:223, in Strategy.sell(self, size, limit, stop, sl, tp)
[212](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:212) def sell(self, *,
[213](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:213) size: float = _FULL_EQUITY,
[214](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:214) limit: float = None,
[215](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:215) stop: float = None,
[216](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:216) sl: float = None,
[217](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:217) tp: float = None):
[218](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:218) """
[219](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:219) Place a new short order. For explanation of parameters, see `Order` and its properties.
[220](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:220)
[221](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:221) See also `Strategy.buy()`.
[222](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:222) """
--> [223](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:223) assert 0 < size < 1 or round(size) == size, \
[224](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:224) "size must be a positive fraction of equity, or a positive whole number of units"
[225](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/lib/python3.10/site-packages/backtesting/backtesting.py:225) return self._broker.new_order(-size, limit, stop, sl, tp)
AssertionError: size must be a positive fraction of equity, or a positive whole number of units
Steps to Reproduce
- Copy and paste the Backtesting.py snippet into your IDE
- Change argument
sizeinself.buy()and/orself.sell()to a floating point number - An assertion error will occur.
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA, GOOG
class SmaCross(Strategy):
n1 = 10
n2 = 20
def init(self):
close = self.data.Close
self.sma1 = self.I(SMA, close, self.n1)
self.sma2 = self.I(SMA, close, self.n2)
def next(self):
if crossover(self.sma1, self.sma2):
self.buy(size=1.1)
elif crossover(self.sma2, self.sma1):
self.sell(size=1.1)
bt = Backtest(GOOG, SmaCross,
cash=10000, commission=.002,
exclusive_orders=True)
output = bt.run()
bt.plot()Additional info
realfishsam commented
This closes the issue:
#1160