Skip to main content

futu_cache/qot_cache/
spread.rs

1/// C++ `INNData_Qot_Spread` cached price-grid row.
2///
3/// Backend CMD6503 uses 1e9 fixed-point integers for `price_from` /
4/// `price_to` / `value`. `QotRealTimeData::ParsePrice` and
5/// `APIServer_Qot_StockSnapshot` pass current price in the same scale to
6/// `INNBiz_Qot_Spread::AdjustPrice`, then expose `priceSpread = value / 1e9`.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct CachedSpreadBand {
9    pub price_from: i64,
10    pub price_to: i64,
11    pub value: i64,
12}
13
14pub(super) fn spread_value_raw_from_bands(bands: &[CachedSpreadBand], price_raw: i64) -> i64 {
15    let mut min_price = 0;
16    let mut max_price = 0;
17    let mut min_spread = 0;
18    let mut max_spread = 0;
19    let mut selected = None;
20    for band in bands {
21        if price_raw >= band.price_from && price_raw < band.price_to {
22            selected = Some(band.value);
23            break;
24        }
25        if min_price == 0 || band.price_from < min_price {
26            min_price = band.price_from;
27            min_spread = band.value;
28        }
29        if max_price == 0 || band.price_to > max_price {
30            max_price = band.price_to;
31            max_spread = band.value;
32        }
33    }
34
35    let mut value = selected.unwrap_or({
36        if price_raw <= min_price {
37            min_spread
38        } else if price_raw >= max_price {
39            max_spread
40        } else {
41            0
42        }
43    });
44
45    // C++ quote paths call AdjustPrice with `enTrdMarket=Unknown`. That falls
46    // into the non-US/non-futures branch and truncates the backend 1e9 quote
47    // spread to the trade grid by `(spread / 1e6) * 1e6`; zero then falls back
48    // to 0.01.
49    // Ref: NNBiz_Qot_Spread.cpp:337-344 and 164.
50    value = (value / 1_000_000) * 1_000_000;
51    if value == 0 { 10_000_000 } else { value }
52}