itch-mold-replay 1.0
Time-accurate NASDAQ TotalView-ITCH 5.0 replay over MoldUDP64
Loading...
Searching...
No Matches
pacer.h
Go to the documentation of this file.
1#pragma once
2
3#include "imr/util/log.h"
4#include <chrono>
5#include <concepts>
6
7namespace imr::mold::downstream
8{
9 enum class MarketPhase
10 {
11 pre,
12 open,
13 close
14 };
15
16 inline constexpr std::chrono::nanoseconds phase_to_ns(MarketPhase phase)
17 {
18 using namespace std::chrono_literals;
19 switch (phase)
20 {
22 return 0ns;
24 return 9h + 30min;
26 return 16h;
27 default:
28 return 0ns;
29 }
30 }
31
32 // less verbose for caller than imr::mold::downstream::phase_to_ns(imr::mold::downstream::MarketPhase::pre)
36
37 // templated so we can mock this for testing
38 template <typename T>
39 concept ClockConcept = requires {
40 typename T::time_point;
41 { T::now() } -> std::same_as<typename T::time_point>;
42 };
43
44 /// Calculates relative delay for a given message timestamp for downstream (which passes the first message's timestamp from each packet )
45 template <ClockConcept Clock = std::chrono::steady_clock>
46 class Pacer
47 {
48 public:
49 struct Config
50 {
51
52 /// Scale calculated delays
53 double playback_speed{1.0};
54 /* Returns delay of 0 for timestamps below this
55 *
56 * Use this with MarketPhase/phase_to_ns to skip market phases you don't want to sleep or send messages in
57 **/
58 std::chrono::nanoseconds skip_before{phase_to_ns(MarketPhase::pre)};
59 };
60
61 Pacer(const Config& cfg)
62 : playback_speed_{cfg.playback_speed},
63 skip_before_{cfg.skip_before}
64 {
65 }
66
67 /// True/false if given timestamp is before `skip_before_`
68 [[nodiscard]]
69 bool should_skip(std::chrono::nanoseconds packet_timestamp)
70 {
71 return packet_timestamp < skip_before_;
72 }
73
74 /** Returns calculated delay
75 *
76 * The first time this is called this will set `replay_origin_` to `Clock::now()`
77 */
78 [[nodiscard]]
79 std::chrono::nanoseconds get_delay(std::chrono::nanoseconds packet_timestamp)
80 {
81 if (!replay_origin_.has_value())
82
83 {
84 replay_origin_ = packet_timestamp;
85 wall_origin_ = Clock::now();
86
87 util::log::debug("Downstream pacer: replay origin set at {}ns (wall origin {}ns since epoch)",
88 replay_origin_->count(),
89 wall_origin_.time_since_epoch().count());
90 }
91
92 return calculate_delay(packet_timestamp);
93 }
94
95 private:
96 double playback_speed_;
97 std::chrono::nanoseconds skip_before_;
98 Clock::time_point wall_origin_;
99 std::optional<std::chrono::nanoseconds> replay_origin_;
100
101 [[nodiscard]]
102 std::chrono::nanoseconds calculate_delay(std::chrono::nanoseconds packet_timestamp)
103 {
104 const auto replay_offset{packet_timestamp - *replay_origin_};
105
106 const auto scaled_offset{
107 std::chrono::nanoseconds(
108 static_cast<int64_t>(static_cast<double>(replay_offset.count()) / playback_speed_)),
109 };
110
111 const auto send_at{wall_origin_ + scaled_offset};
112
113 const auto now{Clock::now()};
114
115 if (send_at <= now)
116 {
117 return std::chrono::nanoseconds{0};
118 }
119
120 return std::chrono::nanoseconds(send_at - now);
121 }
122 };
123}
Calculates relative delay for a given message timestamp for downstream (which passes the first messag...
Definition pacer.h:47
Pacer(const Config &cfg)
Definition pacer.h:61
std::chrono::nanoseconds get_delay(std::chrono::nanoseconds packet_timestamp)
Returns calculated delay.
Definition pacer.h:79
bool should_skip(std::chrono::nanoseconds packet_timestamp)
True/false if given timestamp is before skip_before_
Definition pacer.h:69
MarketPhase
Definition pacer.h:10
constexpr auto market_close
Definition pacer.h:35
constexpr auto market_open
Definition pacer.h:33
constexpr std::chrono::nanoseconds phase_to_ns(MarketPhase phase)
Definition pacer.h:16
constexpr auto market_pre
Definition pacer.h:34
Definition log.h:8
Definition file_descriptor.h:11
Definition feed.h:17
double playback_speed
Scale calculated delays.
Definition pacer.h:53
std::chrono::nanoseconds skip_before
Definition pacer.h:58