
    KJj
W              	       x   d Z ddlZ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mZmZ ddlmZ g dZdZg dZg d	Zd
 Zd Z	 	 ddZi Zd Z	 ddZd Zd Zd Z y)u  
MLP-based activation score strategy.

Replaces the single-layer weighted sum of strategy_activation_scores.py with a
small multi-layer perceptron over the pre-normalised feature columns (55 for
BTC, 50 for alts; see FEATURE_COLS / the artifact's feature_cols):

    score = 1000 * tanh( W3 . tanh( W2 . tanh(W1.x + b1) + b2 ) + b3 )

Everything downstream of the score — crossunder entry/exit, confirmation,
trailing stop, regime/MVRV gates, metrics — is identical to the existing
strategy (imported or mirrored verbatim), so results stay directly comparable
and the TV-parity workflow carries over.

Weights are NOT optimizer params: they live in a JSON artifact
(strategies/params/mlp/mlp_weights_{ASSET}_{TF}.json) produced by
tools/train_mlp.py and referenced via the `mlp_weights_file` param.
The same weights are embedded into strategy_mlp_scores.pine as array literals
by tools/generate_pine_mlp_presets.py, with tanh implemented in Pine by the
identical clamped closed form (Pine v6 has no math.tanh):

    tanh(x) = x >= 20 ?  1.0
            : x <= -20 ? -1.0
            : (exp(2x) - 1) / (exp(2x) + 1)

Pilot limitations (documented intentionally):
  - `i_div_window` is unsupported — the MLP consumes raw single-bar RSID flags,
    matching _prepare_features()'s window=1 behaviour.
    Nz..)_apply_trailing_stop_prepare_featurescalculate_metrics)calculate_positions)	FEATURE_COLSFEATURE_WEIGHT_PARAMSACTIVATION_NAME_tanhmlp_forwardsave_mlp_artifactload_mlp_artifactgenerate_signalsr   tanh_closed_form_clamp20)7
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bearish_engulfing_scorem2_nooff_normm2_div_normbullish_hammer_scorebullish_engulfing_scoreshooting_star_score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	sopr_norm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oi_roc_normusdt_d_norm
basis_normcvd_normbtc_gold_normrsi_subtf_normbb_pct_b_normr>   	rvol_normactive1y_normhr_normr0   )2	i_w_stochi_w_macd_predi_w_osci_w_totalvoli_w_m3_momentumi_w_m2_tinyi_w_newaddri_w_stoch_div_osci_w_vwap_div_osci_w_stoch_peakingi_w_sendaddri_w_m3_div_osci_w_bearish_engulfingi_w_m2_div_osc_noOffseti_w_m2_div_osc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i_w_bb_pct_bc                     t        j                  | t         j                        } t        j                  |       }| dk\  }| dk  }||z   }d||<   d||<   t        j                  d| |   z        }|dz
  |dz   z  ||<   |S )a  Clamped closed-form tanh, formula-identical to the Pine implementation.

    Inside (-20, 20): (exp(2x) - 1) / (exp(2x) + 1). At |x| >= 20 returns
    exactly +/-1.0 (Pine and Python clamp at the same point so neither side
    can diverge in the saturated region).
    dtypeg      4@g      4      ?g      g       @)npasarrayfloat64
empty_likeexp)xouthilomide2xs         w/Users/jameslopez/projects/TradingBot25/.worktrees/mlp-results-and-research-learnings/strategies/strategy_mlp_scores.pyr
   r
      s     	

1BJJ'A
--
C	
dB	
eBG*CCGCG
&&qv
Cc	cCi(CHJ    c                    t        j                  | t         j                        }|D ]  \  }}t        j                  ddd      5  |t        j                  |t         j                        j                  z  t        j                  |t         j                        z   }ddd       t              } |dddf   dz  S # 1 sw Y   "xY w)a	  Forward pass: X (T, F) float64, layers = [(W, b), ...] with W (n_out, n_in).

    Every layer (including the output layer) applies _tanh; the final scalar is
    scaled by 1000 so scores live in [-1000, +1000] like the existing
    activation-score convention.
    rx   ignore)divideoverinvalidNr   g     @@)r{   r|   r}   errstateTr
   )XlayersaWbzs         r   r   r      s     	

