bot_core/strategy.rs
1//! Strategy trait and engine context.
2//!
3//! A strategy is a deterministic state machine. It receives [`Event`] values,
4//! reads current engine state through [`StrategyContext`], and emits commands
5//! through that same context.
6//!
7//! # Lifecycle
8//!
9//! ```text
10//! on_start -> on_event / on_timer ... -> on_stop
11//! ```
12//!
13//! Strategies should keep exchange I/O out of their implementation. Direct HTTP
14//! calls belong in exchange adapters.
15
16use crate::commands::*;
17use crate::events::Event;
18use crate::types::*;
19use std::time::Duration;
20
21/// Timer registration result.
22pub struct TimerHandle {
23 /// Timer ID that can be passed back to [`StrategyContext::cancel_timer`].
24 pub id: TimerId,
25}
26
27/// Strategy context - what strategies use to interact with the engine.
28///
29/// This is passed to strategy methods and provides:
30/// - Command emission (place/cancel orders)
31/// - Timer management
32/// - Read-only access to market data and state
33/// - Logging
34pub trait StrategyContext {
35 // -------------------------------------------------------------------------
36 // Commands
37 // -------------------------------------------------------------------------
38
39 /// Place a new order. Returns immediately; acceptance/rejection comes via events.
40 fn place_order(&mut self, cmd: PlaceOrder);
41
42 /// Place multiple orders in a single batch. Returns immediately; acceptance/rejection comes via events.
43 fn place_orders(&mut self, cmds: Vec<PlaceOrder>);
44
45 /// Cancel an existing order by client_id.
46 fn cancel_order(&mut self, cmd: CancelOrder);
47
48 /// Cancel all open orders (optionally for an instrument).
49 fn cancel_all(&mut self, cmd: CancelAll);
50
51 /// Request strategy stop. This will trigger on_stop callback after current event processing.
52 fn stop_strategy(&mut self, strategy_id: StrategyId, reason: &str);
53
54 // -------------------------------------------------------------------------
55 // Timers
56 // -------------------------------------------------------------------------
57
58 /// Register a one-shot timer that fires after `delay`.
59 fn set_timer(&mut self, delay: Duration) -> TimerId;
60
61 /// Register a repeating timer that fires every `interval`.
62 fn set_interval(&mut self, interval: Duration) -> TimerId;
63
64 /// Cancel a previously set timer.
65 fn cancel_timer(&mut self, timer_id: TimerId);
66
67 // -------------------------------------------------------------------------
68 // Read-only state
69 // -------------------------------------------------------------------------
70
71 /// Get the current mid price for an instrument (if available from quote poller)
72 fn mid_price(&self, instrument: &InstrumentId) -> Option<Price>;
73
74 /// Get the current best bid/ask quote for an instrument
75 fn quote(&self, instrument: &InstrumentId) -> Option<Quote>;
76
77 /// Get instrument metadata
78 fn instrument_meta(&self, instrument: &InstrumentId) -> Option<&InstrumentMeta>;
79
80 /// Get this strategy's current balance for an asset
81 fn balance(&self, asset: &AssetId) -> Balance;
82
83 /// Get the current position for an instrument
84 fn position(&self, instrument: &InstrumentId) -> Position;
85
86 /// Check if exchange is Active or Halted
87 fn exchange_health(&self, exchange: &ExchangeInstance) -> ExchangeHealth;
88
89 /// Get an order by client_id (if tracked)
90 fn order(&self, client_id: &ClientOrderId) -> Option<&LiveOrder>;
91
92 // -------------------------------------------------------------------------
93 // Time
94 // -------------------------------------------------------------------------
95
96 /// Current time in milliseconds (deterministic in backtests)
97 fn now_ms(&self) -> i64;
98
99 // -------------------------------------------------------------------------
100 // Logging
101 // -------------------------------------------------------------------------
102
103 /// Log an info message
104 fn log_info(&self, msg: &str);
105
106 /// Log a warning message
107 fn log_warn(&self, msg: &str);
108
109 /// Log an error message
110 fn log_error(&self, msg: &str);
111
112 /// Log a debug message
113 fn log_debug(&self, msg: &str);
114}
115
116/// The Strategy trait - what every strategy must implement.
117///
118/// Strategies are deterministic state machines that:
119/// - Receive canonical events
120/// - Emit commands via the context
121/// - Maintain internal state
122///
123/// Strategies never call HTTP directly.
124pub trait Strategy: Send + 'static {
125 /// Unique identifier for this strategy instance
126 fn id(&self) -> &StrategyId;
127
128 /// Declare the synchronization mechanism for this strategy.
129 /// Default is Poll (incremental fills).
130 fn sync_mechanism(&self) -> SyncMechanism {
131 SyncMechanism::Poll
132 }
133
134 /// Called once when the engine starts this strategy.
135 /// Use this to initialize state, set timers, etc.
136 fn on_start(&mut self, ctx: &mut dyn StrategyContext);
137
138 /// Called for every event routed to this strategy.
139 fn on_event(&mut self, ctx: &mut dyn StrategyContext, event: &Event);
140
141 /// Called when a timer fires.
142 fn on_timer(&mut self, ctx: &mut dyn StrategyContext, timer_id: TimerId);
143
144 /// Called once when the engine is stopping this strategy.
145 /// Use this to cancel orders, log final state, etc.
146 fn on_stop(&mut self, ctx: &mut dyn StrategyContext);
147}