FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
param_schedule.hpp
Go to the documentation of this file.
1#pragma once
2// ParamSchedule: parameter updates of a backtest, delivered to the engine at simulated times
3// (ADR-0013). SimDriver hands the earliest update to the engine when its time comes, like any other
4// input, so the journal records it at that time and a replay applies it at the same point.
5//
6// sim::ParamSchedule schedule;
7// schedule.at(start + seconds(1), update); // an update built elsewhere
8// schedule.set_delay(milliseconds(5)); // sink(): 5 ms after the publish
9// ParamPublisher pub(schedule.sink(backend.clock), strategy.params());
10// session.set_param_schedule(&schedule); // bt::BacktestSession
11//
12// Pending updates do not keep a run alive: SimDriver ends when its data does.
13//
14// threaded_sink() also accepts updates from other threads (a model thread of a Python strategy):
15// they wait in an inbox until SimDriver next looks at the schedule, and are delivered delay() after
16// the simulated time of that moment, which depends on thread timing.
18#include "fastmm/core/time.hpp"
20
21#include <atomic>
22#include <cstddef>
23#include <cstdint>
24#include <map>
25#include <mutex>
26#include <thread>
27#include <vector>
28
29namespace fastmm::sim {
30
31class ParamSchedule {
32 public:
33 // Delivers `m` at `ts`; updates with the same time keep the order they were added in.
34 void at(Timestamp ts, const ParamUpdateMsg& m) { queue_.emplace(ts.ns, m); }
35
36 // Added to the time of updates that arrive through sink() (the slow tier's delay).
37 void set_delay(Duration d) noexcept { delay_ = d; }
38 [[nodiscard]] Duration delay() const noexcept { return delay_; }
39 // A ParamPublisher sink: each update is delivered delay() after `clock`'s time at the publish.
40 [[nodiscard]] ParamSink sink(const SimClock& clock) noexcept {
41 clock_ = &clock;
42 return {this, [](void* c, const ParamUpdateMsg& m) noexcept {
43 auto* self = static_cast<ParamSchedule*>(c);
44 try {
45 self->at(self->clock_->now() + self->delay_, m);
46 return true;
47 } catch (...) {
48 return false;
49 }
50 }};
51 }
52
53 // Like sink(), for a schedule that other threads publish to as well: an update from the thread
54 // that called threaded_sink() (the thread that runs the SimDriver) is delivered delay() after
55 // `clock`'s time; one from another thread goes to the inbox (post()).
56 [[nodiscard]] ParamSink threaded_sink(const SimClock& clock) noexcept {
57 clock_ = &clock;
58 driver_thread_ = std::this_thread::get_id();
59 return {this, [](void* c, const ParamUpdateMsg& m) noexcept {
60 auto* self = static_cast<ParamSchedule*>(c);
61 try {
62 if (std::this_thread::get_id() == self->driver_thread_) {
63 self->at(self->clock_->now() + self->delay_, m);
64 } else {
65 self->post(m);
66 }
67 return true;
68 } catch (...) {
69 return false;
70 }
71 }};
72 }
73
74 // Any thread: holds `m` until the next collect().
75 void post(const ParamUpdateMsg& m) {
76 const std::lock_guard<std::mutex> lock(inbox_mutex_);
77 inbox_.push_back(m);
78 inbox_pending_.store(true, std::memory_order_release);
79 }
80 // Driver thread: schedules the posted updates delay() after the clock's time (the clock passed to
81 // sink() or threaded_sink()). Cheap when nothing was posted.
82 void collect() {
83 if (FASTMM_LIKELY(!inbox_pending_.load(std::memory_order_acquire))) return;
84 const std::lock_guard<std::mutex> lock(inbox_mutex_);
85 const Timestamp ts = clock_ != nullptr ? clock_->now() + delay_ : Timestamp{};
86 for (const ParamUpdateMsg& m : inbox_) at(ts, m);
87 inbox_.clear();
88 inbox_pending_.store(false, std::memory_order_release);
89 }
90
91 [[nodiscard]] bool empty() const noexcept { return queue_.empty(); }
92 [[nodiscard]] std::size_t size() const noexcept { return queue_.size(); }
93 // The earliest delivery time (Timestamp::max() when empty).
94 [[nodiscard]] Timestamp next_ts() const noexcept {
95 return queue_.empty() ? Timestamp::max() : Timestamp{queue_.begin()->first};
96 }
97 // Removes the earliest update; its recv_ts is the delivery time.
98 [[nodiscard]] ParamUpdateMsg pop() noexcept {
99 ParamUpdateMsg m = queue_.begin()->second;
100 m.hdr.recv_ts = Timestamp{queue_.begin()->first};
101 queue_.erase(queue_.begin());
102 return m;
103 }
104
105 private:
106 std::multimap<std::int64_t, ParamUpdateMsg> queue_;
107 Duration delay_{};
108 const SimClock* clock_ = nullptr;
109 std::thread::id driver_thread_{};
110 std::mutex inbox_mutex_;
111 std::vector<ParamUpdateMsg> inbox_;
112 std::atomic<bool> inbox_pending_{false};
113};
114
115} // namespace fastmm::sim
Deterministic clock for sim/backtest/replay. 1 cycle == 1 ns.
Definition time.hpp:338
Timestamp now() const noexcept
Definition time.hpp:342
Definition param_schedule.hpp:33
void set_delay(Duration d) noexcept
Added to the time of updates that arrive through sink() (the slow tier's delay).
Definition param_schedule.hpp:39
Duration delay() const noexcept
Definition param_schedule.hpp:40
Timestamp next_ts() const noexcept
The earliest delivery time (Timestamp::max() when empty).
Definition param_schedule.hpp:96
ParamSink threaded_sink(const SimClock &clock) noexcept
Like sink(), for a schedule that other threads publish to as well: an update from the thread that cal...
Definition param_schedule.hpp:58
void collect()
Driver thread: schedules the posted updates delay() after the clock's time (the clock passed to sink(...
Definition param_schedule.hpp:84
void post(const ParamUpdateMsg &m)
Any thread: holds m until the next collect().
Definition param_schedule.hpp:77
bool empty() const noexcept
Definition param_schedule.hpp:93
ParamSink sink(const SimClock &clock) noexcept
A ParamPublisher sink: each update is delivered delay() after clock's time at the publish.
Definition param_schedule.hpp:42
ParamUpdateMsg pop() noexcept
Removes the earliest update; its recv_ts is the delivery time.
Definition param_schedule.hpp:100
std::size_t size() const noexcept
Definition param_schedule.hpp:94
void at(Timestamp ts, const ParamUpdateMsg &m)
Delivers m at ts; updates with the same time keep the order they were added in.
Definition param_schedule.hpp:36
Event bus message structs (5.5).
Definition strategy_harness.hpp:40
ParamPublisher: builds ParamUpdate messages for a running engine, off the engine thread (ADR-0013).
Definition time.hpp:22
Timestamp recv_ts
T0 wall-clock (40)
Definition messages.hpp:64
Where a built update goes; push returns false when it was not accepted (full).
Definition param_publisher.hpp:45
New strategy parameter values (ADR-0013): up to kMaxFields (field index, raw value) pairs,...
Definition messages.hpp:574
EventHeader hdr
Definition messages.hpp:584
Definition time.hpp:45
std::int64_t ns
Definition time.hpp:46
static constexpr Timestamp max() noexcept
Definition time.hpp:56
Time primitives.