1BJJ'A1[[xJBJJq

3555

1BJJ8WWA K!H  QT7V KJs   AC  C		c                 \   |t        |      nt        t              }t        t        j                  |d   d         j
                  d         g|D cg c]0  \  }}t        t        j                  |      j
                  d         2 c}}z   }d|||t        ||D 	cg c]l  \  }}	t        j                  |t        j                        j                         t        j                  |	t        j                        j                         dn c}	}|xs i d}
t        j                  t        j                  j                  t        j                  j                  |             d       t        | d	      5 }t        j                   |
|       ddd       yc c}}w c c}	}w # 1 sw Y   yxY w)
zWrites the weights artifact JSON. Floats serialised via repr (full
    precision) by json, so load() round-trips bit-exact float64.Nr      rx   )r   r   )versionasset	timeframearch
activationfeature_colsr   trainingT)exist_okw)listr   intr{   r|   shaper	   r}   tolistosmakedirspathdirnameabspathopenjsondump)r   r   r   r   r   r   r   _r   r   payloadfs               r   r   r      sV    *6)A4%tLGYL

6!9Q<(..q123067061BJJqM"#7 D %$ 
 1 **Qbjj188:**Qbjj188:<

 NG KK 56F	dCA		'1 
%7
 
s   5FA1F6F""F+c                    t         j                  j                  |       }t         j                  j                  |      }t        j                  |      }||d   |k(  r|d   S t        |      5 }t        j                  |      }ddd       d   D cg c]R  }t        j                  |d   t        j                        t        j                  |d   t        j                        fT }}||d   |d	   |j                  d
      |j                  d      |j                  d      |j                  d      |j                  di       dd}||ft        |<   |S # 1 sw Y   xY wc c}w )a  Loads (and caches by path+mtime) a weights artifact.

    Returns {"layers": [(W, b), ...] float64, "arch": [...],
             "feature_cols": [...], "meta": {...}}.

    Note: artifacts trained on the 49-feature set (pre-bb_pct_b_norm) are still
    loadable via load_mlp_artifact() and mlp_results_table; they carry their own
    feature_cols list and will fall back to 0 for any missing column.
    Nr   r   r   r   rx   r   r   r   r   r   r   r   r   )r   r   r   r   r   )r   r   r   meta)r   r   r   getmtime_ARTIFACT_CACHEgetr   r   loadr{   arrayr}   )	r   r   mtimecachedr   r   layerr   arts	            r   r   r      sA    ggood#GGGW%E  )FfQi50ay	g!))A, 
 X&&E 
%*BJJ	/%*BJJ1WX&  
 /{{9-[[) [1!++l3J3
	C !&s|OGJ' 
s   .EAE$E!c	                    t        |      }	t        j                  |	t        j                        }
t        j                  |	t              }t        j                  |	t              }d}d}d}d}t        |	      D ]  }|dnt	        ||         }|rB||k7  r||   |kD  r||   }|d|z
  z  }||   |k  }|r||   n||   }|s|r0|r.d}d||<   d}d}d}n | |   r|rd}d||<   ||   }|dz   }||   |k  }|rdnd|
|<    |
||fS )	u  Like _apply_trailing_stop but with dead-zone guard for exit smoothing.

    When a trade enters while score_exit[entry_bar] < exit_threshold (i.e. the
    SMA is already below the exit threshold — the "dead-zone"), the smoothed
    crossunder can never fire for that trade because there is no transition from
    above to below the threshold.  In that case we switch to the raw-score
    crossunder (exit_raw_fallback_arr) for the duration of that trade only.

    time_ok_arr (optional bool array): see _apply_trailing_stop — Pine gates
    strategy.close on timeCondition, so entries/exits only fire where
    time_ok_arr[t] is True; a position open at window end is held open.
    rx   F        Trz   r   r   lenr{   zerosint32boolrange)entry_raw_arrexit_raw_arrexit_raw_fallback_arrscore_exit_arrexit_threshold	close_arrhigh_arrtrail_stop_pcttime_ok_arrnin_pos
