quantaxis_rs/
qaaccount.rs

1use std::borrow::Borrow;
2use std::collections::{BTreeMap, HashMap};
3use std::error::Error;
4use std::io;
5
6use chrono::format::ParseError;
7use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
8use csv;
9use qifi_rs::{Account, Order, Position, Trade, QIFI};
10use serde::{Deserialize, Serialize};
11use serde_json::to_string;
12use uuid::v1::{Context, Timestamp};
13use uuid::Uuid;
14use log::{info,error,warn};
15use crate::market_preset::{CodePreset, MarketPreset};
16use crate::qaorder::QAOrder;
17use crate::qaposition;
18use crate::qaposition::{QA_Frozen, QA_Postions};
19use crate::trade_date::QATradeDate;
20use crate::transaction;
21use crate::transaction::QATransaction;
22
23#[derive(Debug, Clone, Deserialize, Serialize)]
24pub struct QAAccountSlice {
25    pub datetime: String,
26    pub cash: f64,
27    pub accounts: account,
28    pub positions: HashMap<String, QA_Postions>,
29}
30
31#[derive(Debug, Clone, Deserialize, Serialize)]
32pub struct QAMOMSlice {
33    pub datetime: String,
34    pub user_id: String,
35    // 用户号 兼容diff协议, ==> 实盘则为具体账户号
36    pub pre_balance: f64,
37    // 上一个交易日的结算权益
38    pub close_profit: f64,
39    // 平仓盈亏
40    pub commission: f64,
41    pub position_profit: f64,
42    // 持仓盈亏
43    pub float_profit: f64,
44    // 浮动盈亏
45    pub balance: f64,
46    // 当前权益
47    pub margin: f64,
48    // 保证金
49    // 冻结附加费用
50    pub available: f64,
51    // 可用资金
52    pub risk_ratio: f64, // 风险度
53}
54
55#[derive(Debug, Clone, Deserialize, Serialize)]
56pub struct account_info {
57    pub datetime: String,
58    pub balance: f64,
59    pub account_cookie: String,
60}
61
62#[derive(Debug, Clone, Deserialize, Serialize)]
63pub struct account {
64    pub user_id: String,
65    // 用户号 兼容diff协议, ==> 实盘则为具体账户号
66    pub currency: String,
67    // 货币属性 兼容diff协议
68    pub pre_balance: f64,
69    // 上一个交易日的结算权益
70    pub deposit: f64,
71    // 今日转入资金
72    pub withdraw: f64,
73    // 今日转出资金
74    pub WithdrawQuota: f64,
75    // 当前可取字段(QIFI 独有)
76    pub close_profit: f64,
77    // 平仓盈亏
78    pub commission: f64,
79    // 手续费
80    pub premium: f64,
81    // 附加费
82    pub static_balance: f64,
83    // 静态权益(一般= pre_balance)
84    pub position_profit: f64,
85    // 持仓盈亏
86    pub float_profit: f64,
87    // 浮动盈亏
88    pub balance: f64,
89    // 当前权益
90    pub margin: f64,
91    // 保证金
92    pub frozen_margin: f64,
93    // 冻结保证金
94    pub frozen_commission: f64,
95    // 冻结手续费
96    pub frozen_premium: f64,
97    // 冻结附加费用
98    pub available: f64,
99    // 可用资金
100    pub risk_ratio: f64, // 风险度
101}
102
103#[warn(non_camel_case_types)]
104#[derive(Debug, Clone)]
105pub struct QA_Account {
106    init_cash: f64,
107    init_hold: HashMap<String, QA_Postions>,
108
109    allow_t0: bool,
110    allow_sellopen: bool,
111    allow_margin: bool,
112
113    auto_reload: bool,
114    market_preset: MarketPreset,
115    time: String,
116    pub events: HashMap<String, String>,
117    pub accounts: account,
118    pub cash: Vec<f64>,
119    pub money: f64,
120    pub trades: HashMap<String, QATransaction>,
121    pub hold: HashMap<String, QA_Postions>,
122    pub frozen: HashMap<String, QA_Frozen>,
123    pub dailyassets: HashMap<String, QAAccountSlice>,
124    pub history: Vec<transaction::QATransaction>,
125    pub account_cookie: String,
126    pub portfolio_cookie: String,
127    pub user_cookie: String,
128    pub dailytrades: BTreeMap<String, Trade>,
129    pub dailyorders: BTreeMap<String, Order>,
130    environment: String,
131    event_id: i32,
132    commission_ratio: f64,
133    // 手续费率
134    tax_ratio: f64,        // tax for qaaccount
135}
136
137impl QA_Account {
138    pub fn new(
139        account_cookie: &str,
140        portfolio_cookie: &str,
141        user_cookie: &str,
142        init_cash: f64,
143        auto_reload: bool,
144        environment: &str,
145    ) -> Self {
146        let mut acc = Self {
147            init_cash,
148            init_hold: HashMap::new(),
149            allow_t0: false,
150            allow_sellopen: false,
151            allow_margin: false,
152            market_preset: MarketPreset::new(),
153            auto_reload,
154            time: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
155            events: HashMap::new(),
156            accounts: account {
157                user_id: account_cookie.to_string(),
158                currency: "CNY".to_string(),
159                pre_balance: init_cash.clone(),
160                deposit: 0.0,
161                withdraw: 0.0,
162                WithdrawQuota: init_cash.clone(),
163                close_profit: 0.0,
164                commission: 0.0,
165                premium: 0.0,
166                static_balance: init_cash.clone(),
167                position_profit: 0.0,
168                float_profit: 0.0,
169                balance: init_cash.clone(),
170                margin: 0.0,
171                frozen_margin: 0.0,
172                frozen_commission: 0.0,
173                frozen_premium: 0.0,
174                available: init_cash.clone(),
175                risk_ratio: 0.0,
176            },
177            cash: vec![init_cash],
178            money: init_cash,
179            hold: HashMap::new(),
180            trades: HashMap::new(),
181            frozen: HashMap::new(),
182            history: vec![],
183            account_cookie: account_cookie.parse().unwrap(),
184            portfolio_cookie: portfolio_cookie.parse().unwrap(),
185            user_cookie: user_cookie.parse().unwrap(),
186            environment: environment.to_string(),
187            dailyorders: Default::default(),
188            dailytrades: Default::default(),
189            dailyassets: HashMap::new(),
190            event_id: 0,
191            commission_ratio: 0.00025,
192            tax_ratio: 0.001, // only in stock model
193        };
194
195        if auto_reload {
196            acc.reload()
197        }
198        acc
199    }
200    pub fn set_portfoliocookie(&mut self, portfolio: String) {
201        self.portfolio_cookie = portfolio;
202    }
203
204    pub fn new_from_qifi(message: QIFI) -> Self {
205        let mut pos = message.positions;
206        let mut accpos: HashMap<String, QA_Postions> = HashMap::new();
207        //    positions: {
208        //         "rb2010": Position {
209        //             user_id: "RustT01B2_rb2010_1min",
210        //             exchange_id: "",
211        //             instrument_id: "rb2010",
212        //             volume_long_today: 0.0,
213        //             volume_long_his: 0.0,
214        //             volume_long: 0.0,
215        //             volume_long_frozen_today: 0.0,
216        //             volume_long_frozen_his: 0.0,
217        //             volume_long_frozen: 0.0,
218        //             volume_short_today: 1.0,
219        //             volume_short_his: 0.0,
220        //             volume_short: 1.0,
221        //             volume_short_frozen_today: 0.0,
222        //             volume_short_frozen_his: 0.0,
223        //             volume_short_frozen: 0.0,
224        //             volume_long_yd: 0.0,
225        //             volume_short_yd: 0.0,
226        //             pos_long_his: 0.0,
227        //             pos_long_today: 0.0,
228        //             pos_short_his: 0.0,
229        //             pos_short_today: 1.0,
230        //             open_price_long: 0.0,
231        //             open_price_short: 3281.0,
232        //             open_cost_long: 0.0,
233        //             open_cost_short: 32810.0,
234        //             position_price_long: 0.0,
235        //             position_price_short: 3281.0,
236        //             position_cost_long: 0.0,
237        //             position_cost_short: 32810.0,
238        //             last_price: 3294.0,
239        //             float_profit_long: 0.0,
240        //             float_profit_short: -130.0,
241        //             float_profit: -130.0,
242        //             position_profit_long: 0.0,
243        //             position_profit_short: -130.0,
244        //             position_profit: -130.0,
245        //             margin_long: 0.0,
246        //             margin_short: 2952.9,
247        //             margin: 2952.9,
248        //         },
249        //     },
250        for pos_i in pos.values_mut() {
251            accpos.insert(
252                pos_i.instrument_id.to_string(),
253                QA_Postions::new_with_inithold(
254                    pos_i.instrument_id.to_string(),
255                    pos_i.user_id.to_string(),
256                    message.account_cookie.to_string().clone(),
257                    message.account_cookie.to_string().clone(),
258                    message.account_cookie.to_string(),
259                    pos_i.volume_long_today,
260                    pos_i.volume_long_his,
261                    pos_i.volume_short_today,
262                    pos_i.volume_short_his,
263                    pos_i.open_price_long,
264                    pos_i.open_price_short,
265                ),
266            );
267        }
268
269        let mut acc = Self {
270            init_cash: message.accounts.available,
271            init_hold: HashMap::new(),
272            allow_t0: false,
273            allow_sellopen: false,
274            allow_margin: false,
275            market_preset: MarketPreset::new(),
276            auto_reload: false,
277            time: message.updatetime.clone(),
278            events: HashMap::new(),
279            accounts: account {
280                user_id: message.accounts.user_id.clone(),
281                currency: "CNY".to_string(),
282                pre_balance: message.accounts.pre_balance.clone(),
283                deposit: message.accounts.deposit.clone(),
284                withdraw: message.accounts.withdraw.clone(),
285                WithdrawQuota: message.accounts.WithdrawQuota.clone(),
286                close_profit: message.accounts.close_profit.clone(),
287                commission: message.accounts.commission.clone() as f64,
288                premium: message.accounts.premium.clone() as f64,
289                static_balance: message.accounts.static_balance.clone(),
290                position_profit: message.accounts.position_profit.clone(),
291                float_profit: message.accounts.float_profit.clone(),
292                balance: message.accounts.balance.clone(),
293                margin: message.accounts.margin.clone(),
294                frozen_margin: message.accounts.frozen_margin.clone(),
295                frozen_commission: message.accounts.frozen_commission.clone(),
296                frozen_premium: message.accounts.frozen_premium.clone(),
297                available: message.accounts.available.clone(),
298                risk_ratio: message.accounts.risk_ratio.clone(),
299            },
300            cash: vec![message.accounts.available],
301            money: message.money.clone(),
302            hold: accpos,
303            trades: HashMap::new(),
304            frozen: HashMap::new(),
305            history: vec![],
306            account_cookie: message.account_cookie.clone(),
307            portfolio_cookie: message.portfolio.clone(),
308            user_cookie: message.account_cookie.clone(),
309            environment: "real".to_string(),
310            dailyorders: message.orders.clone(),
311            dailytrades: message.trades.clone(),
312            dailyassets: HashMap::new(),
313            event_id: 0,
314            commission_ratio: 0.00025,
315            tax_ratio: 0.001, // only in stock model
316        };
317        acc
318    }
319
320    pub fn init_h(&mut self, code: &str) {
321        let code: String = code.parse().unwrap();
322        self.hold.insert(
323            code.clone(),
324            QA_Postions::new(
325                code.clone(),
326                self.account_cookie.clone(),
327                self.account_cookie.clone(),
328                self.account_cookie.clone(),
329                self.portfolio_cookie.clone(),
330            ),
331        );
332    }
333
334    pub fn reload(&mut self) {}
335
336    pub fn get_cash(&mut self) -> f64 {
337        self.cash.last().unwrap().to_owned()
338    }
339    pub fn get_riskratio(&mut self) -> f64 {
340        0.0
341    }
342
343    pub fn get_mom_slice(&mut self) -> QAMOMSlice {
344        QAMOMSlice {
345            datetime: self.time.clone(),
346            user_id: self.account_cookie.clone(),
347            pre_balance: self.accounts.pre_balance,
348            close_profit: self.accounts.close_profit,
349            commission: self.accounts.commission,
350            position_profit: self.get_positionprofit(),
351            float_profit: self.get_floatprofit(),
352            balance: self.get_balance(),
353            margin: self.get_margin(),
354            available: self.money,
355            risk_ratio: self.get_riskratio(),
356        }
357    }
358
359    /// 创建QIFI的账户切片, 注意他是一个结构体
360    pub fn get_qifi_slice(&mut self) -> QIFI {
361        let mut pos: HashMap<String, Position> = HashMap::new();
362        for posx in self.hold.values_mut() {
363            pos.insert(posx.instrument_id.clone(), posx.get_qifi_position());
364        }
365
366        QIFI {
367            account_cookie: self.account_cookie.clone(),
368            portfolio: self.portfolio_cookie.clone(),
369            broker_name: "QASIM".to_string(),
370            money: self.money.clone(),
371            updatetime: self.time.clone(),
372            bankname: "QASIM".to_string(),
373            trading_day: self.get_tradingday(),
374            status: 200,
375            accounts: self.get_accountmessage(),
376            orders: self.dailyorders.clone(),
377            positions: pos,
378            trades: self.dailytrades.clone(),
379            ..QIFI::default()
380        }
381    }
382
383    pub fn get_accountmessage(&mut self) -> Account {
384        Account {
385            user_id: self.account_cookie.clone(),
386            currency: "CNY".to_string(),
387            pre_balance: self.accounts.pre_balance,
388            deposit: self.accounts.deposit,
389            withdraw: self.accounts.withdraw,
390            WithdrawQuota: self.accounts.WithdrawQuota,
391            close_profit: self.accounts.close_profit,
392            commission: self.accounts.commission as f32,
393            premium: self.accounts.premium as f32,
394            static_balance: self.accounts.static_balance,
395            position_profit: self.get_positionprofit(),
396            float_profit: self.get_floatprofit(),
397            balance: self.get_balance(),
398            margin: self.get_margin(),
399            frozen_margin: 0.0,
400            frozen_commission: 0.0,
401            frozen_premium: 0.0,
402            available: self.money,
403            risk_ratio: self.get_riskratio(),
404        }
405    }
406    /// positions about
407    ///
408    /// a fast way to get the realtime price/cost/volume/history
409    pub fn get_position(&mut self, code: &str) -> Option<&mut QA_Postions> {
410        let pos = self.hold.get_mut(code);
411        pos
412    }
413
414    pub fn get_volume_long(&mut self, code: &str) -> f64 {
415        let pos = self.get_position(code).unwrap();
416        pos.volume_long()
417    }
418    pub fn get_volume_short(&mut self, code: &str) -> f64 {
419        let pos = self.get_position(code).unwrap();
420        pos.volume_short()
421    }
422    pub fn get_open_price_long(&mut self, code: &str) -> f64 {
423        self.get_position(code).unwrap().open_price_long
424    }
425    pub fn get_open_price_short(&mut self, code: &str) -> f64 {
426        self.get_position(code).unwrap().open_price_short
427    }
428
429    /// frozen & margin
430    pub fn get_frozen(&mut self, code: &str) -> f64 {
431        self.get_position(code).unwrap().frozen
432    }
433    pub fn get_margin(&mut self) -> f64 {
434        let mut margin = 0.0;
435        for pos in self.hold.values_mut() {
436            margin += pos.margin();
437        }
438        margin
439    }
440
441    /// profit
442    pub fn get_floatprofit(&mut self) -> f64 {
443        let mut fp = 0.0;
444        for pos in self.hold.values_mut() {
445            fp += pos.float_profit();
446        }
447        fp
448    }
449    pub fn get_positionprofit(&mut self) -> f64 {
450        let mut pp = 0.0;
451        for pos in self.hold.values_mut() {
452            pp += pos.float_profit();
453        }
454        pp
455    }
456
457    /// balance
458    pub fn get_balance(&mut self) -> f64 {
459        let fp = self.get_floatprofit();
460        //info!("{} {} {} {} {}", self.accounts.static_balance, self.accounts.deposit, self.accounts.withdraw, fp, self.accounts.close_profit);
461        self.accounts.static_balance + self.accounts.deposit - self.accounts.withdraw
462            + fp
463            + self.accounts.close_profit
464    }
465
466    pub async fn settle_async(&mut self) {
467        self.settle();
468    }
469
470    pub fn settle(&mut self) {
471        self.dailyassets.insert(
472            self.time.clone(),
473            QAAccountSlice {
474                datetime: self.time.clone(),
475                cash: self.money.clone(),
476                accounts: self.accounts.clone(),
477                //events: self.events.clone(),
478                positions: self.hold.clone(),
479                //frozen: self.frozen.clone(),
480                //trades: self.trades.clone(),
481            },
482        );
483        self.trades = HashMap::new();
484        self.dailyorders = BTreeMap::new();
485        self.dailytrades = BTreeMap::new();
486        self.events = HashMap::new();
487        self.event_id = 0;
488
489        for pos in self.hold.values_mut() {
490            pos.settle();
491        }
492        // init the next day cash
493        let balance_settle =
494            self.accounts.pre_balance + self.accounts.close_profit - self.accounts.commission;
495        self.accounts = account {
496            user_id: self.account_cookie.to_string(),
497            currency: "CNY".to_string(),
498            pre_balance: balance_settle.clone(),
499            deposit: 0.0,
500            withdraw: 0.0,
501            WithdrawQuota: balance_settle.clone(),
502            close_profit: 0.0,
503            commission: 0.0,
504            premium: 0.0,
505            static_balance: balance_settle.clone(),
506            position_profit: 0.0,
507            float_profit: 0.0,
508            balance: balance_settle.clone(),
509            margin: self.accounts.margin,
510            frozen_margin: 0.0,
511            frozen_commission: 0.0,
512            frozen_premium: 0.0,
513            available: balance_settle.clone(),
514            risk_ratio: self.get_riskratio(),
515        }
516    }
517
518    pub fn get_codeSubscribed(&mut self) -> Vec<String> {
519        // if a QAAccount trades a packages, then it need the get_codeSubscribed to update the price
520        // some examples like below
521        // let codes = account.get_codeSubscribed();
522        // for code in codes.iter():
523        //     acc.on_price_change(code, price.get(code), datetime)
524        let mut codeSub = vec![];
525        for key in self.hold.keys() {
526            codeSub.push(key.to_string())
527        }
528        codeSub
529    }
530
531    pub fn get_slice(&mut self) -> QAAccountSlice {
532        // get a realtime slice of account
533        // this can be save to database
534
535        QAAccountSlice {
536            datetime: self.time.clone(),
537            cash: self.money.clone(),
538            accounts: self.accounts.clone(),
539            positions: self.hold.clone(),
540        }
541    }
542
543    pub fn get_account_info(&mut self) -> account_info {
544        account_info {
545            datetime: self.time.clone(),
546            balance: self.get_balance(),
547            account_cookie: self.account_cookie.clone(),
548        }
549    }
550
551    pub fn get_latest_info(&mut self) -> String {
552        let info = self.get_account_info();
553
554        serde_json::to_string(&info).unwrap()
555    }
556
557    /// history about
558
559    pub fn history_table(&self) {
560        for item in self.history.iter() {
561            info!("{:?}", transaction::QATransaction::to_json(item));
562        }
563    }
564
565    pub fn to_csv(&self) -> Result<(), Box<dyn Error>> {
566        let mut wtr = csv::Writer::from_path(format!("{}.csv", self.account_cookie)).unwrap();
567        for item in self.history.iter() {
568            wtr.serialize(item)?;
569            wtr.flush()?;
570        }
571        Ok(())
572    }
573
574    /// order about
575    /// buy| sell| buy_open| sell_open| buy_close| sell_close|
576    /// send_order
577    pub fn buy(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
578        self.send_order(code, amount, time, 1, price, "BUY")
579    }
580    pub fn sell(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
581        self.send_order(code, amount, time, -1, price, "SELL")
582    }
583    pub fn buy_open(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
584        self.send_order(code, amount, time, 2, price, "BUY_OPEN")
585    }
586    pub fn sell_open(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
587        self.send_order(code, amount, time, -2, price, "SELL_OPEN")
588    }
589    pub fn buy_close(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
590        self.send_order(code, amount, time, 3, price, "BUY_CLOSE")
591    }
592    pub fn sell_close(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
593        self.send_order(code, amount, time, -3, price, "SELL_CLOSE")
594    }
595    pub fn buy_closetoday(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
596        self.send_order(code, amount, time, 4, price, "BUY_CLOSETODAY")
597    }
598    pub fn sell_closetoday(&mut self, code: &str, amount: f64, time: &str, price: f64) -> Result<QAOrder, ()> {
599        self.send_order(code, amount, time, -4, price, "SELL_CLOSETODAY")
600    }
601    pub fn get_tradingday(&mut self) -> String {
602        let mut u = QATradeDate::new();
603        u.get_trade_day(self.time.clone())
604    }
605
606    fn order_check(
607        &mut self,
608        code: &str,
609        amount: f64,
610        price: f64,
611        towards: i32,
612        order_id: String,
613    ) -> bool {
614        let mut res = false;
615        if self.hold.contains_key(code) {} else {
616            self.init_h(code);
617        }
618        let qapos = self.get_position(code).unwrap();
619
620        match towards {
621            3 => {
622                if (qapos.volume_short() - qapos.volume_short_frozen()) >= amount {
623                    qapos.volume_short_frozen_today += amount;
624                    qapos.volume_short_today -= amount;
625                    res = true;
626                } else {
627                    warn!("仓位不足");
628                }
629            }
630            4 => {
631                if (qapos.volume_short_today - qapos.volume_short_frozen_today) >= amount {
632                    qapos.volume_short_frozen_today += amount;
633                    qapos.volume_short_today -= amount;
634                    res = true;
635                } else {
636                    warn!("今日仓位不足");
637                }
638            }
639
640            -1 => {
641                if (qapos.volume_long_his - qapos.volume_long_frozen()) >= amount {
642                    qapos.volume_long_frozen_today += amount;
643                    qapos.volume_long_today -= amount;
644                    res = true;
645                } else {
646                    warn!("SELL CLOSE 仓位不足");
647                }
648            }
649
650            -3 => {
651                if (qapos.volume_long() - qapos.volume_long_frozen()) >= amount {
652                    qapos.volume_long_frozen_today += amount;
653                    qapos.volume_long_today -= amount;
654                    res = true;
655                } else {
656                    warn!("SELL CLOSE 仓位不足");
657                }
658            }
659
660            -4 => {
661                if (qapos.volume_long_today - qapos.volume_short_frozen_today) >= amount {
662                    qapos.volume_long_frozen_today += amount;
663                    qapos.volume_long_today -= amount;
664                    res = true;
665                } else {
666                    warn!("SELL CLOSETODAY 仓位不足");
667                }
668            }
669
670            1 | 2 | -2 => {
671                let coeff = qapos.preset.calc_coeff() * price;
672
673                let frozen = coeff * amount;
674                //                println!("OPEN FROZEN{:#?}", frozen);
675                //                println!("ORDER ID {:#?}", order_id);
676
677                if self.money > frozen {
678                    self.money -= frozen;
679
680                    self.frozen.insert(
681                        order_id,
682                        QA_Frozen {
683                            amount,
684                            coeff,
685                            money: frozen,
686                        },
687                    );
688
689                    res = true
690                } else {
691                    warn!("余额不足,当前可用money {:#?}, 需要冻结 {:#?}", self.money, frozen);
692                }
693            }
694            _ => {}
695        }
696        res
697    }
698
699    pub async fn send_order_async(
700        &mut self,
701        code: &str,
702        amount: f64,
703        time: &str,
704        towards: i32,
705        price: f64,
706        order_id: &str,
707    ) -> Option<QAOrder> {
708        let order = self.send_order(code, amount, time, towards, price, order_id);
709        let result = if order.is_ok() {
710            Some(order.unwrap())
711        } else {
712            None
713        };
714        result
715    }
716    pub fn send_order(
717        &mut self,
718        code: &str,
719        amount: f64,
720        time: &str,
721        towards: i32,
722        price: f64,
723        order_id: &str,
724    ) -> Result<QAOrder, ()> {
725        self.event_id += 1;
726        let datetimer = if time.len() == 10 {
727            format!("{} 00:00:00", time.to_string())
728        } else {
729            time.to_string()
730        };
731        let datetime = datetimer.as_str();
732
733        let context = Context::new(self.event_id as u16);
734        let ts = Timestamp::from_unix(&context, 1497624119, 1234);
735        let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]).expect("failed to generate UUID");
736
737        let order_id: String = uuid.to_string();
738
739        if self.order_check(code, amount, price, towards, order_id.clone()) {
740            let order = QAOrder::new(
741                self.account_cookie.clone(),
742                code.clone().to_string(),
743                towards,
744                "".to_string(),
745                datetime.to_string(),
746                amount,
747                price,
748                order_id.clone(),
749            );
750            match self.environment.as_ref() {
751                "backtest" => {
752                    self.receive_deal(
753                        code.parse().unwrap(),
754                        amount,
755                        price,
756                        datetime.parse().unwrap(),
757                        order_id.clone(),
758                        order_id.clone(),
759                        order_id.clone(),
760                        towards,
761                    );
762                }
763                "real" => {
764                    let (direction, offset) = self.get_direction_or_offset(towards);
765                    self.dailyorders.insert(
766                        order_id.clone(),
767                        Order {
768                            seqno: self.event_id.clone(),
769                            user_id: self.account_cookie.clone(),
770                            order_id: order_id.clone(),
771                            exchange_id: "".to_string(),
772                            instrument_id: code.clone().to_string(),
773                            direction,
774                            offset,
775                            volume_orign: amount,
776                            price_type: "LIMIT".to_string(),
777                            limit_price: price,
778                            time_condition: "AND".to_string(),
779                            volume_condition: "GFD".to_string(),
780                            insert_date_time: Utc
781                                .datetime_from_str(datetime, "%Y-%m-%d %H:%M:%S")
782                                .unwrap()
783                                .timestamp_nanos()
784                                - 28800000000000,
785                            exchange_order_id: Uuid::new_v4().to_string(),
786                            status: "FINISHED".to_string(),
787                            volume_left: 0.0,
788                            last_msg: "".to_string(),
789                        },
790                    );
791
792                    self.receive_deal_real(
793                        code.parse().unwrap(),
794                        amount,
795                        price,
796                        datetime.parse().unwrap(),
797                        order_id.clone(),
798                        order_id.clone(),
799                        order_id.clone(),
800                        towards,
801                        self.event_id.clone(),
802                    )
803                    // self.events.insert(self.datetime.clone(), "order insert".to_string());
804                }
805                _ => {
806                    self.events
807                        .insert(self.time.clone(), "order insert".to_string());
808                }
809            }
810            Ok(order.clone())
811        } else {
812            Err(())
813        }
814    }
815
816
817    pub fn on_price_change(&mut self, code: String, price: f64, datetime: String) {
818        // 当行情变化时候 要更新计算持仓
819        let pos = self.get_position(code.as_ref()).unwrap();
820        pos.on_price_change(price, datetime.clone());
821        self.change_datetime(datetime);
822    }
823
824    pub fn change_datetime(&mut self, datetime: String) {
825        // 用于切换时间
826        self.time = datetime;
827    }
828
829    pub fn set_init_cash(&mut self, cash: f64) {
830        self.init_cash = cash;
831    }
832
833    /// 获取成交单方向信息的API, 支持股票与期货
834    pub fn get_direction_or_offset(&mut self, towards: i32) -> (String, String) {
835        let rt = match towards {
836            1 => (String::from("BUY"), String::from("OPEN")),
837            2 => (String::from("BUY"), String::from("OPEN")),
838            3 => (String::from("BUY"), String::from("CLOSE")),
839            4 => (String::from("BUY"), String::from("CLOSETODAY")),
840            -1 => (String::from("SELL"), String::from("CLOSE")),
841            -2 => (String::from("SELL"), String::from("OPEN")),
842            -3 => (String::from("SELL"), String::from("CLOSE")),
843            -4 => (String::from("SELL"), String::from("CLOSETODAY")),
844            _ => (String::from(""), String::from("")),
845        };
846        rt
847    }
848    fn receive_deal_real(
849        &mut self,
850        code: String,
851        amount: f64,
852        price: f64,
853        datetime: String,
854        order_id: String,
855        trade_id: String,
856        realorder_id: String,
857        towards: i32,
858        event_id: i32,
859    ) {
860        self.time = datetime.clone();
861        if self.frozen.contains_key(&order_id) {
862            let frozen = self.frozen.get_mut(&order_id).unwrap();
863            self.money += frozen.money;
864            self.frozen.remove(&order_id);
865        } else {
866            if towards == -1 | 1 | 2 | -2 {
867                error!("NOT IN DAY ORDER {}", order_id)
868            }
869        }
870        let qapos = self.get_position(code.as_ref()).unwrap();
871
872        let commission = qapos
873            .clone()
874            .preset
875            .calc_commission(price.clone(), amount.clone());
876        let tax = qapos
877            .clone()
878            .preset
879            .calc_tax(price.clone(), amount.clone(), towards.clone());
880        qapos.on_price_change(price.clone(), datetime.clone());
881        let (margin, close_profit) = qapos.update_pos(price, amount, towards);
882        let (direction, offset) = self.get_direction_or_offset(towards);
883        // add calc tax/coeff
884        //        qapos.preset.commission_coeff_pervol
885
886        self.money -= (margin - close_profit + commission + tax);
887        self.accounts.close_profit += close_profit;
888        self.cash.push(self.money);
889        self.accounts.commission += commission + tax;
890
891        // println!("{:?} {:?} {:?} {:?}", datetime,code,direction,offset);
892
893        let td = Utc
894            .datetime_from_str(datetime.as_ref(), "%Y-%m-%d %H:%M:%S")
895            .unwrap()
896            .timestamp_nanos()
897            - 28800000000000;
898        let trade = Trade {
899            seqno: event_id,
900            user_id: self.account_cookie.clone(),
901            price,
902            order_id,
903            trade_id: trade_id.clone(),
904            exchange_id: "".to_string(),
905            commission: commission + tax,
906            direction,
907            offset,
908            instrument_id: code,
909            exchange_trade_id: "".to_string(),
910            volume: amount,
911            trade_date_time: td,
912        };
913        self.dailytrades.insert(trade_id, trade.clone());
914    }
915
916    fn receive_deal(
917        &mut self,
918        code: String,
919        amount: f64,
920        price: f64,
921        datetime: String,
922        order_id: String,
923        trade_id: String,
924        realorder_id: String,
925        towards: i32,
926    ) {
927        self.time = datetime.clone();
928        if self.frozen.contains_key(&order_id) {
929            let frozen = self.frozen.get_mut(&order_id).unwrap();
930            self.money += frozen.money;
931            self.frozen.remove(&order_id);
932
933            // self.frozen.insert(order_id.clone(), QA_Frozen {
934            //     amount: 0.0,
935            //     coeff: 0.0,
936            //     money: 0.0,
937            // });
938        } else {
939            if towards == -1 | 1 | 2 | -2 {
940                error!("ERROR NO THAT ORDER {}", order_id)
941            }
942        }
943
944        let qapos = self.get_position(code.as_ref()).unwrap();
945        let commission = qapos
946            .clone()
947            .preset
948            .calc_commission(price.clone(), amount.clone());
949        let tax = qapos
950            .clone()
951            .preset
952            .calc_tax(price.clone(), amount.clone(), towards.clone());
953        qapos.on_price_change(price.clone(), datetime.clone());
954
955        let (margin, close_profit) = qapos.update_pos(price, amount, towards);
956
957        //println!("MARGIN RELEASE {:#?}", margin.clone());
958        //println!("CLOSE PROFIT RELEASE {:#?}", close_profit.clone());
959        self.money -= (margin - close_profit + commission + tax);
960        self.accounts.close_profit += close_profit;
961        self.cash.push(self.money);
962        self.accounts.commission += commission + tax;
963        let transaction = transaction::QATransaction {
964            code,
965            amount,
966            price,
967            datetime,
968            order_id,
969            trade_id: trade_id.clone(),
970            realorder_id,
971            account_cookie: self.account_cookie.clone(),
972            commission,
973            tax,
974            message: "".to_string(),
975            frozen: 0.0,
976            direction: towards,
977        };
978        self.trades.insert(trade_id, transaction.clone());
979        self.history.push(transaction);
980    }
981}
982
983#[cfg(test)]
984mod tests {
985    use super::*;
986
987    #[test]
988    fn test_new() {
989        // create a new account
990        // 可以正确的创建一个账户
991        let mut acc = QA_Account::new(
992            "RustT01B2_RBL8",
993            "test",
994            "admin",
995            100000.0,
996            false,
997            "backtest",
998        );
999        acc.history_table();
1000
1001        let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real");
1002    }
1003
1004    #[test]
1005    fn test_pos() {
1006        // test get position function
1007        // 可以正确初始化一个/多个持仓
1008        let mut acc = QA_Account::new(
1009            "RustT01B2_RBL8",
1010            "test",
1011            "admin",
1012            100000.0,
1013            false,
1014            "backtest",
1015        );
1016        acc.init_h("RB2005");
1017        acc.get_position("RB2005");
1018
1019        acc.init_h("000001");
1020
1021        println!("{:#?}", acc.get_codeSubscribed());
1022    }
1023
1024    #[test]
1025    fn test_init_h() {
1026        // test init a position function
1027        let mut acc = QA_Account::new(
1028            "RustT01B2_RBL8",
1029            "test",
1030            "admin",
1031            100000.0,
1032            false,
1033            "backtest",
1034        );
1035        acc.init_h("RB2005");
1036        println!("{:#?}", acc.get_position("RB2005").unwrap().message());
1037    }
1038
1039    #[test]
1040    fn test_buy_open() {
1041        println!("test buy open");
1042        let code = "RB2005";
1043        let mut acc = QA_Account::new(
1044            "RustT01B2_RBL8",
1045            "test",
1046            "admin",
1047            1000000.0,
1048            false,
1049            "backtest",
1050        );
1051
1052        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1053        println!("MONEY LEFT{:#?}", acc.money);
1054        assert_eq!(acc.get_volume_long(code), 10.0);
1055        acc.history_table();
1056    }
1057
1058    #[test]
1059    fn test_buy_open_for_stock() {
1060        println!("test buy open");
1061        let code = "000001";
1062        let mut acc = QA_Account::new(
1063            "RustT01B2_RBL8",
1064            "test",
1065            "admin",
1066            1000000.0,
1067            false,
1068            "backtest",
1069        );
1070
1071        acc.buy_open(code, 1000.0, "2020-01-20", 35.0);
1072        println!("MONEY LEFT{:#?}", acc.money);
1073        assert_eq!(acc.get_volume_long(code), 1000.0);
1074        acc.history_table();
1075    }
1076
1077    #[test]
1078    fn test_realaccountmodel_for_stock() {
1079        println!("test buy open");
1080        let code = "000001";
1081        let mut acc = QA_Account::new(
1082            "RustT01B2_RBL8",
1083            "test",
1084            "admin",
1085            1000000.0,
1086            false,
1087            "real",
1088        );
1089
1090        acc.buy_open(code, 1000.0, "2020-01-20", 35.0);
1091        println!("MONEY LEFT{:#?}", acc.money);
1092        assert_eq!(acc.get_volume_long(code), 1000.0);
1093        println!("QIFI {:#?}", acc.get_qifi_slice());
1094        //acc.history_table();
1095    }
1096
1097    #[test]
1098    fn test_sell_open() {
1099        println!("test sell open");
1100        let code = "RB2005";
1101        let mut acc = QA_Account::new(
1102            "RustT01B2_RBL8",
1103            "test",
1104            "admin",
1105            100000.0,
1106            false,
1107            "backtest",
1108        );
1109        acc.sell_open(code, 10.0, "2020-01-20", 3500.0);
1110        assert_eq!(acc.get_volume_short(code), 10.0);
1111        acc.history_table();
1112    }
1113
1114    #[test]
1115    fn test_buy_close() {
1116        println!("test buy close");
1117        let code = "RB2005";
1118        let mut acc = QA_Account::new(
1119            "RustT01B2_RBL8",
1120            "test",
1121            "admin",
1122            100000.0,
1123            false,
1124            "backtest",
1125        );
1126        acc.init_h(code);
1127        acc.sell_open(code, 10.0, "2020-01-20", 3500.0);
1128        assert_eq!(acc.get_volume_short(code), 10.0);
1129        acc.buy_close(code, 10.0, "2020-01-20", 3600.0);
1130        assert_eq!(acc.get_volume_short(code), 0.0);
1131        println!("LATEST MONEY {:#?}", acc.money);
1132        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1133        acc.history_table();
1134    }
1135
1136    #[test]
1137    fn test_sell_close() {
1138        println!("test sell close");
1139        let code = "RB2005";
1140        let mut acc = QA_Account::new(
1141            "RustT01B2_RBL8",
1142            "test",
1143            "admin",
1144            100000.0,
1145            false,
1146            "backtest",
1147        );
1148        acc.init_h(code);
1149        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1150        assert_eq!(acc.get_volume_long(code), 10.0);
1151        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1152        assert_eq!(acc.get_volume_long(code), 0.0);
1153        println!("LATEST MONEY {:#?}", acc.money);
1154        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1155        acc.history_table();
1156    }
1157
1158    #[test]
1159    fn test_sellclose_for_stock_rzrq() {
1160        println!("test buy open");
1161        let code = "000001";
1162        let mut acc = QA_Account::new(
1163            "RustT01B2_RBL8",
1164            "test",
1165            "admin",
1166            1000000.0,
1167            false,
1168            "backtest",
1169        );
1170
1171        acc.buy_open(code, 1000.0, "2020-01-20", 35.0);
1172        println!("MONEY LEFT{:#?}", acc.money);
1173        assert_eq!(acc.get_volume_long(code), 1000.0);
1174
1175        acc.sell_close(code, 1000.0, "2020-01-20", 36.0);
1176        acc.history_table();
1177    }
1178
1179    #[test]
1180    fn test_stock_buy() {
1181        println!("test account slice");
1182        let code = "000001";
1183        let mut acc = QA_Account::new(
1184            "rust_test_stock",
1185            "test",
1186            "admin",
1187            100000.0,
1188            false,
1189            "backtest",
1190        );
1191
1192        acc.buy(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1193        assert_eq!(acc.get_volume_long(code), 10.0);
1194        println!("{:#?}", acc.trades)
1195    }
1196
1197    #[test]
1198    fn test_stocksell() {
1199        println!("test account slice");
1200        let code = "000001";
1201        let mut acc = QA_Account::new("rust_test_stock", "test", "admin", 100000.0, false, "real");
1202        acc.init_h(code);
1203        acc.buy(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1204        assert_eq!(acc.get_volume_long(code), 10.0);
1205
1206        acc.settle();
1207        acc.sell(code, 10.0, "2020-01-22 22:10:00", 3600.0);
1208        println!("{:#?}", acc.dailytrades);
1209    }
1210
1211    #[test]
1212    fn test_stocksell_with_settle() {
1213        println!("test account slice");
1214        let code = "000001";
1215        let mut acc = QA_Account::new("rust_test_stock", "test", "admin", 100000.0, false, "real");
1216        acc.init_h(code);
1217        acc.buy(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1218        assert_eq!(acc.get_volume_long(code), 10.0);
1219        acc.settle();
1220        acc.sell(code, 10.0, "2020-01-20 22:10:00", 3600.0);
1221        println!("{:#?}", acc.dailytrades);
1222    }
1223
1224    #[test]
1225    fn test_accountSlice() {
1226        println!("test account slice");
1227        let code = "RB2005";
1228
1229        let mut acc = QA_Account::new(
1230            "RustT01B2_RBL8",
1231            "test",
1232            "admin",
1233            100000.0,
1234            false,
1235            "backtest",
1236        );
1237        acc.init_h(code);
1238        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1239        let slice = acc.get_slice();
1240        println!("account Slice  {:#?}", slice);
1241        assert_eq!(acc.get_volume_long(code), 10.0);
1242        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1243        assert_eq!(acc.get_volume_long(code), 0.0);
1244        println!("LATEST MONEY {:#?}", acc.money);
1245        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1246        let slice = acc.get_slice();
1247        println!("account Slice  {:#?}", slice);
1248        acc.history_table();
1249    }
1250
1251    #[test]
1252    fn test_accountSlice_for_qifi() {
1253        println!("test account slice");
1254        let code = "RB2005";
1255        let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real");
1256        acc.init_h(code);
1257        acc.buy_open(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1258        let slice = acc.get_qifi_slice();
1259        println!("account Slice  {:#?}", slice);
1260        assert_eq!(acc.get_volume_long(code), 10.0);
1261        acc.sell_close(code, 10.0, "2020-01-20 22:10:00", 3600.0);
1262        assert_eq!(acc.get_volume_long(code), 0.0);
1263        println!("LATEST MONEY {:#?}", acc.money);
1264        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1265        let slice = acc.get_qifi_slice();
1266        let slicestr = serde_json::to_string(&slice).unwrap();
1267        println!("{:#?}", slicestr);
1268    }
1269
1270    #[test]
1271    fn test_qifi_reload() {
1272        println!("test account slice");
1273        let code = "RB2005";
1274        let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real");
1275        acc.buy_open(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1276        let slice = acc.get_qifi_slice();
1277        assert_eq!(acc.get_volume_long(code), 10.0);
1278        let slice = acc.get_qifi_slice();
1279        let mut new_acc = QA_Account::new_from_qifi(slice);
1280        assert_eq!(new_acc.get_volume_long(code), 10.0);
1281        new_acc.sell_close(code, 10.0, "2020-01-20 22:10:00", 3600.0);
1282
1283        println!("{:#?}", new_acc.trades);
1284
1285        println!("{:#?}", new_acc.dailytrades);
1286    }
1287
1288    #[test]
1289    fn test_stock_qifi_reload() {
1290        println!("test account slice");
1291        let code = "000001";
1292        let mut acc = QA_Account::new("rust_test_stock", "test", "admin", 100000.0, false, "real");
1293        acc.init_h(code);
1294        acc.buy_open(code, 10.0, "2020-01-20 22:10:00", 3500.0);
1295        let slice = acc.get_qifi_slice();
1296        assert_eq!(acc.get_volume_long(code), 10.0);
1297        let slice = acc.get_qifi_slice();
1298        let mut new_acc = QA_Account::new_from_qifi(slice);
1299        assert_eq!(new_acc.get_volume_long(code), 10.0);
1300        new_acc.sell_close(code, 10.0, "2020-01-20 22:10:00", 3600.0);
1301        println!("{:#?}", new_acc.trades);
1302        println!("{:#?}", new_acc.dailytrades);
1303    }
1304
1305    #[test]
1306    fn test_getaccountmessage() {
1307        println!("test account slice");
1308        let code = "RB2005";
1309        let mut acc = QA_Account::new(
1310            "RustT01B2_RBL8",
1311            "test",
1312            "admin",
1313            100000.0,
1314            false,
1315            "backtest",
1316        );
1317        acc.init_h(code);
1318        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1319        let slice = acc.get_accountmessage();
1320        println!("account Slice  {:#?}", slice);
1321        assert_eq!(acc.get_volume_long(code), 10.0);
1322        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1323        assert_eq!(acc.get_volume_long(code), 0.0);
1324        println!("LATEST MONEY {:#?}", acc.money);
1325        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1326        let slice = acc.get_accountmessage();
1327        println!("account Slice  {:#?}", slice);
1328        acc.history_table();
1329    }
1330
1331    #[test]
1332    fn test_settle() {
1333        println!("test account slice");
1334        let code = "RB2005";
1335
1336        let mut acc = QA_Account::new(
1337            "RustT01B2_RBL8",
1338            "test",
1339            "admin",
1340            100000.0,
1341            false,
1342            "backtest",
1343        );
1344        acc.init_h(code);
1345        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1346
1347        let slice = acc.get_accountmessage();
1348        println!("before settle");
1349        println!("account Slice  {:#?}", slice);
1350        assert_eq!(acc.get_volume_long(code), 10.0);
1351
1352        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1353
1354        assert_eq!(acc.get_volume_long(code), 0.0);
1355        //println!("{:#?}", )
1356        println!("LATEST MONEY {:#?}", acc.money);
1357        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1358
1359        let slice = acc.get_accountmessage();
1360
1361        println!("account Slice  {:#?}", slice);
1362
1363        acc.settle();
1364        println!("after settle");
1365
1366        let slice = acc.get_accountmessage();
1367        println!("account Slice  {:#?}", slice);
1368
1369        acc.buy_open(code, 10.0, "2020-01-22", 3500.0);
1370
1371        acc.on_price_change(code.to_string(), 3523.0, "2020-01-22".to_string());
1372        let slice = acc.get_accountmessage();
1373        println!("before settle");
1374        println!("account Slice  {:#?}", slice);
1375        acc.settle();
1376        println!("after settle");
1377
1378        let slice = acc.get_accountmessage();
1379        println!("account Slice  {:#?}", slice);
1380
1381        acc.history_table();
1382    }
1383
1384    #[test]
1385    fn test_on_pricechange() {
1386        println!("test on price change");
1387        let code = "RB2005";
1388
1389        let mut acc = QA_Account::new(
1390            "RustT01B2_RBL8",
1391            "test",
1392            "admin",
1393            100000.0,
1394            false,
1395            "backtest",
1396        );
1397        acc.init_h(code);
1398        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1399        assert_eq!(acc.get_volume_long(code), 10.0);
1400
1401        acc.on_price_change(code.to_string(), 3520.0, "2020-01-20".to_string());
1402        assert_eq!(2000.0, acc.get_floatprofit());
1403
1404        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1405
1406        assert_eq!(acc.get_volume_long(code), 0.0);
1407        //println!("{:#?}", )
1408        println!("LATEST MONEY {:#?}", acc.money);
1409        println!("CLOSE PROFIT {:#?}", acc.accounts.close_profit);
1410
1411        acc.history_table();
1412    }
1413
1414    #[test]
1415    fn test_to_csv() {
1416        println!("test sell close");
1417        let code = "RB2005";
1418
1419        let mut acc = QA_Account::new(
1420            "RustT01B2_RBL8",
1421            "test",
1422            "admin",
1423            100000.0,
1424            false,
1425            "backtest",
1426        );
1427        acc.init_h(code);
1428        acc.buy_open(code, 10.0, "2020-01-20", 3500.0);
1429        assert_eq!(acc.get_volume_long(code), 10.0);
1430        acc.sell_close(code, 10.0, "2020-01-20", 3600.0);
1431
1432        assert_eq!(acc.get_volume_long(code), 0.0);
1433        //println!("{:#?}", )
1434        println!("{:#?}", acc.money);
1435        acc.to_csv();
1436        //acc.history_table();
1437    }
1438
1439    #[test]
1440    fn test_get_info() {
1441        let mut acc = QA_Account::new(
1442            "RustT01B2_RBL8",
1443            "test",
1444            "admin",
1445            100000.0,
1446            false,
1447            "backtest",
1448        );
1449        acc.buy_open("rb2005", 10.0, "2020-01-20", 3500.0);
1450        let r = acc.get_latest_info();
1451        println!("{:#?}", r);
1452        assert_eq!(100000.0, acc.get_balance());
1453        acc.on_price_change("rb2005".to_string(), 3600.0, "2020-01-21".to_string());
1454        let r = acc.get_latest_info();
1455        println!("{:#?}", r);
1456        assert_eq!(110000.0, acc.get_balance());
1457    }
1458}