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}