Ë
    sw±j§!  ã                   óˆ  — d Z ddlZddlmc mZ ddlZddlZ	ddl
ZddlZddlmZ ddlmZmZmZmZmZ ej,                  j/                  ej,                  j1                  e«      dddd«      ZdZdZ ej:                  d	¬
«      d„ «       Zdd„Zd„ Z ddddddœZ!d„ Z" G d„ d«      Z# G d„ d«      Z$ G d„ d«      Z% G d„ d«      Z&y)u¹  
TDD tests for strategy_mlp_scores.generate_signals.

Core guarantee under test (linear degeneracy): an MLP constructed to be an
(arbitrarily good) linear approximation of the existing perceptron score must
reproduce the existing strategy's behaviour â€” near-perfect score correlation
and identical entry/exit bars when thresholds are scaled by the same factor.
This pins the new module's crossunder/trailing/position logic to the old one.
é    N)ÚFEATURE_COLSÚFEATURE_WEIGHT_PARAMSÚ_score_to_signalsÚgenerate_signalsÚsave_mlp_artifactz..ÚdataÚmlpzCOINBASE_BTCUSD, 360.csvgü©ñÒMbP?Úmodule)Úscopec                  ó(  — t         j                  j                  t        «      st	        j
                  d«       t        j                  t        «      } | j                  j                  j                  «       j                  j                  «       | _        t        j                  | d   d¬«      j                  j                  d «      | d<   | j                  dd«      | d<   | j                  dd«      | d	<   | j!                  d
«      j#                  d¬«      S )Nz8real-export integration requires local BTC 6H chart dataÚtimeT)ÚutcÚqqq_spy_roc_signç        Ú	sopr_normÚfear_greed_normÚcvd_normi¸  )Údrop)ÚosÚpathÚexistsÚDATA_CSVÚpytestÚskipÚpdÚread_csvÚcolumnsÚstrÚlowerÚstripÚto_datetimeÚdtÚtz_localizeÚgetÚtailÚreset_index)Údfs    úg/Users/jameslopez/projects/TradingBot25/.worktrees/mlp-comparable-measurement/tests/test_mlp_signals.pyÚdf_realr)       sÅ   € ä�7‰7�>‰>œ(Ô#Ü�‰ÐNÔOÜ	�‰”XÓ	€BØ—‘—‘×%Ñ%Ó'×+Ñ+×1Ñ1Ó3€B„JÜ—‘  6¡
°Ô5×8Ñ8×DÑDÀTÓJ€B€v�Jð —f‘fÐ/°Ó5€B€{�OØ—V‘VÐ-¨sÓ3€B€z�Nà�7‰7�4‹=×$Ñ$¨$Ð$Ó/Ð/ó    c                 ó|  — ddl }t        |j                  «      }t        j                  j                  | «      }|j                  dd|¬«      }d|t        j                  |«      dk  <   t        t        |j                  |«      «      }t        j                  t        D �cg c]  }||v r||   ‘Œ c}«      }||fS c c}w )uU  Random nonzero weight per feature for the 49 features shared with the perceptron.

    Uses only the params that the perceptron strategy actually reads (config.WEIGHT_COLS).
    bb_pct_b_norm (col 49) is MLP-only and excluded so both strategies normalise by
    the same denominator â€” keeping the linear-degeneracy equivalence intact.
    r   Niœÿÿÿéd   )Úsizeg      @)ÚconfigÚlenÚWEIGHT_COLSÚnpÚrandomÚdefault_rngÚuniformÚabsÚdictÚzipÚarrayr   )Úseedr.   ÚnÚrngÚwÚw_paramsÚparamÚ
artifact_ws           r(   Ú_perceptron_weightsr@   0   s³   € ó ÜˆF×ÑÓ€AÜ
�)‰)×
Ñ
 Ó
