-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase.py
More file actions
158 lines (134 loc) · 5.76 KB
/
Copy pathdatabase.py
File metadata and controls
158 lines (134 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# ============================================================
# database.py — Local SQLite storage (zero setup required)
# ============================================================
import sqlite3
import json
from datetime import datetime
from pathlib import Path
DB_PATH = Path(__file__).parent / "data" / "edges.db"
def get_connection() -> sqlite3.Connection:
DB_PATH.parent.mkdir(exist_ok=True)
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
"""Create tables if they don't exist."""
with get_connection() as conn:
conn.executescript("""
CREATE TABLE IF NOT EXISTS edges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
found_at TEXT NOT NULL,
game TEXT NOT NULL,
sport TEXT NOT NULL,
game_time TEXT NOT NULL,
market TEXT NOT NULL,
side TEXT NOT NULL,
soft_book TEXT NOT NULL,
soft_odds INTEGER NOT NULL,
pinnacle_odds INTEGER NOT NULL,
true_prob REAL NOT NULL,
ev_percent REAL NOT NULL,
kelly_pct REAL NOT NULL
);
CREATE TABLE IF NOT EXISTS bet_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
logged_at TEXT NOT NULL,
game TEXT NOT NULL,
sport TEXT NOT NULL,
market TEXT NOT NULL,
side TEXT NOT NULL,
soft_book TEXT NOT NULL,
odds INTEGER NOT NULL,
stake REAL NOT NULL,
result TEXT DEFAULT NULL, -- 'win', 'loss', 'push', 'pending'
profit_loss REAL DEFAULT NULL,
notes TEXT DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS scan_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scanned_at TEXT NOT NULL,
sports TEXT NOT NULL,
events_fetched INTEGER NOT NULL,
edges_found INTEGER NOT NULL
);
""")
def save_edges(edges: list):
"""Save a batch of edges to the database."""
now = datetime.now().isoformat()
with get_connection() as conn:
conn.executemany("""
INSERT INTO edges
(found_at, game, sport, game_time, market, side, soft_book,
soft_odds, pinnacle_odds, true_prob, ev_percent, kelly_pct)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", [
(now, e.game, e.sport, e.game_time, e.market, e.side,
e.soft_book, e.soft_odds, e.pinnacle_odds, e.true_prob,
e.ev_percent, e.kelly_pct)
for e in edges
])
def log_scan(sports: list, events_fetched: int, edges_found: int):
with get_connection() as conn:
conn.execute("""
INSERT INTO scan_log (scanned_at, sports, events_fetched, edges_found)
VALUES (?, ?, ?, ?)
""", (datetime.now().isoformat(), json.dumps(sports), events_fetched, edges_found))
def log_bet(game: str, sport: str, market: str, side: str, soft_book: str,
odds: int, stake: float, notes: str = ""):
"""Record a bet you placed."""
with get_connection() as conn:
conn.execute("""
INSERT INTO bet_log
(logged_at, game, sport, market, side, soft_book, odds, stake, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), game, sport, market, side,
soft_book, odds, stake, notes))
print(f"✓ Bet logged: {side} @ {odds} for ${stake}")
def settle_bet(bet_id: int, result: str, profit_loss: float):
"""Update a bet with its result. result = 'win', 'loss', 'push'"""
with get_connection() as conn:
conn.execute("""
UPDATE bet_log SET result=?, profit_loss=? WHERE id=?
""", (result, profit_loss, bet_id))
print(f"✓ Bet #{bet_id} settled: {result} ${profit_loss:+.2f}")
def get_roi_summary() -> dict:
"""Calculate overall ROI from settled bets."""
with get_connection() as conn:
row = conn.execute("""
SELECT
COUNT(*) as total_bets,
SUM(CASE WHEN result != 'pending' AND result IS NOT NULL THEN 1 ELSE 0 END) as settled,
SUM(CASE WHEN result = 'win' THEN 1 ELSE 0 END) as wins,
SUM(CASE WHEN result = 'loss' THEN 1 ELSE 0 END) as losses,
SUM(stake) as total_staked,
SUM(COALESCE(profit_loss, 0)) as total_profit
FROM bet_log
""").fetchone()
if row and row["total_staked"]:
roi = (row["total_profit"] / row["total_staked"]) * 100
else:
roi = 0.0
return {
"total_bets": row["total_bets"] or 0,
"settled": row["settled"] or 0,
"wins": row["wins"] or 0,
"losses": row["losses"] or 0,
"total_staked": row["total_staked"] or 0,
"total_profit": row["total_profit"] or 0,
"roi_pct": round(roi, 2),
}
def get_pending_bets() -> list:
"""Get all unsettled bets."""
with get_connection() as conn:
rows = conn.execute("""
SELECT * FROM bet_log WHERE result IS NULL OR result = 'pending'
ORDER BY logged_at DESC
""").fetchall()
return [dict(r) for r in rows]
def get_recent_edges(limit: int = 50) -> list:
"""Get the most recently found edges."""
with get_connection() as conn:
rows = conn.execute("""
SELECT * FROM edges ORDER BY found_at DESC, ev_percent DESC LIMIT ?
""", (limit,)).fetchall()
return [dict(r) for r in rows]