openpine_vm/quote_types.rs
1use serde::{Deserialize, Serialize};
2
3use crate::TradeSession;
4
5/// OHLCV candlestick data used as VM input.
6///
7/// Each candlestick represents one bar of market data. Feed these to
8/// [`Instance::run()`](crate::Instance::run) via a
9/// [`DataProvider`](crate::DataProvider) wrapped in a [`CandlestickItem`].
10///
11/// # Examples
12///
13/// ```
14/// use openpine_vm::{Candlestick, TradeSession};
15///
16/// let bar = Candlestick::new(
17/// 1_700_000_000_000, // epoch milliseconds
18/// 150.0,
19/// 155.0,
20/// 149.0,
21/// 153.0,
22/// 1_000_000.0,
23/// 0.0,
24/// TradeSession::Regular,
25/// );
26///
27/// assert_eq!(bar.hl2(), (155.0 + 149.0) / 2.0);
28/// assert_eq!(bar.ohlc4(), (150.0 + 155.0 + 149.0 + 153.0) / 4.0);
29/// ```
30#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
31pub struct Candlestick {
32 /// Candle timestamp (epoch milliseconds).
33 pub time: i64,
34 /// Open price.
35 pub open: f64,
36 /// High price.
37 pub high: f64,
38 /// Low price.
39 pub low: f64,
40 /// Close price.
41 pub close: f64,
42 /// Volume.
43 pub volume: f64,
44 /// Turnover.
45 pub turnover: f64,
46 /// Trading session for this candle.
47 pub trade_session: TradeSession,
48}
49
50impl Default for Candlestick {
51 #[inline]
52 fn default() -> Self {
53 Self {
54 time: 0,
55 open: 0.0,
56 high: 0.0,
57 low: 0.0,
58 close: 0.0,
59 volume: 0.0,
60 turnover: 0.0,
61 trade_session: TradeSession::Regular,
62 }
63 }
64}
65
66/// An item yielded by
67/// [`DataProvider::candlesticks`](crate::DataProvider::candlesticks).
68///
69/// The three variants let the `DataProvider` signal the boundary between
70/// historical (finite, synchronous) and realtime (potentially infinite,
71/// asynchronous) data.
72///
73/// # Stream protocol
74///
75/// The expected sequence is:
76///
77/// ```text
78/// Confirmed(t0) Confirmed(t1) … Confirmed(tN) ← historical closed bars
79/// HistoryEnd ← boundary marker (once)
80/// Realtime(tA) ← forming bar, first tick
81/// Realtime(tA) ← same timestamp → update
82/// Realtime(tB) (tB > tA) ← later timestamp:
83/// VM auto-confirms tA,
84/// then opens tB
85/// …
86/// ```
87///
88/// Do **not** emit `Confirmed` for a realtime bar that has just closed.
89/// Send the next `Realtime` tick with a later timestamp instead; the VM
90/// confirms the previous bar automatically.
91///
92/// # Examples
93///
94/// ```
95/// use openpine_vm::{Candlestick, CandlestickItem, TradeSession};
96///
97/// let bar = Candlestick::new(
98/// 1_700_000_000_000,
99/// 150.0,
100/// 155.0,
101/// 149.0,
102/// 153.0,
103/// 1_000_000.0,
104/// 0.0,
105/// TradeSession::Regular,
106/// );
107///
108/// // Historical (confirmed, closed) bar
109/// let _ = CandlestickItem::Confirmed(bar);
110///
111/// // Realtime (forming) bar
112/// let _ = CandlestickItem::Realtime(bar);
113///
114/// // Marks the end of historical data; realtime items follow
115/// let _ = CandlestickItem::HistoryEnd;
116/// ```
117#[derive(Debug, Copy, Clone)]
118pub enum CandlestickItem {
119 /// A confirmed (closed) historical bar.
120 ///
121 /// Only used for historical bars. Do **not** emit `Confirmed` to signal
122 /// that a realtime bar has closed; send the next `Realtime` tick with a
123 /// later timestamp and the VM confirms the previous bar automatically.
124 Confirmed(Candlestick),
125 /// The current forming (realtime) bar.
126 ///
127 /// Consecutive yields with the **same** timestamp update the bar in place.
128 /// A yield with a **later** timestamp implicitly confirms the previous bar
129 /// and opens a new one.
130 Realtime(Candlestick),
131 /// Marks the boundary between historical and realtime data.
132 ///
133 /// Emit exactly once, after all historical `Confirmed` bars and before the
134 /// first `Realtime` bar. After this item the VM switches from blocking to
135 /// non-blocking stream consumption so that a stalled or slow symbol never
136 /// freezes execution.
137 HistoryEnd,
138}
139
140impl Candlestick {
141 /// Creates a new candlestick.
142 #[inline]
143 #[allow(clippy::too_many_arguments)]
144 pub const fn new(
145 time: i64,
146 open: f64,
147 high: f64,
148 low: f64,
149 close: f64,
150 volume: f64,
151 turnover: f64,
152 trade_session: TradeSession,
153 ) -> Self {
154 Self {
155 time,
156 open,
157 high,
158 low,
159 close,
160 volume,
161 turnover,
162 trade_session,
163 }
164 }
165
166 /// Returns the HL2 value of the candlestick.
167 pub fn hl2(&self) -> f64 {
168 (self.high + self.low) / 2.0
169 }
170
171 /// Returns the HLC3 value of the candlestick.
172 #[inline]
173 pub fn hlc3(&self) -> f64 {
174 (self.high + self.low + self.close) / 3.0
175 }
176
177 /// Returns the OHLC4 value of the candlestick.
178 #[inline]
179 pub fn ohlc4(&self) -> f64 {
180 (self.open + self.high + self.low + self.close) / 4.0
181 }
182
183 /// Returns the HLCC4 value of the candlestick.
184 #[inline]
185 pub fn hlcc4(&self) -> f64 {
186 (self.high + self.low + self.close + self.close) / 4.0
187 }
188}