algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
stop.hpp
1#pragma once
2
4
5#include <array>
6#include <atomic>
7#include <cstdio>
8#include <optional>
9#include <string>
10#include <string_view>
11
12namespace algotradelib {
13namespace runner {
14
48
50inline constexpr std::array<StopIntent, 3> all_stop_intents{
54};
55
61inline constexpr std::string_view stop_intent_name(StopIntent intent) noexcept {
62 switch (intent) {
64 return "quiesce";
66 return "withdraw";
68 return "flatten";
69 }
70 return "";
71}
72
73static_assert(
74 lists_every_value(all_stop_intents, [](StopIntent intent) { return !stop_intent_name(intent).empty(); }),
75 "all_stop_intents must list every StopIntent"
76);
77
83inline std::optional<StopIntent> stop_intent_from(std::string_view text) noexcept {
85}
86
96inline constexpr int stop_intent_strength(StopIntent intent) noexcept {
97 switch (intent) {
99 return 0;
101 return 1;
103 return 2;
104 }
105 return 0;
106}
107
114inline constexpr bool stop_intent_closes(StopIntent intent) noexcept {
115 switch (intent) {
117 return false;
120 return true;
121 }
122 return true;
123}
124
131inline constexpr bool stop_intent_closes_positions(StopIntent intent) noexcept {
132 switch (intent) {
134 return true;
137 return false;
138 }
139 return false;
140}
141
148inline constexpr bool stop_intent_covers(StopIntent held, StopIntent candidate) noexcept {
149 return stop_intent_strength(held) >= stop_intent_strength(candidate);
150}
151
159public:
167 void request(StopIntent intent) noexcept {
168 const int desired = static_cast<int>(intent);
169 int current = requested_.load(std::memory_order_relaxed);
170 // A failed exchange reloads `current`, so the held intent's strength is read again each time.
171 while (current < 0 || stop_intent_strength(static_cast<StopIntent>(current)) < stop_intent_strength(intent)) {
172 if (requested_.compare_exchange_weak(
173 current, desired, std::memory_order_release, std::memory_order_relaxed)) {
174 return;
175 }
176 }
177 }
178
183 bool requested() const noexcept {
184 return requested_.load(std::memory_order_acquire) >= 0;
185 }
186
191 std::optional<StopIntent> intent() const noexcept {
192 const int held = requested_.load(std::memory_order_acquire);
193 if (held < 0) {
194 return std::nullopt;
195 }
196 return static_cast<StopIntent>(held);
197 }
198
200 void clear() noexcept { requested_.store(-1, std::memory_order_release); }
201
202private:
203 // Negative means no request; otherwise a `StopIntent`.
204 std::atomic<int> requested_{-1};
205
206 // Asserted rather than assumed, because the signal-safety argument above rests entirely on it. If
207 // this ever fails on some platform, `request()` is no longer callable from a handler and the
208 // signal installer is unsound -- which is worth a compile error rather than a subtle deadlock in a
209 // handler that fires once a month.
210 static_assert(
211 std::atomic<int>::is_always_lock_free,
212 "StopSource requires a lock-free atomic<int> to stay signal-safe");
213};
214
230public:
235 explicit FileStopChannel(std::string path = {}) : path_(std::move(path)) {}
236
241 bool enabled() const noexcept { return !path_.empty(); }
242
247 const std::string& path() const noexcept { return path_; }
248
254 std::optional<StopIntent> poll() {
255 unrecognized_.clear();
256 if (path_.empty()) {
257 return std::nullopt;
258 }
259 std::FILE* handle = std::fopen(path_.c_str(), "rb");
260 if (handle == nullptr) {
261 return std::nullopt;
262 }
263 char buffer[64] = {};
264 const std::size_t read = std::fread(buffer, 1, sizeof(buffer) - 1, handle);
265 std::fclose(handle);
266
267 std::string_view text(buffer, read);
268 const auto begin = text.find_first_not_of(" \t\r\n");
269 if (begin == std::string_view::npos) {
270 return std::nullopt;
271 }
272 text.remove_prefix(begin);
273 const auto end = text.find_first_of(" \t\r\n");
274 if (end != std::string_view::npos) {
275 text = text.substr(0, end);
276 }
277
278 if (auto intent = stop_intent_from(text)) {
279 return intent;
280 }
281 unrecognized_.assign(text);
282 return std::nullopt;
283 }
284
289 const std::string& unrecognized() const noexcept { return unrecognized_; }
290
291private:
292 std::string path_;
293 std::string unrecognized_;
294};
295
296} // namespace runner
297} // namespace algotradelib
FileStopChannel(std::string path={})
Constructs a channel.
Definition stop.hpp:235
bool enabled() const noexcept
Tests whether a path was configured.
Definition stop.hpp:241
const std::string & path() const noexcept
Returns the watched path.
Definition stop.hpp:247
std::optional< StopIntent > poll()
Reads the file's first word.
Definition stop.hpp:254
const std::string & unrecognized() const noexcept
Returns the word the last poll did not recognize.
Definition stop.hpp:289
A stop request the loop polls; safe to set from a signal handler.
Definition stop.hpp:158
std::optional< StopIntent > intent() const noexcept
Returns the requested intent.
Definition stop.hpp:191
bool requested() const noexcept
Tests whether a stop has been requested.
Definition stop.hpp:183
void clear() noexcept
Clears the request (for tests, or a supervisor deliberately resuming); the loop never does.
Definition stop.hpp:200
void request(StopIntent intent) noexcept
Requests a stop, keeping whichever of this and an earlier intent is stronger.
Definition stop.hpp:167
A compile-time check that a list names every value of an enum, and a parser over the list.
Runs a strategy: the runner, the polling loop, the order journal, and stopping.
Definition clock.hpp:7
constexpr bool stop_intent_closes_positions(StopIntent intent) noexcept
Tests whether a stop intent closes positions with market orders, after withdrawing the working orders...
Definition stop.hpp:131
std::optional< StopIntent > stop_intent_from(std::string_view text) noexcept
Parses an intent's name.
Definition stop.hpp:83
constexpr int stop_intent_strength(StopIntent intent) noexcept
Returns an intent's strength: a stronger intent does everything a weaker one does.
Definition stop.hpp:96
constexpr std::array< StopIntent, 3 > all_stop_intents
Every StopIntent, in declaration order.
Definition stop.hpp:50
StopIntent
What an operator asks for when stopping a running loop.
Definition stop.hpp:29
@ FLATTEN
Withdraw, then ask the broker to close the positions with market orders.
Definition stop.hpp:46
@ WITHDRAW
Quiesce, then ask the broker to cancel the working orders.
Definition stop.hpp:40
@ QUIESCE
Stop sending new orders; nothing is cancelled or closed. The default, and what a signal means.
Definition stop.hpp:34
constexpr bool stop_intent_covers(StopIntent held, StopIntent candidate) noexcept
Tests whether one intent does everything another would.
Definition stop.hpp:148
constexpr bool stop_intent_closes(StopIntent intent) noexcept
Tests whether a stop intent asks for a closing pass (cancellations, and for FLATTEN closing orders) a...
Definition stop.hpp:114
constexpr std::string_view stop_intent_name(StopIntent intent) noexcept
Returns a lowercase name for an intent, as written in the control file ("withdraw").
Definition stop.hpp:61
The library: broker-neutral types (core), and the modules below.
constexpr std::optional< Enum > value_named(const std::array< Enum, N > &values, Name name, std::string_view text) noexcept
Returns the value of a list whose name is text, so a parser reads exactly what its writer writes.
constexpr bool lists_every_value(const std::array< Enum, N > &values, IsEnumerator is_enumerator) noexcept
Tests whether a list holds every value of an enum exactly once, in any order.