a
    ¤t€jZ3  ã                   @   sÜ   d 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	 ddl
ZddlmZ ddlmZmZ ddlmZ ddlmZ e	d	ƒZe	d
ƒZdd„ Zdd„ Zddd„Zdd„ Zdd„ Zddd„Zdd„ ZedkrØeƒ  eƒ  dS )aÔ  Can we predict WHICH LEGS FILL, at the moment we post them?

WHY. The maker's whole problem is partial fills: the prize is ~3c a book and
every repair (crossing to complete, flattening a stranded leg) costs about that
much. If fill probability is predictable at post time, the fix is to quote only
books whose three legs are all likely to fill -- and skip the rest, which costs
nothing.

NO LOOK-AHEAD. Every feature is read from the FIRST quote row for that leg, so
it is knowable at the instant we decide to post. Nothing about how the quote
evolved, and nothing about the trade that filled it, is allowed in.

THE BAR IT HAS TO CLEAR. We already know price distance predicts fills -- legs
that walk to the touch fill ~25%, legs that stay 2c above fill 1-6%. A model
that only rediscovers that is worth nothing. So the honest test is whether the
full feature set beats a one-feature baseline on held-out data, not whether it
beats random.

Usage:  python research\fill_model.py
é    N)Údefaultdict)ÚPath)ÚLogisticRegression)ÚStratifiedKFoldÚcross_val_score)Úmake_pipeline)ÚStandardScalerz&E:\soccer\research\mm_paper_quotes.csvz'E:\soccer\research\mm_paper_results.csvc              	   C   s8   zt | ƒ}||kr|nd W S  ttfy2   Y d S 0 d S )N)ÚfloatÚ	TypeErrorÚ
ValueError)ÚxÚv© r   ú E:\soccer\research\fill_model.pyÚ_f&   s
    r   c              
   C   s>   dD ]4}zt j t| ƒ|¡W   S  ttfy6   Y q0 qd S )N)z%Y-%m-%dT%H:%M:%S.%f%zz%Y-%m-%dT%H:%M:%S%zz%Y-%m-%d %H:%M:%S)ÚdtÚdatetimeÚstrptimeÚstrr   r
   )ÚsÚfr   r   r   Ú_pt.   s    r   Fc           !      C   sB  i i  }}t jdddd��z}t |¡D ]^}| d¡}|s:q&t| d¡ƒ ¡ dv rTdnd	||< t| d
