From b322aaa4213ae6f0fd875695d7a62a5a1b7d1fd8 Mon Sep 17 00:00:00 2001 From: blade <8019068@qq.com> Date: Thu, 21 Aug 2025 18:37:33 +0800 Subject: [PATCH] optimize statistics --- .../mean_reversion_sandbox.cpython-312.pyc | Bin 21869 -> 23132 bytes core/trade/mean_reversion_sandbox.py | 109 ++++++++++++++---- trade_sandbox_main.py | 5 +- 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/core/trade/__pycache__/mean_reversion_sandbox.cpython-312.pyc b/core/trade/__pycache__/mean_reversion_sandbox.cpython-312.pyc index 88faf8a6c0abd143f64f3b101c123d431305a123..63c3cb6a7a0329e07c6ff821f680f6e556bfcddf 100644 GIT binary patch delta 3982 zcma)8e^3M7j(~ ztMODdPYn#F*;4BrfEj5vGH=X9os0{nRIgg=#S~NoqoT23!YYI zx_^B3^ZoJmy?vkezVH3z4RY=XF@I(<2@E`;Z(e`&i0@rkuWSzb`?+~VF`mgRJd_`H zmIWKXayTQEbz_!8hHzTN*EUCF;nEW;!V6o2-+84qY%dSly@N#e5ZglZ-s_$8#lmcR zO7>MY%VfTidK;ZAoNpb+pXou^0MNzJzZHhbkLanQwuB9<7u@PYPZ+ys zxcEw$my?RI)y?gtUhw&0IWkJVRE{YJ0S4#Njjc|qD>3mLtRw#@b(U0eF&$&HgjSWT zV#y?(D6Ju5^m1uAIY6CdPVzpjEZae1OK-k@F&G%0e(T`$uKm+5AGq-0 z(GDeLpa7y5KG+TJ%SX3g{&>fQW6xZk+&LXOIQ{l}KrX(&{lc-gE_}3W`h|DmpG$!T z)BE1IeEj&uz&nW_1R?SvCLIHa@>|yR4*2}ie)PNo;MGgTD2Fsbzgtn1gB!{}(A)3V zgHM&z?T*XzD!2wq6qzO~Z2SW#(^WcE@w^bp$UamaPA&=XR9rZBzRMG63N9L18S$(D zvN&wajM&lxt&#Ms03X;gVvab=fjkk`r9~_$0aIvn;c7M=M-=6JlC)9s^6{lJf#Vvd z+QlCJHSD`o>HFF06w>?Z+QsBy0Ypc5bPbVo*UZ9Lp13e*QH?YCe?+Dygw$FhvvB?=!I0qb z$LCz5D9gc|@(IdnQ94jqhwu2V2PMk%2|HeZCh^AlTN)b_-Hlrq$pejQ|Bpt>8oSL% zyr$6g8WWp_WgxC9$|!w_faT!1x(2w%EUnA6hR}5c;XQ<-bW2^;U8j-GZd&Oqn72wb z1ya=wmmf&b%AKH<#dg+G6}D6zt3H)oLb5`WxwCtxDh6ohr&-p z9j=@{PSnhBS@TqjoFr>{Iw2YIq|rx1)CWJvBea+romaJ+$Kc_ zo^C@rQ8T26FDW<5ApwRdC}*FbL=Cb**7Y>fe>LRV(i6uvr0;8%8G68Fr}GQ~{ZE6( zm2pcv2PWRBY>LdDx%h=z*DV=PgQ0J|#NBpsvJ(H=n;m+3=_TU$3HtF}w(bLO{=)ad`z>OfSX^>Y6<0KtxDt%blQ(mp)fv+ZP znb|W?`V0cm)UHQ;T|H|b^Yu&JUH-KjxA>&Bj|^<}8WWD59{j?u5paak=QM43sly0# z_Fx1OKO4hz&GLHfY215REdA;79K($>F+xu*uXN;jgH2;k_G^V4AMBtm5MpgtoS3k|^EKTFR!dp?$3<*^)oNU;P<%mChr}LiQ(Qc7OrA z3^X_12RD(6yI$;&zNXLIzkt7pIn(r$`yXWIvo!O86TZXfS%V+~MEQ1~)D3qHyxvKt z(4!H%yZR-(M@X+BC{=9=Sr{RT@D0E?BUK<*ZiiN62sk|d2D+rpJ>1B08IjcVSdv2A zxhg4NsFHh@sANU+H6ZQSFAXlOFd?fG4L{-X!zy5C-1$6Jhq5lY+Eqw5)|xa~ZRVz!MVDiYFrLB#i`qG8_L5Nn!C}@3t>o5rOa p%27NF&=`Hb{ado1ZtLhI*XbWS(ukh^vqQA>=}5{|22q*N{{qiE2-E-o delta 2714 zcmZuy4Qx}_6@K^G@A)sWo%laaVml^@O$>>hkd$Elf)^SBSqUUG6UeZ85aR;sGV6g0-nx~fvEP*sHrIuV*}UItn=jcwf+^OkATG>z@t=NE9; zp5)KxeCND#?>X<>b8lWGe|m@Lua=f-8GHh=wu z0Mk`o1v&aLY;i5si@e}TtA&~{t~TO?Cf*8fyUf)hCwLd=bV-67y_ePsdIvfoy95%| zo{3ue3bIC-t&{a~S+AFMv#k4M-74#TS+~pjQdzH%^#)lF$oeu_UtZ9)LZeJn!nnIq zpLF7Oqz89c2bbMdtDDELGD6E)_CCKtdPOa)pfQBjSOvo@DnL=zSS1^2g9(opahJzK z9+Bin;4M$kNsj(q$s~1!xL5_%SyGUNR`|0gs3=XBLa@eGw(?y2!s>)cm+r3zqBU7o z3M?LM3bo}hD5sIY-C9b0bQMgyau&(pA$X&v+l{V zS#46>BMe1_J;^-@J-Wf+`fm1~0)AZoKIwuZz6QnT?ycGPePu-PbCU;h{@U`t@Ql4K z*S7W*Lq<1kZ~3*!ll3hfBkFBz7t#3t)(`JAxL9>L+-hhvp2m=3jKY3|K_yfMrpeQA zH_$70A3RkOA%X03%XX25GfFW)T^GNUP$K8XM+{Np8wg`QI9_3aueb81ua2gogYgkz zI5`|2`4!p{$Dp&ZO);m_XTQ%{U(|sJ%3FKLy%9;!6}pDJ0>!6;5BLpA+k-`a^_S{@|ViqeBVtb;_JX@T&_O%s^#p zgCn6viWY)Cg<_mSW^B1!3+e%r#2A&B2!X|#3ZuBCB7Z5tdF)Dr! zURd4grDc0iF7aJdA8MDYC)TX<574?K(Rhf-TbUBWvlG)g*QwwQ-I9zc6P@<2o+`WH z@5q_#@a&q7W|RG2jS3O}Hbg=-z{-m6r+-#Y)V7S~R!S?dkOX)&VcjKy#>M)S`M?bC+#_f}m|T2uiwju?sv}o0k(53quGGh@2MwtEkU| z^fxX#ApheR!Far&!Pp#~RU@20*OBD3@^EWfkygfbqK{K*jyn-u2~tF0sdGvzk=oQFStX}v~b@yPmMH)k)GZy`SB!^X#Y2nL9DI)lzvREz%da;ui3& zdfP)DthAn5an;*lG=b@GdeR6;Nh*!`sYaQG-G#HD1Z73-Y4<@4VgF|8zXRKdW zG&H1n{wWPJ2`9UotXM8a8cGEyjHb`9+0@N1(q@|?(6G6N%)#TE+gPUyPH)~)Zmax_ z(K@L+uDf2{Gh=)#XLDv7w{V2TX8TXCGrPAZuE3%F)2)4cVXF}Att2zh)7!+}AlXB` zm)PYwT=om86=$hbO*eFtN@j#n4MKNh)9>GX=JxY1Zx?Stysw%6CH0wuOkY2Hhn(o2 zBIn>zzqOIxDdHyxvr1ujFe#p*`kNG_Lv@WxpHY~h@LS+_RFf#w?r=}+CR|0%>c}f3 z!R?iW&@)1RdGv}ODZ-ieB=OuZ0)A`{u!v%TWhIz{6_YpH!oiXJZ z{F^e()4zPc%vs(uQ_3>cak=YK*F3|aoDabCj^+t7XP&dzCU+j+nMc-~=NY~6*uas2 zyozdS#%P+?P$}T$9QPejE^?$=!I4UvNSJfvT$mF5Um7^?KjWXLaz0J$oF!8u5e$m? l7CQ&ueEbf19TEdO$z8ZUU?&RrVn8s&RK)UEhN3h>{|l52k;VW3 diff --git a/core/trade/mean_reversion_sandbox.py b/core/trade/mean_reversion_sandbox.py index bfd0561..547c479 100644 --- a/core/trade/mean_reversion_sandbox.py +++ b/core/trade/mean_reversion_sandbox.py @@ -45,7 +45,7 @@ class MeanReversionSandbox: desc_dict = { "买入": [ "1. 窗口周期为100, 即100个K线", - "2. 当前close_10_low为1, 即当前收盘价在窗口周期的10分位以下", + "2. 满足close_10_low为1, 即当前收盘价在窗口周期的10分位以下", "3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量", "4. 当前K线为阳线, 即close > open或者K线为一字, 长倒T线, 倒T线, 长十字星, 十字星", # "5. 相同symbol的1H当前周期, ma5大于ma10", @@ -143,7 +143,7 @@ class MeanReversionSandbox: for index, row in market_data.iterrows(): # check buy condition if trade_pair_dict.get("buy_timestamp", None) is None: - buy_condition = self.check_buy_condition(market_data, row, index) + buy_condition = self.check_buy_condition(market_data, row, index, window_size) else: buy_condition = False if buy_condition: @@ -170,17 +170,20 @@ class MeanReversionSandbox: sell_condition = False # check stop loss condition sell_condition = self.check_stop_loss_condition(trade_pair_dict, row) - if sell_condition: + sell = sell_condition["sell"] + if sell: trade_pair_dict["sell_type"] = "止损" else: # check take profit condition sell_condition = self.check_take_profit_condition( trade_pair_dict, market_data, row, index ) - if sell_condition: + sell = sell_condition["sell"] + if sell: trade_pair_dict["sell_type"] = "止盈" - if sell_condition: + if sell: + trade_pair_dict["sell_reason"] = sell_condition["reason"] trade_pair_dict["sell_timestamp"] = row["timestamp"] trade_pair_dict["sell_date_time"] = timestamp_to_datetime( row["timestamp"] @@ -218,17 +221,50 @@ class MeanReversionSandbox: if len(trade_list) == 0: return None trade_data = pd.DataFrame(trade_list) + trade_data = trade_data[ + [ + "solution", + "symbol", + "bar", + "window_size", + "sell_type", + "sell_reason", + "profit_pct", + "buy_timestamp", + "buy_date_time", + "sell_timestamp", + "sell_date_time", + "buy_close", + "buy_pct_chg", + "sell_close", + "sell_pct_chg", + "buy_volume", + "buy_huge_volume", + "buy_volume_ratio", + "buy_k_shape", + "buy_close_10_low", + "buy_ma5_lt_ma10", + "sell_volume", + "sell_huge_volume", + "sell_volume_ratio", + "sell_k_shape", + "sell_close_80_high", + "sell_close_90_high", + "sell_close_10_low", + "sell_close_20_low", + ] + ] trade_data.sort_values(by="buy_timestamp", inplace=True) trade_data.reset_index(drop=True, inplace=True) return trade_data def check_buy_condition( - self, market_data: pd.DataFrame, row: pd.Series, index: int + self, market_data: pd.DataFrame, row: pd.Series, index: int, window_size: int ): """ 买入条件 1. 窗口周期为100, 即100个K线, - 2. 当前close_10_low为1, 即当前收盘价在窗口周期的10分位以下, + 2. 满足close_10_low为1, 即当前收盘价在窗口周期的10分位以下 3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量, 4. (当前K线为阳线, 即close > open)或者K线为一字, 长倒T线, 倒T线, 长十字星, 十字星, """ @@ -243,6 +279,8 @@ class MeanReversionSandbox: ]: return False + + # 满足close_10_low为1, 即当前收盘价在窗口周期的10分位以下 if row["close_10_low"] != 1: return False @@ -310,13 +348,18 @@ class MeanReversionSandbox: ) buy_close = trade_pair_dict["buy_close"] current_close = row["close"] + result = {"sell": False, "reason": ""} if ( current_close < buy_close and (current_close - buy_close) / buy_close < down_median ): logger.info(f"符合止损条件") - return True - return False + result["sell"] = True + result["reason"] = f"亏损超过下跌波段跌幅中位数" + return result + result["sell"] = False + result["reason"] = "未达到止损条件" + return result def check_take_profit_condition( self, @@ -353,16 +396,23 @@ class MeanReversionSandbox: 1. 当前close_80_high为1或者close_90_high为1 2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量 """ + result = {"sell": False, "reason": ""} if row["close_80_high"] != 1 and row["close_90_high"] != 1: - return False + result["sell"] = False + result["reason"] = "未达到止盈条件" + return result if ( row["huge_volume"] != 1 and market_data.loc[index - 1, "huge_volume"] != 1 and market_data.loc[index - 2, "huge_volume"] != 1 ): - return False + result["sell"] = False + result["reason"] = "未达到止盈条件" + return result logger.info(f"符合高位放量止盈 - 简易版条件") - return True + result["sell"] = True + result["reason"] = "符合高位放量止盈 - 简易版条件" + return result def check_take_profit_condition_solution_2( self, @@ -380,11 +430,16 @@ class MeanReversionSandbox: 2. K线为阳线, 即close >= open, 且k_shape满足: 一字, 长吊锤线, 吊锤线, 长倒T线, 倒T线, 长十字星, 十字星, 长上影线纺锤体, 长下影线纺锤体 """ + result = {"sell": False, "reason": ""} if not self.check_take_profit_condition_solution_1(market_data, row, index): - return False + result["sell"] = False + result["reason"] = "未达到止盈条件" + return result if row["close"] < row["open"]: logger.info(f"符合高位放量止盈 - 复杂版条件") - return True + result["sell"] = True + result["reason"] = "符合高位放量止盈 - 复杂版条件" + return result elif row["k_shape"] in [ "一字", "长吊锤线", @@ -397,9 +452,13 @@ class MeanReversionSandbox: "长下影线纺锤体", ]: logger.info(f"符合高位放量止盈 - 复杂版条件") - return True + result["sell"] = True + result["reason"] = "符合高位放量止盈 - 复杂版条件" + return result else: - return False + result["sell"] = False + result["reason"] = "未达到止盈条件" + return result def check_take_profit_condition_solution_3( self, trade_pair_dict: dict, row: pd.Series @@ -413,12 +472,14 @@ class MeanReversionSandbox: """ current_close = row["close"] last_max_close = trade_pair_dict.get("last_max_close", None) - + result = {"sell": False, "reason": ""} if trade_pair_dict["buy_ma5_lt_ma10"]: if trade_pair_dict.get("process_ma5_gt_ma10", None): if row["ma5"] < row["ma10"]: logger.info(f"MA5小于MA10发生转势, 卖出") - return True + result["sell"] = True + result["reason"] = "MA5小于MA10发生转势" + return result if row["ma5"] > row["ma10"]: trade_pair_dict["process_ma5_gt_ma10"] = True @@ -429,10 +490,14 @@ class MeanReversionSandbox: if current_close >= last_max_close: logger.info(f"价格上涨, 继续持仓") trade_pair_dict["last_max_close"] = current_close - return False + result["sell"] = False + result["reason"] = "价格上涨, 继续持仓" + return result else: logger.info(f"符合上涨波段盈利中位数止盈法条件") - return True + result["sell"] = True + result["reason"] = "符合上涨波段盈利中位数止盈条件" + return result else: symbol = trade_pair_dict["symbol"] bar = trade_pair_dict["bar"] @@ -461,7 +526,9 @@ class MeanReversionSandbox: need_record = False if need_record: trade_pair_dict["last_max_close"] = current_close - return False + result["sell"] = False + result["reason"] = "未达到止盈条件" + return result def check_metrics_over_buy(self, row: pd.Series): """ diff --git a/trade_sandbox_main.py b/trade_sandbox_main.py index bcf4a9e..3714c00 100644 --- a/trade_sandbox_main.py +++ b/trade_sandbox_main.py @@ -112,6 +112,7 @@ class MeanReversionSandboxMain: profit_pct_max = bar["profit_pct"].max() profit_pct_min = bar["profit_pct"].min() profit_pct_mean = bar["profit_pct"].mean() + profit_pct_sum = bar["profit_pct"].sum() profit_pct_gt_0_mean = bar[bar["profit_pct"] > 0]["profit_pct"].mean() profit_pct_lt_0_mean = bar[bar["profit_pct"] < 0]["profit_pct"].mean() @@ -125,6 +126,7 @@ class MeanReversionSandboxMain: "solution": solution, "symbol": symbol_name, "bar": bar_name, + "profit_pct_sum": profit_pct_sum, "take_profit_count": take_profit_count, "take_profit_ratio": take_profit_ratio, "stop_loss_count": stop_loss_count, @@ -133,9 +135,9 @@ class MeanReversionSandboxMain: "profit_pct_gt_0_ratio": profit_pct_gt_0_ratio, "profit_pct_lt_0_count": profit_pct_lt_0_count, "profit_pct_lt_0_ratio": profit_pct_lt_0_ratio, + "profit_pct_mean": profit_pct_mean, "profit_pct_max": profit_pct_max, "profit_pct_min": profit_pct_min, - "profit_pct_mean": profit_pct_mean, "profit_pct_gt_0_mean": profit_pct_gt_0_mean, "profit_pct_lt_0_mean": profit_pct_lt_0_mean, } @@ -176,6 +178,7 @@ class MeanReversionSandboxMain: y_axis_fields = [ "take_profit_ratio", "stop_loss_ratio", + "profit_pct_sum", "profit_pct_mean", "profit_pct_gt_0_mean", "profit_pct_lt_0_mean",