o
    ¸L¹jïV  ã                   @   sœ  d Z ddlZddlZddlZddlZddlZddlZddlmZm	Z	 ddl
m
Z
mZmZ ddlmZ ddlmZ eeƒ ¡ jZej deeƒ¡ ddlmZ deƒ  d	i ¡d
< ddlZddlmZ ddlZejZ eeƒ d d ƒZ!dedede
fdd„Z"d2dd„Z#defdd„Z$defdd„Z%G dd„ dƒZ&dd„ Z'ed d!�Z(d3d#d$„Z)G d%d&„ d&ƒZ*G d'd(„ d(ƒZ+d4d)d*„Z,d+d,„ Z-d-d.„ Z.d/d0„ Z/e0d1krÌe/ƒ  dS dS )5a  
simulate.py -- trade1 backtester.

Replays saved trade-by-trade market data through the REAL bot: it builds
monitor.SessionOrchestrator (the same class that trades live) with a
replay clock, a simulated broker and a replayed data stream, so every
buy/sell decision comes from the same code path and the same rule files
(breakout_rules.py, reversal_rules.py, exit_rules.py) as live trading.

    python simulate.py --days all                 every saved day
    python simulate.py --date 2026-09-22          one day
    python simulate.py --days all --entry breakout_rules --exit exit_rules
    python simulate.py --days all --rules-dir /some/dir --entry my_test_rules
        (--rules-dir: also import rule files from that folder -- for trying
         a variant without touching the real rule files)
    python simulate.py --fixed-entries sim/entries/j_20day.json --exit exit_rules
        EXIT test: buys exactly the listed entries (sim/rules/fixed_entries.py),
        only on their days and symbols, no slot limit -- compare exits on the
        same entries
    python simulate.py ... --set exit_rules.swing_minutes=3 --set exit_rules.trigger=touch
        override any config value for this run only (value parsed as JSON)

Data:
  candidates  sim/candidates/<date>.json (the 9:28 top-30 lists; 8/27-9/24
              are the rebuilt lists from the 20-day study, 9/25 the bot's
              real list), else data/candidates/<date>_scanner.json
  ticks       simulator.cache_dir in config.json (shared with screener/trade:
              <cache_dir>/<date>/<SYMBOL>.jsonl); missing symbols are
              downloaded from Alpaca on first use
Output:
  data/simulations/<name>.json   every trade + per-day summary
  data/simulations/<name>/decisions_<date>.jsonl   rule decisions (on change)
  logs/sim_*.log                 (kept apart from the live bot's log)

Simplifications (same as screener/trade's simulator): fills at the last
trade price (no slippage), fixed equity for sizing, no intraday rescans
(each day's 9:28 list is watched all day), 5-second poll grid.
é    N)ÚThreadPoolExecutorÚas_completed)ÚdatetimeÚ	timedeltaÚtimezone)ÚPath)ÚZoneInfo)Ú
get_configÚsimÚloggingÚfilename_prefix©ÚSymbolBufferÚscheduler   Údate_strÚhhmmssÚreturnc              	   C   s@   dd„ |  d¡D ƒ\}}}t | ¡}t|j|j|j|||td�S )Nc                 s   s   � | ]}t |ƒV  qd S ©N)Úint©Ú.0Úx© r   ú$/var/www/screener/trade1/simulate.pyÚ	<genexpr>D   ó   € z_et_dt.<locals>.<genexpr>ú:)Útzinfo)Úsplitr   ÚfromisoformatÚyearÚmonthÚdayÚET)r   r   ÚhÚmÚsÚdr   r   r   Ú_et_dtC   s   
r(   é   c                 c   s@   � | }||k rt |t|d� |ƒ}||fV  |}||k sd S d S )N©Úminutes)Úminr   )ÚstartÚendr+   ÚcurÚnxtr   r   r   Ú_chunksL   s   €
ýr1   Úpathc                 C   sî   |  d¡}d}t|dƒ�\}t||ƒD ]N\}}	|  |||	¡}
|  |||	¡}dd„ |
D ƒdd„ |D ƒ }|jdd„ d	� |D ]\}}}}| t | 	¡ |||g¡d
 ¡ q<|t
