use ek_data::EnumParseError;
use ek_data::constant::*;
use std::fmt::{Debug, Display};
use std::str::FromStr;
fn wire<T>(v: T, wit: &str, aliases: &[&str], name: &'static str)
where
T: Copy + Display + FromStr + PartialEq + Debug,
T::Err: Debug,
{
assert_eq!(v.to_string(), wit, "{name} Display");
assert_eq!(T::from_str(wit).unwrap(), v, "{name} from wire");
for a in aliases {
assert_eq!(T::from_str(a).unwrap(), v, "{name} from alias {a:?}");
}
}
#[test]
fn direction_roundtrip() {
wire(Direction::LONG, "LONG", &["多"], "Direction");
wire(Direction::SHORT, "SHORT", &["空"], "Direction");
wire(Direction::NET, "NET", &["净"], "Direction");
assert_eq!(Direction::ALL.len(), 3);
assert!(Direction::from_str("long").is_err());
assert!(matches!(
Direction::from_str("bull"),
Err(EnumParseError {
type_name: "Direction",
..
})
));
}
#[test]
fn offset_roundtrip() {
wire(Offset::NONE, "NONE", &[""], "Offset");
wire(Offset::OPEN, "OPEN", &["开"], "Offset");
wire(Offset::CLOSE, "CLOSE", &["平"], "Offset");
wire(Offset::CLOSETODAY, "CLOSETODAY", &["平今"], "Offset");
wire(
Offset::CLOSEYESTERDAY,
"CLOSEYESTERDAY",
&["平昨"],
"Offset",
);
assert_eq!(Offset::ALL.len(), 5);
}
#[test]
fn status_roundtrip() {
wire(Status::SUBMITTING, "SUBMITTING", &["提交中"], "Status");
wire(Status::NOTTRADED, "NOTTRADED", &["未成交"], "Status");
wire(Status::PARTTRADED, "PARTTRADED", &["部分成交"], "Status");
wire(Status::ALLTRADED, "ALLTRADED", &["全部成交"], "Status");
wire(Status::CANCELLED, "CANCELLED", &["已撤销"], "Status");
wire(Status::REJECTED, "REJECTED", &["拒单"], "Status");
wire(Status::UNKNOWN, "UNKNOWN", &["未知"], "Status");
assert_eq!(Status::ALL.len(), 7);
assert!(!Status::UNKNOWN.is_active());
assert_eq!(
ACTIVE_STATUSES,
[Status::SUBMITTING, Status::NOTTRADED, Status::PARTTRADED]
);
}
#[test]
fn asset_and_instrument_types() {
wire(Asset::EQUITY, "EQUITY", &["股票"], "Asset");
wire(Asset::BOND, "BOND", &["债券"], "Asset");
wire(Asset::FUTURES, "FUTURES", &["期货"], "Asset");
wire(Asset::OPTION, "OPTION", &["期权"], "Asset");
wire(Asset::FOREX, "FOREX", &["外汇"], "Asset");
wire(Asset::UNKNOWN, "UNKNOWN", &["未知"], "Asset");
assert_eq!(Asset::ALL.len(), 6);
wire(InstrumentType::STOCK, "STOCK", &["股票"], "InstrumentType");
wire(InstrumentType::ETF, "ETF", &["ETF"], "InstrumentType");
wire(InstrumentType::INDEX, "INDEX", &["指数"], "InstrumentType");
wire(
InstrumentType::FUTURES_CONTRACT,
"FUTURES_CONTRACT",
&["期货合约"],
"InstrumentType",
);
wire(
InstrumentType::MAIN_CONTRACT,
"MAIN_CONTRACT",
&["主力合约"],
"InstrumentType",
);
wire(
InstrumentType::CONTINUOUS_CONTRACT,
"CONTINUOUS_CONTRACT",
&["连续合约"],
"InstrumentType",
);
wire(
InstrumentType::OPTION_CONTRACT,
"OPTION_CONTRACT",
&["期权合约"],
"InstrumentType",
);
wire(InstrumentType::BOND, "BOND", &["债券"], "InstrumentType");
wire(
InstrumentType::CONVERTIBLE_BOND,
"CONVERTIBLE_BOND",
&["可转债"],
"InstrumentType",
);
wire(
InstrumentType::SPREAD,
"SPREAD",
&["价差组合"],
"InstrumentType",
);
wire(
InstrumentType::HEDGE,
"HEDGE",
&["对冲组合"],
"InstrumentType",
);
assert_eq!(InstrumentType::ALL.len(), 11);
}
#[test]
fn order_option_types() {
wire(OrderType::LIMIT, "LIMIT", &["限价"], "OrderType");
wire(OrderType::MARKET, "MARKET", &["市价"], "OrderType");
wire(OrderType::STOP, "STOP", &[], "OrderType");
wire(OrderType::FAK, "FAK", &[], "OrderType");
wire(OrderType::FOK, "FOK", &[], "OrderType");
wire(OrderType::RFQ, "RFQ", &["询价"], "OrderType");
wire(OrderType::UNKNOWN, "UNKNOWN", &["未知"], "OrderType");
assert_eq!(OrderType::ALL.len(), 7);
wire(OptionType::CALL, "CALL", &["看涨期权"], "OptionType");
wire(OptionType::PUT, "PUT", &["看跌期权"], "OptionType");
}
#[test]
fn exchange_full_replica_and_special_wires() {
assert_eq!(Exchange::ALL.len(), 51);
assert_eq!(Exchange::UNKNOWN.to_string(), "UNKNOWN");
assert_eq!(
Exchange::from_str("未知").unwrap(),
Exchange::UNKNOWN,
"UNKNOWN 的中文别名必须可解析"
);
assert_ne!(Exchange::UNKNOWN, Exchange::LOCAL);
assert_eq!(Exchange::CBOT.to_string(), "CBT");
assert_eq!(Exchange::EUREX.to_string(), "EUX");
assert_eq!(Exchange::ETF_INDEX.to_string(), "ETF_index");
assert_eq!(Exchange::from_str("CBT").unwrap(), Exchange::CBOT);
assert_eq!(Exchange::from_str("EUX").unwrap(), Exchange::EUREX);
assert_eq!(
Exchange::from_str("ETF_index").unwrap(),
Exchange::ETF_INDEX
);
wire(Exchange::CFFEX, "CFFEX", &[], "Exchange");
wire(Exchange::SHFE, "SHFE", &[], "Exchange");
wire(Exchange::SZSE, "SZSE", &[], "Exchange");
wire(Exchange::LOCAL, "LOCAL", &[], "Exchange");
assert!(Exchange::from_str("NOPE").is_err());
}
#[test]
fn currency_and_interval() {
wire(Currency::USD, "USD", &[], "Currency");
wire(Currency::HKD, "HKD", &[], "Currency");
wire(Currency::CNY, "CNY", &[], "Currency");
wire(Currency::CAD, "CAD", &[], "Currency");
wire(Interval::TICK, "TICK", &[], "Interval");
wire(Interval::MINUTE, "MINUTE", &[], "Interval");
wire(Interval::MINUTE_5, "MINUTE_5", &[], "Interval");
wire(Interval::MINUTE_15, "MINUTE_15", &[], "Interval");
wire(Interval::MINUTE_30, "MINUTE_30", &[], "Interval");
wire(Interval::HOUR, "HOUR", &[], "Interval");
wire(Interval::DAILY, "DAILY", &[], "Interval");
wire(Interval::WEEKLY, "WEEKLY", &[], "Interval");
assert_eq!(Interval::TICK.seconds(), 0);
assert_eq!(Interval::MINUTE.seconds(), 60);
assert_eq!(Interval::MINUTE_5.seconds(), 300);
assert_eq!(Interval::MINUTE_15.seconds(), 900);
assert_eq!(Interval::MINUTE_30.seconds(), 1800);
assert_eq!(Interval::HOUR.seconds(), 3600);
assert_eq!(Interval::DAILY.seconds(), 86400);
assert_eq!(Interval::WEEKLY.seconds(), 604800);
}
#[test]
fn wire_values_and_aliases_are_collision_free() {
fn check<T: Display + FromStr + Copy + Debug + PartialEq>(
all: &[T],
aliases_of: &dyn Fn(&T) -> &'static [&'static str],
label: &str,
) where
T::Err: Debug,
{
let mut wires: Vec<String> = Vec::new();
let mut aliases: Vec<String> = Vec::new();
for v in all {
let w = v.to_string();
assert_eq!(T::from_str(&w).unwrap(), *v, "{label} 线值自回路失败: {w}");
assert!(!wires.contains(&w), "{label} 线值冲突: {w}");
wires.push(w);
for a in aliases_of(v) {
assert!(T::from_str(a).unwrap() == *v, "{label} 别名解析异常: {a:?}");
assert!(!wires.contains(&a.to_string()), "{label} 别名 {a:?} 撞线值");
assert!(!aliases.contains(&a.to_string()), "{label} 别名冲突: {a:?}");
aliases.push(a.to_string());
}
}
}
check(Direction::ALL, &|v| v.chinese_alias(), "Direction");
check(Offset::ALL, &|v| v.chinese_alias(), "Offset");
check(Status::ALL, &|v| v.chinese_alias(), "Status");
check(Asset::ALL, &|v| v.chinese_alias(), "Asset");
check(
InstrumentType::ALL,
&|v| v.chinese_alias(),
"InstrumentType",
);
check(OrderType::ALL, &|v| v.chinese_alias(), "OrderType");
check(OptionType::ALL, &|v| v.chinese_alias(), "OptionType");
check(Exchange::ALL, &|v| v.chinese_alias(), "Exchange");
check(Currency::ALL, &|v| v.chinese_alias(), "Currency");
check(Interval::ALL, &|v| v.chinese_alias(), "Interval");
}