Skip to main content

QotCache

Struct QotCache 

Source
pub struct QotCache {
    pub basic_qot: DashMap<SecurityKey, CachedBasicQot>,
    pub us_stock_overnight: DashMap<u64, bool>,
    pub klines: DashMap<String, Vec<CachedKLine>>,
    pub order_books: DashMap<SecurityKey, CachedOrderBook>,
    pub tickers: DashMap<SecurityKey, Vec<CachedTicker>>,
    pub rt_data: DashMap<String, Vec<CachedTimeShare>>,
    pub brokers: DashMap<SecurityKey, CachedBroker>,
    pub broker_dict: DashMap<i64, CachedBrokerInfo>,
    pub spread_tables: DashMap<i32, Vec<CachedSpreadBand>>,
    pub cold_cache_waiters: DashMap<String, Arc<Notify>>,
    /* private fields */
}
Expand description

行情缓存管理器

Fields§

§basic_qot: DashMap<SecurityKey, CachedBasicQot>

基本报价缓存

§us_stock_overnight: DashMap<u64, bool>

US stock overnight-enabled state, keyed by backend stock_id.

C++ stores this as stockID -> bool in INNData_Qot_USStockOvernight:

  • NNData_Qot_USStockOvernight.cpp:21-35 (missing key => false)
  • NNBiz_Qot_USStockState.cpp:180-190 writes overnight_type == 1
  • APIServer_Qot_MarketState.cpp:238-244 reads it for 11 -> 37 projection
§klines: DashMap<String, Vec<CachedKLine>>

K 线缓存: key = “market_code:kl_type”

§order_books: DashMap<SecurityKey, CachedOrderBook>

摆盘缓存

§tickers: DashMap<SecurityKey, Vec<CachedTicker>>

逐笔缓存: 保留最近 N 条

§rt_data: DashMap<String, Vec<CachedTimeShare>>

分时缓存 v1.4.106 codex 1140 F6 (P2 audit Finding 6): RT cache key 加 session 维度. 之前 DashMap<SecurityKey, ...> 把 RTH/ETH/PRE/AFTER 全部混到 同一桶, 客户端订阅 RTH 也能读到 PRE 数据. 现在 key 是 “sec_key:s{session}” (RequestSection 0=NORMAL/1=FULL/2=PREMARKET/ 3=AFTERHOURS), 隔离不同 session.

§brokers: DashMap<SecurityKey, CachedBroker>

经纪队列缓存

§broker_dict: DashMap<i64, CachedBrokerInfo>

v1.4.106 codex 1140 F7 (P2 audit Finding 7): 券商 ID → 信息映射. 由 CMD 18008 拉取后填充, 用于 push parser 从 broker_id 查真名 (替代 Broker#{bid} 占位符).

§spread_tables: DashMap<i32, Vec<CachedSpreadBand>>

C++ INNData_Qot_Spread: spread table code → price bands.

Filled from CMD6503 at QOT handler registration time and refreshed every 8h, matching NNBiz_Qot_Spread::SetTimerUpdateSpreadInfo. Read paths are synchronous and lock-free enough for push hot paths.

§cold_cache_waiters: DashMap<String, Arc<Notify>>

v1.4.110 codex Phase 3 Slice 6c: cold-cache wait waiters.

key = "<cache_key>:<wait_kind>" (e.g. "91_BTCUSDT@b1007:basic" / "1_00700:orderbook"). value = shared Arc<Notify> 让 handler 阻塞等 push parser 写 cache 后唤醒.

对齐 C++ APIServer_Qot_StockBasic.cpp:226-320 WaitForReady — 已订阅但 cache 未就绪时 handler 主动 Pull_SubData + 等 push 写 cache.

设计 trade-off:

  • DashMap<String, Arc<Notify>> 而非 RwLock<HashMap>: 高并发读 写不锁全表
  • key 编码 wait_kind 防 basic / orderbook 共用同一 Notify 互相错唤醒
  • update path 调 notify_waiters (broadcast 给所有 awaiter) 然后从 map 中 remove (Arc 被 awaiter 持有, 自然释放)

Implementations§

Source§

impl QotCache

Source

pub fn make_kline_key_by_dims(sec_key: &str, dims: KlineDims) -> String

构造 K 线 cache key (v1.4.106 codex 1140 F3 4-tuple).

之前 key 仅 (sec_key, kl_type) 2-tuple, 同股票同 KLType 但前复权 vs 后复权 / RTH vs ETH 数据互相覆盖. 对齐 C++ APIServer_Qot_KL.cpp: GetNewestKLByCount(stock_id, enRehabType, enKLType, num, session, ...) 用 4 维 key.

  • rehab: proto Qot_Common.RehabType (0=None, 1=Forward, 2=Backward), 对齐 backend FTCmdKline.ExrightType. 同一股票同一 kl_type 不同 rehab 走独立 cache, 不互相覆盖.
  • kl_type: proto Qot_Common.KLType (1=1Min, 2=Day, …, 11=Quarter).
  • session: proto FTCmdKline.RequestSection (0=NORMAL, 1=FULL, 2=PREMARKET, 3=AFTERHOURS). RTH/ETH 隔离, push 来自 backend 的 point.section_type[0] 决定写入桶, read 由 client subscription session 决定 (尚无, 默认 NORMAL).
Source

pub fn make_kline_key( sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> String

Source

pub fn update_klines_by_dims( &self, sec_key: &str, dims: KlineDims, klines: Vec<CachedKLine>, )

更新 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).

Source

pub fn update_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )

