use chrono::{DateTime, NaiveDate, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Exchange {
SH,
SZ,
BJ,
Unknown,
}
impl Exchange {
pub fn from_stock_code(code: &str) -> Self {
let first_two: String = code.chars().take(2).collect();
match first_two.as_str() {
s if s.starts_with('6') => Exchange::SH,
"11" => Exchange::SH,
s if s.starts_with('5') => Exchange::SH,
s if s.starts_with('0') || s.starts_with('3') => Exchange::SZ,
"12" => Exchange::SZ,
s if s.starts_with('1') && !s.starts_with("11") => Exchange::SZ,
s if s.starts_with('4') || s.starts_with('8') => Exchange::BJ,
_ => Exchange::Unknown,
}
}
pub fn market_prefix(&self) -> &'static str {
match self {
Exchange::SH => "sh",
Exchange::SZ => "sz",
Exchange::BJ => "bj",
Exchange::Unknown => "",
}
}
}
impl std::fmt::Display for Exchange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Exchange::SH => write!(f, "SH"),
Exchange::SZ => write!(f, "SZ"),
Exchange::BJ => write!(f, "BJ"),
Exchange::Unknown => write!(f, "Unknown"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum KLineType {
Daily = 1,
Weekly = 2,
Monthly = 3,
Quarterly = 4,
Min5 = 5,
Min15 = 15,
Min30 = 30,
Min60 = 60,
}
impl KLineType {
pub fn to_api_value(&self) -> u32 {
match self {
KLineType::Daily => 101,
KLineType::Weekly => 102,
KLineType::Monthly => 103,
KLineType::Quarterly => 104,
KLineType::Min5 => 5,
KLineType::Min15 => 15,
KLineType::Min30 => 30,
KLineType::Min60 => 60,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum AdjustType {
None = 0,
#[default]
Forward = 1,
Backward = 2,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StockCode {
pub stock_code: String,
pub short_name: String,
pub exchange: Exchange,
pub list_date: Option<NaiveDate>,
}
impl StockCode {
pub fn new(stock_code: String, short_name: String, exchange: Exchange) -> Self {
Self {
stock_code,
short_name,
exchange,
list_date: None,
}
}
pub fn full_symbol(&self) -> String {
format!("{}{}", self.exchange.market_prefix(), self.stock_code)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketData {
pub stock_code: String,
pub trade_time: DateTime<Utc>,
pub trade_date: NaiveDate,
pub open: f64,
pub close: f64,
pub high: f64,
pub low: f64,
pub volume: u64,
pub amount: f64,
pub change: f64,
pub change_pct: f64,
pub turnover_ratio: f64,
pub pre_close: f64,
}
impl MarketData {
pub fn is_valid(&self) -> bool {
self.volume > 0 && self.amount > 0.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentMarketData {
pub stock_code: String,
pub short_name: String,
pub price: f64,
pub change: f64,
pub change_pct: f64,
pub volume: u64,
pub amount: f64,
pub open: Option<f64>,
pub high: Option<f64>,
pub low: Option<f64>,
pub pre_close: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MinuteData {
pub stock_code: String,
pub trade_time: DateTime<Utc>,
pub price: f64,
pub change: f64,
pub change_pct: f64,
pub volume: u64,
pub avg_price: f64,
pub amount: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderBookData {
pub stock_code: String,
pub short_name: String,
pub sell_prices: [f64; 5],
pub sell_volumes: [u64; 5],
pub buy_prices: [f64; 5],
pub buy_volumes: [u64; 5],
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TickData {
pub stock_code: String,
pub trade_time: DateTime<Utc>,
pub price: f64,
pub volume: u64,
pub direction: char,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StockInfo {
pub stock_code: String,
pub full_name: String,
pub short_name: String,
pub exchange: Exchange,
pub industry: Option<String>,
pub total_shares: Option<u64>,
pub circulating_shares: Option<u64>,
pub list_date: Option<NaiveDate>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapitalFlowData {
pub stock_code: String,
pub stock_name: String,
pub main_net_inflow: f64,
pub main_inflow: f64,
pub main_outflow: f64,
pub super_large_net_inflow: f64,
pub large_net_inflow: f64,
pub medium_net_inflow: f64,
pub small_net_inflow: f64,
pub main_net_ratio: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapitalFlowHistory {
pub stock_code: String,
pub trade_date: NaiveDate,
pub main_net_inflow: f64,
pub small_net_inflow: f64,
pub medium_net_inflow: f64,
pub large_net_inflow: f64,
pub super_large_net_inflow: f64,
pub close: f64,
pub change_pct: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BillboardItem {
pub stock_code: String,
pub stock_name: String,
pub trade_date: NaiveDate,
pub close: f64,
pub change_pct: f64,
pub turnover_ratio: f64,
pub net_buy_amount: f64,
pub buy_amount: f64,
pub sell_amount: f64,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BillboardDetail {
pub stock_code: String,
pub trade_date: NaiveDate,
pub trader_name: String,
pub buy_amount: f64,
pub sell_amount: f64,
pub net_amount: f64,
pub direction: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EarningsForecast {
pub stock_code: String,
pub stock_name: String,
pub forecast_type: String,
pub profit_min: Option<f64>,
pub profit_max: Option<f64>,
pub change_min: Option<f64>,
pub change_max: Option<f64>,
pub report_period: String,
pub announce_date: NaiveDate,
pub summary: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StockConnectData {
pub trade_date: NaiveDate,
pub north_net_buy: f64,
pub sh_net_buy: f64,
pub sz_net_buy: f64,
pub north_buy: f64,
pub north_sell: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarginTradingData {
pub stock_code: String,
pub stock_name: String,
pub trade_date: NaiveDate,
pub margin_balance: f64,
pub margin_buy: f64,
pub margin_repay: f64,
pub short_balance: f64,
pub short_volume: u64,
pub total_balance: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IPOData {
pub stock_code: String,
pub stock_name: String,
pub issue_price: f64,
pub sub_date: NaiveDate,
pub list_date: Option<NaiveDate>,
pub winning_rate: Option<f64>,
pub issue_quantity: Option<u64>,
pub online_quantity: Option<u64>,
pub pe_ratio: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockTradeData {
pub stock_code: String,
pub stock_name: String,
pub trade_date: NaiveDate,
pub price: f64,
pub close_price: f64,
pub premium_rate: f64,
pub volume: u64,
pub amount: f64,
pub buyer: String,
pub seller: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstitutionalResearchData {
pub stock_code: String,
pub stock_name: String,
pub research_date: NaiveDate,
pub institution_count: u32,
pub institutions: String,
pub research_type: String,
pub researchers: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResearchReportData {
pub report_id: String,
pub stock_code: String,
pub stock_name: String,
pub title: String,
pub institution: String,
pub analysts: String,
pub rating: Option<String>,
pub publish_date: NaiveDate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StockValuation {
pub stock_code: String,
pub stock_name: String,
pub price: f64,
pub market_cap: f64,
pub float_cap: f64,
pub pe_ttm: Option<f64>,
pub pe_static: Option<f64>,
pub pb: Option<f64>,
pub ps: Option<f64>,
pub eps: Option<f64>,
pub bps: Option<f64>,
pub roe: Option<f64>,
pub gross_margin: Option<f64>,
pub net_margin: Option<f64>,
pub revenue: Option<f64>,
pub net_profit: Option<f64>,
pub revenue_yoy: Option<f64>,
pub profit_yoy: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopHolder {
pub stock_code: String,
pub report_date: NaiveDate,
pub rank: u32,
pub holder_name: String,
pub hold_quantity: u64,
pub hold_ratio: f64,
pub change_quantity: Option<i64>,
pub holder_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundHolding {
pub stock_code: String,
pub stock_name: String,
pub report_date: NaiveDate,
pub fund_name: String,
pub hold_shares: u64,
pub hold_ratio: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DividendData {
pub stock_code: String,
pub stock_name: String,
pub report_date: NaiveDate,
pub ex_date: Option<NaiveDate>,
pub record_date: Option<NaiveDate>,
pub dividend_per_share: f64,
pub bonus_shares: f64,
pub transfer_shares: f64,
pub dividend_yield: Option<f64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exchange_from_code() {
assert_eq!(Exchange::from_stock_code("600000"), Exchange::SH);
assert_eq!(Exchange::from_stock_code("000001"), Exchange::SZ);
assert_eq!(Exchange::from_stock_code("300001"), Exchange::SZ);
assert_eq!(Exchange::from_stock_code("830001"), Exchange::BJ);
}
#[test]
fn test_stock_code_full_symbol() {
let stock = StockCode::new("600519".to_string(), "贵州茅台".to_string(), Exchange::SH);
assert_eq!(stock.full_symbol(), "sh600519");
}
#[test]
fn test_kline_type_api_value() {
assert_eq!(KLineType::Daily.to_api_value(), 101);
assert_eq!(KLineType::Min5.to_api_value(), 5);
}
}