use bitcode::{Decode, Encode};
use crate::{Direction, Operation, Transaction};
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub enum LimitOrder {
New(NewLimitOrder),
Posted(PostedLimitOrder),
Filled(FilledLimitOrder),
Rejected(RejectedLimitOrder),
Canceled(CanceledLimitOrder),
}
impl LimitOrder {
#[allow(clippy::new_ret_no_self)]
pub fn new(direction: Direction, lots: u32, price: f64) -> NewLimitOrder {
NewLimitOrder {
direction,
lots,
price,
}
}
pub fn as_new(self) -> Option<NewLimitOrder> {
match self {
LimitOrder::New(o) => Some(o),
LimitOrder::Posted(_) => None,
LimitOrder::Filled(_) => None,
LimitOrder::Rejected(_) => None,
LimitOrder::Canceled(_) => None,
}
}
pub fn as_posted(self) -> Option<PostedLimitOrder> {
match self {
LimitOrder::New(_) => None,
LimitOrder::Posted(o) => Some(o),
LimitOrder::Filled(_) => None,
LimitOrder::Rejected(_) => None,
LimitOrder::Canceled(_) => None,
}
}
pub fn as_filled(self) -> Option<FilledLimitOrder> {
match self {
LimitOrder::New(_) => None,
LimitOrder::Posted(_) => None,
LimitOrder::Filled(o) => Some(o),
LimitOrder::Rejected(_) => None,
LimitOrder::Canceled(_) => None,
}
}
pub fn as_rejected(self) -> Option<RejectedLimitOrder> {
match self {
LimitOrder::New(_) => None,
LimitOrder::Posted(_) => None,
LimitOrder::Filled(_) => None,
LimitOrder::Rejected(o) => Some(o),
LimitOrder::Canceled(_) => None,
}
}
pub fn as_canceled(self) -> Option<CanceledLimitOrder> {
match self {
LimitOrder::New(_) => None,
LimitOrder::Posted(_) => None,
LimitOrder::Filled(_) => None,
LimitOrder::Rejected(_) => None,
LimitOrder::Canceled(o) => Some(o),
}
}
pub fn is_new(&self) -> bool {
matches!(self, LimitOrder::New(_))
}
pub fn is_posted(&self) -> bool {
matches!(self, LimitOrder::Posted(_))
}
pub fn is_filled(&self) -> bool {
matches!(self, LimitOrder::Filled(_))
}
pub fn is_rejected(&self) -> bool {
matches!(self, LimitOrder::Rejected(_))
}
pub fn is_canceled(&self) -> bool {
matches!(self, LimitOrder::Canceled(_))
}
pub fn broker_id(&self) -> Option<&String> {
match self {
Self::New(_) => None,
Self::Posted(o) => Some(&o.broker_id),
Self::Filled(o) => Some(&o.broker_id),
Self::Canceled(o) => Some(&o.broker_id),
Self::Rejected(_) => None,
}
}
}
impl std::fmt::Display for LimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::New(order) => write!(f, "{order}"),
Self::Posted(order) => write!(f, "{order}"),
Self::Filled(order) => write!(f, "{order}"),
Self::Canceled(order) => write!(f, "{order}"),
Self::Rejected(order) => write!(f, "{order}"),
}
}
}
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub struct NewLimitOrder {
pub direction: Direction,
pub lots: u32,
pub price: f64,
}
impl NewLimitOrder {
pub fn post(self, broker_id: &str) -> PostedLimitOrder {
PostedLimitOrder {
direction: self.direction,
lots: self.lots,
price: self.price,
broker_id: broker_id.to_string(),
transactions: Vec::new(),
}
}
pub fn reject(self, meta: &str) -> RejectedLimitOrder {
RejectedLimitOrder {
direction: self.direction,
lots: self.lots,
price: self.price,
meta: meta.to_string(),
}
}
}
impl std::fmt::Display for NewLimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LimitOrder::New={} {}x{}",
self.direction, self.lots, self.price
)
}
}
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub struct PostedLimitOrder {
pub direction: Direction,
pub lots: u32,
pub price: f64,
pub broker_id: String,
pub transactions: Vec<Transaction>,
}
impl PostedLimitOrder {
pub fn add_transaction(&mut self, t: Transaction) {
self.transactions.push(t);
}
pub fn fill(self, ts_nanos: i64, commission: f64) -> FilledLimitOrder {
let operation =
Operation::build(ts_nanos, &self.transactions, commission);
FilledLimitOrder {
direction: self.direction,
lots: self.lots,
price: self.price,
broker_id: self.broker_id,
transactions: self.transactions,
operation,
}
}
pub fn cancel(self) -> CanceledLimitOrder {
CanceledLimitOrder {
direction: self.direction,
lots: self.lots,
price: self.price,
broker_id: self.broker_id,
transactions: self.transactions,
}
}
}
impl std::fmt::Display for PostedLimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LimitOrder::Posted={} {}x{} id={} t={:?}",
self.direction,
self.lots,
self.price,
self.broker_id,
self.transactions
)
}
}
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub struct FilledLimitOrder {
pub direction: Direction,
pub lots: u32,
pub price: f64,
pub broker_id: String,
pub transactions: Vec<Transaction>,
pub operation: Operation,
}
impl std::fmt::Display for FilledLimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LimitOrder::Filled={} {}x{} id={} t={:?} {}",
self.direction,
self.lots,
self.price,
self.broker_id,
self.transactions,
self.operation
)
}
}
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub struct CanceledLimitOrder {
pub direction: Direction,
pub lots: u32,
pub price: f64,
pub broker_id: String,
pub transactions: Vec<Transaction>,
}
impl std::fmt::Display for CanceledLimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LimitOrder::Canceled={} {}x{} id={} transactions={:?}",
self.direction,
self.lots,
self.price,
self.broker_id,
self.transactions
)
}
}
#[derive(Debug, PartialEq, Decode, Encode, Clone)]
pub struct RejectedLimitOrder {
pub direction: Direction,
pub lots: u32,
pub price: f64,
pub meta: String,
}
impl std::fmt::Display for RejectedLimitOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LimitOrder::Rejected={} {}x{} meta={}",
self.direction, self.lots, self.price, self.meta
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Utc;
#[test]
fn new_post_fill() {
let new = LimitOrder::new(Direction::Buy, 2, 4500.0);
let mut posted = new.post("order_id=100500");
assert_eq!(posted.broker_id, "order_id=100500");
let t1 = Transaction::new(1, 4500.0);
posted.add_transaction(t1);
assert_eq!(posted.broker_id, "order_id=100500");
assert_eq!(posted.transactions.len(), 1);
let t2 = Transaction::new(1, 4510.0);
posted.add_transaction(t2);
assert_eq!(posted.broker_id, "order_id=100500");
assert_eq!(posted.transactions.len(), 2);
let dt = Utc::now();
let ts = dt.timestamp_nanos_opt().unwrap();
let order = posted.fill(ts, 4.5);
assert_eq!(order.operation.dt(), dt);
assert_eq!(order.operation.ts_nanos, ts);
assert_eq!(order.operation.quantity, 2);
assert_eq!(order.operation.value, 9010.0);
assert_eq!(order.operation.commission, 4.5);
}
#[test]
fn reject() {
let new = LimitOrder::new(Direction::Buy, 100, 400.0);
assert_eq!(new.direction, Direction::Buy);
assert_eq!(new.lots, 100);
assert_eq!(new.price, 400.0);
let reject = new.reject("not enought money");
assert_eq!(reject.meta, "not enought money");
}
}