Skip to content

Commit 5852a76

Browse files
committed
ENH: Add the possibility to close trades at end of bt.run (kernc#273 & kernc#343)
1 parent 73e1534 commit 5852a76

File tree

3 files changed

+43
-12
lines changed

3 files changed

+43
-12
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ What's New
44
These were the major changes contributing to each release:
55

66

7-
### 0.x.x
7+
### 0.3.2
8+
* new param on run to manage close, or not the trades at end (#273)
89

910

1011
### 0.3.1
@@ -114,4 +115,4 @@ These were the major changes contributing to each release:
114115
### 0.1.0
115116
(2019-01-15)
116117

117-
* Initial release
118+
* Initial release

backtesting/backtesting.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,8 @@ def __init__(self,
984984
margin: float = 1.,
985985
trade_on_close=False,
986986
hedging=False,
987-
exclusive_orders=False
987+
exclusive_orders=False,
988+
close_all_at_end=False
988989
):
989990
"""
990991
Initialize a backtest. Requires data and a strategy to test.
@@ -1029,6 +1030,9 @@ def __init__(self,
10291030
trade/position, making at most a single trade (long or short) in effect
10301031
at each time.
10311032
1033+
if 'close_all_at_end' is 'False', the trade will not be close at end,
1034+
and will not apear in _Stats.
1035+
10321036
[FIFO]: https://www.investopedia.com/terms/n/nfa-compliance-rule-2-43b.asp
10331037
"""
10341038

@@ -1078,7 +1082,7 @@ def __init__(self,
10781082
warnings.warn('Data index is not datetime. Assuming simple periods, '
10791083
'but `pd.DateTimeIndex` is advised.',
10801084
stacklevel=2)
1081-
1085+
self._close_all_at_end = bool(close_all_at_end)
10821086
self._data: pd.DataFrame = data
10831087
self._broker = partial(
10841088
_Broker, cash=cash, commission=commission, margin=margin,
@@ -1164,14 +1168,15 @@ def run(self, **kwargs) -> pd.Series:
11641168
# Next tick, a moment before bar close
11651169
strategy.next()
11661170
else:
1167-
# Close any remaining open trades so they produce some stats
1168-
for trade in broker.trades:
1169-
trade.close()
1170-
1171-
# Re-run broker one last time to handle orders placed in the last strategy
1172-
# iteration. Use the same OHLC values as in the last broker iteration.
1173-
if start < len(self._data):
1174-
try_(broker.next, exception=_OutOfMoneyError)
1171+
if self._close_all_at_end is True:
1172+
# Close any remaining open trades so they produce some stats
1173+
for trade in broker.trades:
1174+
trade.close()
1175+
1176+
# Re-run broker one last time to handle orders placed in the last strategy
1177+
# iteration. Use the same OHLC values as in the last broker iteration.
1178+
if start < len(self._data):
1179+
try_(broker.next, exception=_OutOfMoneyError)
11751180

11761181
# Set data back to full length
11771182
# for future `indicator._opts['data'].index` calls to work

backtesting/test/_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,31 @@ def next(self):
398398

399399
self.assertFalse(Backtest(SHORT_DATA, S).run()._trades.empty)
400400

401+
def test_dont_close_orders_from_last_strategy_iteration(self):
402+
class S(Strategy):
403+
def init(self): pass
404+
405+
def next(self):
406+
if not self.position:
407+
self.buy()
408+
elif len(self.data) == len(SHORT_DATA):
409+
self.position.close()
410+
self.assertEqual(len(
411+
Backtest(SHORT_DATA, S, close_all_at_end=False).run()._strategy.closed_trades), 0)
412+
self.assertEqual(len(
413+
Backtest(SHORT_DATA, S, close_all_at_end=False).run()._strategy.trades), 1)
414+
415+
def test_dont_close_orders_trades_from_last_strategy_iteration(self):
416+
class S(Strategy):
417+
def init(self): pass
418+
419+
def next(self):
420+
if not self.position:
421+
self.buy()
422+
423+
self.assertEqual(len(
424+
Backtest(SHORT_DATA, S, close_all_at_end=False).run()._strategy.trades), 1)
425+
401426
def test_check_adjusted_price_when_placing_order(self):
402427
class S(Strategy):
403428
def init(self): pass

0 commit comments

Comments
 (0)