quantaxis_rs/
qaposition.rs

1use qifi_rs::Position;
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4
5use crate::market_preset::{CodePreset, MarketPreset};
6use crate::qaorder::QAOrder;
7
8#[derive(Debug, Clone, Deserialize, Serialize)]
9pub struct QA_Frozen {
10    pub amount: f64,
11    pub coeff: f64,
12    pub money: f64,
13}
14impl QA_Frozen {
15    pub fn reset(&mut self) {
16        self.amount = 0.0;
17        self.money = 0.0;
18    }
19}
20
21#[derive(Debug, Clone, Deserialize, Serialize)]
22pub struct QA_Postions {
23    pub preset: CodePreset,
24    pub code: String,
25    pub instrument_id: String,
26    pub user_id: String,
27    pub portfolio_cookie: String,
28    pub username: String,
29    pub position_id: String,
30    pub account_cookie: String,
31    pub frozen: f64,
32    pub name: String,
33    pub spms_id: String,
34    pub oms_id: String,
35    pub market_type: String,
36    pub exchange_id: String,
37    pub lastupdatetime: String,
38    //# 持仓量
39    pub volume_long_today: f64,
40    pub volume_long_his: f64,
41    pub volume_short_today: f64,
42    pub volume_short_his: f64,
43    //# 平仓委托冻结(未成交)
44    pub volume_long_frozen_today: f64,
45    pub volume_long_frozen_his: f64,
46
47    pub volume_short_frozen_today: f64,
48    pub volume_short_frozen_his: f64,
49
50    //# 保证金
51    pub margin_long: f64,
52    pub margin_short: f64,
53    //# 持仓字段
54    pub position_price_long: f64,
55    pub position_cost_long: f64,
56    pub position_price_short: f64,
57    pub position_cost_short: f64,
58    //# 平仓字段
59    pub open_price_long: f64,
60    pub open_cost_long: f64,
61    pub open_price_short: f64,
62    pub open_cost_short: f64,
63
64    pub lastest_price: f64,
65    pub lastest_datetime: String,
66}
67
68pub fn adjust_market(code: &str) -> String {
69    let re = Regex::new(r"[a-zA-z]+").unwrap();
70    let res = re.captures(code);
71    if res.is_some() {
72        "future_cn".to_string()
73    } else {
74        "stock_cn".to_string()
75    }
76}
77
78impl QA_Postions {
79    pub(crate) fn message(&self) {
80        println!("{}", self.code.clone());
81        let u = serde_json::to_string(self).unwrap();
82        println!("{:#?}", u);
83    }
84    pub fn new(
85        code: String,
86        user_id: String,
87        username: String,
88        account_cookie: String,
89        portfolio_cookie: String,
90    ) -> Self {
91        let mut preset: CodePreset = MarketPreset::new().get(code.as_ref());
92
93        let pos = Self {
94            preset,
95            code: code.clone(),
96            instrument_id: code.clone(),
97            user_id,
98            portfolio_cookie,
99            username,
100            position_id: "".to_string(),
101            account_cookie,
102            frozen: 0.0,
103            name: "".to_string(),
104            spms_id: "".to_string(),
105            oms_id: "".to_string(),
106            market_type: adjust_market(&code),
107            exchange_id: "".to_string(),
108            lastupdatetime: "".to_string(),
109            volume_long_today: 0.0,
110            volume_long_his: 0.0,
111
112            volume_short_today: 0.0,
113            volume_short_his: 0.0,
114
115            volume_long_frozen_today: 0.0,
116            volume_long_frozen_his: 0.0,
117
118            volume_short_frozen_today: 0.0,
119            volume_short_frozen_his: 0.0,
120
121            margin_long: 0.0,
122            margin_short: 0.0,
123
124            position_price_long: 0.0,
125            position_cost_long: 0.0,
126
127            position_price_short: 0.0,
128            position_cost_short: 0.0,
129
130            open_price_long: 0.0,
131            open_cost_long: 0.0,
132
133            open_price_short: 0.0,
134            open_cost_short: 0.0,
135            lastest_price: 0.0,
136            lastest_datetime: "".to_string(),
137        };
138        pos
139    }
140
141    pub fn new_with_inithold(
142        code: String,
143        user_id: String,
144        username: String,
145        account_cookie: String,
146        portfolio_cookie: String,
147        volume_long_today: f64,
148        volume_long_his: f64,
149        volume_short_today: f64,
150        volume_short_his: f64,
151        open_price_long: f64,
152        open_price_short: f64,
153    ) -> Self {
154        let mut preset: CodePreset = MarketPreset::new().get(code.as_ref());
155
156        let mut pos = Self {
157            preset,
158            code: code.clone(),
159            instrument_id: code.clone(),
160            user_id,
161            portfolio_cookie,
162            username,
163            position_id: "".to_string(),
164            account_cookie,
165            frozen: 0.0,
166            name: "".to_string(),
167            spms_id: "".to_string(),
168            oms_id: "".to_string(),
169            market_type: adjust_market(&code),
170            exchange_id: "".to_string(),
171            lastupdatetime: "".to_string(),
172            volume_long_today: 0.0,
173            volume_long_his: 0.0,
174
175            volume_short_today: 0.0,
176            volume_short_his: 0.0,
177
178            volume_long_frozen_today: 0.0,
179            volume_long_frozen_his: 0.0,
180
181            volume_short_frozen_today: 0.0,
182            volume_short_frozen_his: 0.0,
183
184            margin_long: 0.0,
185            margin_short: 0.0,
186
187            position_price_long: 0.0,
188            position_cost_long: 0.0,
189
190            position_price_short: 0.0,
191            position_cost_short: 0.0,
192
193            open_price_long: 0.0,
194            open_cost_long: 0.0,
195
196            open_price_short: 0.0,
197            open_cost_short: 0.0,
198            lastest_price: 0.0,
199            lastest_datetime: "".to_string(),
200        };
201        // println!(
202        //     "his {:#?}/ today {:#?}/ openprice {:#?}",
203        //     volume_long_his, volume_long_today, open_price_long
204        // );
205        // println!(
206        //     "his {:#?}/ today {:#?}/ openprice {:#?}",
207        //     volume_short_his, volume_short_today, open_price_short
208        // );
209
210        if volume_long_his > 0.0 {
211            pos.update_pos(open_price_long, volume_long_his, 1);
212            pos.settle();
213        }
214        if volume_short_his > 0.0 {
215            pos.update_pos(open_price_short, volume_short_his, -2);
216            pos.settle();
217        }
218
219        if volume_long_today > 0.0 {
220            pos.update_pos(open_price_long, volume_long_today, 1);
221        }
222        if volume_short_today > 0.0 {
223            pos.update_pos(open_price_short, volume_short_today, -2);
224        }
225        pos
226    }
227
228    pub fn get_price_tick(&mut self) -> f64 {
229        self.preset.price_tick
230    }
231
232    pub fn margin(&mut self) -> f64 {
233        self.margin_long + self.margin_short
234    }
235
236    pub fn settle(&mut self) {
237        self.volume_long_his += (self.volume_long_today + self.volume_long_frozen_today);
238        self.volume_long_today = 0.0;
239        self.volume_long_frozen_today = 0.0;
240        self.volume_short_his += (self.volume_short_today + self.volume_short_frozen_today);
241        self.volume_short_today = 0.0;
242        self.volume_short_frozen_today = 0.0;
243    }
244
245    pub async fn settle_async(&mut self) {
246        self.volume_long_his += (self.volume_long_today + self.volume_long_frozen_today);
247        self.volume_long_today = 0.0;
248        self.volume_long_frozen_today = 0.0;
249        self.volume_short_his += (self.volume_short_today + self.volume_short_frozen_today);
250        self.volume_short_today = 0.0;
251        self.volume_short_frozen_today = 0.0;
252    }
253
254    pub fn on_price_change(&mut self, price: f64, datetime: String) {
255        // 当行情变化时候 要更新计算持仓
256        self.lastest_price = price;
257        self.lastest_datetime = datetime;
258    }
259
260    pub fn float_profit_long(&mut self) -> f64 {
261        self.lastest_price * self.volume_long() * self.preset.unit_table as f64
262            - self.open_cost_long
263    }
264
265    pub fn float_profit_short(&mut self) -> f64 {
266        self.open_cost_short
267            - self.lastest_price * self.volume_short() * self.preset.unit_table as f64
268    }
269
270    pub fn float_profit(&mut self) -> f64 {
271        self.float_profit_long() + self.float_profit_short()
272    }
273
274    pub fn get_qifi_position(&mut self) -> Position {
275        Position {
276            user_id: self.user_id.clone(),
277            exchange_id: self.exchange_id.clone(),
278            instrument_id: self.instrument_id.clone(),
279            volume_long_today: self.volume_long_today.clone(),
280            volume_long_his: self.volume_long_his.clone(),
281            volume_long: self.volume_long(),
282            volume_long_frozen_today: self.volume_long_frozen_today.clone(),
283            volume_long_frozen_his: self.volume_long_frozen_his.clone(),
284            volume_long_frozen: self.volume_long_frozen(),
285            volume_short_today: self.volume_short_today.clone(),
286            volume_short_his: self.volume_short_his.clone(),
287            volume_short: self.volume_short(),
288            volume_short_frozen_today: self.volume_short_frozen_today.clone(),
289            volume_short_frozen_his: self.volume_short_frozen_his.clone(),
290            volume_short_frozen: self.volume_short_frozen(),
291            volume_long_yd: 0.0,
292            volume_short_yd: 0.0,
293            pos_long_his: self.volume_long_his.clone(),
294            pos_long_today: self.volume_long_today.clone(),
295            pos_short_his: self.volume_short_his.clone(),
296            pos_short_today: self.volume_short_today.clone(),
297            open_price_long: self.open_price_long.clone(),
298            open_price_short: self.open_price_short.clone(),
299            open_cost_long: self.open_cost_long.clone(),
300            open_cost_short: self.open_cost_short.clone(),
301            position_price_long: self.position_price_long.clone(),
302            position_price_short: self.position_price_short.clone(),
303            position_cost_long: self.position_cost_long.clone(),
304            position_cost_short: self.position_cost_short.clone(),
305            last_price: self.lastest_price.clone(),
306            float_profit_long: self.float_profit_long(),
307            float_profit_short: self.float_profit_short(),
308            float_profit: self.float_profit(),
309            position_profit_long: self.position_profit_long(),
310            position_profit_short: self.position_profit_short(),
311            position_profit: self.position_profit(),
312            margin_long: self.margin_long.clone(),
313            margin_short: self.margin_short.clone(),
314            margin: self.margin(),
315        }
316    }
317
318    pub fn position_profit_long(&mut self) -> f64 {
319        self.lastest_price * self.volume_long() * self.preset.unit_table as f64
320            - self.position_cost_long
321    }
322
323    pub fn position_profit_short(&mut self) -> f64 {
324        self.position_cost_short
325            - self.lastest_price * self.volume_short() * self.preset.unit_table as f64
326    }
327    pub fn position_profit(&mut self) -> f64 {
328        self.position_profit_long() + self.position_profit_short()
329    }
330
331    pub fn volume_long(&mut self) -> f64 {
332        self.volume_long_today + self.volume_long_his + self.volume_long_frozen()
333    }
334    pub fn volume_short(&mut self) -> f64 {
335        self.volume_short_his + self.volume_short_today + self.volume_short_frozen()
336    }
337
338    pub fn volume_long_frozen(&mut self) -> f64 {
339        self.volume_long_frozen_his + self.volume_long_frozen_today
340    }
341    pub fn volume_short_frozen(&mut self) -> f64 {
342        self.volume_short_frozen_his + self.volume_short_frozen_today
343    }
344
345    pub fn update_pos(&mut self, price: f64, amount: f64, towards: i32) -> (f64, f64) {
346        // when update_pos // calc commission fee
347        let temp_cost = self.preset.calc_marketvalue(price, amount);
348        let mut margin_value = temp_cost * self.preset.buy_frozen_coeff;
349        self.lastest_price = price;
350        //self.on_price_change(price.clone());
351        let mut profit = 0.0;
352        match towards {
353            // 当日买入计入volume long frozen
354            1 => {
355                // buy open logic
356                self.margin_long += margin_value;
357                self.open_price_long = (self.open_price_long * self.volume_long() + price * amount)
358                    / (self.volume_long() + amount);
359                self.position_price_long = self.open_price_long;
360                self.volume_long_today += amount;
361                self.open_cost_long += temp_cost;
362                self.position_cost_long += temp_cost;
363            }
364            2 => {
365                // buy open logic
366                self.margin_long += margin_value;
367                self.open_price_long = (self.open_price_long * self.volume_long() + price * amount)
368                    / (self.volume_long() + amount);
369                self.position_price_long = self.open_price_long;
370                self.volume_long_today += amount;
371                self.open_cost_long += temp_cost;
372                self.position_cost_long += temp_cost;
373            }
374            -2 => {
375                // sell open logic
376                self.margin_short += margin_value;
377                self.open_price_short = (self.open_price_short * self.volume_short()
378                    + price * amount)
379                    / (self.volume_short() + amount);
380                self.position_price_short = self.open_price_short;
381                self.volume_short_today += amount;
382                self.open_cost_short += temp_cost;
383                self.position_cost_short += temp_cost;
384            }
385            3 => {
386                //self.volume_short_today -= amount;
387                // 有昨仓先平昨仓
388
389                let volume_short = self.volume_short();
390                self.position_cost_short =
391                    self.position_cost_short * (volume_short - amount) / volume_short;
392                self.open_cost_short =
393                    self.open_cost_short * (volume_short - amount) / volume_short;
394
395                self.volume_short_frozen_today -= amount;
396
397                //println!("amount  {},position_price_short {}", amount, self.position_price_short);
398
399                //self.preset.print();
400
401                margin_value = -1.0
402                    * (self.position_price_short
403                    * amount
404                    * self.preset.sell_frozen_coeff
405                    * self.preset.unit_table as f64);
406
407                //println!("BUY CLOSE XX MV{:#?}", margin_value);
408
409                profit =
410                    (self.position_price_short - price) * amount * self.preset.unit_table as f64;
411                self.margin_short += margin_value;
412            }
413            -1 => {
414                // sell open logic
415                if amount <= self.volume_long_his {
416                    let volume_long = self.volume_long();
417
418                    self.position_cost_long =
419                        self.position_cost_long * (volume_long - amount) / volume_long;
420                    self.open_cost_long =
421                        self.open_cost_long * (volume_long - amount) / volume_long;
422
423                    self.volume_long_frozen_today -= amount;
424                    margin_value = -1.0
425                        * (self.position_price_long
426                        * amount
427                        * self.preset.unit_table as f64
428                        * self.preset.buy_frozen_coeff);
429                    profit =
430                        (price - self.position_price_long) * amount * self.preset.unit_table as f64;
431                    self.margin_long += margin_value;
432                } else {}
433            }
434            -3 => {
435                //self.volume_long_today -= amount;
436
437                let volume_long = self.volume_long();
438                self.position_cost_long =
439                    self.position_cost_long * (volume_long - amount) / volume_long;
440                self.open_cost_long = self.open_cost_long * (volume_long - amount) / volume_long;
441
442                self.volume_long_frozen_today -= amount;
443                margin_value = -1.0
444                    * (self.position_price_long
445                    * amount
446                    * self.preset.unit_table as f64
447                    * self.preset.buy_frozen_coeff);
448                profit =
449                    (price - self.position_price_long) * amount * self.preset.unit_table as f64;
450                self.margin_long += margin_value;
451            }
452            _ => {}
453        }
454        (margin_value, profit)
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use super::*;
461
462    #[test]
463    fn test_new_future() {
464        // create a new account
465        // 一个期货合约
466        let mut pos = QA_Postions::new(
467            "rb2005".to_string(),
468            "test".to_string(),
469            "test_username".to_string(),
470            "test_accountcookie".to_string(),
471            "test_portfolio".to_string(),
472        );
473        pos.message();
474        assert_eq!(pos.market_type, "future_cn")
475    }
476
477    #[test]
478    fn test_new_stock() {
479        // create a new account
480        // 一个股票合约
481        let mut pos = QA_Postions::new(
482            "000001".to_string(),
483            "test".to_string(),
484            "test_username".to_string(),
485            "test_accountcookie".to_string(),
486            "test_portfolio".to_string(),
487        );
488        pos.message();
489
490
491        println!("{:#?}", pos.preset);
492        assert_eq!(pos.market_type, "stock_cn")
493    }
494
495    #[test]
496    fn test_new_with_inithold() {
497        let mut b = QA_Postions::new_with_inithold(
498            "rb2010".to_string(),
499            "test".to_string(),
500            "test".to_string(),
501            "test".to_string(),
502            "test".to_string(),
503            0.0,
504            0.0,
505            1.0,
506            0.0,
507            0.0,
508            3281.0,
509        );
510        b.on_price_change(3294.0, "2020-04-09 13:34:00".to_string());
511        println!("{:#?}", b.get_qifi_position());
512    }
513
514    #[test]
515    fn test_re() {
516        // 基于re模块自动识别  adjust_market 函数
517        let a = adjust_market("000001");
518        assert_eq!("stock_cn", &a);
519
520        let b = adjust_market("RB2005");
521        assert_eq!("future_cn", &b);
522    }
523
524    #[test]
525    fn test_receivedeal() {
526        // create a new account
527        // 测试接受到一个成交单的情况
528        let mut pos = QA_Postions::new(
529            "rb2005".to_string(),
530            "test".to_string(),
531            "test_username".to_string(),
532            "test_accountcookie".to_string(),
533            "test_portfolio".to_string(),
534        );
535        pos.update_pos(3600.0, 10.0, 2); //buy open
536
537        assert_eq!(10.0, pos.volume_long());
538    }
539
540    #[test]
541    fn test_stock_receivedeal() {
542        // create a new account
543        // 股票成交单
544        let mut pos = QA_Postions::new(
545            "000001".to_string(),
546            "test".to_string(),
547            "test_username".to_string(),
548            "test_accountcookie".to_string(),
549            "test_portfolio".to_string(),
550        );
551        pos.update_pos(36.0, 10000.0, 1); //buy open
552
553        assert_eq!(10000.0, pos.volume_long());
554    }
555
556    #[test]
557    fn test_settle() {
558        // create a new account
559        // 股票成交单
560        let mut pos = QA_Postions::new(
561            "000001".to_string(),
562            "test".to_string(),
563            "test_username".to_string(),
564            "test_accountcookie".to_string(),
565            "test_portfolio".to_string(),
566        );
567        pos.update_pos(36.0, 10000.0, 1); //buy open
568
569        assert_eq!(0.0, pos.volume_long_his);
570        assert_eq!(10000.0, pos.volume_long_today);
571
572        pos.settle();
573
574        assert_eq!(10000.0, pos.volume_long());
575        assert_eq!(10000.0, pos.volume_long_his);
576    }
577
578    #[test]
579    fn test_pricetick() {
580        // create a new account
581        // 一个期货合约
582        let mut pos = QA_Postions::new(
583            "rb2005".to_string(),
584            "test".to_string(),
585            "test_username".to_string(),
586            "test_accountcookie".to_string(),
587            "test_portfolio".to_string(),
588        );
589        pos.message();
590        assert_eq!(pos.get_price_tick(), 1.0)
591    }
592
593    #[test]
594    fn test_onpricechange() {
595        // create a new account
596        // 测试收到一条新的行情的时候的计算
597        let mut pos = QA_Postions::new(
598            "rb2005".to_string(),
599            "test".to_string(),
600            "test_username".to_string(),
601            "test_accountcookie".to_string(),
602            "test_portfolio".to_string(),
603        );
604        pos.update_pos(3600.0, 10.0, 2); //buy open
605
606        assert_eq!(10.0, pos.volume_long());
607        pos.on_price_change(3605.0, "2020-02-20 09:55:00".to_string());
608        println!("float profit{}", pos.float_profit());
609
610        assert_eq!(500.0, pos.float_profit_long());
611        assert_eq!(500.0, pos.float_profit());
612        pos.on_price_change(3589.0, "2020-02-20 13:55:00".to_string());
613        println!("float profit{}", pos.float_profit());
614        assert_eq!(-1100.0, pos.float_profit_long());
615        pos.update_pos(3585.0, 10.0, -2); //sell open
616
617        assert_eq!(-1500.0, pos.float_profit_long());
618        assert_eq!(0.0, pos.float_profit_short());
619
620        pos.on_price_change(3605.0, "2020-02-20 09:55:00".to_string());
621        println!("float profit{}", pos.float_profit());
622
623        assert_eq!(-2000.0, pos.float_profit_short());
624        assert_eq!(500.0, pos.float_profit_long());
625
626        pos.on_price_change(3589.0, "2020-02-20 13:55:00".to_string());
627        println!("float profit{}", pos.float_profit());
628
629        assert_eq!(-400.0, pos.float_profit_short());
630        assert_eq!(-1100.0, pos.float_profit_long());
631    }
632}