
    KJj:              	          d dl Zd dlZd dlZd dlZej                  j                  ej                  j                  ej                  j                  ej                  j                  e      d                   d dlmZ d dlmZmZmZmZmZ 	 d dlmZ  ej,                         ZdZdZdZ	 dd	Zd
 ZddZddZerej@                  d        Z!d Z"d Z#yy# e$ r dZY ;w xY w)    Nz..)get_min_trades)calculate_activation_score_poccalculate_positionscalculate_bullish_engulfingcalculate_bullish_hammercalculate_shooting_star)cudaF   g{Gzt?c                    t        |      }t        j                  |t        j                        }t        j                  |t              }t        j                  |t              }	d}
d}d}t        |      D ]n  }|dnt	        ||         }|
r7||k7  r||   |kD  r||   }|d|z
  z  }||   |k  }|s||   r&|r$d}
d|	|<   d}d}n| |   r|rd}
d||<   ||   }|dz   }|
rdnd||<   p |||	fS )	u  
    Simulates positions bar-by-bar with a percentage-based trailing stop.
    Replaces calculate_positions when i_trailing_stop_threshold > 0.

    Matches Pine Script behaviour with fill_orders_on_standard_ohlc=true:
      - Entry signal fires at bar-close T; order fills at open[T+1].
      - Pine sets trail_high = position_avg_price = open[T+1] on the fill bar.
        It does NOT update with high[T+1] on that first bar.
      - From bar T+2 onward: trail_high = max(trail_high, high).
      - Stop fires when close <= trail_high * (1 - trail_stop_pct).
      - Python records signal on bar T; TV records fill on bar T+1 (1-day offset).

    The fill_bar skip: when entry fires on bar T, bar T+1 is the fill bar.
    We initialise trade_high = close[T] ≈ open[T+1] = position_avg_price, and
    skip the trail_high update on T+1 to match Pine's initialisation logic.

    time_ok_arr (optional bool array): Pine gates BOTH strategy.close calls
    (score-exit and trailing-stop) on `timeCondition = time>=startDate and
    time<=endDate`. When supplied, entries and exits (trail OR score) may only
    fire on bars where time_ok_arr[t] is True — so a position open when the
    window ends is held open to the data end, exactly like Pine (it cannot
    close after endDate). The trail high-water mark keeps updating (Pine does
    not gate that), only the close is gated. Default None = no gating (legacy
    behaviour; the activation strategy and optimizer paths pass nothing).

    Returns (in_pos, exec_entry, exec_exit).
    dtypeF        T      ?   r   )lennpzerosint32boolrange)entry_raw_arrexit_raw_arr	close_arrhigh_arrtrail_stop_pcttime_ok_arrnin_pos
exec_entry	exec_exitposition
trade_highfill_bartallowed
stop_price	trail_hits                    ~/Users/jameslopez/projects/TradingBot25/.worktrees/mlp-results-and-research-learnings/strategies/strategy_activation_scores.py_apply_trailing_stopr*   &   s   : 	IA!288,F!4(J!4(IHJH1X%-$4A3G H}!z!9%a[
#s^';<J"13I \!_'!#	! 
QG! $
1&q\
U!Aqq	/ 2 :y((    c                 n     j                           j                  j                  j                         j                  j	                          _        t        |j                  dd            }|dkD  rAg d}|D ]8  }| j                  v s |   j                  |d      j                          |<   : d fd	} |d      } |d      } |d      } |d	      }	 |d
      }
 |d      } |d      } |d      } |d      } |d      } |d      } |d      } |d      } |d      } |dd      } |d      } |d      } |d      } d   j                  j                  t        j                        } d   j                  j                  t        j                        }|j                  dd      }|j                  dd      }|j                  dd       }|j                  d!d"      }|j                  d#d$      }|j                  d%d&      }|j                  d'd"      } |j                  d(d)      }!|j                  d*d+      }"|j                  d,d-      }#|j                  d.d"      }$|j                  d/d0      }%|j                  d1d2      }&|j                  d3d4      }'|j                  d5d6      }(|j                  d7d"      })|j                  d8d"      }*|j                  d9d"      }+|j                  d:d"      },|j                  d;d"      }-|j                  d<d"      }.|j                  d=d"      }/|j                  d>d"      }0|j                  d?d"      }1|j                  d@d"      }2|j                  dAd"      }3|j                  dBd"      }4|j                  dCd"      }5|j                  dDd"      }6|j                  dEd"      }7|j                  dFd"      }8|j                  dGd"      }9|j                  dHd"      }:|j                  dId"      };|j                  dJd"      }<|j                  dKd"      }=|j                  dLd"      }>|j                  dMd"      }?|j                  dNd"      }@|j                  dOd"      }A|j                  dPd"      }B|j                  dQd"      }C|j                  dRd"      }D|j                  dSd"      }E|j                  dTd"      }F|j                  dUd"      }G|j                  dVd"      }H|j                  dWd"      }I|j                  dXd"      }J |dY      }K |dZ      }L |d[      }M |d\      }N |d]      }O |d^      }P |d_      }Q |d`      }R |da      }S |db      }T |dc      }U |dd      }V |de      }W |df      }X |dg      }Y |dh      }Z |di      }[ |dj      }\ |dk      }] |dl      }^ |dm      }_ |dn      }` |do      }a |dp      }b |dq      }c |dr      }d |ds      }e |dt      }f |du      }g |dv      }h |dw      }i||z  ||z  z   ||z  z   |	|z  z   |
