a
    L'êjgG  ã                   @  s@  U d Z ddlm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	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 ddlmZmZmZ ej ej e¡¡Zej ed¡ZdZ d	Z!d
dde "de!i¡dœgdœZ#da$de%d< e &¡ Z'ej ed¡Z(dZ)dZ*dZ+dZ,dZ-e &¡ Z.dd„ Z/dPddddœd d!„Z0dddd"œd#d$„Z1dQddd%d&d'œd(d)„Z2ddd*d+d,œd-d.„Z3e)fdd/d0œd1d2„Z4d3d4„ Z5d/dd5œd6d7„Z6d/dd5œd8d9„Z7d/d/d:d;œd<d=„Z8d>d+d?œd@dA„Z9dBdCœdDdE„Z:d+dCœdFdG„Z;dd:dHœdIdJ„Z<G dKdL„ dLeƒZ=d+dCœdMdN„Z>e?dOk�r<e>ƒ  dS )Ru   Polymarket BTC å›¾è¡¨æœ¬åœ°æœ�åŠ¡ã€‚

ä¸€ä¸ªè¿›ç¨‹å�šä¸‰ä»¶äº‹ï¼š
  1. é�™æ€�æ–‡ä»¶æœ�åŠ¡ï¼ˆcharts ç›®å½•ï¼Œæ›¿ä»£ python -m http.serverï¼‰
  2. å�Žå�°å¸¸é©»è®¢é˜… Chainlink RTDSï¼ˆcrypto_prices_chainlink / btc/usdï¼‰ï¼Œç§’çº§è�½ç›˜
  3. æ��ä¾› /api/cl?hours=N ç»™é¡µé�¢è¯»åŽ†å�²ï¼Œæ‰“å¼€å�³æœ‰å®Œæ•´ TWAP

ç”¨æ³•:
  python serve.py                 # é»˜è®¤ 8900ï¼Œä»£ç�† 127.0.0.1:10808
  python serve.py --port 8900 --proxy 127.0.0.1:10808
  python serve.py --no-proxy      # å¢ƒå¤–æœºå™¨ç›´è¿ž
  python serve.py --no-recorder   # é‡‡é›†å™¨ç”±ç‹¬ç«‹è¿›ç¨‹/å�•å…ƒç›‘ç®¡ï¼ˆç”Ÿäº§ç”¨ï¼‰

æ•°æ�®æ–‡ä»¶: data/chainlink_btc_YYYYMMDD.ndjsonï¼ˆæ¯�è¡Œ {"t": ç§’, "v": ä»·æ ¼}ï¼‰
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚSimpleHTTPRequestHandlerÚThreadingHTTPServer)Úparse_qsÚquoteÚurlparseÚdataz!wss://ws-live-data.polymarket.comzbtc/usdZ	subscribeÚcrypto_prices_chainlinkÚupdateZsymbol)ÚtopicÚtypeÚfilters)ÚactionZsubscriptions)NNztuple[str | None, int | None]ÚPROXYÚcacheg      @z)https://data-api.polymarket.com/v2/tradesz'https://gamma-api.polymarket.com/eventséx   é   c                  C  sD   t d r:dt d › dt d › �} tj tj | | dœ¡¡S tj ¡ S )Nr   úhttp://ú:é   )ZhttpZhttps)r   ÚurllibÚrequestZbuild_openerZProxyHandler)Zpx© r   úserve.pyÚ_openerB   s    ÿr   ç      4@ÚstrÚfloatztuple[int, bytes])ÚurlÚtimeoutÚreturnc              
   C  sÖ   t jj| ddid�}zHtƒ j||d��$}t|jƒ| ¡ fW  d   ƒ W S 1 sP0    Y  W nt t jj	y– } zt|j
