Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deploy/Dockerfile.hft
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ ENV HFT_TARGET=${TARGET}

# 健康檢查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD if [ "$HFT_TARGET" = "live" ]; then curl --fail --silent http://localhost:9090/readiness; else exit 0; fi
CMD if [ "$HFT_TARGET" = "live" ]; then curl --fail --silent http://localhost:9090/health; else exit 0; fi

# 默認暴露端口
EXPOSE 9090 9092
Expand Down
6 changes: 2 additions & 4 deletions rust_hft/apps/live/src/helpers/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ async fn run_axum_metrics_server(
interval.tick().await;

if let Ok(engine) = sync_engine_arc.try_lock() {
let latency_stats = engine.get_latency_stats();
if !latency_stats.is_empty() {
infra_metrics::MetricsRegistry::global()
.update_from_latency_monitor(&latency_stats);
if engine.get_statistics().is_running {
engine.sync_latency_metrics_to_prometheus();
}
}
}
Expand Down
28 changes: 17 additions & 11 deletions rust_hft/apps/live/src/helpers/sentinel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ async fn run_sentinel_loop(
let mut interval = tokio::time::interval(Duration::from_millis(check_interval_ms));

let mut last_state = SentinelState::Normal;
let mut last_orders_submitted = 0_u64;

loop {
interval.tick().await;
Expand All @@ -88,25 +89,30 @@ async fn run_sentinel_loop(
// 從引擎獲取真實 PnL、延遲和回撤統計 (drawdown 現在由 Portfolio 計算)
let sentinel_stats = engine.get_sentinel_stats();

// 估算活躍訂單數:提交 - 完成 - 取消 - 拒絕
let active_orders = engine_stats
.orders_submitted
.saturating_sub(engine_stats.orders_filled)
.saturating_sub(engine_stats.orders_canceled)
.saturating_sub(engine_stats.orders_rejected);

let stats = SystemStats {
latency_p99_us: sentinel_stats.latency_p99_us,
latency_p50_us: sentinel_stats.latency_p50_us,
drawdown_pct: sentinel_stats.drawdown_pct,
pnl: sentinel_stats.pnl,
high_water_mark: sentinel_stats.high_water_mark,
position_count: active_orders as i64,
notional_value: 0.0,
order_rate: 0.0,
position_count: sentinel_stats.position_count,
notional_value: sentinel_stats.notional_value,
order_rate: engine_stats
.orders_submitted
.saturating_sub(last_orders_submitted) as f64
/ (check_interval_ms.max(1) as f64 / 1_000.0),
// ponytail: engine does not expose venue reconnects; wire adapter metrics here
// if Sentinel policy begins to act on reconnect frequency.
ws_reconnect_count: 0,
data_gap_count: 0,
data_gap_count: u32::try_from(
engine_stats
.market_events_dropped
.saturating_add(engine_stats.snapshot_publish_failed)
.saturating_add(engine_stats.data_integrity_gaps),
)
.unwrap_or(u32::MAX),
};
last_orders_submitted = engine_stats.orders_submitted;

(stats, engine_stats.is_running)
};
Expand Down
11 changes: 9 additions & 2 deletions rust_hft/apps/live/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut system = builder.build();
let attribution_observer = if let Some(deployment) = activation.as_ref() {
let feedback_log = open_feedback_log(&args)?;
let (receiver, market_reader) = {
let (receiver, market_reader, account_reader, runtime_truth_reader) = {
let engine = system.engine.lock().await;
(engine.subscribe_execution_events(), engine.market_reader())
(
engine.subscribe_execution_events(),
engine.market_reader(),
engine.account_reader(),
engine.runtime_truth_reader(),
)
};
Some(RuntimeAttributionObserver::new(
receiver,
market_reader,
account_reader,
runtime_truth_reader,
deployment.request.clone(),
feedback_log,
market_stale_us,
Expand Down
Loading
Loading