Source

pub fn get_klines_by_dims( &self, sec_key: &str, dims: KlineDims, ) -> Option<Vec<CachedKLine>>

获取 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).

Source

pub fn get_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>

Source

pub fn update_klines_broker_by_dims( &self, key: &QotSecurityKey, dims: KlineDims, klines: Vec<CachedKLine>, )

v1.4.110 Phase 2 Slice 5: 更新 K 线 (broker-aware).

QotSecurityKey::cache_key() 作 prefix, broker_id=None 时退化到原行为. composite 维度仍是 4-tuple (rehab, kl_type, session), broker_id 是第 5 维通过 QotSecurityKey 注入到 prefix.

Source

pub fn update_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )

Source

pub fn get_klines_broker_by_dims( &self, key: &QotSecurityKey, dims: KlineDims, ) -> Option<Vec<CachedKLine>>

v1.4.110 Phase 2 Slice 5: 获取 K 线 (broker-aware).

Source

pub fn get_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>

Source§

impl QotCache

Source

pub fn register_cold_cache_waiter(&self, wait_key: &str) -> Arc<Notify>

v1.4.110 codex Phase 3 Slice 6c: 注册 cold-cache wait waiter.

返已存在或新建的 Arc<Notify>. handler 调:

  1. register_cold_cache_waiter("91_BTCUSDT@b1007:basic") 获 Notify
  2. 主动发 Pull_SubData CMD6824
  3. tokio::time::timeout(Duration::from_secs(3), notify.notified())
  4. get_basic_qot_broker(&key) 读 cache (可能仍 None — 真 timeout)

wait_kind 推荐: "basic" / "orderbook". 不混 sub_type 数字防误唤.

Source

pub fn register_cold_cache_waiter_flight( &self, wait_key: &str, ) -> (Arc<Notify>, bool)

Register a cold-cache waiter and identify the request that created the flight.

The returned bool is true only for the caller that inserted the waiter. Handlers use it to singleflight the active Pull_SubData request while still letting all concurrent callers await the same Notify.

Source

pub fn notify_cold_cache_waiters(&self, wait_key: &str)

v1.4.110 codex Phase 3 Slice 6c: 唤醒指定 cold-cache wait waiter.

push parser update path 调 (update_basic_qot / update_order_book / _broker 变种). 没 waiter → no-op. 有 waiter → notify_waiters() broadcast 给所有 awaiter, 然后 remove (Arc 仍被 awaiter 持有, 自然释放).

Source

pub fn has_cold_cache_waiters(&self) -> bool

Source

pub fn notify_basic_qot_cold_cache_waiters(&self, cache_key: &str)

Source

pub fn notify_order_book_cold_cache_waiters(&self, cache_key: &str)

Source

pub fn notify_odd_lot_order_book_cold_cache_waiters(&self, cache_key: &str)

Source

pub fn notify_ticker_cold_cache_waiters(&self, cache_key: &str)

Source

pub fn cleanup_cold_cache_waiter_if_idle( &self, wait_key: &str, caller_notify: &Arc<Notify>, )

v1.4.110 codex audit Round3 #22: cold-cache wait timeout 后清 idle waiter.