|z  z   ||z  z   || z  z   ||!z  z   ||"z  z   ||#z  z   ||$z  z   ||%z  z   ||(z  z   ||'z  z   ||&z  z   ||)z  z   ||*z  z   ||+z  z   |K|,z  z   |L|-z  z   |M|.z  z   |N|/z  z   |O|0z  z   |P|1z  z   |Q|2z  z   |R|3z  z   |S|4z  z   |T|5z  z   |U|6z  z   |V|7z  z   |W|8z  z   |X|9z  z   |Y|:z  z   |Z|;z  z   |[|<z  z   |\|=z  z   |]|>z  z   |^|?z  z   |_|@z  z   |`|Az  z   |a|Bz  z   |b|Cz  z   |c|Dz  z   |d|Ez  z   |e|Fz  z   |f|Gz  z   |g|Hz  z   |h|Iz  z   |i|Jz  z   }jt        |      t        |      z   t        |      z   t        |      z   t        |      z   t        |      z   t        |       z   t        |!      z   t        |"      z   t        |#      z   t        |$      z   t        |%      z   t        |(      z   t        |'      z   t        |&      z   t        |)      z   t        |*      z   t        |+      z   t        |,      z   t        |-      z   t        |.      z   t        |/      z   t        |0      z   t        |1      z   t        |2      z   t        |3      z   t        |4      z   t        |5      z   t        |6      z   t        |7      z   t        |8      z   t        |9      z   t        |:      z   t        |;      z   t        |<      z   t        |=      z   t        |>      z   t        |?      z   t        |@      z   t        |A      z   t        |B      z   t        |C      z   t        |D      z   t        |E      z   t        |F      z   t        |G      z   t        |H      z   t        |I      z   t        |J      z   }k|kdxkD  rjkz  dyz  }jj dz<   |j                  d{d|      }l|j                  d}d~      }m|j                  dd      }n|j                  dd4      }o|j                  dd      }p dz   }qt        |j                  ddx            }rt        |j                  dd            }s|rdxkD  r%qj                  rd      j                         skD  }tn!t!        j"                  d j$                        }tt'        |j                  dd            }u|urd j                  v rt d   dxk\  z  }tor0qj)                  d      mk\  |qj)                  d      |mk  z  |qnk  z  }vnPt!        j"                  |dxk7   j$                        }wqj)                  d      mk\  |qj)                  d      |mk  z  |wz  }vprTqj)                  d      lk\  |qj)                  d      |lk  z  |q|qj)                  d      kD  z  vj)                  dd       z  }xnqj)                  d      lk\  |q|lk  z  }xxtz  }xt        |j                  dd"            dz  }y|yd"kD  rlt+        xj-                  d      j                  j                  t&              vj-                  d      j                  j                  t&              ||y      \  }z}{}|nht/        xj-                  d      j                  j                  t&              vj-                  d      j                  j                  t&                    \  }z}{}|zj                  t
               d<   { d<   | d<    S )a  
    Generates entry/exit signals using the activation score framework.

    Reads pre-calculated component values from the TradingView export (TV_Export.csv),
    applies proper normalization via calculate_activation_score_poc (matching the
    Pine Script library function), then implements the same crossunder entry/exit
    logic as the Pine Script strategy.

    Parity notes:
    - Prefers non-DB_ columns (which match the inputs used to compute activation_score_poc).
    - Falls back to DB_ columns if non-DB columns are absent.
    - Entry:  score crosses FROM ABOVE to BELOW i_long_entry_activation_threshold
              (Pine: ta.crossunder(activation_score_poc, entry_threshold))
    - Exit:   score crossed under i_long_exit_activation_threshold one bar ago
              AND current score < i_long_exit_activation_confirmation_threshold
              (Pine: ta.crossunder(activation_score_poc[1], exit_threshold)
                     AND activation_score_poc < exit_conf_threshold)
    i_div_windowr   )
