test_backtest.jl
1 using PlanarDev.Stubs 2 using Test 3 using .Planar.Engine.Simulations.Random 4 using PlanarDev.Planar.Engine.Lang: @m_str 5 6 openval(s, a) = s.universe[a].ohlcv.open[begin] 7 closeval(s, a) = s.universe[a].ohlcv.close[end] 8 test_synth(s) = begin 9 @test openval(s, m"sol") == 101.0 10 @test closeval(s, m"sol") == 1753.0 11 @test openval(s, m"eth") == 99.0 12 @test closeval(s, m"eth") == 574.0 13 @test openval(s, m"btc") == 97.0 14 @test closeval(s, m"btc") == 123.0 15 end 16 17 _ai_trades(s) = s[m"eth"].history 18 eq1(a, b) = isapprox(a, b; atol=1e-1) 19 test_nomargin_market(s) = begin 20 @test egn.marginmode(s) isa egn.NoMargin 21 s.attrs[:overrides] = (; ordertype=:market) 22 egn.start!(s) 23 @test first(_ai_trades(s)).order isa egn.MarketOrder 24 @info "TEST: " s.cash.value 25 @test eq1(Cash(:USDT, 9.39228334), s.cash.value) 26 @test eq1(Cash(:USDT, 0.0), s.cash_committed) 27 @test st.trades_count(s) == 4657 28 mmh = st.minmax_holdings(s) 29 @test mmh.count == 0 30 @test mmh.min[1] == :USDT 31 @test mmh.min[2] ≈ Inf 32 @test mmh.max[1] == :USDT 33 @test mmh.max[2] ≈ 0.0 atol = 1e-4 34 end 35 36 test_nomargin_gtc(s) = begin 37 @test marginmode(s) isa egn.NoMargin 38 s.attrs[:overrides] = (; ordertype=:gtc) 39 egn.start!(s) 40 @test first(_ai_trades(s)).order isa egn.GTCOrder 41 @info "TEST: " s.cash.value 42 @test eq1(Cash(:USDT, 7615.8), s.cash.value) 43 @test eq1(Cash(:USDT, 0.0), s.cash_committed) 44 @test st.trades_count(s) == 10105 45 mmh = st.minmax_holdings(s) 46 @test mmh.count == 0 47 @test mmh.min[1] == :USDT 48 @test mmh.min[2] ≈ Inf atol = 1e3 49 @test mmh.max[1] == :USDT 50 @test mmh.max[2] ≈ 0 atol = 1e3 51 end 52 53 test_nomargin_ioc(s) = begin 54 @test marginmode(s) isa egn.NoMargin 55 s.attrs[:overrides] = (; ordertype=:ioc) 56 egn.start!(s) 57 @test first(_ai_trades(s)).order isa egn.IOCOrder 58 @info "TEST: " s.cash.value 59 @test Cash(:USDT, 694.909e3) ≈ s.cash atol = 1 60 @info "TEST: " s.cash_committed.value 61 @test Cash(:USDT, -0.4e-7) ≈ s.cash_committed atol = 1e-6 62 @test st.trades_count(s) == 10244 63 mmh = st.minmax_holdings(s) 64 @test mmh.count == 0 65 @test mmh.min[1] == :USDT 66 @test mmh.min[2] ≈ Inf atol = 1e-1 67 @test mmh.max[1] == :USDT 68 @test mmh.max[2] ≈ 0.0 atol = 1e-1 69 end 70 71 test_nomargin_fok(s) = begin 72 @test marginmode(s) isa egn.NoMargin 73 s.attrs[:overrides] = (; ordertype=:fok) 74 # increase cash to trigger order kills 75 s.config.initial_cash = 1e6 76 s.config.min_size = 1e3 77 egn.start!(s) 78 @test first(_ai_trades(s)).order isa egn.FOKOrder 79 @test Cash(:USDT, 999.547) ≈ s.cash atol = 1e-1 80 @test Cash(:USDT, 0.0) ≈ s.cash_committed atol = 1e-7 81 @test st.trades_count(s) == 2051 82 mmh = st.minmax_holdings(s) 83 reset!(s, true) 84 @test mmh.count == 1 85 @test mmh.min[1] == :BTC 86 @test mmh.min[2] ≈ 4.068469375875e6 atol = 1e2 87 @test mmh.max[1] == :BTC 88 @test mmh.max[2] ≈ 4.068469375875e6 atol = 1e2 89 end 90 91 function margin_overrides(ot=:market) 92 (; 93 ordertype=ot, 94 def_lev=10.0, 95 longdiff=1.02, 96 buydiff=1.01, 97 selldiff=1.012, 98 long_k=0.02, 99 short_k=0.02, 100 per_order_leverage=false, 101 verbose=false, 102 ) 103 end 104 105 test_margin_market(s) = begin 106 s[:per_order_leverage] = false 107 @test marginmode(s) isa egn.Isolated 108 s.attrs[:overrides] = margin_overrides(:market) 109 egn.start!(s) 110 @test first(_ai_trades(s)).order isa ect.AnyMarketOrder 111 @test Cash(:USDT, -0.056) ≈ s.cash atol = 1e-3 112 @test Cash(:USDT, 0.0) ≈ s.cash_committed atol = 1e-1 113 @test ect.tradescount(s) == st.trades_count(s) == 480 114 mmh = st.minmax_holdings(s) 115 @test mmh.count == 0 116 @test mmh.min[1] == :USDT 117 @test mmh.min[2] ≈ Inf atol = 1e-3 118 @test mmh.max[1] == :USDT 119 @test mmh.max[2] ≈ 0.0 atol = 1e-3 120 end 121 122 test_margin_gtc(s) = begin 123 @test marginmode(s) isa egn.Isolated 124 s.attrs[:overrides] = margin_overrides(:gtc) 125 egn.start!(s) 126 @test first(_ai_trades(s)).order isa ect.AnyGTCOrder 127 @test Cash(:USDT, -0.105) ≈ s.cash atol = 1e-3 128 @test Cash(:USDT, 0.0) ≈ s.cash_committed atol = 1e-1 129 @test st.trades_count(s) == 541 130 mmh = st.minmax_holdings(s) 131 reset!(s, true) 132 @test mmh.count == 0 133 @test mmh.min[1] == :USDT 134 @test mmh.min[2] ≈ Inf atol = 1e-3 135 @test mmh.max[1] == :USDT 136 @test mmh.max[2] ≈ 0.0 atol = 1e-3 137 end 138 139 test_margin_fok(s) = begin 140 @test marginmode(s) isa egn.Isolated 141 s.attrs[:overrides] = margin_overrides(:fok) 142 # increase cash to trigger order kills 143 s.config.initial_cash = 1e6 144 s.config.min_size = 1e3 145 egn.start!(s) 146 @test first(_ai_trades(s)).order isa ect.AnyFOKOrder 147 @test Cash(:USDT, -0.036) ≈ s.cash atol = 1e1 148 @test Cash(:USDT, 0.0) ≈ s.cash_committed atol = 1e1 149 @test st.trades_count(s) == 2352 150 mmh = st.minmax_holdings(s) 151 reset!(s, true) 152 @test mmh.count == 0 153 @test mmh.min[1] == :USDT 154 @test mmh.min[2] ≈ Inf atol = 1e-3 155 @test mmh.max[1] == :USDT 156 @test mmh.max[2] ≈ 0.0 atol = 1e-3 157 end 158 159 test_margin_ioc(s) = begin 160 @test marginmode(s) isa egn.Isolated 161 s.attrs[:overrides] = margin_overrides(:ioc) 162 # increase cash to trigger order kills 163 s.config.initial_cash = 1e6 164 s.config.min_size = 1e3 165 egn.start!(s) 166 @test first(_ai_trades(s)).order isa ect.AnyIOCOrder 167 @test Cash(:USDT, -0.048) ≈ s.cash atol = 1e1 168 @test Cash(:USDT, 0.0) ≈ s.cash_committed atol = 1e-1 169 @test st.trades_count(s) == 2354 170 mmh = st.minmax_holdings(s) 171 reset!(s, true) 172 @test mmh.count == 0 173 @test mmh.min[1] == :USDT 174 @test mmh.min[2] ≈ Inf atol = 1e-3 175 @test mmh.max[1] == :USDT 176 @test mmh.max[2] ≈ 0.0 atol = 1e-3 177 end 178 179 _nomargin_backtest_tests(s) = begin 180 @testset test_synth(s) 181 @testset test_nomargin_market(s) 182 @testset test_nomargin_gtc(s) 183 @testset test_nomargin_ioc(s) 184 @testset test_nomargin_fok(s) 185 end 186 187 _margin_backtest_tests(s) = begin 188 @testset test_margin_market(s) 189 @testset test_margin_gtc(s) 190 @testset test_margin_ioc(s) 191 @testset test_margin_fok(s) 192 end 193 194 test_backtest() = begin 195 @eval begin 196 using PlanarDev.Planar.Engine: Engine as egn 197 using .egn.Instruments: Cash 198 Planar.@environment! 199 using .Planar.Engine.Strategies: reset! 200 if isnothing(Base.find_package("BlackBoxOptim")) && @__MODULE__() == Main 201 import Pkg 202 Pkg.add("BlackBoxOptim") 203 end 204 end 205 # NOTE: Don't override exchange of these tests, since they rely on 206 # specific assets precision/limits 207 @testset failfast = FAILFAST "backtest" begin 208 s = backtest_strat(:Example) 209 @info "TEST: Example strat" exc = nameof(exchange(s)) 210 invokelatest(_nomargin_backtest_tests, s) 211 212 s = backtest_strat(:ExampleMargin) 213 @info "TEST: ExampleMargin strat" exc = nameof(exchange(s)) 214 invokelatest(_margin_backtest_tests, s) 215 end 216 end