wait_for_basic_cache / wait_for_order_book_cache 3s timeout 仍 cache miss 时调. 若 push 始终没来, notify_cold_cache_waiters 不会触发, entry 会一直留在 cold_cache_waiters map (虽 bounded by distinct wait_key 数, 仍是慢速 leak).

只删 caller 自己注册的那个 entry, 且无其他并发 awaiter 时才删: remove_if closure 在 entry lock 下原子检查两条:

  1. Arc::ptr_eq(stored, caller_notify) — stored 必须就是 caller 当初 register_cold_cache_waiter 拿到的同一 Arc. 防 race: caller timeout 后到本调用之间, 若 push 触发 notify_cold_cache_waiters 删了旧 entry, 另一个 wait_for_* 又 register 建了 entry (不同 Arc), ptr_eq false → 不误删别人的新 entry.
  2. Arc::strong_count(stored) <= 2 — DashMap stored Arc 1 + caller 持有的 caller_notify 1. > 2 表示有其他 wait_for_* 仍 await 同 entry → 保留让它们能被 notify 唤醒.

caller 约定: 必须把 register_cold_cache_waiter 返回的 Arc<Notify> 原样传进来 (caller 全程持有未 drop).

Source§

impl QotCache

Source

pub fn new() -> Self

Source

pub fn replace_spread_tables<I>(&self, tables: I)
where I: IntoIterator<Item = (i32, Vec<CachedSpreadBand>)>,

Replace the whole spread-table cache after a successful CMD6503 pull.

C++ INNData_Qot_Spread::SetSpreadInfo installs a flattened full table snapshot. We use a code-keyed map but preserve the same replacement semantics so stale removed codes do not linger across refreshes.

Source

pub fn spread_value_raw(&self, spread_code: u32, price_raw: i64) -> i64

Return the raw 1e9 fixed-point spread value for a security price.

Mirrors the spread-band selection in NNBiz_Qot_Spread::GetStockSpreadPriceForTade with bUp=true and enTrdMarket=Unknown, which is what quote snapshot / BasicQot push use. Missing table returns 0, matching C++ cache miss falling through with initial nPriceSpread=0 at the API projection layer.

Source

pub fn price_spread_for_raw_price( &self, spread_code: u32, price_raw: i64, ) -> f64

Project BasicQot.priceSpread / SnapshotBasicData.priceSpread.

Source

pub fn price_spread_for_price(&self, spread_code: u32, price: f64) -> f64

Project priceSpread from a floating-point API price.

Source

pub fn set_us_stock_overnight_state(&self, stock_id: u64, is_overnight: bool)

Update C++-style US overnight stock state (stockID -> bool).

Ref: NNData_Qot_USStockOvernight.cpp:21-35 and NNBiz_Qot_USStockState.cpp:180-190.

Source

pub fn is_us_stock_overnight(&self, stock_id: u64) -> bool

Query whether a US stock is currently in overnight trading.

C++ cache miss returns false (NNData_Qot_USStockOvernight.cpp:29-34).

Source

pub fn get_broker_name(&self, broker_id: i64) -> Option<String>

