use super::{
ApiResponse, deserialize_krx_date, deserialize_optional_f64, deserialize_optional_u64,
};
use crate::error::Result;
use chrono::NaiveDate;
use polars::prelude::*;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct FuturesDailyRecord {
#[serde(rename = "BAS_DD", deserialize_with = "deserialize_krx_date")]
pub base_date: NaiveDate,
#[serde(rename = "ISU_CD")]
pub issue_code: String,
#[serde(rename = "ISU_NM")]
pub issue_name: String,
#[serde(rename = "PROD_NM")]
pub product_name: String,
#[serde(rename = "MKT_NM")]
pub market_name: String,
#[serde(rename = "TDD_CLSPRC", deserialize_with = "deserialize_optional_f64")]
pub close_price: Option<f64>,
#[serde(rename = "TDD_OPNPRC", deserialize_with = "deserialize_optional_f64")]
pub open_price: Option<f64>,
#[serde(rename = "TDD_HGPRC", deserialize_with = "deserialize_optional_f64")]
pub high_price: Option<f64>,
#[serde(rename = "TDD_LWPRC", deserialize_with = "deserialize_optional_f64")]
pub low_price: Option<f64>,
#[serde(rename = "SETL_PRC", deserialize_with = "deserialize_optional_f64")]
pub settlement_price: Option<f64>,
#[serde(rename = "SPOT_PRC", deserialize_with = "deserialize_optional_f64")]
pub spot_price: Option<f64>,
#[serde(
rename = "CMPPREVDD_PRC",
deserialize_with = "deserialize_optional_f64"
)]
pub price_change: Option<f64>,
#[serde(rename = "ACC_TRDVOL", deserialize_with = "deserialize_optional_u64")]
pub trading_volume: Option<u64>,
#[serde(rename = "ACC_TRDVAL", deserialize_with = "deserialize_optional_u64")]
pub trading_value: Option<u64>,
#[serde(rename = "ACC_OPNINT_QTY")]
pub open_interest_quantity: String,
}
#[derive(Debug, Deserialize)]
pub struct OptionsDailyRecord {
#[serde(rename = "BAS_DD", deserialize_with = "deserialize_krx_date")]
pub base_date: NaiveDate,
#[serde(rename = "ISU_CD")]
pub issue_code: String,
#[serde(rename = "ISU_NM")]
pub issue_name: String,
#[serde(rename = "PROD_NM")]
pub product_name: String,
#[serde(rename = "RGHT_TP_NM")]
pub right_type: String,
#[serde(rename = "TDD_CLSPRC")]
pub close_price: String,
#[serde(rename = "TDD_OPNPRC")]
pub open_price: String,
#[serde(rename = "TDD_HGPRC")]
pub high_price: String,
#[serde(rename = "TDD_LWPRC")]
pub low_price: String,
#[serde(rename = "CMPPREVDD_PRC")]
pub price_change: String,
#[serde(rename = "ACC_TRDVOL", deserialize_with = "deserialize_optional_u64")]
pub trading_volume: Option<u64>,
#[serde(rename = "ACC_TRDVAL", deserialize_with = "deserialize_optional_u64")]
pub trading_value: Option<u64>,
#[serde(
rename = "ACC_OPNINT_QTY",
deserialize_with = "deserialize_optional_u64"
)]
pub open_interest_quantity: Option<u64>,
#[serde(rename = "IMP_VOLT", deserialize_with = "deserialize_optional_f64")]
pub implied_volatility: Option<f64>,
#[serde(
rename = "NXTDD_BAS_PRC",
deserialize_with = "deserialize_optional_f64"
)]
pub next_day_base_price: Option<f64>,
}
#[derive(Debug, Deserialize)]
pub struct EquityKosdaqFuturesDailyRecord {
#[serde(rename = "BAS_DD", deserialize_with = "deserialize_krx_date")]
pub base_date: NaiveDate,
#[serde(rename = "ISU_CD")]
pub issue_code: String,
#[serde(rename = "ISU_NM")]
pub issue_name: String,
#[serde(rename = "PROD_NM")]
pub product_name: String,
#[serde(rename = "MKT_NM")]
pub market_name: String,
#[serde(rename = "TDD_CLSPRC", deserialize_with = "deserialize_optional_f64")]
pub close_price: Option<f64>,
#[serde(rename = "SETL_PRC", deserialize_with = "deserialize_optional_f64")]
pub settlement_price: Option<f64>,
#[serde(rename = "SPOT_PRC", deserialize_with = "deserialize_optional_f64")]
pub spot_price: Option<f64>,
#[serde(
rename = "CMPPREVDD_PRC",
deserialize_with = "deserialize_optional_f64"
)]
pub price_change: Option<f64>,
#[serde(rename = "ACC_TRDVOL", deserialize_with = "deserialize_optional_u64")]
pub trading_volume: Option<u64>,
#[serde(rename = "ACC_TRDVAL", deserialize_with = "deserialize_optional_u64")]
pub trading_value: Option<u64>,
#[serde(rename = "ACC_OPNINT_QTY")]
pub open_interest_quantity: String,
}
pub type EquityStockFuturesDailyRecord = EquityKosdaqFuturesDailyRecord;
pub type EquityKosdaqOptionsDailyRecord = OptionsDailyRecord;
pub type EquityStockOptionsDailyRecord = OptionsDailyRecord;
pub fn parse_futures_daily(response: ApiResponse<FuturesDailyRecord>) -> Result<DataFrame> {
let records = response.data;
if records.is_empty() {
return Ok(DataFrame::empty());
}
let mut dates = Vec::with_capacity(records.len());
let mut issue_codes = Vec::with_capacity(records.len());
let mut issue_names = Vec::with_capacity(records.len());
let mut product_names = Vec::with_capacity(records.len());
let mut market_names = Vec::with_capacity(records.len());
let mut close_prices = Vec::with_capacity(records.len());
let mut settlement_prices = Vec::with_capacity(records.len());
let mut spot_prices = Vec::with_capacity(records.len());
let mut price_changes = Vec::with_capacity(records.len());
let mut trading_volumes = Vec::with_capacity(records.len());
let mut trading_values = Vec::with_capacity(records.len());
let mut open_interest_quantities = Vec::with_capacity(records.len());
for record in records {
dates.push(record.base_date.format("%Y-%m-%d").to_string());
issue_codes.push(record.issue_code);
issue_names.push(record.issue_name);
product_names.push(record.product_name);
market_names.push(record.market_name);
close_prices.push(record.close_price);
settlement_prices.push(record.settlement_price);
spot_prices.push(record.spot_price);
price_changes.push(record.price_change);
trading_volumes.push(record.trading_volume.map(|v| v as i64));
trading_values.push(record.trading_value.map(|v| v as i64));
open_interest_quantities.push(record.open_interest_quantity);
}
let df = df! {
"날짜" => dates,
"종목코드" => issue_codes,
"종목명" => issue_names,
"상품명" => product_names,
"시장구분" => market_names,
"종가" => close_prices,
"정산가격" => settlement_prices,
"현물가격" => spot_prices,
"대비" => price_changes,
"거래량" => trading_volumes,
"거래대금" => trading_values,
"미결제약정수량" => open_interest_quantities,
}?;
Ok(df)
}
pub fn parse_options_daily(response: ApiResponse<OptionsDailyRecord>) -> Result<DataFrame> {
let records = response.data;
if records.is_empty() {
return Ok(DataFrame::empty());
}
let mut dates = Vec::with_capacity(records.len());
let mut issue_codes = Vec::with_capacity(records.len());
let mut issue_names = Vec::with_capacity(records.len());
let mut product_names = Vec::with_capacity(records.len());
let mut right_types = Vec::with_capacity(records.len());
let mut close_prices = Vec::with_capacity(records.len());
let mut price_changes = Vec::with_capacity(records.len());
let mut trading_volumes = Vec::with_capacity(records.len());
let mut trading_values = Vec::with_capacity(records.len());
let mut open_interest_quantities = Vec::with_capacity(records.len());
let mut implied_volatilities = Vec::with_capacity(records.len());
let mut next_day_base_prices = Vec::with_capacity(records.len());
for record in records {
dates.push(record.base_date.format("%Y-%m-%d").to_string());
issue_codes.push(record.issue_code);
issue_names.push(record.issue_name);
product_names.push(record.product_name);
right_types.push(record.right_type);
close_prices.push(record.close_price);
price_changes.push(record.price_change);
trading_volumes.push(record.trading_volume.map(|v| v as i64));
trading_values.push(record.trading_value.map(|v| v as i64));
open_interest_quantities.push(record.open_interest_quantity.map(|v| v as i64));
implied_volatilities.push(record.implied_volatility);
next_day_base_prices.push(record.next_day_base_price);
}
let df = df! {
"날짜" => dates,
"종목코드" => issue_codes,
"종목명" => issue_names,
"상품명" => product_names,
"권리구분" => right_types,
"종가" => close_prices,
"대비" => price_changes,
"거래량" => trading_volumes,
"거래대금" => trading_values,
"미결제약정수량" => open_interest_quantities,
"내재변동성" => implied_volatilities,
"익일기준가격" => next_day_base_prices,
}?;
Ok(df)
}
pub fn parse_equity_kosdaq_futures_daily(
response: ApiResponse<EquityKosdaqFuturesDailyRecord>,
) -> Result<DataFrame> {
let records = response.data;
if records.is_empty() {
return Ok(DataFrame::empty());
}
let mut dates = Vec::with_capacity(records.len());
let mut issue_codes = Vec::with_capacity(records.len());
let mut issue_names = Vec::with_capacity(records.len());
let mut product_names = Vec::with_capacity(records.len());
let mut close_prices = Vec::with_capacity(records.len());
let mut settlement_prices = Vec::with_capacity(records.len());
let mut spot_prices = Vec::with_capacity(records.len());
let mut price_changes = Vec::with_capacity(records.len());
let mut trading_volumes = Vec::with_capacity(records.len());
let mut trading_values = Vec::with_capacity(records.len());
for record in records {
dates.push(record.base_date.format("%Y-%m-%d").to_string());
issue_codes.push(record.issue_code);
issue_names.push(record.issue_name);
product_names.push(record.product_name);
close_prices.push(record.close_price);
settlement_prices.push(record.settlement_price);
spot_prices.push(record.spot_price);
price_changes.push(record.price_change);
trading_volumes.push(record.trading_volume.map(|v| v as i64));
trading_values.push(record.trading_value.map(|v| v as i64));
}
let df = df! {
"날짜" => dates,
"종목코드" => issue_codes,
"종목명" => issue_names,
"상품명" => product_names,
"종가" => close_prices,
"정산가격" => settlement_prices,
"현물가격" => spot_prices,
"대비" => price_changes,
"거래량" => trading_volumes,
"거래대금" => trading_values,
}?;
Ok(df)
}
pub fn parse_equity_stock_futures_daily(
response: ApiResponse<EquityStockFuturesDailyRecord>,
) -> Result<DataFrame> {
parse_equity_kosdaq_futures_daily(response)
}
pub fn parse_equity_kosdaq_options_daily(
response: ApiResponse<EquityKosdaqOptionsDailyRecord>,
) -> Result<DataFrame> {
parse_options_daily(response)
}
pub fn parse_equity_stock_options_daily(
response: ApiResponse<EquityStockOptionsDailyRecord>,
) -> Result<DataFrame> {
parse_options_daily(response)
}