新增实盘策略:ITrendStrategy(SA)

This commit is contained in:
2026-01-25 23:26:03 +08:00
parent 4a37652269
commit fa4749b02e
66 changed files with 44943 additions and 5961 deletions

File diff suppressed because one or more lines are too long

View File

@@ -38,6 +38,7 @@ class SpectralTrendStrategy(Strategy):
order_direction: Optional[List[str]] = None,
indicators: Indicator = None,
model_indicator: Indicator = None,
holding_indicators: Indicator = None,
reverse: bool = False,
):
super().__init__(context, main_symbol, enable_log)
@@ -62,6 +63,7 @@ class SpectralTrendStrategy(Strategy):
self.order_direction = order_direction
self.model_indicator = model_indicator or Empty()
self.indicators = indicators or Empty()
self.holding_indicators = holding_indicators or Empty()
self.reverse = reverse
# 计算窗口大小
@@ -208,6 +210,13 @@ class SpectralTrendStrategy(Strategy):
self.log(f'trend_strength: {trend_strength:.2f}')
avg_entry_price = self.get_average_position_price(self.symbol)
if not self.holding_indicators.is_condition_met(*self.get_indicator_tuple()):
direction = "CLOSE_LONG" if volume > 0 else "CLOSE_SHORT"
self.log(f"Exit (Signal): {direction} | Strength={trend_strength:.2f} < {self.exit_threshold}")
self.close_position(direction, abs(volume))
self.entry_time = None
self.position_direction = None
# 确保 ATR 和 均价 有效
if current_atr > 0 and avg_entry_price > 0:
is_stop_loss = False

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,399 @@
tree
version=v4
num_class=1
num_tree_per_iteration=1
label_index=0
max_feature_idx=74
objective=binary sigmoid:1
feature_names=feat_rsi_7 feat_rsi_14 feat_rsi_20 feat_rsi_35 feat_rsi_40 feat_hurst_23 feat_hurst_115 feat_hurst_230 feat_range_0 feat_range_1 feat_range_6 feat_range_13 feat_range_20 feat_stoch_k_14_3 feat_stoch_k_5_3 feat_stoch_k_21_5 feat_roc_5 feat_roc_10 feat_roc_15 feat_roc_23 feat_roc_230 feat_roc_ma_5_5 feat_roc_ma_5_10 feat_roc_ma_10_10 feat_roc_ma_10_20 feat_roc_ma_20_20 feat_roc_ma_20_40 feat_natr_5 feat_natr_14 feat_natr_21 feat_log_natr_5 feat_log_natr_14 feat_log_natr_21 feat_adx_7 feat_adx_14 feat_adx_30 feat_adx_60 feat_adx_120 feat_adx_240 feat_bbw_10_15 feat_bbw_23_15 feat_bbw_230_15 feat_bb_dev_sig_23_20 feat_bb_dev_sig_30_20 feat_bb_dev_sig_50_25 feat_bb_dev_sig_100_25 feat_bb_dev_sig_200_30 feat_bb_dev_abs_23_20 feat_bb_dev_abs_30_20 feat_bb_dev_abs_50_25 feat_bb_dev_abs_100_25 feat_bb_dev_abs_200_30 feat_kc_dev_sig_5_5_15 feat_kc_dev_sig_23_23_15 feat_kc_dev_sig_230_230_15 feat_kc_dev_abs_5_5_15 feat_kc_dev_abs_23_23_15 feat_kc_dev_abs_230_230_15 feat_atr_ratio_5_15 feat_atr_ratio_10_30 feat_atr_ratio_5_20 feat_atr_ratio_10_20 feat_atr_ratio_10_50 feat_atr_ratio_20_50 feat_atr_ratio_20_100 feat_atr_ratio_50_200 feat_z_atr_7_100 feat_z_atr_14_100 feat_fft_trend_46_2 feat_fft_trend_46_1 feat_vol_skew_20_60 feat_vol_trend_rel_20_3 pos_side pos_duration pos_pnl_pct
feature_infos=[4.1749763484354183:95.692684172271655] [10.966258982511313:92.838880797680986] [16.148879912048468:90.459183924550672] [21.654928846403447:85.138004569098669] [23.228070821680802:83.606602004087421] [0.24870137359576905:0.72617402795498243] [0.3723181248889465:0.65037413807453892] [0.4053255846824676:0.62961326094798253] [0:154] [0:154] [0:154] [0:154] [0:154] [4.5889218351173134e-13:100.00000000000024] [-1.8000415972589204e-13:99.999999999999844] [0.66985645933025584:100.00000000000014] [-8.0973451327433601:10.292072322670375] [-8.8105726872246706:10.215427380125085] [-10.89108910891089:11.926605504587151] [-11.651728553137009:12.084805653710241] [-32.099792099792104:30.563002680965145] [-6.0082638533665982:9.8737855671923302] [-4.0318488784273265:5.0001357657470287] [-6.9723012838997445:9.5959526329294427] [-5.0732526764135155:5.6245025971869707] [-9.1390495205231463:10.060435043999286] [-6.1229820202566234:6.2031481288535968] [0.19155607866967048:2.6047831162674902] [0.24224091681764223:1.832498585336056] [0.26317150770375652:1.6313365104087267] [0.02614409563292204:0.33951816619072289] [0.033020573219181927:0.23028506451876501] [0.035835485229702466:0.20259957075646215] [10.103194843261845:88.389362935634765] [8.3338624745195133:76.55187949216392] [6.6456814702223186:56.608551928653135] [5.8940965086979995:45.129224484962243] [4.8894285853614523:31.979883413529056] [3.7422258440600675:18.622886610055492] [0.14771643849036414:14.124561207876937] [0.32301782035527027:14.3834200236369] [2.2664155746197787:29.330156146664194] [-104.25272626958441:106.65490931305388] [-117.30577229720308:120.14751614715206] [-89.087915672281298:101.68225136191111] [-94.0298009514228:121.21603763166453] [-73.774569249612796:131.74949645954291] [0:106.65490931305388] [0:120.14751614715206] [0:101.68225136191111] [0:121.21603763166453] [0.0021963350533475237:131.74949645954291] [-63.081915156405479:77.925939486443923] [-205.02862945303647:339.76038153178325] [-508.67538007748345:919.30816009235696] [0.00079620631142760998:77.925939486443923] [0.0010958753686159464:339.76038153178325] [0.053401883260772338:919.30816009235696] [36.037217713863122:189.07832492816303] [65.071098755691651:177.84169624732908] [32.576662864801527:220.11606794935113] [71.206674891347987:146.45428033448994] [61.329451570735969:218.01879842896216] [80.246788153976468:148.86475009881894] [67.956832073759216:179.65994695265684] [73.878657186595987:158.85985305614176] [-2.8036321848802204:6.9129862835139217] [-3.3159021383063334:6.637129878802404] [8.8624726731213354e-09:0.92792774938128475] [0.0025257266758123491:0.96038875491251152] [-2.2989327103588426:3.8617176894327168] [-1:1] [-1:1] [0:515] [-0.037835459744830623:0.34425549564496061]
tree_sizes=3405 3430 3424 3448 3450 3436 3442 3443 3432 3426
Tree=0
num_leaves=31
num_cat=0
split_feature=8 6 40 8 58 0 65 20 73 15 19 58 50 61 6 26 35 54 58 16 34 20 27 62 53 15 62 8 57 35
split_gain=11.8541 11.851 11.9108 11.3315 10.5642 13.2269 10.0351 10.0759 9.66644 14.6341 14.0867 15.6224 10.0023 9.54126 9.42371 8.98244 11.2302 8.81379 8.66153 9.13362 9.79702 9.31566 10.2424 10.5123 12.5546 12.2835 11.1985 12.5627 8.57323 10.8982
threshold=9.5000000000000018 0.44890015619872498 1.0945580896275151 15.500000000000002 106.94086062844231 60.022771813097556 92.328849145332796 0.28427261887589156 57.500000000000007 7.0881883090713442 -1.6207371510454147 104.40603039569643 11.469113795454961 89.069341646999717 0.5348491753673783 0.57525621641785685 16.610715135619355 -280.16259482985544 87.278527221382078 0.66766723569113851 16.968280841956442 0.55651283191462408 0.62663934261445775 92.186092613280991 6.4020247565478519 64.990199273269738 98.162748085378823 14.500000000000002 42.822021170537759 11.104415448787751
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=8 4 3 6 5 -2 -3 -8 -1 -10 11 12 -11 -12 17 16 -9 -14 -4 -20 -21 22 -22 -24 26 -26 -25 -28 29 -15
right_child=1 2 18 -5 -6 -7 7 15 9 10 13 -13 14 28 -16 -17 -18 -19 19 20 21 -23 23 24 25 -27 27 -29 -30 -31
leaf_value=-0.10900939158306706 -0.088727940938449587 -0.11385270247948483 -0.093880512867820537 -0.077495165472128619 -0.071174296671953546 -0.12276546192523434 -0.073287437741809847 -0.08596191214494113 -0.14727555040104548 -0.13658023906614608 -0.084342773338984933 -0.056730016940817543 -0.10630560778751952 -0.11740388007115064 -0.10733736250490213 -0.084249608606102533 -0.13390641123242122 -0.067824666198935596 -0.10566472598122889 -0.11485039943236725 -0.1125157885626224 -0.10259271798771365 -0.052769566622839506 -0.14213357379772842 -0.065922916311119129 -0.092876984128712287 -0.065216696193627632 -0.11492223361297477 -0.10690363553752007 -0.089220521408526432
leaf_weight=499.65818417072296 43.383495032787323 26.92768657207489 81.780381441116333 30.667643040418625 19.447773635387421 13.463843286037443 18.201121479272842 7.2305825054645565 6.7319216430187216 4.9866086244583121 20.445095360279083 10.970538973808287 7.7292433679103842 15.9571475982666 19.697104066610336 15.707817167043684 11.219869405031202 17.453130185604099 804.83863198757172 33.410277783870697 17.453130185604095 149.34892830252647 7.2305825054645529 6.4825912117958095 23.437060534954071 43.383495032787323 7.2305825054645565 12.466521561145781 415.8831592798233 54.603364437818527
leaf_count=2004 174 108 328 123 78 54 73 29 27 20 82 44 31 64 79 63 45 70 3228 134 70 599 29 26 94 174 29 50 1668 219
internal_value=-0.103597 -0.101751 -0.102426 -0.0929805 -0.0902601 -0.0967895 -0.0989701 -0.0913162 -0.105958 -0.103304 -0.102782 -0.0891417 -0.0962723 -0.104419 -0.0917936 -0.100923 -0.115117 -0.0796356 -0.103301 -0.103999 -0.099535 -0.0976187 -0.0913064 -0.0876133 -0.0903223 -0.0834229 -0.107932 -0.0966759 -0.105263 -0.0955941
internal_weight=2447.43 1373.31 1297.02 109.955 76.2951 56.8473 79.2871 52.3594 1074.12 574.457 567.725 60.8366 49.8661 506.889 44.8795 34.1583 18.4505 25.1824 1187.06 1105.28 300.443 267.033 117.684 100.231 93.0003 66.8206 26.1797 19.6971 486.444 70.5605
internal_count=9816 5508 5202 441 306 228 318 210 4308 2304 2277 244 200 2033 180 137 74 101 4761 4433 1205 1071 472 402 373 268 105 79 1951 283
is_linear=0
shrinkage=1
Tree=1
num_leaves=31
num_cat=0
split_feature=8 33 49 23 7 60 74 49 19 25 74 37 27 13 27 32 23 25 27 32 2 54 68 52 34 2 8 53 37 18
split_gain=11.1539 9.42169 12.5268 12.4571 13.2347 12.5516 11.4116 11.1116 10.7164 10.3 10.0392 9.45297 9.29534 11.1816 9.15722 10.14 11.4262 9.78816 9.05519 8.88606 8.72077 11.2969 10.7219 9.8483 8.53609 8.48145 11.6686 8.39751 8.31146 10.144
threshold=9.5000000000000018 24.963534499034065 5.8747663587644352 -0.043180050441747504 0.53615945383601027 85.696136743009546 0.014116965031849874 4.8947540277980588 -0.91855983558886678 -0.17172238929329689 -0.00056308466402773562 8.682761460567276 0.37379182816348605 24.720243808726753 0.79584692424344083 0.11215216241668734 0.21631977580716669 -4.0181326704158211 1.1034289839021032 0.05764352725636062 53.15476576563497 139.16045117485385 0.10209017254480358 12.360185125180637 14.194104309901531 50.075202668851041 10.500000000000002 -28.081918885128662 12.587069933782873 0.74937429138888245
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=12 3 7 5 28 -2 8 25 -7 -6 -10 -11 13 27 17 16 -16 -14 24 -4 21 23 -22 -15 -8 -3 -27 -1 29 -5
right_child=1 2 19 4 9 6 18 -9 10 11 -12 -13 14 20 15 -17 -18 -19 -20 -21 22 -23 -24 -25 -26 26 -28 -29 -30 -31
leaf_value=0.0061407230076742265 0.020882398901871674 -0.013295774819014124 0.018008810991961315 0.022835398918446034 0.044087640432325104 0.044720360023100744 -0.035511663394249014 -0.028860204725034554 0.03194948474359588 0.045898485185781192 -0.011535753260326109 0.0061809784621390381 -0.039370203713278387 0.0068442417500025506 0.050304916589966144 -0.0031451522940833016 -0.0013838558574138251 -0.0036895240934555693 0.02715418533622041 0.0012026432909032826 0.013808961290249529 0.050945028207973052 -0.010039198248375776 0.051120876237921012 -0.0088514350536512045 -0.036085234299957046 0.0093769078099966395 -0.044124495846953583 -0.0045643208581227137 -0.0037957736775348708
leaf_weight=5.2342454493045807 20.705438837409019 49.366370841860771 29.171294718980789 37.152251020073891 17.459924772381783 6.4829368442297008 13.960633367300032 20.196428894996643 6.733015760779387 6.9829739034175899 16.456846296787258 23.689346924424171 6.973759949207305 48.36820025742054 9.4776339977979642 18.94561006128788 6.4810961186885825 889.97747120261192 4.9866906255483618 964.45434604585171 29.665974944829941 7.2292121797800055 39.636731177568436 4.9874031394720069 47.874360650777817 5.733849585056304 44.637215510010719 6.9805562645196897 37.654529720544815 19.697664931416512
leaf_count=21 83 198 117 149 70 26 56 81 27 28 66 95 28 194 38 76 26 3570 20 3868 119 29 159 20 192 23 179 28 151 79
internal_value=-8.84701e-08 0.00179086 0.000590583 0.00693478 0.012889 -0.000311773 -0.00485954 -0.00856793 0.0106223 0.0256938 0.00108988 0.0152232 -0.00229038 0.00485678 -0.00338028 0.0116953 0.0293133 -0.00396694 -0.0117344 0.00169605 0.00743745 0.0157514 0.000169334 0.010983 -0.0148706 -0.00445883 0.00420185 -0.022585 0.00636744 0.0136081
internal_weight=2447.35 1373.4 1113.56 259.837 142.637 117.2 96.4945 119.934 29.6728 48.1322 23.1899 30.6723 1073.96 142.102 931.856 34.9043 15.9587 896.951 66.8217 993.626 129.888 60.5848 69.3027 53.3556 61.835 99.7374 50.3711 12.2148 94.5044 56.8499
internal_count=9816 5508 4466 1042 572 470 387 481 119 193 93 123 4308 570 3738 140 64 3598 268 3985 521 243 278 214 248 400 202 49 379 228
is_linear=0
shrinkage=0.03
Tree=2
num_leaves=31
num_cat=0
split_feature=8 6 40 8 58 0 65 1 22 73 73 15 19 58 69 50 61 69 14 0 57 35 70 65 35 20 1 42 37 69
split_gain=10.4953 11.1224 10.8313 10.6208 9.91968 12.4485 9.56207 9.74769 11.1624 10.1078 8.99914 13.6031 13.1427 14.4503 13.3165 10.1801 8.97629 8.47801 8.21866 8.8777 8.08265 10.1994 10.8535 10.1221 8.08188 9.32161 8.02115 11.7275 14.4175 11.4232
threshold=9.5000000000000018 0.44890015619872498 1.0945580896275151 15.500000000000002 106.94086062844231 60.022771813097556 92.328849145332796 47.358798351530176 0.12636262832362091 56.500000000000007 57.500000000000007 7.0881883090713442 -1.6207371510454147 104.40603039569643 0.73596182611612571 15.572053958397168 89.069341646999717 0.26160331850235236 46.49690723220106 58.91153323441317 42.822021170537759 11.104415448787751 -0.1990856123026363 94.111291781800432 16.610715135619355 3.1064027847885538 74.812671577494697 23.820727233874944 15.23859930148044 0.69845875231335897
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=10 4 3 6 5 -2 18 -8 24 -10 -1 -12 13 14 -13 -16 -14 -5 -3 -20 21 23 -23 -18 -9 -26 27 -22 29 -29
right_child=1 2 -4 17 -6 -7 7 8 9 -11 11 12 16 -15 15 -17 20 -19 19 -21 26 22 -24 -25 25 -27 -28 28 -30 -31
leaf_value=-0.0051673130632829646 0.01434884442882985 -0.028096251214216668 0.00024741037954944644 -0.0087092934299948174 0.031343126173214879 -0.018681141685662032 0.031077569549142727 0.020700113727035514 0.057024463116463357 8.1314287364919066e-05 -0.042138497289113334 0.028264720799851421 0.018723932126309399 0.045149259329413438 -0.040534449539337415 0.003557053707045735 0.014692595001654816 0.032422270911858439 0.033120029770375745 -0.014605080004208272 -0.0056085598235659862 -0.021671873486772984 0.019000726854325228 -0.033712190931735275 0.0061892444675824476 -0.036847579160283625 -0.030820949262331383 0.0083237781192268406 0.023335173384394271 -0.015122681515708017
leaf_weight=499.42783744633198 43.417028278112411 12.464026719331743 1187.1008097529411 5.4924017488956478 19.476357772946358 13.450859934091566 16.220855206251144 6.4904090464115169 5.4906335920095426 5.7368736416101456 6.7060062587261191 17.467202305793762 20.460950285196304 10.992004349827765 5.7239339351654044 26.681163266301155 6.7264371812343589 25.217614501714706 5.9843349158763877 8.4769643843173963 297.61500142514706 6.7350739091634741 47.900536507368088 9.2142005711793882 7.985695734620097 10.465775936841963 9.2215356677770597 47.360331520438194 30.658342361450195 30.905383199453354
leaf_count=2004 174 50 4761 22 78 54 65 26 22 23 27 70 82 44 23 107 27 101 24 34 1194 27 192 37 32 42 37 190 123 124
internal_value=-1.68171e-07 0.00173695 0.001082 0.0100867 0.0128648 0.00653632 0.00428684 0.0117543 0.00308841 0.0279285 -0.00222208 0.000338877 0.000840679 0.0140127 0.00715017 -0.00423113 -0.000741232 0.025066 -0.0102431 0.00514439 -0.00156016 0.00782673 0.0139869 -0.0132869 -0.00809325 -0.0182215 -0.00315361 -0.00252603 0.00589642 -0.000934705
internal_weight=2447.27 1373.47 1297.13 110.026 76.3442 56.8679 79.3156 52.3902 36.1694 11.2275 1073.8 574.368 567.662 60.8643 49.8723 32.4051 506.798 30.71 26.9253 14.4613 486.337 70.5762 54.6356 15.9406 24.9419 18.4515 415.761 406.539 108.924 78.2657
internal_count=9816 5508 5202 441 306 228 318 210 145 45 4308 2304 2277 244 200 130 2033 123 108 58 1951 283 219 64 100 74 1668 1631 437 314
is_linear=0
shrinkage=0.03
Tree=3
num_leaves=31
num_cat=0
split_feature=68 58 8 39 56 67 26 6 51 30 66 43 51 73 33 27 7 27 37 42 71 32 21 26 74 39 23 66 66 60
split_gain=10.0618 11.9994 10.6377 11.6936 9.87286 9.44169 10.3847 8.77729 11.0016 15.7442 10.0352 9.12798 8.66364 8.61024 8.47591 8.90721 8.33438 8.10186 8.65138 14.1512 9.33663 10.7372 9.18502 8.72277 8.69525 8.15928 8.66033 7.96301 7.80609 7.72673
threshold=0.001581363479764652 87.416108523401917 9.5000000000000018 3.1525045913828036 113.55964385702097 2.9955194997850447 1.2265492883036118 0.44890015619872498 24.431387605477948 0.077856338538928163 -0.14782096075099135 68.677074911130205 37.580108771525843 116.50000000000001 39.046971721305034 0.69179328700357012 0.59708932823621996 0.37379182816348605 13.097580753343411 22.933353103936245 0.19617465847343452 0.085951030012503205 0.21659266525962997 -0.32337744310428423 0.038221116347537602 1.8117628726484212 -0.91438487393805234 0.34647834795395921 -1.8005767318444026 75.990673909797508
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 -1 3 12 -5 7 -7 8 9 -4 -10 28 16 -13 -14 -16 17 -2 -19 20 22 -22 25 24 -24 26 -20 -12 -9 -28
right_child=2 -3 5 4 -6 6 -8 11 10 -11 27 13 14 -15 15 -17 -18 18 19 -21 21 -23 23 -25 -26 -27 29 -29 -30 -31
leaf_value=0.033283279468946644 0.0041529718104186854 0.004691280395606099 -0.010307774107587056 0.036468221502919021 -0.0066925670515351155 -0.025397017332258406 0.014394565441545014 0.024914001441649118 -0.019573056683411857 0.034374499941326297 0.045283354599672303 0.032007314514565378 -0.044495733576802997 -0.0027865447245352969 -0.015591769231245136 0.027381782772347041 0.014202605249656776 -0.0062073842661570598 0.027567058282773799 0.012731417929272799 -0.039837354433332521 0.0077761570813594926 -0.019755942928748511 -0.027682651806474261 0.020375180396022836 -0.024822424015852072 0.034076644204872136 0.0013393403072372817 0.00057530470253146075 -0.00020748215204848588
leaf_weight=16.213799729943275 124.89783628284931 71.31194069981575 9.4824699610471708 12.464807361364366 7.7265841066837302 22.690755680203441 7.978157937526702 11.979563117027281 14.220854282379149 28.215818837285042 5.2401745021343258 16.461377471685413 7.710496947169303 10.473882317543028 33.625717803835869 4.9843791574239722 26.167844966053963 548.45571558177471 12.71319144964218 63.580344244837761 12.457510590553285 6.4798020869493476 9.7184041142463702 20.677813604474068 9.7182658910751325 9.4575343877077085 6.2353944927453986 12.719824045896528 1187.4150460511446 115.61966402828693
leaf_count=65 501 286 38 50 31 91 32 48 57 113 21 66 31 42 135 20 105 2201 51 255 50 26 39 83 39 38 25 51 4762 464
internal_value=-2.41787e-07 0.00998784 -0.000370739 -0.00265401 0.019952 0.0014063 -0.0150457 0.00179557 0.0121373 0.0231353 -0.000746305 0.00120627 -0.00310483 0.0184776 -0.0157789 -0.0100441 -0.00249721 -0.00296209 -0.00405231 0.000380183 -0.00348679 -0.0235454 -0.00142393 -0.0141196 0.000309476 0.00211211 0.00400509 0.0141609 0.0008184 0.00154686
internal_weight=2447.09 87.5257 2359.57 1032.69 20.1914 1326.88 30.6689 1296.21 69.8791 37.6983 32.1809 1226.33 1012.5 26.9353 46.3206 38.6101 966.179 940.011 815.114 266.658 203.078 18.9373 184.14 40.1145 19.4367 144.026 134.568 17.96 1199.39 121.855
internal_count=9816 351 9465 4144 81 5321 123 5198 280 151 129 4918 4063 108 186 155 3877 3772 3271 1070 815 76 739 161 78 578 540 72 4810 489
is_linear=0
shrinkage=0.03
Tree=4
num_leaves=31
num_cat=0
split_feature=68 58 8 39 56 67 25 15 19 71 21 40 58 32 6 39 47 20 23 30 40 56 38 30 6 51 30 59 21 43
split_gain=9.46323 11.3115 10.0109 10.997 9.2717 8.89038 10.6715 8.59387 8.81486 10.256 10.2144 10.0809 14.6987 10.5514 9.08851 9.04346 12.1894 10.0511 9.97437 8.90229 10.9804 8.70886 9.17245 8.34626 8.25622 10.3427 14.796 8.60102 8.85342 8.5813
threshold=0.001581363479764652 84.132627915218293 9.5000000000000018 3.1525045913828036 113.55964385702097 2.9955194997850447 1.0923210893195126 8.2312185197735364 3.3009411629820411 -0.089096886679836132 -1.3843151358285704 3.007578898386106 75.825655364128323 0.056047442273747979 0.41999315508139684 0.73919029312892726 26.791589611264268 -5.4284762512839029 -0.28882880358511215 0.093121977344489321 3.2572854535972975 46.678964918510317 7.7741136264582851 0.054093040307849539 0.44890015619872498 24.431387605477948 0.077856338538928163 98.402293750850347 -0.17562404005891713 68.677074911130205
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 -1 3 7 -5 24 -7 -2 9 11 -11 12 -9 21 -12 16 17 -16 -19 20 -13 22 -14 -18 25 26 -4 28 -27 -26
right_child=2 -3 5 4 -6 6 -8 8 -10 10 14 19 13 -15 15 -17 23 18 -20 -21 -22 -23 -24 -25 29 27 -28 -29 -30 -31
leaf_value=0.040160984735970705 -0.025078952481515857 0.0058691845255318164 -0.010002893103654363 0.03532573314448114 -0.0064940724489905583 -0.026005500842824453 0.012892231377100155 0.036470792333117057 -0.021374464713773821 0.030745202885279453 -0.045603987551781418 0.024873890321319703 0.021069214570156099 -0.0031509561663804943 0.016619328160642256 -0.0099248210061143056 -0.0026709363141228172 0.015866807351669492 -0.016187677443868943 0.03480556462636325 -0.0095892824131627568 -0.018803420294879778 -0.0039495031592717677 0.037013273537114262 0.00079381454229745412 0.0053985780147224865 0.03331553071061201 0.01101329328103191 -0.046770503906876711 0.017910021916220552
leaf_weight=9.7409740537404996 15.638819754123686 77.821340829133987 9.4784059524536115 12.481618762016298 7.7239098995923987 21.663136675953869 8.9786756336688978 9.2330893576145154 22.162835851311684 6.4798372387886038 5.4794514775276175 14.699056968092917 42.144037336111069 519.23774285614491 15.945793882012365 223.2033955603838 7.7280056774616268 10.714204952120779 47.337678611278534 12.714141994714735 19.174124136567116 8.7186270058155042 19.195664569735527 12.460524171590803 1199.4385347515345 5.7375911921262741 28.238908514380455 20.459097191691399 5.978168770670889 26.954566776752472
leaf_count=39 63 312 38 50 31 87 36 37 89 26 22 59 169 2084 64 896 31 43 190 51 77 35 77 50 4810 23 113 82 24 108
internal_value=-3.12353e-07 0.00968402 -0.000359718 -0.00257506 0.0193394 0.00136402 -0.0146077 -0.00301249 -0.00266623 -0.00224074 -0.00654816 -4.16814e-05 -0.00108795 -0.00167642 -0.00729662 -0.00663528 0.00116031 -0.00447688 -0.0102716 0.0134002 0.0053658 0.00925213 0.0132398 0.0218225 0.00174157 0.0117707 0.0224296 -0.000724326 -0.0212216 0.00117001
internal_weight=2446.96 87.5623 2359.4 1032.47 20.2055 1326.93 30.6418 1012.27 996.628 974.465 329.349 645.116 598.529 589.296 322.869 317.39 94.1862 73.9977 58.0519 46.5873 33.8732 70.0583 61.3397 20.1885 1296.29 69.8922 37.7173 32.1749 11.7158 1226.39
internal_count=9816 351 9465 4144 81 5321 123 4063 4000 3911 1322 2589 2402 2365 1296 1274 378 297 233 187 136 281 246 81 5198 280 151 129 47 4918
is_linear=0
shrinkage=0.03
Tree=5
num_leaves=31
num_cat=0
split_feature=68 58 9 39 56 3 15 6 33 0 40 9 65 20 64 1 45 14 0 22 71 29 21 29 5 39 47 20 65 15
split_gain=8.9013 10.7661 9.42136 10.3454 8.71495 8.23841 8.35905 7.93252 9.42811 9.48571 8.76917 12.5701 9.93015 9.36991 9.72487 8.09737 8.56984 7.90426 9.96812 7.86502 7.83248 11.4994 9.42405 8.97814 9.30173 8.84328 12.4882 9.75167 8.51295 10.7521
threshold=0.001581363479764652 87.416108523401917 9.5000000000000018 3.1525045913828036 113.55964385702097 68.712995139349573 8.2312185197735364 0.44890015619872498 19.456395301819065 62.65529220840066 1.0945580896275151 15.500000000000002 92.328849145332796 0.28427261887589156 102.092091413185 47.358798351530176 20.798764858559295 46.49690723220106 58.91153323441317 0.13804739988049272 -0.089096886679836132 0.41000891748779061 -1.3843151358285704 0.37753275650528417 0.49837029264811061 0.73919029312892726 26.791589611264268 -5.4284762512839029 100.46757951945671 79.502207599847409
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 -1 3 5 -5 6 -2 8 -4 -10 11 12 17 -14 15 -15 -17 -9 -19 -13 21 23 -22 -8 -25 26 27 -24 -23 -30
right_child=2 -3 7 4 -6 -7 20 10 9 -11 -12 19 13 14 -16 16 -18 18 -20 -21 22 28 25 24 -26 -27 -28 -29 29 -31
leaf_value=0.031434598565742856 -0.024378084457291081 0.0043721979693970526 -0.024126658083728412 0.034242481993383984 -0.0063015361740495013 -0.025241641331292704 -0.005595777124920185 -0.030624874806346687 0.020778068234022236 -0.0066512178005946323 1.889539054590332e-05 0.035500817895771877 0.027854237363059673 0.019779241247822731 0.020345818935970723 -0.045128695450465627 0.0023744944785691661 0.032038168110324006 -0.019168010059852029 -0.00075120775864844305 0.029784111521907731 -0.0044025404761120013 0.016106289620044362 0.026254981742400301 -0.0024726413156118446 -0.0099592753748634633 0.021691222135058776 -0.010290539180229333 0.0064503294912431237 -0.011299036206333444
leaf_weight=16.242090299725533 15.606271773576735 71.347620502114296 6.2326295524835578 12.490026667714121 7.7211512923240653 14.669241949915884 20.438409194350243 11.69739453494549 49.696067005395889 14.704529166221617 1152.0142739713192 20.988227337598804 17.990526482462883 4.9947103559970882 13.467537015676497 7.9612632691860181 5.9893864691257477 5.9929995238780958 7.9737256765365601 7.245539814233779 6.4881282448768607 376.67529977858067 15.959884554147719 35.425663530826569 14.214062958955763 227.29140834510326 20.456450417637825 59.751769363880157 167.423818603158 37.618072479963303
leaf_count=65 63 286 25 50 31 59 82 47 199 59 4620 84 72 20 54 32 24 24 32 29 26 1512 64 142 57 913 82 240 672 151
internal_value=-3.78268e-07 0.00939048 -0.000349035 -0.00249853 0.0187537 -0.00292296 -0.00259469 0.00132305 0.0111054 0.0145152 0.000773062 0.00910287 0.00275788 0.0104924 0.000855832 -0.0129989 -0.0247343 -0.0124324 0.00280411 0.0261976 -0.00224841 -0.000341905 -0.00601463 0.0111388 0.018029 -0.0067327 0.000893248 -0.00472613 -0.00172496 0.00319394
internal_weight=2446.77 87.5897 2359.18 1032.23 20.2112 1012.02 997.349 1326.95 70.6332 64.4006 1256.32 104.301 76.0675 50.4034 32.4129 18.9454 13.9506 25.6641 13.9667 28.2338 981.743 651.795 329.948 70.0781 49.6397 323.46 96.1681 75.7117 581.717 205.042
internal_count=9816 351 9465 4144 81 4063 4004 5321 283 258 5038 418 305 202 130 76 56 103 56 113 3941 2616 1325 281 199 1299 386 304 2335 823
is_linear=0
shrinkage=0.03
Tree=6
num_leaves=31
num_cat=0
split_feature=68 48 5 39 8 39 67 25 56 25 69 54 17 22 24 39 47 37 19 61 52 55 68 28 28 39 51 45 52 48
split_gain=8.37367 10.4713 10.5646 10.0558 8.86652 9.73537 8.34909 10.0771 8.31758 8.11726 12.5351 8.38789 8.17106 9.58618 13.4212 9.87334 8.52398 7.85474 7.7164 7.59403 9.24199 10.6692 12.782 10.3449 10.9787 12.9263 10.073 11.1946 8.7659 9.25954
threshold=0.001581363479764652 11.315720249873509 0.50175159979585005 1.0033057089604585 9.5000000000000018 3.1525045913828036 2.9955194997850447 1.0923210893195126 87.664254572268746 1.8854598641635449 0.92858221877435632 -404.65528370113981 -1.7158619529386629 0.40543002408107692 0.70602177007048328 0.82139940175235959 4.8427013095625639 19.093071492028187 3.3009411629820411 112.54366883359353 -27.043360071124138 7.0345197017783185 0.0042229782053788628 0.56911231016376129 0.64372727473083347 0.93649101518765765 25.738193881907339 -6.5631467868953139 -2.8108708335612351 21.132150043017479
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 -1 3 -3 5 18 9 -8 -7 11 12 -6 -11 14 -14 -16 -17 -15 -2 20 -13 22 -22 28 25 -25 27 -27 -24 -30
right_child=4 2 -4 -5 6 8 7 -9 -10 10 -12 19 13 17 15 16 -18 -19 -20 -21 21 -23 23 24 -26 26 -28 -29 29 -31
leaf_value=0.026255710964659281 -0.002444516657835715 0.028140269609636621 -0.010279299358753494 -0.0046241340184729604 -0.02904875232415885 0.041374598783719081 -0.025297891584237044 0.012503190955128148 0.0022139781088937856 -0.023027528383225842 -0.041590356897875186 -0.0067789201122794704 -0.017316512779808935 -0.012818445162168412 -0.0083093826180558328 -0.011568858247170034 0.023526566008477363 0.013073910921861727 -0.020365282382112576 0.017605965780496856 -0.043150119706824128 0.0053182818918618468 -0.0305357701389069 0.036883943443750629 -0.00069760772468183336 -0.036756315142169017 0.03919814268744274 0.012344046899288362 0.011029220228348484 -0.019336240887884402
leaf_weight=23.46327069401741 989.65095825493336 19.726564139127731 29.695309743285179 14.722319319844244 7.4802130609750739 8.2460300028324145 21.624876409769062 8.9838197380304319 11.963048323988913 15.95137897133827 7.7296670377254477 90.271729186177254 17.209315687417984 61.590009182691574 15.960310563445089 7.2317931354045859 44.895831152796745 12.72287632524967 22.107458934187889 30.428583353757858 6.4773970395326605 684.59854075312614 16.706107258796692 16.469222113490105 202.49806348979473 7.4754133373498961 4.9866860508918753 9.4774297475814819 17.951003536581993 18.202732488512993
leaf_count=94 3974 79 119 59 30 33 87 36 48 64 31 362 69 247 64 29 180 51 89 122 26 2745 67 66 812 30 20 38 72 73
internal_value=-4.29739e-07 0.00910688 0.00283402 0.0141378 -0.000338669 -0.00242428 0.00128334 -0.0142031 0.0181929 0.00164901 -0.00420094 0.00261237 -0.00255475 -0.000508708 0.00635373 0.0123364 0.0186577 -0.0083855 -0.00283609 0.0028266 0.0024083 0.00325041 -0.00146463 -0.000545484 0.00209158 0.0167967 0.0017179 -0.00930692 -0.0125638 -0.00425922
internal_weight=2446.5 87.6075 64.1442 34.4489 2358.89 1031.97 1326.92 30.6087 20.2091 1296.31 183.291 1113.02 175.562 159.61 85.2973 68.0879 52.1276 74.3129 1011.76 1105.54 1075.11 984.843 300.244 293.767 240.907 38.4088 21.9395 16.9528 52.8598 36.1537
internal_count=9816 351 257 138 9465 4144 5321 123 81 5198 735 4463 704 640 342 273 209 298 4063 4433 4311 3949 1204 1178 966 154 88 68 212 145
is_linear=0
shrinkage=0.03
Tree=7
num_leaves=31
num_cat=0
split_feature=41 50 4 22 53 2 1 6 5 54 49 37 5 68 63 16 67 74 67 22 33 1 15 31 64 24 55 24 55 24
split_gain=8.09436 11.0889 11.07 13.8091 9.3984 8.92296 8.52129 9.5813 8.34011 12.7438 12.628 12.1517 12.5328 11.7849 10.0934 10.3869 9.9113 11.0352 9.36751 9.129 12.0765 12.6248 8.99135 9.47463 13.0278 8.8744 15.3022 14.0434 8.64961 8.63664
threshold=15.961316021127724 3.7481776231255988 36.008827376268805 0.091906410013551612 18.774555057501924 46.460899090499574 70.39496992019518 0.55087143432158803 0.5157104888206101 -313.93485518500023 6.8725516935166064 6.6128035800233524 0.47446294984700921 0.31618675061798202 99.630107964021519 -0.58728528648157574 -0.64801453262224384 -0.00056308466402773562 -0.6120171990127462 0.31761569878613144 29.254060751078281 57.945336895601379 35.257471364614339 0.08687080950392409 91.52913454878265 0.21326167423973835 35.076204706962876 0.1889542517765079 0.47689566967823982 -0.23966332663400089
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=8 -2 -3 4 -4 -5 7 -7 11 14 19 -1 25 16 -10 -16 17 -12 -18 22 -21 -22 23 24 -11 27 -27 29 -15 -13
right_child=1 2 3 5 -6 6 -8 -9 9 10 13 12 -14 28 15 -17 18 -19 -20 20 21 -23 -24 -25 -26 26 -28 -29 -30 -31
leaf_value=0.012996027445263719 -0.030962915168748748 -0.022305126247827392 0.015311870774001504 -0.031596988271506497 0.052956832002935529 0.013519044340841009 -0.020224101848601413 -0.017030141626200805 -0.049346910502009705 -0.027203793610221436 -0.02832887275103721 -0.0043010026992580412 0.0037916019504846696 -0.04113093870058461 -0.030860940948479996 0.017177196794424088 0.046100177105886729 -0.00065248053354920762 0.0055553319036420962 0.023588756316016105 -0.024473928988543378 0.028923803752864505 -0.021694838965874517 -0.023969942696139519 0.024344124939122365 -0.0082130648669851859 0.036845213255155329 0.03324209796682713 -0.0039659791798130044 0.0035852406496060525
leaf_weight=71.801050171256065 6.9755945801734915 10.455816626548765 74.337414234876633 6.977225959300994 6.4898244589567176 52.372252315282822 11.223435431718825 11.219297155737875 8.9418485164642352 6.2189775258302715 14.700069785118101 272.9197410941124 544.00354418158531 5.7304882258176795 9.2157110720872897 7.2282870560884467 5.2337263822555533 109.91362611949444 255.2301999181509 14.46261529624462 16.692885175347332 5.2345083206892005 65.770172670483589 11.95387354493141 15.19106078147888 235.20799872279167 6.9848603308200827 11.222186252474783 341.83530473709106 230.56967747211456
leaf_count=288 28 42 298 28 26 210 45 45 36 25 59 1095 2183 23 37 29 21 441 1024 58 67 21 264 48 61 944 28 45 1372 925
internal_value=-4.8928e-07 0.0061218 0.00761646 0.0095403 0.0183345 0.000849877 0.00387586 0.00812933 -0.000486895 -0.00274259 -0.00212996 0.000981433 0.000318311 -0.000573814 -0.0236942 -0.00974482 0.00304097 -0.00391733 0.00637003 -0.0105425 0.00230878 -0.0117268 -0.0152599 -0.00257465 0.00937099 -0.00217802 -0.00691358 5.02578e-05 -0.00457874 -0.000689549
internal_weight=2446.31 180.051 173.075 162.619 80.8272 81.7922 74.815 63.5915 2266.26 893.553 868.168 1372.71 1300.91 732.643 25.3858 16.444 385.078 124.614 260.464 135.524 36.39 21.9274 99.1341 33.3639 21.41 756.904 242.193 514.712 347.566 503.489
internal_count=9816 722 694 652 324 328 300 255 9094 3586 3484 5508 5220 2940 102 66 1545 500 1045 544 146 88 398 134 86 3037 972 2065 1395 2020
is_linear=0
shrinkage=0.03
Tree=8
num_leaves=31
num_cat=0
split_feature=73 48 63 64 63 74 29 32 1 68 52 3 5 13 58 14 50 56 49 61 53 14 68 44 8 16 30 21 41 35
split_gain=7.87364 8.2757 8.77728 11.1026 10.9073 9.2569 8.21131 7.94681 7.66835 7.4714 8.51582 12.713 8.77946 7.18627 7.50437 9.22247 7.14386 8.66867 6.83599 6.5806 6.53354 8.31044 7.25527 6.80862 7.50321 14.7737 8.77677 6.21779 6.18798 5.98258
threshold=18.500000000000004 1.1708863323260765 98.131072714614902 98.021270745894881 98.771252078414676 0.027995823792242393 0.59457222114332675 0.14563390880953023 22.391583291323091 0.0028038151892468923 30.166796129847274 61.314471191354066 0.54944378049574449 27.198322618800976 83.862407564547411 31.592023172905105 24.977956731981337 55.33440710261096 4.3551200901605229 122.63586073331932 -89.402106833137211 20.723676967794045 0.0032781211234971679 -43.060525249380625 7.5000000000000009 -2.2558199348711443 0.11480287207393002 0.15181477661381235 13.75587037846398 16.82484254715051
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 -1 3 6 16 7 18 -6 -2 10 11 12 13 -10 -15 -16 -4 -18 -3 20 23 27 -23 24 -11 28 -27 -22 -26 -14
right_child=8 2 4 -5 5 -7 -8 -9 9 19 -12 -13 29 14 15 -17 17 -19 -20 -21 21 22 -24 -25 25 26 -28 -29 -30 -31
leaf_value=0.021747106414379363 -0.020060292715018042 -0.045399173699633671 0.032636393601174277 -0.034290522100323531 -0.0023686566479082655 -0.033035664050564434 0.00074216370631535809 0.034988007067516383 0.035178541113330949 0.03991902502712829 0.045443105428794817 -0.039593056098063781 0.0058197579435792495 0.038375787205338574 -0.028436638780999002 0.010570897288633134 -0.023362154550000645 0.029012688330017442 -0.010849221606811784 -0.030876391652917654 -0.0049675894728680673 -0.0300480770191991 0.0011819316060260724 0.037083668870111108 0.037750426093113729 -0.039464455049044049 0.0031065935261055163 -0.038988916393915746 -0.0022933828526632929 -0.03121236696821799
leaf_weight=11.457908466458319 15.671964362263678 5.4751130342483512 13.707988560199739 14.694379568099974 252.33387386798859 8.7216631025075895 69.533096477389336 5.2313148677349082 12.229346126317976 5.2273382991552344 5.4933698624372473 4.9855568856000891 8.9788992851972562 7.2434636354446402 6.2333811670541754 43.682775661349297 6.2309661805629712 5.2325191646814346 87.894350543618202 5.9635397642850867 161.16096125543118 6.7226380407810202 1629.7257421016693 7.9740230441093436 9.4712206125259346 8.4737279415130669 8.9751405268907529 4.9843009710311881 5.484199032187461 6.9770927131175986
leaf_count=46 63 22 55 59 1013 35 279 21 49 21 22 20 36 29 25 175 25 21 353 24 647 27 6539 32 38 34 36 20 22 28
internal_value=-5.67833e-07 -0.00344301 -0.00405835 -0.00931561 -0.000854891 -0.0026392 -0.00706279 -0.00160992 0.000840951 0.00100893 0.00917746 0.006972 0.00969216 0.0143062 0.00984057 0.00569975 0.0180212 0.000544394 -0.0128752 0.000586781 0.000688303 0.000404588 0.00105364 0.0119023 0.00656646 0.00118615 -0.0175673 -0.00598822 0.0230662 -0.0103733
internal_weight=2446.17 480.513 469.055 177.597 291.458 266.287 162.903 257.565 1965.66 1949.99 95.8239 90.3305 85.345 69.389 57.1596 49.9162 25.1715 11.4635 93.3695 1854.16 1848.2 1802.59 1636.45 45.6056 37.6316 32.4043 17.4489 166.145 14.9554 15.956
internal_count=9816 1929 1883 713 1170 1069 654 1034 7887 7824 384 362 342 278 229 200 101 46 375 7440 7416 7233 6566 183 151 130 70 667 60 64
is_linear=0
shrinkage=0.03
Tree=9
num_leaves=31
num_cat=0
split_feature=8 39 6 40 30 13 55 71 0 26 58 0 5 15 25 8 56 31 61 34 15 52 65 30 7 54 34 58 16 34
split_gain=7.68754 8.77779 8.58388 9.28536 9.40266 10.4117 13.7845 12.6296 11.2816 9.06513 8.95852 10.9407 9.08715 8.41762 8.32177 8.00175 7.8617 7.83863 8.3717 8.89088 8.81543 8.08143 10.688 7.87556 7.45826 9.53823 7.42828 7.3883 7.95488 9.26702
threshold=9.5000000000000018 3.1525045913828036 0.44890015619872498 1.0945580896275151 0.063862747691025942 49.516451679658111 6.9793139056555793 1.0000000180025095e-35 54.426351385151726 0.52532369160063153 106.94086062844231 60.022771813097556 0.54872512805177143 8.2312185197735364 0.86971356589714921 19.500000000000004 113.55964385702097 0.052673740856233768 94.459743550825763 18.359084104787659 57.811181349761917 1.9274650322031956 89.705416658989009 0.045293438877995369 0.59708932823621996 -300.75601562224051 20.250131820808473 87.278527221382078 0.66766723569113851 16.968280841956442
decision_type=2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
left_child=1 13 10 4 9 6 -6 -8 -7 -4 11 12 15 -1 -11 -2 -3 18 20 -20 23 -21 -23 -15 -19 -26 -10 -5 -29 -30
right_child=2 16 3 27 5 8 7 -9 26 14 -12 -13 -14 17 -16 -17 -18 24 19 21 -22 22 -24 -25 25 -27 -28 28 29 -31
leaf_value=-0.024013095387934467 0.025557174568448967 0.032402465021033791 -0.014218714526596593 0.0088340293570560394 -0.026503578565767537 0.046778675463359724 0.0014552571258563546 0.046105909153747029 0.026984900329587048 0.045025587881750734 0.028813369546585983 -0.018417224790606791 -0.018805520155427149 -0.0089951844862938956 -0.0032751669200620889 -0.0096604207848021723 -0.0061157947759325959 -0.0032064816266536068 0.027732734331395795 -0.022986201614523763 0.031637622096960541 0.021497393695582366 -0.019799546086292214 0.032013278008686814 -0.018075500929056479 0.022310881824201965 -0.0089776984732041001 -0.0020620881524482061 -0.011239559953096959 0.005520660972572475
leaf_weight=15.798326835036276 29.229165479540825 12.481599390506746 23.661712005734444 81.857741639018059 9.7070900946855527 14.983826428651808 18.469421133399013 8.2471718788146955 13.23467229306698 5.4932533204555494 19.488814577460289 13.432914495468138 6.9779017716646186 14.216827720403673 7.7248391658067703 7.2458040118217459 7.717806249856948 920.92100304365158 7.7329429537057868 17.162558764219284 18.970552012324333 13.447590529918672 9.7151652127504331 5.9908913373947135 6.9751639068126705 21.442294657230377 8.4823282361030561 804.6910892277956 33.403404027223587 267.14387786388397
leaf_count=64 117 50 95 328 39 60 74 33 53 22 78 54 28 57 31 29 31 3699 31 69 76 54 39 24 28 86 34 3228 134 1071
internal_value=-6.16077e-07 -0.00190379 0.00148561 0.000910119 0.00924831 0.0154771 0.00411404 0.0152385 0.0267544 -0.00310203 0.0112594 0.00524555 0.0125606 -0.00227979 0.0167979 0.0185611 0.0176854 -0.00194856 0.00665747 -0.00173361 0.0169504 -0.00738419 0.0041762 0.00316241 -0.00273938 0.0123979 0.0129385 0.000137445 -0.000506653 0.0036579
internal_weight=2446.05 1072.57 1373.48 1297.1 110.004 73.1245 36.4237 26.7166 36.7008 36.8798 76.3746 56.8858 43.4529 1052.37 13.2181 36.475 20.1994 1036.57 87.2365 48.0583 39.1783 40.3253 23.1628 20.2077 949.338 28.4175 21.717 1187.1 1105.24 300.547
internal_count=9816 4308 5508 5202 441 293 146 107 147 148 306 228 174 4227 53 146 81 4163 350 193 157 162 93 81 3813 114 87 4761 4433 1205
is_linear=0
shrinkage=0.03
end of trees
feature_importances:
feat_range_0=13
feat_atr_ratio_5_15=12
feat_bbw_10_15=11
feat_hurst_115=9
feat_stoch_k_21_5=9
feat_fft_trend_46_2=9
feat_rsi_7=7
feat_log_natr_5=7
feat_kc_dev_abs_23_23_15=7
feat_rsi_14=6
feat_hurst_23=6
feat_roc_230=6
feat_roc_ma_20_20=6
feat_natr_5=6
feat_adx_120=6
feat_bbw_23_15=6
feat_atr_ratio_50_200=6
feat_roc_23=5
feat_roc_ma_5_5=5
feat_roc_ma_5_10=5
feat_log_natr_21=5
feat_adx_14=5
feat_adx_30=5
feat_kc_dev_sig_5_5_15=5
feat_kc_dev_sig_230_230_15=5
feat_atr_ratio_10_20=5
feat_z_atr_14_100=5
pos_duration=5
pos_pnl_pct=5
feat_stoch_k_5_3=4
feat_roc_5=4
feat_roc_ma_10_10=4
feat_roc_ma_10_20=4
feat_roc_ma_20_40=4
feat_adx_7=4
feat_bb_dev_abs_50_25=4
feat_bb_dev_abs_100_25=4
feat_bb_dev_abs_200_30=4
feat_kc_dev_sig_23_23_15=4
feat_kc_dev_abs_5_5_15=4
feat_fft_trend_46_1=4
feat_vol_trend_rel_20_3=4
feat_rsi_20=3
feat_hurst_230=3
feat_stoch_k_14_3=3
feat_natr_21=3
feat_bb_dev_abs_23_20=3
feat_bb_dev_abs_30_20=3
feat_atr_ratio_20_50=3
feat_atr_ratio_20_100=3
feat_z_atr_7_100=3
feat_rsi_35=2
feat_range_1=2
feat_natr_14=2
feat_log_natr_14=2
feat_bbw_230_15=2
feat_bb_dev_sig_23_20=2
feat_bb_dev_sig_30_20=2
feat_bb_dev_sig_100_25=2
feat_kc_dev_abs_230_230_15=2
feat_atr_ratio_5_20=2
feat_atr_ratio_10_50=2
feat_rsi_40=1
feat_roc_10=1
feat_roc_15=1
feat_adx_240=1
feat_bb_dev_sig_50_25=1
feat_atr_ratio_10_30=1
feat_vol_skew_20_60=1
parameters:
[boosting: gbdt]
[objective: binary]
[metric: auc]
[tree_learner: serial]
[device_type: cpu]
[data_sample_strategy: bagging]
[data: ]
[valid: ]
[num_iterations: 100]
[learning_rate: 0.03]
[num_leaves: 31]
[num_threads: 20]
[seed: 42]
[deterministic: 0]
[force_col_wise: 0]
[force_row_wise: 0]
[histogram_pool_size: -1]
[max_depth: -1]
[min_data_in_leaf: 20]
[min_sum_hessian_in_leaf: 0.001]
[bagging_fraction: 1]
[pos_bagging_fraction: 1]
[neg_bagging_fraction: 1]
[bagging_freq: 0]
[bagging_seed: 400]
[bagging_by_query: 0]
[feature_fraction: 0.8]
[feature_fraction_bynode: 1]
[feature_fraction_seed: 30056]
[extra_trees: 0]
[extra_seed: 12879]
[early_stopping_round: 0]
[early_stopping_min_delta: 0]
[first_metric_only: 0]
[max_delta_step: 0]
[lambda_l1: 0]
[lambda_l2: 0]
[linear_lambda: 0]
[min_gain_to_split: 0]
[drop_rate: 0.1]
[max_drop: 50]
[skip_drop: 0.5]
[xgboost_dart_mode: 0]
[uniform_drop: 0]
[drop_seed: 17869]
[top_rate: 0.2]
[other_rate: 0.1]
[min_data_per_group: 100]
[max_cat_threshold: 32]
[cat_l2: 10]
[cat_smooth: 10]
[max_cat_to_onehot: 4]
[top_k: 20]
[monotone_constraints: ]
[monotone_constraints_method: basic]
[monotone_penalty: 0]
[feature_contri: ]
[forcedsplits_filename: ]
[refit_decay_rate: 0.9]
[cegb_tradeoff: 1]
[cegb_penalty_split: 0]
[cegb_penalty_feature_lazy: ]
[cegb_penalty_feature_coupled: ]
[path_smooth: 0]
[interaction_constraints: ]
[verbosity: -1]
[saved_feature_importance_type: 0]
[use_quantized_grad: 0]
[num_grad_quant_bins: 4]
[quant_train_renew_leaf: 0]
[stochastic_rounding: 1]
[linear_tree: 0]
[max_bin: 255]
[max_bin_by_feature: ]
[min_data_in_bin: 3]
[bin_construct_sample_cnt: 200000]
[data_random_seed: 175]
[is_enable_sparse: 1]
[enable_bundle: 1]
[use_missing: 1]
[zero_as_missing: 0]
[feature_pre_filter: 1]
[pre_partition: 0]
[two_round: 0]
[header: 0]
[label_column: ]
[weight_column: ]
[group_column: ]
[ignore_column: ]
[categorical_feature: ]
[forcedbins_filename: ]
[precise_float_parser: 0]
[parser_config_file: ]
[objective_seed: 16083]
[num_class: 1]
[is_unbalance: 0]
[scale_pos_weight: 1]
[sigmoid: 1]
[boost_from_average: 1]
[reg_sqrt: 0]
[alpha: 0.9]
[fair_c: 1]
[poisson_max_delta_step: 0.7]
[tweedie_variance_power: 1.5]
[lambdarank_truncation_level: 30]
[lambdarank_norm: 1]
[label_gain: ]
[lambdarank_position_bias_regularization: 0]
[eval_at: ]
[multi_error_top_k: 1]
[auc_mu_weights: ]
[num_machines: 1]
[local_listen_port: 12400]
[time_out: 120]
[machine_list_filename: ]
[machines: ]
[gpu_platform_id: -1]
[gpu_device_id: -1]
[gpu_use_dp: 0]
[num_gpu: 1]
end of parameters
pandas_categorical:[]

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@ from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.indicators.indicators import Empty, ADX
from src.strategies.base_strategy import Strategy
@@ -97,6 +97,8 @@ class DualModeKalmanStrategy(Strategy):
bar_history = self.get_bar_history()
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
self.cancel_all_pending_orders(symbol)
# --- 通用数据计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
@@ -196,6 +198,7 @@ class DualModeKalmanStrategy(Strategy):
def evaluate_entry_signal(self, current_bar: Bar, kalman_price: float, current_atr: float):
deviation = current_bar.close - kalman_price
deviation_in_atr = deviation / current_atr
self.log(f'deviation_in_atr: {deviation_in_atr:.4f}')
direction = None
@@ -219,7 +222,7 @@ class DualModeKalmanStrategy(Strategy):
if direction:
self.log(
f"{self.strategy_mode} Mode: Catalyst Fired. Direction: {direction}. Deviation: {deviation_in_atr:.2f} ATRs.")
f"{self.strategy_mode} Mode: Catalyst Fired. Direction: {direction}. Deviation: {deviation_in_atr:.2f} ATRs., entry_threshold_atr: {self.entry_threshold_atr}")
entry_price = current_bar.close + (1 if direction == "BUY" else -1)
stop_loss_price = entry_price - self.initial_stop_atr_multiplier * current_atr if direction in ["BUY",
"CLOSE_SHORT"] else entry_price + self.initial_stop_atr_multiplier * current_atr