rsid_reg_bull_normrsid_reg_bear_normrsid_hid_bull_normrsid_hid_bear_normrsid_rt_bull_normrsid_rt_bear_normrsid_slow_bull_normrsid_slow_bear_normrsid_delayed_peak_normrsid_delayed_dip_norm)min_periodsc                     | j                   v r|    j                  d      j                  S |r,|j                   v r|   j                  d      j                  S t        j                  t                    S )zLReturn column values, preferring primary over fallback, defaulting to zeros.r   )columnsfillnavaluesr   r   r   )primaryfallbackdfs     r)   get_colz!generate_signals.<locals>.get_col   sf    bjj g;%%a(///BJJ.h<&&q)000xxB  r+   
stoch_normmacd_pred_normosc_normtotalvol_normm3_momentum_normm2_tiny_normnewaddr_normstoch_div_normvwap_div_normstoch_peak_normsendaddr_normm3_div_normm2_div_normm2_nooff_normbearish_engulfing_score
db_bearishbullish_hammer_scorebullish_engulfing_scoreshooting_star_scoreclosehigh	i_w_stochgGz@i_w_macd_predg     @Q@i_w_oscgffffffi_w_totalvolr   i_w_m3_momentumg_vO?i_w_m2_tinyg      Q@i_w_newaddri_w_stoch_div_oscgQi_w_vwap_div_oscg      7@i_w_stoch_peakingg
ףp=
i_w_sendaddri_w_m3_div_oscgv/	i_w_m2_div_oscg      K@i_w_m2_div_osc_noOffsetr   i_w_bearish_engulfingg     C@i_w_bullish_hammeri_w_bullish_engulfingi_w_shooting_stari_w_btc_spx_corri_w_dxyi_w_vixi_w_btc_dom	i_w_us10yi_w_spyi_w_goldi_w_mvrvi_w_mvrv_conti_w_nupli_w_fed_net_liqi_w_gc_positioni_w_us2yi_w_yield_curvei_w_qqq_spy_ratioi_w_rsid_reg_bulli_w_rsid_reg_beari_w_rsid_hid_bulli_w_rsid_hid_beari_w_rsid_rt_bulli_w_rsid_rt_beari_w_rsid_slow_bulli_w_rsid_slow_beari_w_rsid_delayed_peaki_w_rsid_delayed_dip
i_w_oi_roc
i_w_usdt_d	i_w_basisi_w_fear_greedi_w_btc_goldi_w_rsi_subtfbtc_spx_corr_30dxy_roc_normvix_pctrank_invbtc_dom_roc_signus10y_roc_inv_signspy_above_200emagold_roc_pctrankmvrv_zscore_valuemvrv_zscore_cont	nupl_normfed_net_liq_signgc_positionus2y_roc_inv_signyield_curve_signqqq_spy_roc_signr.   r/   r0   r1   r2   r3   r4   r5   r6   r7   oi_roc_normusdt_d_norm
basis_normfear_greed_normbtc_gold_normrsi_subtf_normr        @@activation_score!i_long_entry_activation_thresholdg     Z@ i_long_exit_activation_thresholdg     a@-i_long_exit_activation_confirmation_thresholdg4@@i_use_long_exit_confirmationi_use_long_entry_confirmationFi_regime_windowi_regime_entry_min_scoreg     @)windowr8   T)indexi_mvrv_suppress_bearmvrv_regime   
fill_valuei_trailing_stop_threshold      Y@r"   execute_entryexecute_exitN)copyr:   strlowerstripintgetrollingmaxr<   astyper   float64absfloatmeanpdSeriesr   r   shiftr*   r;   r   )}r?   params_div_window
_rsid_cols_colr@   stoch_nmacd_pred_nosc_n
totalvol_nm3_mom_n	m2_tiny_n	newaddr_nstoch_div_n
vwap_div_nstoch_peak_n
sendaddr_nm3_div_nm2_div_n
m2_nooff_n	bearish_n
bull_ham_n
bull_eng_nstar_nr   r   w_stochw_macd_predw_osc
w_totalvolw_m3_momentum	w_m2_tiny	w_newaddrw_stoch_div_oscw_vwap_div_oscw_stoch_peaking
w_sendaddrw_m3_div_oscw_m2_div_oscw_m2_div_osc_nooffsetw_bearish_engulfingw_bullish_hammerw_bullish_engulfingw_shooting_starw_btc_spx_corrw_dxyw_vix	w_btc_domw_us10yw_spyw_goldw_mvrvw_mvrv_contw_nuplw_fed_net_liqw_gc_positionw_us2yw_yield_curvew_qqq_spy_ratiow_rsid_reg_bullw_rsid_reg_bearw_rsid_hid_bullw_rsid_hid_bearw_rsid_rt_bullw_rsid_rt_bearw_rsid_slow_bullw_rsid_slow_bearw_rsid_delayed_peakw_rsid_delayed_dipw_oi_rocw_usdt_dw_basisw_fear_greed
w_btc_goldw_rsi_subtfbtc_spx_corrr   
vix_pr_invbtc_dom_signus10y_inv_sign
spy_200emagold_pctrankmvrv_zscore_valr   r   r   r   us2y_inv_signyield_curve_n	qqq_spy_nrsid_reg_bull_nrsid_reg_bear_nrsid_hid_bull_nrsid_hid_bear_nrsid_rt_bull_nrsid_rt_bear_nrsid_slow_bull_nrsid_slow_bear_nrsid_dpeak_nrsid_ddip_noi_roc_nusdt_d_nbasis_nfear_greed_n
btc_gold_nrsi_subtf_nscores	max_scoreentry_thresholdexit_thresholdexit_conf_thresholduse_exit_confuse_entry_confscoreregime_window
regime_min	regime_oksuppress_bearexit_raw
stoch_peak	entry_rawr   r   r    r!   s}   `                                                                                                                            r)   generate_signalsr1  h   s%   & 
B%%'++113BJ fjj34KQ

 Drzz!d8++KQ+GKKM4 ! <(G+,K:&E?+J-.H>*I>*I+,K?+J,-L?+J=)H=)H?+J4lCI12J45J01F7""))"**5I6
!!((4H #JJ{$GG"JJ$GK"JJy$GE"JJ~#FJ"JJ'8&IM"JJ}$GI"JJ}#FI"JJ':%HO"JJ'9$GN"JJ':%HO"JJ~#FJ"JJ'7&IL"JJ'7$GL"JJ'@#F"JJ'>$G"JJ';3G"JJ'>3G"JJ':3GO"JJ'93GN"JJy3GE"JJy3GE"JJ}3GI"JJ{3GG"JJy3GE"JJz3GF"JJz3GF"JJ3GK"JJz3GF"JJ'83GM"JJ'83GM"JJz3GF"JJ'83GM"JJ':3GO"JJ':3GO"JJ':3GO"JJ':3GO"JJ':3GO"JJ'93GN"JJ'93GN"JJ';3G"JJ';3G"JJ'>3G"JJ'=3G"JJ|3GH"JJ|3GH"JJ{3GG"JJ'73GL"JJ~3GJ"JJ3GK 01L~.L01J12L34N12J12L23O12{+I12}-K23M12M12I34O34O34O34O23N23N454578L67K }-H}-H|,G01L 1J/0K 	w{"	#u	 	z!	" 	}$		%
 	y 	! 	y 	! 	&	' 	~%	& 	&		' 	z!
	" 	|#	$ 	**	+ 	,,	- 	|#	$ 	''	(  	**!	+" 	&#	'$ 	~%%	&& 	u'	( 	u)	* 	y +	!, 	 -	!. 	u/	0 	v1	2 	6!3	"4 	;&5	'6 	v7	8 	=(9	): 	}$;	%< 	=	> 	%?	&@ 	&A 	'B 	?*C!	+D 	?*E"	+F 	?*G#	+H 	?*I$	+J 	>)K%	*L 	>)M&	*N 	++O'	,P 	++Q(	,R 	..S)	/T 	--U*	.V 	8#W+	$X 	8#Y,	$Z 	7"[-	#\ 	<'].	(^ 	:%_/	&` 	;&a0	' h 	Gs;''#e*4s:FM	 ^	,.1)n	=?B??S	TN	!/2	358_	EGJ<GX	Y 	 	! $''<#=	> ADL@Q	R 			 !$$7 8		9 <?;O		P
 	N	
 "%j	)
 ,/u:	6
 9<I	G 	G	 5z	" %(K	0 	F	 +&	' *-V	5 	M	 !/	0 	F		 -(		) ,/+?		@ 	O
	  #?3
	4 	O	  #?3	4 	N	  #>2	3 		 !$$4 5	6 	 	! $''9#:	; 	H	 H	& ),G	5  	L!	   
