rusty_feeder/exchange/upbit/data/
trade.rs

1/*
2 * Upbit trade data structures and processing
3 * Optimized for minimal allocations and low latency
4 */
5
6use rust_decimal::Decimal;
7use rusty_model::enums::OrderSide;
8use serde::{Deserialize, Serialize};
9use smartstring::alias::String;
10
11/// Raw trade message from Upbit WebSocket
12#[derive(Debug, Clone, Deserialize, Serialize)]
13#[repr(align(64))] // Cache line alignment for better performance
14pub struct TradeMessage {
15    /// Message type
16    #[serde(rename = "type")]
17    pub message_type: String,
18
19    /// Market code (e.g., "KRW-BTC")
20    pub code: String,
21
22    /// Trade price
23    pub trade_price: Decimal,
24
25    /// Trade volume
26    pub trade_volume: Decimal,
27
28    /// Ask/Bid indicator
29    pub ask_bid: String,
30
31    /// Previous day's closing price
32    pub prev_closing_price: Decimal,
33
34    /// Change direction (RISE, EVEN, FALL)
35    pub change: String,
36
37    /// Change amount (absolute value)
38    pub change_price: Decimal,
39
40    /// Trade date (UTC) in yyyy-MM-dd format
41    pub trade_date: String,
42
43    /// Trade time (UTC) in HH:mm:ss format
44    pub trade_time: String,
45
46    /// Trade timestamp in milliseconds
47    pub trade_timestamp: u64,
48
49    /// Message timestamp in milliseconds
50    pub timestamp: u64,
51
52    /// Sequential ID (unique for each trade)
53    pub sequential_id: u64,
54
55    /// Best ask price at the time of trade
56    pub best_ask_price: Option<Decimal>,
57
58    /// Best ask size at the time of trade
59    pub best_ask_size: Option<Decimal>,
60
61    /// Best bid price at the time of trade
62    pub best_bid_price: Option<Decimal>,
63
64    /// Best bid size at the time of trade
65    pub best_bid_size: Option<Decimal>,
66
67    /// Stream type (SNAPSHOT or REALTIME)
68    pub stream_type: String,
69}
70
71/// Processed trade transaction with additional information
72#[derive(Debug, Clone)]
73#[repr(align(64))] // Cache line alignment for better performance
74pub struct Transaction {
75    /// Market code (e.g., "KRW-BTC")
76    pub code: String,
77
78    /// Sequential ID (unique ID for the trade)
79    pub sequential_id: u64,
80
81    /// Trade price
82    pub price: Decimal,
83
84    /// Trade volume
85    pub volume: Decimal,
86
87    /// Trade side (Buy/Sell)
88    pub side: OrderSide,
89
90    /// Trade timestamp in nanoseconds
91    pub trade_timestamp_ns: u64,
92
93    /// Message timestamp in nanoseconds
94    pub timestamp_ns: u64,
95
96    /// Local timestamp when processed (nanoseconds)
97    pub local_time_ns: u64,
98
99    /// Best ask price at the time of trade
100    pub best_ask_price: Option<Decimal>,
101
102    /// Best ask size at the time of trade
103    pub best_ask_size: Option<Decimal>,
104
105    /// Best bid price at the time of trade
106    pub best_bid_price: Option<Decimal>,
107
108    /// Best bid size at the time of trade
109    pub best_bid_size: Option<Decimal>,
110}
111
112impl Transaction {
113    /// Create a new transaction from a trade message
114    #[inline(always)]
115    #[must_use]
116    pub fn from_trade_message(msg: &TradeMessage, local_time_ns: u64) -> Self {
117        let side = match msg.ask_bid.as_str() {
118            "BID" => OrderSide::Buy,
119            _ => OrderSide::Sell,
120        };
121
122        // Convert millisecond timestamps to nanoseconds for HFT precision
123        let trade_timestamp_ns = msg.trade_timestamp * 1_000_000;
124        let timestamp_ns = msg.timestamp * 1_000_000;
125
126        Self {
127            code: msg.code.clone(),
128            sequential_id: msg.sequential_id,
129            price: msg.trade_price,
130            volume: msg.trade_volume,
131            side,
132            trade_timestamp_ns,
133            timestamp_ns,
134            local_time_ns,
135            best_ask_price: msg.best_ask_price,
136            best_ask_size: msg.best_ask_size,
137            best_bid_price: msg.best_bid_price,
138            best_bid_size: msg.best_bid_size,
139        }
140    }
141}