pub struct Bot { /* private fields */ }Expand description
The embedded trading bot.
Owns a Supervisor, an ExchangeClient, one or more Brains,
and the in-process MarketDataBus. Created via Bot::new; run
via Bot::run_until_shutdown; observed and steered via the
BotHandle returned from Bot::handle.
§Example
use std::sync::Arc;
use rustrade::{Bot, BotConfig};
let config = BotConfig::builder()
.name("my-bot")
.symbol("BTCUSDT")
.build()?;
let bot = Bot::new(config, exchange, vec![Arc::new(MyBrain) as Arc<dyn rustrade_core::Brain>])?;
let handle = bot.handle();
// Spawn the bot; ask it to shut down from elsewhere.
let task = tokio::spawn(async move { bot.run_until_shutdown().await });
handle.shutdown();
task.await??;Implementations§
Source§impl Bot
impl Bot
Sourcepub fn new(
config: BotConfig,
exchange: Arc<dyn ExchangeClient>,
brains: Vec<Arc<dyn Brain>>,
) -> Result<Self>
pub fn new( config: BotConfig, exchange: Arc<dyn ExchangeClient>, brains: Vec<Arc<dyn Brain>>, ) -> Result<Self>
Construct a Bot. Validates that at least one brain is provided.
The exchange client and brain set are immutable for the bot’s
lifetime — to change them, build a new Bot.
Sourcepub fn with_metrics(self, sink: Arc<dyn MetricsSink>) -> Self
pub fn with_metrics(self, sink: Arc<dyn MetricsSink>) -> Self
Install a MetricsSink. The framework’s services emit
counters and histograms to this sink on every observable event;
the default is NoopSink, which discards everything.
Sourcepub fn with_state_store(self, store: Arc<dyn StateStore>) -> Self
pub fn with_state_store(self, store: Arc<dyn StateStore>) -> Self
Install a StateStore so per-symbol risk state (session PnL and
circuit breaker) survives restarts.
Without a store, risk state is in-memory only: a crash mid-session resets the daily drawdown cap and the loss-streak breaker. With one wired, the bot:
- Restores each symbol’s snapshot on startup, then applies the stale-snapshot policy — a session from an earlier UTC day rolls over to fresh, and a breaker whose cooldown elapsed during downtime auto-resets.
- Persists after every realised trade (whether fed via
BotHandle::record_trade_outcomeor auto-routed by theFillRoutingService). - Flushes on graceful shutdown.
Use rustrade_core::InMemoryStore for a non-durable default, or a
disk-/database-backed implementation from a downstream crate for
real durability. Snapshots are keyed by (bot name, symbol), so
distinct bots can share one backend without collision.
Sourcepub fn with_order_tracking(self, ttl: Duration, poll_cadence: Duration) -> Self
pub fn with_order_tracking(self, ttl: Duration, poll_cadence: Duration) -> Self
Enable resting-order lifecycle tracking.
The ExecutionService records every resting order it places (limit
/ post-only / IOC / FOK — market orders never rest, so they’re
skipped), and a supervised OrderReaperService periodically:
- reconciles the tracker against
exchange.get_open_orders(so orders filled or cancelled out-of-band stop being tracked), and - cancels any resting order older than
ttlviaexchange.cancel_order.
poll_cadence is how often a sweep runs. The reaper is only spawned
if the adapter advertises Capability::OrderTracking; otherwise the
call is a no-op (with a warning at startup), since the framework can’t
list or cancel orders without it. Live tracked orders are visible via
BotHandle::tracked_orders.
Sourcepub fn with_candle_poller(
self,
source: Arc<dyn CandleSource>,
symbol: impl Into<Symbol>,
interval: Duration,
poll_cadence: Duration,
limit: usize,
) -> Self
pub fn with_candle_poller( self, source: Arc<dyn CandleSource>, symbol: impl Into<Symbol>, interval: Duration, poll_cadence: Duration, limit: usize, ) -> Self
Register a CandleSource to be polled every poll_cadence for
(symbol, interval). Polled candles are deduplicated by
timestamp and published to the bot’s MarketDataBus. Repeated
calls accumulate — one supervised service per registered tuple.
Sourcepub fn with_external_cancel(self, token: CancellationToken) -> Self
pub fn with_external_cancel(self, token: CancellationToken) -> Self
Tie this bot’s shutdown to an externally-owned cancellation token.
When the external token is cancelled, the bot’s supervisor token
is cancelled too — equivalent to calling BotHandle::shutdown
but without spawning a linker task in the host.
The reverse is not true: cancelling the bot does not cancel the external token.
Sourcepub fn with_market_source(self, source: Arc<dyn MarketSource>) -> Self
pub fn with_market_source(self, source: Arc<dyn MarketSource>) -> Self
Attach a MarketSource to be driven by a supervised
MarketFeedService. Source implementors are responsible for
publishing to the bot’s MarketDataBus (obtain via
bot.market_data_bus().clone() before constructing the source).
Sourcepub fn with_fill_source(self, source: Arc<dyn FillSource>) -> Self
pub fn with_fill_source(self, source: Arc<dyn FillSource>) -> Self
Attach a FillSource to be driven by a supervised
FillRoutingService. Fills are routed to every brain via
Brain::on_fill and the position cache is refreshed from the
exchange after each one.
Sourcepub fn handle(&self) -> BotHandle
pub fn handle(&self) -> BotHandle
Cheap cloneable handle for host services. Can be obtained at any
point — call before Self::run_until_shutdown so the host can
drive shutdown while the bot is running.
Sourcepub fn market_data_bus(&self) -> &MarketDataBus
pub fn market_data_bus(&self) -> &MarketDataBus
Borrow the in-process market-data bus. Host services and adapters publish here; the bot’s framework services subscribe.
Sourcepub fn signal_bus(&self) -> &SignalBus
pub fn signal_bus(&self) -> &SignalBus
Borrow the in-process signal bus. The execution service publishes
a Signal to this bus on every
non-Hold decision the brain emits; host services subscribe via
BotHandle::subscribe_signals.
Sourcepub async fn run_until_shutdown(self) -> Result<()>
pub async fn run_until_shutdown(self) -> Result<()>
Spawn the framework services and run until shutdown.
Returns after all spawned services have drained (or the configured
shutdown timeout elapses). Consumes self to make the
“construct → run → exit” lifecycle explicit; persistent observation
of the running bot is done via the BotHandle obtained earlier.
§Runtime requirements
- Multi-thread tokio runtime. The supervisor spawns each
service onto
tokio::spawn. A current-thread runtime works for small loads but loses the per-service parallelism the framework is designed for. Use#[tokio::main(flavor = "multi_thread")]ortokio::runtime::Builder::new_multi_thread()in the host. tokio::spawnis used internally. Anywhere the host embeds this method, a tokio runtime context must be active.- No nested runtimes.
Bot::run_until_shutdownis async; do not callblock_onon it from inside another runtime.
§Resource expectations
- Memory per active symbol: O(few hundred bytes) for the
position cache entry,
SymbolRisk(aSessionPnlplus aCircuitBreakerwhose ring buffer is bounded byloss_limit), plus the per-symbol slot in any host-owned subscriber. - Channel buffers:
market_bus_capacity+signal_bus_capacityslots per bus, each slot holding a clone ofMarketDataEvent/Signal. Drop-oldest semantics — back-pressure is not propagated to publishers. - Expected shutdown time: ≤
shutdown_timeout. A well-behaved service responds to its cancel token in milliseconds; the timeout is the worst-case bound, not the typical case. - Restart-after-crash latency: bounded by
BackoffConfig. Defaults: 100 ms base, 60 s cap, 10 retries within a 10-minute window before the circuit breaker trips.