Skip to content

Add Events

add_events lets an AQE strategy subscribe to extra market-data event streams in addition to the strategy’s main timeframe. The first supported event stream is EventStreamType::Bar.

The main strategy timeframe is always registered automatically from the timeframe passed to run_backtest(...) or configured for the live runtime. You only call add_events when you want additional feature timeframes such as 1m, 15m, or 1h.

Register feature streams in on_start:

use aq_engine::core::strategy::{EventStreamType, StrategyContext};
use aq_engine::core::utils::timeframe::{TimeFrame, TimeFrameUnit};
fn on_start(&mut self, ctx: &mut dyn StrategyContext) {
ctx.add_events(
EventStreamType::Bar,
Some(TimeFrame::new(15, TimeFrameUnit::Minute)),
);
}

AQE applies that stream to every asset in the loaded universe. If the strategy trades BTC and ETH, a 15m feature bar stream is registered for both symbols.

Calling ctx.add_events(EventStreamType::Bar, None) refers to the main timeframe and is de-duplicated with the automatic main stream.

Use EventStreamRequest when you need options:

use aq_engine::core::strategy::{EventStreamRequest, EventStreamType, StrategyContext};
use aq_engine::core::utils::timeframe::{TimeFrame, TimeFrameUnit};
fn on_start(&mut self, ctx: &mut dyn StrategyContext) {
ctx.add_events_with_options(
EventStreamRequest::bar(Some(TimeFrame::new(1, TimeFrameUnit::Hour)))
.allow_trading(true),
);
}

allow_trading controls strategy and alpha-model generation for that event stream:

  • false is the default for feature streams. AQE updates history and calls on_bar, but does not call alpha model generate_insights or strategy generate_insights for that feature event.
  • true allows alpha model generate_insights and strategy generate_insights to run for that feature event.

Broker execution still follows the normal execution path. Feature bars are designed to enrich strategy context and signal generation; they should not be treated as a separate broker fill stream.

The main timeframe keeps the base symbol as its history key:

let main_history = ctx.history().get(symbol);

Feature timeframes use {symbol}:{timeframe}:

let feature_history = ctx.history().get("BTC:15m");
let faster_history = ctx.history().get("BTC:1m");
let hourly_history = ctx.history().get("ETH:1h");

This keeps feature data separate from the main strategy history. Indicators and custom state should read from the key that matches the event or signal they are using.

Use ctx.current_event() inside on_bar, alpha models, or generate_insights to inspect the event that AQE is currently processing:

fn on_bar(&mut self, ctx: &mut dyn StrategyContext, symbol: &str, _bar: &BarData) {
let Some(event) = ctx.current_event() else {
return;
};
let history = ctx.history().get(&event.history_key);
if event.is_feature {
println!(
"feature bar: {} {} history={} allow_trading={}",
event.symbol,
event.timeframe,
event.history_key,
event.allow_trading,
);
} else {
println!("main bar: {symbol}");
}
}

StrategyEventContext includes:

  • event_type
  • symbol
  • timeframe
  • history_key
  • is_feature
  • allow_trading
  • timestamp

The context remains active through the full bar-processing scope for that event: history update, indicator update, on_bar, alpha model generation, and strategy generation when the event is allowed to generate insights.

Use feature streams when a strategy needs more than one market-data resolution:

  • a daily strategy that reads 15m structure before generating a signal;
  • a 1h strategy that uses 1m volatility or liquidity filters;
  • a main execution timeframe with higher-timeframe confirmation.

Keep the main timeframe as the primary execution clock. Use feature streams for context unless you intentionally allow that stream to generate insights.