¡ƒpldt| d¡ƒp|d ||< q&W d  ƒ n1 sš0    Y  tt	 
¡ ƒd }t t|¡ i }|jdddd��T}t |¡D ]8}| d¡}|rà||v rà||vrà| d¡sàdsà|||< qàW d  ƒ n1 �s00    Y  g d¢}g g g   }	}
}ttƒttƒ }}| ¡ D ]b\}}t| d¡ƒ}|du�r || d¡  |¡ t| d¡ƒ}|du�rl|| d¡  |¡ �ql| ¡ D �]0\}}t| d¡ƒt| d¡ƒt| d¡ƒ  }}}t| d¡ƒt| d¡ƒ }}t| d¡ƒt| d¡ƒ }}| | d¡¡�p`g }d||||fv �sØ|�rØ|du �sØ|du �r’�qØd	|  k �r¶|  k �r¶dk �s¼n �qØ||  ¡ d }d	|  k �rädk�sên �qØd	dl}t| d¡ƒ ¡ }t| d¡ƒ}t| d¡ƒ}t| d ¡ƒ}| | d¡¡�pDdg}|	 ||| d! || d! |d" d! |du�r€|d" d! ndt|ƒt|ƒt| d¡ƒ�p dt| d#¡ƒ�p²dt|ƒ||d$k�rÈd"nd|d%k�rØd"nd|du�rð|| d! ndt| d&¡ƒ�pdt| d'¡ƒ�pdt| d(¡ƒ�p(dt| d)¡ƒ�p:dt| d*¡ƒ�pLdt| d+¡ƒ�p^d| d,|j |�prd	 d- ¡| d,|j |�pŽd	 d- ¡|du�r¬|| d! nd|du�r¼d"ndg¡ |
 || ¡ | | d¡||| |d¡t|ƒ| d.¡�p d/d0œ¡ �qØt |	t¡t |
t¡ }} | �r8|| ||fS || |fS )1aB  (X, y, names[, events]) -- one row per leg, features from its FIRST quote.

    ONE code path. An earlier version rebuilt the event list with a copy of the
    row filter, which can drift from this one and silently pair a leg's features
    with another leg's book -- the kind of bug that produces a plausible number.
    Ú zutf-8Úreplace)ÚnewlineÚencodingÚerrorsZ
leg_tickerZfilled)Ú1ÚTrueé   r   Z	pnl_centsç        ÚsizeNzmm_paper_quotes.snap.csvÚreason)Úour_askÚedge_over_mkt_cZspread_cZoverround_cZ
over_mid_cZmin_leg_volZbook_vol_sumÚask_sizeÚbid_sizeZbook_ask_size_minZ	hrs_to_koZis_drawZis_homeZ
fair_gap_cÚarmÚ	mkt_age_hÚopen_interestÚ
volume_24hÚno_bidÚno_askZhour_sinZhour_cosZpc_gap_cZhas_pcZvolumeÚeventr%   r#   Zk_bidZk_askZover_askZpin_fairÚtsZkickoffg      ¬@é   ÚsideZover_midZpc_askZ
local_hourç      Y@ç      ð?r&   ZdrawÚhomer'   r(   r)   r*   r+   r,   é   g      8@Úregimeú?)r-   ÚkoZtickerÚpnlÚvolr5   )ÚRESULTSÚopenÚcsvZ
DictReaderÚgetr   Ústripr   r   ÚtempfileZ
gettempdirÚshutilZcopyfileÚQUOTESr   ÚlistÚitemsÚappendr   Ztotal_secondsÚmathÚlowerÚminÚsumZsinÚpiZcosÚnpZarrayr	   Úint)!Úwith_eventsZlabelZlegpnlZfhÚrÚtÚtmpÚfirstZNAMESÚXÚyÚeventsZbookvolZbookaskr   ÚaZaskZkbZkaZoverZfairr.   r7   ZvolsZhrsrE   r0   ZomidZpcaZhourZasksZXaZyar   r   r   Úbuild8   s˜    

"H
"*

.,&


éþrU   c                  C   s"  t ƒ \} }}| j\}}t|› d| ¡ › dd| ¡  d›d�ƒ |dk sb| ¡ dk sb|| ¡  dk rntdƒ d S td	d
dd�}ttƒ tddd�ƒ}t	|| ||dd�}| 
d¡}t	|| d d …|gf ||dd�}	tdd›ddd›ddd›�ƒ tdd›d|	 ¡ d›d|	 ¡ d›�ƒ tdd›d| ¡ d›d| ¡ d›�ƒ tdd›d| ¡ |	 ¡  d›�ƒ td ƒ t|ƒD ]F\}
}t	|| d d …|
gf ||dd�}td!|d"›d| ¡ d#›�ƒ �qh| | |¡ |d$ jd }td%ƒ tt||ƒd&d'„ d(�D ]"\}}td!|d"›d|d)›�ƒ �qètd*ƒ t||ƒ d S )+Nz( legs with complete post-time features, z	 filled (r1   z.0fz%)
é<   é   z2too few events to fit anything honest -- stopping.é   Tr   )Zn_splitsZshuffleZrandom_stateéÐ  r2   ©Zmax_iterÚCZroc_auc)ÚcvZscoringr$   ZmodelZ28ú zCV AUCú>8Zsdú>7zprice distance onlyz>8.3fz>7.3fzall featureszlift from everything elsez>+8.3fz
per-feature CV AUC on its own:ú  Z20z.3féÿÿÿÿz8
standardised coefficients (full fit, sign = direction):c                 S   s   t | d ƒ S ©Nr   )Úabs©rN   r   r   r   Ú<lambda>º   ó    zmain.<locals>.<lambda>©Úkeyz+.3fz’
AUC 0.5 is a coin flip; 0.7+ is a usable signal. Judge the LIFT line, not
the headline -- price distance is already known and already actionable.)rU   ÚshapeÚprintrH   Úmeanr   r   r   r   r   ÚindexZstdÚ	enumerateÚfitZcoef_ÚsortedÚzipÚbook_readout)rQ   rR   ÚnamesÚnÚkr\   ÚpipeÚfullZi_edgeÚbaseÚiZnmr   ZcoefÚcr   r   r   Úmainš   s<    
ÿ
$
ÿ
 &&"  rz   c                 C   sà  ddl m} tdd�\}}}}t|ƒdk r.dS || |||dd�dd…d	f }td
d„ ƒ}t|||ƒD ]6\}	}
}||d  d  |	¡ ||d  d  |
¡ qddd„ | ¡ D ƒ}t|ƒdk rÒtdt|ƒ› d�ƒ dS | 	¡  tdt|ƒ› d�ƒ tddd›ddd›ddd›ddd›�ƒ t
d	t|ƒd  ƒ}td!ƒD ]¢\}}|d"k �rZ||| |d	 | … n|d"| d… }|�st�q0tt d#d„ |D ƒ¡ƒ}tt d$d„ |D ƒ¡ƒ}td|d›dt|ƒd›d|d%›d|d&›�ƒ �q0td'ƒ dS )(a1  A leg model is only useful if it separates COMPLETABLE BOOKS.

    A book pays only when all three legs fill, so the decision at post time is
    P(all three) -- the product of the per-leg probabilities. Probabilities are
    OUT-OF-FOLD, so no book is scored by a model that trained on its own legs.
    r   )Úcross_val_predictT©rL   rV   NÚpredict_proba)r\   Úmethodr   c                   S   s
   g g dœS )N)ÚprR   r   r   r   r   r   re   Ð   rf   zbook_readout.<locals>.<lambda>r-   r   rR   c                 S   sD   g | ]<}t |d  ƒdkrt |d ¡tt|d  ƒƒt |d  ƒf‘qS )rR   é   r   )ÚlenrJ   ÚprodrK   Úall)Ú.0Údr   r   r   Ú
<listcomp>Ô   s   ÿz book_readout.<locals>.<listcomp>é   ú
only z+ complete 3-leg books -- too few to bucket.z(
BOOK COMPLETION vs PREDICTED P(all 3), z books, out-of-foldr`   ZbucketZ14r]   Úbooksz>6Zpredr_   Zactualr^   é   )z	worst 25%Z2ndZ3rdzbest 25%r€   c                 S   s   g | ]}|d  ‘qS )r   r   ©r„   r   r   r   r   r†   â   rf   c                 S   s   g | ]}|d  ‘qS )r   r   r‹   r   r   r   r†   ã   rf   z>7.1%z>8.1%zD  If actual rises with predicted, the model can pick books to quote.)Úsklearn.model_selectionr{   rU   r�   r   rp   rD   Úvaluesrj   ÚsortÚmaxrm   r	   rJ   rk   )ru   r\   r{   rQ   rR   rr   rS   r   r‰   rI   ZyiÚevZrowsÚqrx   ÚlblZsegZprZacr   r   r   rq   Ã   s6    ÿ(20rq   çÍÌÌÌÌÌä?c              
      s  t dd�\}‰}}tdd„ ƒ}t|ƒD ]–\}}||d  }|d  |¡ |d  |d 7  < |d d	u rp|d nt|d |d ƒ|d< |d
 d	u rš|d
 nt|d
 |d
 ƒ|d
