Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 05e8eb0

Browse files
committed
perf(quote): lazy-load trading days per market on first candlestick push
Previously connect() eagerly fetched trading days for all 4 markets (HK/US/SG/CN) via 4 sequential WebSocket round-trips, even when the caller never subscribed to candlesticks. Now trading days are fetched on demand the first time a quote or trade push arrives for a given market, via ensure_trading_days(market). Results are cached in TradingDays.fetched so subsequent pushes for the same market are free. The 24h periodic refresh only refreshes markets that have already been fetched. Also drops the unused normal_days field from TradingDays and replaces the bulk fetch_trading_days() with a single-market fetch_trading_days_for_market() helper.
1 parent 127d1ec commit 05e8eb0

1 file changed

Lines changed: 64 additions & 46 deletions

File tree

rust/src/quote/core.rs

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ pub(crate) enum Command {
9797

9898
#[derive(Debug, Default)]
9999
struct TradingDays {
100-
normal_days: HashMap<Market, HashSet<Date>>,
101100
half_days: HashMap<Market, HashSet<Date>>,
101+
fetched: HashSet<Market>,
102102
}
103103

104104
impl TradingDays {
@@ -265,8 +265,6 @@ impl Core {
265265
.collect();
266266
ws_cli.set_rate_limit(rate_limit.clone());
267267

268-
let current_trade_days = fetch_trading_days(&ws_cli).await?;
269-
270268
let mut table = Table::new();
271269
for market_packages in quote_package_details_by_market {
272270
if market_packages.warning.is_empty() {
@@ -301,7 +299,7 @@ impl Core {
301299
});
302300

303301
self.rate_limit = rate_limit;
304-
self.trading_days = current_trade_days;
302+
self.trading_days = TradingDays::default();
305303
self.ws_cli = Some(ws_cli);
306304
self.session = Some(session);
307305
Ok(())
@@ -422,10 +420,23 @@ impl Core {
422420
}
423421
}
424422
_ = update_trading_days_interval.tick() => {
425-
if let Some(ws_cli) = &self.ws_cli
426-
&& let Ok(days) = fetch_trading_days(ws_cli).await
427-
{
428-
self.trading_days = days;
423+
if let Some(ws_cli) = &self.ws_cli {
424+
let markets: Vec<Market> =
425+
self.trading_days.fetched.iter().copied().collect();
426+
for market in markets {
427+
match fetch_trading_days_for_market(ws_cli, market).await {
428+
Ok(half_days) => {
429+
self.trading_days.half_days.insert(market, half_days);
430+
}
431+
Err(err) => {
432+
tracing::warn!(
433+
market = ?market,
434+
error = %err,
435+
"failed to refresh trading days"
436+
);
437+
}
438+
}
439+
}
429440
}
430441
}
431442
}
@@ -823,7 +834,26 @@ impl Core {
823834
async fn handle_ws_event(&mut self, event: WsEvent) -> Result<()> {
824835
match event {
825836
WsEvent::Error(err) => Err(err.into()),
826-
WsEvent::Push { command_code, body } => self.handle_push(command_code, body),
837+
WsEvent::Push { command_code, body } => self.handle_push(command_code, body).await,
838+
}
839+
}
840+
841+
/// Fetch and cache trading days for `market` if not already fetched.
842+
/// Errors are logged and silently swallowed so a failed fetch never
843+
/// disrupts the push handling path.
844+
async fn ensure_trading_days(&mut self, market: Market) {
845+
if self.trading_days.fetched.contains(&market) {
846+
return;
847+
}
848+
let Some(ws_cli) = &self.ws_cli else { return };
849+
match fetch_trading_days_for_market(ws_cli, market).await {
850+
Ok(half_days) => {
851+
self.trading_days.half_days.insert(market, half_days);
852+
self.trading_days.fetched.insert(market);
853+
}
854+
Err(err) => {
855+
tracing::warn!(market = ?market, error = %err, "failed to fetch trading days");
856+
}
827857
}
828858
}
829859

@@ -954,7 +984,7 @@ impl Core {
954984
}
955985
}
956986

957-
fn handle_push(&mut self, command_code: u8, body: Vec<u8>) -> Result<()> {
987+
async fn handle_push(&mut self, command_code: u8, body: Vec<u8>) -> Result<()> {
958988
match PushEvent::parse(command_code, &body) {
959989
Ok((mut event, tag)) => {
960990
tracing::info!(event = ?event, tag = ?tag, "push event");
@@ -964,6 +994,9 @@ impl Core {
964994
}
965995

966996
if let PushEventDetail::Quote(push_quote) = &event.detail {
997+
if let Some(market) = parse_market_from_symbol(&event.symbol) {
998+
self.ensure_trading_days(market).await;
999+
}
9671000
self.merge_candlesticks_by_quote(&event.symbol, push_quote);
9681001

9691002
if !self
@@ -975,6 +1008,9 @@ impl Core {
9751008
return Ok(());
9761009
}
9771010
} else if let PushEventDetail::Trade(trades) = &event.detail {
1011+
if let Some(market) = parse_market_from_symbol(&event.symbol) {
1012+
self.ensure_trading_days(market).await;
1013+
}
9781014
self.merge_candlesticks_by_trades(&event.symbol, trades);
9791015

9801016
if !self
@@ -1115,46 +1151,28 @@ fn merge_type(
11151151
})
11161152
}
11171153

1118-
async fn fetch_trading_days(cli: &WsClient) -> Result<TradingDays> {
1119-
let mut days = TradingDays::default();
1154+
async fn fetch_trading_days_for_market(cli: &WsClient, market: Market) -> Result<HashSet<Date>> {
11201155
let begin_day = OffsetDateTime::now_utc().date() - time::Duration::days(5);
11211156
let end_day = begin_day + time::Duration::days(30);
11221157

1123-
for market in [Market::HK, Market::US, Market::SG, Market::CN] {
1124-
let resp = cli
1125-
.request::<_, MarketTradeDayResponse>(
1126-
cmd_code::GET_TRADING_DAYS,
1127-
None,
1128-
MarketTradeDayRequest {
1129-
market: market.to_string(),
1130-
beg_day: format_date(begin_day),
1131-
end_day: format_date(end_day),
1132-
},
1133-
)
1134-
.await?;
1135-
1136-
days.normal_days.insert(
1137-
market,
1138-
resp.trade_day
1139-
.iter()
1140-
.map(|value| {
1141-
parse_date(value).map_err(|err| Error::parse_field_error("half_trade_day", err))
1142-
})
1143-
.collect::<Result<HashSet<_>>>()?,
1144-
);
1145-
1146-
days.half_days.insert(
1147-
market,
1148-
resp.half_trade_day
1149-
.iter()
1150-
.map(|value| {
1151-
parse_date(value).map_err(|err| Error::parse_field_error("half_trade_day", err))
1152-
})
1153-
.collect::<Result<HashSet<_>>>()?,
1154-
);
1155-
}
1158+
let resp = cli
1159+
.request::<_, MarketTradeDayResponse>(
1160+
cmd_code::GET_TRADING_DAYS,
1161+
None,
1162+
MarketTradeDayRequest {
1163+
market: market.to_string(),
1164+
beg_day: format_date(begin_day),
1165+
end_day: format_date(end_day),
1166+
},
1167+
)
1168+
.await?;
11561169

1157-
Ok(days)
1170+
resp.half_trade_day
1171+
.iter()
1172+
.map(|value| {
1173+
parse_date(value).map_err(|err| Error::parse_field_error("half_trade_day", err))
1174+
})
1175+
.collect()
11581176
}
11591177

11601178
#[allow(clippy::too_many_arguments)]

0 commit comments

Comments
 (0)