134 lines
3.6 KiB
Python
134 lines
3.6 KiB
Python
"""Core data contracts for BeVault."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
from dataclasses import asdict, dataclass, field
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
@dataclass
|
|
class LocalArbProblem:
|
|
asset: str
|
|
objective: str # e.g., 'min_cost', 'maximize_profit'
|
|
risk_budget: float
|
|
constraints: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def validate(self) -> None:
|
|
if not self.asset:
|
|
raise ValueError("asset is required")
|
|
if self.risk_budget < 0:
|
|
raise ValueError("risk_budget must be non-negative")
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class SharedSignals:
|
|
asset: str
|
|
price: float
|
|
latency_ms: float
|
|
timestamp: float = field(default_factory=lambda: time.time())
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def is_fresh(self, max_age_seconds: float, now: Optional[float] = None) -> bool:
|
|
now = time.time() if now is None else now
|
|
return (now - self.timestamp) <= max_age_seconds
|
|
|
|
|
|
@dataclass
|
|
class HedgeDelta:
|
|
asset: str
|
|
hedge_size: float
|
|
timestamp: float = field(default_factory=lambda: time.time())
|
|
policy_tag: str = "default"
|
|
signature: Optional[str] = None
|
|
version_vector: Dict[str, int] = field(default_factory=dict)
|
|
audit_ref: Optional[str] = None
|
|
|
|
def to_signable_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
"asset": self.asset,
|
|
"hedge_size": self.hedge_size,
|
|
"timestamp": self.timestamp,
|
|
"policy_tag": self.policy_tag,
|
|
"version_vector": self.version_vector,
|
|
"audit_ref": self.audit_ref,
|
|
}
|
|
|
|
def to_json(self) -> str:
|
|
return json.dumps(self.to_signable_dict(), sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
@dataclass
|
|
class AuditLog:
|
|
events: List[Dict[str, Any]] = field(default_factory=list)
|
|
entry_ts: float = field(default_factory=lambda: time.time())
|
|
|
|
def record(self, event_type: str, payload: Optional[Dict[str, Any]] = None) -> None:
|
|
self.events.append(
|
|
{
|
|
"event_type": event_type,
|
|
"payload": payload or {},
|
|
"timestamp": time.time(),
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class PrivacyBudget:
|
|
asset: str
|
|
remaining: float
|
|
reset_epoch: float = field(default_factory=lambda: time.time())
|
|
|
|
def consume(self, amount: float) -> bool:
|
|
if amount < 0:
|
|
raise ValueError("amount must be non-negative")
|
|
if amount > self.remaining:
|
|
return False
|
|
self.remaining -= amount
|
|
return True
|
|
|
|
|
|
__all__ = [
|
|
"LocalArbProblem",
|
|
"SharedSignals",
|
|
"HedgeDelta",
|
|
"AuditLog",
|
|
"PrivacyBudget",
|
|
# Toy DSL seed helper (MVP utility)
|
|
"generate_toy_seed",
|
|
]
|
|
|
|
|
|
def generate_toy_seed(asset: str = "AAPL", price: float = 100.0):
|
|
"""Generate a minimal toy DSL seed pair for MVP testing.
|
|
|
|
Returns a tuple of (LocalArbProblem, SharedSignals) suitable for
|
|
driving a toy cross-venue hedge scenario and deterministic delta-sync
|
|
tests without requiring real market data.
|
|
|
|
This helper is intentionally lightweight and is meant for local testing
|
|
and documentation purposes within the MVP boundaries.
|
|
"""
|
|
local = LocalArbProblem(
|
|
asset=asset,
|
|
objective="min_cost",
|
|
risk_budget=float(price) * 0.1,
|
|
constraints={},
|
|
)
|
|
|
|
signals = SharedSignals(
|
|
asset=asset,
|
|
price=price,
|
|
latency_ms=0.5,
|
|
timestamp=time.time(),
|
|
metadata={"toy_seed": True},
|
|
)
|
|
|
|
return local, signals
|