a
    ‘xj0&  ã                   @   sš   d Z ddlZddlZddlZddlmZ ej dd¡ ddlZ	e	j
Z
e	jZdd„ Zdd„ Zddd„Zddd„Zddd„Zdd„ Zdd„ Zedkr–eƒ  dS )aå  Candidate-rule sweep with an explicit train/test split. Trades nothing.

WHY THESE CANDIDATES AND NOT A GRID SEARCH. With 1,717 fixtures and a flexible
rule space, something will always backtest well -- `unique` shows +20.6% at n=55
and is a hindsight artifact whose executable cousin t60 runs -18.3%. So the
candidates here are limited to three MECHANISMS, each motivated by a measured
finding rather than by scanning for a winner:

  1. PRICE FLOOR. The binding problem is variance, not edge: xlow's per-bet stdev
     is 1.94u at a mean price of 0.26, so proving its observed +0.8% ROI needs
     ~100,000 bets (~46 years). Variance of a unit bet scales as (1-p)/p, so
     restricting to higher-priced outcomes cuts the required n roughly 4x at
     p=0.5 versus p=0.26. This does not create edge; it makes edge MEASURABLE.

  2. PERSISTENCE. forward_test_unique_book.fire_xlow's own docstring flags this
     as "a candidate refinement for the n>=100 review -- NOT wired now": on a 1c
     grid with flip-flopping books, scanning for the first qualifying snapshot
     finds TRANSIENT configurations. Requiring the shape to hold N consecutive
     snapshots should drop the noise fires. Pre-registered, not dredged.

  3. KALSHI-ONLY. xlow needs both books and only 411 of 1,717 fixtures carry
     both; 1,364 carry Kalshi+Pinnacle. A single-book rule has 3x the sample,
     which matters more than rule elegance when the whole problem is power.

DISCIPLINE. Train = on/before FREEZE_DATE, test = after. Report BOTH always;
a candidate that only works in train is dead. Note that xlow's core was itself
fitted on train, so train numbers for its variants are optimistic by
construction -- only the TEST column is evidence, and only the INCREMENT over
baseline xlow is attributable to the new dimension.

MULTIPLE COMPARISONS. Every candidate tried is printed, including losers, with
the count. At ~20 candidates, one crossing p=0.05 by luck is the expectation,
not a finding.

Usage:  python research\rule_sweep.py
é    N)ÚdefaultdictzE:\soccer\researchc                 C   sn   t  |  d¡¡t  |  d¡¡t  |  d¡¡  }}}d |||fv sZ|dkrZ|dkrZ|dks^d S t  |||f¡S )NZpin_homeZpin_drawZpin_awayé   )ÚFTZ_fÚgetZ	fair_mult)ÚrZphZpd_Zpa© r   ú E:\soccer\research\rule_sweep.pyÚ	_pin_fair1   s    4&r	   c                 C   sþ   t  | ||¡\}}t  | ||¡\}}|du s<|du s<|du r@dS d|  k rTdk rnn nd|  k rldk srn dS ||kr~dS |t|  }	|	dkr–dS |	| |	 }
|	| |	 }td |
  krÊtd k sÐn dS td |  krìtd k rôn ndS |
||fS )zD(clv, mid, ask) if `book` is the exclusive low on `side`, else None.Nr   r   )r   Ú
book_quoteÚSIDXÚBAND)r   ÚpfÚsideÚbookÚotherÚmÚaZomÚ_ÚfairÚclvZoclvr   r   r   Ú
_qualifies8   s$    . r   ç        r   c              
   C   sP  g }| D �]@}t tƒ}d}|d D ]Ô}t|ƒ}|du r8q"i }	dD ]>}
