ek-data 0.2.8

Trading data contracts for the enkeep ek ecosystem: venue enums, zero-dep epoch-nanosecond time, decimal-exact price rounding, vt-symbol convention, bus event dictionary, tick/bar/order/trade/position/account/contract objects and request objects (Python enkeep_trade trader/object.py + constant.py, faithfully ported).
Documentation
//! 下单请求(`object.py:521-588` + 方法四件套)。

use crate::constant::{Direction, Exchange, Offset, OrderType, Status};
use crate::order::OrderData;

/// 发往网关的开仓/平仓请求。
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OrderRequest {
    /// 合约代码
    pub symbol: String,
    /// 交易所
    pub exchange: Exchange,
    /// 方向(必填)
    pub direction: Direction,
    /// 委托类型(Python `type`;Rust 改名避关键字,serde 线上字段名还原为 `"type"`)
    #[cfg_attr(feature = "serde", serde(rename = "type"))]
    pub order_type: OrderType,
    /// 委托量
    pub volume: f64,
    /// 委托价(市价单可 0)
    pub price: f64,
    /// 开平(缺省 NONE)
    pub offset: Offset,
    /// 策略/来源备注
    pub reference: String,
    /// 追单统计字段(原委托号)
    pub old_order_id: String,
}

impl OrderRequest {
    /// `en_symbol`(派生方法)。
    pub fn en_symbol(&self) -> String {
        crate::symbol::vt_symbol(&self.symbol, self.exchange)
    }

    /// 生成订单数据(`object.py:541`;补 id/gateway 两字段即成 OrderData;
    /// reference 一并搬入,对齐原稿 554 行)。
    pub fn create_order_data(&self, orderid: &str, gateway_name: &str) -> OrderData {
        OrderData {
            gateway_name: gateway_name.to_string(),
            symbol: self.symbol.clone(),
            exchange: self.exchange,
            orderid: orderid.to_string(),
            order_type: self.order_type,
            direction: Some(self.direction),
            offset: self.offset,
            price: self.price,
            volume: self.volume,
            reference: self.reference.clone(),
            // ── 其余字段显式给值,不靠 `..Default`(那会让新增字段静默变默认)──
            // 这是**本地意向单**:刚生成、还没经柜台,所以是"已提交"而不是"未成交"。
            // 柜台真回报到了会按 `onRtnOrder` 整条覆盖。
            status: Status::SUBMITTING,
            // 没成交当然是 0;成交价对委托单没有意义(价在 TradeData 里)。
            traded: 0.0,
            traded_price: 0.0,
            // 本地生成时刻由调用方决定要不要填;此处不装钟。
            datetime: None,
            // 合约中文名要查合约表,`ek-data` 不持有合约表。
            name: String::new(),
            // 本地单还没有柜台反馈,没有废单原因。
            status_msg: String::new(),
        }
    }

    /// 最小交易量校验(`object.py:559`;volume >= min_volume)。
    pub fn is_min_volume_ok(&self, min_volume: f64) -> bool {
        self.volume >= min_volume
    }

    /// 最大交易量校验(`object.py:567`;volume <= max_volume)。
    pub fn is_max_volume_ok(&self, max_volume: f64) -> bool {
        self.volume <= max_volume
    }

    /// 区间校验(`object.py:575`;闭区间 [min_volume, max_volume])。
    pub fn is_volume_range_ok(&self, min_volume: f64, max_volume: f64) -> bool {
        self.volume >= min_volume && self.volume <= max_volume
    }

    /// 倍数校验(`object.py:582`;volume 是 sub_beishu 的整数倍;
    /// Python 除零会抛 ZeroDivisionError,Rust 版约定返回 false)。
    pub fn is_volume_beishu_ok(&self, sub_beishu: f64) -> bool {
        if sub_beishu == 0.0 {
            return false;
        }
        let r = self.volume - (self.volume / sub_beishu).trunc() * sub_beishu;
        r == 0.0
    }
}