Strategy DSL v2 (dsl.py): 40+ action primitives, 40+ market sensors, 12 comparison operators, 16 builtins, full parser. Strategy Generator (generator.py): genetic programming evolution — crossover, mutation, tournament selection, pool management. Supporting: discrepancy tracking, execution quality, hooks, feature importance, observability, parallel eval, auto-rollback, stress testing, structured observations, trajectory recording.
101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
"""
|
|
Structured Observability — compact JSONL logging for every decision.
|
|
|
|
Records:
|
|
- Every planner decision with features and diagnostics
|
|
- Every risk gate decision
|
|
- Every venue action
|
|
- Every fill/discrepancy
|
|
- Replayable audit trail
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Dict, List, Mapping, Optional
|
|
|
|
from malkhut.state import MarketWorldState
|
|
from malkhut.actions import FulfilmentAction, PlannedPolicy, RiskDecision
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class DecisionRecord:
|
|
"""One decision in the audit trail."""
|
|
ts_ns: int
|
|
symbol: str
|
|
action_kind: str
|
|
action_side: Optional[str]
|
|
action_price: Optional[float]
|
|
approved: bool
|
|
risk_reason: str
|
|
plan_latency_ns: int
|
|
policy_version: str
|
|
root_entropy: float
|
|
sims: int
|
|
features: Mapping[str, float] = field(default_factory=dict)
|
|
diagnostics: Mapping[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
class ObservabilityLogger:
|
|
"""
|
|
Compact JSONL logger for every decision.
|
|
|
|
One line per decision. Replayable audit trail.
|
|
"""
|
|
|
|
def __init__(self, log_path: str = "decisions.log") -> None:
|
|
self._log_path = log_path
|
|
self._decisions: list[DecisionRecord] = []
|
|
self._total_decisions = 0
|
|
|
|
def log_decision(
|
|
self,
|
|
state: MarketWorldState,
|
|
planned: PlannedPolicy,
|
|
decision: RiskDecision,
|
|
plan_ns: int,
|
|
features: Optional[Mapping[str, float]] = None,
|
|
) -> None:
|
|
"""Log a decision to the audit trail."""
|
|
record = DecisionRecord(
|
|
ts_ns=state.ts_ns,
|
|
symbol=state.venue.symbol,
|
|
action_kind=planned.selected_action.kind.value,
|
|
action_side=planned.selected_action.side.value if planned.selected_action.side else None,
|
|
action_price=planned.selected_action.price_ticks_from_best if planned.selected_action else None,
|
|
approved=decision.approved,
|
|
risk_reason=decision.reason,
|
|
plan_latency_ns=plan_ns,
|
|
policy_version="live",
|
|
root_entropy=planned.diagnostics.get("entropy", 0.0),
|
|
sims=planned.diagnostics.get("sims", 0),
|
|
features=features or {},
|
|
)
|
|
self._decisions.append(record)
|
|
self._total_decisions += 1
|
|
|
|
# Write to JSONL
|
|
try:
|
|
with open(self._log_path, "a") as f:
|
|
f.write(json.dumps({
|
|
"ts": record.ts_ns,
|
|
"sym": record.symbol,
|
|
"act": record.action_kind,
|
|
"side": record.action_side,
|
|
"app": record.approved,
|
|
"risk": record.risk_reason,
|
|
"lat_ns": record.plan_latency_ns,
|
|
"entropy": round(record.root_entropy, 4),
|
|
"sims": record.sims,
|
|
}, separators=(",", ":")) + "\n")
|
|
except OSError:
|
|
pass
|
|
|
|
@property
|
|
def total_decisions(self) -> int:
|
|
return self._total_decisions
|
|
|
|
def get_recent(self, n: int = 10) -> List[DecisionRecord]:
|
|
return self._decisions[-n:]
|