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 pub volume_long_today: f64,
40 pub volume_long_his: f64,
41 pub volume_short_today: f64,
42 pub volume_short_his: f64,
43 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 pub margin_long: f64,
52 pub margin_short: f64,
53 pub position_price_long: f64,
55 pub position_cost_long: f64,
56 pub position_price_short: f64,
57 pub position_cost_short: f64,
58 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 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 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 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 let mut profit = 0.0;
352 match towards {
353 1 => {
355 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 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 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 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 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 profit =
410 (self.position_price_short - price) * amount * self.preset.unit_table as f64;
411 self.margin_short += margin_value;
412 }
413 -1 => {
414 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 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 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 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 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 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); assert_eq!(10.0, pos.volume_long());
538 }
539
540 #[test]
541 fn test_stock_receivedeal() {
542 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); assert_eq!(10000.0, pos.volume_long());
554 }
555
556 #[test]
557 fn test_settle() {
558 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); 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 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 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); 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); 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}