Leveraged Futures Exchange for Simulated Trading (LFEST)

:warning: This is a personal project, use a your own risk.

:warning: The results may not represent real trading results on any given exchange.

lfest-rs is a blazingly fast simulated exchange capable of leveraged positions. It gets fed external data either as a trade or a candle to update the internal state and check for order execution. For simplicity's sake (and performance) the exchange does not use an order book It also cross-leverages positions so the whole margin balance is used as collateral.

Order Types

The supported order types are: - market - aggressively execute against the best bid / ask - limit - passively place an order into the orderbook - stop_market - A protective but aggressive market order which is triggered at a specific price

Currently limit and stopmarket orders may not work as expected. I advise you to only use market orders or fix limit and stoporder margin calculation and create a pull request.

Performance Metrics:

The following performance metrics are available through AccTracker struct: - winratio - profitlossratio - totalrpnl - sharpe - sharpedailyreturns - sortino - cumulative fees - sharpesterlingratio - maxdrawdown - maxupnldrawdown - numtrades - turnover - tradepercentage - buyratio - limitorderfillratio - limitordercancellationratio

Some of these metric may behave differently from what you would expect, so make sure to take a look at the code.

How to use

To use this crate in your project, add the following to your Cargo.toml: [dependencies] lfest = "^0.7.0"

Then proceed to use it in your code.

Here is a basic example: ```rust //! Example usage of Exchange using external trade data. //! A randomly acting agent places market buy / sell orders every 100 candles

mod load_trades;

[macro_use]

extern crate log;

use lfest::{Config, Exchange, Order, OrderError, Side}; use loadtrades::loadpricesfromcsv; use rand::{thread_rng, Rng}; use std::time::Instant;

fn main() { let t0 = Instant::now();

let config = Config {
    fee_maker: -0.00025,
    fee_taker: 0.001,
    starting_balance_base: 1.0,
    use_candles: false,
    leverage: 1.0,
};
let mut exchange = Exchange::new(config);

// load trades from csv file
let prices = load_prices_from_csv("./data/Bitmex_XBTUSD_1M.csv").unwrap();

// use random action every 100 trades to buy or sell
let mut rng = thread_rng();

for (i, p) in prices.iter().enumerate() {
    let (exec_orders, liq) = exchange.update_state(*p, *p, i as u64);
    if liq {
        // check liquidation
    }
    println!("executed orders: {:?}", exec_orders);

    if i % 100 == 0 {
        // randomly buy or sell using a market order
        let r = rng.gen::<f64>();
        // Trade a fraction of the available wallet balance
        let order_size: f64 = exchange.account().margin().wallet_balance() * 0.1;
        let order: Order = if r > 0.5 {
            Order::market(Side::Sell, order_size).unwrap() // Sell using market order
        } else {
            Order::market(Side::Buy, order_size).unwrap() // Buy using market order
        };
        // Handle order error here if needed
        let response: Result<Order, OrderError> = exchange.submit_order(order);
        match response {
            Ok(order) => println!("succesfully submitted order: {:?}", order),
            Err(order_err) => match order_err {
                OrderError::MaxActiveOrders => {
                    error!("maximum number of active orders reached")
                }
                OrderError::InvalidLimitPrice => error!("invalid limit price of order"),
                OrderError::InvalidTriggerPrice => error!("invalid trigger price of order"),
                OrderError::InvalidOrderSize => error!("invalid order size"),
                OrderError::NotEnoughAvailableBalance => {
                    error!("not enough available balance in account")
                }
            },
        }
    }
}
println!(
    "time to simulate 1 million historical trades and {} orders: {}ms",
    exchange.account().acc_tracker().num_trades(),
    t0.elapsed().as_millis()
);
analyze_results(&exchange);

}

/// analyze the resulting performance metrics of the traded orders fn analyzeresults(e: &Exchange) { let winratio = e.account().acctracker().winratio(); let profitlossratio = e.account().acctracker().profitlossratio(); let rpnl = e.account().acctracker().totalrpnl(); let sharpe = e.account().acctracker().sharpe(); let sortino = e.account().acctracker().sortino(); let sterlingratio = e.account().acctracker().sharpesterlingratio(); let maxdrawdown = e.account().acctracker().maxdrawdown(); let maxupnldrawdown = e.account().acctracker().maxupnldrawdown(); let numtrades = e.account().acctracker().numtrades(); let turnover = e.account().acctracker().turnover(); let buyratio = e.account().acctracker().buyratio(); println!("winratio: {:.2}, profitlossratio: {:.2}, rpnl: {:.2}, sharpe: {:.2}, sortino: {:.2}, sr: {:.2}, \ dd: {:.2}, upnldd: {:.2}, #trades: {}, turnover: {}, buyratio: {:.2},", winratio, profitlossratio, rpnl, sharpe, sortino, sterlingratio, maxdrawdown, maxupnldrawdown, numtrades, turnover, buyratio, ); } ```

TODOs:

Contributions

If you find a bug or would like to help out, feel free to create a pull-request.

Donations :moneybag: :moneywithwings:

I you would like to support the development of this crate, feel free to send over a donation:

Monero (XMR) address: plain 47xMvxNKsCKMt2owkDuN1Bci2KMiqGrAFCQFSLijWLs49ua67222Wu3LZryyopDVPYgYmAnYkSZSz9ZW2buaDwdyKTWGwwb

monero

License

Copyright (C) 2020

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

GNU AGPLv3