architect_api/algo/
twap.rs

1use super::{common_params::TakeThrough, *};
2use crate::{
3    symbology::{ExecutionVenue, MarketdataVenue},
4    AccountIdOrName, Dir, HumanDuration,
5};
6use anyhow::{bail, Result};
7use chrono::{DateTime, Utc};
8use rust_decimal::Decimal;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
12pub struct Twap;
13
14impl Algo for Twap {
15    const NAME: &'static str = "TWAP";
16
17    type Params = TwapParams;
18    type Status = TwapStatus;
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
22pub struct TwapParams {
23    pub symbol: String,
24    pub marketdata_venue: MarketdataVenue,
25    pub execution_venue: ExecutionVenue,
26    pub account: Option<AccountIdOrName>,
27    pub dir: Dir,
28    pub quantity: Decimal,
29    pub interval: HumanDuration,
30    /// The TWAP will finish within 1 interval of the end time.
31    pub end_time: DateTime<Utc>,
32    pub reject_lockout: HumanDuration,
33    /// When placing an order, how aggressively to take.
34    pub take_through: TakeThrough,
35}
36
37impl DisplaySymbols for TwapParams {
38    fn display_symbols(&self) -> Option<Vec<String>> {
39        Some(vec![self.symbol.clone()])
40    }
41}
42
43impl Validate for TwapParams {
44    fn validate(&self) -> Result<()> {
45        if !self.quantity.is_sign_positive() {
46            bail!("quantity must be positive");
47        }
48        if self.interval.num_milliseconds() < 100 {
49            bail!("interval must be >= 100ms");
50        }
51        // if self.reject_lockout.num_milliseconds() < 500
52        //     || self.reject_lockout.num_seconds() > 300
53        // {
54        //     bail!("reject lockout must be between 0.5 seconds and 300 seconds");
55        // }
56        Ok(())
57    }
58}
59
60#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct TwapStatus {
62    pub realized_twap: Option<Decimal>,
63    pub quantity_filled: Decimal,
64}