a
    1ÁRjÃ0  ã                   @   sR  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ZdZdZ	dZ
dd„ Zeƒ \ZZZi Zd	d
„ Zdd„ Zdd„ Zd,dd„Zdd„ Zd-dd„Zdd„ ZzddlZW n eyº   dZY n0 dd„ Zdd„ Zd.dd„Zdd„ Zd/d d!„Zd0d"d#„Ze d$k�rNddl!Z!e!j"j#d%d&d'� e$d(e%eƒ› d)�ƒ eƒ Z&e$d*e%e&ƒ› �ƒ e$d+ƒ eƒ  dS )1uµ  
Polymarket live price checker (read-only) â€” soccer match-winner markets.

Parallel to kalshi_live.py. Polymarket structures each match as a Gamma "event"
titled "Home vs. Away" containing three separate Yes/No markets:
    "Will {Home} win on {date}?"     -> YES price = P(home win)
    "Will {Away} win on {date}?"     -> YES price = P(away win)
    "Will {Home} vs. {Away} end in a draw?" -> YES price = P(draw)
Each market carries bestBid / bestAsk (the YES side). We expose the same
{home,draw,away} x {bid,ask,mid} dict shape that kalshi_live.get_all_prices returns,
so the rest of the pipeline can treat the two books uniformly.

Coverage note: Polymarket's soccer tag is dominated by big tournaments (World Cup)
and a rotating subset of club leagues; it does NOT list every league we track.
get_all_prices returns None when the fixture is not on Polymarket.

NOTHING is traded here. Pure price reads against the public Gamma API (no key).
é    Nz https://gamma-api.polymarket.comZsocceré<   zE:\soccer\data\league_map.jsonc               	   C   s    z:t tdd��} t | ¡}W d  ƒ n1 s.0    Y  W n  ttfyZ   i i g f Y S 0 dd„ | ¡ D ƒ}dd„ | ¡ D ƒ}tdd„ | ¡ D ƒƒ}|||fS )	u  {league_code: poly_tag}, {league_code: poly_prefix}, and the de-duped tag list.

    poly_prefix (added 2026-07-11): Poly's event slugs carry league prefixes
    ('per1-cal-csh-2026-07-19', 'mex-nec-atla-...'). Some leagues' MATCH events
    carry NO league tag at all (Peru/Chile/Romania are only tagged
    sports/games/soccer), so the slug prefix is the only reliable league
    discriminator for them â€” discovered when tag-guess audits wrongly called
    Liga MX and Peru "not on Poly" while the site showed 66-68 markets each.úutf-8)ÚencodingNc                 S   s*   i | ]"\}}|t |tƒr"| d ¡nd“qS )Zpoly_tagN©Ú
isinstanceÚdictÚget©Ú.0ÚcodeÚinfo© r   ú!E:\soccer\main\polymarket_live.pyÚ
<dictcomp>.   s   ÿz%_load_league_tags.<locals>.<dictcomp>c                 S   s*   i | ]"\}}|t |tƒr"| d ¡nd“qS )Zpoly_prefixNr   r	   r   r   r   r   0   s   ÿc                 S   s   h | ]}|r|’qS r   r   )r
   Útr   r   r   Ú	<setcomp>2   ó    z$_load_league_tags.<locals>.<setcomp>)	ÚopenÚLEAGUE_MAP_PATHÚjsonÚloadÚOSErrorÚ
ValueErrorÚitemsÚsortedÚvalues)ZfhZlmZby_codeZprefix_by_codeÚtagsr   r   r   Ú_load_league_tags    s    	,ÿÿr   c                 C   s2   t  d| pd¡ dd¡ ¡  ¡ } t dd| ¡ ¡ S )zALowercase, strip accents/punctuation -> comparable team-name key.ZNFKDÚ ÚasciiÚignorez
[^a-z0-9 ]ú )ÚunicodedataZ	normalizeÚencodeÚdecodeÚlowerÚreÚsubÚstrip)Úsr   r   r   Ú_norm9   s     r*   c                 C   sª   |   d¡}|r.t dt|ƒ¡r.t|ƒdd… S |   d¡p:g D ]h}|  d¡pV|  d¡pVd}t d	t|ƒ¡}|rz| d
¡  S t d|  dd¡pŽd¡}|r<| d
¡  S q<dS )z˜Game date 'YYYY-MM-DD' for an event. Prefers the clean eventDate field,
    falls back to gameStartTime / a 'win on {date}' question (World Cup format).Z	eventDatez\d{4}-\d{2}-\d{2}Né
   ÚmarketsZgameStartTimeZ
