use crate::client::AkShareClient;
use crate::error::{Error, Result};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OptionContractInfoCtp {
pub exchange_id: String,
pub instrument_id: String,
pub instrument_name: String,
pub product_class: String,
pub product_id: String,
pub volume_multiple: f64,
pub price_tick: f64,
pub long_margin_ratio: f64,
pub short_margin_ratio: f64,
pub long_margin_per_lot: f64,
pub short_margin_per_lot: f64,
pub open_fee_ratio: f64,
pub open_fee_per_lot: f64,
pub close_fee_ratio: f64,
pub close_fee_per_lot: f64,
pub close_today_fee_ratio: f64,
pub close_today_fee_per_lot: f64,
pub delivery_year: i64,
pub delivery_month: i64,
pub open_date: String,
pub expire_date: String,
pub delivery_date: String,
pub underlying_instrument_id: String,
pub underlying_multiple: f64,
pub options_type: String,
pub strike_price: f64,
pub inst_life_phase: String,
}
impl AkShareClient {
pub async fn option_contract_info_ctp(&self) -> Result<Vec<OptionContractInfoCtp>> {
let url = "http://dict.openctp.cn/instruments?types=option";
let resp: serde_json::Value = self
.get(url)
.send()
.await
.map_err(Error::from)?
.json()
.await
.map_err(Error::from)?;
let data = resp
.get("data")
.and_then(|d| d.as_array())
.cloned()
.unwrap_or_default();
let mut rows = Vec::with_capacity(data.len());
for item in &data {
rows.push(OptionContractInfoCtp {
exchange_id: json_str(item, "ExchangeID"),
instrument_id: json_str(item, "InstrumentID"),
instrument_name: json_str(item, "InstrumentName"),
product_class: json_str(item, "ProductClass"),
product_id: json_str(item, "ProductID"),
volume_multiple: json_f64(item, "VolumeMultiple"),
price_tick: json_f64(item, "PriceTick"),
long_margin_ratio: json_f64(item, "LongMarginRatioByMoney"),
short_margin_ratio: json_f64(item, "ShortMarginRatioByMoney"),
long_margin_per_lot: json_f64(item, "LongMarginRatioByVolume"),
short_margin_per_lot: json_f64(item, "ShortMarginRatioByVolume"),
open_fee_ratio: json_f64(item, "OpenRatioByMoney"),
open_fee_per_lot: json_f64(item, "OpenRatioByVolume"),
close_fee_ratio: json_f64(item, "CloseRatioByMoney"),
close_fee_per_lot: json_f64(item, "CloseRatioByVolume"),
close_today_fee_ratio: json_f64(item, "CloseTodayRatioByMoney"),
close_today_fee_per_lot: json_f64(item, "CloseTodayRatioByVolume"),
delivery_year: json_i64(item, "DeliveryYear"),
delivery_month: json_i64(item, "DeliveryMonth"),
open_date: json_str(item, "OpenDate"),
expire_date: json_str(item, "ExpireDate"),
delivery_date: json_str(item, "DeliveryDate"),
underlying_instrument_id: json_str(item, "UnderlyingInstrID"),
underlying_multiple: json_f64(item, "UnderlyingMultiple"),
options_type: json_str(item, "OptionsType"),
strike_price: json_f64(item, "StrikePrice"),
inst_life_phase: json_str(item, "InstLifePhase"),
});
}
Ok(rows)
}
}
fn json_str(v: &serde_json::Value, key: &str) -> String {
v.get(key)
.and_then(|x| x.as_str())
.unwrap_or("")
.to_string()
}
fn json_f64(v: &serde_json::Value, key: &str) -> f64 {
match v.get(key) {
Some(serde_json::Value::Number(n)) => n.as_f64().unwrap_or(0.0),
Some(serde_json::Value::String(s)) => s.trim().parse::<f64>().unwrap_or(0.0),
_ => 0.0,
}
}
fn json_i64(v: &serde_json::Value, key: &str) -> i64 {
match v.get(key) {
Some(serde_json::Value::Number(n)) => n.as_i64().unwrap_or(0),
Some(serde_json::Value::String(s)) => s.trim().parse::<i64>().unwrap_or(0),
_ => 0,
}
}