From 0d00679e454608c5044adb7ec07efeb9e370c1e0 Mon Sep 17 00:00:00 2001 From: Taku Suzuki Date: Mon, 24 Apr 2023 21:00:41 +0900 Subject: [PATCH] prevent same orders from execuing and exiting at the same interval (#50) --- backtest/README.md | 20 ++++++++++++++++++++ backtest/run_backtest_market_order_test.go | 21 +++++++++++++++++++++ backtest/strategy_execute.go | 9 +++++++-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/backtest/README.md b/backtest/README.md index 98f1227..e83221c 100644 --- a/backtest/README.md +++ b/backtest/README.md @@ -19,3 +19,23 @@ Market order will be executed on the next OHLCV bar. Limit order will be placed and open until either it is executed or cancelled. +## Order Entry/Exit States + +On each `OnNextOHLCV` method, `Entry()` and `Exit()` enters and exits a position, respectively. + +### Entry() + +- Calling `Entry()` without a limit price will execute on the next bar's open +- Calling `Entry()` with a limit price will execute at the limit price if the next bar's low is equal to or below the limit price. The scenario is the same for short orders using the bar's high value. +- Calling `Entry()` with a limit price that doesn't execute on the next bar's low will be an open order until it is executed or canceled. + + +### Exit() + +- Calling `Exit()` without a limit price will execute on the next bar's open. + +### Entry() and Exit() + +If an order entry and exit both exists for the same order ID, here is the expected behavior: + +- If entry and exit are both exist, no trades will be executed. diff --git a/backtest/run_backtest_market_order_test.go b/backtest/run_backtest_market_order_test.go index b7295a5..c747fdc 100644 --- a/backtest/run_backtest_market_order_test.go +++ b/backtest/run_backtest_market_order_test.go @@ -63,3 +63,24 @@ func TestRunBacktestMarketOrder(t *testing.T) { t.Errorf("Expected NetProfit to be 1.125 but got %+v", res.NetProfit) } } + +// TestRunBacktestMarketCancelOutOrder tests when a market entry order and a market exit order are both queued +func TestRunBacktestMarketCancelOutOrder(t *testing.T) { + b := &testMystrat{} + data := pine.OHLCVTestData(time.Now(), 4, 5*60*1000) + data[0].C = 16 + data[1].C = 16 + data[2].C = 16 + data[3].C = 16 + + series, _ := pine.NewOHLCVSeries(data) + + res, err := RunBacktest(series, b) + if err != nil { + t.Fatal(errors.Wrap(err, "error runbacktest")) + } + + if res.TotalClosedTrades != 0 { + t.Errorf("Expected total trades to be 0 but got %d", res.TotalClosedTrades) + } +} diff --git a/backtest/strategy_execute.go b/backtest/strategy_execute.go index e006a0b..ebc7150 100644 --- a/backtest/strategy_execute.go +++ b/backtest/strategy_execute.go @@ -9,8 +9,13 @@ func (s *strategy) Execute(ohlcv pine.OHLCV) error { // convert open entry orders into open positions for _, v := range s.ordEntry { - _, found := s.findPos(v.OrdID) - if found { + // if this order is already executed and is open, continue + if _, found := s.findPos(v.OrdID); found { + continue + } + + // if this is also in the exit queue, then cancel this out + if _, found := s.ordExit[v.OrdID]; found { continue }