1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
use anchor_lang::prelude::*;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use super::*;
use crate::error::*;
use crate::error_msg;
#[derive(
Eq,
PartialEq,
Copy,
Clone,
TryFromPrimitive,
IntoPrimitive,
Debug,
AnchorSerialize,
AnchorDeserialize,
)]
#[repr(u8)]
pub enum PlaceOrderType {
/// Take existing orders up to price, max_base_quantity and max_quote_quantity.
/// If any base_quantity or quote_quantity remains, place an order on the book
Limit = 0,
/// Take existing orders up to price, max_base_quantity and max_quote_quantity.
/// Never place an order on the book.
ImmediateOrCancel = 1,
/// Never take any existing orders, post the order on the book if possible.
/// If existing orders can match with this order, do nothing.
PostOnly = 2,
/// Ignore price and take orders up to max_base_quantity and max_quote_quantity.
/// Never place an order on the book.
///
/// Equivalent to ImmediateOrCancel with price=i64::MAX.
Market = 3,
/// If existing orders match with this order, adjust the price to just barely
/// not match. Always places an order on the book.
PostOnlySlide = 4,
}
impl PlaceOrderType {
pub fn to_post_order_type(&self) -> Result<PostOrderType> {
match *self {
Self::Market => Err(error_msg!("Market is not a PostOrderType")),
Self::ImmediateOrCancel => Err(error_msg!("ImmediateOrCancel is not a PostOrderType")),
Self::Limit => Ok(PostOrderType::Limit),
Self::PostOnly => Ok(PostOrderType::PostOnly),
Self::PostOnlySlide => Ok(PostOrderType::PostOnlySlide),
}
}
}
#[derive(
Eq,
PartialEq,
Copy,
Clone,
TryFromPrimitive,
IntoPrimitive,
Debug,
AnchorSerialize,
AnchorDeserialize,
)]
#[repr(u8)]
pub enum PostOrderType {
/// Take existing orders up to price, max_base_quantity and max_quote_quantity.
/// If any base_quantity or quote_quantity remains, place an order on the book
Limit = 0,
/// Never take any existing orders, post the order on the book if possible.
/// If existing orders can match with this order, do nothing.
PostOnly = 2,
/// If existing orders match with this order, adjust the price to just barely
/// not match. Always places an order on the book.
PostOnlySlide = 4,
}
#[derive(
Eq,
PartialEq,
Copy,
Clone,
Default,
TryFromPrimitive,
IntoPrimitive,
Debug,
AnchorSerialize,
AnchorDeserialize,
)]
#[repr(u8)]
/// Self trade behavior controls how taker orders interact with resting limit orders of the same account.
/// This setting has no influence on placing a resting or oracle pegged limit order that does not match
/// immediately, instead it's the responsibility of the user to correctly configure his taker orders.
pub enum SelfTradeBehavior {
/// Both the maker and taker sides of the matched orders are decremented.
/// This is equivalent to a normal order match, except for the fact that no fees are applied.
#[default]
DecrementTake = 0,
/// Cancels the maker side of the trade, the taker side gets matched with other maker's orders.
CancelProvide = 1,
/// Cancels the whole transaction as soon as a self-matching scenario is encountered.
AbortTransaction = 2,
}
#[derive(
Eq,
PartialEq,
Copy,
Clone,
TryFromPrimitive,
IntoPrimitive,
Debug,
AnchorSerialize,
AnchorDeserialize,
)]
#[repr(u8)]
pub enum Side {
Bid = 0,
Ask = 1,
}
impl Side {
pub fn invert_side(self: &Side) -> Side {
match self {
Side::Bid => Side::Ask,
Side::Ask => Side::Bid,
}
}
/// Is `lhs` is a better order for `side` than `rhs`?
pub fn is_price_data_better(self: &Side, lhs: u64, rhs: u64) -> bool {
match self {
Side::Bid => lhs > rhs,
Side::Ask => lhs < rhs,
}
}
/// Is `lhs` is a better order for `side` than `rhs`?
pub fn is_price_better(self: &Side, lhs: i64, rhs: i64) -> bool {
match self {
Side::Bid => lhs > rhs,
Side::Ask => lhs < rhs,
}
}
/// Is `price` acceptable for a `limit` order on `side`?
pub fn is_price_within_limit(self: &Side, price: i64, limit: i64) -> bool {
match self {
Side::Bid => price <= limit,
Side::Ask => price >= limit,
}
}
}
/// SideAndOrderTree is a storage optimization, so we don't need two bytes for the data
#[derive(
Eq,
PartialEq,
Copy,
Clone,
TryFromPrimitive,
IntoPrimitive,
Debug,
AnchorSerialize,
AnchorDeserialize,
)]
#[repr(u8)]
pub enum SideAndOrderTree {
BidFixed = 0,
AskFixed = 1,
BidOraclePegged = 2,
AskOraclePegged = 3,
}
impl SideAndOrderTree {
pub fn new(side: Side, order_tree: BookSideOrderTree) -> Self {
match (side, order_tree) {
(Side::Bid, BookSideOrderTree::Fixed) => Self::BidFixed,
(Side::Ask, BookSideOrderTree::Fixed) => Self::AskFixed,
(Side::Bid, BookSideOrderTree::OraclePegged) => Self::BidOraclePegged,
(Side::Ask, BookSideOrderTree::OraclePegged) => Self::AskOraclePegged,
}
}
pub fn side(&self) -> Side {
match self {
Self::BidFixed | Self::BidOraclePegged => Side::Bid,
Self::AskFixed | Self::AskOraclePegged => Side::Ask,
}
}
pub fn order_tree(&self) -> BookSideOrderTree {
match self {
Self::BidFixed | Self::AskFixed => BookSideOrderTree::Fixed,
Self::BidOraclePegged | Self::AskOraclePegged => BookSideOrderTree::OraclePegged,
}
}
}