dD ]4\}}t|||
||ƒ}|rH|d |krH||	|
|f< qHq@t|ƒD ]}||	vrˆd||< qˆd}|	 ¡ D ]>\}}||  d7  < || |krª|du rª|d |d |f}qª|r"|} qøq"|sþq|\}
}\}}}|d |
dk�r"d	nd
k}| |�r>d| d nd||f¡ q|S )zŠOne bet per fixture: first pre-KO snapshot where the shape holds for
    `persist` CONSECUTIVE snapshots. Returns list of (ret, ask, won).NÚpre©ÚhomeZaway))ÚkalshiÚpoly)r   r   r   r   Úftrr   ÚHÚAç      ð?ç      ð¿)r   Úintr	   r   ÚlistÚitemsÚappend)ÚfixturesÚ	min_priceÚpersistÚoutÚfxÚstreakÚpickedr   r   Úliver   r   r   ÚqÚkÚhitr   ZmidÚaskÚwonr   r   r   Úrun_xlowN   s>    

$r3   c                 C   s–  g }| D �]†}t tƒ}d}|d D �]}t|ƒ}|du r:q"i }	dD ]�}
t |d|
¡\}}|du sB|du sBd|  k r|dk s€qB qB|t|
  }|dkr–qB|| | }td |  kr¾td k rBn qB||krB||	|
< qBt|ƒD ]}||	vrÜd||< qÜd}|	 ¡ D ]4\}}||  d7  < || |krþ|du rþ||f}qþ|r"|} �qDq"|�sLq|\}
}|d |
dk�rhd	nd
k}| 	|�r„d| d nd||f¡ q|S )zIKalshi mid-CLV in band vs Pinnacle. No second book required -> 3x sample.Nr   r   r   r   r   r   r   r   r   r    r!   ©
r   r"   r	   r   r
   r   r   r#   r$   r%   )r&   r'   r(   r)   r*   r+   r,   r   r   r-   r   r   r   r   r   r/   r0   r1   r2   r   r   r   Úrun_kalshi_onlyt   sH    
&(


$r5   c                 C   s´  g }| D �]¤}t tƒ}d}|d D �]<}t|ƒ}	|	du r:q"i }
dD ]¨}t |||¡\}}|du sB|du sBd|  k r|dk rBn qBd|  k r”dk s˜qB qB|	t|  }|dkr®qB|| | }td |  krÖtd k rBn qB||krB||
|< qBt|ƒD ]}||
vrôd||< qôd}|
 ¡ D ]:\}}||  d7  < || |k�r|du �r||f}�q|r"|} �qbq"|�sjq|\}}|d |dk�r†dnd	k}| 	|�r¢d
| d
 nd||f¡ q|S )aw  Single-book mid-CLV in band vs Pinnacle, for any one book.

    Poly-US matters here for two reasons beyond sample size: it pays a MAKER
    REBATE (0.0125 x p x (1-p)) where Kalshi charges a taker fee, and it is the
    only venue covering GAP_LEAGUES. Note its history only starts 2026-07-24,
    so it has no pre-freeze period -- the split for it is internal by date.
    Nr   r   r   r   r   r   r   r   r    r!   r4   )r&   r   r'   r(   r)   r*   r+   r,   r   r   r-   r   r   r   r   r   r/   r0   r1   r2   r   r   r   Úrun_book_onlyœ   sH    
