Ë
    Bp±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mZ  e
j                  d«      Zd„ Zd„ Zd„ Zd„ Zd	„ Zy)
u÷  
REDâ†’GREEN acceptance test for the trailing-stop time-gating parity fix.

Pine gates BOTH `strategy.close` calls (score-exit and trailing-stop) on
`timeCondition = time >= startDate and time <= endDate`. So a position that is
open when the window ends is held open to the chart end â€” Pine cannot close it
after `endDate`.

Python's `_apply_trailing_stop` previously fired the trailing stop regardless of
the time window, so a trade open across `endDate` would trail-out in OOS while
TV held it open (the 4H "TV#106 open trade" parity break).

Given/When/Then:
  Given a trade entered just before `endDate` whose price drops below the trail
        stop only on bars AFTER `endDate`,
  When  the signals are generated with a Pine time gate (`_pine_time_end`),
  Then  no exit (trail or score) fires after `endDate` â€” the position is held
        open, matching Pine.

A second case (no time gate) asserts the trailing stop still fires normally, so
the fix does not disable the trail inside the trading window.
é    N)Ú_score_to_signalsz
2026-02-28c                  ó  — t        j                  ddd¬«      } t        j                  g d¢t        ¬«      }t        j                  g d¢t        ¬«      }t        j                  g d¢t        ¬«      }t        j
                  | |||dœ«      S )	Nz
2026-02-24é   ÚD)ÚperiodsÚfreq)é
   é–   é(   r   r   r   r   r   ©Údtype)é2   r   é<   r   éd   éF   r   r   )ÚtimeÚactivation_scoreÚcloseÚhigh)ÚpdÚ
date_rangeÚnpÚarrayÚfloatÚ	DataFrame)ÚtimesÚscorer   r   s       úo/Users/jameslopez/projects/TradingBot25/.worktrees/mlp-comparable-measurement/tests/test_mlp_trail_time_gate.pyÚ_fixture_dfr   "   sj   € ô �M‰M˜,°¸Ô<€Eä�H‰HÒ=ÄUÔK€Eô �H‰HÒ=ÄUÔK€EÜ�8‰8Ò<ÄEÔJ€DÜ�<‰< ¸EØ"'°ñ7ó 8ð 8ó    c            	      ó   — dddddddddœS )	Ng      Y@g      I@g     ˆÃÀg      ð?Fg      4@é   )Ú!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_confirmationÚi_trailing_stop_thresholdÚi_exit_score_windowÚi_entry_score_window© r+   r    r   Ú_base_paramsr,   1   s&   € ð .3Ø,0Ø9AØ(+Ø).Ø%)Ø Ø !ñ	ð 	r    c           	      ó  — t        «       }t        j                  t        |«      t        j                  ¬«      }t        || ||d   j                  t        j                  ¬«      |d   j                  t        j                  ¬«      «      }|S )Nr   r   r   )r   r   ÚzerosÚlenÚfloat64r   Úto_numpy)ÚparamsÚdfÚ
stoch_peakÚouts       r   Ú_runr6   @   sk   € Ü	‹€BÜ—‘œ#˜b›'¬¯©Ô4€JÜ
Ø
ˆF�JØ
ˆ7‰×Ñ¤2§:¡:ÐÓ.Ø
ˆ6‰
×Ñ¤"§*¡*ÐÓ-ó€Cð
 €Jr    c                  óä  — t        «       } t        j                  d«      | d<   t        | d<   t	        | «      }|d   j
                  d   }|sNt        j                  d«      dz   dt        j                  |«      iz  }t        t        j                  |«      «      ‚d	}|d
   t        kD  }|j                  |df   }|j                  } |«       }| }|svt        j                  d«      dz   t        j                  |«      t        j                  |«      t        j                  |«      dœz  }t        t        j                  |«      «      ‚d	x}x}x}}|d   j
                  d   }d}	||	k(  }|s‚t        j                  d|fd||	f«      t        j                  |«      t        j                  |	«      dœz  }
