Skip to main content

Bot

Struct Bot 

Source
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

Source

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.

Source

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.

Source

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_outcome or auto-routed by the FillRoutingService).
  • 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.

Source

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 ttl via exchange.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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

pub fn config(&self) -> &BotConfig

Reference to the bot’s configuration.

Source

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.

Source

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.

Source

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")] or tokio::runtime::Builder::new_multi_thread() in the host.
  • tokio::spawn is used internally. Anywhere the host embeds this method, a tokio runtime context must be active.
  • No nested runtimes. Bot::run_until_shutdown is async; do not call block_on on it from inside another runtime.
§Resource expectations
  • Memory per active symbol: O(few hundred bytes) for the position cache entry, SymbolRisk (a SessionPnl plus a CircuitBreaker whose ring buffer is bounded by loss_limit), plus the per-symbol slot in any host-owned subscriber.
  • Channel buffers: market_bus_capacity + signal_bus_capacity slots per bus, each slot holding a clone of MarketDataEvent / 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.

Auto Trait Implementations§

§

impl !RefUnwindSafe for Bot

§

impl !UnwindSafe for Bot

§

impl Freeze for Bot

§

impl Send for Bot

§

impl Sync for Bot

§

impl Unpin for Bot

§

impl UnsafeUnpin for Bot

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more