ƒ| ¡ fW  Y d }~S d }~0  tyÐ } z$tdt|ƒd d… ƒ W Y d }~dS d }~0 0 d S )Nz
User-Agentzpolymarket-5m-chart/1.0)Zheaders)r!   zupstream error:r   )r   ó    )r   r   ZRequestr   ÚopenÚintÚstatusÚreadÚerrorZ	HTTPErrorÚcodeÚ	ExceptionÚlogÚrepr)r    r!   ZreqÚrÚer   r   r   Úfetch_upstreamK   s    6&r/   )ÚkindÚkeyr"   c                 C  s<   t j t| ¡}t | d¡¡ ¡ d d… d }t j ||¡S )Núutf-8é   ú.json)ÚosÚpathÚjoinÚ	CACHE_DIRÚhashlibZsha1ÚencodeZ	hexdigest)r0   r1   ÚdÚnamer   r   r   Ú_cache_pathW   s     r=   zfloat | Noneúbytes | None)r0   r1   Úttlr"   c                 C  s‚   t | |ƒ}z^|d ur2t ¡ tj |¡ |kr2W d S t|dƒ�}| ¡ W  d   ƒ W S 1 s\0    Y  W n ty|   Y d S 0 d S )NÚrb)r=   Útimer5   r6   Úgetmtimer$   r'   ÚOSError)r0   r1   r?   ÚpÚfr   r   r   Ú
cache_read]   s    
 ,rF   ÚbytesÚNone)r0   r1   Úbodyr"   c              
   C  sÐ   t | |ƒ}z†tjtj |¡dd� |d }t�P t|dƒ�}| |¡ W d   ƒ n1 sZ0    Y  t ||¡ W d   ƒ n1 s„0    Y  W n: t	yÊ } z"t
dt|ƒd d… ƒ W Y d }~n
d }~0 0 d S )NT©Úexist_okz.tmpÚwbzcache write fail:éP   )r=   r5   Úmakedirsr6   ÚdirnameÚ_cache_lockr$   ÚwriteÚreplacerC   r+   r,   )r0   r1   rI   rD   ÚtmprE   r.   r   r   r   Úcache_writeh   s    
(.rT   r%   )Ú	max_age_dr"   c              
   C  s¸   t j t¡sdS t ¡ | d  }d}t  t¡D ]„}t j t|¡}t j |¡sNq.t  |¡D ]X}| d¡shqXt j ||¡}z&t j |¡|k ršt  	|¡ |d7 }W qX t
y®   Y qX0 qXq.|S )u\   åˆ é™¤è¶…é¾„ç¼“å­˜æ–‡ä»¶ã€‚cache/ æ˜¯æ´¾ç”Ÿæ•°æ�®ï¼Œä¸Ž data/ çš„åŽŸå§‹æ•°æ�®ä¸¥æ ¼åˆ†å¼€ã€‚r   é€Q r4   r   )r5   r6   Úisdirr8   rA   Úlistdirr7   ÚendswithrB   ÚremoverC   )rU   ÚcutoffÚnr0   ÚkdÚfnrD   r   r   r   Úprune_cacheu   s&    


r_   c                  G  s$   t t ¡  d¡g| ¢R ddiŽ d S )Nz%H:%M:%SÚflushT)Úprintr   ÚnowÚstrftime)Úar   r   r   r+   Œ   s    r+   )Útsr"   c                 C  s,   t j| tjd� d¡}tj td|› d�¡S )N©Ztzú%Y%m%dZchainlink_btc_ú.ndjson©	r   Zfromtimestampr   Zutcrc   r5   r6   r7   ÚDATA_DIR©re   r;   r   r   r   Úday_path�   s    rl   c                 C  s,   t j| tjd� d¡}tj td|› d�¡S )Nrf   rg   Zokx_btc_rh   ri   rk   r   r   r   Úokx_day_path•   s    rm   zlist[list[float]])Úsince_sÚuntil_sr"   c              
     s  i ‰ | | d  }||krøt |d ƒ}tj |¡rîzªt|dd��Š}|D ]t}| ¡ }|sVqDzt |¡}t|d ƒ}W n t	y†   Y qDY n0 | d |  kr¤|d krDn qDt
|d ƒˆ |< qDW d  ƒ n1 sÎ0    Y  W n tyì   Y n0 |d7 }q‡ fd	d
„tˆ ƒD ƒS )uR   è¯» OKX æˆ�äº¤åŽ†å�²ï¼ˆrecorder.js è�½ç›˜ï¼Œæ¯«ç§’ tsï¼‰ï¼ŒæŒ‰ (t) åŽ»é‡�å�‡åº�ã€‚rV   r   r2   ©ÚencodingÚtéè  rD   Nc                   s   g | ]}|ˆ | g‘qS r   r   ©Ú.0rr   ©Úoutr   r   Ú
<listcomp>±   r#   zload_okx.<locals>.<listcomp>)rm   r5   r6   Úexistsr$   ÚstripÚjsonÚloadsr%   r*   r   rC   Úsorted)rn   ro   Údayr6   rE   Úliner-   rr   r   rv   r   Úload_okxš   s,    

 4
r€   zlist[tuple[int, float]])Úrowsr"   c              
   C  sÈ   t jtdd� t�¢ i }| D ]4\}}d|tt|dƒƒf }| t|ƒg ¡ |¡ q| 	¡ D ]J\}}t
