from __future__ import annotations import math from statistics import fmean from typing import Any def number(value: Any) -> float | None: try: result = float(value) return result if math.isfinite(result) else None except (TypeError, ValueError): return None def change(current: float | None, previous: float | None) -> float | None: if current is None or previous in (None, 0): return None return (current / previous - 1) * 100 def mean(values: list[float | None]) -> float | None: valid = [value for value in values if value is not None] return fmean(valid) if valid else None def ratio(numerator: float | None, denominator: float | None) -> float | None: if numerator is None or denominator in (None, 0): return None return numerator / denominator def rsi(closes: list[float], period: int = 6) -> float | None: if len(closes) <= period: return None differences = [closes[index] - closes[index - 1] for index in range(1, len(closes))] recent = differences[-period:] gains = sum(max(value, 0) for value in recent) / period losses = sum(max(-value, 0) for value in recent) / period if losses == 0: return 100.0 if gains > 0 else 50.0 return 100 - 100 / (1 + gains / losses) def ema(values: list[float], period: int) -> list[float]: if not values: return [] alpha = 2 / (period + 1) result = [values[0]] for value in values[1:]: result.append(value * alpha + result[-1] * (1 - alpha)) return result def macd(values: list[float]) -> tuple[list[float], list[float]]: fast = ema(values, 12) slow = ema(values, 26) difference = [left - right for left, right in zip(fast, slow, strict=True)] return difference, ema(difference, 9) def weekly_series(rows: list[dict[str, Any]]) -> tuple[list[float], list[float]]: weeks: dict[str, dict[str, float]] = {} for row in rows: date = str(row.get("trade_date") or "") if len(date) != 10: continue from datetime import date as date_type parsed = date_type.fromisoformat(date) key = f"{parsed.isocalendar().year}-{parsed.isocalendar().week:02d}" weeks.setdefault(key, {"close": 0.0, "amount": 0.0}) close = number(row.get("close")) amount = number(row.get("amount")) if close is not None: weeks[key]["close"] = close if amount is not None: weeks[key]["amount"] += amount values = list(weeks.values()) return [item["close"] for item in values], [item["amount"] for item in values] def percentile_map(rows: list[dict[str, Any]], field: str, direction: str) -> dict[str, float]: valid = [row for row in rows if number(row.get(field)) is not None] ordered = sorted( valid, key=lambda row: ( number(row[field]) if direction == "asc" else -float(number(row[field]) or 0), str(row["identifier"]), ), ) if len(ordered) == 1: return {str(ordered[0]["identifier"]): 1.0} return { str(row["identifier"]): 1 - index / (len(ordered) - 1) for index, row in enumerate(ordered) } def pearson(left: list[float], right: list[float]) -> float: if len(left) < 3 or len(left) != len(right): return 0.0 left_mean = fmean(left) right_mean = fmean(right) numerator = sum( (first - left_mean) * (second - right_mean) for first, second in zip(left, right, strict=True) ) left_scale = math.sqrt(sum((value - left_mean) ** 2 for value in left)) right_scale = math.sqrt(sum((value - right_mean) ** 2 for value in right)) return numerator / (left_scale * right_scale) if left_scale and right_scale else 0.0 def rounded(value: float | None, digits: int = 4) -> float | None: return round(value, digits) if value is not None else None def calculate_earnings_quality(bars: list[dict[str, Any]], announcement_date: str) -> bool | None: index = next( ( offset for offset, row in enumerate(bars) if str(row.get("trade_date") or "") == announcement_date ), -1, ) if index < 0: return None prior = [ number(row.get("vol")) for row in bars[max(0, index - 5) : index] if number(row.get("vol")) is not None ] baseline = mean(prior) current = bars[index] volume_ratio = ratio(number(current.get("vol")), baseline) bad = ( number(current.get("close")) is not None and number(current.get("open")) is not None and float(number(current["close"]) or 0) < float(number(current["open"]) or 0) and float(number(current.get("pct_chg")) or 0) < 0 and volume_ratio is not None and volume_ratio >= 1.8 ) return not bad