feat(trading_service): wire MLEngine with real EnsembleCoordinator
Replace empty MLEngine struct with actual EnsembleCoordinator from ml crate. Registers DQN, PPO, TFT, Mamba2 models with configurable weights loaded from config repository. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -757,10 +757,30 @@ impl PositionStateValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ML engine and model registry placeholder
|
||||
#[derive(Debug, Default)]
|
||||
/// ML engine backed by the real `ml::ensemble::EnsembleCoordinator`.
|
||||
///
|
||||
/// The coordinator is lazily initialized during
|
||||
/// [`initialize_with_config_repository`](MLEngine::initialize_with_config_repository)
|
||||
/// because it needs async model registration. Before that call,
|
||||
/// [`coordinator()`](MLEngine::coordinator) returns `None`.
|
||||
pub struct MLEngine {
|
||||
// ML models and predictions
|
||||
/// The real ensemble coordinator from the `ml` crate.
|
||||
/// `None` until [`initialize_with_config_repository`] completes.
|
||||
coordinator: Option<ml::ensemble::EnsembleCoordinator>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MLEngine {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MLEngine")
|
||||
.field("coordinator", &self.coordinator)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MLEngine {
|
||||
fn default() -> Self {
|
||||
Self { coordinator: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl MLEngine {
|
||||
@@ -768,11 +788,15 @@ impl MLEngine {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Initialize the ML engine by creating an `EnsembleCoordinator` and
|
||||
/// registering the default production models (DQN, PPO, TFT, MAMBA-2).
|
||||
///
|
||||
/// Model weights and the inference timeout are loaded from the config
|
||||
/// repository. If a config key is missing the default value is used.
|
||||
pub async fn initialize_with_config_repository(
|
||||
&mut self,
|
||||
config_repository: &Arc<PostgresConfigRepository>,
|
||||
) -> TradingServiceResult<()> {
|
||||
// Load and initialize ML models from config repository (no direct database access)
|
||||
// Load ML inference timeout from config repository
|
||||
if let Ok(Some(inference_timeout)) = config_repository
|
||||
.get_config_u64("MachineLearning", "inference_timeout_ms")
|
||||
@@ -784,8 +808,97 @@ impl MLEngine {
|
||||
);
|
||||
}
|
||||
|
||||
// Create the real ensemble coordinator
|
||||
let coordinator = ml::ensemble::EnsembleCoordinator::new();
|
||||
|
||||
// Load per-model weights from config, falling back to equal weights
|
||||
let default_weight = 0.25_f64;
|
||||
|
||||
let dqn_weight = config_repository
|
||||
.get_config_f64("MachineLearning", "dqn_weight")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(default_weight);
|
||||
|
||||
let ppo_weight = config_repository
|
||||
.get_config_f64("MachineLearning", "ppo_weight")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(default_weight);
|
||||
|
||||
let tft_weight = config_repository
|
||||
.get_config_f64("MachineLearning", "tft_weight")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(default_weight);
|
||||
|
||||
let mamba2_weight = config_repository
|
||||
.get_config_f64("MachineLearning", "mamba2_weight")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(default_weight);
|
||||
|
||||
// Register the four production models
|
||||
if let Err(e) = coordinator
|
||||
.register_model("DQN".to_string(), dqn_weight)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to register DQN model: {}", e);
|
||||
}
|
||||
if let Err(e) = coordinator
|
||||
.register_model("PPO".to_string(), ppo_weight)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to register PPO model: {}", e);
|
||||
}
|
||||
if let Err(e) = coordinator
|
||||
.register_model("TFT".to_string(), tft_weight)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to register TFT model: {}", e);
|
||||
}
|
||||
if let Err(e) = coordinator
|
||||
.register_model("MAMBA-2".to_string(), mamba2_weight)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to register MAMBA-2 model: {}", e);
|
||||
}
|
||||
|
||||
let model_count = coordinator.model_count().await;
|
||||
tracing::info!(
|
||||
"ML engine EnsembleCoordinator initialized with {} models \
|
||||
(DQN={:.2}, PPO={:.2}, TFT={:.2}, MAMBA-2={:.2})",
|
||||
model_count,
|
||||
dqn_weight,
|
||||
ppo_weight,
|
||||
tft_weight,
|
||||
mamba2_weight,
|
||||
);
|
||||
|
||||
self.coordinator = Some(coordinator);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a shared reference to the underlying `EnsembleCoordinator`.
|
||||
///
|
||||
/// Returns `None` if the engine has not been initialized yet.
|
||||
pub fn coordinator(&self) -> Option<&ml::ensemble::EnsembleCoordinator> {
|
||||
self.coordinator.as_ref()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying `EnsembleCoordinator`.
|
||||
///
|
||||
/// Useful for adding inference adapters via
|
||||
/// [`EnsembleCoordinator::add_adapter`].
|
||||
/// Returns `None` if the engine has not been initialized yet.
|
||||
pub fn coordinator_mut(&mut self) -> Option<&mut ml::ensemble::EnsembleCoordinator> {
|
||||
self.coordinator.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Market data manager with multiple providers
|
||||
|
||||
Reference in New Issue
Block a user