|ddd��$}| d |¡d ¡ W d	  ƒ qZ1 sš0    Y  qZW d	  ƒ n1 sº0    Y  d	S )
u9   æŒ‰å¤©è¿½åŠ å†™ï¼›å�Œä¸€ç§’é‡�å¤�å†™å…¥ç”±è¯»ç«¯åŽ»é‡�ã€‚TrJ   z{"t":%d,"v":%s}é   rd   r2   rp   Ú
N)r5   rN   rj   Ú_write_lockr,   ÚroundÚ
setdefaultrl   ÚappendÚitemsr$   rQ   r7   )r�   Zbucketsrr   Úvr   r6   ÚlinesrE   r   r   r   Úappend_rows´   s    r‹   zsubprocess.Popen | None)r"   c                   s¦   t  d¡}|stdƒ dS ttjƒ}| r`| d r`d|d< d| d › d| d	 › �|d
< |d
 |d< tj|tj 	t
d¡g|t
d�‰ tdˆ j› d�ƒ t ‡ fdd„¡ ˆ S )uZ   æ‹‰èµ· Node é‡‡é›†å™¨ï¼ˆPython websocket-client å�ªèƒ½æ”¶åˆ°é¦–åŒ…ï¼Œå®žé™…ä¸�æŽ¨é€�ï¼‰ã€‚ÚnodezE!! node not found, recorder disabled (page will rely on live WS only)Nr   Ú1ZNODE_USE_ENV_PROXYr   r   r   Zhttps_proxyZ
http_proxyzrecorder.js)ÚenvÚcwdzrecorder started (pid ú)c                     s   ˆ   ¡ S )N)Z	terminater   ©rD   r   r   Ú<lambda>Î   r#   z start_recorder.<locals>.<lambda>)ÚshutilZwhichr+   Údictr5   ÚenvironÚ
subprocessÚPopenr6   r7   ÚROOTÚpidÚatexitÚregister)ÚproxyrŒ   rŽ   r   r‘   r   Ústart_recorderÁ   s    

r�   c               
   C  s  dd l } d}�z |  ¡ }ddi}td r>|jtd td d� |jtfi |¤Ž | t t	¡¡ t
dƒ d}g }t ¡ }| ¡ }|s†qxzt |¡}W n tyª   Y qxY n0 | d¡d	kr¼qx| d
¡pÈi }t| d¡tƒrä| d¡nd }	|	d u �rt| d¡ttfƒ�r| d¡�r|g}	|	�s"qx|	D ]F}
z*| tt|
d ƒd ƒt|
d ƒf¡ W n t�yh   Y n0 �q&t|ƒdk�sŒt ¡ | dkrx|�r¢t|ƒ | ¡  t ¡ }qxW q t�y } zDt
dt|ƒd d… d|› d�ƒ t |¡ t|d dƒ}W Y d }~qd }~0 0 qd S )Nr   é   r!   é(   r   )Zhttp_proxy_hostZhttp_proxy_portzRTDS connectedr   r   Úpayloadr
   ÚvalueZ	timestamprs   é   é   zRTDS error:r   z	retry in Úsé   )Ú	websocketZ	WebSocketr   r   ZconnectÚRTDS_URLÚsendr{   ÚdumpsÚ	SUBSCRIBEr+   rA   Zrecvr|   r*   ÚgetÚ
isinstanceÚlistr%   r   r‡   Úlenr‹   Úclearr,   ÚsleepÚmin)r¦   ZbackoffZwsÚkwÚpendingZ
last_flushÚrawÚmsgrD   r�   r-   r.   r   r   r   Úrecorder_loopÒ   sT    
,*
 