exec_entry	exec_exitposition
trade_highfill_baruse_rawtallowed
stop_price	trail_hitexit_sigs                         r   _apply_trailing_stop_with_dzr      s?     	IA!288,F!4(J!4(IHJHG1X%-$4A3GH}!z!9%a[
#s^';<J"13I3:,Q/QHX7"#	! 
"QG! $
1&q\
U+A.?!Aqq	/ 2 :y((r   c                 n   t        |       }t        j                  |t        j                        }t        j                  |t              }t        j                  |t              }d}	d}
t        |      D ]9  }|	r|
r||   n||   }|rd}	d||<   d}
n| |   rd}	d||<   ||   |k  }
|	rdnd||<   ; |||fS )zELike calculate_positions but with dead-zone guard (no trailing stop).rx   FTr   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   s                r   _calculate_positions_with_dzr   2  s     	MA!288,F!4(J!4(IHG1X3:,Q/QH #	!Q $
1(+n<!Aqq	  :y((r   c                 2
   |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  d	d
      }	| d   }
t        dt        |j                  dd                  }|dkD  r!|
j                  |d      j	                         n|
}t        dt        |j                  dd                  }|dkD  r!|
j                  |d      j	                         n|
}t        |j                  dd            }t        |j                  dd            }|dkD  r%|
j                  |d      j	                         |kD  }n!t        j                  d| j                        }t        |j                  dd
            }|rd| j                  v r|| d   dk\  z  }t        j                  |dk7  | j                        }|rf|j                  d      |k\  |j                  d      |k  z  ||k  z  }|dkD  r/|
j                  d      |k\  |
j                  d      |k  z  |
|k  z  n|}n_|j                  d      |k\  |j                  d      |k  z  |z  }|dkD  r,|
j                  d      |k\  |
j                  d      |k  z  |z  n|}|	rT|j                  d      |k\  |j                  d      |k  z  |
|
j                  d      kD  z  |j                  dd
       z  }n|j                  d      |k\  ||k  z  }||z  }|j                  d      }|j                  d      }d}||d| j                  v rt        j                  | d         }t        j                  d| j                        }|||t        j                  |      k\  z  }|||t        j                  |      k  z  }||z  }||z  }||z  }|j                  t              }t        |j                  d d!            d"z  }|j                  d
      j                   j#                  t              }|j                  d
      j                   j#                  t              }|j                  d
      j                   j#                  t              }|j                   } |dkD  r2|d!kD  rt%        |||| |||||#	      \  }!}"}#n?t'        |||| |      \  }!}"}#n+|d!kD  rt)        ||||||#      \  }!}"}#nt+        ||      \  }!}"}#|!j#                  t              | d$<   |"| d%<   |#| d&<   | S )'u
  Threshold/regime/crossunder/position block — mirrors
    strategy_activation_scores.generate_signals lines 332-421 verbatim (that
    block is inline in the original and cannot be imported without modifying
    it). df must already contain 'activation_score'.
    !i_long_entry_activation_thresholdg     Z@ i_long_exit_activation_thresholdg     a@-i_long_exit_activation_confirmation_thresholdg4@@i_use_long_exit_confirmationrz   i_use_long_entry_confirmationFactivation_scorer   i_exit_score_window)min_periodsi_entry_score_windowi_regime_windowr   i_regime_entry_min_scoreg     @)windowr   T)indexi_mvrv_suppress_bearmvrv_regime   )
