Skip to content

Commit

Permalink
fix model fields
Browse files Browse the repository at this point in the history
  • Loading branch information
z-Wind committed Aug 28, 2024
1 parent 23ae5be commit 11f81fd
Show file tree
Hide file tree
Showing 19 changed files with 370 additions and 95 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mockito = "1.5"
pretty_assertions = "1.4"
assert-json-diff = "2.0"
float-cmp = "0.9"
regex = "1.10"

[features]
test_online = []
Expand Down
35 changes: 26 additions & 9 deletions src/model/market_data/candle_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use serde::Serialize;
use serde_with::{serde_as, TimestampMilliSeconds};

#[serde_as]
#[serde_with::apply(
Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CandleList {
Expand All @@ -23,7 +26,7 @@ pub struct Candle {
pub close: f64,
#[serde_as(as = "TimestampMilliSeconds<i64>")]
pub datetime: chrono::DateTime<chrono::Utc>,
#[serde(rename = "dateTimeISO8601")]
#[serde(rename = "dateTimeISO8601", skip_serializing_if = "Option::is_none")]
pub datetime_iso8601: Option<chrono::DateTime<chrono::Utc>>,
pub high: f64,
pub low: f64,
Expand All @@ -35,6 +38,8 @@ pub struct Candle {
mod tests {
use super::*;

use assert_json_diff::{assert_json_matches, CompareMode, Config, NumericMode};

#[test]
fn test_de() {
let json = include_str!(concat!(
Expand All @@ -48,26 +53,38 @@ mod tests {
}

#[test]
fn test_de_real() {
fn test_serde_real() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/CandleList_real.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<CandleList>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<CandleList>(json.clone()).unwrap();
dbg!(&val);

assert_json_matches!(
val,
json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat)
);
}

#[test]
fn test_de_real2() {
fn test_serde_real2() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/CandleList_real2.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<CandleList>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<CandleList>(json.clone()).unwrap();
// dbg!(&val);

assert_json_matches!(
val,
json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat)
);
}
}
21 changes: 16 additions & 5 deletions src/model/market_data/expiration_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use super::quote_response::option::SettlementType;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExpirationChain {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
pub expiration_list: Vec<Expiration>,
}

Expand All @@ -16,20 +18,23 @@ pub struct ExpirationChain {
#[serde(rename_all = "camelCase")]
pub struct Expiration {
pub days_to_expiration: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration: Option<String>,
pub expiration_type: ExpirationType,
pub standard: bool,
pub settlement_type: Option<SettlementType>,
pub option_roots: Option<String>,

// not in schama
// not in schema
pub expiration_date: chrono::NaiveDate,
}

#[cfg(test)]
mod tests {
use super::*;

use assert_json_diff::{assert_json_matches, CompareMode, Config, NumericMode};

#[test]
fn test_de() {
let json = include_str!(concat!(
Expand All @@ -43,14 +48,20 @@ mod tests {
}

#[test]
fn test_de_real() {
fn test_serde_real() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/ExpirationChain_real.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<ExpirationChain>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<ExpirationChain>(json.clone()).unwrap();
dbg!(&val);

assert_json_matches!(
val,
json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat)
);
}
}
80 changes: 70 additions & 10 deletions src/model/market_data/instrument.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use chrono::NaiveDateTime;
use serde::Deserialize;
use serde::Serialize;

Expand All @@ -7,6 +8,9 @@ pub struct Instruments {
pub instruments: Vec<InstrumentResponse>,
}

#[serde_with::apply(
Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InstrumentResponse {
Expand All @@ -27,6 +31,9 @@ pub struct InstrumentResponse {
pub type_filed: Option<InstrumentAssetType>,
}

#[serde_with::apply(
Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FundamentalInst {
Expand All @@ -35,7 +42,8 @@ pub struct FundamentalInst {
pub low52: f64,
pub dividend_amount: f64,
pub dividend_yield: f64,
pub dividend_date: Option<String>,
#[serde(default, with = "custom_date_format")]
pub dividend_date: Option<NaiveDateTime>,
pub pe_ratio: f64,
pub peg_ratio: f64,
pub pb_ratio: f64,
Expand Down Expand Up @@ -80,21 +88,26 @@ pub struct FundamentalInst {
pub short_int_day_to_cover: f64,
pub div_growth_rate3_year: f64,
pub dividend_pay_amount: f64,
pub dividend_pay_date: Option<String>,
#[serde(default, with = "custom_date_format")]
pub dividend_pay_date: Option<NaiveDateTime>,
pub beta: f64,
pub vol1_day_avg: f64,
pub vol10_day_avg: f64,
pub vol3_month_avg: f64,
pub avg10_days_volume: f64,
pub avg1_day_volume: f64,
pub avg3_month_volume: f64,
pub declaration_date: Option<String>,
#[serde(default, with = "custom_date_format")]
pub declaration_date: Option<NaiveDateTime>,
pub dividend_freq: i64,
pub eps: f64,
pub corpaction_date: Option<String>,
#[serde(default, with = "custom_date_format")]
pub corpaction_date: Option<NaiveDateTime>,
pub dtn_volume: f64,
pub next_dividend_pay_date: Option<String>,
pub next_dividend_date: Option<String>,
#[serde(default, with = "custom_date_format")]
pub next_dividend_pay_date: Option<NaiveDateTime>,
#[serde(default, with = "custom_date_format")]
pub next_dividend_date: Option<NaiveDateTime>,
pub fund_leverage_factor: f64,
pub fund_strategy: Option<String>,
}
Expand Down Expand Up @@ -149,10 +162,42 @@ pub enum InstrumentAssetType {
Unknown,
}

mod custom_date_format {
use chrono::NaiveDateTime;
use serde::{self, Deserialize, Deserializer, Serializer};

const FORMAT: &str = "%Y-%m-%d %H:%M:%S%.f";

pub fn serialize<S>(date: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match date {
Some(date) => {
let s = date.format(FORMAT).to_string();
serializer.serialize_str(&s)
}
None => serializer.serialize_none(),
}
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NaiveDateTime>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let date = NaiveDateTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)?;

Ok(Some(date))
}
}

#[cfg(test)]
mod tests {
use super::*;

use assert_json_diff::{assert_json_matches_no_panic, CompareMode, Config, NumericMode};

#[test]
fn test_de() {
let json = include_str!(concat!(
Expand All @@ -166,14 +211,29 @@ mod tests {
}

#[test]
fn test_de_real() {
fn test_serde_real() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/Instruments_real.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<Instruments>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<Instruments>(json.clone()).unwrap();
dbg!(&val);

let message = assert_json_matches_no_panic(
&val,
&json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat),
)
.unwrap_err();

let re =
regex::Regex::new(r"(?:json atoms at path.*Date.*are not equal.*\n.*\n.*\n.*\n.*)")
.unwrap();
let message = re.replace_all(&message, "");
let message = message.trim();
println!("{message}");
assert_eq!(message, "");
}
}
28 changes: 23 additions & 5 deletions src/model/market_data/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ use std::collections::HashMap;
pub type Markets = HashMap<String, HashMap<String, Hours>>;

#[allow(clippy::struct_field_names)]
#[serde_with::apply(
Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Hours {
pub date: String,
pub date: chrono::NaiveDate,
pub market_type: MarketType,
pub exchange: Option<String>,
pub category: Option<String>,
Expand Down Expand Up @@ -47,6 +50,8 @@ pub enum MarketType {
mod tests {
use super::*;

use assert_json_diff::{assert_json_matches_no_panic, CompareMode, Config, NumericMode};

#[test]
fn test_de() {
let json = include_str!(concat!(
Expand All @@ -60,14 +65,27 @@ mod tests {
}

#[test]
fn test_de_real() {
fn test_serde_real() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/Markets_real.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<Markets>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<Markets>(json.clone()).unwrap();
dbg!(&val);

let message = assert_json_matches_no_panic(
&val,
&json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat),
)
.unwrap_err();

let re = regex::Regex::new(r"(?:json atoms at path.*start.*are not equal.*\n.*\n.*\n.*\n.*)|(?:json atoms at path.*end.*are not equal.*\n.*\n.*\n.*\n.*)").unwrap();
let message = re.replace_all(&message, "");
let message = message.trim();
println!("{message}");
assert_eq!(message, "");
}
}
20 changes: 16 additions & 4 deletions src/model/market_data/mover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ pub struct Mover {
}

/// Security info of most moved with in an index
/// #[serde_with::apply(
#[serde_with::apply(
Option => #[serde(skip_serializing_if = "Option::is_none")],
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Screener {
Expand Down Expand Up @@ -42,6 +46,8 @@ pub enum Direction {
mod tests {
use super::*;

use assert_json_diff::{assert_json_matches, CompareMode, Config, NumericMode};

#[test]
fn test_de() {
let json = include_str!(concat!(
Expand All @@ -55,14 +61,20 @@ mod tests {
}

#[test]
fn test_de_real() {
fn test_serde_real() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/model/MarketData/Mover_real.json"
));
let json: serde_json::Value = serde_json::from_str(json).unwrap();

let val = serde_json::from_str::<Mover>(json);
println!("{val:?}");
assert!(val.is_ok());
let val = serde_json::from_value::<Mover>(json.clone()).unwrap();
dbg!(&val);

assert_json_matches!(
val,
json,
Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat)
);
}
}
Loading

0 comments on commit 11f81fd

Please sign in to comment.