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 pub pre_balance: f64,
37 pub close_profit: f64,
39 pub commission: f64,
41 pub position_profit: f64,
42 pub float_profit: f64,
44 pub balance: f64,
46 pub margin: f64,
48 pub available: f64,
51 pub risk_ratio: f64, }
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 pub currency: String,
67 pub pre_balance: f64,
69 pub deposit: f64,
71 pub withdraw: f64,
73 pub WithdrawQuota: f64,
75 pub close_profit: f64,
77 pub commission: f64,
79 pub premium: f64,
81 pub static_balance: f64,
83 pub position_profit: f64,
85 pub float_profit: f64,
87 pub balance: f64,
89 pub margin: f64,
91 pub frozen_margin: f64,
93 pub frozen_commission: f64,
95 pub frozen_premium: f64,
97 pub available: f64,
99 pub risk_ratio: f64, }
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 tax_ratio: f64, }
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, };
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 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, };
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 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 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 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 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 pub fn get_balance(&mut self) -> f64 {
459 let fp = self.get_floatprofit();
460 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 positions: self.hold.clone(),
479 },
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 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 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 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 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 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 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 }
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 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 self.time = datetime;
827 }
828
829 pub fn set_init_cash(&mut self, cash: f64) {
830 self.init_cash = cash;
831 }
832
833 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 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 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 } 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 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 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 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 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 }
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!("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!("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!("{:#?}", acc.money);
1435 acc.to_csv();
1436 }
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}