Skip to main content

brokapi_tbank/map/
schedule.rs

1//! Trading schedule.
2
3use std::time::Instant;
4
5use brokapi_core::{Schedule, SessionInterval, SessionKind};
6use brokapi_tbank_protocol::v1 as pb;
7use prost_types::Timestamp;
8
9use crate::ids::{proto_to_nanos, stamps};
10
11fn push(
12    out: &mut Vec<SessionInterval>,
13    start: &Option<Timestamp>,
14    end: &Option<Timestamp>,
15    kind: SessionKind,
16) {
17    if let (Some(s), Some(e)) = (start, end)
18        && s.seconds != 0
19        && e.seconds != 0
20        && e.seconds >= s.seconds
21    {
22        out.push(SessionInterval {
23            start: proto_to_nanos(s),
24            end: proto_to_nanos(e),
25            kind,
26        });
27    }
28}
29
30/// Main session, premarket, evening session (post-market) and auctions of every trading day.
31pub fn schedule(exchange: &str, sched: &pb::TradingSchedule, now: Instant) -> Schedule {
32    let mut sessions = Vec::new();
33    for day in sched.days.iter().filter(|d| d.is_trading_day) {
34        push(
35            &mut sessions,
36            &day.premarket_start_time,
37            &day.premarket_end_time,
38            SessionKind::PreMarket,
39        );
40        push(
41            &mut sessions,
42            &day.opening_auction_start_time,
43            &day.opening_auction_end_time,
44            SessionKind::Auction,
45        );
46        push(
47            &mut sessions,
48            &day.start_time,
49            &day.end_time,
50            SessionKind::Main,
51        );
52        push(
53            &mut sessions,
54            &day.closing_auction_start_time,
55            &day.closing_auction_end_time,
56            SessionKind::Auction,
57        );
58        push(
59            &mut sessions,
60            &day.evening_start_time,
61            &day.evening_end_time,
62            SessionKind::PostMarket,
63        );
64    }
65    sessions.sort_by_key(|s| s.start);
66    Schedule {
67        exchange: exchange.to_string(),
68        sessions,
69        ts: stamps(now, None),
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    fn t(s: i64) -> Option<Timestamp> {
78        Some(Timestamp {
79            seconds: s,
80            nanos: 0,
81        })
82    }
83
84    #[test]
85    fn sessions_of_trading_days_sorted() {
86        let day = pb::TradingDay {
87            is_trading_day: true,
88            start_time: t(1_000),
89            end_time: t(2_000),
90            evening_start_time: t(3_000),
91            evening_end_time: t(4_000),
92            premarket_start_time: t(500),
93            premarket_end_time: t(900),
94            ..Default::default()
95        };
96        let off = pb::TradingDay {
97            is_trading_day: false,
98            start_time: t(10),
99            end_time: t(20),
100            ..Default::default()
101        };
102        let s = schedule(
103            "MOEX",
104            &pb::TradingSchedule {
105                exchange: "MOEX".into(),
106                days: vec![day, off],
107            },
108            Instant::now(),
109        );
110        let kinds: Vec<_> = s.sessions.iter().map(|x| x.kind).collect();
111        assert_eq!(
112            kinds,
113            vec![
114                SessionKind::PreMarket,
115                SessionKind::Main,
116                SessionKind::PostMarket
117            ]
118        );
119    }
120}