avin_core/order/
market_order.rs

1/****************************************************************************
2 * URL:         http://avin.info
3 * AUTHOR:      Alex Avin
4 * E-MAIL:      mr.alexavin@gmail.com
5 * LICENSE:     MIT
6 ****************************************************************************/
7
8use bitcode::{Decode, Encode};
9
10use crate::{Direction, Operation, Transaction};
11
12#[derive(Debug, PartialEq, Decode, Encode, Clone)]
13pub enum MarketOrder {
14    New(NewMarketOrder),
15    Posted(PostedMarketOrder),
16    Filled(FilledMarketOrder),
17    Rejected(RejectedMarketOrder),
18}
19impl MarketOrder {
20    #[allow(clippy::new_ret_no_self)]
21    pub fn new(direction: Direction, lots: u32) -> NewMarketOrder {
22        NewMarketOrder { direction, lots }
23    }
24}
25impl std::fmt::Display for MarketOrder {
26    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
27        match self {
28            Self::New(order) => write!(f, "{order}"),
29            Self::Posted(order) => write!(f, "{order}"),
30            Self::Filled(order) => write!(f, "{order}"),
31            Self::Rejected(order) => write!(f, "{order}"),
32        }
33    }
34}
35
36#[derive(Debug, PartialEq, Decode, Encode, Clone)]
37pub struct NewMarketOrder {
38    pub direction: Direction,
39    pub lots: u32,
40}
41impl NewMarketOrder {
42    pub fn post(self, broker_id: &str) -> PostedMarketOrder {
43        PostedMarketOrder {
44            direction: self.direction,
45            lots: self.lots,
46            broker_id: broker_id.to_string(),
47            transactions: Vec::new(),
48        }
49    }
50    pub fn reject(self, meta: &str) -> RejectedMarketOrder {
51        RejectedMarketOrder {
52            direction: self.direction,
53            lots: self.lots,
54            meta: meta.to_string(),
55        }
56    }
57}
58impl std::fmt::Display for NewMarketOrder {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        write!(f, "MarketOrder::New={} {}", self.direction, self.lots)
61    }
62}
63
64#[derive(Debug, PartialEq, Decode, Encode, Clone)]
65pub struct PostedMarketOrder {
66    pub direction: Direction,
67    pub lots: u32,
68    pub broker_id: String,
69    pub transactions: Vec<Transaction>,
70}
71impl PostedMarketOrder {
72    pub fn add_transaction(&mut self, t: Transaction) {
73        self.transactions.push(t);
74    }
75    pub fn fill(self, ts_nanos: i64, commission: f64) -> FilledMarketOrder {
76        let operation =
77            Operation::from(ts_nanos, &self.transactions, commission);
78        FilledMarketOrder {
79            direction: self.direction,
80            lots: self.lots,
81            broker_id: self.broker_id,
82            transactions: self.transactions,
83            operation,
84        }
85    }
86}
87impl std::fmt::Display for PostedMarketOrder {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(
90            f,
91            "MarketOrder::Posted={} {} id={} t={:?}",
92            self.direction, self.lots, self.broker_id, self.transactions
93        )
94    }
95}
96
97#[derive(Debug, PartialEq, Decode, Encode, Clone)]
98pub struct FilledMarketOrder {
99    pub direction: Direction,
100    pub lots: u32,
101    pub broker_id: String,
102    pub transactions: Vec<Transaction>,
103    pub operation: Operation,
104}
105impl std::fmt::Display for FilledMarketOrder {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(
108            f,
109            "MarketOrder::Filled={} {} id={} t={:?} {}",
110            self.direction,
111            self.lots,
112            self.broker_id,
113            self.transactions,
114            self.operation
115        )
116    }
117}
118
119#[derive(Debug, PartialEq, Decode, Encode, Clone)]
120pub struct RejectedMarketOrder {
121    pub direction: Direction,
122    pub lots: u32,
123    pub meta: String,
124}
125impl std::fmt::Display for RejectedMarketOrder {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        write!(
128            f,
129            "MarketOrder::Rejected={} {} meta={}",
130            self.direction, self.lots, self.meta
131        )
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use chrono::Utc;
139
140    #[test]
141    fn new_post_fill() {
142        let new = MarketOrder::new(Direction::Buy, 10);
143
144        let mut posted = new.post("order_id=100500");
145        assert_eq!(posted.broker_id, "order_id=100500");
146
147        let t1 = Transaction::new(5, 320.0);
148        posted.add_transaction(t1);
149        assert_eq!(posted.broker_id, "order_id=100500");
150        assert_eq!(posted.transactions.len(), 1);
151
152        let t2 = Transaction::new(5, 320.0);
153        posted.add_transaction(t2);
154        assert_eq!(posted.broker_id, "order_id=100500");
155        assert_eq!(posted.transactions.len(), 2);
156
157        let dt = Utc::now();
158        let ts = dt.timestamp_nanos_opt().unwrap();
159        let order = posted.fill(ts, 3.2);
160        assert_eq!(order.operation.dt(), dt);
161        assert_eq!(order.operation.quantity, 10);
162        assert_eq!(order.operation.value, 3200.0);
163        assert_eq!(order.operation.commission, 3.2);
164    }
165    #[test]
166    fn reject() {
167        let new = MarketOrder::new(Direction::Sell, 10);
168        assert_eq!(new.direction, Direction::Sell);
169        assert_eq!(new.lots, 10);
170        dbg!(&new);
171
172        let reject = new.reject("market is closed");
173        assert_eq!(reject.meta, "market is closed");
174        dbg!(&reject);
175    }
176}