r¶   )Úhoursr"   c           	   
     s  t t ¡ ƒ}|t | d ƒ }i ‰ ||d  }||krüt|d ƒ}tj |¡ròz’t|dd��r}|D ]\}| ¡ }|srq`zt 	|¡}W n t
y–   Y q`Y n0 t |d ƒ}||kr`t|d ƒˆ |< q`W d  ƒ n1 sÒ0    Y  W n tyð   Y n0 |d7 }q,‡ fd	d
„tˆ ƒD ƒS )u9   è¯»æœ€è¿‘ N å°�æ—¶çš„ç§’çº§æ•°æ�®ï¼ŒæŒ‰ç§’åŽ»é‡�å�‡åº�ã€‚é  rV   r   r2   rp   rr   r‰   Nc                   s   g | ]}|ˆ | g‘qS r   r   rt   rv   r   r   rx     r#   zload_recent.<locals>.<listcomp>)r%   rA   rl   r5   r6   ry   r$   rz   r{   r|   r*   r   rC   r}   )	r·   rb   Ústartr~   r6   rE   r   r-   rr   r   rv   r   Úload_recent  s0    
4
rº   c                      st   e Zd Z‡ fdd„Z‡ fdd„Zddddd	œd
d„Zdddœdd„Zdddœdd„Z‡ fdd„Z‡ fdd„Z	‡  Z
S )ÚHandlerc                   s   t ƒ j|dti|¤Ž d S )NZ	directory)ÚsuperÚ__init__r˜   )Úselfrd   r²   ©Ú	__class__r   r   r½     s    zHandler.__init__c                   s&   |   dd¡ |   dd¡ tƒ  ¡  d S )NzAccess-Control-Allow-OriginÚ*zCache-Controlzno-store)Úsend_headerr¼   Úend_headers)r¾   r¿   r   r   rÃ   "  s    zHandler.end_headerséÈ   rG   r%   rH   )rI   r&   r"   c                 C  sB   |   |¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S )NúContent-Typeúapplication/jsonúContent-Length)Úsend_responserÂ   r   r®   rÃ   ÚwfilerQ   )r¾   rI   r&   r   r   r   Ú
_send_json'  s
    
zHandler._send_jsonr   r>   )Úqueryr"   c           	      C  sÎ   t |ƒ}ztt| ddg¡d ƒƒ}W n ty<   d}Y n0 |oT|tt ¡ ƒt k}t|rdd| nd }|rŽtd|ƒ}|durŽ|  	|¡ |S t
|ƒ\}}|dkr²|r²td||ƒ |dkr¾dS |  	||¡ |S )uY   é€�ç¬”æˆ�äº¤ï¼šå·²å®šåž‹ï¼ˆend < now-120sï¼‰çš„çª—å�£æ°¸ä¹…ç¼“å­˜ï¼Œå½“å‰�çª—å�£ç›´é€šã€‚Úendr   ú?Ú Ztrades_finalNrÄ   )r   r%   r   r«   Ú
ValueErrorrA   ÚCLOSED_GRACE_SÚ	UP_TRADESrF   rÊ   r/   rT   )	r¾   rË   ÚqrÌ   Zsettledr    ÚhitÚstrI   r   r   r   Úserve_trades.  s&    


