Metrics
All checks were successful
Build and Deploy MoneyMaker / build-and-deploy (push) Successful in 12m22s
All checks were successful
Build and Deploy MoneyMaker / build-and-deploy (push) Successful in 12m22s
This commit is contained in:
46
analyze.py
46
analyze.py
@@ -29,6 +29,7 @@ import math
|
||||
import sqlite3
|
||||
import logging
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# Silence transformer noise before importing
|
||||
@@ -78,6 +79,37 @@ MIN_COVERAGE_SPREAD = 0.0 # disabled: signal_score naturally zeros out single-
|
||||
FINBERT_MIN_CONF = 0.70 # drop neutral articles below this FinBERT confidence
|
||||
ALERT_THRESHOLD = 0.35 # signal_score > this → alert
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Claude metrics
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
METRICS_FILE = Path(__file__).parent / "metrics.json"
|
||||
|
||||
# Pricing: Claude Haiku 4.5 — https://www.anthropic.com/pricing
|
||||
_PRICE_INPUT_PER_TOKEN = 0.80 / 1_000_000 # $0.80 per MTok
|
||||
_PRICE_OUTPUT_PER_TOKEN = 4.00 / 1_000_000 # $4.00 per MTok
|
||||
|
||||
|
||||
def calc_cost(input_tokens: int, output_tokens: int) -> float:
|
||||
return round(input_tokens * _PRICE_INPUT_PER_TOKEN + output_tokens * _PRICE_OUTPUT_PER_TOKEN, 6)
|
||||
|
||||
|
||||
def update_metrics(input_tokens: int, output_tokens: int) -> None:
|
||||
"""Accumulate Claude token usage into metrics.json."""
|
||||
cost = calc_cost(input_tokens, output_tokens)
|
||||
data: dict = {}
|
||||
if METRICS_FILE.exists():
|
||||
try:
|
||||
data = json.loads(METRICS_FILE.read_text())
|
||||
except Exception:
|
||||
pass
|
||||
data["total_calls"] = data.get("total_calls", 0) + 1
|
||||
data["total_input_tokens"] = data.get("total_input_tokens", 0) + input_tokens
|
||||
data["total_output_tokens"] = data.get("total_output_tokens", 0) + output_tokens
|
||||
data["total_cost_usd"] = round(data.get("total_cost_usd", 0.0) + cost, 6)
|
||||
data["last_updated"] = datetime.now().isoformat(timespec="seconds")
|
||||
METRICS_FILE.write_text(json.dumps(data, indent=2))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Model loading
|
||||
@@ -195,16 +227,16 @@ def coverage_spread_score(row) -> float:
|
||||
# Claude structured extraction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def claude_extract(title: str, text: str, tickers: list[str]) -> dict:
|
||||
def claude_extract(title: str, text: str, tickers: list[str]) -> tuple[dict, int, int]:
|
||||
"""
|
||||
Use Claude Haiku to extract structured financial signal.
|
||||
Returns {"confirmed_tickers", "magnitude", "timeframe", "reasoning"}.
|
||||
Returns ({"confirmed_tickers", "magnitude", "timeframe", "reasoning"}, input_tokens, output_tokens).
|
||||
"""
|
||||
import anthropic
|
||||
|
||||
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
||||
if not api_key:
|
||||
return {"confirmed_tickers": tickers, "magnitude": 5, "timeframe": "days", "reasoning": "(no API key)"}
|
||||
return {"confirmed_tickers": tickers, "magnitude": 5, "timeframe": "days", "reasoning": "(no API key)"}, 0, 0
|
||||
|
||||
client = anthropic.Anthropic(api_key=api_key)
|
||||
ticker_ctx = "\n".join(
|
||||
@@ -245,10 +277,10 @@ Fields:
|
||||
raw = msg.content[0].text.strip()
|
||||
raw = re.sub(r"^```(?:json)?\n?", "", raw)
|
||||
raw = re.sub(r"\n?```$", "", raw)
|
||||
return json.loads(raw)
|
||||
return json.loads(raw), msg.usage.input_tokens, msg.usage.output_tokens
|
||||
except Exception as e:
|
||||
print(f" [warn] Claude failed: {e}")
|
||||
return {"confirmed_tickers": tickers, "magnitude": 5, "timeframe": "days", "reasoning": str(e)[:120]}
|
||||
return {"confirmed_tickers": tickers, "magnitude": 5, "timeframe": "days", "reasoning": str(e)[:120]}, 0, 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -476,7 +508,9 @@ def analyze_articles(
|
||||
claude_data: dict = {}
|
||||
if use_claude and not dry_run and os.environ.get("ANTHROPIC_API_KEY"):
|
||||
print(f" [claude] {slug[:50]}")
|
||||
claude_data = claude_extract(title, full_text, list(matches.keys()))
|
||||
claude_data, _in_tok, _out_tok = claude_extract(title, full_text, list(matches.keys()))
|
||||
if _in_tok:
|
||||
update_metrics(_in_tok, _out_tok)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Phase 6 — yfinance momentum + scoring
|
||||
|
||||
Reference in New Issue
Block a user