Alpha Models
What an alpha model does
Section titled “What an alpha model does”An alpha model is responsible for turning market state into candidate insights. It is not the place to manage the full order lifecycle. Its job is to decide when an opportunity exists and return an Insight.
AQE calls alpha models through a dedicated lifecycle:
start()init(asset)generate_insights(symbol)
Alpha model trait
Section titled “Alpha model trait”pub trait AlphaModel { fn version(&self) -> &str; fn start(&mut self, ctx: &mut dyn StrategyContext); fn init(&mut self, ctx: &mut dyn StrategyContext, asset: &Asset); fn generate_insights(&mut self, ctx: &mut dyn StrategyContext, symbol: &str) -> AlphaResult;}What belongs in each phase
Section titled “What belongs in each phase”start()
Section titled “start()”Use start() for runtime-wide setup:
- register indicators
- set warm-up bars
- configure state needed before any symbol-specific initialization
init(asset)
Section titled “init(asset)”Use init() for per-asset setup:
- initialize symbol-specific variables
- read asset metadata
- prepare per-symbol runtime state
generate_insights(symbol)
Section titled “generate_insights(symbol)”Use generate_insights() to:
- read the latest history
- inspect indicators or variables
- check asset metadata
- create and return a new
Insight
Real example
Section titled “Real example”EmaPriceCrossover is a concrete alpha in AQE. It:
- registers ATR and EMA
- reads history
- checks the latest and previous bars
- uses asset metadata to decide whether short signals are allowed
- returns an insight with entry, TP, SL, and TTL settings
fn generate_insights(&mut self, ctx: &mut dyn StrategyContext, symbol: &str) -> AlphaResult { let Some(asset) = self.get_asset(ctx, symbol) else { return AlphaResult::new(None, false, Some(format!("Asset {} not found", symbol)), self.name().to_string()); };
let history = match self.get_history(ctx, symbol) { Some(history) if history.height() >= 2 => history, _ => return AlphaResult::new(None, true, Some("Not enough history".to_string()), self.name().to_string()), };
// derive signal, tp, sl, entry ...
let mut insight = Insight::new( side, symbol.to_string(), StrategyType::Custom(self.name().to_string()), ctx.timeframe().clone(), confidence, None, );
insight .set_limit_price(Some(entry)) .set_take_profit_levels(Some(vec![tp])) .set_stop_loss(Some(sl)) .set_period_unfilled(Some(ttluf as u32)) .set_period_till_tp(Some(ttlf as u32));
AlphaResult::new(Some(insight), true, None, self.name().to_string())}Another simple pattern
Section titled “Another simple pattern”TestEntry shows a simpler alpha shape:
- reads recent price state
- counts active insights per symbol
- emits a new buy or sell insight when conditions are met
That makes it a useful reference for end-user code when you want a lightweight starting point.
What alpha models should not do
Section titled “What alpha models should not do”Alpha models should not become full runtime managers. In most cases, avoid putting these concerns inside the alpha:
- final quantity sizing
- trade submission
- close logic
- stop-loss trigger checks
- time-window filtering
Those fit better in insight pipes.
Related source
Section titled “Related source”aq-engine/src/core/alpha/mod.rsaq-engine/src/core/alpha/ema_price_crossover.rsaq-engine/src/core/alpha/test_entry.rs