O!	,  /2+.>!	? & 1})#f,#B !**%HW\]O **%GW\]N **%TW^_ **%CWZ[M **%DW\]N!"E 

#4a89M&**%?IJJqMMAMFKKMPZZ	IId"((3	
 $:EBCM"**4M!2a!78	 [[^~-[[^n,.((* 	 YY|q0A
[[^~-[[^n,. 	  [[^.[[^o-/U[[^#% ^^A%^001 	 [[^65?;RS	 I%I 6::&A3GH5PN(<U#**11$7OOE"))006)
%
I )<U#**11$7OOE"))006)
%
I
 !--,BzN$B#B~Ir+   c           	      	   | j                         } | d   j                         | d<   | d   j                  d      | d   z  | d<   | dxx   | d   j                  dd      j                  t              t
        z  z  cc<   | dxx   | d	   j                  d      j                  d
      j                  t              t
        z  z  cc<   | }|fd| j                  v rXt        j                  |      }| d   j                  j                  |j                  d      }| | d   |k\     j                         }|j                  dg      }t        |      dkD  rt	        |d   j!                         dz        nd}t        |      dk(  rddddddd|dS d|d   z   j#                         dz
  }|dz  }d|d   z   j%                         }	|	j'                         }
|	|
z
  |
z  }t	        |j)                         dz        }|d   j+                         j-                         j/                  dd      }||}n	 d|j                  v rt        |      dkD  rrt        j                  |d   j0                  d         }t        j                  |d   j0                  d         }t3        d||z
  j4                  dz        }t7        |      }n|t8        nt:        }||k  rt?        |      |dddd||dS |d   j!                         }|d   jA                         }|dkD  r||z  tC        jD                  d      z  nd}tC        jD                  tC        j                   tC        jF                  d|d   jH                        dz              }|dkD  r||z  tC        jD                  d      z  nd}d|d   z   j%                         }|j'                         }||z
  |z  }|j)                         dz  }t        |      dz  }|dkD  r6d|z   dkD  r.tK        |      dkD  r d|z   d|z  z  dz
  dz  }|tK        |      z  }nd}d}tK        |      dk\  rtK        |      |k  r|tK        |      z  }nd}t?        |      |||||||dS # t<        $ r |t8        nt:        }Y w xY w)a_  
    Calculates performance metrics based on the 'position' column.
    position=1 on the entry bar means we entered at that bar's close.
    strategy_return[i] = position[i-1] * pct_change[i]  (next-bar return)

    score_start: optional ISO date string (e.g. "2019-01-01"). When provided, signals
    are generated on the full window but metrics are computed only on bars on/after this
    date. Trades that opened before score_start but are still active at score_start
    contribute returns from score_start onwards. MIN_SCORABLE_TRADES replaces MIN_TRADES
    as the minimum trade guard when a scoring window is active.

    min_trades: optional override for the minimum trade count threshold. When provided,
    takes precedence over MIN_TRADES / MIN_SCORABLE_TRADES. Used by oos_dashboard to
    pass a scaled OOS threshold based on the IS trade rate.
    rT   
