a
    ²Ã?jû  ã                   @   sò   d 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ZdZ	ddhZ
dd	„ Zd
d„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zedkrîeƒ Zedeeƒ› dedd„ eD ƒƒ› dedd„ eD ƒƒ› deedd„ eD ƒƒƒ› d�	ƒ eeƒ eeƒ dS )a—  
Is a team's result sequence (W/D/L) a *predictive* Markov chain -- and is it
EXPLOITABLE vs the closing line?

Three escalating tests on football-data.co.uk results (league_data/*.csv):
  A. Structure  : pooled first-order transition matrix P(next | last) vs the
                  unconditional base rate (chi-square test of independence).
  B. Prediction : train P(FTR | home_last, away_last) on seasons <= 2324,
                  test on 2425+2526. Compare log-loss & Brier to (i) base rates
                  and (ii) the de-vigged closing odds (the market).
  C. Exploit    : on the test set, bet the outcome where the Markov model most
                  disagrees with the market (higher prob), flat-staked at B365
                  closing odds. ROI + binomial significance.

No look-ahead: a match's state is each team's PREVIOUS result within the same
(league, season); train/test split is strictly by season.
é    N)ÚdefaultdictÚCounterzE:\soccer\league_datai	  iy	  iÞ	  c                 C   sD   t j | ¡}zt| d¡d  d¡d ƒW S  ty>   Y d S 0 d S )NÚ_é   Ú.r   )ÚosÚpathÚbasenameÚintÚsplitÚ	Exception)r   Úb© r   ú!E:\soccer\research\markov_test.pyÚ_season   s     r   c              	   C   s(   z
t | ƒW S  ttfy"   Y d S 0 d S ©N)ÚfloatÚ	TypeErrorÚ
ValueError©Úxr   r   r   Ú_f   s    r   c              
   C   sD   dD ]:}zddl m } | | |¡W   S  ttfy<   Y q0 qd S )N)z%d/%m/%Yz%d/%m/%yr   )Údatetime)r   Ústrptimer   r   )ÚsZfmtr   r   r   r   Ú_date"   s    r   c                  C   s¸  g } t   tj td¡¡D �]˜}t|ƒ}|du r0qtj |¡ d¡d }g }t|ddd��l}t	 
|¡D ]P}| d¡ptd ¡  ¡ }|d	vrˆqdt| d
¡ƒ}|du r qd| |||||f¡ qdW d  ƒ n1 sÊ0    Y  |jdd„ d� i }	|D ]Ä\}}}}
}| d¡| d¡ }}|	 |¡|	 |¡ }}|  t||
||||||t| d¡ƒt| d¡ƒt| d¡ƒd�¡ |dk�rvdn|dk�r„dnd|	|< |dk�ršdn|dk�r¨dnd|	|< qìq| S )zCReturn list of match dicts with each team's PRIOR result filled in.z*.csvNr   r   Ú zlatin-1)ÚnewlineÚencodingZFTR©ÚHÚDÚAZDatec                 S   s   | d S )Nr   r   )Útr   r   r   Ú<lambda>9   ó    zload.<locals>.<lambda>©ÚkeyZHomeTeamZAwayTeamZB365HZB365DZB365A)ÚdÚleagueÚseasonÚftrÚhomeÚawayÚhpÚapÚohÚodÚoar    ÚWr!   ÚLr"   )Úglobr   r   ÚjoinÚDIRr   r	   r   ÚopenÚcsvZ
DictReaderÚgetÚstripÚupperr   ÚappendÚsortÚdictr   )Úmatchesr   r*   r)   ZrowsZfhÚrr+   r(   ZlastZlgZseaZhtZatr.   r/   r   r   r   Úload*   s2    


4$þ$(rB   c                 C   sP   | r|r|sd S d|  d| d|   }}}|| | }|| || || dœS )Nr   r   r   )r0   r1   r2   ZihZidrZiar   r   r   r   ÚdevigF   s    (rC   c                    s:  t tƒ}t| dd„ d�D ]„}||d |d |d f  |d dkrFd	n|d d
krVd
nd¡ ||d |d |d f  |d dkr†d	n|d d
kr–d
nd¡ qt tƒ‰tƒ ‰| ¡ D ]D}t||dd … ƒD ],\‰ }ˆˆ  |  d7  < ˆ|  d7  < qÊq´g d¢}tˆ ¡ ƒ‰tdˆƒ tdd 	‡‡fdd„|D ƒ¡ ƒ d}|D ]”‰ tˆˆ   ¡ ƒ‰d 	‡ ‡‡fdd„|D ƒ¡}tdˆ › dˆd›d|› �ƒ |D ]@}ˆˆ|  ˆ }ˆˆ  | }	|dk�r’||	| d | 7 }�q’�qBd}
zddl
m} |j ||
¡}W n t�y   td ƒ}Y n0 td!|d"›d#|
› d$|d%›�ƒ d S )&Nc                 S   s   | d S )Nr(   r   r   r   r   r   r$   O   r%   ztest_A.<locals>.<lambda>r&   r)   r*   r,   r+   r    r3   r!   r4   r-   r"   r   )r3   r!   r4   z@A. First-order result memory (team perspective), N transitions =z   unconditional:  z  c                 3   s&   | ]}|› d ˆ | ˆ d›�V  qdS )ú=ú.3fNr   )Ú.0r   )ÚbaseÚtotr   r   Ú	<genexpr>X   r%   ztest_A.<locals>.<genexpr>ç        ú c                 3   s*   | ]"}|› d ˆˆ  | ˆ d›�V  qdS )ú:rE   Nr   )rF   r   )ÚaÚnaÚtransr   r   rI   \   r%   z	   after z (n=Ú5dz): r   é   é   )ÚstatsÚnanz$   chi-square vs independence: chi2=z.1fz, df=z, p=z.2e)r   ÚlistÚsortedr=   r   ÚvaluesÚzipÚsumÚprintr6   ZscipyrS   Zchi2Zsfr   r   )r@   ZseqsÚmÚseqr   ZstatesZchiÚrowZexpZobsZdfrS   Úpr   )rM   rG   rN   rH   rO   r   Útest_AL   s2    @B(
"&r_   c                    sF  dd„ | D ƒ}dd„ | D ƒ}t tƒ‰|D ](}ˆ|d |d f |d   d7  < q(tdd	„ |D ƒƒ‰ tˆ  ¡ ƒ‰‡ ‡fd
d„dD ƒ‰‡‡fdd„‰dd„ }‡fdd„|D ƒ}‡fdd„|D ƒ}dd„ |D ƒ}tdt|ƒdƒ d|fd|fd|ffD ]<\}}	||	ƒ\}
}}td|› d|d›d|
d›d|d›�ƒ qòtdƒ d  } } }}|D ]¸}t|d! |d" |d# ƒ‰ˆ�sr�qLˆ|ƒ‰td‡‡fd$d%„d&�}ˆ| ˆ|  d k�rª�qL|d! |d" |d# dœ| }|d7 }|d7 }|d |k�rú||d 7 }|d7 }n|d8 }�qL|�rd'| | nd }td(|› d)|› d*|d+›d,|d+›d-�	ƒ d S ).Nc                 S   s,   g | ]$}|d  t kr|d r|d r|‘qS ©r*   r.   r/   )Ú	TRAIN_MAX©rF   r[   r   r   r   Ú
<listcomp>j   r%   ztest_BC.<locals>.<listcomp>c                 S   s,   g | ]$}|d  t v r|d r|d r|‘qS r`   )ÚTEST_SEASONSrb   r   r   r   rc   k   r%   r.   r/   r+   r   c                 s   s   | ]}|d  V  qdS )r+   Nr   rb   r   r   r   rI   o   r%   ztest_BC.<locals>.<genexpr>c                    s   i | ]}|ˆ | ˆ “qS r   r   ©rF   Úk)rG   Úbtr   r   Ú
<dictcomp>p   r%   ztest_BC.<locals>.<dictcomp>r   c                    sF   ˆ| d | d f ‰ t ˆ  ¡ ƒ‰ˆdk r0tˆƒS ‡‡ ‡fdd„dD ƒS )Nr.   r/   é   c                    s*   i | ]"}|ˆ| ˆ | d   ˆd   “qS )é   r   re   )ÚbaserateÚcÚnr   r   rh   t   r%   z-test_BC.<locals>.markov_p.<locals>.<dictcomp>r   )rY   rW   r?   )r[   )rk   Úcnt)rl   rm   r   Úmarkov_pq   s     ztest_BC.<locals>.markov_pc                    sr   d }}d}| D ]N\}‰ |d ‰|t  tˆ ˆ dƒ¡ 7 }|t‡ ‡fdd„dD ƒƒ7 }|d7 }q|| || |fS )	NrJ   r   r+   g•Ö&è.>c                 3   s*   | ]"}ˆ | |ˆkrd nd d V  qdS )g      ð?rJ   rQ   Nr   re   ©r^   Úyr   r   rI   {   r%   z1test_BC.<locals>.logloss_brier.<locals>.<genexpr>r   r   )ÚmathÚlogÚmaxrY   )ZpredsÚllÚbrrm   r[   r   rp   r   Úlogloss_brierv   s    
ztest_BC.<locals>.logloss_brierc                    s   g | ]}|ˆ |ƒf‘qS r   r   rb   )ro   r   r   rc   ~   r%   c                    s   g | ]}|t ˆ ƒf‘qS r   )r?   rb   )rk   r   r   rc      r%   c                 S   s@   g | ]8}t |d  |d |d ƒr|t |d  |d |d ƒf‘qS )r0   r1   r2   )rC   rb   r   r   r   rc   €   r%   z
B. Out-of-sample prediction onztest matches (24/25+25/26):zMarkov (form)zBase rate   zMarket (B365)z   z: n=rP   z
  logloss=z.4fz  brier=zA
C. Exploitable vs closing line? (flat $1 bets at B365, test set)r   r0   r1   r2   c                    s   ˆ |  ˆ|   S r   r   )rf   )ÚmpÚmq_r   r   r$   �   r%   ztest_BC.<locals>.<lambda>r&   éd   z   bets=z  wins=z  net=z+.1fz units  ROI=ú%)r   r   rY   rW   rZ   ÚlenrC   rt   )r@   ZtrainÚtestr[   rw   ZmkZbsZmqZnmZprru   rv   rm   ZstakedZretZwinsZpickZoddsZroir   )rG   rk   rg   rn   ro   rx   ry   r   Útest_BCi   s:    .(
$r~   Ú__main__zLoaded z matches, seasons c                 c   s   | ]}|d  V  qdS )r*   Nr   rb   r   r   r   rI   ™   r%   rI   ú-z, c                 c   s   | ]}|d  V  qdS )r)   Nr   rb   r   r   r   rI   š   r%   z	 leagues
)Ú__doc__r9   r5   r   rr   Úcollectionsr   r   r7   ra   rd   r   r   r   rB   rC   r_   r~   Ú__name__ÚMrZ   r|   Úminrt   Úsetr   r   r   r   Ú<module>   s&    .6ÿ
