pub struct BotHandle { /* private fields */ }Expand description
Cheap cloneable handle into a running Bot.
See the module docs for the host-side contract.
§Example
let config = BotConfig::builder()
.name("demo")
.symbol("BTCUSDT")
.without_signal_handler()
.build()?;
let bot = Bot::new(config, exchange, brains)?;
let handle: BotHandle = bot.handle();
// Subscribe to non-Hold decisions before the bot starts.
let mut signals = handle.subscribe_signals();
// Drive the bot from one task, observe from another.
let task = tokio::spawn(async move { bot.run_until_shutdown().await });
tokio::spawn({
let handle = handle.clone();
async move {
while let Ok(sig) = signals.recv().await {
tracing::info!(?sig, "saw a signal");
}
handle.shutdown();
}
});
task.await??;Implementations§
Source§impl BotHandle
impl BotHandle
Sourcepub async fn tracked_orders(&self) -> Vec<TrackedOrder>
pub async fn tracked_orders(&self) -> Vec<TrackedOrder>
Snapshot of the resting orders the framework is currently tracking.
Empty unless order tracking is wired via
Bot::with_order_tracking and the
adapter advertises
Capability::OrderTracking.
Sourcepub fn is_shutting_down(&self) -> bool
pub fn is_shutting_down(&self) -> bool
Has shutdown been triggered?
Sourcepub async fn await_shutdown(&self)
pub async fn await_shutdown(&self)
Resolves once shutdown has been triggered by anyone (signal,
shutdown() call on this or any other handle clone, or programmatic
supervisor cancellation).
Sourcepub async fn record_trade_outcome(
&self,
symbol: &Symbol,
gross_pnl: f64,
fee: f64,
)
pub async fn record_trade_outcome( &self, symbol: &Symbol, gross_pnl: f64, fee: f64, )
Feed a realised trade outcome into the per-symbol risk state.
Called by the host (or a brain) when a position closes. Updates
the symbol’s SessionPnl and records a win/loss on the
CircuitBreaker based on the net PnL.
Non-finite gross_pnl / fee values are rejected (logged at
error level, risk state unchanged) — a NaN would otherwise make
the accumulated PnL NaN and permanently disable the loss-limit
gate.
Phase 2b does not automate this from the fill stream — that’s
FillRoutingService territory in Phase 2c.
Sourcepub async fn position(&self, symbol: &Symbol) -> Position
pub async fn position(&self, symbol: &Symbol) -> Position
Read the current cached position for a symbol, or Position::FLAT
if the symbol isn’t tracked.
Sourcepub async fn set_position(&self, symbol: &Symbol, position: Position)
pub async fn set_position(&self, symbol: &Symbol, position: Position)
Overwrite the cached position for a symbol. Typically called by the
host’s fill-handling code; Phase 2c’s FillRoutingService will do
this automatically.
Sourcepub fn subscribe_signals(&self) -> Receiver<Signal>
pub fn subscribe_signals(&self) -> Receiver<Signal>
Subscribe to the bot’s signal stream.
The ExecutionService
publishes a Signal on every non-Hold decision a brain emits,
before the risk gates run. Subscribers see the strategic intent;
whether each signal was acted on is observable from order logs and
metrics.
The underlying channel is tokio::sync::broadcast, so slow
subscribers will see RecvError::Lagged(n) if they fall behind.
§Subscriber lifetime
Because BotHandle keeps a Sender clone alive, the channel
does not close when the bot exits. A subscriber that loops on
recv() will block forever after shutdown unless it also watches
a cancellation signal:
let mut rx = handle.subscribe_signals();
let shutdown = host.shutdown_token();
loop {
tokio::select! {
_ = shutdown.cancelled() => break,
r = rx.recv() => match r {
Ok(sig) => handle_signal(sig),
Err(_) => break,
},
}
}Sourcepub fn signal_subscriber_count(&self) -> usize
pub fn signal_subscriber_count(&self) -> usize
Number of currently-attached signal subscribers.