qust

Crates.ioqust
lib.rsqust
version0.1.4
sourcesrc
created_at2024-09-28 17:21:10.828444
updated_at2024-10-07 16:55:42.309557
descriptionA crate for finance quantative reserach
homepage
repositoryhttps://github.com/baiguoname/qust
max_upload_size
id1390250
size305,248
(baiguoname)

documentation

https://docs.rs/qust/

README

Qust

Qust is a Rust libraries for building live-trading and back-test systems. It has the following features:

  • Fast: It's way to handle or to save the kline data, tick data and strategy makes the backtest and live trading fast.
  • Extensible: It provide many ways to build a strategy, and new ways can be implemented by needs, so that you can focus what you care. You can build a simple strategy or complicated one, then backtest it on kline data(for quick scruch) or tick data, on put it on live trading directly. For example, you can build a strategy by following ways:
    1. Accept kline flow, and return a target position.

    2. Accept tick data flow, and return a target position.

    3. Accept tick data flow, and return an order action.

    4. Accept kline and tick flow, return a target positon or an order action.

    5. Accept kline flow, and return a bool.(a least two of it make a strategy, one for open position, another for close)

    6. Add filter conditions to an existed strategy.

    7. Add algorithm method to an existed strategy.

    8. Add order matching methods when backtest a strategy.

    9. Add valitality manager to strategies.

    10. Add portoflio manager to a pool of strategies. and so on.

See this notebook Example for more detail.

Examples

Add this to Cargo.toml:

qust-derive = { version = ">=0.1" }
qust-ds = { version = ">=0.1" }
qust = { version = ">=0.1" }
qust-api = { version = ">=0.1"}
qust-io = {  version = ">=0.1"}
serde = "*"
serde_json = "*"
itertools = "*"
typetag = "*"
tokio = "*"
ta = { version = "0.5.0" }

You can build a strategy basing on kline data and backtest in on kline:

use qust_derive::*;
use qust_ds::prelude::*;
use qust::prelude::*;
use qust_api::prelude::*;
use qust_io::prelude::*;
use ta::{ Next, indicators::SimpleMovingAverage as SMA };

#[ta_derive2]
pub struct TwoMaStra {
    pub short_period: usize,
    pub long_period: usize,
}

#[typetag::serde]
impl Ktn for TwoMaStra {
    fn ktn(&self,_di: &Di) -> RetFnKtn {
        let mut last_norm_hold = NormHold::No;
        let mut short_ma = SMA::new(self.short_period).unwrap();
        let mut long_ma = SMA::new(self.long_period).unwrap();
        let mut last_short_value = 0f64;
        let mut last_long_value = 0f64;
        Box::new(move |di_kline| {
            let c = di_kline.di.c()[di_kline.i] as f64;
            let short_value = short_ma.next(c);
            let long_value = long_ma.next(c);
            match last_norm_hold {
                NormHold::No if di_kline.i != 0 => {
                    if last_short_value < last_long_value && short_value >= long_value {
                        last_norm_hold = NormHold::Lo(1.);
                    }
                }
                NormHold::Lo(_) if short_value < long_value => {
                    last_norm_hold = NormHold::No;
                }
                _ => {}
            }
            last_short_value = short_value;
            last_long_value = long_value;
            last_norm_hold.clone()
        })
    }
}

#[tokio::main]
async fn main() {
    let di = read_remote_kline_data().await;
    let two_ma_stra = TwoMaStra { short_period: 9, long_period: 20 };
    let two_ma_stra_ptm: Ptm = two_ma_stra.ktn_box().to_ktn().to_ptm();
    let pnl_res_dt: PnlRes<dt> = two_ma_stra_ptm.bt_kline((&di, cs1));
    pnl_res_dt.to_csv("pnl_res_dt.csv"); // save the pnl to local csv;
}


Or build strategy basing on tick data, then backtest it in tick data or put it on live trading:

use qust_derive::*;
use qust_ds::prelude::*;
use qust::prelude::*;
use qust_api::prelude::*;
use qust_io::prelude::*;
use ta::{ Next, indicators::SimpleMovingAverage as SMA };

#[ta_derive2]
pub struct TwoMaTickOrderAction;

impl ApiType for TwoMaTickOrderAction {
    fn api_type(&self) -> RetFnApi {
        let mut short_ma = SMA::new(1200).unwrap();
        let mut long_ma = SMA::new(2400).unwrap();
        let mut last_short_value = 0f64;
        let mut last_long_value = 0f64;
        Box::new(move |stream_api| {
            let c = stream_api.tick_data.c as f64;
            let short_value = short_ma.next(c);
            let long_value = long_ma.next(c);
            let hold = stream_api.hold.sum();
            let mut res = OrderAction::No;
            if hold == 0 {
                match last_short_value != 0. && last_short_value < last_long_value && short_value >= long_value {
                    true => {
                        res = OrderAction::LoOpen(1, stream_api.tick_data.bid1);
                    }
                    false => (),
                }
            } else if hold > 0 && short_value < long_value {
                res = OrderAction::ShClose(hold, stream_api.tick_data.ask1);
            }
            last_short_value = short_value;
            last_long_value = long_value;
            res
        })
    }
}

async fn backtest_tick() {
    let tick_data = read_remote_tick_data().await;
    let trade_info_vec: Vec<TradeInfo> = WithMatchBox {
        data: TwoMaTickOrderAction,
        match_box: Box::new(MatchSimple),
    }
        .bt_tick(&tick_data);
    TickerTradeInfo {
        ticker: aler,
        trade_info_vec: trade_info_vec.clone() 
    }.into_pnl_res().to_csv("pnl_res_tick.csv");
}

#[tokio::main]
async fn main() {
    backtest_tick().await;
    // put it on live trading
    let live_stra_pool = TwoMaTickOrderAction.to_live_stra_pool(vec![aler, eber]);
    let ticker_contract_map = ["al2400", "eb2401"].config_parse();
    let stra_api = StraApi::new( live_stra_pool, ticker_contract_map);
    let account = SimnowAccount("171807", "Tangjihede00").config_parse();//account , password
    let running_api = running_api_ctp(stra_api, account);
    run_ctp(running_api).await;
}
Commit count: 16

cargo fmt