fill_value_pine_time_start_pine_time_endNtimerx   i_trailing_stop_thresholdr   g      Y@)r   r   execute_entryexecute_exit)r   maxr   rollingmeanfloatpdSeriesr   r   columnsshiftto_datetime	Timestampto_numpyfillnavaluesastyper   r   r   r   )$dfparamsstoch_peak_arrr   r   entry_thresholdr   exit_conf_thresholduse_exit_confuse_entry_confscoreexit_window
score_exitentry_windowscore_entryregime_window
regime_min	regime_oksuppress_bear
stoch_peakexit_rawexit_raw_fallback	entry_raw
time_starttime_endr   r   time_okr   	entry_arrexit_arrfallback_arrr   r   r   r   s$                                       r   _score_to_signalsr  O  s^    !**%HW\]O **%GW\]N **%TW^_ **%CWZ[M **%DW\]N!"E aVZZ(=qABCKEPST_{:??AZ_J q#fjj)?CDELGSVWGW%--!-<AAC]bK 

#4a89M&**%?IJJqMMAMFKKMPZZ	IId"((3	 $:EBCM"**4M!2a!78	 >Q.bhh?Ja N2a >13--/ 	 1_ [[^~-[[^n,.((* #+	 	 a N2a >13 	 1_ [[^~-[[^n,. #+	 	 q!_4q!O35U[[^#% ^^A%^001 	 !&&q)_<A^_	I%I./Jzz*+H K("6Fbjj<P~~bj)))D1!j)A!ABGh)?!?@G'	g%-7&&T&2 6::&A3GH5PN##E*1188>I??5)0077=H$++E299@@FL&&NQC,H8\8^'	-)FJ	 -I8\-)FJ	 
#	(<xHn#)
%
I
 )<Ix(P%
I --,BzN$B#B~Ir   c                    |j                  d      }|st        d      t        |      }|d   }|t        k7  rt        D cg c]	  }||vs| }}|D cg c]  }|t        vs| }}ddl}|rK|D cg c]  }|| j
                  vs| }	}|	rt        d| d|	       |j                  d| d| d	d
       |r6|j                  d| dt        |       dt        t               d| d	d
       | j                         } | j
                  j                  j                         j                  j                         | _        t        | |      }
t        |
|d         | d<   |
dddf   }| d   j                  j                  t         j"                        }| d   j                  j                  t         j"                        }t%        | ||||      S c c}w c c}w c c}w )u  Generates entry/exit signals from the MLP activation score.

    Params:
      mlp_weights_file — path to the weights artifact JSON (required)
      i_long_entry_activation_threshold / i_long_exit_activation_threshold /
      i_long_exit_activation_confirmation_threshold — score thresholds
      i_use_long_entry_confirmation / i_use_long_exit_confirmation — flags
      i_trailing_stop_threshold — pct (0 disables)
      i_regime_window / i_regime_entry_min_score / i_mvrv_suppress_bear — gates
    mlp_weights_filez9strategy_mlp_scores requires the 'mlp_weights_file' paramr   r   Nz	Artifact z, expects columns not in FEATURE_COLS or df: z) expects extra columns (probe features): z'. These will be read from the input df.r   )
stacklevelz was trained on z$ features; current FEATURE_COLS has z (new: u=   ). New columns default to 0 — retrain for full performance.r   r   	   closehigh)r   
ValueErrorr   r   warningsr   warnr   copystrlowerstripr   r   r   r  r{   r}   r  )r  r  weights_filer   art_colscextramissingr$  missing_and_absentr   r  r   r   s                 r   r   r     s    ::01LTUU
L
)C>"H< )>LqAX,=L>&@h!<*?1h@ .5!LW8K!W!L! ~-Y)*,  MML>)RSZR[ \8 8  
 MML>)9#h- I,,/,=+>geW MMM 	   
B%%'++113BJ"h'A(CM:Bq!tWN7""))"**5I6
!!((4HRHMMI ?@ "Ms$    	G
GG"G2GG)NNNN)N)!__doc__r   r   sysnumpyr{   pandasr   r   appendr   joinr   __file__%strategies.strategy_activation_scoresr   r   r   $strategies.library_activation_scoresr   __all__r	   r   r   r
   r   r   r   r   r   r   r  r    r   r   <module>r;     s   :  	 
   RWW__X-F MN O 
 E -8z *&& JN#'6 #Z 043)l):}@8Nr   