pct_changer"   r   strategy_returnr   Fr   r   )r   timeUTC)subsetr   d   r         $)zTotal TradeszTotal P&L %zSharpe RatiozSortino RatiozCalmar RatiozP&L/DD RatiozMax Drawdown %z% In Marketr         ?g     v@im  r   &.>g?r   r   g      D@)&r   r3  r   r   r   COMMISSION_RATEr;   infer_objectsr:   r   to_datetimedttztz_localizedropnar   r   prodcumprodcummaxmindiffvalue_countsr   ilocr   daysr   MIN_SCORABLE_TRADES
MIN_TRADES	Exceptionr   stdr   sqrtminimumr<   r   ) r?   score_start
min_tradesdf_scorescore_tsdf_cleanpct_in_markettotal_returntotal_pnl_pctcumulative_returns_prepeak_predrawdown_prereal_max_drawdowntradesmin_requiredt0t1is_yearsmean_return
std_returnsharpedownside_stdsortinocumulative_returnspeakdrawdownmax_drawdownn_yearsannualized_return_pctcalmarMAX_DRAWDOWN_CAPpnl_dd_ratios                                    r)   calculate_metricsrp    s     
B'{--/B|zN003b6FFB
 R066qU6KRRSXY\kkkR/66u=KKQVKW^^_dehwww H6RZZ#7>>+.f:=='++E2HbjH,-224'8&9:H@CHPQ@QE(:.335;<WZM
