o
    Éü™jÐ'  ã                
   @   s  d Z ddlZddlm  mZ ddlZddlZddl	Z	ddl
m
Z
mZmZ ej dej ej ej e¡¡¡¡ ddlZddlZddlZddlmZ ddd„ZG dd„ de	jƒZG d	d
„ d
e	jƒZG dd„ de	jƒZG dd„ de	jƒZedkre	jdd� dS dS )z¥
test_core_logic.py

Pure-logic unit tests (no Alpaca API calls). Run with:
    python -m pytest tests/test_core_logic.py -v
or:
    python tests/test_core_logic.py
é    N)ÚdatetimeÚ	timedeltaÚtimezone)Úscore_premarket_candidatec              
   C   sl   |pt dddddtjd�}g }tt| |ƒƒD ]\}\}}| |t|d� |d |d	 |d	 ||d
œ¡ q|S )Niê  é   é   é	   r   )Útzinfo)Úminutesg{®Gáz„?g{®Gáz”?)ÚtÚoÚhÚlÚcÚv)r   r   ÚutcÚ	enumerateÚzipÚappendr   )ÚpricesÚvolumesÚstartÚbarsÚiÚpr   © r   ú4/var/www/screener/premarket/tests/test_core_logic.pyÚ	make_bars   s   
þr   c                   @   s|   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )ÚTestIndicatorsc                 C   s,   t g d¢g d¢ƒ}| jt |¡ddd� d S )N)é
   r   r   )éd   r    r    ç      $@é   ©Úplaces)r   ÚassertAlmostEqualÚ