endDateIsor   z(\d{4}-\d{2}-\d{2})é   zwin on (\d{4}-\d{2}-\d{2})Úquestion)r   r&   ÚmatchÚstrÚgroupÚsearch)ÚeÚdÚmZgstÚmmZqmr   r   r   Ú_event_date?   s    
r7   c                 C   s0   t  d| ¡}|r,d| d¡ ¡ v r,| d¡S | S )z˜'EPL: Southampton vs. Man United' -> 'Southampton vs. Man United'.
    Only strips a leading 'Something: ' when the remainder still looks like 'X vs Y'.z^[^:]{1,40}:\s*(.+)$ú vsr-   )r&   r/   r1   r%   )Útitler5   r   r   r   Ú_strip_league_prefixP   s    
r:   Fc                 C   sÎ   t   ¡ }t | ¡}|s6|dur6||d  tk r6|d S g d }}z$tjt› d�d| d|dœd	d
�}W n tjy|   Y q¼Y n0 |js†q¼| ¡ }|s”q¼||7 }|d7 }t	|ƒdk s¼|dkr@q¼q@||dœt| < |S )zHAll open events for a single Gamma tag_slug (paginated), cached per tag.NÚtsÚeventsr   z/eventsZfalseéd   )ÚclosedZtag_slugÚlimitÚoffseté   )ÚparamsZtimeoutip  )r;   r<   )
ÚtimeÚ_cacher   Ú
_CACHE_TTLÚrequestsÚGAMMAZRequestExceptionÚokr   Úlen)ÚtagÚforceZnowÚcr<   ZoffÚrZbatchr   r   r   Ú
_fetch_tagY   s2    

ÿþ

rN   c                 C   s(   | rt  | ¡nd}|r|tgS ttg S )z§Tags to query for a fixture. With a known league -> just its poly_tag (+ soccer
    for cup/intl spillover); without -> every tracked league tag + soccer (full union).N)Ú_LEAGUE_TAG_BY_CODEr   Ú
SOCCER_TAGÚ_ALL_POLY_TAGS)Úleaguer   r   r   r   Ú_tags_for_leaguew   s    rS   c                 C   sn   |du rt tg }tƒ g  }}|D ]F}t|| d�D ]4}| d¡}|durR||v rRq2| |¡ | |¡ q2q"|S )zäOpen events across the given tags (default: all tracked league poly_tags +
    'soccer'), de-duped by event id. Each tag is paginated and cached _CACHE_TTL sec,
    so repeated calls within a poll cycle reuse the per-tag caches.N)rK   Úid)rQ   rP   ÚsetrN   r   ÚaddÚappend)rK   r   ÚseenÚoutrJ   r3   Zeidr   r   r   Úfetch_soccer_events€   s    


rZ   c                 C   sP   t | ƒt |ƒ }}|r|sdS ||kr*dS ||v s:||v oNtt|ƒt|ƒƒdkS )zQTrue if normalized team names a, b refer to the same club (substring either way).FTé   )r*   ÚminrI   )ÚaÚbZnaZnbr   r   r   Ú_names_match‘   s    r_   c                 C   sv   | g}t durrzPt  | ¡\}}|r,| |¡ |r\| d¡rH| |d ¡ | | d¡pXg ¡ W n typ   Y n0 |S )zGAll known names for a team: itself + its team_map key / fdco / aliases.NZfdcoÚaliases)Ú_mapsZ_lookuprW   r   ÚextendÚ	Exception)ÚteamrY   Úkeyr   r   r   r   Ú	_variants¤   s    

rf   c                    s   t ‡ fdd„t|ƒD ƒƒS )z3_names_match against every known variant of `team`.c                 3   s   | ]}t ˆ |ƒV  qd S )N)r_   )r
   Úv©Únamer   r   Ú	<genexpr>¶   r   z_team_match.<locals>.<genexpr>)Úanyrf   )ri   rd   r   rh   r   Ú_team_match´   s    rl   c                    s†  t | dƒr|  ¡ } t| ƒdd… } |r0t |¡nd}g }tt|ƒd�D ]°}| dd¡}d| ¡ vsFd| ¡ v rpqF|r�t| d	d¡ƒ |d
 ¡s�qFt	 
