futu_cache/
qot_right.rs

1// 行情权限缓存
2// 存储从后端 CMD 6024 获取的行情权限和 API 额度信息
3// 对应 C++ INNData_Qot_Right + INNData_APIInterLimit
4
5use parking_lot::RwLock;
6
7/// 后端 CltQut 值 → API QotRight 值的转换
8///
9/// API QotRight 枚举: 0=Unknow, 1=Bmp, 2=Level1, 3=Level2, 4=SF, 5=No
10///
11/// C++ QotRight_NNToAPI() 有多个重载,对齐 C++ APIServer_Inner_API.cpp:
12/// - HK: CltQut 1=LV2, 2=BMP, 3=LV1, 4=SF → API 3, 1, 2, 4
13/// - US: CltQut 1=LV1, 2=无权限 → API 2, 5(No)
14/// - CN: CltQut 1=LV1, 2=LV2, 3=无权限 → API 2, 3, 5(No)
15/// - SG/JP: CltQut 1=实时(LV2), 2=延时(LV1) → API 3, 2, else 5(No)
16/// - US Future: CltQut 0=No, 1=RT(LV2), 2=Delay(BMP), 3=LV2, 4=LV1 → API 5, 3, 1, 3, 2
17/// - US Option: CltQut 0=Delay(BMP), 1=RT(LV1) → API 1, 2
18fn hk_clt_to_api(clt: u32) -> i32 {
19    match clt {
20        1 => 3, // CLT_QUT_HK_LV2 → QotRight_Level2
21        2 => 1, // CLT_QUT_HK_BMP → QotRight_Bmp
22        3 => 2, // CLT_QUT_HK_LV1 → QotRight_Level1
23        4 => 4, // CLT_QUT_HK_SF → QotRight_SF
24        _ => 0, // QotRight_Unknow
25    }
26}
27
28fn us_clt_to_api(clt: u32) -> i32 {
29    match clt {
30        1 => 2, // CLT_QUT_US_LV1 → QotRight_Level1
31        2 => 5, // CLT_QUT_US_BMP → QotRight_No (无实时权限)
32        _ => 5, // QotRight_No
33    }
34}
35
36fn cn_clt_to_api(clt: u32) -> i32 {
37    match clt {
38        1 => 2, // CLT_QUT_CN_LV1 → QotRight_Level1
39        2 => 3, // CLT_QUT_CN_LV2 → QotRight_Level2
40        3 => 5, // CLT_QUT_CN_BMP → QotRight_No (无实时权限)
41        _ => 5, // QotRight_No
42    }
43}
44
45fn other_clt_to_api(clt: u32) -> i32 {
46    // SG/JP 等市场: C++ QotRight_NNToAPI_OtherFuture
47    // CltQut: 1=实时(LV2), 2=延时(LV1), 其他=无权限
48    match clt {
49        1 => 3, // CLT_QUT_OTHER_RT → QotRight_Level2
50        2 => 2, // CLT_QUT_OTHER_DELAY → QotRight_Level1
51        _ => 5, // QotRight_No
52    }
53}
54
55/// US 期货 CltQut → API QotRight
56/// C++ QotRight_NNToAPI_USFuture:
57/// 0=None→No, 1=RT→Level2, 2=Delay→BMP, 3=LV2→Level2, 4=LV1→Level1
58fn us_future_clt_to_api(clt: u32) -> i32 {
59    match clt {
60        0 => 5, // None → QotRight_No
61        1 => 3, // RT → QotRight_Level2
62        2 => 1, // Delay → QotRight_Bmp
63        3 => 3, // LV2 → QotRight_Level2
64        4 => 2, // LV1 → QotRight_Level1
65        _ => 5, // QotRight_No
66    }
67}
68
69/// US 期权 CltQut → API QotRight
70fn us_option_clt_to_api(clt: u32) -> i32 {
71    match clt {
72        0 => 1, // Delay → BMP
73        1 => 2, // RT → Level1
74        _ => 1,
75    }
76}
77
78/// 行情权限数据
79#[derive(Debug, Clone)]
80pub struct QotRightData {
81    // HK
82    pub hk_qot_right: i32,
83    // US
84    pub us_qot_right: i32,
85    // CN (上证/深证独立)
86    pub sh_qot_right: i32,
87    pub sz_qot_right: i32,
88    // HK 衍生品
89    pub hk_option_qot_right: i32,
90    pub hk_future_qot_right: i32,
91    // US 衍生品
92    pub has_us_option_qot_right: bool,
93    pub us_option_qot_right: i32,
94    pub us_index_qot_right: i32,
95    pub us_otc_qot_right: i32,
96    // US 期货细分
97    pub us_cme_future_qot_right: i32,
98    pub us_cbot_future_qot_right: i32,
99    pub us_nymex_future_qot_right: i32,
100    pub us_comex_future_qot_right: i32,
101    pub us_cboe_future_qot_right: i32,
102    // 其他市场
103    pub sg_future_qot_right: i32,
104    pub jp_future_qot_right: i32,
105    // API 额度
106    pub sub_quota: i32,
107    pub history_kl_quota: i32,
108}
109
110impl Default for QotRightData {
111    fn default() -> Self {
112        let bmp = 1; // QotRight_Bmp
113        Self {
114            hk_qot_right: bmp,
115            us_qot_right: bmp,
116            sh_qot_right: bmp,
117            sz_qot_right: bmp,
118            hk_option_qot_right: bmp,
119            hk_future_qot_right: bmp,
120            has_us_option_qot_right: true,
121            us_option_qot_right: bmp,
122            us_index_qot_right: bmp,
123            us_otc_qot_right: bmp,
124            us_cme_future_qot_right: bmp,
125            us_cbot_future_qot_right: bmp,
126            us_nymex_future_qot_right: bmp,
127            us_comex_future_qot_right: bmp,
128            us_cboe_future_qot_right: bmp,
129            sg_future_qot_right: bmp,
130            jp_future_qot_right: bmp,
131            sub_quota: 4000,
132            history_kl_quota: 100,
133        }
134    }
135}
136
137/// 行情权限缓存
138pub struct QotRightCache {
139    data: RwLock<QotRightData>,
140}
141
142impl Default for QotRightCache {
143    fn default() -> Self {
144        Self::new()
145    }
146}
147
148impl QotRightCache {
149    pub fn new() -> Self {
150        Self {
151            data: RwLock::new(QotRightData::default()),
152        }
153    }
154
155    /// 获取当前权限数据的快照
156    pub fn get(&self) -> QotRightData {
157        self.data.read().clone()
158    }
159
160    /// 从后端 CMD 6024 响应更新权限数据
161    ///
162    /// 对齐 C++:
163    /// - ParseQotRightReply_HK / _US / _CN / _HKOption / _HKFuture 等
164    /// - ParseQotRightReply_APIQuota
165    #[allow(clippy::too_many_arguments)]
166    pub fn update_from_backend(
167        &self,
168        hk_got: Option<u32>,
169        us_got: Option<u32>,
170        cn_got: Option<u32>,
171        sh_auth: Option<u32>,
172        sz_auth: Option<u32>,
173        hk_option: Option<u32>,
174        hk_future: Option<u32>,
175        us_option: Option<u32>,
176        us_future_cme_cboe: Option<u32>,
177        us_future_detail: Option<(u32, u32, u32, u32)>, // cme, cbot, nymex, comex
178        sg_future: Option<u32>,
179        jp_future: Option<u32>,
180        sub_limit: Option<u32>,
181        kl_limit: Option<u32>,
182    ) {
183        let mut d = self.data.write();
184
185        // HK 行情权限
186        if let Some(v) = hk_got {
187            d.hk_qot_right = hk_clt_to_api(v);
188        }
189
190        // US 行情权限
191        if let Some(v) = us_got {
192            d.us_qot_right = us_clt_to_api(v);
193            // US Index 和 OTC 跟随 US 基础权限
194            d.us_index_qot_right = us_clt_to_api(v);
195            d.us_otc_qot_right = us_clt_to_api(v);
196        }
197
198        // CN 行情权限 (细分上证/深证)
199        if let Some(v) = sh_auth {
200            d.sh_qot_right = cn_clt_to_api(v);
201        } else if let Some(v) = cn_got {
202            d.sh_qot_right = cn_clt_to_api(v);
203        }
204        if let Some(v) = sz_auth {
205            d.sz_qot_right = cn_clt_to_api(v);
206        } else if let Some(v) = cn_got {
207            d.sz_qot_right = cn_clt_to_api(v);
208        }
209
210        // HK 衍生品
211        if let Some(v) = hk_option {
212            d.hk_option_qot_right = hk_clt_to_api(v);
213        }
214        if let Some(v) = hk_future {
215            d.hk_future_qot_right = hk_clt_to_api(v);
216        }
217
218        // US 期权
219        if let Some(v) = us_option {
220            d.us_option_qot_right = us_option_clt_to_api(v);
221            d.has_us_option_qot_right = v != 0; // 0=delay → false, 1=RT → true
222        }
223
224        // US 期货 (CME+CBOE 统一字段)
225        if let Some(v) = us_future_cme_cboe {
226            let api_right = us_future_clt_to_api(v);
227            d.us_cme_future_qot_right = api_right;
228            d.us_cboe_future_qot_right = api_right;
229        }
230
231        // US 期货细分 (新版字段覆盖旧值)
232        if let Some((cme, cbot, nymex, comex)) = us_future_detail {
233            if cme > 0 {
234                d.us_cme_future_qot_right = us_future_clt_to_api(cme);
235            }
236            if cbot > 0 {
237                d.us_cbot_future_qot_right = us_future_clt_to_api(cbot);
238            }
239            if nymex > 0 {
240                d.us_nymex_future_qot_right = us_future_clt_to_api(nymex);
241            }
242            if comex > 0 {
243                d.us_comex_future_qot_right = us_future_clt_to_api(comex);
244            }
245        }
246
247        // 其他市场期货
248        if let Some(v) = sg_future {
249            d.sg_future_qot_right = other_clt_to_api(v);
250        }
251        if let Some(v) = jp_future {
252            d.jp_future_qot_right = other_clt_to_api(v);
253        }
254
255        // API 额度
256        if let Some(v) = sub_limit {
257            if v > 0 {
258                d.sub_quota = v as i32;
259            }
260        }
261        if let Some(v) = kl_limit {
262            if v > 0 {
263                d.history_kl_quota = v as i32;
264            }
265        }
266    }
267}