%€CØ�‰�D˜# AˆÓ&€AØ€A„b‡f�fˆQƒi�#�oÑÜ”C˜×*Ñ*¨AÓ.Ó/€HÜ—‘å*óá*ˆEØ�HÑð 	�‹Ø*ñó €Jð
 �xÐÐùòs   ÂB9c                 óò  — ddl }t        t        «      D ��cg c]%  \  }}|t        |j                  «      v r	t
        |   ‘Œ' }}}t        |«      }t        |«      }||k(  }|�sHt        j                  d|fd||f«      dt        j                  «       v st        j                  |«      rt        j                  |«      nddt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      dœz  }	t        j                  d	t        |«      › d
|› �«      dz   d|	iz  }
t        t        j                  |
«      «      ‚dx}}|t!        j"                  t!        j$                  |«      «      z  }t!        j&                  d|f«      }t(        |z  |ddd…f<   t!        j&                  d«      }t!        j*                  t,        t(        z  dgg«      }t!        j&                  d«      }t/        | dz  «      }t1        |||f||fgdd|¬«       |S c c}}w )uü  [N, 2, 1] MLP that linearises to DELTA * perceptron score.

    Uses the same N features as the perceptron (config.WEIGHT_COLS) so normalisation
    denominators match.  bb_pct_b_norm (col 49) is excluded â€” the artifact saves
    its own feature_cols list so generate_signals uses the right subset.

    Hidden unit 0 = tanh(EPS * x.w_hat) ~ EPS * s  (w_hat = w / sum|w|, s in [-1,1])
    Output       = tanh((DELTA/EPS) * h0) ~ DELTA * s
    => mlp_score ~ DELTA * 1000 * s = DELTA * perceptron_score
    r   N©ú==)z0%(py0)s == %(py5)s
{%(py5)s = %(py2)s(%(py3)s)
}r:   r/   r<   )Úpy0Úpy2Úpy3Úpy5zweight vector length z != feature subset length z
>assert %(py7)sÚpy7é   r   é   zmlp_weights_DEGEN_6H.jsonÚTESTÚ6H)ÚassetÚ	timeframeÚfeature_cols)r.   Ú	enumerater   Úsetr0   r   r/   Ú
@pytest_arÚ_call_reprcompareÚ@py_builtinsÚlocalsÚ_should_repr_global_nameÚ	_safereprÚ_format_assertmsgÚAssertionErrorÚ_format_explanationr1   Úsumr5   ÚzerosÚEPSr8   ÚDELTAr   r   )Útmp_pathr<   r.   ÚiÚpÚ	feat_colsr:   Ú@py_assert4Ú@py_assert1Ú@py_format6Ú@py_format8Úw_hatÚW1Úb1ÚW2Úb2r   s                    r(   Ú_degenerate_artifactrl   E   s±  € ó Ü-6Ô7LÔ-Mô 2Ñ-M¡T Q¨Øœ˜V×/Ñ/Ó0Ñ0ô ˜a“Ð-M€Iñ 2äˆI‹€AÜ�A“ÐUˆ1�‰;×U×UÖUˆ1�×U×U×U×U×UÑUˆ1×U×UÓUˆ1×U×U×U×U×UÒU”×U×UÓU”×U×U×U×U×UÒU�A×U×UÓU�A×U×UÔU�×U×U×UÐ/´°A³¨xÐ7QÐRSÐQTÐU×U×U×U×U×UÖUØ”—‘”r—v‘v˜a“yÓ!Ñ!€EÜ	�‰�1�a�&Ó	€BÜ�U‰{€B€qŠ!€t�HÜ	�‰�!‹€BÜ	�‰”EœC‘K Ð%Ð&Ó	'€BÜ	�‰�!‹€BÜˆxÐ5Ñ5Ó6€DÜ�d˜b "˜X¨¨B xÐ0¸È$Ø#,õ.à€Kùó2s   —*I3ç      Y@g     €a@g      >@ç      ð?F)Ú!i_long_entry_activation_thresholdÚ i_long_exit_activation_thresholdÚ-i_long_exit_activation_confirmation_thresholdÚi_use_long_exit_confirmationÚi_use_long_entry_confirmationc                  óX   — t        t        «      } dD ]  }t        |   t        z  | |<   Œ | S )N)ro   rp   rq   )r6   Ú
THRESHOLDSr^   )ÚoutÚks     r(   Ú_scaled_thresholdsrx   j   s3   € Ü
ŒzÓ
€Có?ˆô ˜A‘¤Ñ&ˆˆAŠð?ð €Jr*   c                   ó   — e Zd Zd„ Zd„ Zy)ÚTestLinearDegeneracyc                 ó¼  — t        «       \  }}t        ||«      }t        j                  |j	                  «       fi i |¥t
        ¥¤Ž}t        |j	                  «       fd|it        «       ¤Ž}t        j                  |d   |d   «      d   }d}	||	kD  }