View File

@@ -1,177 +1,221 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
class TVDZScoreStrategy(Strategy):
class DualModeKalmanStrategy(Strategy):
"""
内嵌 TVD (Condat 算法) + Z-Score ATR 的趋势突破策略
无任何外部依赖(如 pytv纯 NumPy 实现。
基于卡尔曼因子对称性的双模自适应策略
因子定义: Deviation = (Current_Close - Kalman_Price) / ATR
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
tvd_lam: float = 50.0,
atr_window: int = 14,
z_window: int = 100,
vol_threshold: float = -0.5,
entry_threshold_atr: float = 3.0,
stop_atr_multiplier: float = 3.0,
order_direction: Optional[List[str]] = None,
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
strategy_mode: str = 'TREND', # 'TREND' 或 'REVERSION'
kalman_process_noise: float = 0.01,
kalman_measurement_noise: float = 0.5,
atr_period: int = 23,
atr_lookback: int = 100,
atr_percentile_threshold: float = 25.0,
entry_threshold_atr: float = 2.5, # 入场偏离倍数
stop_loss_atr: float = 2.0, # 保护性硬止损倍数
trend_trailing_atr: float = 2.5, # 趋势模式下卡尔曼轨道的宽度
order_direction=None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
self.trade_volume = trade_volume
self.order_direction = order_direction or ["BUY", "SELL"]
self.tvd_lam = tvd_lam
self.atr_window = atr_window
self.z_window = z_window
self.vol_threshold = vol_threshold
self.entry_threshold_atr = entry_threshold_atr
self.stop_atr_multiplier = stop_atr_multiplier
if order_direction is None:
order_direction = ['BUY', 'SELL']
self.strategy_mode = strategy_mode.upper()
self.trade_volume = trade_volume
self.atr_period = atr_period
self.atr_lookback = atr_lookback
self.atr_percentile_threshold = atr_percentile_threshold
self.entry_threshold_atr = entry_threshold_atr
self.stop_loss_atr = stop_loss_atr
self.trend_trailing_atr = entry_threshold_atr
# 卡尔曼状态
self.Q = kalman_process_noise
self.R = kalman_measurement_noise
self.P = 1.0
self.x_hat = 0.0
self.kalman_initialized = False
self._atr_history: deque = deque(maxlen=self.atr_lookback)
self.position_meta: Dict[str, Any] = self.context.load_state()
self.main_symbol = main_symbol
self.order_id_counter = 0
self.log(f"TVDZScoreStrategy Initialized | λ={tvd_lam}, VolThresh={vol_threshold}")
self.order_direction = order_direction
if indicators is None:
self.indicators = [Empty(), Empty()]
else:
self.indicators = indicators
@staticmethod
def _tvd_condat(y, lam):
"""Condat's O(N) TVD algorithm."""
n = y.size
if n == 0:
return y.copy()
x = y.astype(np.float64)
k = 0
k0 = 0
vmin = x[0] - lam
vmax = x[0] + lam
for i in range(1, n):
if x[i] < vmin:
while k < i:
x[k] = vmin
k += 1
k0 = i
vmin = x[i] - lam
vmax = x[i] + lam
elif x[i] > vmax:
while k < i:
x[k] = vmax
k += 1
k0 = i
vmin = x[i] - lam
vmax = x[i] + lam
else:
vmin = max(vmin, x[i] - lam)
vmax = min(vmax, x[i] + lam)
if vmin > vmax:
k = k0
s = np.sum(x[k0:i+1])
s /= (i - k0 + 1)
x[k0:i+1] = s
k = i + 1
k0 = k
if k0 < n:
vmin = x[k0] - lam
vmax = x[k0] + lam
while k < n:
x[k] = vmin
k += 1
return x
def _compute_zscore_atr_last(self, high, low, close) -> float:
n = len(close)
min_req = self.atr_window + self.z_window - 1
if n < min_req:
return np.nan
start = max(0, n - (self.z_window + self.atr_window))
seg_h, seg_l, seg_c = high[start:], low[start:], close[start:]
atr_full = talib.ATR(seg_h, seg_l, seg_c, timeperiod=self.atr_window)
atr_valid = atr_full[self.atr_window - 1:]
if len(atr_valid) < self.z_window:
return np.nan
window_atr = atr_valid[-self.z_window:]
mu = np.mean(window_atr)
sigma = np.std(window_atr)
last_atr = window_atr[-1]
return (last_atr - mu) / sigma if sigma > 1e-12 else 0.0
self.log(f"Initialized [{self.strategy_mode}] Mode with Kalman Symmetry.")
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
if len(bar_history) < max(100, self.atr_window + self.z_window):
return
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
closes = np.array([b.close for b in bar_history], dtype=np.float64)
highs = np.array([b.high for b in bar_history], dtype=np.float64)
lows = np.array([b.low for b in bar_history], dtype=np.float64)
self.cancel_all_pending_orders(symbol)
# === TVD 平滑 ===
tvd_prices = self._tvd_condat(closes, self.tvd_lam)
tvd_price = tvd_prices[-1]
# 1. 计算核心指标
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
# === Z-Score ATR ===
current_atr = talib.ATR(highs, lows, closes, timeperiod=self.atr_window)[-1]
if current_atr <= 0:
return
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
self._atr_history.append(current_atr)
deviation = closes[-1] - tvd_price
deviation_in_atr = deviation / current_atr
if current_atr <= 0 or len(self._atr_history) < self.atr_lookback: return
# 2. 更新卡尔曼滤波器
if not self.kalman_initialized:
self.x_hat = closes[-1]
self.kalman_initialized = True
x_hat_minus = self.x_hat
P_minus = self.P + self.Q
K = P_minus / (P_minus + self.R)
self.x_hat = x_hat_minus + K * (closes[-1] - x_hat_minus)
self.P = (1 - K) * P_minus
kalman_price = self.x_hat
# 3. 计算对称性因子:偏离度 (Deviation in ATR)
deviation_in_atr = (closes[-1] - kalman_price) / current_atr
# 4. 状态校验与持仓管理
position_volume = self.get_current_positions().get(self.symbol, 0)
if position_volume != 0:
self.manage_open_position(position_volume, bar_history[-1], current_atr, tvd_price)
if self.trading:
if position_volume != 0:
self.manage_logic(position_volume, bar_history[-1], current_atr, kalman_price, deviation_in_atr)
else:
# 波动率过滤:只在波动率处于自身中高水平时入场
# atr_threshold = np.percentile(list(self._atr_history), self.atr_percentile_threshold)
# if current_atr >= atr_threshold:
self.evaluate_entry_signal(bar_history[-1], deviation_in_atr, current_atr, open_price)
def manage_logic(self, volume: int, current_bar: Bar, current_atr: float, kalman_price: float, dev: float):
"""
基于对称因子的出场逻辑
"""
meta = self.position_meta.get(self.symbol)
if not meta: return
# A. 保护性硬止损 (防止跳空或极端行情)
initial_stop = meta.get('initial_stop_price', 0)
if (volume > 0 and current_bar.low <= initial_stop) or (volume < 0 and current_bar.high >= initial_stop):
self.log(f"Hard Stop Hit. Price: {current_bar.close}")
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume))
return
# B. 对称因子出场逻辑
if self.strategy_mode == 'TREND':
if volume > 0:
trend_floor = kalman_price - self.trend_trailing_atr * current_atr
if dev < 0:
self.log(f"TREND: Structural Floor Hit at {trend_floor:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
else:
trend_ceiling = kalman_price + self.trend_trailing_atr * current_atr
if dev > 0:
self.log(f"TREND: Structural Ceiling Hit at {trend_ceiling:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
elif self.strategy_mode == 'REVERSION':
# 回归模式:出场基于“均值修复成功”或“偏离失控止损”
# 1. 目标达成:回归到均值附近 (止盈)
if volume > 0:
trend_floor = kalman_price - self.trend_trailing_atr * current_atr
if dev > 0:
self.log(f"TREND: Structural Floor Hit at {trend_floor:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
else:
trend_ceiling = kalman_price + self.trend_trailing_atr * current_atr
if dev < 0:
self.log(f"TREND: Structural Ceiling Hit at {trend_ceiling:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
def evaluate_entry_signal(self, current_bar: Bar, dev: float, current_atr: float, open_price: float):
"""
基于对称因子的入场逻辑
"""
direction = None
if "BUY" in self.order_direction and deviation_in_atr > self.entry_threshold_atr:
direction = "BUY"
elif "SELL" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr:
direction = "SELL"
if self.strategy_mode == 'TREND':
# 趋势:顺着偏离方向入场
if dev > self.entry_threshold_atr and self.indicators[0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
elif dev < -self.entry_threshold_atr and self.indicators[1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
elif self.strategy_mode == 'REVERSION':
# 回归:逆着偏离方向入场
if dev > self.entry_threshold_atr and self.indicators[1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL" # 超买做空
elif dev < -self.entry_threshold_atr and self.indicators[0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY" # 超卖做多
if direction:
self.log(f"Signal Fired | Dir: {direction}, Dev: {deviation_in_atr:.2f} ATR")
entry_price = closes[-1]
stop_loss = (
entry_price - self.stop_atr_multiplier * current_atr
if direction == "BUY"
else entry_price + self.stop_atr_multiplier * current_atr
)
meta = {"entry_price": entry_price, "stop_loss": stop_loss}
self.send_market_order(direction, self.trade_volume, "OPEN", meta)
self.save_state(self.position_meta)
# 使用最小变动单位修正价格此处假设最小变动为0.5实盘应从context获取
tick_size = 1
entry_price = open_price
def manage_open_position(self, volume: int, current_bar: Bar, current_atr: float, tvd_price: float):
meta = self.position_meta.get(self.symbol)
if not meta:
return
stop_loss = meta["stop_loss"]
if (volume > 0 and current_bar.low <= stop_loss) or (volume < 0 and current_bar.high >= stop_loss):
self.log(f"Stop Loss Hit at {stop_loss:.4f}")
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume))
# 设置保护性硬止损
stop_offset = self.stop_loss_atr * current_atr
stop_price = entry_price - stop_offset if direction == "BUY" else entry_price + stop_offset
meta = {'entry_price': entry_price, 'initial_stop_price': stop_price}
self.log(f"Entry Signal: {self.strategy_mode} | {direction} | Dev: {dev:.2f}")
self.send_custom_order(entry_price, direction, self.trade_volume, "OPEN", meta)
# --- 辅助函数 ---
def send_custom_order(self, price: float, direction: str, volume: int, offset: str, meta: Dict):
self.position_meta[self.symbol] = meta
order_type = "LIMIT"
order_id = f"{self.symbol}_{direction}_{order_type}_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(
id=order_id, symbol=self.symbol, direction=direction,
volume=volume, price_type=order_type,
submitted_time=self.get_current_time(), offset=offset, limit_price=price
)
self.send_order(order)
self.save_state(self.position_meta)
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE")
if self.symbol in self.position_meta:
del self.position_meta[self.symbol]
self.save_state(self.position_meta)
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta:
self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume,
price_type="MARKET", submitted_time=self.get_current_time(), offset=offset)
order = Order(
id=order_id, symbol=self.symbol, direction=direction,
volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset="CLOSE"
)
self.send_order(order)
self.position_meta.pop(self.symbol, None)
self.save_state(self.position_meta)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol)
self.position_meta = {}
self.log("Rollover: Strategy state reset.")
self.kalman_initialized = False
self._atr_history.clear()
self.log("Rollover: States Reset.")

View File

@@ -1,246 +0,0 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict, Tuple
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
# =============================================================================
# 策略实现 (ImbalanceZFlowStrategy)
# =============================================================================
class ImbalanceZFlowStrategy(Strategy):
"""
一个基于稳态核心指标“Z-Flow”的纯粹动量策略。
核心哲学:
1. 根本性解决稳态问题先将原始imbalance序列通过Z-Score转化为
稳态序列Z_I(t),所有后续分析都基于此坚实基础。
2. 核心驱动 Z_Flow: 对稳态的Z_I(t)序列计算MACD以度量“统计
显著性”的动量,形成一个高质量的稳态振荡器。
3. 真实的回测逻辑所有开仓和风险计算都严格基于当前bar的open_price
杜绝前视偏差。
4. 极致简洁:策略由单一指标、两个对称阈值驱动,逻辑纯粹,鲁棒性强。
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【霍克斯过程参数】 ---
hawkes_lookback: int = 60,
hawkes_alpha: float = 0.8,
hawkes_beta: float = 0.2,
# --- 【Z-Score 标准化参数】 ---
z_score_period: int = 200,
# --- 【Z-Flow 指标参数】 ---
z_flow_fast_ema_period: int = 12,
z_flow_slow_ema_period: int = 26,
# --- 【交易阈值】 ---
entry_threshold: float = 0.5,
exit_threshold: float = 0.1,
# --- 【风险管理】 ---
atr_period: int = 20,
stop_loss_atr_multiplier: float = 3.0,
# --- 其他 ---
order_direction: Optional[List[str]] = None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
# --- 参数赋值 ---
self.trade_volume = trade_volume
self.hawkes_lookback = hawkes_lookback
self.hawkes_alpha = hawkes_alpha
self.hawkes_beta = hawkes_beta
self.z_score_period = z_score_period
self.z_flow_fast_ema_period = z_flow_fast_ema_period
self.z_flow_slow_ema_period = z_flow_slow_ema_period
self.entry_threshold = entry_threshold
self.exit_threshold = exit_threshold
self.atr_period = atr_period
self.stop_loss_atr_multiplier = stop_loss_atr_multiplier
self.order_direction = order_direction
# --- 内部状态变量 ---
self._imbalance_history: deque = deque(maxlen=self.z_score_period + self.z_flow_slow_ema_period)
# 核心指标
self.z_flow = 0.0
self.prev_z_flow = 0.0
self.position_meta: Dict[str, Any] = self.context.load_state()
self.main_symbol = main_symbol
self.order_id_counter = 0
if indicators is None: indicators = [Empty(), Empty()]
self.indicators = indicators
self.log(f"ImbalanceZFlowStrategy Initialized")
def on_init(self):
super().on_init()
self.cancel_all_pending_orders(self.main_symbol)
self.position_meta = self.context.load_state()
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
required_bars = self._imbalance_history.maxlen + self.hawkes_lookback + 5
if len(bar_history) < required_bars:
return
self.prev_z_flow = self.z_flow
self._update_indicators_and_state(bar_history)
position_volume = self.get_current_positions().get(self.symbol, 0)
self._sync_position_state(position_volume, symbol)
if not self.trading: return
if position_volume != 0:
self.manage_open_position(position_volume, bar_history[-1])
else:
self.evaluate_entry_signal(open_price, bar_history) # 传入open_price
def _update_indicators_and_state(self, bar_history: List[Bar]):
"""核心计算函数I(t) -> Z_I(t) -> Z_Flow(t)"""
# --- 1. 计算瞬时不均衡力量 I(t) ---
long_events, short_events = [], []
lookback_bars = bar_history[-self.hawkes_lookback:]
for i, bar in enumerate(lookback_bars):
event_age = len(lookback_bars) - 1 - i
mark = (bar.high - bar.low) * bar.volume
if mark > 0:
if bar.close > bar.open:
long_events.append({'age': event_age, 'mark': mark})
elif bar.close < bar.open:
short_events.append({'age': event_age, 'mark': mark})
lambda_long = sum(self.hawkes_alpha * e['mark'] * np.exp(-self.hawkes_beta * e['age']) for e in long_events)
lambda_short = sum(self.hawkes_alpha * e['mark'] * np.exp(-self.hawkes_beta * e['age']) for e in short_events)
imbalance = lambda_long - lambda_short
self._imbalance_history.append(imbalance)
if len(self._imbalance_history) < self.z_score_period:
self.z_flow = 0.0
return
imbalance_series = pd.Series(list(self._imbalance_history))
# --- 2. Z-Score 标准化,得到稳态序列 Z_I(t) ---
rolling_mean = imbalance_series.rolling(window=self.z_score_period).mean()
rolling_std = imbalance_series.rolling(window=self.z_score_period).std()
# 避免除以零并处理初始NaN值
if rolling_std.iloc[-1] > 1e-9:
z_score_series = (imbalance_series - rolling_mean) / rolling_std
else:
z_score_series = pd.Series(0.0, index=imbalance_series.index)
z_score_series.fillna(0.0, inplace=True)
# --- 3. 对稳态序列 Z_I(t) 计算MACD得到 Z_Flow ---
fast_ema = z_score_series.ewm(span=self.z_flow_fast_ema_period, adjust=False).mean()
slow_ema = z_score_series.ewm(span=self.z_flow_slow_ema_period, adjust=False).mean()
self.z_flow = fast_ema.iloc[-1] - slow_ema.iloc[-1]
def manage_open_position(self, volume: int, current_bar: Bar):
"""由Z-Flow驱动的统一平仓逻辑并辅以价格止损。"""
meta = self.position_meta.get(self.symbol)
if not meta: return
is_long = volume > 0
# 1. 价格硬止损
stop_loss_price = meta['stop_loss_price']
if (is_long and current_bar.low <= stop_loss_price) or \
(not is_long and current_bar.high >= stop_loss_price):
self.log(f"ATR Stop Loss Hit at {stop_loss_price:.4f}")
self.close_position("CLOSE_LONG" if is_long else "CLOSE_SHORT", abs(volume))
return
# 2. Z_Flow 驱动的平仓 (动能耗散)
exit_triggered = False
if is_long and self.z_flow < self.exit_threshold:
exit_triggered = True
elif not is_long and self.z_flow > -self.exit_threshold:
exit_triggered = True
if exit_triggered:
self.log(f"Z-Flow Dissipation Exit. Direction: {'LONG' if is_long else 'SHORT'}. "
f"Z-Flow ({self.z_flow:.2f}) returned to neutral zone.")
self.close_position("CLOSE_LONG" if is_long else "CLOSE_SHORT", abs(volume))
def evaluate_entry_signal(self, open_price: float, bar_history: List[Bar]):
"""当Z-Flow穿越入场阈值时在open_price开仓。"""
direction = None
if "BUY" in self.order_direction and self.prev_z_flow < self.entry_threshold <= self.z_flow:
direction = "BUY"
elif "SELL" in self.order_direction and self.prev_z_flow > -self.entry_threshold >= self.z_flow:
direction = "SELL"
if direction:
self.log(
f"Z-Flow Signal: {direction}. Z-Flow: {self.z_flow:.2f} crossed threshold. Entry on Open: {open_price}")
# 使用完整的bar_history计算ATR
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
if current_atr <= 0: return
# ** 关键修正:基于 open_price 计算止损 **
stop_loss_price = open_price - self.stop_loss_atr_multiplier * current_atr if direction == "BUY" \
else open_price + self.stop_loss_atr_multiplier * current_atr
# ** 关键修正:记录 open_price 为入场价 **
meta = {'entry_price': open_price, 'stop_loss_price': stop_loss_price}
self.send_market_order(direction, self.trade_volume, "OPEN", meta)
self.save_state(self.position_meta)
# ... (辅助函数保持不变) ...
def _sync_position_state(self, position_volume, symbol):
meta = self.position_meta.get(symbol)
if position_volume != 0 and not meta:
self.log(f"警告:持仓({position_volume})与策略状态不一致!将强制平仓。", level='WARNING')
self.close_position("CLOSE_LONG" if position_volume > 0 else "CLOSE_SHORT", abs(position_volume))
return
if position_volume == 0 and meta:
self.log(f"信息:清理过时的策略状态。", level='INFO')
self.position_meta.pop(symbol, None)
self.save_state(self.position_meta)
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE")
if self.symbol in self.position_meta:
self.position_meta.pop(self.symbol, None)
self.save_state(self.position_meta)
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset)
self.send_order(order)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol)
self.position_meta = {}
self._imbalance_history.clear()
self.z_flow = 0.0
self.prev_z_flow = 0.0
self.log("Rollover detected. All strategy states have been reset.")

View File

@@ -5,8 +5,8 @@
"id": "522f09ca7b3fe929",
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-19T06:30:07.814040Z",
"start_time": "2025-10-19T06:30:06.968311Z"
"end_time": "2025-12-17T13:57:21.148878Z",
"start_time": "2025-12-17T13:57:21.108622Z"
}
},
"source": [
@@ -23,15 +23,15 @@
"\n"
],
"outputs": [],
"execution_count": 1
"execution_count": 2
},
{
"cell_type": "code",
"id": "c00ccfeec592844c",
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-19T06:30:08.226844Z",
"start_time": "2025-10-19T06:30:07.822511Z"
"end_time": "2025-12-17T13:57:21.519369Z",
"start_time": "2025-12-17T13:57:21.150889Z"
}
},
"source": [
@@ -51,15 +51,15 @@
"data_file_path = '/mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min15.csv'\n"
],
"outputs": [],
"execution_count": 2
"execution_count": 3
},
{
"cell_type": "code",
"id": "7599fa7cd2cb3d45",
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-19T06:30:08.276053Z",
"start_time": "2025-10-19T06:30:08.233422Z"
"end_time": "2025-12-17T13:57:21.542513Z",
"start_time": "2025-12-17T13:57:21.519369Z"
}
},
"source": [
@@ -76,8 +76,8 @@
"# start_time = datetime(2021, 1, 1)\n",
"# end_time = datetime(2025, 1, 1)\n",
"\n",
"start_time = datetime(2025, 6, 1)\n",
"end_time = datetime(2025, 10, 1)\n",
"start_time = datetime(2025, 10, 1)\n",
"end_time = datetime(2025, 12, 1)\n",
"\n",
"\n",
"indicators = INDICATOR_LIST\n",
@@ -87,15 +87,15 @@
"# data_manager.reset() # 首次运行不需要重置"
],
"outputs": [],
"execution_count": 3
"execution_count": 4
},
{
"cell_type": "code",
"id": "f903fd2761d446cd",
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-19T06:30:51.160327Z",
"start_time": "2025-10-19T06:30:08.282837Z"
"end_time": "2025-12-17T13:57:22.373414Z",
"start_time": "2025-12-17T13:57:21.550522Z"
}
},
"source": [
@@ -153,8 +153,6 @@
"name": "stderr",
"output_type": "stream",
"text": [
"/home/liaozhaorun/miniconda3/envs/quant/lib/python3.12/site-packages/requests/__init__.py:86: RequestsDependencyWarning: Unable to find acceptable character detection dependency (chardet or charset_normalizer).\n",
" warnings.warn(\n",
"在使用天勤量化之前默认您已经知晓并同意以下免责条款如果不同意请立即停止使用https://www.shinnytech.com/blog/disclaimer/\n"
]
},
@@ -162,236 +160,29 @@
"name": "stdout",
"output_type": "stream",
"text": [
"初始化数据管理器...\n",
"数据加载成功: /mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min15.csv\n",
"数据范围从 2020-12-31 14:45:00 到 2025-08-14 09:30:00\n",
"总计 25470 条记录。\n",
"\n",
"初始化回测引擎...\n",
" INFO - TqSdk free 版剩余 0 天到期,如需续费或升级请访问 https://account.shinnytech.com/ 或联系相关工作人员。\n",
"\n",
"初始化 Tqsdk 回测引擎...\n",
"内存仓储已初始化管理ID: 'src.strategies.HawkesStrategy.KalmanStrategy2.DualModeKalmanStrategy_c7bd93715d42fbef1ec746ee2635a5d3'\n",
"TqsdkContext: 初始化完成。\n",
"TqsdkContext: 已设置引擎引用。\n",
"TqsdkEngine: 初始化完成。\n",
"\n",
"开始运行回测...\n",
"TqsdkEngine: 开始运行回测,从 2025-06-01 00:00:00 到 2025-10-01 00:00:00\n",
"DualModeKalmanStrategy 策略初始化回调被调用。\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_0', price_type='LIMIT', limit_price=2995, stop_price=None, submitted_time=Timestamp('2025-06-26 22:30:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_0', price_type='LIMIT', limit_price=2995, stop_price=None, submitted_time=Timestamp('2025-06-26 22:30:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_6d002815bde371e9679eeff6ab3a2913: 时间: 2025-06-26 22:30:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 2995.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_6d002815bde371e9679eeff6ab3a2913: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_1', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-06-30 21:15:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_1', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-06-30 21:15:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_beee6bfdba88ae99ca624cd9f3ce2f3b: 时间: 2025-06-30 21:15:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 2987.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_beee6bfdba88ae99ca624cd9f3ce2f3b: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_2', price_type='LIMIT', limit_price=3014, stop_price=None, submitted_time=Timestamp('2025-07-01 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_2', price_type='LIMIT', limit_price=3014, stop_price=None, submitted_time=Timestamp('2025-07-01 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_6dd75ae79e4ba3b47455ae5c7e77b734: 时间: 2025-07-01 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3014.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_6dd75ae79e4ba3b47455ae5c7e77b734: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_3', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-15 13:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_3', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-15 13:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_65fa28a9de53ea759cef7a640c9f33bb: 时间: 2025-07-15 13:30:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3108.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_65fa28a9de53ea759cef7a640c9f33bb: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_4', price_type='LIMIT', limit_price=3180, stop_price=None, submitted_time=Timestamp('2025-07-18 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_4', price_type='LIMIT', limit_price=3180, stop_price=None, submitted_time=Timestamp('2025-07-18 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_89a26adf3a52ec844c1e57271d827611: 时间: 2025-07-18 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3180.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_89a26adf3a52ec844c1e57271d827611: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_5', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-28 09:00:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_5', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-28 09:00:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_d6bf6ea9c1cbed5a7728d70d7c08c101: 时间: 2025-07-28 09:00:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3249.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_d6bf6ea9c1cbed5a7728d70d7c08c101: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_6', price_type='LIMIT', limit_price=3320, stop_price=None, submitted_time=Timestamp('2025-07-29 11:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='BUY', volume=1, id='SHFE.rb2510_BUY_MARKET_6', price_type='LIMIT', limit_price=3320, stop_price=None, submitted_time=Timestamp('2025-07-29 11:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_b61fde38f971505d50c793a95595d09b: 时间: 2025-07-29 11:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3320.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_b61fde38f971505d50c793a95595d09b: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_7', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-30 14:15:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_LONG', volume=1, id='SHFE.rb2510_CLOSE_LONG_MARKET_7', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-07-30 14:15:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_cd809e06343bde100900018193c8007c: 时间: 2025-07-30 14:15:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3294.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_cd809e06343bde100900018193c8007c: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='SELL', volume=1, id='SHFE.rb2510_SELL_MARKET_8', price_type='LIMIT', limit_price=3278, stop_price=None, submitted_time=Timestamp('2025-07-30 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='SELL', volume=1, id='SHFE.rb2510_SELL_MARKET_8', price_type='LIMIT', limit_price=3278, stop_price=None, submitted_time=Timestamp('2025-07-30 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_c4cd5e82c45ddd980c03c798dd7dfd57: 时间: 2025-07-30 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3278.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_c4cd5e82c45ddd980c03c798dd7dfd57: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2510_CLOSE_SHORT_MARKET_9', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-08-05 21:00:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2510_CLOSE_SHORT_MARKET_9', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-08-05 21:00:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_7cc7c97528cd3275b65f4b0d59e11b2d: 时间: 2025-08-05 21:00:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: BUY, 手数: 1, 价格: 3234.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_7cc7c97528cd3275b65f4b0d59e11b2d: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='SELL', volume=1, id='SHFE.rb2510_SELL_MARKET_10', price_type='LIMIT', limit_price=3209, stop_price=None, submitted_time=Timestamp('2025-08-13 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='SELL', volume=1, id='SHFE.rb2510_SELL_MARKET_10', price_type='LIMIT', limit_price=3209, stop_price=None, submitted_time=Timestamp('2025-08-13 21:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_c1967ceb33977250525c7b5d50362f0f: 时间: 2025-08-13 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3209.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_c1967ceb33977250525c7b5d50362f0f: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2510', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2510_CLOSE_SHORT_MARKET_11', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-08-22 21:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2510', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2510_CLOSE_SHORT_MARKET_11', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-08-22 21:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_ef23037e39ab68f8630f354b07558822: 时间: 2025-08-22 21:30:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: BUY, 手数: 1, 价格: 3144.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_ef23037e39ab68f8630f354b07558822: 全部成交\n",
"TqsdkEngine: 检测到换月信号!从 SHFE.rb2601 切换到 SHFE.rb2601\n",
"回测结束:没有需要平仓的持仓。\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='BUY', volume=1, id='SHFE.rb2601_BUY_MARKET_12', price_type='LIMIT', limit_price=3141, stop_price=None, submitted_time=Timestamp('2025-09-05 14:00:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='BUY', volume=1, id='SHFE.rb2601_BUY_MARKET_12', price_type='LIMIT', limit_price=3141, stop_price=None, submitted_time=Timestamp('2025-09-05 14:00:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_61c801ceffcb4703782d6de986bf3cd0: 时间: 2025-09-05 14:00:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3141.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_61c801ceffcb4703782d6de986bf3cd0: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='CLOSE_LONG', volume=1, id='SHFE.rb2601_CLOSE_LONG_MARKET_13', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-05 22:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='CLOSE_LONG', volume=1, id='SHFE.rb2601_CLOSE_LONG_MARKET_13', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-05 22:30:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_989107326c47cd316aaea3e417db5127: 时间: 2025-09-05 22:30:00.000000, 合约: SHFE.rb2601, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3121.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_989107326c47cd316aaea3e417db5127: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='SELL', volume=1, id='SHFE.rb2601_SELL_MARKET_14', price_type='LIMIT', limit_price=3094, stop_price=None, submitted_time=Timestamp('2025-09-09 21:30:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='SELL', volume=1, id='SHFE.rb2601_SELL_MARKET_14', price_type='LIMIT', limit_price=3094, stop_price=None, submitted_time=Timestamp('2025-09-09 21:30:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_7393718e06ba6c2d616e278a16bddf3b: 时间: 2025-09-09 21:30:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3094.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_7393718e06ba6c2d616e278a16bddf3b: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2601_CLOSE_SHORT_MARKET_15', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-10 09:45:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='CLOSE_SHORT', volume=1, id='SHFE.rb2601_CLOSE_SHORT_MARKET_15', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-10 09:45:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_10f32ba307f98d1fae1a4d20dd088491: 时间: 2025-09-10 09:45:00.000000, 合约: SHFE.rb2601, 开平: CLOSETODAY, 方向: BUY, 手数: 1, 价格: 3110.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_10f32ba307f98d1fae1a4d20dd088491: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='BUY', volume=1, id='SHFE.rb2601_BUY_MARKET_16', price_type='LIMIT', limit_price=3115, stop_price=None, submitted_time=Timestamp('2025-09-12 11:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='BUY', volume=1, id='SHFE.rb2601_BUY_MARKET_16', price_type='LIMIT', limit_price=3115, stop_price=None, submitted_time=Timestamp('2025-09-12 11:15:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_a9c25f5eca9c0209be2fb2c388cb2af3: 时间: 2025-09-12 11:15:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3115.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_a9c25f5eca9c0209be2fb2c388cb2af3: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='CLOSE_LONG', volume=1, id='SHFE.rb2601_CLOSE_LONG_MARKET_17', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-18 10:45:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='CLOSE_LONG', volume=1, id='SHFE.rb2601_CLOSE_LONG_MARKET_17', price_type='MARKET', limit_price=None, stop_price=None, submitted_time=Timestamp('2025-09-18 10:45:00+0800', tz='Asia/Shanghai'), offset='CLOSE')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_target_09fedfe7f7f15b9362444fcd8ee381ca: 时间: 2025-09-18 10:45:00.000000, 合约: SHFE.rb2601, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3134.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_target_09fedfe7f7f15b9362444fcd8ee381ca: 全部成交\n",
"Context: 订单已加入队列: Order(symbol='SHFE.rb2601', direction='SELL', volume=1, id='SHFE.rb2601_SELL_MARKET_18', price_type='LIMIT', limit_price=3113, stop_price=None, submitted_time=Timestamp('2025-09-26 21:00:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
"Engine: 处理订单请求: Order(symbol='SHFE.rb2601', direction='SELL', volume=1, id='SHFE.rb2601_SELL_MARKET_18', price_type='LIMIT', limit_price=3113, stop_price=None, submitted_time=Timestamp('2025-09-26 21:00:00+0800', tz='Asia/Shanghai'), offset='OPEN')\n",
" INFO - 模拟交易下单 TQSIM, PYSDK_insert_2f97ffcd51216e6c3767b412a2aac199: 时间: 2025-09-26 21:00:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3113.0\n",
" INFO - 模拟交易委托单 TQSIM, PYSDK_insert_2f97ffcd51216e6c3767b412a2aac199: 全部成交\n",
" INFO - 回测结束\n",
" INFO - 模拟交易成交记录, 账户: TQSIM\n",
" INFO - 时间: 2025-06-26 22:30:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 2995.000,手续费: 2.98\n",
" INFO - 时间: 2025-06-30 21:15:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 2987.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-01 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3014.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-15 13:30:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3108.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-18 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3180.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-28 09:00:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3249.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-29 11:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3320.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-30 14:15:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3294.000,手续费: 2.98\n",
" INFO - 时间: 2025-07-30 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3278.000,手续费: 2.98\n",
" INFO - 时间: 2025-08-05 21:00:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: BUY, 手数: 1, 价格: 3234.000,手续费: 2.98\n",
" INFO - 时间: 2025-08-13 21:15:00.000000, 合约: SHFE.rb2510, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3209.000,手续费: 2.98\n",
" INFO - 时间: 2025-08-22 21:30:00.000000, 合约: SHFE.rb2510, 开平: CLOSE, 方向: BUY, 手数: 1, 价格: 3144.000,手续费: 2.98\n",
" INFO - 时间: 2025-09-05 14:00:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3141.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-05 22:30:00.000000, 合约: SHFE.rb2601, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3121.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-09 21:30:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3094.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-10 09:45:00.000000, 合约: SHFE.rb2601, 开平: CLOSETODAY, 方向: BUY, 手数: 1, 价格: 3110.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-12 11:15:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: BUY, 手数: 1, 价格: 3115.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-18 10:45:00.000000, 合约: SHFE.rb2601, 开平: CLOSE, 方向: SELL, 手数: 1, 价格: 3134.000,手续费: 3.05\n",
" INFO - 时间: 2025-09-26 21:00:00.000000, 合约: SHFE.rb2601, 开平: OPEN, 方向: SELL, 手数: 1, 价格: 3113.000,手续费: 3.05\n",
" INFO - 模拟交易账户资金, 账户: TQSIM\n",
" INFO - 日期: 2025-06-02, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-03, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-04, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-05, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-06, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-09, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-10, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-11, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-12, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-13, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-16, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-17, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-18, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-19, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-20, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-23, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-24, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-25, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-26, 账户权益: 10000000.00, 可用资金: 10000000.00, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-06-27, 账户权益: 9999997.03, 可用资金: 9997617.03, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-06-30, 账户权益: 10000017.03, 可用资金: 9997637.03, 浮动盈亏: 20.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-01, 账户权益: 9999914.05, 可用资金: 9999914.05, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -100.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-02, 账户权益: 10000421.08, 可用资金: 9998041.08, 浮动盈亏: 510.00, 持仓盈亏: 510.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-03, 账户权益: 10000531.08, 可用资金: 9998151.08, 浮动盈亏: 620.00, 持仓盈亏: 110.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-04, 账户权益: 10000491.08, 可用资金: 9998111.08, 浮动盈亏: 580.00, 持仓盈亏: -40.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-07, 账户权益: 10000381.08, 可用资金: 9998001.08, 浮动盈亏: 470.00, 持仓盈亏: -110.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-08, 账户权益: 10000401.08, 可用资金: 9998021.08, 浮动盈亏: 490.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-09, 账户权益: 10000401.08, 可用资金: 9998021.08, 浮动盈亏: 490.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-10, 账户权益: 10001001.08, 可用资金: 9998621.08, 浮动盈亏: 1090.00, 持仓盈亏: 600.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-11, 账户权益: 10001101.08, 可用资金: 9998721.08, 浮动盈亏: 1190.00, 持仓盈亏: 100.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-14, 账户权益: 10001151.08, 可用资金: 9998771.08, 浮动盈亏: 1240.00, 持仓盈亏: 50.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-15, 账户权益: 10000848.10, 可用资金: 10000848.10, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -300.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-16, 账户权益: 10000848.10, 可用资金: 10000848.10, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-17, 账户权益: 10000848.10, 可用资金: 10000848.10, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-18, 账户权益: 10000848.10, 可用资金: 10000848.10, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-21, 账户权益: 10001285.13, 可用资金: 9998905.13, 浮动盈亏: 440.00, 持仓盈亏: 440.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-22, 账户权益: 10002115.13, 可用资金: 9999735.13, 浮动盈亏: 1270.00, 持仓盈亏: 830.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-23, 账户权益: 10001785.13, 可用资金: 9999405.13, 浮动盈亏: 940.00, 持仓盈亏: -330.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-24, 账户权益: 10001985.13, 可用资金: 9999605.13, 浮动盈亏: 1140.00, 持仓盈亏: 200.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-25, 账户权益: 10002605.13, 可用资金: 10000225.13, 浮动盈亏: 1760.00, 持仓盈亏: 620.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-28, 账户权益: 10001532.15, 可用资金: 10001532.15, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -1070.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-29, 账户权益: 10001799.18, 可用资金: 9999419.18, 浮动盈亏: 270.00, 持仓盈亏: 270.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-07-30, 账户权益: 10001266.20, 可用资金: 10001266.20, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -530.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-07-31, 账户权益: 10001993.23, 可用资金: 9999613.23, 浮动盈亏: 730.00, 持仓盈亏: 730.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-01, 账户权益: 10002013.23, 可用资金: 9999633.23, 浮动盈亏: 750.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-04, 账户权益: 10002003.23, 可用资金: 9999623.23, 浮动盈亏: 740.00, 持仓盈亏: -10.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-05, 账户权益: 10001713.23, 可用资金: 9999333.23, 浮动盈亏: 450.00, 持仓盈亏: -290.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-06, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -10.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-07, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-08, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-11, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-12, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-13, 账户权益: 10001700.25, 可用资金: 10001700.25, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-14, 账户权益: 10001897.28, 可用资金: 9999517.28, 浮动盈亏: 200.00, 持仓盈亏: 200.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 2.98, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-15, 账户权益: 10001907.28, 可用资金: 9999527.28, 浮动盈亏: 210.00, 持仓盈亏: 10.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-18, 账户权益: 10002237.28, 可用资金: 9999857.28, 浮动盈亏: 540.00, 持仓盈亏: 330.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-19, 账户权益: 10002527.28, 可用资金: 10000147.28, 浮动盈亏: 830.00, 持仓盈亏: 290.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-20, 账户权益: 10002467.28, 可用资金: 10000087.28, 浮动盈亏: 770.00, 持仓盈亏: -60.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-21, 账户权益: 10002577.28, 可用资金: 10000197.28, 浮动盈亏: 880.00, 持仓盈亏: 110.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-22, 账户权益: 10002597.28, 可用资金: 10000217.28, 浮动盈亏: 900.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2380.00, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-08-25, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -250.00, 市值: 0.00, 保证金: 0.00, 手续费: 2.98, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-26, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-27, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-28, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-08-29, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-01, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-02, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-03, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-04, 账户权益: 10002344.30, 可用资金: 10002344.30, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-05, 账户权益: 10002361.25, 可用资金: 9999924.45, 浮动盈亏: 20.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 3.05, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-08, 账户权益: 10002138.21, 可用资金: 10002138.21, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -220.00, 市值: 0.00, 保证金: 0.00, 手续费: 3.05, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-09, 账户权益: 10002138.21, 可用资金: 10002138.21, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-10, 账户权益: 10001972.12, 可用资金: 10001972.12, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -160.00, 市值: 0.00, 保证金: 0.00, 手续费: 6.09, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-11, 账户权益: 10001972.12, 可用资金: 10001972.12, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-12, 账户权益: 10002089.07, 可用资金: 9999652.27, 浮动盈亏: 120.00, 持仓盈亏: 120.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 3.05, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-15, 账户权益: 10002179.07, 可用资金: 9999742.27, 浮动盈亏: 210.00, 持仓盈亏: 90.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-16, 账户权益: 10002479.07, 可用资金: 10000042.27, 浮动盈亏: 510.00, 持仓盈亏: 300.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-17, 账户权益: 10002499.07, 可用资金: 10000062.27, 浮动盈亏: 530.00, 持仓盈亏: 20.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-18, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: -340.00, 市值: 0.00, 保证金: 0.00, 手续费: 3.05, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-19, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-22, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-23, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-24, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-25, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-26, 账户权益: 10002156.02, 可用资金: 10002156.02, 浮动盈亏: 0.00, 持仓盈亏: 0.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 0.00, 手续费: 0.00, 风险度: 0.00%\n",
" INFO - 日期: 2025-09-29, 账户权益: 10002312.98, 可用资金: 9999876.18, 浮动盈亏: 160.00, 持仓盈亏: 160.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 3.05, 风险度: 0.02%\n",
" INFO - 日期: 2025-09-30, 账户权益: 10002562.98, 可用资金: 10000126.18, 浮动盈亏: 410.00, 持仓盈亏: 250.00, 平仓盈亏: 0.00, 市值: 0.00, 保证金: 2436.80, 手续费: 0.00, 风险度: 0.02%\n",
" INFO - 胜率: 55.56%, 盈亏额比例: 3.33, 收益率: 0.03%, 年化收益率: 0.07%, 最大回撤: 0.01%, 年化夏普率: -63.7079,年化索提诺比率: -15.3458\n",
"回测结束:开始平仓所有剩余持仓...\n",
"TqsdkEngine: 回测运行完毕。\n",
"TqsdkEngine: API 已关闭。\n",
"\n",
"回测运行完毕。\n"
"初始化数据管理器...\n"
]
},
{
"ename": "KeyError",
"evalue": "'initial_capital'",
"ename": "FileNotFoundError",
"evalue": "数据文件未找到: /mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min15.csv",
"output_type": "error",
"traceback": [
"\u001B[31m---------------------------------------------------------------------------\u001B[39m",
"\u001B[31mKeyError\u001B[39m Traceback (most recent call last)",
"\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[4]\u001B[39m\u001B[32m, line 47\u001B[39m\n\u001B[32m 45\u001B[39m portfolio_snapshots = results[\u001B[33m\"\u001B[39m\u001B[33mportfolio_snapshots\u001B[39m\u001B[33m\"\u001B[39m]\n\u001B[32m 46\u001B[39m trade_history = results[\u001B[33m\"\u001B[39m\u001B[33mtrade_history\u001B[39m\u001B[33m\"\u001B[39m]\n\u001B[32m---> \u001B[39m\u001B[32m47\u001B[39m initial_capital_result = \u001B[43mresults\u001B[49m\u001B[43m[\u001B[49m\u001B[33;43m\"\u001B[39;49m\u001B[33;43minitial_capital\u001B[39;49m\u001B[33;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\n\u001B[32m 48\u001B[39m bars = results[\u001B[33m\"\u001B[39m\u001B[33mall_bars\u001B[39m\u001B[33m\"\u001B[39m]\n",
"\u001B[31mKeyError\u001B[39m: 'initial_capital'"
"\u001B[31mFileNotFoundError\u001B[39m Traceback (most recent call last)",
"\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[5]\u001B[39m\u001B[32m, line 7\u001B[39m\n\u001B[32m 5\u001B[39m \u001B[38;5;66;03m# --- 1. 初始化数据管理器 ---\u001B[39;00m\n\u001B[32m 6\u001B[39m \u001B[38;5;28mprint\u001B[39m(\u001B[33m\"\u001B[39m\u001B[33m初始化数据管理器...\u001B[39m\u001B[33m\"\u001B[39m)\n\u001B[32m----> \u001B[39m\u001B[32m7\u001B[39m data_manager = \u001B[43mDataManager\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfile_path\u001B[49m\u001B[43m=\u001B[49m\u001B[43mdata_file_path\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msymbol\u001B[49m\u001B[43m=\u001B[49m\u001B[43mglobal_config\u001B[49m\u001B[43m[\u001B[49m\u001B[33;43m'\u001B[39;49m\u001B[33;43msymbol\u001B[39;49m\u001B[33;43m'\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mstart_time\u001B[49m\u001B[43m=\u001B[49m\u001B[43mstart_time\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 8\u001B[39m \u001B[43m \u001B[49m\u001B[43mend_time\u001B[49m\u001B[43m=\u001B[49m\u001B[43mend_time\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 10\u001B[39m strategy_parameters = {\n\u001B[32m 11\u001B[39m \u001B[33m'\u001B[39m\u001B[33mmain_symbol\u001B[39m\u001B[33m'\u001B[39m: \u001B[33m'\u001B[39m\u001B[33mrb\u001B[39m\u001B[33m'\u001B[39m, \u001B[38;5;66;03m# <-- 替换为你的交易品种代码,例如 'GC=F' (黄金期货), 'ZC=F' (玉米期货)\u001B[39;00m\n\u001B[32m 12\u001B[39m \u001B[33m'\u001B[39m\u001B[33mtrade_volume\u001B[39m\u001B[33m'\u001B[39m: \u001B[32m1\u001B[39m,\n\u001B[32m (...)\u001B[39m\u001B[32m 15\u001B[39m \u001B[33m'\u001B[39m\u001B[33menable_log\u001B[39m\u001B[33m'\u001B[39m: \u001B[38;5;28;01mFalse\u001B[39;00m\n\u001B[32m 16\u001B[39m }\n\u001B[32m 20\u001B[39m \u001B[38;5;66;03m# --- 2. 初始化回测引擎并运行 ---\u001B[39;00m\n",
"\u001B[36mFile \u001B[39m\u001B[32mD:\\PyProject\\NewQuant\\src\\data_manager.py:22\u001B[39m, in \u001B[36mDataManager.__init__\u001B[39m\u001B[34m(self, file_path, symbol, tz, start_time, end_time)\u001B[39m\n\u001B[32m 20\u001B[39m \u001B[38;5;28mself\u001B[39m.file_path = file_path\n\u001B[32m 21\u001B[39m \u001B[38;5;28mself\u001B[39m.tz = tz\n\u001B[32m---> \u001B[39m\u001B[32m22\u001B[39m \u001B[38;5;28mself\u001B[39m.raw_df = \u001B[43mload_raw_data\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m.\u001B[49m\u001B[43mfile_path\u001B[49m\u001B[43m)\u001B[49m \u001B[38;5;66;03m# 调用函数式加载数据\u001B[39;00m\n\u001B[32m 24\u001B[39m \u001B[38;5;28mself\u001B[39m.start_time = start_time\n\u001B[32m 25\u001B[39m \u001B[38;5;28mself\u001B[39m.end_time = end_time\n",
"\u001B[36mFile \u001B[39m\u001B[32mD:\\PyProject\\NewQuant\\src\\data_processing.py:28\u001B[39m, in \u001B[36mload_raw_data\u001B[39m\u001B[34m(file_path)\u001B[39m\n\u001B[32m 11\u001B[39m \u001B[38;5;250m\u001B[39m\u001B[33;03m\"\"\"\u001B[39;00m\n\u001B[32m 12\u001B[39m \u001B[33;03m从 CSV 文件加载原始数据,并进行初步的数据类型处理。\u001B[39;00m\n\u001B[32m 13\u001B[39m \u001B[33;03m假设 datetime 列已经是北京时间,无需额外时区转换或本地化。\u001B[39;00m\n\u001B[32m (...)\u001B[39m\u001B[32m 25\u001B[39m \u001B[33;03m KeyError: 如果CSV中缺少必要的列。\u001B[39;00m\n\u001B[32m 26\u001B[39m \u001B[33;03m\"\"\"\u001B[39;00m\n\u001B[32m 27\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m os.path.exists(file_path):\n\u001B[32m---> \u001B[39m\u001B[32m28\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mFileNotFoundError\u001B[39;00m(\u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33m数据文件未找到: \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mfile_path\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m\"\u001B[39m)\n\u001B[32m 30\u001B[39m \u001B[38;5;66;03m# 定义期望的列名,用于检查和选择\u001B[39;00m\n\u001B[32m 31\u001B[39m expected_cols = [\u001B[33m'\u001B[39m\u001B[33mdatetime\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mopen\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mhigh\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mlow\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mclose\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mvolume\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mopen_oi\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mclose_oi\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33munderlying_symbol\u001B[39m\u001B[33m'\u001B[39m]\n",
"\u001B[31mFileNotFoundError\u001B[39m: 数据文件未找到: /mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min15.csv"
]
}
],
"execution_count": 4
"execution_count": 5
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-19T06:30:51.251633200Z",
"end_time": "2025-12-17T13:57:22.378533300Z",
"start_time": "2025-10-17T14:27:25.613908Z"
}
},

File diff suppressed because one or more lines are too long

View File

@@ -57,8 +57,10 @@ def run_single_backtest(
if 'kalman_measurement_noise' in common_config:
strategy_parameters['kalman_measurement_noise'] = common_config['kalman_measurement_noise']
if 'entry_threshold_atr' in common_config:
if 'entry_threshold_atr' in common_config and 'entry_threshold_atr' not in strategy_parameters:
strategy_parameters['entry_threshold_atr'] = common_config['entry_threshold_atr']
elif 'entry_threshold_atr' not in strategy_parameters:
strategy_parameters['entry_threshold_atr'] = strategy_parameters['structural_stop_atr_multiplier']
# 打印当前进程正在处理的组合信息
# 注意:多进程打印会交错显示

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,221 @@
import numpy as np
import math
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
class ITrendStrategy(Strategy):
"""
【Ehlers 瞬时趋势线策略 (期货实战版)】
针对期货日内交易优化:
1. 【跳数止损】:使用固定的 min_tick 跳数作为止损,替代百分比。
2. 【价格对齐】:所有计算出的挂单价格,强制对齐到 min_tick 的整数倍。
3. 【无反手】:止损或信号反转仅平仓。
参数含义变更:
- min_tick: 合约最小变动价位 (如 IF为0.2, RB为1)
- stop_loss_ticks: 止损跳数 (如 50跳)
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【合约规格参数】 ---
min_tick: float = 1.0, # 核心:必须根据品种设置 (例如 0.2, 1.0, 5.0)
# --- 【策略参数】 ---
length: int = 20, # 趋势线周期
range_fraction: float = 0.35, # 入场回调系数 (Range的比例)
stop_loss_ticks: int = 30, # 【新】硬止损跳数 (替代百分比)
# --- 【其他】 ---
order_direction: Optional[List[str]] = None,
indicator: Indicator = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
self.trade_volume = trade_volume
self.min_tick = min_tick
self.rng_frac = range_fraction
self.stop_ticks = stop_loss_ticks # 止损跳数
self.order_direction = order_direction
self.alpha = 2.0 / (length + 1.0)
self.indicator = indicator
# 历史数据缓存
self._mid_price_history = deque(maxlen=50)
self._itrend_history = deque(maxlen=50)
self._trigger_history = deque(maxlen=50)
self.bar_count = 0
self.order_id_counter = 0
def round_to_tick(self, price: float) -> float:
"""辅助函数:将价格对齐到最小变动价位"""
if self.min_tick <= 0: return price
return round(price / self.min_tick) * self.min_tick
def on_init(self):
super().on_init()
def on_rollover(self, old_symbol: str, new_symbol: str):
self.log(f"合约换月: {old_symbol} -> {new_symbol},重置状态。")
self._mid_price_history.clear()
self._itrend_history.clear()
self._trigger_history.clear()
self.bar_count = 0
self.symbol = new_symbol
self.cancel_all_pending_orders(old_symbol)
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bars = self.get_bar_history()
if len(bars) < 2: return
self.cancel_all_pending_orders(symbol)
prev_bar = bars[-1]
# --- 1. 指标计算 ---
mid_price = (prev_bar.high + prev_bar.low) / 2.0
self._mid_price_history.append(mid_price)
self.bar_count += 1
if len(self._mid_price_history) < 3:
self._itrend_history.append(mid_price)
self._trigger_history.append(mid_price)
return
price = list(self._mid_price_history)
itrend_prev = list(self._itrend_history)
current_itrend = 0.0
if self.bar_count < 7:
current_itrend = (price[-1] + 2 * price[-2] + price[-3]) / 4.0
else:
alpha = self.alpha
a2 = alpha * alpha
current_itrend = (alpha - a2 / 4) * price[-1] + (a2 / 2) * price[-2] - (alpha - 0.75 * a2) * price[-3] + \
2 * (1 - alpha) * itrend_prev[-1] - (1 - alpha) ** 2 * itrend_prev[-2]
self._itrend_history.append(current_itrend)
if len(self._itrend_history) < 3:
current_trigger = current_itrend
else:
current_trigger = 2.0 * current_itrend - self._itrend_history[-3]
self._trigger_history.append(current_trigger)
# --- 2. 交易决策 ---
if len(self._trigger_history) < 2: return
curr_trig = self._trigger_history[-1]
prev_trig = self._trigger_history[-2]
curr_itrend = self._itrend_history[-1]
prev_itrend = self._itrend_history[-2]
is_bullish = (prev_trig <= prev_itrend) and (curr_trig > curr_itrend)
is_bearish = (prev_trig >= prev_itrend) and (curr_trig < curr_itrend)
position_volume = self.get_current_positions().get(self.symbol, 0)
prev_range = prev_bar.high - prev_bar.low
# === 分支 A: 持仓管理 (止损/平仓) ===
if position_volume != 0:
entry_price = self.get_average_position_price(self.symbol)
# --- A1. 强制跳数止损 (Tick Stop) ---
# 计算止损价差绝对值
stop_diff = self.stop_ticks * self.min_tick
self.log(f'Holding {position_volume} position volume'
f'is_bullish={is_bullish}, is_bearish={is_bearish}'
f'entry_price={entry_price}, stop_diff={stop_diff}')
if position_volume > 0: # 多头
# 止损价 = 开仓价 - 止损点数
stop_price = entry_price - stop_diff
# 对齐一下(虽然entry_price理论上已对齐但为了保险)
stop_price = self.round_to_tick(stop_price)
if prev_bar.close < stop_price:
self.log(
f"LONG STOP TRIGGERED. Close:{prev_bar.close} < Stop:{stop_price} (-{self.stop_ticks} ticks)")
self.send_market_order("CLOSE_LONG", abs(position_volume), "CLOSE")
return
elif position_volume < 0: # 空头
# 止损价 = 开仓价 + 止损点数
stop_price = entry_price + stop_diff
stop_price = self.round_to_tick(stop_price)
if prev_bar.close > stop_price:
self.log(
f"SHORT STOP TRIGGERED. Close:{prev_bar.close} > Stop:{stop_price} (+{self.stop_ticks} ticks)")
self.send_market_order("CLOSE_SHORT", abs(position_volume), "CLOSE")
return
# --- A2. 信号平仓 ---
if position_volume > 0 and is_bearish:
self.send_market_order("CLOSE_LONG", abs(position_volume), "CLOSE")
return
if position_volume < 0 and is_bullish:
self.send_market_order("CLOSE_SHORT", abs(position_volume), "CLOSE")
return
# === 分支 B: 开仓管理 ===
else: # position_volume == 0
# 计算回调距离 (Range Fraction)
# 例如 Range=10, Frac=0.35 -> 3.5 -> 对齐到tick
raw_offset = self.rng_frac
offset_price = self.round_to_tick(raw_offset)
self.log(f'RANDOM OFFSET: {offset_price}, is_bullish={is_bullish}, is_bearish={is_bearish}')
# 开多
if is_bullish and "BUY" in self.order_direction and (self.indicator is None or self.indicator.is_condition_met(*self.get_indicator_tuple())):
# 挂单价 = Open - 回调点数
limit_price = prev_bar.close - offset_price
limit_price = self.round_to_tick(limit_price) # 再次确保对齐
self.send_limit_order(limit_price, "BUY", self.trade_volume, "OPEN")
self.log(f"Signal BUY. Open:{open_price} - Offset:{offset_price} = Limit:{limit_price}")
# 开空
elif is_bearish and "SELL" in self.order_direction and (self.indicator is None or self.indicator.is_condition_met(*self.get_indicator_tuple())):
# 挂单价 = Open + 回调点数
limit_price = prev_bar.close + offset_price
limit_price = self.round_to_tick(limit_price)
self.send_limit_order(limit_price, "SELL", self.trade_volume, "OPEN")
self.log(f"Signal SELL. Open:{open_price} + Offset:{offset_price} = Limit:{limit_price}")
# --- 交易辅助函数 ---
def send_market_order(self, direction: str, volume: int, offset: str):
order_id = f"{self.symbol}_{direction}_MKT_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset)
self.send_order(order)
def send_limit_order(self, limit_price: float, direction: str, volume: int, offset: str):
order_id = f"{self.symbol}_{direction}_LMT_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="LIMIT",
submitted_time=self.get_current_time(), offset=offset, limit_price=limit_price)
self.send_order(order)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,160 @@
import numpy as np
import talib
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.strategies.base_strategy import Strategy
class KalmanMeanReversion(Strategy):
"""
改进版卡尔曼均值回归策略
1. 以 Slow Line 为止盈目标 (真正的均值回归)
2. 严格的 ATR 止损 (防止趋势爆发)
3. 时间衰减退出机制
4. 趋势斜率过滤
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
fast_sensitivity: float = 0.05, # 快线灵敏度
slow_sensitivity: float = 0.01, # 慢线灵敏度 (更平滑作为均值)
lookback_variance: int = 60,
atr_period: int = 20,
entry_threshold: float = 1.2, # 偏离多少个ATR入场
stop_loss_multiplier: float = 2.0, # 止损倍数
max_hold_bars: int = 30, # 最大持仓时间
indicator: Any = None,
):
super().__init__(context, main_symbol, enable_log)
self.trade_volume = trade_volume
self.fast_sensitivity = fast_sensitivity
self.slow_sensitivity = slow_sensitivity
self.lookback_variance = lookback_variance
self.atr_period = atr_period
self.entry_threshold = entry_threshold
self.stop_loss_multiplier = stop_loss_multiplier
self.max_hold_bars = max_hold_bars
self.indicator = indicator
# 状态变量
self.kf_fast = {'x': 0.0, 'P': 1.0}
self.kf_slow = {'x': 0.0, 'P': 1.0}
self.kalman_initialized = False
# 记录持仓信息
self.entry_price = 0.0
self.entry_bar_count = 0
self.order_id_counter = 0
def _update_kalman(self, state: dict, measurement: float, Q: float, R: float) -> float:
"""递归卡尔曼滤波更新"""
p_minus = state['P'] + Q
k_gain = p_minus / (p_minus + R)
state['x'] = state['x'] + k_gain * (measurement - state['x'])
state['P'] = (1 - k_gain) * p_minus
return state['x']
def on_open_bar(self, open_price: float, symbol: str):
bar_history = self.get_bar_history()
if len(bar_history) < 100:
return
self.cancel_all_pending_orders()
closes = np.array([b.close for b in bar_history], dtype=float)
last_price = closes[-1]
# 1. 计算自适应噪声 (基于滚动方差)
rolling_var = np.var(closes[-self.lookback_variance:])
r_base = rolling_var if rolling_var > 0 else 1.0
# 2. 初始化或更新卡尔曼滤波器
if not self.kalman_initialized:
self.kf_fast['x'] = self.kf_slow['x'] = last_price
self.kalman_initialized = True
return
# 快线追踪价格,慢线代表平衡位置
fast_line = self._update_kalman(self.kf_fast, last_price, r_base * self.fast_sensitivity, r_base)
slow_line = self._update_kalman(self.kf_slow, last_price, r_base * self.slow_sensitivity, r_base * 10.0)
# 3. 计算 ATR 和 偏离度
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
if atr <= 0: return
# 计算价格偏离慢线的程度 (Z-Score 的变体)
diff_in_atr = (last_price - slow_line) / atr
# 4. 仓位逻辑
pos = self.get_current_positions().get(symbol, 0)
if pos == 0:
# --- 入场逻辑 ---
# 只有在外部指标允许且偏离度足够大时入场
can_trade = self.indicator is None or self.indicator.is_condition_met(*self.get_indicator_tuple())
if can_trade:
if diff_in_atr > self.entry_threshold:
# 超买,做空
self.execute_order(symbol, "SELL", "OPEN", last_price)
elif diff_in_atr < -self.entry_threshold:
# 超卖,做多
self.execute_order(symbol, "BUY", "OPEN", last_price)
else:
# --- 出场逻辑 ---
self.entry_bar_count += 1
is_long = pos > 0
should_close = False
exit_reason = ""
# A. 均值回归止盈:触碰或穿过慢线
if is_long and last_price >= slow_line:
should_close = True
exit_reason = "Take Profit: Reached Mean"
elif not is_long and last_price <= slow_line:
should_close = True
exit_reason = "Take Profit: Reached Mean"
# B. 严谨止损:背离程度进一步扩大
elif is_long and last_price < self.entry_price - self.stop_loss_multiplier * atr:
should_close = True
exit_reason = "Stop Loss: Deviation Too Large"
elif not is_long and last_price > self.entry_price + self.stop_loss_multiplier * atr:
should_close = True
exit_reason = "Stop Loss: Deviation Too Large"
# C. 时间退出:久攻不下,由于均值回归的时效性,超时即走
elif self.entry_bar_count >= self.max_hold_bars:
should_close = True
exit_reason = "Time Exit: Holding Too Long"
if should_close:
direction = "CLOSE_LONG" if is_long else "CLOSE_SHORT"
self.log(f"EXIT {direction}: Price={last_price}, Reason={exit_reason}")
self.execute_order(symbol, direction, "CLOSE", last_price)
def execute_order(self, symbol, direction, offset, price):
"""执行订单并更新状态"""
if offset == "OPEN":
self.entry_price = price
self.entry_bar_count = 0
order_id = f"{symbol}_{direction}_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(
id=order_id,
symbol=symbol,
direction=direction,
volume=self.trade_volume,
price_type="MARKET",
offset=offset
)
self.send_order(order)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,190 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.strategies.base_strategy import Strategy
import numpy as np
import talib
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.strategies.base_strategy import Strategy
class KalmanTrendFollower(Strategy):
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 显著降低灵敏度,过滤日内杂波 ---
fast_sensitivity: float = 0.05, # 调低:让快线也变得稳重
slow_sensitivity: float = 0.005, # 极低:慢线只代表大趋势方向
lookback_variance: int = 60, # 增加窗口,计算更稳定的市场噪声
# --- 趋势跟踪参数 ---
atr_period: int = 23,
entry_threshold: float = 0.5, # 差值需超过0.5倍ATR才考虑入场
trailing_stop_multiplier: float = 4.0, # 关键4倍ATR跟踪止损给趋势留足空间
structural_stop_multiplier: float = 1.0, # 价格破位慢线多少ATR才出场
indicator: Indicator = None,
):
super().__init__(context, main_symbol, enable_log)
self.trade_volume = trade_volume
# 参数初始化
self.fast_sensitivity = fast_sensitivity
self.slow_sensitivity = slow_sensitivity
self.lookback_variance = lookback_variance
self.atr_period = atr_period
self.entry_threshold = entry_threshold
self.trailing_stop_multiplier = trailing_stop_multiplier
self.structural_stop_multiplier = structural_stop_multiplier
self.indicator = indicator
# 状态变量
self.kf_fast = {'x': 0.0, 'P': 1.0}
self.kf_slow = {'x': 0.0, 'P': 1.0}
self.kalman_initialized = False
self.position_meta: Dict[str, Any] = {}
self.order_id_counter = 0
def _update_kalman(self, state: dict, measurement: float, Q: float, R: float) -> float:
p_minus = state['P'] + Q
k_gain = p_minus / (p_minus + R)
state['x'] = state['x'] + k_gain * (measurement - state['x'])
state['P'] = (1 - k_gain) * p_minus
return state['x']
def on_open_bar(self, open_price: float, symbol: str):
bar_history = self.get_bar_history()
if len(bar_history) < 100: return
self.cancel_all_pending_orders()
closes = np.array([b.close for b in bar_history], dtype=float)
last_price = closes[-1]
# 1. 动态计算卡尔曼参数
# 增加基础噪声 R使曲线更平滑
rolling_var = np.var(closes[-self.lookback_variance:])
r_base = rolling_var if rolling_var > 0 else 1.0
# 计算快慢线
if not self.kalman_initialized:
self.kf_fast['x'] = self.kf_slow['x'] = last_price
self.kalman_initialized = True
return
fast_line = self._update_kalman(self.kf_fast, last_price, r_base * self.fast_sensitivity, r_base)
slow_line = self._update_kalman(self.kf_slow, last_price, r_base * self.slow_sensitivity, r_base * 5.0)
# 2. 计算 ATR 和 趋势指标
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
diff = fast_line - slow_line
diff_in_atr = diff / atr if atr > 0 else 0
# 3. 仓位管理逻辑
pos = self.get_current_positions().get(symbol, 0)
if pos == 0 and (self.indicator is None or self.indicator.is_condition_met(*self.get_indicator_tuple())):
# --- 入场逻辑:必须形成明显的发散 ---
if diff_in_atr > self.entry_threshold:
self.open_trade(symbol, "BUY", open_price, atr, slow_line)
elif diff_in_atr < -self.entry_threshold:
self.open_trade(symbol, "SELL", open_price, atr, slow_line)
else:
# --- 出场逻辑:保护肥尾收益 ---
self.manage_exit(symbol, pos, last_price, atr, slow_line)
def open_trade(self, symbol, direction, price, atr, slow_line):
# 记录入场时的最高/最低价,用于动态跟踪止损
meta = {
'entry_price': price,
'extreme_price': price, # 记录持仓期间到达过的最高(多头)或最低(空头)
'direction': direction,
'initial_atr': atr
}
self.send_limit_order(symbol, direction, price, self.trade_volume, "OPEN", meta)
self.log(f"TREND ENTRY {direction}: Price={price}, ATR={atr:.2f}")
def manage_exit(self, symbol, pos, price, atr, slow_line):
meta = self.position_meta.get(symbol)
if not meta: return
is_long = pos > 0
should_close = False
# 更新持仓期间的极端价格(用于计算吊灯止损)
if is_long:
meta['extreme_price'] = max(meta['extreme_price'], price)
# 吊灯止损位:最高价回落 N 倍 ATR
chandelier_stop = meta['extreme_price'] - self.trailing_stop_multiplier * atr
# 结构止损位:跌破慢速趋势线一定距离
structural_stop = slow_line - self.structural_stop_multiplier * atr
# 综合取较严的价格作为保护,但不轻易离场
if price < max(chandelier_stop, structural_stop):
should_close = True
exit_reason = "Trailing/Structural Break"
else:
meta['extreme_price'] = min(meta['extreme_price'], price)
chandelier_stop = meta['extreme_price'] + self.trailing_stop_multiplier * atr
structural_stop = slow_line + self.structural_stop_multiplier * atr
if price > min(chandelier_stop, structural_stop):
should_close = True
exit_reason = "Trailing/Structural Break"
if should_close:
direction = "CLOSE_LONG" if is_long else "CLOSE_SHORT"
self.log(f"EXIT {direction}: Price={price}, Reason={exit_reason}")
self.close_position(symbol, direction, abs(pos))
# (底层 send_market_order / close_position 同前,注意更新 state 时保留 meta['extreme_price'])
# --- 底层封装 ---
def send_market_order(self, symbol, direction, volume, offset, meta=None):
if offset == "OPEN": self.position_meta[symbol] = meta
order_id = f"{symbol}_{direction}_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=symbol, direction=direction,
volume=volume, price_type="MARKET", offset=offset)
self.send_order(order)
self.save_state(self.position_meta)
def send_limit_order(self, symbol, direction, price, volume, offset, meta=None):
if offset == "OPEN": self.position_meta[symbol] = meta
order_id = f"{symbol}_{direction}_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=symbol, direction=direction,
volume=volume, price_type="LIMIT", offset=offset, limit_price=price)
self.send_order(order)
self.save_state(self.position_meta)
def close_position(self, symbol, direction, volume):
order_id = f"{symbol}_{direction}_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=symbol, direction=direction,
volume=volume, price_type="MARKET", offset="CLOSE")
self.send_order(order)
self.position_meta.pop(symbol, None)
self.save_state(self.position_meta)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
import multiprocessing
from typing import Tuple, Dict, Any, Optional
from src.analysis.result_analyzer import ResultAnalyzer
from src.backtest_engine import BacktestEngine
from src.data_manager import DataManager
# --- 单个回测任务函数 ---
# 这个函数将在每个独立的进程中运行,因此它必须是自包含的
def run_single_backtest(
combination: Tuple[float, float], # 传入当前参数组合
common_config: Dict[str, Any] # 传入公共配置 (如数据路径, 初始资金等)
) -> Optional[Dict[str, Any]]:
"""
运行单个参数组合的回测任务。
此函数将在一个独立的进程中执行。
"""
p1_value, p2_value = combination
# 从 common_config 中获取必要的配置
symbol = common_config['symbol']
data_path = common_config['data_path']
initial_capital = common_config['initial_capital']
slippage_rate = common_config['slippage_rate']
commission_rate = common_config['commission_rate']
start_time = common_config['start_time']
end_time = common_config['end_time']
roll_over_mode = common_config['roll_over_mode']
# bar_duration_seconds = common_config['bar_duration_seconds'] # 如果DataManager需要可以再传
param1_name = common_config['param1_name']
param2_name = common_config['param2_name']
# 每个进程内部独立初始化 DataManager 和 BacktestEngine
# 确保每个进程有自己的数据副本和模拟状态
data_manager = DataManager(
file_path=data_path,
symbol=symbol,
# bar_duration_seconds=bar_duration_seconds, # 如果DataManager需要根据数据文件路径推断或者额外参数传入
# start_date=start_time.date(), # DataManager 现在通过 file_path 和 symbol 处理数据
# end_date=end_time.date(),
)
# data_manager.load_data() # DataManager 内部加载数据
strategy_parameters = {
'main_symbol': common_config['main_symbol'],
'trade_volume': 1,
param1_name: p1_value, # 15分钟扫荡K线下影线占其总范围的最小比例。
param2_name: p2_value, # 15分钟限价单的入场点位于扫荡K线低点到收盘价的斐波那契回撤比例。
# 'order_direction': common_config['order_direction'],
'enable_log': False, # 建议在调试和测试时开启日志
}
if 'order_direction' in common_config:
strategy_parameters['order_direction'] = common_config['order_direction']
if 'strategy_mode' in common_config:
strategy_parameters['strategy_mode'] = common_config['strategy_mode']
if 'kalman_measurement_noise' in common_config:
strategy_parameters['kalman_measurement_noise'] = common_config['kalman_measurement_noise']
# if 'entry_threshold_atr' in common_config and 'entry_threshold_atr' not in strategy_parameters:
# strategy_parameters['entry_threshold_atr'] = common_config['entry_threshold_atr']
# elif 'entry_threshold_atr' not in strategy_parameters:
# strategy_parameters['entry_threshold_atr'] = strategy_parameters['structural_stop_atr_multiplier']
# 打印当前进程正在处理的组合信息
# 注意:多进程打印会交错显示
# print(f"--- 正在运行组合: {strategy_parameters} (PID: {multiprocessing.current_process().pid}) ---")
try:
# 初始化回测引擎
engine = BacktestEngine(
data_manager=data_manager,
strategy_class=common_config['strategy'],
strategy_params=strategy_parameters,
initial_capital=initial_capital,
slippage_rate=slippage_rate,
commission_rate=commission_rate,
roll_over_mode=True, # 保持换月模式
start_time=common_config['start_time'],
end_time=common_config['end_time']
)
# 运行回测,传入时间范围
engine.run_backtest()
# 获取回测结果并分析
results = engine.get_backtest_results()
portfolio_snapshots = results["portfolio_snapshots"]
trade_history = results["trade_history"]
bars = results["all_bars"]
initial_capital_result = results["initial_capital"]
if portfolio_snapshots:
analyzer = ResultAnalyzer(portfolio_snapshots, trade_history, bars, initial_capital_result)
# analyzer.generate_report()
# analyzer.plot_performance()
metrics = analyzer.calculate_all_metrics()
# 将当前组合的参数和性能指标存储起来
result_entry = {**strategy_parameters, **metrics}
return result_entry
else:
print(
f" 组合 {strategy_parameters} 没有生成投资组合快照,无法进行结果分析。(PID: {multiprocessing.current_process().pid})")
# 返回一个包含参数和默认0值的结果以便追踪失败组合
return {**strategy_parameters, "total_return": 0.0, "annualized_return": 0.0, "sharpe_ratio": 0.0,
"max_drawdown": 0.0, "error": "No portfolio snapshots"}
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(
f" 组合 {strategy_parameters} 运行失败: {e}\n{error_trace} (PID: {multiprocessing.current_process().pid})")
# 返回错误信息,以便后续处理
return {**strategy_parameters, "error": str(e), "traceback": error_trace}

File diff suppressed because one or more lines are too long

View File

@@ -97,6 +97,8 @@ class DualModeKalmanStrategy(Strategy):
bar_history = self.get_bar_history()
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
self.cancel_all_pending_orders(symbol)
# --- 通用数据计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,120 @@
import multiprocessing
from typing import Tuple, Dict, Any, Optional
from src.analysis.result_analyzer import ResultAnalyzer
from src.backtest_engine import BacktestEngine
from src.data_manager import DataManager
# --- 单个回测任务函数 ---
# 这个函数将在每个独立的进程中运行,因此它必须是自包含的
def run_single_backtest(
combination: Tuple[float, float], # 传入当前参数组合
common_config: Dict[str, Any] # 传入公共配置 (如数据路径, 初始资金等)
) -> Optional[Dict[str, Any]]:
"""
运行单个参数组合的回测任务。
此函数将在一个独立的进程中执行。
"""
p1_value, p2_value = combination
# 从 common_config 中获取必要的配置
symbol = common_config['symbol']
data_path = common_config['data_path']
initial_capital = common_config['initial_capital']
slippage_rate = common_config['slippage_rate']
commission_rate = common_config['commission_rate']
start_time = common_config['start_time']
end_time = common_config['end_time']
roll_over_mode = common_config['roll_over_mode']
# bar_duration_seconds = common_config['bar_duration_seconds'] # 如果DataManager需要可以再传
param1_name = common_config['param1_name']
param2_name = common_config['param2_name']
# 每个进程内部独立初始化 DataManager 和 BacktestEngine
# 确保每个进程有自己的数据副本和模拟状态
data_manager = DataManager(
file_path=data_path,
symbol=symbol,
# bar_duration_seconds=bar_duration_seconds, # 如果DataManager需要根据数据文件路径推断或者额外参数传入
# start_date=start_time.date(), # DataManager 现在通过 file_path 和 symbol 处理数据
# end_date=end_time.date(),
)
# data_manager.load_data() # DataManager 内部加载数据
strategy_parameters = {
'main_symbol': common_config['main_symbol'],
'trade_volume': 1,
param1_name: p1_value, # 15分钟扫荡K线下影线占其总范围的最小比例。
param2_name: p2_value, # 15分钟限价单的入场点位于扫荡K线低点到收盘价的斐波那契回撤比例。
# 'order_direction': common_config['order_direction'],
'enable_log': False, # 建议在调试和测试时开启日志
}
# if 'order_direction' in common_config:
# strategy_parameters['order_direction'] = common_config['order_direction']
#
# if 'strategy_mode' in common_config:
# strategy_parameters['strategy_mode'] = common_config['strategy_mode']
#
# if 'kalman_measurement_noise' in common_config:
# strategy_parameters['kalman_measurement_noise'] = common_config['kalman_measurement_noise']
# if 'entry_threshold_atr' in common_config and 'entry_threshold_atr' not in strategy_parameters:
# strategy_parameters['entry_threshold_atr'] = common_config['entry_threshold_atr']
# elif 'entry_threshold_atr' not in strategy_parameters:
# strategy_parameters['entry_threshold_atr'] = strategy_parameters['structural_stop_atr_multiplier']
# 打印当前进程正在处理的组合信息
# 注意:多进程打印会交错显示
# print(f"--- 正在运行组合: {strategy_parameters} (PID: {multiprocessing.current_process().pid}) ---")
for key, value in common_config['strategy_params'].items():
strategy_parameters[key] = value
try:
# 初始化回测引擎
engine = BacktestEngine(
data_manager=data_manager,
strategy_class=common_config['strategy'],
strategy_params=strategy_parameters,
initial_capital=initial_capital,
slippage_rate=slippage_rate,
commission_rate=commission_rate,
roll_over_mode=True, # 保持换月模式
start_time=common_config['start_time'],
end_time=common_config['end_time']
)
# 运行回测,传入时间范围
engine.run_backtest()
# 获取回测结果并分析
results = engine.get_backtest_results()
portfolio_snapshots = results["portfolio_snapshots"]
trade_history = results["trade_history"]
bars = results["all_bars"]
initial_capital_result = results["initial_capital"]
if portfolio_snapshots:
analyzer = ResultAnalyzer(portfolio_snapshots, trade_history, bars, initial_capital_result)
# analyzer.generate_report()
# analyzer.plot_performance()
metrics = analyzer.calculate_all_metrics()
# 将当前组合的参数和性能指标存储起来
result_entry = {**strategy_parameters, **metrics}
return result_entry
else:
print(
f" 组合 {strategy_parameters} 没有生成投资组合快照,无法进行结果分析。(PID: {multiprocessing.current_process().pid})")
# 返回一个包含参数和默认0值的结果以便追踪失败组合
return {**strategy_parameters, "total_return": 0.0, "annualized_return": 0.0, "sharpe_ratio": 0.0,
"max_drawdown": 0.0, "error": "No portfolio snapshots"}
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(
f" 组合 {strategy_parameters} 运行失败: {e}\n{error_trace} (PID: {multiprocessing.current_process().pid})")
# 返回错误信息,以便后续处理
return {**strategy_parameters, "error": str(e), "traceback": error_trace}