< |d |d< q&dd„ | ¡ D ƒ}|jdd„ d� t|ƒdk �rtdt|ƒ› d�ƒ d	S t	t|ƒ|  ƒ}	|d	|	… ||	d	…  }
}dd„ |
D ƒ}t
tƒ tddd�ƒ}| || ˆ| ¡ g }|D ]n\}}}tt | ||d  ¡d	d	…df ¡ƒ}t	t‡fdd„|d D ƒƒƒ}| ||d ||d
 dœ¡ �qntdd› dt|
ƒ› dt|ƒ› d�ƒ td |
d! d! d"›d#|
d$ d! d"›d%|d! d! d"›d#|d$ d! d"›�ƒ td&d„ |
D ƒƒ}td'd„ |D ƒƒ}td(|› d%|› �ƒ d)d*„ }td+d,d-›d.d/d0›d1d2d3›d4d5d3›d6�	ƒ |d7|ƒ |d8d9d„ |D ƒƒ d:D ]&‰ |d;ˆ d<›�‡ fd=d„|D ƒƒ �qÖtd>ƒ td?ƒ d	S )@aõ  TEMPORAL split, scored in dollars.

    Cross-validation leaks here: legs from the same book, the same day and the
    same regime land in different folds, and a gate is a decision made BEFORE
    the market it is judged on. So train on the earliest books and test on the
    latest, and score the test set the way a bankroll would feel it -- realised
    P&L of the books the gate would have admitted, against the two baselines
    that already exist: quote everything, and the live volume gate.
    Tr|   c                   S   s   g dd d d dœS )Nr    )Úidxr8   r7   r9   r5   r   r   r   r   r   re   õ   s   ÿzbacktest.<locals>.<lambda>r-   r”   r8   r7   Nr9   r5   c                 S   s.   g | ]&\}}t |d  ƒdkr|d ||f‘qS )r”   r€   r7   )r�   )r„   r�   Úbr   r   r   r†   þ   rf   zbacktest.<locals>.<listcomp>c                 S   s   | d S )Nr   r   rd   r   r   r   re   ÿ   rf   rg   rV   rˆ   z# full books -- too few to backtest.c                 S   s$   g | ]\}}}|d  D ]}|‘qqS )r”   r   )r„   Ú_r•   rx   r   r   r   r†     rf   rY   r2   rZ   r   c                 3   s   | ]}ˆ | d kV  qdS )r   Nr   )r„   rx   )rR   r   r   Ú	<genexpr>  rf   zbacktest.<locals>.<genexpr>)r   r8   Úcompr9   Ú