|
s™t        j                  d|
fd||	f«      dt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |	«      dœz  }d	d
|iz  }t        t        j                   |«      «      ‚d x}
}	t        j"                  j%                  |d   j&                  |d   j&                  «       t        j"                  j%                  |d   j&                  |d   j&                  «       t        j"                  j%                  |d   j&                  |d   j&                  «       y )NÚmlp_weights_fileÚactivation_score)r   rJ   gé!çýÿï?)Ú>)z%(py0)s > %(py3)sÚcorr)rD   rF   zassert %(py5)srG   Úexecute_entryÚexecute_exitÚposition)r@   rl   Ú
perceptronr   Úcopyru   rx   r1   ÚcorrcoefrR   rS   rT   rU   rV   rW   rY   rZ   ÚtestingÚassert_array_equalÚvalues)Úselfr_   r)   r<   r=   ÚartifactÚoldÚnewr   Ú@py_assert2rd   Ú@py_format4re   s                r(   Ú+test_score_correlation_and_identical_tradesz@TestLinearDegeneracy.test_score_correlation_and_identical_tradest   s[  € Ü)Ó+‰ˆˆ8Ü'¨°!Ó4ˆä×)Ñ)¨'¯,©,«.ÑWÐ<V¸xÐ<VÌ:Ð<VÑWˆÜ˜wŸ|™|›~ÑaÀÐaÔL^ÓL`Ñaˆä�{‰{˜3Ð1Ñ2°CÐ8JÑ4KÓLÈTÑRˆØÐˆt�h‰××Õˆt�h×××××Ñˆt××Óˆt××Ô�h××××××Ñä
�
‰
×%Ñ%Ø�Ñ ×'Ñ'¨¨_Ñ)=×)DÑ)Dô	Fä
�
‰
×%Ñ%Ø�Ñ×&Ñ&¨¨NÑ(;×(BÑ(Bô	Dä
�
‰
×%Ñ% c¨*¡o×&<Ñ&<¸cÀ*¹o×>TÑ>TÕUr*   c                 óJ  — t        d¬«      \  }}t        ||«      }ddi}t        j                  |j	                  «       fi i |¥t
        ¥|¥¤Ž}t        |j	                  «       fd|ii t        «       ¥|¥¤Ž}t        j                  j                  |d   j                  |d   j                  «       t        j                  j                  |d   j                  |d   j                  «       t        j                  j                  |d   j                  |d   j                  «       y )	Né   )r9   Úi_trailing_stop_thresholdg      $@r|   r‚   r€   r�   )r@   rl   rƒ   r   r„   ru   rx   r1   r†   r‡   rˆ   )	r‰   r_   r)   r<   r=   rŠ   Útrailr‹   rŒ   s	            r(   Ú#test_trailing_stop_path_equivalencez8TestLinearDegeneracy.test_trailing_stop_path_equivalence„   s
  € Ü)¨rÔ2‰ˆˆ8Ü'¨°!Ó4ˆØ,¨dÐ3ˆä×)Ñ)¨'¯,©,«.Ñ`Ð<_¸xÐ<_Ì:Ð<_ÐY^Ð<_Ñ`ˆÜ˜wŸ|™|›~ñ DÀð DØ!BÔ$6Ó$8Ð!B¸EÐ!BñDˆô 	�
‰
×%Ñ% c¨*¡o×&<Ñ&<¸cÀ*¹o×>TÑ>TÔUÜ
�
‰
×%Ñ%Ø�Ñ ×'Ñ'¨¨_Ñ)=×)DÑ)Dô	Fä
�
‰
×%Ñ%Ø�Ñ×&Ñ&¨¨NÑ(;×(BÑ(Bõ	Dr*   N)Ú__name__Ú
__module__Ú__qualname__r�   r”   © r*   r(   rz   rz   s   s   „ òVó Dr*   rz   c                   ó   — e Zd Zd„ Zd„ Zy)ÚTestParamValidationc                 ó    — t        j                  t        d¬«      5  t        |j	                  «       fi t
        ¤Ž d d d «       y # 1 sw Y   y xY w)Nr|   ©Úmatch)r   ÚraisesÚ
ValueErrorr   r„   ru   )r‰   r)   s     r(   Ú test_missing_weights_file_raisesz4TestParamValidation.test_missing_weights_file_raises•   s1   € Ü�]‰]œ:Ð-?Ö@Ü˜WŸ\™\›^Ñ:¬zÒ:÷ A×@Ñ@ús   œAÁAc                 óˆ  — t        |dz  «      }t        j                  d«      t        j                  d«      ft        j                  d«      t        j                  d«      fg}t        ||g d¢¬«       t	        j
                  t        d¬	«      5  t        |j                  «       fd
|it        ¤Ž d d d «       y # 1 sw Y   y xY w)Nzmlp_weights_BAD_6H.json)rI   é   rI   )rJ   rI   rJ   )ÚaÚbÚc)rO   rO   rœ   r|   )
r   r1   r\   r   r   rž   rŸ   r   r„   ru   )r‰   r_   r)   r   Úlayerss        r(   Ú#test_mismatched_feature_cols_raisesz7TestParamValidation.test_mismatched_feature_cols_raises™   s†   € Ü�8Ð7Ñ7Ó8ˆÜ—8‘8˜FÓ#¤R§X¡X¨a£[Ð1´B·H±H¸VÓ4DÄbÇhÁhÈqÃkÐ3RÐSˆÜ˜$ ²_ÕEÜ�]‰]œ:¨^Ö<Ü˜WŸ\™\›^ÑQ¸dÐQÄjÒQ÷ =×<Ñ<ús   Â!B8Â8CN)r•   r–   r—   r    r§   r˜   r*   r(   rš   rš   ”   s   „ ò;óRr*   rš   c                   ó   — e Zd Zd„ Zy)ÚTestPineTimeGatec           	      óv  — t        j                  t        j                  g d¢«      g d¢g d¢g d¢dœ«      }ddddd	d
dœ}t        |j	                  «       |t        j                  t        |«      «      |d   j                  t
        j                  «      |d   j                  t
        j                  «      «      }|d   }|j                  } |«       }g d¢}||k(  }|s”t        j                  d|fd||f«      t        j                  |«      t        j                  |«      t        j                  |«      t        j                  |«      dœz  }	dd|	iz  }
t        t        j                  |
«      «      ‚d x}x}x}x}}|d   }|j                  } |«       }g d¢}||k(  }|s”t        j                  d|fd||f«      t        j                  |«      t        j                  |«      t        j                  |«      t        j                  |«      dœz  }	dd|	iz  }
t        t        j                  |
«      «      ‚d x}x}x}x}}y )N)z2017-11-30 16:00z2017-12-01 00:00ú2017-12-01 08:00z2017-12-01 16:00)ç      i@ç      I@r¬   r­   )rm   g     @Y@g     €Y@g     ÀY@)r   r}   ÚcloseÚhighrm   g      Œ@g      ŒÀrn   Fr«   )ro   rp   rq   rr   rs   Ú_pine_time_startr®   r¯   r€   )FFFTrB   )zE%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.tolist
}()
} == %(py8)s)Úpy1rF   rG   Úpy8zassert %(py10)sÚpy10r‚   )r   r   r   rJ   )r   Ú	DataFramer!   r   r„   r1   r\   r/   Úto_numpyÚfloat64ÚtolistrR   rS   rW   rY   rZ   )r‰   r'   ÚparamsÚsignalsÚ@py_assert0r�   rc   Ú@py_assert7Ú@py_assert6Ú@py_format9Ú@py_format11s              r(   Ú0test_pre_start_crossunder_does_not_open_positionzATestPineTimeGate.test_pre_start_crossunder_does_not_open_position¢   s§  € Ü�\‰\Ü—N‘Nò $ó ò !;Ú1Ú0ñ

ó 
ˆð 27Ø05Ø=CØ,/Ø-2Ø 2ñ
ˆô $Ø�G‰G‹IØÜ�H‰H”S˜“WÓØˆw‰K× Ñ ¤§¡Ó,Øˆv‰J×Ñ¤§
¡
Ó+ó
ˆð �Ñ'ÐOÐ'×.Ñ.ÐOÑ.Ó0ÐOÒ4OÐOÐ0Ð4OÑO×O×OÕOÐ0Ð4O×O×OÓOÐ'×O×OÒOÐ.×O×OÒOÐ0×O×OÒOÐ4O×O×O×O×O×O×O×OØ�zÑ"Ð;Ð"×)Ñ)Ð;Ñ)Ó+Ð;ª|Ð;Ð+¨|Ñ;×;×;Õ;Ð+¨|×;×;Ó;Ð"×;×;Ò;Ð)×;×;Ò;Ð+×;×;Ò;¨|×;×;×;×;×;×;×;Ð;r*   N)r•   r–   r—   r¿   r˜   r*   r(   r©   r©   ¡   s   „ ó<r*   r©   c                   ó   — e Zd Zd„ Zd„ Zy)ÚTestFeatureWeightParamsc           	      óè  — t        t        «      }d}||k(  }|sþt        j                  d|fd||f«      dt	        j
                  «       v st        j                  t         «      rt        j                  t         «      nddt	        j
                  «       v st        j                  t        «      rt        j                  t        «      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚d x}x}}t        t        «      }d	}||k(  }|sþt        j                  d|fd||f«      dt	        j
                  «       v st        j                  t         «      rt        j                  t         «      ndd
t	        j
                  «       v st        j                  t        «      rt        j                  t        «      nd
t        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚d x}x}}d„ t        D «       }t        |«      }|s•ddt	        j
                  «       v st        j                  t        «      rt        j                  t        «      ndt        j                  |«      t        j                  |«      dœz  }t        t        j                  |«      «      ‚d x}}t        t        «      }t        |«      }d}	||	k(  }
|
�s[t        j                  d|
fd||	f«      dt	        j
                  «       v st        j                  t         «      rt        j                  t         «      nddt	        j
                  «       v st        j                  t        «      rt        j                  t        «      nddt	        j
                  «       v st        j                  t        «      rt        j                  t        «      ndt        j                  |«      t        j                  |«      t        j                  |	«      dœz  }dd|iz  }t        t        j                  |«      «      ‚d x}x}x}
}	y )Né2   rB   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr/   r   )rD   r±   rF   Úpy6zassert %(py8)sr²   é7   r   c              3   ó>   K  — | ]  }|j                  d «      –— Œ y­w)Úi_w_N)Ú
startswith)Ú.0ra   s     r(   Ú	<genexpr>zETestFeatureWeightParams.test_one_param_per_feature.<locals>.<genexpr>Ë   s   è ø€ ÐGÑ1F¨A�1—<‘< ×'Ñ1Fùs   ‚z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}Úall)rD   rE   Úpy4)zN%(py6)s
{%(py6)s = %(py0)s(%(py4)s
{%(py4)s = %(py1)s(%(py2)s)
})
} == %(py9)srQ   )rD   r±   rE   rÌ   rÄ   Úpy9zassert %(py11)sÚpy11)r/   r   rR   rS   rT   rU   rV   rW   rY   rZ   r   rË   rQ   )r‰   r�   Ú@py_assert5rc   Ú@py_format7r½   rd   Ú@py_assert3Ú@py_format5Ú@py_assert8r»   Ú@py_format10Ú@py_format12s                r(   Útest_one_param_per_featurez2TestFeatureWeightParams.test_one_param_per_featureÄ   sP  € ô
 Ô(Ó)Ð/¨RÐ/Ð)¨RÑ/×/×/Õ/Ð)¨R×/×/×/×/×/Ñ/Œs×/×/Ó/Œs×/×/×/×/×/Ò/Ô(×/×/Ó/Ô(×/×/Ô/Ð)×/×/Ò/¨R×/×/×/×/×/×/Ó/Ü”<Ó Ð& BÐ&Ð  BÑ&×&×&Õ&Ð  B×&×&×&×&×&Ñ&Œs×&×&Ó&Œs×&×&×&×&×&Ò&”<×&×&Ó&”<×&×&Ô&Ð ×&×&Ò& B×&×&×&×&×&×&Ó&ÙGÕ1FÓGÐGŒsÐGÓGÐGÑG×G×G×G×GÕGŒs×G×GÓGŒs×G×GÔGÐG×G×GÒGÐG×G×G×G×G×GÒGÜÔ,Ó-Ð4ŒsÐ-Ó.Ð4°"Ð4Ð.°"Ñ4×4×4Ö4Ð.°"×4×4×4×4×4Ñ4Œs×4×4Ó4Œs×4×4×4×4×4Ò4”3×4×4Ó4”3×4×4×4×4×4Ò4Ô,×4×4Ó4Ô,×4×4Ô4Ð-×4×4Ò4Ð.×4×4Ò4°"×4×4×4×4×4×4Ö4r*   c                 óâ  — dd l }|j                  }t        |«      }|j                  }t        t        «      } ||«      }|�s¤ddt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      t        j                  |«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  t        «      rt        j                  t        «      ndt        j                  |«      t        j                  |«      dœ	z  }t        t        j                  |«      «      ‚d x}x}x}x}}y )Nr   z«assert %(py13)s
{%(py13)s = %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.WEIGHT_COLS
})
}.issubset
}(%(py11)s
{%(py11)s = %(py8)s(%(py9)s)
})
}rQ   r.   r   )	rD   r±   rF   rG   rH   r²   rÍ   rÎ   Úpy13)r.   r0   rQ   Úissubsetr   rT   rU   rR   rV   rW   rY   rZ   )r‰   r.   r�   rc   r¼   Ú@py_assert10Ú@py_assert12Ú@py_format14s           r(   Ú!test_config_weight_cols_is_subsetz9TestFeatureWeightParams.test_config_weight_cols_is_subsetÎ   s  € ó 	Ø×%Ñ%ÐKŒsÐ%Ó&ÐKÐ&×/Ñ/ÐK´Ô4IÓ0JÐKÑ/Ð0JÓKÐKÒK×K×K×K×KÕKŒs×K×KÓKŒs×K×K×K×K×KÒK�6×K×KÓK�6×K×KÔKÐ%×K×KÒKÐ&×K×KÒKÐ/×K×K×K×K×KÐK´×K×KÓK´×K×K×K×K×KÒKÔ4I×K×KÓKÔ4I×K×KÔKÐ0J×K×KÒKÐK×K×K×K×K×K×KÑKr*   N)r•   r–   r—   rÖ   rÝ   r˜   r*   r(   rÁ   rÁ   Ã   s   „ ò5óLr*   rÁ   )é   )'Ú__doc__ÚbuiltinsrT   Ú_pytest.assertion.rewriteÚ	assertionÚrewriterR   r   Únumpyr1   Úpandasr   r   Ú%strategies.strategy_activation_scoresÚstrategy_activation_scoresrƒ   Ústrategies.strategy_mlp_scoresr   r   r   r   r   r   ÚjoinÚdirnameÚ__file__r   r]   r^   Úfixturer)   r@   rl   ru   rx   rz   rš   r©   rÁ   r˜   r*   r(   Ú<module>rí      sÕ   ðñ÷ „ ƒ	ã Û Û å :÷õ ð �7‰7�<‰<˜Ÿ™Ÿ™¨Ó1°4¸ÀÐHbÓc€ð €Ø€ð €‡��hÔñ0ó  ð0ó ò*ð: */Ø(-Ø59Ø$'Ø%*ñ€
ò÷Dñ D÷B
Rñ 
R÷<ñ <÷DLò Lr*   