diff --git a/crates/fidc-core/src/lib.rs b/crates/fidc-core/src/lib.rs index a0d87b9..4f76163 100644 --- a/crates/fidc-core/src/lib.rs +++ b/crates/fidc-core/src/lib.rs @@ -84,9 +84,10 @@ pub use strategy::{ }; pub use strategy_ai::{ ManualExample, ManualFactorSource, ManualField, ManualFieldGroup, ManualFunction, - ManualSection, StrategyAiCatalog, StrategyAiGenerateRequest, StrategyAiManual, - StrategyAiOptimizeRequest, build_generation_prompt, build_optimization_prompt, - built_in_strategy_manual, merge_catalog_into_manual, render_manual_markdown, + ManualSection, StrategyAiCatalog, StrategyAiGenerateRequest, StrategyAiHoldingCountContract, + StrategyAiManual, StrategyAiOptimizeRequest, build_generation_prompt, + build_optimization_prompt, built_in_strategy_manual, merge_catalog_into_manual, + render_manual_markdown, }; pub use universe::{ BandRegime, DynamicMarketCapBandSelector, SelectionContext, SelectionDiagnostics, diff --git a/crates/fidc-core/src/strategy_ai.rs b/crates/fidc-core/src/strategy_ai.rs index 4a7dbc6..015903e 100644 --- a/crates/fidc-core/src/strategy_ai.rs +++ b/crates/fidc-core/src/strategy_ai.rs @@ -72,6 +72,29 @@ pub struct StrategyAiCatalog { pub data_lake_fields: Vec, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StrategyAiHoldingCountContract { + #[serde( + default, + alias = "holdingCount", + alias = "holding_count", + alias = "targetHoldingCount", + alias = "target_holding_count" + )] + #[serde(skip_serializing_if = "Option::is_none")] + pub count: Option, + #[serde( + default, + alias = "kind", + alias = "holdingCountMode", + alias = "holding_count_mode", + alias = "targetHoldingCountMode", + alias = "target_holding_count_mode" + )] + #[serde(skip_serializing_if = "Option::is_none")] + pub mode: Option, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StrategyAiGenerateRequest { pub user_goal: String, @@ -79,6 +102,9 @@ pub struct StrategyAiGenerateRequest { pub market: String, pub benchmark_symbol: String, pub signal_symbol: String, + #[serde(default, alias = "holdingCountContract")] + #[serde(skip_serializing_if = "Option::is_none")] + pub holding_count_contract: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -87,6 +113,9 @@ pub struct StrategyAiOptimizeRequest { pub objective: String, pub result_summary: serde_json::Value, pub diagnostics: Vec, + #[serde(default, alias = "holdingCountContract")] + #[serde(skip_serializing_if = "Option::is_none")] + pub holding_count_contract: Option, } const DEFAULT_THREE_YEAR_RETURN_TARGET_PROMPT: &str = "默认收益目标:用户没有明确指定更高收益阈值时,三年回测区间策略总收益 >= 150% 即视为满足收益目标;达到该阈值后可以继续优化夏普、回撤、换手和稳定性,但不得把已达标策略判为失败或为了追更高收益破坏无未来数据、持仓数量和同条件对账合同。"; @@ -602,6 +631,7 @@ mod tests { market: "CN_A".to_string(), benchmark_symbol: "000852.SH".to_string(), signal_symbol: "000001.SH".to_string(), + holding_count_contract: None, }, ); @@ -624,6 +654,7 @@ mod tests { objective: "优化收益".to_string(), result_summary: json!({ "total_return": 1.49 }), diagnostics: Vec::new(), + holding_count_contract: None, }, );