t        j                  d«      dz   d|
iz  }t        t        j                  |«      «      ‚d	x}x}}	y	)z?Given a Pine time gate, the trailing stop must NOT fire in OOS.z
2017-12-01Ú_pine_time_startÚ_pine_time_endÚexecute_entryé   úexpected entry at idx2ú
>assert %(py1)sÚpy1Nr   Úexecute_exitu}   trailing stop fired after endDate â€” Python should hold the position open to match Pine's timeCondition-gated strategy.closezD
>assert not %(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.any
}()
})r>   Úpy3Úpy5Úpositionéÿÿÿÿr"   ©ú==©z%(py1)s == %(py4)s©r>   Úpy4z(position should remain open past endDateú
>assert %(py6)sÚpy6)r,   r   Ú	TimestampÚENDr6   ÚilocÚ
@pytest_arÚ_format_assertmsgÚ	_safereprÚAssertionErrorÚ_format_explanationÚlocÚanyÚ_call_reprcompare)r2   r5   Ú@py_assert0Ú@py_format2Ú	after_endÚ@py_assert2Ú@py_assert4Ú@py_assert6Ú@py_format7Ú@py_assert3Ú@py_format5s              r   Ú6test_trail_does_not_fire_after_endDate_when_time_gatedr_   K   s‘  € ä‹^€FÜ!#§¡¨lÓ!;€FÐÑÜ"€FÐÑÜ
ˆv‹,€Cð ˆÑ×$Ñ$ QÑ'ÐAÑ'×AÖAÐ)A×A×AÖAÐ'×A×A×A×A×Að �F‘œcÑ!€IØ�w‰w�y .Ð0Ñ1ð Ð1×5Ñ5ð Ñ5Ó7ð Ð7Ð7ð Ñ7ô ïé ð	B÷ó ïé ð 2÷ ð ïé ð 6÷ ð ïé ð 8÷ ÷ ò ïé ÷÷ ñ ð
 ˆz‰?×Ñ Ñ#ÐT qÐTÐ# qÑ(×T×TÕTÐ# q×T×TÓTÐ#×T×TÒT q×T×T×TÐ*T×T×T×T×T×T×TÑTr    c                  ó  — t        «       } t        | «      }|d   j                  d   }|sNt        j                  d«      dz   dt        j
                  |«      iz  }t        t        j                  |«      «      ‚d}|d   j                  d   }|sNt        j                  d	«      dz   dt        j
                  |«      iz  }t        t        j                  |«      «      ‚d}|d
   j                  d   }d}||k(  }|s‚t        j                  d|fd||f«      t        j
                  |«      t        j
                  |«      dœz  }t        j                  d«      dz   d|iz  }t        t        j                  |«      «      ‚dx}x}}y)zFSanity: without a time gate, the trailing stop fires normally at idx5.r:   r;   r<   r=   r>   Nr?   é   z5trailing stop should fire at idx5 without a time gaterB   rC   r   rD   rF   rG   z/position should be closed after the trail firesrI   rJ   )	r,   r6   rM   rN   rO   rP   rQ   rR   rU   )r2   r5   rV   rW   r]   rY   r^   r\   s           r   Ú*test_trail_still_fires_when_not_time_gatedrb   _   sô   € ä‹^€FÜ
ˆv‹,€CàˆÑ×$Ñ$ QÑ'ÐAÑ'×AÖAÐ)A×A×AÖAÐ'×A×A×A×A×Aàˆ~Ñ×#Ñ# AÑ&Ð_Ñ&×_Ö_Ð(_×_×_Ö_Ð&×_×_×_×_×_Øˆz‰?×Ñ Ñ#Ð[ qÐ[Ð# qÑ(×[×[Õ[Ð# q×[×[Ó[Ð#×[×[Ò[ q×[×[×[Ð*[×[×[×[×[×[×[Ñ[r    )Ú__doc__ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriterN   Únumpyr   Úpandasr   ÚpytestÚstrategies.strategy_mlp_scoresr   rK   rL   r   r,   r6   r_   rb   r+   r    r   Ú<module>rm      sJ   ðñ÷. „ Ó Û Û å <ð €b‡l�l�<Ó €ò8òòòUó(\r    