Spaces:
Running
Running
Download guozu/tools/simulate.py from Ethscriptions/POLS: direct link, hf CLI and curl.
- Browser
- Download file 5.48 kB
-
https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/guozu/tools/simulate.py
- Command line
-
hf download hf://spaces/Ethscriptions/POLS/guozu/tools/simulate.py
-
curl -L -o simulate.py https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/guozu/tools/simulate.py
5.48 kB
| import json,collections | |
| TEAMS={} | |
| for l in open('teams.tsv').read().strip().split('\n'): | |
| p=l.split('\t'); TEAMS[p[0]]=p[1] | |
| lines=[l.split('\t') for l in open('China.tsv').read().strip().split('\n')] | |
| COMP={'WC':'世界杯','WQ':'世预赛','WQS':'世预赛','AC':'亚洲杯','SQ':'亚预赛','EAH':'东亚杯'} | |
| MARGIN=1.07 | |
| def num(s): return int(s.replace('−','-')) | |
| CUTOFF='2026-10-07' # 数据截止日 | |
| FCOMP={'F':'友谊赛','FT':'邀请赛','FFS':'国际足联系列赛'} | |
| def make(l, comp): | |
| y,m,d,t1,t2,s1,s2,tc,loc,ch,r1a,r2a=l[:12] | |
| ch=num(ch); r1=num(r1a)-ch; r2=num(r2a)+ch | |
| cn_first = t1=='CN' | |
| opp = t2 if cn_first else t1 | |
| rc, ro = (r1,r2) if cn_first else (r2,r1) | |
| gc, go = (int(s1),int(s2)) if cn_first else (int(s2),int(s1)) | |
| # 主场判定 | |
| if loc=='' : home = 'CN' if cn_first else opp | |
| elif loc=='CN': home='CN' | |
| elif loc==opp: home=opp | |
| else: home=None | |
| ha = 100 if home=='CN' else (-100 if home==opp else 0) | |
| dr = rc - ro + ha | |
| We = 1/(10**(-dr/400)+1) | |
| pD = max(0.05, 0.28 - 0.25*(2*We-1)**2) | |
| pW = max(0.01, We*(1-pD)); pL=max(0.01,(1-We)*(1-pD)) | |
| s=pW+pD+pL; pW,pD,pL=pW/s,pD/s,pL/s | |
| oW = max(1.01, round(1/(pW*MARGIN),2)); oNW = max(1.01, round(1/((pD+pL)*MARGIN),2)); oD=round(1/(pD*MARGIN),2); oL=max(1.01,round(1/(pL*MARGIN),2)) | |
| res = 'W' if gc>go else 'D' if gc==go else 'L' | |
| return dict(date=f'{y}-{m}-{d}',opp=opp,oppName=TEAMS.get(opp,opp),comp=comp[tc],code=tc,gf=gc,ga=go,res=res,home='H' if home=='CN' else ('A' if home==opp else 'N'),eloC=rc,eloO=ro,pW=round(pW,3),pD=round(pD,3),pL=round(pL,3),oW=oW,oD=oD,oL=oL,oNW=oNW) | |
| out=[make(l,COMP) for l in lines if '2002'<=l[0] and l[7] in COMP and f'{l[0]}-{l[1]}-{l[2]}'<=CUTOFF] | |
| LAST=out[-1]['date'] | |
| # 最后一场正式比赛之后(截至 CUTOFF)的非正式比赛:单独展示,不计入正式结算 | |
| extra=[make(l,FCOMP) for l in lines if l[7] in FCOMP and LAST<f'{l[0]}-{l[1]}-{l[2]}'<=CUTOFF] | |
| bank=0;nw=0;peak=(0,'');trough=(0,'');streak=0;maxl=0 | |
| for o in out: | |
| pnl = 100*(o['oW']-1) if o['res']=='W' else -100 | |
| bank+=pnl; o['pnl']=round(pnl,2); o['bank']=round(bank,2) | |
| nw += 100*(o['oNW']-1) if o['res']!='W' else -100; o['bankNW']=round(nw,2) | |
| if bank>peak[0]: peak=(bank,o['date']) | |
| if bank<trough[0]: trough=(bank,o['date']) | |
| c=collections.Counter(o['res'] for o in out) | |
| print('matches',len(out),c,'first',out[0]['date'],'last',out[-1]['date']) | |
| print('stake',100*len(out),'final',round(bank,2),'ROI',round(bank/(100*len(out))*100,2),'%','peak',peak,'trough',trough,'reverse',round(nw,2)) | |
| print('avg odds win', round(sum(o['oW'] for o in out)/len(out),2)) | |
| yr=collections.defaultdict(float) | |
| for o in out: yr[o['date'][:4]]+=o['pnl'] | |
| print({k:round(v) for k,v in sorted(yr.items())}) | |
| best=max(out,key=lambda o:o['pnl']); print('best',best) | |
| wins=[o for o in out if o['res']=='W']; print('top wins', sorted([(o['oW'],o['date'],o['oppName'],o['gf'],o['ga']) for o in wins],reverse=True)[:6]) | |
| cmp=collections.defaultdict(lambda:[0,0]) | |
| for o in out: cmp[o['comp']][0]+=1; cmp[o['comp']][1]+=o['pnl'] | |
| print({k:(v[0],round(v[1])) for k,v in cmp.items()}) | |
| json.dump(out,open('matches.json','w'),ensure_ascii=False) | |
| # ---------- 导出给网页 ---------- | |
| ZH={'CW':'库拉索','CM':'喀麦隆','NZ':'新西兰','AU':'澳大利亚','BH':'巴林','BR':'巴西','BT':'不丹','CR':'哥斯达黎加','GU':'关岛','HK':'中国香港','ID':'印度尼西亚','IQ':'伊拉克','IR':'伊朗','JO':'约旦','JP':'日本','KG':'吉尔吉斯斯坦','KP':'朝鲜','KR':'韩国','KW':'科威特','LA':'老挝','LB':'黎巴嫩','MM':'缅甸','MV':'马尔代夫','MY':'马来西亚','OM':'阿曼','PH':'菲律宾','PS':'巴勒斯坦','QA':'卡塔尔','SA':'沙特阿拉伯','SG':'新加坡','SY':'叙利亚','TH':'泰国','TJ':'塔吉克斯坦','TR':'土耳其','UZ':'乌兹别克斯坦','VN':'越南'} | |
| import os | |
| summary=dict(n=len(out),stake=100*len(out),final=round(bank,2),W=c['W'],D=c['D'],L=c['L'], | |
| back=round(sum(o['pnl']+100 for o in out if o['res']=='W'),2),maxBank=max(o['bank'] for o in out), | |
| minBank=min(o['bank'] for o in out),minDate=min(out,key=lambda o:o['bank'])['date'], | |
| ev=round(sum(100*(o['pW']*o['oW']-1) for o in out),2),margin=MARGIN) | |
| for k,lab in [('oL','opp'),('oD','draw')]: | |
| b=0;series=[] | |
| for o in out: | |
| hit=(o['res']=='L' and k=='oL') or (o['res']=='D' and k=='oD') | |
| b+=100*(o[k]-1) if hit else -100; series.append(round(b,2)) | |
| summary[lab]=round(b,2) | |
| for o,v in zip(out,series): o['bank_'+lab]=v | |
| summary['ret']=round(summary['final']/summary['stake']*100,2) | |
| for o in out: o['oppZh']=ZH.get(o['opp'],o['oppName']) | |
| eb=0 | |
| for o in extra: | |
| o['oppZh']=ZH.get(o['opp'],o['oppName']); pnl=100*(o['oW']-1) if o['res']=='W' else -100; eb+=pnl; o['pnl']=round(pnl,2); o['bank']=round(eb,2) | |
| ec=collections.Counter(o['res'] for o in extra) | |
| summary.update(cutoff=CUTOFF,last=LAST,extraN=len(extra),extraW=ec['W'],extraD=ec['D'],extraL=ec['L'],extraPnl=round(eb,2)) | |
| ROOT=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| js='// 自动生成:tools/simulate.py —— 数据来源 eloratings.net(比赛、赛前评分),赔率为模型估算\nwindow.GZ='+json.dumps(dict(summary=summary,matches=out,extra=extra),ensure_ascii=False,separators=(',',':'))+';\n' | |
| open(os.path.join(ROOT,'data','matches.js'),'w',encoding='utf-8').write(js) | |
| print("summary",summary) | |
| for o in extra: print(o['date'],o['oppZh'],o['comp'],o['home'],o['gf'],o['ga'],o['oW'],o['pnl'],o['bank']) | |