mirror of
https://github.com/KhizarImran/backtestingfx.git
synced 2026-11-02 11:36:31 +00:00
Document and test stop-first intrabar execution policy (#49)
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import unittest
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from backtestingfx import Backtest, Strategy
|
||||
|
||||
|
||||
class IntrabarExecutionTest(unittest.TestCase):
|
||||
def test_new_entries_are_checked_from_the_following_bar(self):
|
||||
for is_long in (True, False):
|
||||
for count in (1, 2):
|
||||
with self.subTest(is_long=is_long, count=count):
|
||||
seen = []
|
||||
|
||||
class EnterOnce(Strategy):
|
||||
def next(self):
|
||||
seen.append(len(self.positions))
|
||||
if self.index == 0:
|
||||
if is_long:
|
||||
self.buy(1.0, stop_loss=1.09, take_profit=1.11)
|
||||
else:
|
||||
self.sell(1.0, stop_loss=1.11, take_profit=1.09)
|
||||
|
||||
index = pd.date_range("2026-01-01", periods=count, freq="h", tz="UTC")
|
||||
data = pd.DataFrame(
|
||||
{"open": [1.1] * count, "high": [1.12] * count,
|
||||
"low": [1.08] * count, "close": [1.1] * count},
|
||||
index=index,
|
||||
)
|
||||
stats = Backtest(data, EnterOnce, commission=0, spread=0).run()
|
||||
self.assertEqual(stats.num_trades, 1)
|
||||
trade = stats.trades[0]
|
||||
self.assertEqual(trade.entry_timestamp, int(index[0].timestamp()))
|
||||
self.assertEqual(trade.exit_timestamp, int(index[-1].timestamp()))
|
||||
if count == 1:
|
||||
# Final liquidation at close: the entry bar's range is not replayed.
|
||||
self.assertAlmostEqual(trade.exit_price, 1.1)
|
||||
self.assertAlmostEqual(trade.pnl, 0.0)
|
||||
self.assertEqual(seen, [0])
|
||||
else:
|
||||
# Both levels touched on the next bar: stop is processed before next().
|
||||
self.assertAlmostEqual(trade.exit_price, 1.09 if is_long else 1.11)
|
||||
self.assertAlmostEqual(trade.pnl, -1000.0)
|
||||
self.assertEqual(seen, [0, 0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user