indicatorsÚvwap©Úselfr   r   r   r   Útest_vwap_basic#   s   zTestIndicators.test_vwap_basicc                 C   s.   t g d¢dgd ƒ}|  tj|dd�d¡ d S )N)r   ç333333$@çffffff$@çš™™™™™$@çÍÌÌÌÌÌ$@ç      %@r    é   é   ©Úlookbackr   )r   ÚassertGreaterr&   Ú
vwap_sloper(   r   r   r   Útest_vwap_slope_rising'   s   z%TestIndicators.test_vwap_slope_risingc                 C   ó   g d¢}|   t |¡d¡ d S )N©iÐÝ iàu	 i�” i`� çffffffò?)r4   r&   Úvolume_acceleration©r)   r   r   r   r   Ú#test_volume_acceleration_developing+   ó   z2TestIndicators.test_volume_acceleration_developingc                 C   r7   )N©i€O iÐ éð` r?   r9   )Ú
assertLessr&   r:   r;   r   r   r   Útest_volume_acceleration_fading0   r=   z.TestIndicators.test_volume_acceleration_fadingc                 C   ó,   t g d¢dgd ƒ}|  tj|dd�¡ d S )N)g       @g333333 @çffffff @g33333³ @ç      !@r    é   é   r2   )r   Ú
assertTruer&   Úis_higher_highs_higher_lowsr(   r   r   r   Ú"test_higher_highs_higher_lows_true5   ó   z1TestIndicators.test_higher_highs_higher_lows_truec                 C   rB   )N)rD   rC   rD   rC   rC   r    rE   rF   r2   )r   ÚassertFalser&   rH   r(   r   r   r   Ú*test_higher_highs_higher_lows_false_choppy9   rJ   z9TestIndicators.test_higher_highs_higher_lows_false_choppyc                 C   ó0   t g d¢dgd ƒ}|  tj|dddd�¡ d S )N)çÍÌÌÌÌÌ#@çš™™™™$@g)\�Âõ($@r+   r    rF   r!   çš™™™™™¹?é   ©Ú
resistanceÚ
buffer_pctÚ	hold_bars)r   rG   r&   Úbreakout_confirmedr(   r   r   r   Útest_breakout_confirmed_holds=   ó   z,TestIndicators.test_breakout_confirmed_holdsc                 C   rM   )N)rN   rO   çfffffæ#@rN   r    rF   r!   rP   rQ   rR   )r   rK   r&   rV   r(   r   r   r   Ú'test_breakout_not_confirmed_spike_failsA   rX   z6TestIndicators.test_breakout_not_confirmed_spike_failsc                 C   s   | j t dd¡ddd� d S )Ngö(\�Âõ#@g
×£p=
$@gš™™™™™Ù?r"   r#   )r%   r&   Ú
spread_pct)r)   r   r   r   Útest_spread_pctE   s   zTestIndicators.test_spread_pctc                 C   óT   dd„ t dƒD ƒ}dd„ t dƒD ƒ}t|| dgd ƒ}|  tj|dddd	�¡ d S )
Nc                 S   ó   g | ]}d |d  ‘qS ©r!   g(¯¡¼†òª?r   ©Ú.0r   r   r   r   Ú
<listcomp>K   ó    zZTestIndicators.test_extended_downtrend_flags_bounce_still_off_the_high.<locals>.<listcomp>é   c                 S   ó   g | ]}d |d  ‘qS )ç      "@g¡?r   r`   r   r   r   rb   L   rc   r   r    é   ç      ø?©Úmin_barsÚmin_decline_pctÚrecovery_threshold_pct)Úranger   rG   r&   Úis_extended_downtrend)r)   ÚdeclineÚbouncer   r   r   r   Ú7test_extended_downtrend_flags_bounce_still_off_the_highH   s   ÿzFTestIndicators.test_extended_downtrend_flags_bounce_still_off_the_highc                 C   r]   )
Nc                 S   r^   r_   r   r`   r   r   r   rb   U   rc   zWTestIndicators.test_extended_downtrend_false_once_high_is_reclaimed.<locals>.<listcomp>rd   c                 S   re   )rf   gUUUUUUÅ?r   r`   r   r   r   rb   V   rc   r   r    rg   rh   ri   ©rm   r   rK   r&   rn   )r)   ro   Úrecoveryr   r   r   r   Ú4test_extended_downtrend_false_once_high_is_reclaimedQ   s   ÿzCTestIndicators.test_extended_downtrend_false_once_high_is_reclaimedc                 C   s>   dd„ t dƒD ƒ}t|dgd ƒ}|  tj|dddd�¡ d S )Nc                 S   r^   )r!   gš™™™™™©?r   r`   r   r   r   rb   \   rc   zZTestIndicators.test_extended_downtrend_false_with_insufficient_history.<locals>.<listcomp>r   r    rd   rh   ri   rr   )r)   ro   r   r   r   r   Ú7test_extended_downtrend_false_with_insufficient_history[   s
   ÿzFTestIndicators.test_extended_downtrend_false_with_insufficient_historyc                 C   sd   t g d¢dgd ƒ}t g d¢dgd ƒ}|  t dd„ |D ƒ¡d¡ |  t dd„ |D ƒ¡d¡ d S )	N)r   r+   r,   r-   r.   r    rE   )r.   r-   r,   r+   r!   c                 S   ó   g | ]}|d  ‘qS ©r   r   ©ra   Úbr   r   r   rb   e   ó    zSTestIndicators.test_regression_fit_pct_change_matches_direction.<locals>.<listcomp>r   c                 S   rv   rw   r   rx   r   r   r   rb   g   rz   )r   r4   r&   Úregression_fit_pct_changer@   )r)   ÚrisingÚfallingr   r   r   Ú0test_regression_fit_pct_change_matches_directiona   s   ÿÿz?TestIndicators.test_regression_fit_pct_change_matches_directionc                 C   s>   t g d¢dgd ƒ}tj|dd�}|  |d¡ |  |d¡ d S )N)r   r,   r+   r-   ç     €$@r    rE   rF   )Úperiodr   g      ð?)r   r&   Úatrr4   r@   )r)   r   Úar   r   r   Útest_atr_reasonablei   s   z"TestIndicators.test_atr_reasonableN)Ú__name__Ú
__module__Ú__qualname__r*   r6   r<   rA   rI   rL   rW   rZ   r\   rq   rt   ru   r~   rƒ   r   r   r   r   r   "   s    	
r   c                   @   s4   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ ZdS )ÚTestRiskManagerc                 C   sH   d}d| }dD ]}t ||| ƒ}|  ||¡ |}q| j|ddd� d S )NrP   r!   )r!   rO   r+   r,   r+   rQ   r#   )ÚmaxÚassertGreaterEqualr%   )r)   ÚdistanceÚstopÚpriceÚnew_stopr   r   r   Ú test_trailing_stop_only_moves_upq   s   z0TestRiskManager.test_trailing_stop_only_moves_upc                 C   s(   d}d}t j||d d�}|  |d¡ d S )Nr/   r.   )r   )Úrisk_managerÚupdate_trailing_stopÚassertEqual)r)   ÚhighestÚcurrent_stopr�   r   r   r   Ú*test_stop_never_moves_backward_on_pullback{   s   z:TestRiskManager.test_stop_never_moves_backward_on_pullbackc                 C   s   t  d¡}| j|ddd� d S )Nr!   rN   rQ   r#   )r�   Úcompute_initial_stopr%   )r)   r‹   r   r   r   Útest_initial_stop_fixed_cents‚   s   
z-TestRiskManager.test_initial_stop_fixed_centsc                 C   s    t jdddd�}|  |d¡ d S )Ni † r!   rN   ©Úaccount_equityÚentry_priceÚinitial_stopiÐ  ©r�   Úcompute_position_sizer‘   ©r)   Úsharesr   r   r   Ú&test_position_sizing_respects_risk_pct†   s   ÿz6TestRiskManager.test_position_sizing_respects_risk_pctc                 C   s    t jdddd�}|  |d¡ d S )Nr   r!   rN   r—   r›   r�   r   r   r   Ú'test_position_sizing_zero_on_bad_inputsŽ   s   ÿz7TestRiskManager.test_position_sizing_zero_on_bad_inputsN)r„   r…   r†   rŽ   r”   r–   rŸ   r    r   r   r   r   r‡   p   s    
r‡   c                   @   s$   e Zd Zdd„ Zdd„ Zdd„ ZdS )Ú
TestScorerc              	   C   sh   t g d¢g d¢ƒ}t g d¢g d¢ƒ}td|dddd	d
d�}td|ddddd
d�}|  |d |d ¡ d S )N)g×£p=
× @g…ëQ¸!@ç)\�Âõ(!@g¸…ëQ8!@r8   )gš™™™™™!@g…ëQ¸…!@çÍÌÌÌÌL!@ç333333!@r>   ÚDEVr£   gÍÌÌÌÌÌ @i€„ r¤   g=
×£p=!@)Úpm_highÚpm_lowÚavg_vol_baselineÚbidÚaskÚFADEg33333³!@gš™™™™!@r¢   Útotal_score)r   r   r4   )r)   Údeveloping_barsÚfading_barsÚdevÚfader   r   r   Ú3test_developing_candidate_scores_higher_than_fading–   s   ÿÿ
ÿ
ÿz>TestScorer.test_developing_candidate_scores_higher_than_fadingc                 C   sT   t g d¢g d¢ƒ}td|dddd�}|  d|¡ |  d	|d ¡ |  d
|d ¡ d S )N)r!   r+   r,   ©iPÃ  i`ê  ip ÚXYZr   rY   é@B ©r¦   r§   r¨   Ú	breakdownÚprice_qualityÚvolume_quality)r   r   ÚassertIn©r)   r   Úresultr   r   r   Ú/test_score_breakdown_present_and_explains_total£   s   
ÿz:TestScorer.test_score_breakdown_present_and_explains_totalc                 C   s<   t g d¢g d¢ƒ}td|dddd�}|  |d d	 d
¡ d S )N)g      4@gš™™™™4@g3333334@r²   ÚHIGHg     @4@gfffffæ3@r´   rµ   r¶   r·   g        )r   r   r‘   rº   r   r   r   Ú0test_out_of_price_band_scores_zero_price_quality«   s
   
