Upload alpha_factory/infra/factor_store.py with huggingface_hub
Browse files
alpha_factory/infra/factor_store.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Factor Store — DuckDB
|
| 3 |
Single source of truth for every alpha ever submitted.
|
| 4 |
"""
|
| 5 |
import duckdb
|
|
@@ -80,6 +80,8 @@ class FactorStore:
|
|
| 80 |
iteration: int = 1,
|
| 81 |
):
|
| 82 |
"""Insert a new alpha candidate (before BRAIN results arrive)."""
|
|
|
|
|
|
|
| 83 |
self.conn.execute("""
|
| 84 |
INSERT OR REPLACE INTO alphas (alpha_id, expression, neutralization, decay,
|
| 85 |
fields_used, operators_used, archetype, theme, anomaly_tag,
|
|
@@ -91,17 +93,24 @@ class FactorStore:
|
|
| 91 |
|
| 92 |
def update_metrics(self, alpha_id: str, metrics: BrainMetrics, fitness_score: float):
|
| 93 |
"""Update alpha with BRAIN simulation results."""
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
def update_verdict(self, alpha_id: str, verdict: Verdict, memo: str = ""):
|
| 107 |
"""Set the final verdict for an alpha."""
|
|
@@ -144,8 +153,11 @@ class FactorStore:
|
|
| 144 |
|
| 145 |
def count_consecutive_kills(self) -> int:
|
| 146 |
"""Count consecutive kills from most recent (for kill switch)."""
|
|
|
|
| 147 |
results = self.conn.execute("""
|
| 148 |
-
SELECT verdict FROM alphas
|
|
|
|
|
|
|
| 149 |
""").fetchall()
|
| 150 |
count = 0
|
| 151 |
for r in results:
|
|
|
|
| 1 |
"""
|
| 2 |
+
Factor Store — DuckDB persistence for all alphas.
|
| 3 |
Single source of truth for every alpha ever submitted.
|
| 4 |
"""
|
| 5 |
import duckdb
|
|
|
|
| 80 |
iteration: int = 1,
|
| 81 |
):
|
| 82 |
"""Insert a new alpha candidate (before BRAIN results arrive)."""
|
| 83 |
+
# NOTE: All SQL uses parameterized queries (? placeholders) to prevent injection.
|
| 84 |
+
# The expression field is user-controlled (from LLM output) but is passed as a param.
|
| 85 |
self.conn.execute("""
|
| 86 |
INSERT OR REPLACE INTO alphas (alpha_id, expression, neutralization, decay,
|
| 87 |
fields_used, operators_used, archetype, theme, anomaly_tag,
|
|
|
|
| 93 |
|
| 94 |
def update_metrics(self, alpha_id: str, metrics: BrainMetrics, fitness_score: float):
|
| 95 |
"""Update alpha with BRAIN simulation results."""
|
| 96 |
+
# Wrap in transaction for atomicity
|
| 97 |
+
self.conn.execute("BEGIN TRANSACTION")
|
| 98 |
+
try:
|
| 99 |
+
self.conn.execute("""
|
| 100 |
+
UPDATE alphas SET
|
| 101 |
+
sharpe_full = ?, sharpe_is = ?, sharpe_os = ?,
|
| 102 |
+
fitness_brain = ?, turnover = ?, returns_total = ?,
|
| 103 |
+
max_drawdown = ?, yearly_sharpe = ?, yearly_returns = ?,
|
| 104 |
+
margin_pct = ?, fitness_score = ?
|
| 105 |
+
WHERE alpha_id = ?
|
| 106 |
+
""", [metrics.sharpe_full, metrics.sharpe_is, metrics.sharpe_os,
|
| 107 |
+
metrics.fitness, metrics.turnover, metrics.returns,
|
| 108 |
+
metrics.max_drawdown, metrics.yearly_sharpe, metrics.yearly_returns,
|
| 109 |
+
metrics.margin_pct, fitness_score, alpha_id])
|
| 110 |
+
self.conn.execute("COMMIT")
|
| 111 |
+
except Exception:
|
| 112 |
+
self.conn.execute("ROLLBACK")
|
| 113 |
+
raise
|
| 114 |
|
| 115 |
def update_verdict(self, alpha_id: str, verdict: Verdict, memo: str = ""):
|
| 116 |
"""Set the final verdict for an alpha."""
|
|
|
|
| 153 |
|
| 154 |
def count_consecutive_kills(self) -> int:
|
| 155 |
"""Count consecutive kills from most recent (for kill switch)."""
|
| 156 |
+
# Use rowid as tiebreaker for deterministic ordering when timestamps are equal
|
| 157 |
results = self.conn.execute("""
|
| 158 |
+
SELECT verdict FROM alphas
|
| 159 |
+
ORDER BY submitted_at DESC, rowid DESC
|
| 160 |
+
LIMIT 50
|
| 161 |
""").fetchall()
|
| 162 |
count = 0
|
| 163 |
for r in results:
|