선발투수 FIP · 팀 wRC+ 등 통계 지표를 입력하면,
포아송·정규분포 모델이 각 마켓의 기댓값(EV)을 계산합니다.
베트맨 배당의 내재 확률과 비교해 가치픽을 추천합니다.
리그 선택
한국프로야구
수동 스탯 입력 (스탯티즈)
메이저리그
StatsAPI 자동 입력
잉글리시 프리미어리그
xG 기반 모델
미국프로농구
포인트스프레드 모델
| 팀 | wRC+(시즌) | wRC+(최근5·선택) | SLG | AVG |
|---|
잉글리시 프리미어리그 xG 기반 모델
출시 예정미국프로농구 포인트스프레드 모델
출시 예정function doGet(e){
var action=e.parameter.action;
if(action==='ping')return json_({ok:true,v:2});
if(action==='getPicks')return json_({picks:readAll_()});
if(action==='getTeamDB')return json_(readTeamDB_());
return json_({ok:false,error:'unknown action'});
}
function doPost(e){
var lock=LockService.getScriptLock();
lock.waitLock(20000);
try{
var body=JSON.parse(e.postData.contents);
if(body.action==='appendPicks'||body.action==='upsertPicks'){upsertPicks_(body.picks||[],false);return json_({ok:true,v:2});}
if(body.action==='syncFinalPicks'){upsertPicks_(body.picks||[],true);return json_({ok:true,v:2});}
if(body.action==='deletePicks'){deletePicks_(body.keys||[]);return json_({ok:true,v:2});}
if(body.action==='saveTeamDB'){var t=saveTeamDB_(body.teamDB||{});return json_({ok:true,v:2,savedAt:t});}
return json_({ok:false,error:'unknown action'});
}finally{lock.releaseLock();}
}
var HEADERS=['savedAt','date','league','match','market','pick','P','fair','ev','odds','stake','score','tier','expTotal','expMargin','actHome','actAway','actH1Home','actH1Away','hFIP','aFIP','hWRC','aWRC','hBP','aBP','hGB','aGB','PF','weather','wWeight','edge','result','archived'];
function sheet_(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName('Picks');
if(!sh){ sh=ss.insertSheet('Picks'); sh.appendRow(HEADERS); }
if(sh.getMaxColumns()<HEADERS.length)sh.insertColumnsAfter(sh.getMaxColumns(),HEADERS.length-sh.getMaxColumns());
if(sh.getRange(1,HEADERS.length).getValue()!==HEADERS[HEADERS.length-1])sh.getRange(1,1,1,HEADERS.length).setValues([HEADERS]);
return sh;
}
var TEAMDB_HEADERS=['team','wrc','wrcR','slg','avg','savedAt'];
function teamDbSheet_(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName('TeamDB');
if(!sh){ sh=ss.insertSheet('TeamDB'); sh.appendRow(TEAMDB_HEADERS); }
return sh;
}
function saveTeamDB_(teamDB){
var sh=teamDbSheet_();
var savedAt=new Date().toISOString(); // 저장 시각은 서버가 찍음 — 기기 시계가 어긋나도 순서 보장
sh.clearContents();
sh.appendRow(TEAMDB_HEADERS);
Object.keys(teamDB).forEach(function(team){
var t=teamDB[team]||{};
sh.appendRow([team, t.wrc==null?'':t.wrc, t.wrcR==null?'':t.wrcR, t.slg==null?'':t.slg, t.avg==null?'':t.avg, savedAt]);
});
return savedAt;
}
function readTeamDB_(){
var sh=teamDbSheet_();
var data=sh.getDataRange().getValues();
var db={}, savedAt='';
for(var i=1;i<data.length;i++){
var row=data[i]; if(!row[0])continue;
db[row[0]]={wrc:row[1]===''?null:row[1], wrcR:row[2]===''?null:row[2], slg:row[3]===''?null:row[3], avg:row[4]===''?null:row[4]};
if(row[5])savedAt=row[5] instanceof Date?row[5].toISOString():String(row[5]);
}
return {teamDB:db, savedAt:savedAt};
}
function json_(obj){
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
// 픽 식별키: 저장시각(초 단위 정규화)|마켓|픽 — 사이트 쪽 pickKey와 동일 규칙
function keyOf_(savedAt,date,market,pick){
var s=savedAt instanceof Date?savedAt.toISOString():String(savedAt||date||'');
var t=Date.parse(s);
var norm=isNaN(t)?s:new Date(t).toISOString().slice(0,19);
return norm+'|'+market+'|'+pick;
}
// 같은 키의 픽이 있으면 그 행을 갱신, 없으면 추가. finalize=true면 아카이브 표시(모든 기기 목록에서 제외)
function upsertPicks_(picks,finalize){
var sh=sheet_();
var data=sh.getDataRange().getValues();
var rowByKey={};
for(var i=1;i<data.length;i++){rowByKey[keyOf_(data[i][0],data[i][1],data[i][4],data[i][5])]=i;}
picks.forEach(function(p){
if(finalize)p.archived='1';
var key=keyOf_(p.savedAt,p.date,p.market,p.pick);
var i=rowByKey[key];
if(i===-1)return;
if(i!==undefined){
var row=HEADERS.map(function(h,c){ var v=p[h]!==undefined?p[h]:data[i][c]; return v===undefined?'':v; });
sh.getRange(i+1,1,1,HEADERS.length).setValues([row]);
data[i]=row;
} else {
sh.appendRow(HEADERS.map(function(h){ return p[h]!==undefined?p[h]:''; }));
rowByKey[key]=-1;
}
});
}
function deletePicks_(keys){
var sh=sheet_();
var data=sh.getDataRange().getValues();
var set={}; keys.forEach(function(k){set[k]=true;});
for(var i=data.length-1;i>=1;i--){
if(set[keyOf_(data[i][0],data[i][1],data[i][4],data[i][5])])sh.deleteRow(i+1);
}
}
function readAll_(){
var sh=sheet_();
var data=sh.getDataRange().getValues();
var picks=[];
for(var i=1;i<data.length;i++){
var row=data[i]; if(!row[0]&&!row[1])continue;
var obj={}; HEADERS.forEach(function(h,idx){ obj[h]=row[idx]!==undefined?row[idx]:''; });
picks.push(obj);
}
return picks;
}
| 날짜 | 리그 | 경기 | 픽 | P | 배당 | 픽점수(/100) | 실제 홈 | 실제 원정 | 삭제 |
|---|
이 구글 스프레드시트는 KBO·MLB 베팅 EV 예측 모델의 픽 기록입니다. 각 열 의미: - market: 마켓종류 | pick: 최선픽 | P_model: 모델예측확률 | devig: 배당내재확률 | ev: 기댓값 - hFIP/aFIP: 홈·원정 선발FIP | hWRC/aWRC: 홈·원정 wRC+ | PF: 구장계수 - actHome/actAway: 최종점수 | actH1Home/actH1Away: 5회말 기준 전반점수 | result: W/L/P 아래 순서로 분석해주세요: 1. 마켓별 적중률·ROI 요약표 2. P_model 캘리브레이션 — 예측확률 구간별 실제 적중률 비교 3. FIP·wRC+ 등 입력변수와 오차의 상관관계 (과대평가·과소평가 패턴) 4. 계수 보정 제안 (fipCoefKBO·wrcFactor·sigF 등 조정 방향) 5. 마켓별 전략 개선점 (전반 마켓 포함) 6. 위 분석을 바탕으로 계수·모델 개선 로드맵을 우선순위와 함께 제시