Skip to content

Universe Models

A universe model is a modular symbol selector. It returns symbols through UniverseResult:

aq-engine/src/core/universe/mod.rs
pub trait UniverseModel {
fn version(&self) -> &str;
fn run(&mut self, ctx: &mut dyn StrategyContext) -> UniverseResult;
}

AQE merges the symbols returned by Strategy::universe(ctx) and every registered universe model. The final universe is a de-duplicated union. That means a static universe can remain inline while one or more universe models add symbols from watchlists, filters, account rules, or date-aware selection logic.

state.add_universe_model(
UniverseModelBuilder::new(Box::new(MyUniverseModel::new()))
.can_fail(false)
.build(),
);
use aq_engine::core::strategy::StrategyContext;
use aq_engine::core::universe::{UniverseModel, UniverseResult};
use std::collections::HashSet;
pub struct CryptoMajors;
impl CryptoMajors {
pub fn new() -> Self {
Self
}
}
impl UniverseModel for CryptoMajors {
fn version(&self) -> &str {
"1.0"
}
fn run(&mut self, _ctx: &mut dyn StrategyContext) -> UniverseResult {
let symbols = ["BTCUSD", "ETHUSD"]
.into_iter()
.map(str::to_string)
.collect::<HashSet<_>>();
UniverseResult::passed(symbols, self.name().to_string())
}
}

Inline lifecycle code is still valid:

  • put simple startup registration in Strategy::on_start
  • put simple per-asset setup in Strategy::init
  • return a fixed symbol set from Strategy::universe
  • place one-off cleanup in Strategy::on_teardown

Prefer universe models when symbol selection has a clear module boundary, needs constructor configuration, or should be reused between strategies.