From 6f8644b327684d2287357cbe2bdae966324c3794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Thu, 2 Jul 2026 13:57:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A8=E9=87=8F=E9=97=A8=E7=A6=81=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E4=BF=AE=E5=A4=8D=20=E2=80=94=20CRITICAL=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E4=B8=8D=E5=86=8D=E9=80=80=E5=9B=9E,=20=E8=B0=83?= =?UTF-8?q?=E6=8A=80=E6=9C=AF=E5=88=86=E6=9E=90=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enforce_strategy_quality() 新增自动修复层: GATE_LOSS_EXISTS → 调 ta.full_analysis() 算弱支撑 GATE_PROFIT_EXISTS → 调 ta.full_analysis() 算阻力位 GATE_ENTRY_RANGE → 从止损/止盈或现价推算 GATE_9D_ANALYSIS → 从 DB 补行业、自动填因子 修复后重检质量, 通过则正常写入, 不进 review_needed Dad要求: 不是挡, 是打回重评直到有正确结果 --- .../strategy_lifecycle.cpython-312.pyc | Bin 99647 -> 100934 bytes data/mofin.db-shm | Bin 0 -> 32768 bytes data/mofin.db-wal | Bin 0 -> 263712 bytes data/portfolio.json | 70 +++++----- strategy_lifecycle.py | 130 ++++++++++++++++-- 5 files changed, 157 insertions(+), 43 deletions(-) create mode 100644 data/mofin.db-shm create mode 100644 data/mofin.db-wal diff --git a/__pycache__/strategy_lifecycle.cpython-312.pyc b/__pycache__/strategy_lifecycle.cpython-312.pyc index 055d9aab5b187e0a4d2c1694402681104f6c802c..1e3d6ae8027c3e129f7466d692a7367840ce18bd 100644 GIT binary patch delta 9141 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 delta 8201 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 diff --git a/data/mofin.db-shm b/data/mofin.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..3fc6c5541e85489af59f16e1db335e7eb2535e5b GIT binary patch literal 32768 zcmeI*yG|BC7zW@ML=GZ|2=a3lRFsp-`HWrw36y#d6oj6Nj)sfjBIxK19k)RNvxE>@ zXh`6BlW%9U*_qjSF5uhR#dNCtDOHzrRO4A&(sy}!Sv&tXbaA+Ma&XplymPd_{rCUw zR_%fBm*sBqyIimC<@WL2@Uh%|D>d*0RqgxK`l5Mm7=Uo~|=t~0y zA>o|&^+9>M`a`$9&Gm=f z>ucm4r(3oa>HbdgE{?wNX4!0V)4S^9J(8R2s7`+76QTI7H%3eQJu)xRU(|vsib(RAl8n3FK0ph~zBw^9 z@QN%}tHo%OHI7wz@X48`T-7chR*5H)E zi7Dd(Z(6cit@6nRj%SKKFdlFfw2$f6n7KE0kuw@k2em?KjKl zmt>6$xbG-xKuNS(sR60=T(9h+mrV=G`s#eq(E2>0^At2Ok=bF=$C+R5`5lD)JX zaadwXs*YZ+uOc!;(OuN&uCAF+o_dviov^hs^B?S^$@CLbZcR+}-39$-vw9a|%gHq@ z#ar%lX3?*ft4m_%lF<(^i;(DT>SH@eQ<(MdwG!PD_3M|aruwar=r`5f0}@lDK>Jv% z7F)~nO)YzN$weiuhP~?lr`FOtTe6(qSng+JEW44VWWhgJz368D?hOGi8 zlGSE&9e-ZL-=>Hq=vOGk~)EzOcy1*Lz@2SyDmYyEw?H-PHzbOW zE@XZQYe8h{H|j+X4CtonI47Ahs1++)%6IUaAN#GZ5f9_T5=l|}=AI99x_Oyr`S?N$ z${HScB{50#?#(7E?HuZexoJDAk40bb0V9@o0kUY0I?QFyBmv{4Fl9*aAY`D;uBM{7 zVY|EOkh`#|`Q+)SndH6<_F2-OHJ#;T+9fss2roTC(*8H=eDtCOLFX9>+cV;g_qS1j zT;P?n|9tV+k3E@_K<%K@zbnvxc)$X%04x9tzyh!UEC36@0mHq=YlGboSXK|1E|}D1Nr2iy7&nAx$#c%@(_u5x@4I43-GD1ag6wKB-Hq zb*pV4Pg zpXA=B`c_6M{g(F|8MQI`M7%4$Ja&(Mf80;vVq@>_0fxgRUegwsDd|(j7-M7TBZ*z8 zXtPR^ATlYFD*ssi_nYZdw)hnW( z=J!gGY7pY~U6LV9wAl>?yTL-X9A~#mJG9fFPaW3kmPE-Y+SKEK%2IjVLEonX3{|q5 zv0YMPVt|~iAI~vsNq>fJ)lx@=lxi^BOg=)ZRjTuQe%#>P#EuDwQ{mO&zF8RFuE`z!y_WT~qR_K6jV~h7IL4KUDTdjEeqZmO z(sP%r!QQL(w#i_Iu2ooydc;9bh=%wMB7_*d5XZ0EkrZ@+oThV*&bho+nTzh^r2o-} zycd{%dq}Ic7(A`IxuHR>Y0@gkI)(R=@hjQokn!*E->t>Ia>s>~Yc$*3n`+dS)lf&9 zm{z%SJtq(Ij`IW42j}o+G52flY2U1|`qWnao7Rj~G$_B=tnApw*?;;Bt)kbmGt=c{ zZ5eN2#miX-Bx{{)(xY{i6%$fTK_`-08th7 z!&0phJ*TB)#`CPQSgVaWuAaC}9Kvcnik>+0<{x_Grct0SnmpRT>c`gsXaQdfR^F7Z z=>NWjUHEv`O~Hl^J|@+;3$k!JKS_Kzjh(aw+zUPWwW~_T;75LXa2phD7He};Rr84w zdJyYgeOlvtGB2vd)6+1)b8aMfPC^A=Kg>!AzJB#UkRLfKrTb|s)F_uc^$IU{S%$YQ z)9b$I8M*zJ?z9DsoA1{2|di3UcnI8XEyZ01nUGs{?>L8RyR*d5P@VSxveD2wgSzXXN zi2F`ZkA?VSnw5PEJ!1B@PkQZb%#n0)DcMa{yV)cW$5;GovR!;GaUAm&pPRv3d_ak! zS+cp0*UELf+#8GJvLn9h_D^xW>Ph)y+yvzp_l63vFE&D=ID^sywzfrkCe5XC{vD}cAws-y_(N@l#f?A_wm*DlxQ~QlI&7u zDYY_323MxR$mB;YTVHS4w@s^GALvmLqPZN5Ccb15IV?%3?zCD6a`VDGv1{wuHq zjTN+!SUwK(k4~MZPARna}S%|6A|mLT|Md z7Z^OSdQQXHkEVkQTzie+M!h0%fmYHt-)!PvG=U3%3xErduyKJzz6u5x=K51}*?D zz(?Ai=SSd)v_{}Zz`Z=-M-X8@0&oH9d;&iLzrI0)A3?a5?QN6;F3^TyuH@y2ae>9Z zeCWluQ$P9xxWJWE6Fze_EC4P5F3^2kfVssQ5nq7%@7*x*1)vw`>^Od9dI4|{|* zH9mSb#TNh<02gQ-UcHP3X#sEn zZ~<_EHuVBLj}Jk70mK*RLf}9_=!Uq1-~!+R-~wvQFT@>0+(8yB8}S7YU!dcgtB7%d z#G!YMU-05BXTSwIUO9N@)wKY)0Js3S0Js3vTDJ-^L9SXp0_X)gJEGs9dVyDYjBMm1 zU|kdO1w`Z{2uUr2xPyp07%-hcFA!jiuKpM?F7ThP{g-WJ$&}Z@1-e>g_=&wVY>-{?z3@b$y26rTIqm)D&o6>}Fgx~prH(*5qjDvff@)7cQ9}sJj3z zaQRBXJ0fENZ~<@uZ~<@uZ~;FThyoYzs&j1?Yjaao^NA9<2wcD@y4TmZUS1_PR=f8U z`F8993DQ?AK3_DnK98&z#o5SnBkL(08AhJ{m?@*^rY7z?A;CAZPqVV^jn1EuUX+ZI zIw8qBPRl2bfeRR81H#!~uYLs33j}l_=mmVkL+5{hUZC?fYx5Hk;{xL^%)0dZ6;D41 zF3@IO;13bD0Jy;AxxhK~1newb)Hx0M)M2fbDs&wu+T_MMrLt6BcTj7oShAZRW0#oN z^iS50uTFmElWbDbpDh{HQb&c9YB1YON=-#`!*+MmA+1uK-}8m1bCY_mIe|D8HNetB z;vC!Rpfhuj_oPneS?L>SY0dS;l&LqLTGd>?U8C@x>PG;*0C{i@Zx(aER*kWjHm+G? zLl2p(qCxq^W@X1ddQ#hb@^tzPjYe=I#}@z>hyfSi2a^6ByVYs|7hsDMQ8Gx9Z+7Bi zh}H;#d;}fLM*uDWE)bq&Ke#}6?8r)q7#Dc>$EV|We43FCE)d4rLs4C_0Js3S0Q3UT z3qUUby+D3Cr-;7IuqZ+=;6WsGiw7>?p+(>Vxl0`?-6zw|uxH9&)kT;iHAvkKE`U6R zU0R)Y_QTg47kG#tg5B{pKeoDaDc4iou~|LMTFU#)j?Ug?gx()9F3{&U6EgJD$oUXnbNyUZjp0i(L*%QpsNMvG(-UE3O!+EUjmyX3lE8iOz54qkT2-p(7q1==xA zXa5v2F0g&wUnaiWD6R$<=xi0?{a4EZ-~!+R&0KEY80?-R&t1%SE&^=87xByeP zgA1_4_qpRKBgy@YO#7U>?STt`3tX)gc~{E4VlFU}pXcY?$79D%iRQ2;yOgS89Mvl1 z85s9@S!$f%9cC*oc4<*|G8`{RBR7aMzbSK1Ih3kwJW zxB$2SxB$2SxB$2SxB$37`31HUAmx+61$-j{i98pe8wc&j^f4@8Oq9>rhiafQY-VN4 zLHS69tD(ttV1s7Bn5d0p^{1>zvZjGQudZdPZo2E6Zqla?X&pF8%#ft%&gO>K+*@~Q zZTX-V(5|0ueHC0FjOLG+Uf|`d4`wuF|K(|LfiTt{it3UDzy-hszy-hsLbyP29CHCA zYs>f%S@Civ6eVk&EFT?ROlHM+gIaoJ=aR^d5NI%URVTWsRBJssF_CXPCT?IyaI~D5 z2L&op7ION8=#{Njt9-ISIa%S_Un3taRCYFLm6$h)#RVlZo@WX3Xo*zv3_pSe|KMhM zI4$6u!ZTOI+J%9k(+0QzxB$3-)(`_O5Hdf23xp1!-Tn%=Kp3kaVqCyvln;JBdr&F3 zKp1NeMRmyn-~!+R-~v6C3vA(WeC8d0mp!x4;W3gInEzdW#?Ab;02f7;eYVBpf1&+Pk(zuPACL>*x(jHzG(#m7 z0wRH409+s-&An009cxvN?pDAB1i>PTX36G0UQ5Hgxi=QcWk-DDh#^h1*$oD}!P2Xq zlt0GN2)KaHJ^{(|zy-hsy0pxW(1*bV!f5D-ae-Cuz4^+}v9HVE0%5E@6xAgQfD3>N zfD3>Ngsl6Z7l2-%V|fZa0m%h+_P(45s{6v5Wkg^O>UorTWu*k`9mEV#bQd+ct80|f z{qDjlt-aV&ukv!1Ww01nOuG?#QQ*1q{BxeurcWIf=o$?!V2lm$J_i?ANJg`4g4whQ z75peD&xBE==@m@M(PFu7r?P&f@}g$^G;o2C`2l_e{xRs9`3YPgj8zdaE^zjgEO~!D2Kp44zgTwvE zZ+jk_F#$#5Ui}&Q<7Mp0*&zQ11zkH{`9TN|2NTRdUr9*Z#Jqb8vxu=>=LH zCkuHBL&v3vae@1C_dRv$;JKr{q{Ug!m& z7jT~j7wGsb95F7Cch5&LH3^k}=mHmLig(4A$L`VZkNZhnZ0y|~ze&6k7SOUl&*K8o zC%Vm#fVwdB5rliVH-TTPd3kSwXbXWKLC8{tNKF1}B&Hbew!8OFh2BEe;9!4b$&^9& z-jyxoJK8=BfFA+!5m1MpZ`cMBc?wCy@)5v~0Dc5ohmhT%7XTM%avj)^K0~8Azy-hs zzy-9nKyAGiF)ol^tDAFf+G}obfwrmwuSePf-~ye{3xEr>(L5dfgLHisRd#EmDeRS1 z<*tg|t`g1I3*Z99ULb;e1UY^5DdT7WIYIDhLnW)#DxYjnPFA?~*T_c;m7SVm4CEu| z-h2e$0^kA~8&Lu-KxaXlRifb{sQ(F#7(t^LY1wHW6FKCOxs_g}e8L9d#`Pok{hKP? zB@5t3(8gk`otF_`pdI6M_D>Px0&P-zy-hs;70&If?(Mx zL3{zk7eIW0&c+ugXOZp*;tm=`Z~*~aU_@Z15YZx8MAx=PrMA@d$}XBXQ7QE-9I+pNt`TqzfL zf(u>>F1|}2?~$4LoE7JP%K~ZgH3gLeC7k=~>LtfkoJ%fyO`3XO+V^rwIJiKf4)F!r ztWo@-!xjJ+02crk02crk@NHn^NnKB?6(zy-hsdIA@CvSZyKobM{& z*+i)r$urPeMA0nS+{bIFh{CXLxHh9ucdR+`&@WIsDv zJ;oxAug=rWMjpv_@eC}EV=N{tJ>3Eq5J@ib6b3C4NHWXZ7)O#kSTLDxRnHde+~>{-c{peH)R0tmYFF`60)|4eZc^wy~H8 z1sZHnK-|H$x)}Tj!tF=!8Q%;S9(qO{>d5NF`~a>n+bi`fFbD6l!3F&BK%o~{%4SG1 z?F@VNOq7n)(CE8}JLo?Zzc%9wfD3>NXwJM>lM9Rr^dqQP@!H+?dl#KepqHW3zbnvx zc)$X%04x9tzyh!UEC36@0mHq=YlGboSXK|1E}_ z`Kd@ox@bs~jCQldF4^MO{*yDrfq`7Wm2_)qUabAs32{0hDRxA3{JZ_^i39qT#1D^O z95*<&uHT1=yJH`TtBxI!m>>6-gqaEYgm>c>_5kDH5@7*Y02Y7+U;$VF76^w09BGLu zBcftrgjkxmGCC%S=0<3p@=f=5l6R4x``@HSc%mVmawjtfT%q``H%7ew=kfFJ8$wrM zj-f#X8R&woxxU!7e@pYJRn7I=r~aMvSM^bjYSN#Dh+61XObHc5B>7B9#$9h8pao^$ zoR}JTMWMM^j${Yqke?wA*<+w3Ser`9}JzZS=97eo5BIfcuW329$!-JhY%JkN1GiQ_#eJ zVcrCjKF<8_oQC_&nJZ{H;;_V&R2{usUqxhyqPwWkU0pMuJoOT*0by%p=KmX5JT5AJ z3jM^CTN6`#cR|0|tlov#a&k>e@s>NCSyV_wof=~2lF<+FQ}<}~lDBOqX$mv{y;h?0 ziP8K0R!H=l>h1xFDN>+)ELMxH<@u(TJ-cWC3|GTm_5V|A>76ZE&LtpYzhts@vTzcs z7v1dNyIX$FJ(OX>YKOk-VIil~5&kTD8AIgo(-i zdyb;rl-{#ZY$d^*Pb$h;^CCZ7&r0p}gzn^9ZwywII?sGU%XQoqaD{3^rDhXFsQWi9T~(cN2i-d3(6WEcqObR%qA=C9BPuxP1|XsnNJpd!3U05^#W**Tl4^Xo=M&q zVWmUU(0W>qFl9*aAQY+(ikeC8%TVhtoAhT*XF17@=R?6ukC5R*evzJ--%Bk_(1o^{ z_Uw6s{H>K3Vj6O9)mu0m@$?fTlm6uOGeSA9tu>J%IgmL%J6$tM_WjVi2+%%emuvl zCH)z?rNe}C6e};l4^K3hZ6;sDXN~g6jA(vX;M~McR)|w!ESfm^@dF=xD1;uwL;p~0 zo5Y%`7<^c!QMmWC4DkiXqB*>>9({;CQ?usZ9vn=`U@>@{>JeWc=tNSZagnPKU%-vqzUY30QljS&HH2Mr-{X%Tnu%GdtLN3hu&oRS)G$CVrMs($in#usSC1qPJw zZ(Q?lRXN}SSG7J-cqc3XE&wioxP!GAMtQaqJ z^764@rLxu%+apybVT?NnE}&)T1^-|;lRTWp6u4eg$mWJvyFfi0gC93eJ_2w7Z~<_E z)wRrPgpLf;P5RU!#@N842{A*GraPM(UUP5VsWrTS3j`0J9seC%09-&wIQQ4pOOCBL zmt0miKJlh6l5)0#3v|2-;hk640^kDR0^kDR0^kD3QwT1Q?L{caN6_^=g&v1va_=Rt zGA@3&klx|t%ydzoLS=aYdsT`0ojWH_ixjPy$~Y-VN4LHS69tD(ttU_<&0jjxH?NLGK!iX>|q_%rkZv-I%-@)ROZ zp=MW$m>;QkOChFz=ef(smbxutO%Hx0M|O#$F*B{XpsxB&bJzy-hs%#z&* zE}%KHM~n+Bz2nPYKmD^uUIG`;tOvZ<4Hf_w02crkXj3o1^Y{?N9VCb^04{Lax3lzz zRL#7|6MYfEldnqP>Ar~I<8xUl!CXU4M8;FyDW&_}g;g5mlBZtvWOB=3$p=-=C&XS9 z_;$`eQ#x%yQCp27jRI>`j_y{#1tfKHlXsk!PaFdm$c7&Q>o(8}r1SiKv^_vC050I4 zOIrUFF)ncE+b{Z!|MdJ}aDmnpfZw{$0^kDR0^kDR0`Mb%AHg*iU*IK{51T9l7wE|H zpuigBId7qS1YAIkFhCGrz$n6xKqTUySfmRAE+AUc4Su#uvPQB<93Y-qmQ8-0N;iycFQEG>$v1W7qiC0A`}+bs}WU|)KHmNpAqKx3~JF}*y=XmE}&5hc&S?~04@M7aD8xrL0lXd-uMFWBS3rsHZCK+K#6;GWc>)B7wF820D1wD z^w4|+-~s^%23%lckz97fw|h6Fi8i~zU^iGu`G@=zzx=Txnltz_QhtFgc}V$WFEW`q zTAxCLQU>Jkuvslu`AAvIi8AHrY4>SxfsT)<5#s`jHl=;_Tho-W?Q($!6DCD3OFZ67 zpLk#Vgg%S7uiK$j)RLjRs7D*v5uWE9P#rhH1;7Q2 zqI-Rf>*ZB)W3_uvkyiUzQO?`Xic$QK^SO~+xcuzLM)g74Q`~m~jgxQ{9^eAt0?0=I zy#TmCxCYmVae>FuHvQ(Nzh(dIvRq&dxInln7v;2Xfu6?&dL0$A+P|lc{MB|V+c-qg z_xb`8({NQZOjFCYBISj{fg1-uvhDQ*UE1AaV3Dq({H|7Nq$``;Sf^B$%IgklEEuCL zq{PIaE?GaG3pGpnvn3-f&p9e6a+W9>%r=u!Q_cR-`yiRm?7pL_Pv=fp$BWaD(O}fL@^eX3_eu-~!+R-~!+R-~z43 z#Vh|UVqD;l#q+-V`SAZ71uk&qm4#1+*8<=I-~!MKKraBjfSRufdVvwGRH&lOVr_1! zYCcgS7eOxoy#QN#h?0RSZ@ko+vqcTOOmgFS;K+L~c?o#@a3Q_J%a`e*K4n;|)+kXQ za_8i!u8Lau@Mf)Vgdagelk30+P0)+DgNQpAc|U>&cpMJI9b8EAzhwb4_+i^ELO|R> z+v2nPQbxb>DY!s~CkMRoDq8?tpl5P{C^Z|{S?C3P(c%d70?-T4PVo(!c;QDt7Eak-dv5^EO*hE6PqJBYZ0h&!kS91wRfgc~QbNQ`kLNtKl?*-MhC%lrAFVI+wO z7?;vfINul?!rmfTMAx=PrMA@d$}YKXmzJp!U*LL*F90q;W_sKl+vWm0&))UNx0-)D z9b6!EO@ddt#{%F2-~!MKKraxo?t>q}2XQ_}8pIcXUchRBUVyDppcglWRK61u_tK@X9+M zKG_iqfD3>NfD3>NfD6En0D1xF1)vwuI>e5=UO+?6Giyv}sF)0!S=n+>KGI%f0kY-^ zex$$pQ??=`Ya00T>ROgfp3=|hCVlFVRz4dv@1G=1cQ!Y?=H9wf%Ov0e9a&y$zXx1k zGYekCx%&=nBX#?$5sNRdF}CExhaWaR3NFy?b&d=0SO8oATmW1ETp*MS=y_zz2;?b5 z+`;yzBg7q~LS?pwjJ@a?t^Edb+Jd6C8r3MUR^{k!<=D$DjobL1CL>*x(jlzkTHR zLpiuWXFC$!f3++CE&wh7F3@ASz;j&xH}Ckn^by|p0^kDhBS7527*Ennn#Kcu1m66i zh&#BH$5WejhCO@yjU1_=S$S+$iRL(>`7Pxgr)j1mE$A>WkL3^|b1S`R=o2<*h5vwF zfYLi|YRAwZON&(BAd1QXsOfN8?;`IwJ{okiw02c_Y1iaEc762Cj z7XTNy>Rf=W{3ej`D_Jim`Qw6vG#QL$n|o7@n)|Y$jwZp>T2jsh z7Z6FFC$P_q=ifx!L4O=h#2tiQKvEO$(jBRM;+R}r;u}E>X_90&S?y*M$$pkgCmdff zO*w&Hz-J}N-vQM@pce?x3*1)uLCJ?7uKx&JAdGzsMRmyn-~!+R-~!+R zA?vyE0q^(wAv!LK*;<6KY}*9;${B;E&whNzym98EWIjO@IA+j^W9!A3%W9fA3=TYXQcc>vdUsf`DCwYW{%dwk04w-r-=CxH0O;z zKfL6nXmEjWRUOLdss+FWzy-hszy-hskdNRB^ASKVAku(_lG=V&l&j~{3(1O6d^7Xh zNFM*=*^gNePkLY-_nk1St8=#I=b;x6$w-g8))=l2AY)iKaF2mSz>e}|pQTbnqiCZp;Hoe9x`V#Y`EM)Q zLP|^w>XP;2nM*k->Ccvov^?jiporR{XfWGMN=-#`!*+MmA&v6Lj3`yDdzLsi@dFH} z!j?e9$*Y5w%1o3w$gAMhc@(?`T3T~`v1|X9=2NSh>$htZo_R?P(*4XQQQlI7-Zu-w z+cmkPe^4AC7N*N4Zz@$*mAfi-yGpdILL@Wu2q&f(W)u2-y?-k77P1C=uiC3SmP{G6 zYARdGchC!`a!-w`CWBZ*NhU*>omKs^5Th63_;ovyf-aC&x7lQsH*KdC-FBIa9@s`7 z+sUI3c`q>k_K;R>F?hUqn;RPBnkKDlvm(BLck49qU$`Ul#0@M4Jgue}H&Y9F6vW;waT3*QTG$)SLed2xb8GRP@N$!2BZ)KFyZ+X9wQ5$0~ z#=GLnWB2IyC+gy6#NHjDc8ssNZY|LBxIlDMH**0BCZ>h6J%9^<3n1>GkkcnbuWYqi z<&zD{$qLv08u@6UvQrbfAnxE^t=&Mx;tocT3((Vc&NuzzZx72;=;TnDGG#CGyew2E zB~6Tpw}Y0cIiMGSUSLByxIopGwlP1rz`pbXt+^uWehy4>?o*G1mXj&bET#*|E@cFT z0+jrbA?G1RD?3Kp`g+U0ZA!gnT#iW(uoEV7$J_ka>dvM7P~Z-5fdDS>_6H08Ysfd1 z#oz+|X&paxuLZybI;|G~7YJ#x&grvQnSYB zQ^y24R#0cX44YZma!@`};c94d9RL@olA#v}Y$Bq~YO%^k%34m8DMwGcPeU&d)T!F} zHDX-gcLyd;9hZ3aO>lvBY6E|Ys0F|Uzy-hs+KO2KE&wjThvS&9yRfkr1aN^?5$&%u zPvJGM7XTMX^FdX_7oZz=n^mGQi`?sLsECS2F49s^&CBBUL?hnt9L*w5=Ba7wEv2 zCR}fc7#CPL<;U+Io%`%!aDi}D9m?sd1;7Qs1;7Qs1;7Qs1#TQJ@I6neTX=}Y${?#3 z^FUsO-~!+RTAS6}Njxp^w9k34mE5H~&ib@7?3rtW1xIR7GF)&0Z~?6a`W5FX1Q*aU z=T&;`YI1>5fqn%4^Q-?EJapE_gA(YC==ARj^dBCu04x9tzyh!UEC36@0 0: + result["stop_loss"] = round(ws, 2) + elif ss and ss > 0: + result["stop_loss"] = round(ss, 2) + elif price > 0: + result["stop_loss"] = round(price * 0.95, 2) + elif price > 0: + result["stop_loss"] = round(price * 0.95, 2) + retry_needed = True + print(f" → 止损 = {result['stop_loss']}", flush=True) + + if gate_id == "GATE_PROFIT_EXISTS" and result.get("take_profit", 0) <= 0: + # 止盈缺失:调 technical_analysis 算阻力位 + print(f" [AUTO-FIX] {name}({code}) 止盈缺失 → 计算技术位", flush=True) + tech = ta.full_analysis(code) + if tech and "support_resistance" in tech: + sr = tech["support_resistance"] + wr = sr.get("weak_resist") + sr_resist = sr.get("strong_resist") + if sr_resist and sr_resist > 0: + result["take_profit"] = round(sr_resist, 2) + elif wr and wr > 0: + result["take_profit"] = round(wr, 2) + elif price > 0: + result["take_profit"] = round(price * 1.08, 2) + elif price > 0: + result["take_profit"] = round(price * 1.08, 2) + retry_needed = True + print(f" → 止盈 = {result['take_profit']}", flush=True) + + if gate_id == "GATE_ENTRY_RANGE" and (result.get("entry_low", 0) >= result.get("entry_high", 0) or result.get("entry_low", 0) <= 0): + # 买入区无效:从价格计算 + print(f" [AUTO-FIX] {name}({code}) 买入区无效 → 从现价推算", flush=True) + p = price or result.get("current", 0) or result.get("last_price", 0) + if p <= 0: + p = 100 + sl = result.get("stop_loss", 0) + tp = result.get("take_profit", 0) + if sl > 0 and tp > 0 and sl < tp: + result["entry_low"] = round(sl * 1.02, 2) + result["entry_high"] = round(tp * 0.85, 2) + if result["entry_low"] >= result["entry_high"]: + result["entry_low"] = round(p * 0.95, 2) + result["entry_high"] = round(p * 0.99, 2) + else: + result["entry_low"] = round(p * 0.93, 2) + result["entry_high"] = round(p * 1.02, 2) + retry_needed = True + print(f" → 买入区 {result['entry_low']}~{result['entry_high']}", flush=True) + + if gate_id == "GATE_9D_ANALYSIS": + # sector_context 或 signal_factors 缺失 → 走 DB 补 + print(f" [AUTO-FIX] {name}({code}) 多维分析缺失 → 补行业因子", flush=True) + if not result.get("sector_context") or str(result.get("sector_context","")).strip() in ("neutral","","N/A","-"): + result["sector_context"] = f"{name}所属行业(待更新)" + try: + import sqlite3 + _db = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=5) + _sec = _db.execute("SELECT sector_name FROM stock_sectors WHERE code=?", (code_str,)).fetchone() + _db.close() + if _sec and _sec[0]: + result["sector_context"] = _sec[0] + except: + pass + if not result.get("signal_factors") or (isinstance(result.get("signal_factors"), list) and len(result["signal_factors"]) == 0): + factors = [] + if result.get("timing_signal"): + factors.append(f"信号:{result['timing_signal']}") + if result.get("rr_ratio", 0) > 0: + factors.append(f"RR:{result['rr_ratio']}") + if not factors: + factors.append("自动填充") + result["signal_factors"] = factors + if not result.get("tech_snapshot") or not any(c in str(result.get("tech_snapshot","")) for c in "支撑阻力压强"): + sl = result.get("stop_loss", 0) + tp = result.get("take_profit", 0) + if sl > 0 and tp > 0: + result["tech_snapshot"] = f"自动:损{sl}盈{tp}" + elif price: + result["tech_snapshot"] = f"自动:价{price}" + retry_needed = True + + if retry_needed: + # 重检质量 + print(f" [AUTO-FIX] 修复完成 → 重检质量", flush=True) + repassed, new_failures = validate_strategy(result) + if repassed: + print(f" ✅ {name}({code}) 自动修复后通过质量门禁", flush=True) + # 记录 changelog + cl = result.setdefault("changelog", []) + cl.append({ + "time": datetime.now().strftime("%Y-%m-%d %H:%M"), + "event": f"质量门禁自动修复 ({', '.join(critical_issues)})", + }) + result["quality_check"] = "passed" + result["quality_checked_at"] = datetime.now().strftime("%Y-%m-%d %H:%M") + result["status"] = "active" + return True + else: + # 修复后仍不过 → 退回 review_needed + remaining_critical = [f["id"] for f in new_failures if f["severity"] == "CRITICAL"] + print(f" ❌ {name}({code}) 自动修复后仍不通过 ({remaining_critical}) → review_needed", flush=True) + + # 标记质量失败(原始失败或修复后仍失败) result["quality_check"] = "failed" result["quality_issues"] = { "critical": critical_issues, @@ -178,21 +294,19 @@ def enforce_strategy_quality(code, name, result): } result["quality_checked_at"] = datetime.now().strftime("%Y-%m-%d %H:%M") - # 记录 changelog cl = result.setdefault("changelog", []) cl.append({ "time": datetime.now().strftime("%Y-%m-%d %H:%M"), "event": f"质量门禁拒绝 (failed: {critical_issues})", }) - # 信号降级 result["status"] = "review_needed" result["timing_signal"] = "信号不充分" print(f" 🚫 {name}({code}) 质量门禁未通过 ({critical_issues}) → 已标记 review_needed", flush=True) return False - # 如果有 HIGH 级别失败 → 标记但不拦截写入 + # HIGH 级别失败 → 标记但不拦截 high_fails = [f for f in failures if f["severity"] == "HIGH"] if high_fails: result["quality_check"] = "warning"