mod common;
use common::{Capture, fixture, wait_until};
use ek_data::{
AccountData, Direction, EVENT_ACCOUNT, EVENT_ORDER, EVENT_TRADE, Exchange, Offset, OrderData,
OrderRequest, OrderType, Status, TradeData,
};
use ek_event::Event;
use std::sync::Arc;
use std::time::Duration;
fn fx_req() -> OrderRequest {
OrderRequest {
symbol: "IF2506".into(),
exchange: Exchange::CFFEX,
direction: Direction::LONG,
order_type: OrderType::LIMIT,
volume: 2.0,
price: 3520.2,
offset: Offset::OPEN,
reference: "demo".into(),
old_order_id: String::new(),
}
}
#[test]
fn f3_gates_on_request() {
let req = fx_req();
assert!(req.is_min_volume_ok(1.0), "2 >= 1 满 min");
assert!(req.is_min_volume_ok(2.0), "闭口径 >=(object.py:559)");
assert!(!req.is_min_volume_ok(3.0));
assert!(req.is_max_volume_ok(50.0));
assert!(!req.is_max_volume_ok(1.0));
assert!(
req.is_volume_range_ok(1.0, 50.0),
"闭区间口径(object.py:575)"
);
assert!(!req.is_volume_range_ok(3.0, 50.0));
assert!(req.is_volume_beishu_ok(0.5));
assert!(!req.is_volume_beishu_ok(1.5), "2 不是 1.5 的整数倍");
assert_eq!(ek_data::round_to(3520.3, 0.2), 3520.4);
}
#[test]
fn f4_order_lifecycle_over_bus() {
let eng = ek_event::EventEngine::new();
let live = Capture::<OrderData>::new(); let bare = Capture::<OrderData>::new(); let other = Capture::<OrderData>::new(); live.register_on(&eng, format!("{EVENT_ORDER}xt.xt.1"));
bare.register_on(&eng, EVENT_ORDER);
other.register_on(&eng, format!("{EVENT_ORDER}xt.xt.2"));
eng.start();
let req = fx_req();
let o = req.create_order_data("xt.1", "xt");
assert_eq!(o.status, Status::SUBMITTING);
assert_eq!(o.reference, "demo");
assert_eq!(o.en_orderid(), "xt.xt.1");
assert!(o.is_active());
let o_key = format!("{EVENT_ORDER}{}", o.en_orderid());
let mut submitted = o.clone();
submitted.status = Status::ALLTRADED;
submitted.traded = 2.0;
submitted.datetime = Some(fixture::t0());
eng.put(Event::with_data(o_key.clone(), Arc::new(o.clone())))
.unwrap();
eng.put(Event::with_data(o_key, Arc::new(submitted.clone())))
.unwrap();
let mut other_o = o.clone();
other_o.orderid = "xt.2".into();
eng.put(Event::with_data(
format!("{EVENT_ORDER}{}", other_o.en_orderid()),
Arc::new(other_o),
))
.unwrap();
assert!(wait_until(Duration::from_secs(2), || live.len() == 2));
eng.stop();
assert_eq!(bare.len(), 0, "裸键未授权(本场景只发组合键)");
assert_eq!(other.len(), 1, "异单不进本单的键");
let timeline = live.all();
assert_eq!(timeline[0].status, Status::SUBMITTING);
assert_eq!(timeline[1].status, Status::ALLTRADED);
assert_eq!(timeline[1].traded, 2.0);
assert!(!timeline[1].is_active(), "终态即非活跃");
assert_eq!(timeline[1].datetime, Some(fixture::t0()));
let cxl = timeline[1].create_cancel_request();
assert_eq!(cxl.orderid, "xt.1");
assert_eq!(cxl.symbol, "IF2506");
assert_eq!(cxl.exchange, Exchange::CFFEX);
}
#[test]
fn f5_trade_flow() {
let eng = ek_event::EventEngine::new();
let per = Capture::<TradeData>::new(); let acct = Capture::<AccountData>::new(); eng.start();
let req = fx_req();
let o = req.create_order_data("xt.1", "xt");
let trade = TradeData {
gateway_name: "xt".into(),
symbol: o.symbol.clone(),
exchange: o.exchange,
orderid: o.orderid.clone(),
tradeid: "xt.90001".into(),
direction: Some(Direction::LONG),
offset: Offset::OPEN,
price: 3520.2,
volume: 2.0,
datetime: Some(fixture::t0()),
..Default::default()
};
let acct0 = AccountData {
gateway_name: "xt".into(),
accountid: "demo".into(),
balance: 100_000.0,
frozen: 300.0,
};
assert_eq!(trade.en_tradeid(), "xt.xt.90001");
assert_eq!(trade.en_orderid(), "xt.xt.1");
let t_key = format!("{EVENT_TRADE}{}", trade.en_tradeid());
let a_key = format!("{EVENT_ACCOUNT}{}", acct0.en_accountid());
per.register_on(&eng, t_key.clone());
acct.register_on(&eng, a_key.clone());
eng.put(Event::with_data(t_key, Arc::new(trade.clone())))
.unwrap();
assert_eq!(acct0.available(), 99_700.0);
assert_eq!(acct0.en_accountid(), "xt.demo");
eng.put(Event::with_data(a_key, Arc::new(acct0))).unwrap();
assert!(wait_until(Duration::from_secs(2), || per.len() == 1 && acct.len() == 1));
eng.stop();
let t = per.last().unwrap();
assert_eq!(t.price, 3520.2);
assert_eq!(t.volume, 2.0);
let a = acct.last().unwrap();
assert_eq!(a.available(), 99_700.0);
}