v1.4.106 codex 1140 F7 (P2): 查 broker_id → broker name (中文简称). cache miss → None, 调用方决定 fallback 策略 (push parser 用 Broker#{id} 作 emergency fallback, 但同时 warn-log 提示 dict 未加载).

Source

pub fn get_broker_name_for_app_lang( &self, broker_id: i64, app_lang: i32, ) -> Option<String>

latest C++ GetBrokerName 按 App 语言选择券商简称,并在缺省时 fallback 到 en/tc/sc。Rust 当前 CMD 18008 cache 只保存简称三语字段; 因此这里对齐 C++ 的 abbreviation-first 分支,完整名 fallback 等 cache 结构扩展后再自然接入。

Source

pub fn install_broker_dict(&self, entries: Vec<(i64, CachedBrokerInfo)>)

v1.4.106 codex 1140 F7 (P2): 批量写入 broker dict (CMD 18008 解析后调).

Source

pub fn update_basic_qot(&self, key: &str, qot: CachedBasicQot)

更新基本报价

Source

pub fn get_basic_qot(&self, key: &str) -> Option<CachedBasicQot>

获取基本报价

Source

pub fn update_basic_qot_broker(&self, key: &QotSecurityKey, qot: CachedBasicQot)

v1.4.110 Phase 2 Slice 5: 更新基本报价 (broker-aware).

QotSecurityKey::cache_key() 派生 String key. broker_id=None → 与 update_basic_qot(public_sec_key, ...) 等价; broker_id=Some(N) → 写 独立 cache key "market_code@b{N}" (crypto multi-broker isolation).

Source

pub fn get_basic_qot_broker( &self, key: &QotSecurityKey, ) -> Option<CachedBasicQot>

v1.4.110 Phase 2 Slice 5: 获取基本报价 (broker-aware).

Source

pub fn make_rt_key(sec_key: &str, session: i32) -> String

构造 RT 分时 cache key (v1.4.106 codex 1140 F6).

之前 key 仅 sec_key, RTH/ETH/PRE/AFTER/OVERNIGHT 混桶. 现在 “sec_key:s{session}” 隔离. session 来自 push 解析的 TimeSharingPlans.section_type[0] (RequestSection enum): 0=NORMAL/1=FULL/2=PREMARKET/3=AFTERHOURS/5=OVERNIGHT. GetRT handler 对 C++ 的 Session_ETH/Session_ALL 读取语义做动态拼接, 不依赖额外 aggregate cache 桶。

Source

pub fn make_rt_key_broker(key: &QotSecurityKey, session: i32) -> String

v1.4.110 Phase 2 Slice 5: 构造 RT 分时 cache key (broker-aware).

QotSecurityKey::cache_key() 作 prefix. broker_id=None → 与 make_rt_key(public_sec_key, session) 等价; broker_id=Some(N) → 写 独立 cache key "market_code@b{N}:s{session}".

Source

pub fn update_rt_data_broker( &self, key: &QotSecurityKey, session: i32, rt_data: Vec<CachedTimeShare>, )

v1.4.110 Phase 2 Slice 5: 更新 RT 分时 (broker-aware).

Source

pub fn get_rt_data_broker( &self, key: &QotSecurityKey, session: i32, ) -> Option<Vec<CachedTimeShare>>

v1.4.110 Phase 2 Slice 5: 获取 RT 分时 (broker-aware).

Source

pub fn update_order_book(&self, key: &str, ob: CachedOrderBook)

更新摆盘

Source

pub fn update_order_book_broker( &self, key: &QotSecurityKey, ob: CachedOrderBook, )

v1.4.110 Phase 2 Slice 5: 更新摆盘 (broker-aware).

Source

pub fn update_odd_lot_order_book_broker( &self, key: &QotSecurityKey, ob: CachedOrderBook, )

Update odd-lot order book cache (MY/SG only in C++ 10.7).

Source

pub fn get_order_book_broker( &self, key: &QotSecurityKey, ) -> Option<CachedOrderBook>

v1.4.110 Phase 2 Slice 5: 获取摆盘 (broker-aware).

Source

pub fn get_odd_lot_order_book_broker( &self, key: &QotSecurityKey, ) -> Option<CachedOrderBook>

Get odd-lot order book cache (MY/SG only in C++ 10.7).

Source

pub fn append_tickers(&self, key: &str, new_tickers: Vec<CachedTicker>)

追加逐笔(保留最近 1000 条)

Source

pub fn append_tickers_broker( &self, key: &QotSecurityKey, new_tickers: Vec<CachedTicker>, )

v1.4.110 Phase 2 Slice 5: 追加逐笔 (broker-aware).

Source

pub fn get_tickers_broker( &self, key: &QotSecurityKey, ) -> Option<Vec<CachedTicker>>

v1.4.110 Phase 2 Slice 5: 获取逐笔 (broker-aware).

Source

pub fn update_broker(&self, key: &str, broker: CachedBroker)

更新经纪队列

Source

pub fn get_broker(&self, key: &str) -> Option<CachedBroker>

获取经纪队列

Source

pub fn clear_security(&self, key: &str)

清除指定股票的所有缓存

Source

pub fn clear_security_broker(&self, key: &QotSecurityKey)

v1.4.110 Phase 2 Slice 5: 清除指定股票的所有缓存 (broker-aware).

QotSecurityKey::cache_key() 派生 cache key 字符串. broker_id=None → 与 clear_security(public_sec_key) 等价; broker_id=Some(N) → 只清 该 broker 下的 cache (其他 broker 下同 stock_id 的 cache 保留).

Trait Implementations§

Source§

impl Default for QotCache

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more