use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
pub name: String,
pub version: String,
pub status: String,
pub ts: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Health {
pub status: String,
#[serde(default)]
pub components: BTreeMap<String, ComponentHealth>,
#[serde(default)]
pub dependencies: BTreeMap<String, String>,
#[serde(default)]
pub circuit_breakers: BTreeMap<String, String>,
#[serde(default)]
pub risk: RiskSummary,
#[serde(default)]
pub recovery: Option<RecoveryStatus>,
#[serde(default)]
pub ws_connections: u64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct RecoveryStatus {
pub status: Option<String>,
pub source: Option<String>,
pub durable: bool,
pub journal_path: Option<String>,
pub decisions_recovered: Option<u32>,
pub fills_recovered: Option<u32>,
pub rejections_recovered: Option<u32>,
pub positions_recovered: Option<u32>,
pub last_decision_at: Option<String>,
pub current_decisions: Option<u32>,
pub current_fills: Option<u32>,
pub current_rejections: Option<u32>,
pub current_positions: Option<u32>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HyperliquidStatus {
pub enabled: bool,
pub exchange: Option<String>,
pub endpoint: Option<String>,
pub coins: Option<u32>,
pub mids: BTreeMap<String, f64>,
pub secrets_required: Option<bool>,
pub reason: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HyperliquidAccount {
pub schema_version: String,
pub exchange: String,
pub user: String,
pub as_of: Option<String>,
pub account_value: Option<f64>,
pub margin_used: Option<f64>,
pub withdrawable: Option<f64>,
pub positions: Vec<HyperliquidAccountPosition>,
pub open_orders: Vec<Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HyperliquidAccountPosition {
pub symbol: String,
pub side: String,
pub quantity: f64,
pub entry_price: f64,
pub position_value: f64,
pub unrealized_pnl: f64,
pub margin_used: f64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HyperliquidReconciliation {
pub schema_version: String,
pub exchange: String,
pub status: String,
pub risk_increasing_allowed: bool,
pub reason: String,
pub as_of: Option<String>,
pub drifts: Vec<HyperliquidReconciliationDrift>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HyperliquidReconciliationDrift {
pub code: String,
pub severity: String,
pub symbol: Option<String>,
pub reason: String,
pub local_quantity: Option<f64>,
pub exchange_quantity: Option<f64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct MarketQuote {
pub symbol: String,
pub price: f64,
pub source: String,
pub as_of: Option<String>,
pub mode: Option<String>,
pub live: bool,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LivePreflight {
pub schema_version: String,
pub exchange: String,
pub mode: String,
pub ready: bool,
pub live_mode: String,
pub controls_ready: bool,
pub checks: Vec<LivePreflightCheck>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LivePreflightCheck {
pub name: String,
pub status: String,
pub note: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCertification {
pub schema_version: String,
pub mode: String,
pub passed: bool,
pub live_start_certified: bool,
pub summary: BTreeMap<String, Value>,
pub drills: Vec<LiveCertificationDrill>,
pub evidence_requirements: Vec<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCertificationDrill {
pub name: String,
pub status: String,
pub note: String,
pub evidence: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveEvidence {
pub schema_version: String,
pub generated_at: Option<String>,
pub mode: String,
pub live_mode: String,
pub ready: bool,
pub risk_increasing_allowed: bool,
pub operator_context: OperatorContext,
pub summary: BTreeMap<String, Value>,
pub artifacts: Vec<LiveEvidenceArtifact>,
pub canary_rule: BTreeMap<String, Value>,
pub privacy: BTreeMap<String, Value>,
pub evidence_hash: String,
pub signature: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveEvidenceArtifact {
pub name: String,
pub schema_version: String,
pub status: String,
pub hash: String,
pub included: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCanaryPolicy {
pub schema_version: String,
pub policy_version: String,
pub generated_at: Option<String>,
pub mode: String,
pub summary: LiveCanaryPolicySummary,
pub policy: BTreeMap<String, Value>,
pub phases: Vec<LiveCanaryPolicyPhase>,
pub recommendation: LiveCanaryRecommendation,
pub operator_context: OperatorContext,
pub request: Option<BTreeMap<String, Value>>,
pub privacy: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)] #[serde(default)]
pub struct LiveCanaryPolicySummary {
pub ready_for_canary: bool,
pub policy_armed: bool,
pub live_order_attempted: bool,
pub live_order_accepted: bool,
pub receipts_accepted: u64,
pub exchange_evidence_attached: bool,
pub publishable_canary_evidence: bool,
pub refusal_evidence_qualified: bool,
pub qualified: bool,
pub next_step: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCanaryPolicyPhase {
pub name: String,
pub status: String,
pub detail: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCanaryRecommendation {
pub action: String,
pub risk_direction: String,
pub reason: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)] #[serde(default)]
pub struct RuntimeParity {
pub schema_version: String,
pub available: bool,
pub ok: bool,
pub mode: String,
pub source: Option<String>,
pub generated_at: Option<String>,
pub cycles_requested: u64,
pub cycles_run: u64,
pub paper_only: bool,
pub places_live_orders: bool,
pub paper: RuntimeParityPaper,
pub live_shadow: RuntimeParityLiveShadow,
pub feedback: RuntimeParityFeedback,
pub certification: LiveCertification,
pub checks: BTreeMap<String, Value>,
pub claim_boundary: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct RuntimeParityPaper {
pub decisions: u64,
pub fills: u64,
pub rejections: u64,
pub open_positions: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct RuntimeParityLiveShadow {
pub mode: String,
pub accepted: u64,
pub refused: u64,
pub adapter_orders_placed: u64,
pub records: Vec<Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct RuntimeParityFeedback {
pub schema_version: String,
pub cycles: u64,
pub sample_size: u64,
pub fills: u64,
pub rejections: u64,
pub rejection_rate: f64,
pub by_rejection_reason: BTreeMap<String, u64>,
pub by_rejection_symbol: BTreeMap<String, u64>,
pub items: Vec<Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveExecutionReceipts {
pub schema_version: String,
pub generated_at: Option<String>,
pub mode: String,
pub operator_context: OperatorContext,
pub summary: BTreeMap<String, Value>,
pub receipts: Vec<LiveExecutionReceipt>,
pub privacy: BTreeMap<String, Value>,
pub receipts_hash: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveExecutionReceipt {
pub schema_version: String,
pub accepted: bool,
pub status: String,
pub reason: String,
pub as_of: Option<f64>,
pub request: BTreeMap<String, Value>,
pub request_hash: String,
pub operator_context_hash: Option<String>,
pub trace_hash: Option<String>,
pub idempotency_hash: Option<String>,
pub venue_ack_hash: Option<String>,
pub receipt_hash: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpit {
pub schema_version: String,
pub generated_at: Option<String>,
pub mode: String,
pub live_mode: String,
pub ready: bool,
pub controls_ready: bool,
pub risk_increasing_allowed: bool,
pub next_action: String,
pub operator_context: OperatorContext,
pub preflight: LiveCockpitPreflight,
pub immune: LiveCockpitImmune,
pub reconciliation: LiveCockpitReconciliation,
pub certification: LiveCockpitCertification,
pub heartbeat: LiveCockpitHeartbeat,
pub live_records: LiveCockpitRecords,
pub operator_actions: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct OperatorContext {
pub schema_version: Option<String>,
pub operator_id: String,
pub handle: String,
pub role: String,
pub scope: String,
pub source: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitPreflight {
pub schema_version: String,
pub ready: bool,
pub live_mode: String,
pub controls_ready: bool,
pub summary: BTreeMap<String, Value>,
pub failed_checks: Vec<LivePreflightCheck>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitImmune {
pub schema_version: String,
pub risk_increasing_allowed: bool,
pub summary: BTreeMap<String, Value>,
pub open_breakers: Vec<ImmuneBreaker>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitReconciliation {
pub schema_version: String,
pub status: String,
pub risk_increasing_allowed: bool,
pub reason: String,
pub drifts: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitCertification {
pub schema_version: String,
pub mode: String,
pub passed: bool,
pub live_start_certified: bool,
pub summary: BTreeMap<String, Value>,
pub failed_drills: Vec<LiveCertificationDrill>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitHeartbeat {
pub configured: bool,
pub expired: bool,
pub last_heartbeat_at: Option<f64>,
pub timeout_s: Option<f64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveCockpitRecords {
pub total: u64,
pub accepted: u64,
pub refused: u64,
pub exchange_error: u64,
pub recent: Vec<Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ImmuneReport {
pub schema_version: String,
pub generated_at: Option<String>,
pub mode: String,
pub risk_increasing_allowed: bool,
pub summary: BTreeMap<String, Value>,
pub breakers: Vec<ImmuneBreaker>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ImmuneBreaker {
pub name: String,
pub status: String,
pub blocks_risk: bool,
pub severity: String,
pub reason: String,
pub evidence: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct LiveControlResponse {
pub ok: bool,
pub state: Option<String>,
pub reason: Option<String>,
pub orders: Vec<Value>,
pub operator_context: Option<OperatorContext>,
pub action: Option<String>,
pub risk_direction: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentHealth {
pub status: String,
pub last_seen: Option<String>,
#[serde(default)]
pub age_s: Option<f64>,
}
impl ComponentHealth {
#[must_use]
pub fn is_healthy(&self) -> bool {
self.status == "healthy"
}
#[must_use]
pub fn is_dead(&self) -> bool {
self.status == "dead"
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RiskSummary {
pub equity: Option<f64>,
pub drawdown_pct: Option<f64>,
#[serde(default)]
pub kill_all: bool,
}
impl Health {
#[must_use]
pub fn is_ok(&self) -> bool {
self.status == "ok"
}
#[must_use]
pub fn component_counts(&self) -> ComponentCounts {
let mut c = ComponentCounts::default();
for comp in self.components.values() {
match comp.status.as_str() {
"healthy" => c.healthy += 1,
"stale" => c.stale += 1,
"dead" => c.dead += 1,
_ => c.unknown += 1,
}
}
c
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct ComponentCounts {
pub healthy: u32,
pub stale: u32,
pub dead: u32,
pub unknown: u32,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Position {
#[serde(alias = "coin")]
pub symbol: String,
#[serde(alias = "direction")]
pub side: String,
#[serde(alias = "size_coins")]
pub size: f64,
#[serde(alias = "entry_price")]
pub entry: f64,
pub mark: Option<f64>,
pub unrealized_pnl: Option<f64>,
pub unrealized_r: Option<f64>,
pub stop: Option<f64>,
pub target: Option<f64>,
pub lens_id: Option<String>,
pub age_s: Option<f64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Positions {
#[serde(alias = "positions", default)]
pub items: Vec<Position>,
#[serde(default)]
pub account_value: Option<f64>,
#[serde(default)]
pub total_unrealized_pnl: Option<f64>,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Risk {
pub account_value: Option<f64>,
pub drawdown_pct: Option<f64>,
pub daily_pnl_usd: Option<f64>,
pub daily_loss_usd: Option<f64>,
pub peak_equity: Option<f64>,
pub peak_equity_30d: Option<f64>,
pub open_count: Option<u32>,
pub halted: bool,
pub global_halt: bool,
pub stop_failure_halt: bool,
pub capital_floor_hit: bool,
pub halt_reason: Option<String>,
pub halt_until: Option<String>,
pub updated_at: Option<String>,
pub daily_loss_since: Option<String>,
pub last_drawdown_alert_pct: Option<f64>,
pub per_runner: BTreeMap<String, Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl Risk {
#[must_use]
pub fn is_halted(&self) -> bool {
self.halted || self.global_halt || self.stop_failure_halt
}
#[must_use]
pub fn daily_loss_pct(&self) -> Option<f64> {
let loss = self.daily_loss_usd?;
let peak = self.peak_equity.or(self.peak_equity_30d)?;
if peak <= 0.0 {
return None;
}
Some((loss / peak) * 100.0)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Regime {
pub regime: Option<String>,
pub confidence: Option<f64>,
pub coin: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Brief {
pub timestamp: Option<String>,
pub fear_greed: Option<i64>,
pub open_positions: Option<u32>,
pub positions: Vec<Position>,
pub recent_signals: Vec<Value>,
pub approaching: Vec<Value>,
pub last_cycle: Value,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl Brief {
#[must_use]
pub fn has_content(&self) -> bool {
self.fear_greed.is_some()
|| self.open_positions.is_some_and(|n| n > 0)
|| !self.positions.is_empty()
|| !self.recent_signals.is_empty()
|| !self.approaching.is_empty()
|| !self.last_cycle.is_null()
&& self.last_cycle.as_object().is_some_and(|o| !o.is_empty())
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct EvaluationLayer {
pub layer: String,
pub passed: bool,
pub value: Value,
pub detail: String,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Evaluation {
pub coin: Option<String>,
pub price: Option<f64>,
pub consensus: Option<i64>,
pub conviction: Option<f64>,
pub direction: Option<String>,
pub regime: Option<String>,
pub layers: Vec<EvaluationLayer>,
pub data_fresh: Option<bool>,
pub timestamp: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl Evaluation {
#[must_use]
pub fn verdict(&self) -> &'static str {
if self.layers.iter().any(|l| !l.passed) {
"REJECT"
} else if self
.direction
.as_deref()
.is_some_and(|d| d.eq_ignore_ascii_case("LONG") || d.eq_ignore_ascii_case("SHORT"))
{
"PASS"
} else {
"HOLD"
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct PulseEvent {
pub kind: Option<String>,
pub coin: Option<String>,
pub message: Option<String>,
pub ts: Option<String>,
pub severity: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Pulse {
#[serde(alias = "pulse", alias = "events", default)]
pub items: Vec<PulseEvent>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Approaching {
pub coin: String,
pub direction: Option<String>,
pub distance_to_gate: Option<f64>,
pub gate: Option<String>,
pub ts: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ApproachingFeed {
#[serde(alias = "approaching", alias = "items", default)]
pub items: Vec<Approaching>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Rejection {
pub coin: Option<String>,
pub direction: Option<String>,
pub stage: Option<String>,
pub reason: Option<String>,
pub ts: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RejectionsFeed {
#[serde(alias = "rejections", alias = "items", default)]
pub items: Vec<Rejection>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct V2Confidence {
pub score: Option<f64>,
pub level: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct V2Market {
pub regime: Option<String>,
pub health: Option<f64>,
pub signal: Option<String>,
pub prediction: Option<String>,
pub fear_greed: Option<i64>,
pub coins_tradeable: Option<u32>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct V2Positions {
pub open: Option<u32>,
pub unrealized_pnl: Option<f64>,
pub equity: Option<f64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct V2Today {
pub trades: Option<u32>,
pub wins: Option<u32>,
pub pnl: Option<f64>,
pub streak: Option<i32>,
pub sizing_mult: Option<f64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct V2Status {
pub confidence: V2Confidence,
pub market: V2Market,
pub positions: V2Positions,
pub today: V2Today,
pub approaching: Vec<Value>,
pub blind_spots: Vec<Value>,
pub alert: Option<Value>,
pub recovery: Option<RecoveryStatus>,
pub ts: Option<String>,
pub hl_rate: Option<HlRate>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct HlRate {
pub used: u32,
pub cap: u32,
}
impl V2Status {
#[must_use]
pub fn regime(&self) -> Option<&str> {
self.market.regime.as_deref()
}
#[must_use]
pub fn engine_confidence(&self) -> Option<f64> {
self.confidence.score
}
#[must_use]
pub fn confidence_level(&self) -> Option<&str> {
self.confidence.level.as_deref()
}
#[must_use]
pub fn equity(&self) -> Option<f64> {
self.positions.equity
}
#[must_use]
pub fn open(&self) -> Option<u32> {
self.positions.open
}
#[must_use]
pub fn unrealized_pnl(&self) -> Option<f64> {
self.positions.unrealized_pnl
}
#[must_use]
#[allow(clippy::unused_self)]
pub fn drawdown_pct(&self) -> Option<f64> {
None
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct OperatorEventsAccepted {
pub accepted: u32,
pub snapshot: zero_operator_state::Snapshot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ExecuteSide {
Buy,
Sell,
}
impl ExecuteSide {
#[must_use]
pub const fn as_wire(self) -> &'static str {
match self {
Self::Buy => "buy",
Self::Sell => "sell",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecuteRequest {
pub coin: String,
pub side: ExecuteSide,
pub size: f64,
pub idempotency_key: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecuteResponse {
pub accepted: bool,
#[serde(default)]
pub simulated: bool,
#[serde(default)]
pub fill_id: Option<String>,
#[serde(default)]
pub coin: Option<String>,
#[serde(default)]
pub side: Option<ExecuteSide>,
#[serde(default)]
pub size: Option<f64>,
#[serde(default)]
pub reason: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutoToggleRequest {
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutoToggleResponse {
pub state: AutoState,
#[serde(default)]
pub simulated: bool,
#[serde(default)]
pub reason: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AutoState {
On,
Off,
}
impl AutoState {
#[must_use]
pub const fn as_wire(self) -> &'static str {
match self {
Self::On => "on",
Self::Off => "off",
}
}
}
#[cfg(test)]
mod wire_compat_tests {
use super::*;
#[test]
fn positions_bus_file_shape_parses_with_aliases() {
let raw = r#"{
"updated_at": "2026-04-22T12:19:29.563466+00:00",
"positions": [
{
"coin": "TRX",
"direction": "LONG",
"entry_price": 0.33444,
"size_coins": 149.0,
"size_usd": 49.83156,
"stop_loss_pct": 0.025,
"id": "TRX_LONG_1776857828",
"strategy": "production",
"lens_id": "lens_flow"
},
{
"coin": "BTC",
"direction": "SHORT",
"entry_price": 63450.0,
"size_coins": 0.0012,
"size_usd": 76.14
}
]
}"#;
let parsed: Positions = serde_json::from_str(raw).expect("engine shape must parse");
assert_eq!(parsed.items.len(), 2);
assert_eq!(parsed.items[0].symbol, "TRX");
assert_eq!(parsed.items[0].side, "LONG");
assert!((parsed.items[0].size - 149.0).abs() < f64::EPSILON);
assert!((parsed.items[0].entry - 0.33444).abs() < f64::EPSILON);
assert_eq!(parsed.items[0].lens_id.as_deref(), Some("lens_flow"));
assert!(parsed.items[0].extra.contains_key("size_usd"));
}
#[test]
fn risk_bus_file_shape_parses() {
let raw = r#"{
"account_value": 581.49647,
"updated_at": "2026-04-22T12:19:29.564814+00:00",
"daily_pnl_usd": 0.0,
"daily_loss_usd": 0.0,
"global_halt": false,
"halted": false,
"drawdown_pct": 9.24,
"peak_equity": 613.450419,
"peak_equity_30d": 640.7,
"open_count": 2
}"#;
let parsed: Risk = serde_json::from_str(raw).expect("risk shape must parse");
assert_eq!(parsed.account_value, Some(581.49647));
assert_eq!(parsed.drawdown_pct, Some(9.24));
assert_eq!(parsed.open_count, Some(2));
assert!(!parsed.halted);
}
}