8} !# %u #"%mE 	E
 !23399;a?L 3&M  (+<"==FFH%,,.H*X5ALl..0367j!&&(557;;AqAF!		Z)))c(ma.?^^HV$4$9$9!$<=^^HV$4$9$9"$=>sR"WNNV$;<-h76A6M2S] K(!"!/(	
 		
 ,-224K,-113J:Dq.kJ&"''#,6eF 772772::a:K1L1S1S#TXY#YZ[L=ID=P{\)RWWS\9V[Gh'899BBD$$&D"T)T1H<<>C'L
 (mf$G{L(D0S5F5L"#l"2g!F!LPU U&\):: 
<CC$59I$I$s<'88 F$ $&$	 	_  	Z2=2I.zL	Zs   ,BQ& &R Rc                      j                           j                  j                  j                         j                  j	                          _        d5 fd	}|Ft        j                  |D cg c]
  } ||       c}      j                  t
        j                        S g  |d       |d       |d       |d       |d       |d       |d       |d	       |d
       |d       |d       |d       |dd       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d       |d        |d!       |d"       |d#       |d$       |d%       |d&       |d'       |d(       |d)       |d*       |d+       |d,       |d-       |d.       |d/       |d0       |d1       |d2       |d3       |d4      }t        j                  |      j                  t
        j                        S c c}w )6u  
    Reads pre-normalised indicator columns from the TV-exported CSV and returns
    a float64 (T x N) array ready for the GPU kernel / MLP forward pass.

    All columns are already in [-1, +1] — Pine exports the normalised form.
    No normalisation is performed here.  Column order must match i_w_* key
    order in params_strategy_activation_scores.json.

    feature_cols: if provided, build the matrix from exactly those column names
    (in order), falling back to 0 for any missing column.  When None, uses the
    full hardcoded 49/50-column list below (perceptron / GPU kernel path).

    col 0  → i_w_stoch               → stoch_norm
    col 1  → i_w_macd_pred           → macd_pred_norm
    col 2  → i_w_osc                 → osc_norm
    col 3  → i_w_totalvol            → totalvol_norm
    col 4  → i_w_m3_momentum         → m3_momentum_norm
    col 5  → i_w_m2_tiny             → m2_tiny_norm
    col 6  → i_w_newaddr             → newaddr_norm
    col 7  → i_w_stoch_div_osc       → stoch_div_norm
    col 8  → i_w_vwap_div_osc        → vwap_div_norm
    col 9  → i_w_stoch_peaking       → stoch_peak_norm
    col 10 → i_w_sendaddr            → sendaddr_norm
    col 11 → i_w_m3_div_osc          → m3_div_norm
    col 12 → i_w_bearish_engulfing   → bearish_engulfing_score
    col 13 → i_w_m2_div_osc_noOffset → m2_nooff_norm
    col 14 → i_w_m2_div_osc          → m2_div_norm
    col 15 → i_w_bullish_hammer      → bullish_hammer_score
    col 16 → i_w_bullish_engulfing   → bullish_engulfing_score
    col 17 → i_w_shooting_star       → shooting_star_score
    col 18 → i_w_btc_spx_corr        → btc_spx_corr_30
    col 19 → i_w_dxy                 → dxy_roc_norm
    col 20 → i_w_vix                 → vix_pctrank_inv
    col 21 → i_w_btc_dom             → btc_dom_roc_sign
    col 22 → i_w_us10y               → us10y_roc_inv_sign
    col 23 → i_w_spy                 → spy_above_200ema
    col 24 → i_w_gold                → gold_roc_pctrank
    col 25 → i_w_mvrv                → mvrv_zscore_value
    col 26 → i_w_mvrv_cont           → mvrv_zscore_cont
    col 27 → i_w_nupl                → nupl_norm
    col 28 → i_w_fed_net_liq         → fed_net_liq_sign
    col 29 → i_w_gc_position         → gc_position
    col 30 → i_w_us2y                → us2y_roc_inv_sign
    col 31 → i_w_yield_curve         → yield_curve_sign
    col 32 → i_w_qqq_spy_ratio       → qqq_spy_roc_sign
    col 33 → i_w_rsid_reg_bull       → rsid_reg_bull_norm
    col 34 → i_w_rsid_reg_bear       → rsid_reg_bear_norm
    col 35 → i_w_rsid_hid_bull       → rsid_hid_bull_norm
    col 36 → i_w_rsid_hid_bear       → rsid_hid_bear_norm
    col 37 → i_w_rsid_rt_bull        → rsid_rt_bull_norm
    col 38 → i_w_rsid_rt_bear        → rsid_rt_bear_norm
    col 39 → i_w_rsid_slow_bull      → rsid_slow_bull_norm
    col 40 → i_w_rsid_slow_bear      → rsid_slow_bear_norm
    col 41 → i_w_rsid_delayed_peak   → rsid_delayed_peak_norm
    col 42 → i_w_rsid_delayed_dip    → rsid_delayed_dip_norm
    col 43 → i_w_oi_roc              → oi_roc_norm
    col 44 → i_w_usdt_d              → usdt_d_norm
    col 45 → i_w_basis               → basis_norm
    col 46 → i_w_fear_greed          → fear_greed_norm
    col 47 → i_w_btc_gold            → btc_gold_norm
    col 48 → i_w_rsi_subtf           → rsi_subtf_norm
    c                    | j                   v r;|    j                  d      j                  j                  t        j
                        S |rI|j                   v r;|   j                  d      j                  j                  t        j
                        S t	        j                  t              t        j
                        S )Nr   r   )r:   r;   r<   r   r   r   r   r   )colr>   r?   s     r)   r   z_prepare_features.<locals>.get  s    "**c7>>!$++222::>>BJJ.h<&&q)0077

