Skip to main content

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}