>(

$r6   c                 C   s    t | ƒ}|dkrd S t dd„ | D ƒ¡}t dd„ | D ƒ¡p>d}|t |¡ }t|||| tdd„ | D ƒƒ| t dd„ | D ƒ¡|d	| tt	|ƒd
ƒ d d�S )Nr   c                 s   s   | ]}|d  V  qdS )r   Nr   ©Ú.0Úxr   r   r   Ú	<genexpr>Î   ó    zsummarise.<locals>.<genexpr>c                 S   s   g | ]}|d  ‘qS )r   r   r7   r   r   r   Ú
<listcomp>Ï   r;   zsummarise.<locals>.<listcomp>g•Ö&è.>c                 s   s   | ]}|d  V  qdS )é   Nr   r7   r   r   r   r:   Ñ   r;   c                 s   s   | ]}|d  V  qdS )r   Nr   r7   r   r   r   r:   Ò   r;   ç\�Âõ(\ÿ?g�íµ ÷Æ°>r=   )ÚnÚroiÚzÚwrÚimpÚsdÚneed)
ÚlenÚ
statisticsZmeanZpstdevÚmathZsqrtÚdictÚsumÚmaxÚabs)Zrsr?   r   rD   Zser   r   r   Ú	summariseÊ   s     þrM   c                  C   sd  t  ¡ } dd„ | D ƒ}dd„ | D ƒ}tt| ƒ› dt j› dt|ƒ› dt|ƒ› �ƒ tdƒ tdƒ g }d	D ]4}d
D ]*}| d|d›d|› �||fdd„f¡ qpqhdD ]4}dD ]*}| d|d›d|› �||fdd„f¡ qªq¢tdd›ddd›ddd›ddd›ddd›ddd›ddd›ddd ›�ƒ g }|D ]®\}}t||ƒƒt||ƒƒ }	}
|	�r&|
�sX�q&| ||	|
f¡ t|d›d|	d! d"›dd#|	d  d$›d%|	d d&›d|
d! d'›dd#|
d  d$›d%|
d d&›d|
d( d)›�ƒ �q&td*t|ƒ› d+d,t|ƒ d-›d.�ƒ d/d„ |D ƒ}td0t|ƒ› �ƒ |D ]@\}}	}
td|› d1|
d! › d2d#|
d  d3›d4|
d d5›�ƒ �qd S )6Nc                 S   s   g | ]}|d  t jkr|‘qS ©Úgd©r   ÚFREEZE_DATE©r8   Úfr   r   r   r<   Ø   r;   zmain.<locals>.<listcomp>c                 S   s   g | ]}|d  t jkr|‘qS rN   rP   rR   r   r   r   r<   Ù   r;   z fixtures | train (<=z) z | test zGtrain numbers for xlow variants are OPTIMISTIC -- xlow was fitted here.z"only the TEST column is evidence.
)r   çš™™™™™É?g      Ð?ç333333Ó?çš™™™™™Ù?)r   r=   é   zxlow  minP>=z.2fz	 persist=c                 S   s   t | ||ƒS ©N)r3   ©rS   ÚmpÚpzr   r   r   Ú<lambda>â   r;   zmain.<locals>.<lambda>)r   rT   rU   rV   )r   r=   zkalshi-only minP>=c                 S   s   t | ||ƒS rX   )r5   rY   r   r   r   r\   æ   r;   Ú	candidateZ36sú zTRAIN nz>8sr@   z>7srA   z>6sz   zTEST nzneed nz>9sr?   Z8déd   z+6.1fz% z+6.2fZ7drE   z9,.0fÚ
z candidates tried. At p=0.05, ~gš™™™™™©?z.1fz0 are expected to look significant by luck alone.c                 S   s    g | ]}|d  d dkr|‘qS )r=   rA   r>   r   )r8   r   r   r   r   r<   õ   r;   zcandidates with TEST z > 1.96: z	: test n=z roi=z+.1fz% z=z+.2f)r   Zload_fixturesÚprintrF   rQ   r%   rM   )r*   ZtrainÚtestZcandsrZ   r[   ZrowsÚnameÚfnr   ÚbZgoodr   r   r   ÚmainÖ   s\    ,ÿ
ÿ
"ÿÿÿÿ2ÿÿÿÿ$rf   Ú__main__)r   r   )r   r   )r   r   )Ú__doc__rH   rG   ÚsysÚcollectionsr   ÚpathÚinsertZforward_test_unique_bookr   r   r   r	   r   r3   r5   r6   rM   rf   Ú__name__r   r   r   r   Ú<module>   s"   $
&
(
.%