CCxxBrzz22r+   rA   rB   rC   rD   rE   rF   rG   rH   rI   rJ   rK   rL   rO   rP   rN   rM   rQ   rR   rS   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r   r   r   r   r   r   bb_pct_b_normr   )	r   r:   r   r   r   r   column_stackr   r   )r?   feature_colsr   ccolss   `    r)   _prepare_featuresry  f  s   ~ 
B%%'++113BJ3 =1A=>EEbjjQQ3L33 	J3 	O	3
 	3 	N3 	N3 	3 	O3 	3 	O3 	M3 	%|43 	O3 	M3  	"#!3" 	%&#3$ 	!"%3& 	'3( 	N)3* 	+3, 	-3. 	 !/30 	132 	334 	 536 	738 	K93: 	;3< 	M=3> 	 ?3@ 	A3B 	C3D 	 !E3F 	 !G3H 	 !I3J 	 !K3L 	 M3N 	 O3P 	!"Q3R 	!"S3T 	$%U3V 	#$W3X 	MY3Z 	M[3\ 	L]3^ 	_3` 	Oa3b 	c3d 	Oe3Dj ??4 ''

33q  >s   .I&c                    t        j                  d      }||j                  d   k\  ry| j                  d   }	| j                  d   }
d}d}d}d}d}d}d}d}||df   }||df   }||df   }||df   d	z  }||df   d
kD  }||df   d
kD  }d}t        |
      D ]  }|||f   }||dk\  r|n| z  } |dk(  rd}d}t        |
      D ]  }|| d|f   |||f   z  z  } ||z  dz  }|}d}d}t        d|	      D ]e  } d}!|r$|dkD  r||    |kD  r||    }|d|z
  z  }"||    |"k  rd}!d}#|r"|| dz
     dk7  r||    || dz
     z
  || dz
     z  }#||#z  }||#|#z  z  }|#dk  r||#|#z  z  }|d|#z   z  }||kD  r|}|dkD  r||z
  |z  nd}$|$|k  r|$}d}%t        |
      D ]  }|%| | |f   |||f   z  z  }% |%|z  dz  }%d}&d}'d}(||k\  xr ||k  })|)r|r|%|k  rd}(n| | df   dk  rd}(|s"|r||k\  r||k  r|%|kD  r|sd}&n||k\  r|%|k  rd}&|r|(s|!rd}'|r"|'rId}d}|dz  }|dkD  r||z
  |z  nd}$|$|k  r,|$}n)|&r'd}||    }|dz  }|dz  }|dkD  r||z
  |z  nd}$|$|k  r|$}|}|%}|(}h |dz
  d	z  }*t	        |d	z        }+d},d}-|dk\  rQ|	dkD  rL|	dz
  }.||.z  }/||.z  |/|/z  z
  }0|0dkD  r|0d