ÿz;TestScorer.test_out_of_price_band_scores_zero_price_qualityN)r„   r…   r†   r±   r¼   r¾   r   r   r   r   r¡   •   s    r¡   c                   @   sD   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dS )ÚTestSymbolFiltersc                 C   ó*   t  dd¡\}}|  |¡ |  d|¡ d S )NÚSOXLú+Direxion Daily Semiconductor Bull 3X SharesÚETF©Úsymbol_filtersÚpasses_symbol_filtersrK   r¹   ©r)   ÚokÚreasonr   r   r   Útest_leveraged_etf_rejected³   ó
   ÿ
z-TestSymbolFilters.test_leveraged_etf_rejectedc                 C   ó*   t  dd¡\}}|  |¡ |  |d¡ d S )NÚSPYúSPDR S&P 500 ETF TrustzETF/fund/trust)rÅ   rÆ   rK   r‘   rÇ   r   r   r   Útest_plain_etf_rejectedº   ó   
z)TestSymbolFilters.test_plain_etf_rejectedc                 C   s   t  dd¡\}}|  |¡ d S )NÚSQQQzProShares UltraPro Short QQQ)rÅ   rÆ   rK   rÇ   r   r   r   Útest_inverse_product_rejected¿   s   ÿz/TestSymbolFilters.test_inverse_product_rejectedc                 C   rÌ   )NÚAAPLú
Apple Inc.Ú )rÅ   rÆ   rG   r‘   rÇ   r   r   r   Ú test_clean_common_stock_acceptedÅ   rÐ   z2TestSymbolFilters.test_clean_common_stock_acceptedc                 C   rÀ   )Nr³   z<Telecommunication Systems International Holdings CorporationÚ	syllablesrÄ   rÇ   r   r   r   Ú)test_long_name_rejected_over_syllable_capÊ   rË   z;TestSymbolFilters.test_long_name_rejected_over_syllable_capc                 C   s   t  d¡}|  |d¡ d S )NúFord Motor CompanyrF   )rÅ   Úcount_syllablesÚassertLessEqual)r)   Úsylr   r   r   Ú/test_legal_suffixes_dont_inflate_syllable_countÑ   s   
zATestSymbolFilters.test_legal_suffixes_dont_inflate_syllable_countc                 C   st   G dd„ dƒ}|ddƒ|ddƒ|ddƒ|d	d
ƒg}t j|dd�}|  d|¡ |  d	|¡ |  d|¡ |  d|¡ d S )Nc                   @   s   e Zd Zdd„ ZdS )zGTestSymbolFilters.test_filter_symbol_list_end_to_end.<locals>.FakeAssetc                 S   s   || _ || _d S ©N)ÚsymbolÚname)r)   rß   rà   r   r   r   Ú__init__Ù   s   
zPTestSymbolFilters.test_filter_symbol_list_end_to_end.<locals>.FakeAsset.__init__N)r„   r…   r†   rá   r   r   r   r   Ú	FakeAssetØ   s    râ   rÓ   rÔ   rÍ   rÎ   rÁ   rÂ   ÚFrÙ   F)Úlog_rejections)rÅ   Úfilter_symbol_listr¹   ÚassertNotIn)r)   râ   ÚassetsÚ	survivorsr   r   r   Ú"test_filter_symbol_list_end_to_end×   s   üz4TestSymbolFilters.test_filter_symbol_list_end_to_endN)
r„   r…   r†   rÊ   rÏ   rÒ   rÖ   rØ   rÝ   ré   r   r   r   r   r¿   ²   s    r¿   Ú__main__rQ   )Ú	verbosityrÞ   )Ú__doc__ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arÚsysÚosÚunittestr   r   r   ÚpathÚinsertÚdirnameÚabspathÚ__file__r&   r�   rÅ   Úscorerr   r   ÚTestCaser   r‡   r¡   r¿   r„   Úmainr   r   r   r   Ú<module>   s$    "	&
N%8ÿ