zHandler.serve_tradesc           	      C  sî   t |ƒ}| d¡pdgd }t d|¡s8|  dd¡ dS d| }td	|ƒ}|d
ur`|  |¡ |S td|tƒ}|d
ur‚|  |¡ |S tt› dt	|ƒ› �ƒ\}}|dkr¨d
S |dkrÞ| 
dd¡}d|v rÒtd	||ƒ ntd||ƒ |  ||¡ |S )ua   å¸‚åœºå…ƒæ•°æ�®ï¼šå·²ç»“ç®—æ°¸ä¹…ç¼“å­˜ï¼›æœªç»“ç®—çŸ­ TTLï¼ˆé�¿å…�åˆ·æ–°æ—¶é‡�å¤�æ‰“ gammaï¼‰ã€‚ÚslugrÎ   r   z[A-Za-z0-9\-]{1,80}s   {"error":"bad slug"}i�  s   {}zslug:Z
meta_finalNZ	meta_livez?slug=rÄ   ó    r#   s   "closed":true)r   r«   ÚreÚ	fullmatchrÊ   rF   ÚMETA_LIVE_TTL_Sr/   ÚUP_GAMMAr   rR   rT   )	r¾   rË   rÒ   rÖ   r1   rÓ   rÔ   rI   Zcompactr   r   r   Ú
serve_metaD  s0    


zHandler.serve_metac              
     s�  t | jƒ}|jdkr¸t|jƒ}zt| ddg¡d ƒ}W n tyN   d}Y n0 tdt|dƒƒ}t	j
t|ƒdd	� ¡ }|  d
¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S |jdkrê|  |j¡}|d urÚd S |  dd¡ d S |jdk�r |  |j¡}|d u�rd S |  dd¡ d S |jdk�rØt|jƒ}zt| dtg¡d ƒ}W n t�yh   t}Y n0 tdt|dƒƒ}t|ƒ}t	 
||dœ¡ ¡ }|  d
¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S |jdk�rêti dœ}tj t¡�rštt t¡ƒD ]Ž}tj t|¡}	tj |	¡�s.�q
d}d}
t |	¡D ]B}z&|
tj tj |	|¡¡7 }
|d7 }W n t �y~   Y n0 �q@||
dœ|d |< �q
t	 
|¡ ¡ }|  d
¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S |jdk�rt|jƒ}t!t" "¡ ƒ}zt!t| d|g¡d ƒƒ}W n t�yB   |}Y n0 z"t!t| d|d g¡d ƒƒ}W n t�y‚   |d }Y n0 t||ƒ}t||d ƒ}t||ƒ}t#||ƒ}t	j
|dd	� ¡ }|  d
¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S |jd k�r‚t	 
d!tj t$¡�r2tt t$¡ƒng d"œ¡ ¡ }|  d
¡ |  dd¡ |  dtt|ƒƒ¡ |  ¡  | j |¡ d S t%ƒ  &¡  d S )#Nú/api/clr·   Ú2r   g       @gš™™™™™¹?g      H@)ú,r   )Z
separatorsrÄ   rÅ   rÆ   rÇ   z/api/tradesiö  zupstream unavailablez	/api/metaz/api/cache/pruneÚdaysg     Ðv@)ÚremovedZmax_age_daysz/api/cache/status)ÚdirÚkindsr   )ÚfilesrG   rã   z/api/okxÚuntilÚsincer¸   i`T  z/api/cl/statusT)Úokrä   )'r	   r6   r   rË   r   r«   rÏ   Úmaxr±   r{   r©   rº   r:   rÈ   rÂ   r   r®   rÃ   rÉ   rQ   rÕ   Z
send_errorrÜ   ÚCACHE_MAX_AGE_Dr_   r8   r5   rW   r}   rX   r7   ÚgetsizerC   r%   rA   r€   rj   r¼   Údo_GET)r¾   ÚurÒ   r·   rI   rà   r\   r    Úkr]   Úsizer^   Znow_srå   ræ   r�   r¿   r   r   rë   `  sÀ    














"



0
zHandler.do_GETc                   s>   |r|d nd}t |tƒr&d|v r&d S tƒ j|g|¢R Ž  d S )Nr   rÎ   rÝ   )r¬   r   r¼   Úlog_message)r¾   ZfmtÚargsrµ   r¿   r   r   rï   Â  s    zHandler.log_message)rÄ   )Ú__name__Ú
__module__Ú__qualname__r½   rÃ   rÊ   rÕ   rÜ   rë   rï   Ú__classcell__r   r   r¿   r   r»     s   br»   c               
   C  s"  t  ¡ } | jdtdd� | jddd� | jdtj d¡p:d	d� | jd
dd� | jdddd� |  ¡ }|js |j	r t
d|j	v r„|j	nd|j	 ƒ}|j|jpœdfatjtdd� tƒ }|rÐtd|› dtd›d�ƒ |jsÞttƒ t|j|jftƒ}td|j› d|j› dt› dt› d�	ƒ | ¡  d S )Nz--portiÄ"  )r   Údefaultz--hostz	127.0.0.1)rõ   z--proxyZHTTPS_PROXYzhttp://127.0.0.1:10808z
--no-proxyÚ
store_true)r   z--no-recorderuH   ä¸�è‡ªå�¯ recorder.jsï¼ˆäº¤ç»™å¤–éƒ¨ç›‘ç®¡ï¼Œå¦‚ç‹¬ç«‹ systemd å�•å…ƒï¼‰)r   Úhelpz://r   i�  TrJ   zcache pruned: z file(s) older than Úgr;   zserving http://r   z/  (data dir: z	, proxy: r�   )ÚargparseÚArgumentParserÚadd_argumentr%   r5   r•   r«   Ú
parse_argsZno_proxyrœ   r	   ZhostnameZportr   rN   rj   r_   r+   ré   Zno_recorderr�   r   Zhostr»   Zserve_forever)Zaprð   rD   rá   Zsrvr   r   r   ÚmainÌ  s*    ÿ&rý   Ú__main__)r   )N)@Ú__doc__Z
__future__r   rù   rš   r9   r{   r5   rØ   r“   r–   Z	threadingrA   Zurllib.errorr   Zurllib.requestr   r   Zhttp.serverr   r   Zurllib.parser   r   r	   r6   rO   ÚabspathÚ__file__r˜   r7   rj   r§   Z	CL_SYMBOLr©   rª   r   Ú__annotations__ZLockr„   r8   ré   rÑ   rÛ   rÐ   rÚ   rP   r   r/   r=   rF   rT   r_   r+   rl   rm   r€   r‹   r�   r¶   rº   r»   rý   rñ   r   r   r   r   Ú<module>   sl   ýþ		0 /
