112 if (!incarnation || (target_.incarnation && target_.incarnation != incarnation))
113 throw std::logic_error(
"definition identity mismatch");
114 Definition d = definition_; d.incarnation_ = incarnation;
return d;
119 const std::optional<Suspension>&
suspension()
const {
return suspension_; }
120 const std::array<std::optional<Retirement>, 3>&
retirements()
const {
return retired_; }
121 const std::optional<Action>&
last_action()
const {
return last_; }
123 if (!suspension_)
return false;
124 for (
Leg selected : suspension_->legs)
if (selected == leg)
return true;
127 bool retired(
Leg leg)
const {
return retired_[index(leg)].has_value(); }
130 return leg !=
Leg::Trail || !suspension_ || !suspension_->window
131 || suspension_->window->excluded.bar != bar;
133 bool dormant()
const {
return suspension_.has_value(); }
136 return suspension_ && suspension_->revival_definition
137 ? suspension_->revival_definition->prices().stop_price :
absent();
140 if (!suspension_)
return {};
141 if (suspension_->replacement)
return suspension_->replacement->release;
142 return suspension_->hold;
144 int64_t
hold_bar()
const {
return suspension_ && suspension_->hold ? suspension_->hold->requested.bar : -1; }
145 int64_t
excluded_bar()
const {
return suspension_ && suspension_->window ? suspension_->window->excluded.bar : -1; }
146 double trail_best()
const {
return suspension_ && suspension_->window ? suspension_->window->best :
absent(); }
147 double trail_prefix()
const {
return suspension_ && suspension_->window ? suspension_->window->prefix :
absent(); }
151 void attach(uint64_t incarnation, int64_t owner) {
152 if (!incarnation || owner < 0 || target_.incarnation)
throw std::logic_error(
"exit lifecycle already attached");
153 target_ = {incarnation, owner}; definition_.incarnation_ = incarnation;
155 void fork(uint64_t incarnation, int64_t owner) {
156 if (!incarnation || owner < 0 || incarnation == target_.incarnation)
throw std::logic_error(
"invalid exit lifecycle fork");
157 target_ = {incarnation, owner}; definition_.incarnation_ = incarnation;
158 revision_ = 0; last_.reset();
161 if (suspension_ && suspension_->replacement)
162 suspension_->replacement->queue_predecessor = 0;
163 if (suspension_ && suspension_->hold) {
164 suspension_->hold->target = target_; suspension_->hold->revision = revision_;
166 if (suspension_ && suspension_->replacement) {
167 suspension_->replacement->release.target = target_;
168 suspension_->replacement->release.revision = revision_;
176 if (revision_ == UINT64_MAX || definition_.revision_ == UINT64_MAX)
177 throw std::overflow_error(
"exit definition revision exhausted");
178 auto next = std::make_shared<const Prices>(std::move(value));
179 definition_.value_ = std::move(next); ++definition_.revision_; ++revision_;
181#define PF_LEG_PRICE_SETTER(name) \
182 double set_##name(double value) { auto next = prices(); next.name = value; set_prices(std::move(next)); return value; }
190#undef PF_LEG_PRICE_SETTER
197 if (last_ && action.
cause.
event == last_->cause.event) {
206 ++next.revision_; next.last_ = action;
207 *
this = std::move(next);
213 template<
class Sink>
void visit(Sink& f)
const {
214 f.u(target_.incarnation); f.i(target_.owner); f.u(revision_);
215 visit_definition(f, definition_);
217 for (
const auto& retirement : retired_) {
218 f.b(retirement.has_value());
219 if (retirement) { f.u(retirement->generation); visit_frame(f, retirement->cause); }
221 f.b(suspension_.has_value());
223 visit_frame(f, suspension_->cause); visit_legs(f, suspension_->legs);
224 visit_barrier(f, suspension_->hold);
225 f.b(suspension_->revival_definition.has_value());
226 if (suspension_->revival_definition) visit_definition(f, *suspension_->revival_definition);
227 f.b(suspension_->replacement.has_value());
228 if (suspension_->replacement) visit_replacement(f, *suspension_->replacement);
229 visit_window(f, suspension_->window);
231 f.b(last_.has_value());
if (last_) visit_action(f, *last_);
236 uint64_t revision_ = 0;
237 std::array<uint64_t, 3> generations_{{1, 1, 1}};
238 std::array<std::optional<Retirement>, 3> retired_;
239 std::optional<Suspension> suspension_;
240 std::optional<Action> last_;
242 static size_t index(
Leg leg) {
return static_cast<size_t>(leg); }
243 static bool valid_legs(
const std::vector<Leg>& legs) {
245 for (
Leg leg : legs) {
const auto n = index(leg);
if (n >= 3 || (seen & (1u << n)))
return false; seen |= 1u << n; }
246 return !legs.empty();
248 static bool valid_frame(
const Frame& f) {
249 return static_cast<unsigned>(f.domain) <=
static_cast<unsigned>(
Domain::RawTicks)
252 static bool valid_payload(
const Action& action) {
253 if (!valid_frame(action.cause))
return false;
254 return std::visit([](
const auto& op) {
255 using T = std::decay_t<
decltype(op)>;
256 if constexpr (std::is_same_v<T, Suspend>)
257 return valid_legs(op.legs) && (op.retire.empty() || valid_legs(op.retire))
258 && (!op.hold || valid_frame(op.hold->requested))
259 && (!op.window || valid_frame(op.window->excluded));
260 else if constexpr (std::is_same_v<T, Restore> || std::is_same_v<T, Cancel>)
return valid_legs(op.legs);
261 else if constexpr (std::is_same_v<T, BindOwner>)
return op.owner >= 0;
262 else if constexpr (std::is_same_v<T, Observe>)
return (op.direction == 1 || op.direction == -1)
264 else if constexpr (std::is_same_v<T, CompleteBarrier>)
return valid_frame(op.completed)
265 && (!op.requested || valid_frame(op.requested->requested));
266 else if constexpr (std::is_same_v<T, StageReplacement>)
return valid_frame(op.relation.release.requested);
268 }, action.operation);
270 static bool unissued(
const Barrier& b) {
271 return b.target.incarnation == 0 &&
b.target.owner == 0 &&
b.revision == 0;
273 Barrier issue(Barrier b)
const {
274 b.target = target_;
b.revision = revision_ + 1;
return b;
276 static bool same_barrier(
const Barrier& a,
const Barrier& b) {
277 return a.target.incarnation ==
b.target.incarnation && a.target.owner ==
b.target.owner
278 && a.revision ==
b.revision && a.requested.event ==
b.requested.event
279 && a.requested.bar ==
b.requested.bar && a.requested.domain ==
b.requested.domain
280 && a.requested.phase ==
b.requested.phase;
284 static bool not_after(
const Frame& occurrence,
const Frame& processing) {
285 return occurrence.event <= processing.event
286 && (occurrence.domain != processing.domain
287 || occurrence.bar < processing.bar
288 || (occurrence.bar == processing.bar
289 &&
static_cast<unsigned>(occurrence.phase)
290 <=
static_cast<unsigned>(processing.phase)));
292 bool restore(
const std::vector<Leg>& legs) {
293 if (!valid_legs(legs))
return false;
294 for (
Leg leg : legs)
if (generations_[index(leg)] == UINT64_MAX)
return false;
295 for (
Leg leg : legs) {
296 ++generations_[index(leg)]; retired_[index(leg)].reset();
298 auto& list = suspension_->legs;
299 for (
auto it = list.begin(); it != list.end();) {
300 if (*it == leg) it = list.erase(it);
else ++it;
304 if (suspension_ && suspension_->legs.empty()) suspension_.reset();
307 bool perform(
const Action& action) {
308 return std::visit([&](
const auto& op) ->
bool {
309 using T = std::decay_t<
decltype(op)>;
310 if constexpr (std::is_same_v<T, BindOwner>) { target_.owner = op.owner;
return true; }
311 else if constexpr (std::is_same_v<T, Suspend>) {
312 if (!valid_legs(op.legs) || (!op.retire.empty() && !valid_legs(op.retire)))
return false;
313 if (op.hold && !unissued(*op.hold))
return false;
314 const auto hold = op.hold ? std::optional<Barrier>{issue(*op.hold)} : std::nullopt;
315 suspension_ = Suspension{action.cause, op.legs, hold, {}, {}, op.window};
316 for (
Leg leg : op.retire)
if (!retired_[index(leg)])
317 retired_[index(leg)] = Retirement{generations_[index(leg)], action.cause};
319 }
else if constexpr (std::is_same_v<T, StageReplacement>) {
320 if (!op.relation.revival_definition.incarnation() || !unissued(op.relation.release))
return false;
324 if (suspension_ && (suspension_->hold || suspension_->replacement))
return false;
325 if (!suspension_) suspension_ = Suspension{action.cause, {
Leg::Stop,
Leg::Limit}, {}, {}, {}, {}};
326 suspension_->revival_definition = op.relation.revival_definition;
327 suspension_->replacement = op.relation;
328 suspension_->replacement->release = issue(op.relation.release);
330 }
else if constexpr (std::is_same_v<T, CancelDeferredActivation>) {
331 if (!suspension_)
return false;
332 suspension_->replacement.reset();
return true;
333 }
else if constexpr (std::is_same_v<T, Restore>) {
return restore(op.legs); }
334 else if constexpr (std::is_same_v<T, CompleteBarrier>) {
336 if (!expected || !op.requested || !same_barrier(*expected, *op.requested))
return false;
337 const auto& origin = expected->requested;
345 if (!not_after(origin, op.completed)
346 || !not_after(op.completed, action.cause)
347 || !not_after(origin, action.cause))
return false;
349 suspension_->hold.reset();
351 }
else if constexpr (std::is_same_v<T, Observe>) {
352 if (!suspension_ || !suspension_->window || (op.direction != 1 && op.direction != -1))
return false;
353 auto& w = *suspension_->window;
357 if (action.cause.event <= w.excluded.event
358 || (action.cause.domain == w.excluded.domain
359 && action.cause.bar <= w.excluded.bar))
return false;
361 const double price = op.direction > 0 ? op.high : op.low;
362 if (std::isnan(w.best) || (op.direction > 0 ?
price > w.best :
price < w.best)) w.best =
price;
364 }
else if constexpr (std::is_same_v<T, Cancel>) {
365 if (!valid_legs(op.legs))
return false;
366 for (
Leg leg : op.legs)
if (!retired_[index(leg)])
367 retired_[index(leg)] = Retirement{generations_[index(leg)], action.cause};
371 }, action.operation);
373 template<
class S>
static void visit_frame(S& f,
const Frame& v) {
374 f.u(v.event); f.i(v.bar); f.u(
static_cast<uint64_t
>(v.domain)); f.u(
static_cast<uint64_t
>(v.phase));
376 template<
class S>
static void visit_prices(S& f,
const Prices& p) {
377 f.d(p.limit_price); f.d(p.stop_price); f.d(p.trail_points); f.d(p.trail_price);
378 f.d(p.trail_offset); f.d(p.profit_ticks); f.d(p.loss_ticks);
380 template<
class S>
static void visit_definition(S& f,
const Definition& d) {
381 f.u(d.incarnation_); f.u(d.revision_); f.b(d.value_ !=
nullptr); visit_prices(f, d.prices());
383 template<
class S>
static void visit_legs(S& f,
const std::vector<Leg>& legs) {
384 f.u(legs.size());
for (
Leg leg : legs) f.u(
static_cast<uint64_t
>(leg));
386 template<
class S>
static void visit_barrier_value(S& f,
const Barrier& b) {
387 visit_frame(f,
b.requested); f.u(
b.target.incarnation); f.i(
b.target.owner); f.u(
b.revision);
389 template<
class S>
static void visit_barrier(S& f,
const std::optional<Barrier>& b) {
390 f.b(
b.has_value());
if (b) visit_barrier_value(f, *b);
392 template<
class S>
static void visit_window(S& f,
const std::optional<ObservationWindow>& w) {
393 f.b(w.has_value());
if (w) { visit_frame(f, w->excluded); f.d(w->best); f.d(w->prefix); }
395 template<
class S>
static void visit_replacement(S& f,
const Replacement& r) {
396 f.u(
r.queue_predecessor); visit_definition(f,
r.revival_definition); visit_barrier_value(f,
r.release);
400 template<
class S>
struct ReplaySink {
402 void u(uint64_t v) { sink.u(v); }
403 void i(int64_t v) { sink.i(v); }
404 void b(
bool v) { sink.b(v); }
405 void d(
double v) { uint64_t bits; std::memcpy(&bits, &v,
sizeof(bits)); sink.u(bits); }
407 template<
class S>
static void visit_action(S& f,
const Action& a) {
408 ReplaySink<S> raw{f};
409 visit_action_fields(raw, a);
411 template<
class S>
static void visit_action_fields(S& f,
const Action& a) {
412 f.u(a.target.incarnation); f.i(a.target.owner); f.u(a.expected_revision); visit_frame(f, a.cause);
413 f.u(a.operation.index());
414 std::visit([&](
const auto& op) {
415 using T = std::decay_t<
decltype(op)>;
416 if constexpr (std::is_same_v<T, BindOwner>) f.i(op.owner);
417 else if constexpr (std::is_same_v<T, Suspend>) { visit_legs(f, op.legs); visit_barrier(f, op.hold); visit_window(f, op.window); visit_legs(f, op.retire); }
418 else if constexpr (std::is_same_v<T, StageReplacement>) visit_replacement(f, op.relation);
419 else if constexpr (std::is_same_v<T, Restore> || std::is_same_v<T, Cancel>) visit_legs(f, op.legs);
420 else if constexpr (std::is_same_v<T, CompleteBarrier>) { visit_frame(f, op.completed); visit_barrier(f, op.requested); }
421 else if constexpr (std::is_same_v<T, Observe>) { f.d(op.high); f.d(op.low); f.i(op.direction); f.u(
static_cast<uint64_t
>(op.fold)); }
425 std::vector<uint64_t> words;
426 void u(uint64_t v) { words.push_back(v); }
427 void i(int64_t v) { u(
static_cast<uint64_t
>(v)); }
428 void b(
bool v) { u(v ? 1 : 0); }
429 void d(
double v) { uint64_t bits; std::memcpy(&bits, &v,
sizeof(bits)); u(bits); }
431 static bool equal(
const Action& a,
const Action& b) { Exact x, y; visit_action(x, a); visit_action(y, b);
return x.words == y.words; }