z  nd}1|1dkD  r|/|1z  dz  },||.z  }2|2dkD  r|2d
z  nd}3|3dkD  r|/|3z  dz  }-|*||df<   |+||df<   |,||df<   |-||df<   |||df<   y)uE  
        GPU kernel: runs one complete backtest per thread (one parameter set per row
        of weights/thresholds/configs).

        features  — (T, 49) pre-normalised float64 feature matrix
        weights   — (N, 49) float64 weight vectors
        thresholds — (N, 4) float64: [entry_threshold, exit_threshold, exit_conf_threshold, trailing_stop_pct]
        configs   — (N, 2) float64: [use_exit_conf, use_entry_conf]
        close     — (T,) float64 close prices
        high      — (T,) float64 bar high prices (for trailing stop high-water mark)
        low       — (T,) float64 bar low prices  (for trailing stop trigger)
        results   — (N, 5) output: [total_pnl_pct, max_dd_pct, sharpe, sortino, trades]

        Signal logic (matches CPU generate_signals):
          Entry (conf=false): 1-bar crossunder — prev_score >= entry_thr AND score < entry_thr
          Entry (conf=true) : 2-bar crossunder — pprev_score >= entry_thr AND prev_score < entry_thr
                              AND score > prev_score (rising) AND NOT prev_exit_cond_raw
          Exit  : 2-bar crossunder + confirmation
                  pprev_score >= exit_thr AND prev_score < exit_thr
                  AND (score < exit_conf_thr  [if use_exit_conf]
                       OR  stoch_is_peaking  [otherwise, feature col 9 < -0.5])
        r   r   NFr   r   r      r   r:  r   T	   g      gףp=
?r9  r
   r;  g^3@   )r	   gridshaper   r   )4featuresweights
thresholdsconfigsrT   rU   lowresultsidxTFin_positionr]  sum_ret
sum_sq_retsum_sq_neg_retequitypeak_equityrj  	entry_threxit_thrconf_thrr   r'  r(  r#  fw
prev_scorepprev_scoreprev_exit_cond_rawr#   r%   trail_stop_hitstop_price_now	daily_retddr)  entry_signalexit_signalexit_cond_rawcrossedrX  
max_dd_pctrd  rf  r   mean_retvar_retstd_retdownside_varre  s4                                                       r)   _backtest_kernelr    s   0 iil'--""NN1NN1
#CF+	#CF+#CF+#CF+e3 a3. a3. 	qAQAa3hQB.I  I 
qA(1a4.736?::J )+f4
 "
q!A #N~3 7Z'!%aJ!+s^/C!D8~-%)N IuQU|s2"1Xa!e4a!eD	y G)i//J3)i"77sY'F#$9Ds9J&;&+5PSBL ! E1X!Q$'#q&/99 I%.E LK "M!X-G*x2GG x'(,  1~,(, !"i/J4JuWaOaj|'+ "Y.593D'+ ""'K!$J{+FALsAR&;.+=X[BL(')"&K!&qJaKF{+FALsAR&;.+=X[BL(')$KJ!.s x  #.-.
 R<AEAA{H!A~(X*=>G(/$gnCG~"W,>)A-L2>2E<3.3Ld"#l2|D'Q$Q Q!Q Qr+   c                 h   | j                         } | j                  j                  j                         j                  j	                         | _        t        |       }| d   j                  j                  t        j                        }| d   j                  j                  t        j                        }| d   j                  j                  t        j                        }t        j                  |      t        j                  |      t        j                  |      t        j                  |      dS )zCNormalises features on CPU and uploads static data to the GPU once.rT   rU   r  )r  rT   rU   r  )r   r:   r   r   r   ry  r<   r   r   r   r	   	to_device)r?   r  rT   rU   r  s        r)   prepare_gpu_datar    s    WWYZZ^^))+//557
$R(7""))"**56
!!((45	  ''

3x0u-t,s+	
 	
r+   c           
      @   |j                   d   }t        j                  |j                  t        j
                              }t        j                  |j                  t        j
                              }t        j                  |j                  t        j
                              }t        j                  t	        j                  |dft        j
                              }d}	||	z   dz
  |	z  }
t        |
|	f   | d   |||| d   | d   | d	   |       |j                         S )
z=Launches the backtest kernel for a batch of N parameter sets.r      r      r   r  rT   rU   r  )	r  r	   r  r   r   r   r   r  copy_to_host)gpu_datar  r  r  N	d_weightsd_thresholds	d_configs	d_resultsthreadsblockss              r)   execute_gpu_batchr    s    MM!~~gnnRZZ&@A	~~j&7&7

&CD~~gnnRZZ&@A	~~bhh1vRZZ&HI	w;?w.)Z )\9Wx/%)	
 %%''r+   r   )NN)$numpyr   pandasr   ossyspathappendabspathjoindirname__file__configr   $strategies.library_activation_scoresr   r   r   r   r   numbar	   is_availableHAS_GPUrM  rL  rK  r<  r*   r1  rp  ry  jitr  r  r   r+   r)   <module>r     s      	 
 RWW__X-F MN O ! 
d!G 
    &*?)DJZ
}bB4J 	XXr! r!h
(K w  Gs   C C C 