dt|ƒ¡}t|ƒdkr®qF|\}	}
t|	|ƒrÊt|
|ƒpÜt|	|ƒoÜt|
|ƒ}|säqF| |t|ƒf¡ qF|�sdS dd„ ‰ |j‡ fdd„d� |D ]\}}|| k�r"|  S �q"‡ fdd„|D ƒ}t|ƒdk�rh|d S t|ƒdk�r‚|d d S dS )uÃ  Return the Gamma event dict for this fixture, or None.
       game_date: 'YYYY-MM-DD' string OR datetime.date (normalized here â€” live_monitor
                  passes a date object; comparing that against Gamma's string dates
                  silently matched nothing until 2026-07-03).
       league:    optional tracked league code (e.g. 'E0') -> fetch only that league's
                  Polymarket tag instead of the full union (much cheaper).Ú	isoformatNr+   )r   r9   r   r8   úmore marketZslugú-z\s+vs\.?\s+é   c                 S   s   dt |  dd¡ƒvS )Nz - r9   r   )r:   r   )r3   r   r   r   Ú_is_mainÝ   s    zfind_event.<locals>._is_mainc                    s"   ˆ | d ƒ t | d  dd¡ƒfS )Nr   r9   r   )rI   r   )r   ©rq   r   r   Ú<lambda>ß   r   zfind_event.<locals>.<lambda>)re   c                    s   g | ]\}}ˆ |ƒr|‘qS r   r   )r
   r3   Ú_rr   r   r   Ú
<listcomp>å   r   zfind_event.<locals>.<listcomp>r-   r   )Úhasattrrm   r0   Ú_LEAGUE_PREFIX_BY_CODEr   rZ   rS   r%   Ú
startswithr&   Úsplitr:   rI   rl   rW   r7   Úsort)Ú	game_dateÚ	home_teamÚ	away_teamrR   ÚprefixZcandsr3   r9   ÚpartsÚhr]   rH   r4   Zmainsr   rr   r   Ú
find_event¹   s@    
ÿ
r�   c                 C   sX   dd„ }||   d¡ƒ}||   d¡ƒ}|dkr8|| d n|}t|dƒt|dƒt|dƒfS )zu(bid, ask, mid) for the YES side of a Yes/No market. mid mirrors kalshi_live:
       (ask+bid)/2 when bid>0 else ask.c              	   S   s(   z
t | ƒW S  ttfy"   Y dS 0 d S )Ng        )ÚfloatÚ	TypeErrorr   )Úxr   r   r   Ú_fî   s    z_yes_prices.<locals>._fZbestAskZbestBidr   rp   é   )r   Úround)Zmarketr…   ÚaskÚbidÚmidr   r   r   Ú_yes_pricesë   s
    r‹   c                 C   sÐ   t | |||d�}|sdS i }| d¡p(g D ]œ}| dd¡}| ¡ }t|ƒ\}	}
}d|v r^d}n>t d|¡}|spq*| d	¡}t||ƒrŠd
}nt||ƒr*d}nq*|	||› d�< |
||› d�< |||› d�< q*|pÎdS )zÿFetch home/draw/away YES prices from Polymarket. Returns dict or None.
       Shape matches kalshi_live.get_all_prices:
         {home_bid,home_ask,home_mid, draw_*, away_*}
       league: optional tracked league code to narrow the Gamma fetch to one tag.)rR   Nr,   r.   r   zend in a drawZdrawzwill (.+?) (?:beat|win on)r-   ÚhomeZawayZ_bidZ_askZ_mid)r�   r   r%   r‹   r&   r2   r1   rl   )r{   r|   r}   rR   ZevÚresultr5   ÚqZqlr‰   rˆ   rŠ   Zlabelr6   Zwhor   r   r   Úget_all_prices÷   s.    


r�   c                 C   sˆ   g }t ƒ D ]R}| dd¡}d| ¡ vs
d| ¡ v r4q
t|ƒ}| rJ|| krJq
|r
| ||f¡ q
t|ƒD ]\}}td|› d|› �ƒ qf|S )zMList soccer matches Polymarket currently has (optionally filtered to a date).r9   r   r8   rn   z  )rZ   r   r%   r7   rW   r   Úprint)Ztarget_daterY   r3   r9   r4   r   r   r   r   Úlist_matches  s    
r‘   Ú__main__r   Úreplace)r   Úerrorsz	querying z league tags + 'soccer'zopen events (union, deduped): z
upcoming matches:)F)FN)N)N)N)'Ú__doc__r&   rC   r   Úosr"   rF   rG   rP   rE   r   r   rO   rw   rQ   rD   r*   r7   r:   rN   rS   rZ   r_   Úmapsra   ÚImportErrorrf   rl   r�   r‹   r�   r‘   Ú__name__ÚsysÚstdoutÚreconfigurer�   rI   Zevsr   r   r   r   Ú<module>   sD   (	
	


2
"