z@================================================================z
TEMPORAL BACKTEST  (train z books, test ú)z  train r   z%Y-%m-%dz .. ra   z   test c                 S   s   g | ]\}}}|d  ‘qS ©r5   r   ©r„   r–   r•   r   r   r   r†     rf   c                 S   s   g | ]\}}}|d  ‘qS r›   r   rœ   r   r   r   r†     rf   z  regimes  train c                 S   s–   |s t d| d›ddd›�ƒ d S tdd„ |D ƒƒd }dtd	d„ |D ƒƒ t|ƒ }t d| d›dt|ƒd
›d|d›d|d›d|t|ƒ d›d�ƒ d S )Nr`   Ú26r]   z0 booksz>9c                 s   s   | ]}|d  V  qdS )r8   Nr   r‹   r   r   r   r—     rf   z(backtest.<locals>.row.<locals>.<genexpr>r1   c                 s   s   | ]}|d  V  qdS )r˜   Nr   r‹   r   r   r   r—     rf   ú>5z books  z>5.0fz% complete  z>+8.2fz $  z>+7.3fz $/book)rj   rH   r�   )r’   Zselr8   Zcrr   r   r   Úrow  s     ÿÿzbacktest.<locals>.rowz
  Zruler�   r]   r‰   rž   z	         Zcompleter^   z      zP&Lz   per bookzquote everythingzlive gate: vol >= 500c                 S   s   g | ]}|d  dkr|‘qS )r9   iô  r   r‹   r   r   r   r†   %  rf   )gš™™™™™É?g333333Ó?gš™™™™™Ù?g      à?g333333ã?zmodel: P(all 3) >= z.2fc                    s   g | ]}|d  ˆ kr|‘qS )r   r   r‹   )Úthrr   r   r†   '  rf   zE
  The gate can only ever REMOVE books, so it wins by cutting losers.z>  Judge it against "quote everything" on the same test window.)rU   r   rm   rD   rG   rC   rŽ   r�   rj   rK   r   r   r   rn   r	   rJ   r‚   r}   rƒ   Úcollections_counter)Z
train_fracrQ   rr   Úmetar‰   rx   Úmr•   rv   ZcutZtrainÚtestZtr_idxru   Zscoredr7   r�   r   r˜   Ztr_regZte_regrŸ   r   )r    rR   r   Úbacktestê   s^    
**
ÿ( ÿ
$ÿÿ	ÿ

$r¥   c                 C   s2   t tƒ}| D ]}||  d7  < qtt| ¡ ƒƒS rb   )r   rK   Údictro   rC   )Zxsry   r   r   r   r   r¡   ,  s    r¡   Ú__main__)F)r“   )Ú__doc__r<   r@   r?   r   r   Úcollectionsr   Úpathlibr   ZnumpyrJ   Zsklearn.linear_modelr   rŒ   r   r   Zsklearn.pipeliner   Zsklearn.preprocessingr   rA   r:   r   r   rU   rz   rq   r¥   r¡   Ú__name__r   r   r   r   Ú<module>   s.   

b)'
B