pub(crate) mod event;
pub(crate) mod optimize;
pub(crate) mod position;
mod signal;
use crate::engine_v2::catalog::{CatalogSignal, resolve_signal_category};
use crate::engine_v2::compiler::event::compile_events;
use crate::engine_v2::compiler::position::compile_positions;
use crate::engine_v2::compiler::signal::{CompiledSignalPlan, compile_signals};
use crate::sig_parse::SignalConfig;
use czsc_core::objects::position::Position;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ExecutionPlanInput {
pub symbol: String,
pub base_freq: String,
#[serde(default)]
pub signals_config: Vec<SignalConfig>,
#[serde(default)]
pub positions: Vec<Position>,
pub market: Option<String>,
pub bg_max_count: Option<usize>,
pub sdt: Option<String>,
#[serde(default)]
pub include_sdt_bar: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct ExecutionPlan {
pub symbol: String,
pub base_freq: String,
pub signals_config: Vec<SignalConfig>,
pub positions: Vec<Position>,
pub market: Option<String>,
pub bg_max_count: usize,
pub sdt: Option<String>,
pub include_sdt_bar: bool,
pub catalog_signals: Vec<CatalogSignal>,
pub signal_plan: CompiledSignalPlan,
pub event_plan: event::CompiledEventPlan,
pub position_plan: position::CompiledPositionPlan,
}
pub(crate) use signal::CompiledSignalPlan as CompiledSignalPlanV2;
impl ExecutionPlan {
pub fn compile(input: ExecutionPlanInput) -> Result<Self, String> {
if input.symbol.trim().is_empty() {
return Err("strategy.symbol 不能为空".to_string());
}
if input.positions.is_empty() {
return Err("strategy.positions 不能为空".to_string());
}
let ExecutionPlanInput {
symbol,
base_freq,
signals_config,
mut positions,
market,
bg_max_count,
sdt,
include_sdt_bar,
} = input;
for pos in &mut positions {
pos.normalize_runtime_fields();
}
let mut catalog_signals = Vec::with_capacity(signals_config.len());
for sc in &signals_config {
catalog_signals.push(resolve_signal_category(sc)?);
}
let signal_plan = compile_signals(&signals_config, &catalog_signals)?;
let event_plan = compile_events(&positions);
let position_plan = compile_positions(&positions);
Ok(Self {
symbol,
base_freq,
signals_config,
positions,
market,
bg_max_count: bg_max_count.unwrap_or(5000),
sdt,
include_sdt_bar: include_sdt_bar.unwrap_or(false),
catalog_signals,
signal_plan,
event_plan,
position_plan,
})
}
}
#[cfg(test)]
mod tests {
use super::{ExecutionPlan, ExecutionPlanInput};
#[test]
fn compile_rejects_empty_symbol() {
let input = ExecutionPlanInput {
symbol: String::new(),
base_freq: "30分钟".to_string(),
signals_config: vec![],
positions: vec![],
market: None,
bg_max_count: None,
sdt: None,
include_sdt_bar: None,
};
assert!(ExecutionPlan::compile(input).is_err());
}
}