|ƒ7 }~
~~t ¡  qW d  ƒ n1 skw   Y  | |¡ |S )z6One 20-min chunk at a time -- this host has ~2 GB RAM.z
.jsonl.tmpr   Úwc                 S   ó$   g | ]}|d  d|d |d f‘qS )ÚtÚtradeÚpr&   r   ©r   r5   r   r   r   Ú
<listcomp>\   ó   $ z!_cache_symbol.<locals>.<listcomp>c                 S   r4   )r5   ÚquoteÚbÚar   )r   Úqr   r   r   r9   ]   r:   c                 S   s   | d S ©Nr   r   )Úer   r   r   Ú<lambda>^   ó    z_cache_symbol.<locals>.<lambda>)ÚkeyÚ
N)Úwith_suffixÚopenr1   Úget_historical_tradesÚget_historical_quotesÚsortÚwriteÚjsonÚdumpsÚ	isoformatÚlenÚgcÚcollectÚrename)ÚclientÚsymbolr-   r.   r2   ÚtmpÚnÚfr=   r<   ÚtradesÚquotesÚevÚtsÚkindr   Úyr   r   r   Ú_cache_symbolT   s&   
ÿ"
öÿ
r]   Ú	cache_dirc           	         s<  ˆ j ddd� ‡ fdd„|D ƒ}|sd S tƒ d }t||d ƒ t¡‰t||d ƒ t¡‰| ƒ ‰tdt|ƒ› d	�tjd
� t	dd��R‰‡ ‡‡‡‡fdd„|D ƒ}t
|ƒD ]7}ztd|| › d| ¡ › d�tjd
� W qT ty‹ } ztd|| › d|› �tjd
� W Y d }~qTd }~ww W d   ƒ d S 1 s—w   Y  d S )NT©ÚparentsÚexist_okc                    s"   g | ]}ˆ |› d �   ¡ s|‘qS ©ú.jsonl)Úexists©r   r&   ©r^   r   r   r9   j   s   " z ensure_cache.<locals>.<listcomp>r   Úmarket_open_timeÚmarket_close_timez  downloading ticks for z symbols ...©Úfileé   )Úmax_workersc                    s,   i | ]}ˆ  tˆ|ˆˆˆ |› d � ¡|“qS rb   )Úsubmitr]   re   ©r^   rR   r.   Úpoolr-   r   r   Ú
<dictcomp>s   s   , z ensure_cache.<locals>.<dictcomp>ú    ú: z eventsz	: FAILED )Úmkdirr	   r(   Ú
astimezoneÚUTCÚprintrN   ÚsysÚstderrr   r   ÚresultÚ	Exception)	Ú	client_fnÚsymbolsr   r^   ÚtodoÚschedÚfutsÚfutr@   r   rn   r   Úensure_cacheh   s(   
(*€ÿý"þr�   c                   @   s6   e Zd ZdZdefdd„Zdd„ Zdd„ Zd	d
„ ZdS )ÚEventReaderz/Streams one symbol's cached ticks line by line.r2   c                 C   s(   |  ¡ rt|ƒnd | _d | _|  ¡  d S r   )rd   rF   Ú_fÚ_nextÚ_advance)Úselfr2   r   r   r   Ú__init__~   s   zEventReader.__init__c                 C   sL   | j r| j  ¡ nd}|sd | _d S t |¡\}}}}t |¡|||f| _d S )NÚ )rƒ   Úreadliner„   rK   Úloadsr   r   )r†   ÚlinerZ   r[   r=   r<   r   r   r   r…   ƒ   s   zEventReader._advancec                 C   sn   | j d ur1| j d |kr3| j \}}}}|dkr|n||||ƒ |  ¡  | j d ur5| j d |ksd S d S d S d S )Nr   r6   )r„   r…   )r†   r5   Úon_tradeÚon_quoterZ   r[   r=   r<   r   r   r   Údrain_up_to‹   s
   (ýzEventReader.drain_up_toc                 C   s   | j r
| j  ¡  d S d S r   )rƒ   Úclose©r†   r   r   r   r�   ‘   s   ÿzEventReader.closeN)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r‡   r…   rŽ   r�   r   r   r   r   r‚   {   s    r‚   c              	      sò   t |dƒ t¡‰i }ddtjfddtjffD ]`\}}}||› d� }| ¡ r-t | 	¡ ¡ni ‰ ‡ fdd„|D ƒ}	|	rr| ¡ sr| ƒ  
|	ˆt|d	� ˆtd
d� ¡}
|
 ¡ D ]\}}|‡fdd„|D ƒƒ}|ri|ˆ |< qT| t ˆ ¡¡ ˆ ||< q|S )z‡Daily ATR and 5-day reference as of the morning of date_str
    (cached next to the ticks, same files screener/trade's simulator uses).z00:00:00Ú	daily_atré-   Úref5dé   ú.jsonc                    ó   g | ]}|ˆ vr|‘qS r   r   re   )Údatar   r   r9   Ÿ   s    z_daily_ref.<locals>.<listcomp>)Údaysé   ©Úsecondsc                    s   g | ]
}|d  ˆ k r|‘qS )r5   r   ©r   r<   )r"   r   r   r9   £   ó    )r(   rt   ru   Ú
volatilityr•   Úfive_day_referencerd   rK   rŠ   Ú	read_textÚget_daily_bars_bulkr   ÚitemsÚ
write_textrL   )r{   r|   r   r^   ÚoutÚnameÚ	days_backÚfnr7   ÚmissingÚbarsr&   ÚblÚvr   )r›   r"   r   Ú
_daily_ref–   s$   

ÿ$€
r°   é   rž   Úbars1mc           
      C   sˆ   i }|D ]=}||› d|› d� }|  ¡ s3t|dƒ}| ƒ j||t|dƒdd�}	| t dd„ |	D ƒ¡¡ d	d„ t | ¡ ¡D ƒ||< q|S )
a  Alpaca's OFFICIAL 1-min bars (what the live bar stream delivers),
    cached next to the ticks. Used for the watched symbols (the live stream
    upserts them into the buffer and forwards them to the rule modules) and
    for config streaming.benchmark_symbols (SPY/IWM, bars only).Ú_r™   z09:30:00z16:00:00iè  )r-   r.   Úlimitc              	   S   s@   g | ]}|j  ¡ t|jƒt|jƒt|jƒt|jƒt|jƒg‘qS r   )Ú	timestamprM   ÚfloatrF   ÚhighÚlowr�   Úvolumer    r   r   r   r9   ¹   s
     ÿÿz _minute_bars.<locals>.<listcomp>c              	   S   s<   g | ]}t  |d  ¡|d |d |d |d |d dœ‘qS )r   r�   r±   rk   é   é   )r5   Úor$   ÚlÚcr¯   )r   r   )r   Úrr   r   r   r9   »   s    4ÿ)rd   r(   Úget_minute_barsr§   rK   rL   rŠ   r¤   )
r{   r|   r   r^   Úprefixr¨   r&   r7   r=   Úrawr   r   r   Ú_minute_bars®   s   

ÿÿrÃ   c                   @   sV   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Zddd„Z	dd„ Z
dd„ ZdS )ÚSimPositionManagerc                 C   s(   || _ || _|| _|| _i | _g | _d S r   )ÚequityÚcfgÚmax_positionsÚclockÚ	positionsrW   )r†   rÅ   Útrading_cfgrÇ   rÈ   r   r   r   r‡   Ä   s   
zSimPositionManager.__init__c                 C   s   t | jƒ| jk S r   )rN   rÉ   rÇ   r�   r   r   r   Úhas_available_slotÌ   s   z%SimPositionManager.has_available_slotc                 C   s
   || j v S r   )rÉ   )r†   rS   r   r   r   Úis_symbol_openÏ   ó   
z!SimPositionManager.is_symbol_openc                 C   ó
   t | jƒS r   )ÚlistrÉ   r�   r   r   r   Úget_open_symbolsÒ   rÍ   z#SimPositionManager.get_open_symbolsc                 C   rÎ   r   )rÏ   rW   r�   r   r   r   Úclosed_trades_todayÕ   rÍ   z&SimPositionManager.closed_trades_todayc                 C   sl   || }|dks
|sdS t | j| jd  d | ƒ}t|t | j| jd  d | ƒƒ}|| jd kr4|S dS )Nr   Úaccount_risk_pct_per_tradeg      Y@Ú#max_position_notional_pct_of_equityÚ
min_shares)r   rÅ   rÆ   r,   )r†   Úentry_priceÚ
stop_priceÚrisk_per_shareÚqtyr   r   r   Úcalculate_qtyØ   s   "z SimPositionManager.calculate_qtyNc                 C   sb   |   |¡s	|  ¡ sdS |  ||¡}|dkrdS |||||  ¡  ¡ |p#g ||p'i ||dœ
| j|< dS )NFr   )
rS   rØ   rÕ   rÖ   Ú
entry_timeÚreasonsÚsetupÚplanÚ
peak_priceÚ	low_priceT)rÌ   rË   rÙ   rÈ   rM   rÉ   )r†   rS   rÕ   rÖ   rÛ   rÜ   rÝ   rØ   r   r   r   Úenter_positioná   s   ýz!SimPositionManager.enter_positionc                 C   s<   | j  |¡}|rt|d |ƒ|d< t|d |ƒ|d< d S d S )NrÞ   rß   )rÉ   ÚgetÚmaxr,   )r†   rS   Úpricer7   r   r   r   Úmarkî   s
   þzSimPositionManager.markc                 C   sÊ   | j  |d ¡}|sd S t|d |ƒt|d |ƒ}}||d  |d  }| j i |¥||  ¡  ¡ |d||t||d  d d dƒt|d	ƒt||d  |d  d	ƒt||d  |d  | d	ƒd
œ
¥¡ d S )NrÞ   rß   rÕ   rØ   Úclosedr�   éd   rk   r±   )
Ú
exit_priceÚ	exit_timeÚexit_reasonÚstatusrÞ   rß   Úpl_pctÚ
pl_dollarsÚmfe_dollarsÚgiveback_dollars)	rÉ   Úpoprâ   r,   rW   ÚappendrÈ   rM   Úround)r†   rS   rç   Úreasonr7   Úpeakr¸   Úplr   r   r   Úexit_positionô   s   ÿ ûz SimPositionManager.exit_position)NNN)r‘   r’   r“   r‡   rË   rÌ   rÐ   rÑ   rÙ   rà   rä   rõ   r   r   r   r   rÄ   Ã   s    
	rÄ   c                   @   sz   e Zd ZdZdd„ Zddd„Zdd„ Zd	d
„ Zdefdd„Z	dd„ Z
dd„ Zdd„ Zddd„Zdd„ Zdd„ Zdd„ ZdS )Ú	SimStreamzÊStand-in for stream.StreamManager: the same SymbolBuffer class the
    live stream uses, fed from saved ticks; forwards ticks and completed
    1-min bars to listeners exactly like the live stream does.c                    s‚   t ƒ  di ¡}| dd¡‰tdt| dd¡d ˆ ƒƒ‰ ‡ ‡fdd	„|D ƒ| _g | _tƒ | _t|ƒ| _i | _	i | _
i | _i | _d S )
NÚ	streamingÚsub_minute_bucket_secondsé   r�   Úsub_minute_buffer_minutesr˜   é<   c                    s   i | ]	}|t ˆˆ d �“qS ))Úsub_bucket_secondsÚ
sub_maxlenr   re   ©Úsub_nÚsub_sr   r   rp     s    z&SimStream.__init__.<locals>.<dictcomp>)r	   rá   râ   r   ÚbuffersÚ	listenersÚsetÚ	bars_onlyÚ_subscribedÚ	_last_barÚbenchÚofficialÚ	_released)r†   r|   Úscr   rþ   r   r‡     s   

zSimStream.__init__Nc              	   C   sÆ   | j  |d¡}|t|ƒk r\|| d tdd� t |kr\|| }|d ur:| |d |d |d |d |d	 |d ¡ |  d
|t|ƒ¡ |d7 }|t|ƒk r\|| d tdd� t |ks|| j |< d S )Nr   r5   r�   r*   r¼   r$   r½   r¾   r¯   Úon_bar)r	  rá   rN   r   Ú	BAR_DELAYr  Ú_notifyÚdict)r†   r&   r®   r5   ÚbufÚir<   r   r   r   Ú_release  s   *,*ûzSimStream._releasec                 C   s&   | j  ¡ D ]\}}|  |||¡ qd S r   )r  r¦   r  )r†   r5   r&   r®   r   r   r   Ú
feed_bench  s   ÿzSimStream.feed_benchc                 G   s(   | j D ]}t||d ƒ}|r||Ž  qd S r   )r  Úgetattr)r†   ÚmethodÚargsÚlstr«   r   r   r   r  #  s   
€ýzSimStream._notifyÚreaderc                    sV   ˆj ˆ ‰ ‡ ‡‡fdd„}‡ ‡‡fdd„}| |||¡ ˆ ˆˆj ˆg ¡|ˆ ¡ d S )Nc                    s$   ˆ   ||| ¡ ˆ dˆ| ||¡ d S )NrŒ   )rŒ   r  )rZ   rã   Úsize©r  r†   rS   r   r   rŒ   ,  s   z SimStream.feed.<locals>.on_tradec              	      s(   ˆ   ||| ¡ ˆ dˆ| ||dd¡ d S )Nr�   g        )r�   r  )rZ   ÚbidÚaskr  r   r   r�   0  s   z SimStream.feed.<locals>.on_quote)r  rŽ   r  r  rá   )r†   rS   r  r5   rŒ   r�   r   r  r   Úfeed)  s
   
zSimStream.feedc                 C   sJ   | j  |¡}|d u r|| jv r| j| d | j |d¡… S |r#| ¡ S g S r?   )r  rá   r  r	  Úget_bars©r†   rS   r<   r   r   r   r  8  s   zSimStream.get_barsc                 C   s   | j  |¡}|r| ¡ S g S r   )r  rá   Úget_bars_subr  r   r   r   r  >  s   zSimStream.get_bars_subc                 C   s   | j  |¡}|r|jS d S r   )r  rá   Úlatest_quoter  r   r   r   Ú	get_quoteB  s   zSimStream.get_quoteç      4@c                 C   s   | j  |¡}|r| |¡S d S r   )r  rá   Úget_trade_imbalance)r†   rS   Úwindow_secondsr<   r   r   r   r#  F  s   zSimStream.get_trade_imbalancec                 C   ó   d S r   r   ©r†   r|   r   r   r   Úadd_symbolsJ  ó   zSimStream.add_symbolsc                 C   r%  r   r   r&  r   r   r   Úremove_symbolsM  r(  zSimStream.remove_symbolsc                 C   r%  r   r   r�   r   r   r   ÚstopP  r(  zSimStream.stopr   )r"  )r‘   r’   r“   r”   r‡   r  r  r  r‚   r  r  r  r!  r#  r'  r)  r*  r   r   r   r   rö     s    


rö   c                 C   sp   |rt |ƒgng td d | › d� td d | › d� g D ]}| ¡ r0t | ¡ ¡|f  S qtd| › �ƒ‚)Nr
   Ú
candidatesr™   r›   z_scanner.jsonzno candidate list for )r   ÚBASE_DIRrd   rK   rŠ   r¤   ÚFileNotFoundError)r   Úoverrider7   r   r   r   Úload_candidatesU  s   
þÿr/  c                   C   s    t dd„ td d  d¡D ƒƒS )Nc                 s   s   � | ]}|j V  qd S r   )Ústem)r   r7   r   r   r   r   _  s   € zall_days.<locals>.<genexpr>r
   r+  z*.json)Úsortedr,  Úglobr   r   r   r   Úall_days^  s    r3  c                    sR  t ƒ }|d }t| |jƒ\}}|jd ur5|j | tƒ ¡‰‡fdd„|D ƒdd„ tˆdd„ |D ƒ ƒD ƒ }dd„ |D ƒ‰t| di ¡ d	td
 d ¡ƒ|  ‰ t	|ˆ| ˆ ƒ t
|ˆ| ˆ ƒ}	dt| |d ƒ t¡i‰‡fdd„}
t||d |jp{|d d |
ƒ}tˆƒ}t|‡fdd„| di ¡ dg ¡D ƒ| ˆ dd�|_t|ˆ| ˆ ƒ|_|d| › d� }t|dƒ‰‡‡fdd„}tjtƒ |||
|dd�}|jD ]}| ¡  qÆ| |dd„ |D ƒ|	d  |	d! d"œ¡ ‡ fd#d„ˆD ƒ}ˆd }t| |d$ ƒ t¡}t| |d% ƒ t¡}t|d& d'�}||k�rZ|ˆd< | |¡ ˆD ]}| ||| |¡ �q| ¡ D ]}| |¡}|�r=|  ||d( d) ¡ �q)| !¡  | "¡ �rQ||k �rQ| #¡  ||7 }||k�s|ˆd< |jD ]}| $d*¡ �qa| ¡ D ]}| |¡}| %||�r�|d( d) n|j&| d+ d,¡ �qn| '¡ D ]}| (¡  �q�ˆ (¡  ~~t) *¡  |j+t,|ƒfS )-Nr   c                    s   g | ]
}|d  ˆ v r|‘qS ©rS   r   ©r   r¾   )Úwantr   r   r9   h  r¡   zrun_day.<locals>.<listcomp>c                 S   s   g | ]}|i d œ‘qS ))rS   Úmetricsr   r   r   r   r   r9   i  s    c                 S   s   h | ]}|d  ’qS r4  r   r5  r   r   r   Ú	<setcomp>i  ó    zrun_day.<locals>.<setcomp>c                 S   s   g | ]}|d  ‘qS r4  r   r5  r   r   r   r9   j  r9  Ú	simulatorr^   r›   Úcacher5   rg   c                      s   ˆ d S )Nr5   r   r   )Únowr   r   rA   p  rB   zrun_day.<locals>.<lambda>ÚtradingrÇ   c                    rš   r   r   r    )r|   r   r   r9   s  s    ÿr÷   Úbenchmark_symbolsr  )rÁ   Ú
decisions_rc   r3   c                    s,   ˆ   tjdˆd  ¡ i| ¥td�d ¡ d S )Nrµ   r5   )ÚdefaultrD   )rJ   rK   rL   rM   Ústr)Úrec)Údec_fr<  r   r   Úsinky  s   ,zrun_day.<locals>.sinkF)rR   Úposition_mgrÚstreamrÈ   Údecision_sinkÚlivec                 S   s    i | ]}|d  |d   d¡“qS )rS   r7  Úavg_daily_volume)rá   r5  r   r   r   rp   ‚  ó     zrun_day.<locals>.<dictcomp>r•   r—   )r+  Ú	baselinesr•   r—   c                    s    i | ]}|t ˆ |› d � ƒ“qS rb   )r‚   re   rf   r   r   rp   †  rJ  Úno_new_entries_afterÚforce_liquidate_timeÚpoll_interval_secondsrž   éÿÿÿÿr¾   Úon_session_endrÕ   Ú
END_OF_DAY)-r	   r/  Úcandidates_fileÚ_fixed_symsrá   r  r1  r   r,  r�   r°   r(   rt   ru   rÄ   rÇ   rö   rÃ   r  r  rF   ÚmonitorÚSessionOrchestratorÚobjectÚall_modulesÚloadÚ_apply_scanr   r  r  rÐ   r  rä   Ú_update_open_positionsrË   Ú_scan_for_entriesÚcallÚ_sellrÉ   Úvaluesr�   rO   rP   rW   rA  )r   r  rÅ   r{   Úout_dirrÆ   r~   ÚcandsÚcpathÚrefrÈ   ÚpmrF  Údec_pathrD  r¼   r%   Úreadersr5   ÚcutoffÚeodÚstepr&   r­   r¿   r   )r^   rC  r<  r|   r6  r   Úrun_dayb  s‚   
ÿ$ $ÿ
ÿ

ý


€
ô

.ri  c                     s†  t jtt jd�} | jddd� | jddd� | jddd� | jd	d
d� | jddd� | jddd� | jdtdd� | jdtdd� | jddd� | jddd� | jddg ddd� |  ¡ }|jD ]8}| 	dd¡\}}zt
 |¡}W n	 ty{   Y nw tƒ }| 	d¡}|d d … D ]}| |i ¡}qŠ|||d  < qad |_|jrút|jƒ ¡ }tj d!ttd" d# ƒ¡ d$gtƒ d# d%< t|ƒtƒ  d$i ¡d&< i |_t
 t|ƒ¡d' D ]}	|j |	d( tƒ ¡ |	d) ¡ qÓ|jsëd*|_|jsú|jsúd+ t|jƒ¡|_|j �rtj d!tt|j ƒ ¡ ƒ¡ tƒ  d#i ¡}
d,|
d-< |j!�r,|j�s,d.d/„ |j! 	d+¡D ƒ|
d%< |j"�r7|j" #¡ |
d0< |j�r?|jgn|jd1k�rHt$ƒ nd2d/„ |j�pPd3 	d+¡D ƒ}|�s^|  %d4¡ i ‰ ‡ fd5d6„}|j&�pqt|ƒ  '¡ j&ƒ}|j(�p’d7d8 |
 )d%g ¡¡ d9 t|
 )d0¡ƒ d: t* +¡  ,d;¡ }td< d= | }|j-d>d>d?� t.d@|
 )d%¡› dA|
 )d0¡› dB|dC›dDt/|ƒ› dE|› �
tj0dF� ||
 )d%¡|
 )d0¡|i dGœ}|D ] }t1 1¡ }t2|||||ƒ\}}||dHœ|dI |< t3dJdK„ |D ƒƒ}t.|› dLt/|ƒ› dMt3dNdK„ |D ƒƒ› dO|dP›dQt1 1¡ | dR›dS�
d>dT� |D ]B}t.dU|d) dV›dW|dX › dY|dZ d[d\… › d]|d^ d_›d`|da d[d\… › d]|db d_›dc|dd dP›de|df d dg… › dh�d>dT� �qtd< d= |› di�  4t
j5|dtdj�¡ �qÑdkd/„ |dI  6¡ D ƒ}dld/„ |D ƒ}t.dmt/|ƒ› dnt/|ƒ› dMt/|ƒ› dot3dpdK„ |D ƒƒdP›dqt/|ƒt/|ƒ › drt3dsdK„ |D ƒƒdP›dtt3dudK„ |D ƒƒdP›�ƒ d S )vN)ÚdescriptionÚformatter_classz--datezone day, YYYY-MM-DD)Úhelpz--daysz='all' (every file in sim/candidates) or a comma list of datesz--candidates-filez,use this candidate list (single --date only)z--entryzAcomma list of entry modules (default: config rules.entry_modules)z--exitz/exit module (default: config rules.exit_module)z--rules-dirz&extra folder to import rule files fromz--max-positionsz%default: config trading.max_positions)Útyperl  z--equityz;sizing equity (default: the paper account's current equity)z--namez1results name (default: from the modules and time)z--fixed-entriesz/exit test: buy exactly the entries in this filez--setrð   zSECTION.KEY=VALUEz1override a config value for this run (repeatable))Úactionr@  Úmetavarrl  Ú=r�   Ú.rO  r   r
   ÚrulesÚfixed_entriesÚentry_modulesrj   ÚentriesÚdaterS   éc   ú,FÚ
hot_reloadc                 S   ó   g | ]
}|  ¡ r|  ¡ ‘qS r   ©Ústripr   r   r   r   r9   Õ  r¡   zmain.<locals>.<listcomp>Úexit_moduleÚallc                 S   rz  r   r{  )r   r'   r   r   r   r9   Ú  r¡   rˆ   zgive --date or --daysc                     s&   dˆ vrddl m}  | ƒ ˆ d< ˆ d S )Nr¾   r   ©Ú
get_client)Úalpaca_clientr€  r  ©Ú_clientr   r   r{   à  s   
zmain.<locals>.client_fnÚsim_ú+Ú__r³   z%Y%m%d_%H%M%Sr›   ÚsimulationsTr_   ztrade1 simulator | entry=z exit=z | equity=$z,.2fz | z day(s) -> ri   )r©   rt  r}  rÅ   rœ   )r+  rW   rœ   c                 s   ó   � | ]}|d  V  qdS ©rì   Nr   r8   r   r   r   r   ô  r   zmain.<locals>.<genexpr>rr   z	 trades, c                 s   s   � | ]	}|d  dkV  qdS ©rì   r   Nr   r8   r   r   r   r   õ  s   € z winners, $z+.2fz  [z.0fzs])Úflushrq   Ú6z [rÜ   z] rÚ   é   é   zZ @ rÕ   z.2fz -> rè   rç   z  $rì   z  (ré   rû   ú)r™   )Úindentr@  c                 S   s   g | ]}|d  D ]}|‘qqS )rW   r   )r   r¯   r5   r   r   r   r9   ý  s    c                 S   s   g | ]
}|d  dkr|‘qS )rì   r   r   r8   r   r   r   r9   þ  r¡   z
TOTAL z days: z winners ($c                 s   rˆ  r‰  r   r8   r   r   r   r      r   z), z
 losers ($c                 s   s$   � | ]}|d  dkr|d  V  qdS rŠ  r   r8   r   r   r   r     s   €" z), net $c                 s   rˆ  r‰  r   r8   r   r   r   r     r   )7ÚargparseÚArgumentParserr”   ÚRawDescriptionHelpFormatterÚadd_argumentr   r¶   Ú
