extern crate trade_aggregation;
use crate::acc_tracker::AccTracker;
use crate::{
max, min, Account, Config, FeeType, Margin, Order, OrderError, OrderType, Position, Side,
Validator,
};
use trade_aggregation::*;
const MAX_NUM_LIMIT_ORDERS: usize = 50;
const MAX_NUM_STOP_ORDERS: usize = 50;
#[derive(Debug, Clone)]
pub struct Exchange {
config: Config,
account: Account,
validator: Validator,
bid: f64,
ask: f64,
init: bool,
next_order_id: u64,
step: u64,
high: f64,
low: f64,
}
impl Exchange {
pub fn new(config: Config) -> Exchange {
assert!(config.leverage > 0.0);
let account = Account::new(config.leverage, config.starting_balance_base);
let validator = Validator::new(config.fee_maker, config.fee_taker);
Exchange {
config,
account,
validator,
bid: 0.0,
ask: 0.0,
init: true,
next_order_id: 0,
step: 0,
high: 0.0,
low: 0.0,
}
}
pub fn bid(&self) -> f64 {
self.bid
}
pub fn ask(&self) -> f64 {
self.ask
}
pub fn account(&self) -> &Account {
&self.account
}
pub fn account_mut(&mut self) -> &mut Account {
&mut self.account
}
pub fn consume_trade(&mut self, trade: &Trade) -> (Vec<Order>, bool) {
assert!(!self.config.use_candles);
if self.init {
self.init = false;
self.bid = trade.price;
self.ask = trade.price;
}
if trade.size > 0.0 {
self.ask = trade.price;
} else {
self.bid = trade.price;
}
self.validator.update(trade.price, trade.price);
if self.check_liquidation() {
self.liquidate();
return (vec![], true);
}
self.check_orders();
self.account.update(trade.price, trade.timestamp as u64);
self.step += 1;
return (self.account.executed_orders(), false);
}
pub fn consume_candle(&mut self, candle: &Candle) -> (Vec<Order>, bool) {
assert!(self.config.use_candles);
self.bid = candle.close;
self.ask = candle.close;
self.high = candle.high;
self.low = candle.low;
self.validator.update(candle.close, candle.close);
if self.check_liquidation() {
self.liquidate();
return (vec![], true);
}
self.check_orders();
self.account.update(candle.close, candle.timestamp as u64);
self.step += 1;
return (self.account.executed_orders(), false);
}
fn check_liquidation(&mut self) -> bool {
false
}
fn execute_market(&mut self, side: Side, amount_quote: f64) {
let price: f64 = match side {
Side::Buy => self.ask,
Side::Sell => self.bid,
};
let fee_quote = self.config.fee_taker * amount_quote;
let fee_base = fee_quote / price;
self.account.deduce_fees(fee_base);
self.account.change_position(side, amount_quote, price);
}
fn execute_limit(&mut self, side: Side, price: f64, amount_quote: f64) {
let fee_quote = self.config.fee_maker * amount_quote;
let fee_base = fee_quote / price;
self.account.deduce_fees(fee_base);
self.account.change_position(side, amount_quote, price);
}
fn liquidate(&mut self) {
debug!("liquidating");
if self.account.position().size() > 0.0 {
self.execute_market(Side::Sell, self.account.position().size());
} else {
self.execute_market(Side::Buy, self.account.position().size().abs());
}
}
fn check_orders(&mut self) {
for i in 0..self.account.active_limit_orders().len() {
match self.account.active_limit_orders()[i].order_type {
OrderType::Limit => self.handle_limit_order(i),
_ => panic!("there should only be limit orders in active_limit_orders"),
}
}
for i in 0..self.account.active_stop_orders().len() {
match self.account.active_stop_orders()[i].order_type {
OrderType::StopMarket => self.handle_stop_market_order(i),
_ => panic!("there should only be stop market orders in active_stop_orders"),
}
}
}
fn handle_stop_market_order(&mut self, order_idx: usize) {
match self.account().active_stop_orders()[order_idx].side {
Side::Buy => match self.config.use_candles {
true => {
if self.account().active_stop_orders()[order_idx].trigger_price > self.high {
return;
}
}
false => {
if self.account().active_stop_orders()[order_idx].trigger_price > self.ask {
return;
}
}
},
Side::Sell => match self.config.use_candles {
true => {
if self.account().active_stop_orders()[order_idx].trigger_price < self.low {
return;
}
}
false => {
if self.account().active_stop_orders()[order_idx].trigger_price > self.bid {
return;
}
}
},
}
self.execute_market(
self.account().active_stop_orders()[order_idx].side,
self.account().active_stop_orders()[order_idx].size,
);
self.account.finalize_stop_order(order_idx);
}
fn handle_limit_order(&mut self, order_idx: usize) {
let o: &Order = &self.account.active_limit_orders()[order_idx];
match o.side {
Side::Buy => {
match self.config.use_candles {
true => {
if self.low <= o.limit_price {
self.execute_limit(o.side, o.limit_price, o.size);
} else {
return;
}
}
false => {
if self.bid < o.limit_price {
self.execute_limit(o.side, o.limit_price, o.size);
} else {
return;
}
}
}
}
Side::Sell => {
match self.config.use_candles {
true => {
if self.high >= o.limit_price {
self.execute_limit(o.side, o.limit_price, o.size);
} else {
return;
}
}
false => {
if self.ask > o.limit_price {
self.execute_limit(o.side, o.limit_price, o.size);
} else {
return;
}
}
}
}
}
self.account.finalize_limit_order(order_idx);
}
pub fn submit_order(&mut self, mut order: Order) -> Result<Order, OrderError> {
match order.order_type {
OrderType::StopMarket => {
if self.account().active_limit_orders().len() >= MAX_NUM_LIMIT_ORDERS {
return Err(OrderError::MaxActiveOrders);
}
}
_ => {
if self.account().active_stop_orders().len() >= MAX_NUM_STOP_ORDERS {
return Err(OrderError::MaxActiveOrders);
}
}
}
self.validator.validate(&order, &self.account)?;
order.id = self.next_order_id;
self.next_order_id += 1;
order.timestamp = self.step;
return match order.order_type {
OrderType::Market => {
self.execute_market(order.side, order.size);
Ok(order)
}
_ => {
self.account.append_order(order.clone());
Ok(order)
}
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::round;
#[test]
fn submit_order_limit() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::market(Side::Buy, 500.0).unwrap();
exchange.submit_order(o).unwrap();
let o = Order::limit(Side::Buy, 900.0, 250.0).unwrap();
exchange.submit_order(o).unwrap();
assert_eq!(exchange.account().active_limit_orders().len(), 1);
let o = Order::limit(Side::Sell, 1200.0, 500.0).unwrap();
exchange.submit_order(o).unwrap();
assert_eq!(exchange.account().active_limit_orders().len(), 2);
}
#[test]
fn test_handle_limit_order() {
}
#[test]
fn handle_stop_market_order_w_trade() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::stop_market(Side::Buy, 1010.0, 100.0).unwrap();
exchange.submit_order(o).unwrap();
assert_eq!(exchange.account().active_stop_orders().len(), 1);
let t = Trade {
timestamp: 2,
price: 1010.0,
size: 100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account().position().size(), 100.0);
assert_eq!(exchange.account().position().entry_price(), 1010.0);
}
#[test]
fn handle_stop_market_order_w_candle() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: true,
leverage: 1.0,
};
let mut exchange = Exchange::new(config);
let c = Candle {
timestamp: 0,
open: 40_000.0,
high: 40_500.0,
low: 35_000.0,
close: 40_100.0,
volume: 0.0,
directional_trade_ratio: 0.0,
directional_volume_ratio: 0.0,
num_trades: 0,
arithmetic_mean_price: 0.0,
weighted_price: 0.0,
std_dev_prices: 0.0,
std_dev_sizes: 0.0,
time_velocity: 0.0,
};
exchange.consume_candle(&c);
let o = Order::stop_market(Side::Buy, 40_600.0, 4060.0).unwrap();
exchange.submit_order(o).unwrap();
let c = Candle {
timestamp: 0,
open: 40_100.0,
high: 40_700.0,
low: 36_000.0,
close: 40_500.0,
volume: 0.0,
directional_trade_ratio: 0.0,
directional_volume_ratio: 0.0,
num_trades: 0,
arithmetic_mean_price: 0.0,
weighted_price: 0.0,
std_dev_prices: 0.0,
std_dev_sizes: 0.0,
time_velocity: 0.0,
};
exchange.consume_candle(&c);
assert_eq!(exchange.account().position().size(), 4060.0);
assert_eq!(round(exchange.account().position().value(), 1), 0.1);
assert_eq!(round(exchange.account().margin().position_margin(), 1), 0.1);
}
#[test]
fn long_market_win_full() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let value = exchange.account().margin().available_balance() * 0.8;
let size = exchange.ask * value;
let o = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base = size * fee_taker;
let fee_asset1 = fee_base / exchange.bid;
assert_eq!(exchange.account().position().size(), size);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), value);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.0 - fee_asset1
);
assert_eq!(exchange.account().margin().position_margin(), 0.8);
assert_eq!(
round(exchange.account().margin().available_balance(), 4),
round(0.2 - fee_asset1, 4)
);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.4);
let size = 800.0;
let fee_base2 = size * fee_taker;
let fee_asset2 = fee_base2 / 2000.0;
let o = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().position().value(), 0.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.4 - fee_asset1 - fee_asset2
);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(
exchange.account().margin().available_balance(),
1.4 - fee_asset1 - fee_asset2
);
}
#[test]
fn long_market_loss_full() {
if let Err(_) = pretty_env_logger::try_init() {}
let config = Config {
fee_maker: 0.0,
fee_taker: 0.0,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::market(Side::Buy, 800.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), 800.0);
assert_eq!(exchange.account().position().value(), 0.8);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(exchange.account().margin().wallet_balance(), 1.0);
assert_eq!(
round(exchange.account().margin().available_balance(), 2),
0.2
);
assert_eq!(exchange.account().margin().order_margin(), 0.0);
assert_eq!(exchange.account().margin().position_margin(), 0.8);
let t = Trade {
timestamp: 0,
price: 800.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 800.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account.position().unrealized_pnl(), -0.2);
let o = Order::market(Side::Sell, 800.0).unwrap();
exchange.submit_order(o).unwrap();
exchange.check_orders();
let fee_base0 = fee_taker * 800.0;
let fee_asset0 = fee_base0 / 1000.0;
let fee_base1 = fee_taker * 800.0;
let fee_asset1 = fee_base1 / 800.0;
let fee_combined = fee_asset0 + fee_asset1;
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().position().value(), 0.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
round(exchange.account().margin().wallet_balance(), 5),
round(0.8 - fee_combined, 5)
);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(
round(exchange.account().margin().available_balance(), 5),
round(0.8 - fee_combined, 5)
);
}
#[test]
fn short_market_win_full() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::market(Side::Sell, 800.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), -800.0);
let t = Trade {
timestamp: 0,
price: 800.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 800.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account.position().unrealized_pnl(), 0.2);
let o = Order::market(Side::Buy, 800.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base0 = fee_taker * 800.0;
let fee_asset0 = fee_base0 / 1000.0;
let fee_base1 = fee_taker * 800.0;
let fee_asset1 = fee_base1 / 800.0;
let fee_combined = fee_asset0 + fee_asset1;
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().position().value(), 0.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.2 - fee_combined
);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(
exchange.account().margin().available_balance(),
1.2 - fee_combined
);
}
#[test]
fn short_market_loss_full() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let value = exchange.account.margin().available_balance() * 0.4;
let size = exchange.ask * value;
let o = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base1 = size * fee_taker;
let fee_asset1 = fee_base1 / exchange.bid;
assert_eq!(exchange.account().position().size(), -size);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), value);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.0 - fee_asset1
);
assert_eq!(exchange.account().margin().position_margin(), 0.4);
assert_eq!(
exchange.account().margin().available_balance(),
0.6 - fee_asset1
);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: -100.0,
};
exchange.consume_trade(&t);
let size = 400.0;
let fee_base2 = size * fee_taker;
let fee_asset2 = fee_base2 / 2000.0;
assert_eq!(exchange.account.position().unrealized_pnl(), -0.2);
let o = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().position().value(), 0.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
round(exchange.account().margin().wallet_balance(), 5),
round(0.8 - fee_asset1 - fee_asset2, 5)
);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(
round(exchange.account().margin().available_balance(), 5),
round(0.8 - fee_asset1 - fee_asset2, 5)
);
}
#[test]
fn long_market_win_partial() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let value = exchange.account.margin().available_balance() * 0.8;
let size = exchange.ask * value;
let o = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base = size * fee_taker;
let fee_asset1 = fee_base / exchange.bid;
assert_eq!(exchange.account().position().size(), size);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), value);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.0 - fee_asset1
);
assert_eq!(exchange.account().margin().position_margin(), 0.8);
assert_eq!(
round(exchange.account().margin().available_balance(), 4),
round(0.2 - fee_asset1, 4)
);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: -100.0,
};
exchange.consume_trade(&t);
let size = 400.0;
let fee_base2 = size * fee_taker;
let fee_asset2 = fee_base2 / 2000.0;
assert_eq!(exchange.account().position().unrealized_pnl(), 0.4);
let o = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), 400.0);
assert_eq!(exchange.account().position().value(), 0.2);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.2);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.2 - fee_asset1 - fee_asset2
);
assert_eq!(exchange.account().margin().position_margin(), 0.4);
assert_eq!(
round(exchange.account().margin().available_balance(), 5),
round(0.8 - fee_asset1 - fee_asset2, 5)
);
}
#[test]
fn long_market_loss_partial() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::market(Side::Buy, 800.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), 800.0);
let t = Trade {
timestamp: 0,
price: 800.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 800.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account.position().unrealized_pnl(), -0.2);
let o = Order::market(Side::Sell, 400.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base0 = fee_taker * 800.0;
let fee_asset0 = fee_base0 / 1000.0;
let fee_base1 = fee_taker * 400.0;
let fee_asset1 = fee_base1 / 800.0;
let fee_combined = fee_asset0 + fee_asset1;
assert_eq!(exchange.account().position().size(), 400.0);
assert_eq!(exchange.account().position().value(), 0.5);
assert_eq!(exchange.account().position().unrealized_pnl(), -0.1);
assert_eq!(
round(exchange.account().margin().wallet_balance(), 6),
round(0.9 - fee_combined, 6)
);
assert_eq!(exchange.account().margin().position_margin(), 0.4);
assert_eq!(
round(exchange.account().margin().available_balance(), 6),
round(0.5 - fee_combined, 6)
);
}
#[test]
fn short_market_win_partial() {
let config = Config {
fee_maker: 0.0,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let o = Order::market(Side::Sell, 800.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), -800.0);
assert_eq!(exchange.account().position().value(), 0.8);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(exchange.account().margin().wallet_balance(), 0.9994);
assert_eq!(
round(exchange.account().margin().available_balance(), 3),
0.199
);
assert_eq!(exchange.account().margin().order_margin(), 0.0);
assert_eq!(exchange.account().margin().position_margin(), 0.8);
let t = Trade {
timestamp: 0,
price: 800.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 800.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account.position().unrealized_pnl(), 0.2);
let o = Order::market(Side::Buy, 400.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base0 = fee_taker * 800.0;
let fee_asset0 = fee_base0 / 1000.0;
let fee_base1 = fee_taker * 400.0;
let fee_asset1 = fee_base1 / 800.0;
let fee_combined = fee_asset0 + fee_asset1;
assert_eq!(exchange.account().position().size(), -400.0);
assert_eq!(exchange.account().position().value(), 0.5);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.1);
assert_eq!(
round(exchange.account().margin().wallet_balance(), 6),
round(1.1 - fee_combined, 6)
);
assert_eq!(exchange.account().margin().position_margin(), 0.4);
assert_eq!(
round(exchange.account().margin().available_balance(), 6),
round(0.7 - fee_combined, 6)
);
}
#[test]
fn short_market_loss_partial() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let value = exchange.account.margin().available_balance() * 0.8;
let size = exchange.ask * value;
let o = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
exchange.check_orders();
let fee_base1 = size * fee_taker;
let fee_asset1 = fee_base1 / exchange.bid;
assert_eq!(exchange.account().position().size(), -size);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), value);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.0 - fee_asset1
);
assert_eq!(exchange.account().margin().position_margin(), 0.8);
assert_eq!(
round(exchange.account().margin().available_balance(), 4),
round(0.2 - fee_asset1, 4)
);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: -100.0,
};
exchange.consume_trade(&t);
let size = 400.0;
let fee_base2 = size * fee_taker;
let fee_asset2 = fee_base2 / 2000.0;
assert_eq!(exchange.account().position().unrealized_pnl(), -0.4);
let o = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
let t = Trade {
timestamp: 0,
price: 2000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 2000.0,
size: -100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account().position().size(), -400.0);
assert_eq!(exchange.account().position().value(), 0.2);
assert_eq!(exchange.account().position().unrealized_pnl(), -0.2);
assert_eq!(
round(exchange.account().margin().wallet_balance(), 5),
round(0.8 - fee_asset1 - fee_asset2, 5)
);
assert_eq!(exchange.account().margin().position_margin(), 0.4);
assert_eq!(
round(exchange.account().margin().available_balance(), 2),
round(0.4 - fee_asset1 - fee_asset2, 2)
);
}
#[test]
fn test_market_roundtrip() {
let config = Config {
fee_maker: -0.00025,
fee_taker: 0.00075,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let fee_taker = config.fee_taker;
let mut exchange = Exchange::new(config);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let value = exchange.account().margin().available_balance() * 0.9;
let size = exchange.ask * value;
let buy_order = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(buy_order);
assert!(order_err.is_ok());
exchange.check_orders();
let sell_order = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(sell_order);
assert!(order_err.is_ok());
let fee_base = size * fee_taker;
let fee_asset = fee_base / exchange.ask;
exchange.check_orders();
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), 0.0);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert_eq!(
exchange.account().margin().wallet_balance(),
1.0 - 2.0 * fee_asset
);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(
exchange.account().margin().available_balance(),
1.0 - 2.0 * fee_asset
);
let size = 900.0;
let buy_order = Order::market(Side::Buy, size).unwrap();
let order_err = exchange.submit_order(buy_order);
assert!(order_err.is_ok());
exchange.check_orders();
let size = 950.0;
let sell_order = Order::market(Side::Sell, size).unwrap();
let order_err = exchange.submit_order(sell_order);
assert!(order_err.is_ok());
exchange.check_orders();
assert_eq!(exchange.account().position().size(), -50.0);
assert_eq!(exchange.account().position().entry_price(), 1000.0);
assert_eq!(exchange.account().position().value(), 0.05);
assert_eq!(exchange.account().position().unrealized_pnl(), 0.0);
assert!(exchange.account().margin().wallet_balance() < 1.0);
assert_eq!(exchange.account().margin().position_margin(), 0.05);
assert!(exchange.account().margin().available_balance() < 1.0);
}
#[test]
fn check_liquidation() {
}
#[test]
fn test_liquidate() {
}
#[test]
fn execute_limit() {
let config = Config {
fee_maker: 0.0,
fee_taker: 0.001,
starting_balance_base: 1.0,
use_candles: false,
leverage: 1.0,
};
let mut exchange = Exchange::new(config.clone());
let t = Trade {
timestamp: 0,
price: 1000.0,
size: 100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 0,
price: 1000.0,
size: -100.0,
};
exchange.consume_trade(&t);
let o: Order = Order::limit(Side::Buy, 900.0, 450.0).unwrap();
exchange.submit_order(o).unwrap();
assert_eq!(exchange.account().active_limit_orders().len(), 1);
assert_eq!(exchange.account().margin().available_balance(), 0.5);
assert_eq!(exchange.account().margin().order_margin(), 0.5);
let t = Trade {
timestamp: 1,
price: 750.0,
size: 100.0,
};
let (exec_orders, liq) = exchange.consume_trade(&t);
assert!(!liq);
assert_eq!(exec_orders.len(), 0);
let t = Trade {
timestamp: 1,
price: 750.0,
size: -100.0,
};
let (exec_orders, liq) = exchange.consume_trade(&t);
assert!(!liq);
assert_eq!(exec_orders.len(), 1);
assert_eq!(exchange.bid, 750.0);
assert_eq!(exchange.ask, 750.0);
assert_eq!(exchange.account().active_limit_orders().len(), 0);
assert_eq!(exchange.account().position().size(), 450.0);
assert_eq!(exchange.account().position().value(), 0.6);
assert_eq!(exchange.account().position().entry_price(), 900.0);
assert_eq!(exchange.account().margin().wallet_balance(), 1.0);
let o: Order = Order::limit(Side::Sell, 1000.0, 450.0).unwrap();
let order_err = exchange.submit_order(o);
assert!(order_err.is_ok());
assert_eq!(exchange.account().active_limit_orders().len(), 1);
let t = Trade {
timestamp: 1,
price: 1200.0,
size: -100.0,
};
exchange.consume_trade(&t);
let t = Trade {
timestamp: 1,
price: 1200.0,
size: 100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account().active_limit_orders().len(), 0);
assert_eq!(exchange.account().position().size(), 0.0);
assert_eq!(exchange.account().margin().position_margin(), 0.0);
assert_eq!(exchange.account().margin().wallet_balance(), 1.05);
assert_eq!(exchange.account().margin().available_balance(), 1.05);
let o: Order = Order::limit(Side::Sell, 1200.0, 600.0).unwrap();
exchange.submit_order(o).unwrap();
assert_eq!(exchange.account().active_limit_orders().len(), 1);
let t = Trade {
timestamp: 1,
price: 1201.0,
size: 100.0,
};
exchange.consume_trade(&t);
assert_eq!(exchange.account().position().size(), -600.0);
assert_eq!(round(exchange.account().position().value(), 1), 0.5);
assert_eq!(round(exchange.account().margin().position_margin(), 1), 0.5);
assert_eq!(round(exchange.account().margin().wallet_balance(), 2), 1.05);
assert_eq!(
round(exchange.account().margin().available_balance(), 2),
0.55
);
}
}