parse_argsr  r   rK   rŠ   Ú
ValueErrorr	   Ú
setdefaultrS  rs  r   Úresolverw   r2   ÚinsertrA  r,  rX  rF   ÚaddrÇ   rv  rœ   Újoinr1  Ú	rules_dirÚentryÚexitr|  r3  ÚerrorrÅ   Úget_accountr©   rá   r   r<  Ústrftimers   rv   rN   rx   Útimeri  Úsumr§   rL   r^  )Úapr  ÚkvÚkr¯   ÚnodeÚpartsr7   Úfer@   Úrcrœ   r{   rÅ   r©   r_  Úresultsr'   Út0rW   Úsrcrô   r5   ÚalltÚwinsr   r‚  r   Úmain§  sÞ   ÿ
ÿ
 "ÿ
.ÿÿÿÿÿÿÿ&ÿÿÿ8ÿÿÿÿ
þ,"ÿÿþýr°  Ú__main__)r)   )r²   r   )1r”   r‘  rO   Ú	importlibrK   rw   r¢  Úconcurrent.futuresr   r   r   r   r   Úpathlibr   Úzoneinfor   Ú__file__r˜  Úparentr,  r2   r™  rA  Úconfig_loaderr	   r—  r¢   rF  r   rT  Úutcru   r#   r(   r1   r]   r�   r‚   r°   r  rÃ   rÄ   rö   r/  r3  ri  r°  r‘   r   r   r   r   Ú<module>   sJ    &
	

@
R	E^
ÿ