110 if (!incarnation || (target_.incarnation && target_.incarnation != incarnation))
111 throw std::logic_error(
"definition identity mismatch");
112 Definition d = definition_; d.incarnation_ = incarnation;
return d;
117 const std::optional<Suspension>&
suspension()
const {
return suspension_; }
118 const std::array<std::optional<Retirement>, 3>&
retirements()
const {
return retired_; }
119 const std::optional<Action>&
last_action()
const {
return last_; }
121 if (!suspension_)
return false;
122 for (
Leg selected : suspension_->legs)
if (selected == leg)
return true;
125 bool retired(
Leg leg)
const {
return retired_[index(leg)].has_value(); }
128 return leg !=
Leg::Trail || !suspension_ || !suspension_->window
129 || suspension_->window->excluded.bar != bar;
131 bool dormant()
const {
return suspension_.has_value(); }
134 return suspension_ && suspension_->revival_definition
135 ? suspension_->revival_definition->prices().stop_price :
absent();
138 if (!suspension_)
return {};
139 if (suspension_->replacement)
return suspension_->replacement->release;
140 return suspension_->hold;
142 int64_t
hold_bar()
const {
return suspension_ && suspension_->hold ? suspension_->hold->requested.bar : -1; }
143 int64_t
excluded_bar()
const {
return suspension_ && suspension_->window ? suspension_->window->excluded.bar : -1; }
144 double trail_best()
const {
return suspension_ && suspension_->window ? suspension_->window->best :
absent(); }
145 double trail_prefix()
const {
return suspension_ && suspension_->window ? suspension_->window->prefix :
absent(); }
149 void attach(uint64_t incarnation, int64_t owner) {
150 if (!incarnation || owner < 0 || target_.incarnation)
throw std::logic_error(
"exit lifecycle already attached");
151 target_ = {incarnation, owner}; definition_.incarnation_ = incarnation;
153 void fork(uint64_t incarnation, int64_t owner) {
154 if (!incarnation || owner < 0 || incarnation == target_.incarnation)
throw std::logic_error(
"invalid exit lifecycle fork");
155 target_ = {incarnation, owner}; definition_.incarnation_ = incarnation;
156 revision_ = 0; last_.reset();
159 if (suspension_ && suspension_->replacement)
160 suspension_->replacement->queue_predecessor = 0;
161 if (suspension_ && suspension_->hold) {
162 suspension_->hold->target = target_; suspension_->hold->revision = revision_;
164 if (suspension_ && suspension_->replacement) {
165 suspension_->replacement->release.target = target_;
166 suspension_->replacement->release.revision = revision_;
174 if (revision_ == UINT64_MAX || definition_.revision_ == UINT64_MAX)
175 throw std::overflow_error(
"exit definition revision exhausted");
176 auto next = std::make_shared<const Prices>(std::move(value));
177 definition_.value_ = std::move(next); ++definition_.revision_; ++revision_;
179#define PF_LEG_PRICE_SETTER(name) \
180 double set_##name(double value) { auto next = prices(); next.name = value; set_prices(std::move(next)); return value; }
188#undef PF_LEG_PRICE_SETTER
195 if (last_ && action.
cause.
event == last_->cause.event) {
204 ++next.revision_; next.last_ = action;
205 *
this = std::move(next);
211 template<
class Sink>
void visit(Sink& f)
const {
212 f.u(target_.incarnation); f.i(target_.owner); f.u(revision_);
213 visit_definition(f, definition_);
215 for (
const auto& retirement : retired_) {
216 f.b(retirement.has_value());
217 if (retirement) { f.u(retirement->generation); visit_frame(f, retirement->cause); }
219 f.b(suspension_.has_value());
221 visit_frame(f, suspension_->cause); visit_legs(f, suspension_->legs);
222 visit_barrier(f, suspension_->hold);
223 f.b(suspension_->revival_definition.has_value());
224 if (suspension_->revival_definition) visit_definition(f, *suspension_->revival_definition);
225 f.b(suspension_->replacement.has_value());
226 if (suspension_->replacement) visit_replacement(f, *suspension_->replacement);
227 visit_window(f, suspension_->window);
229 f.b(last_.has_value());
if (last_) visit_action(f, *last_);
234 uint64_t revision_ = 0;
235 std::array<uint64_t, 3> generations_{{1, 1, 1}};
236 std::array<std::optional<Retirement>, 3> retired_;
237 std::optional<Suspension> suspension_;
238 std::optional<Action> last_;
240 static size_t index(
Leg leg) {
return static_cast<size_t>(leg); }
241 static bool valid_legs(
const std::vector<Leg>& legs) {
243 for (
Leg leg : legs) {
const auto n = index(leg);
if (n >= 3 || (seen & (1u << n)))
return false; seen |= 1u << n; }
244 return !legs.empty();
246 static bool valid_frame(
const Frame& f) {
247 return static_cast<unsigned>(f.domain) <=
static_cast<unsigned>(
Domain::RawTicks)
250 static bool valid_payload(
const Action& action) {
251 if (!valid_frame(action.cause))
return false;
252 return std::visit([](
const auto& op) {
253 using T = std::decay_t<
decltype(op)>;
254 if constexpr (std::is_same_v<T, Suspend>)
255 return valid_legs(op.legs) && (op.retire.empty() || valid_legs(op.retire))
256 && (!op.hold || valid_frame(op.hold->requested))
257 && (!op.window || valid_frame(op.window->excluded));
258 else if constexpr (std::is_same_v<T, Restore> || std::is_same_v<T, Cancel>)
return valid_legs(op.legs);
259 else if constexpr (std::is_same_v<T, BindOwner>)
return op.owner >= 0;
260 else if constexpr (std::is_same_v<T, Observe>)
return (op.direction == 1 || op.direction == -1)
262 else if constexpr (std::is_same_v<T, CompleteBarrier>)
return valid_frame(op.completed)
263 && (!op.requested || valid_frame(op.requested->requested));
264 else if constexpr (std::is_same_v<T, StageReplacement>)
return valid_frame(op.relation.release.requested);
266 }, action.operation);
268 static bool unissued(
const Barrier& b) {
269 return b.target.incarnation == 0 &&
b.target.owner == 0 &&
b.revision == 0;
271 Barrier issue(Barrier b)
const {
272 b.target = target_;
b.revision = revision_ + 1;
return b;
274 static bool same_barrier(
const Barrier& a,
const Barrier& b) {
275 return a.target.incarnation ==
b.target.incarnation && a.target.owner ==
b.target.owner
276 && a.revision ==
b.revision && a.requested.event ==
b.requested.event
277 && a.requested.bar ==
b.requested.bar && a.requested.domain ==
b.requested.domain
278 && a.requested.phase ==
b.requested.phase;
282 static bool not_after(
const Frame& occurrence,
const Frame& processing) {
283 return occurrence.event <= processing.event
284 && (occurrence.domain != processing.domain
285 || occurrence.bar < processing.bar
286 || (occurrence.bar == processing.bar
287 &&
static_cast<unsigned>(occurrence.phase)
288 <=
static_cast<unsigned>(processing.phase)));
290 bool restore(
const std::vector<Leg>& legs) {
291 if (!valid_legs(legs))
return false;
292 for (
Leg leg : legs)
if (generations_[index(leg)] == UINT64_MAX)
return false;
293 for (
Leg leg : legs) {
294 ++generations_[index(leg)]; retired_[index(leg)].reset();
296 auto& list = suspension_->legs;
297 for (
auto it = list.begin(); it != list.end();) {
298 if (*it == leg) it = list.erase(it);
else ++it;
302 if (suspension_ && suspension_->legs.empty()) suspension_.reset();
305 bool perform(
const Action& action) {
306 return std::visit([&](
const auto& op) ->
bool {
307 using T = std::decay_t<
decltype(op)>;
308 if constexpr (std::is_same_v<T, BindOwner>) { target_.owner = op.owner;
return true; }
309 else if constexpr (std::is_same_v<T, Suspend>) {
310 if (!valid_legs(op.legs) || (!op.retire.empty() && !valid_legs(op.retire)))
return false;
311 if (op.hold && !unissued(*op.hold))
return false;
312 const auto hold = op.hold ? std::optional<Barrier>{issue(*op.hold)} : std::nullopt;
313 suspension_ = Suspension{action.cause, op.legs, hold, {}, {}, op.window};
314 for (
Leg leg : op.retire)
if (!retired_[index(leg)])
315 retired_[index(leg)] = Retirement{generations_[index(leg)], action.cause};
317 }
else if constexpr (std::is_same_v<T, StageReplacement>) {
318 if (!op.relation.revival_definition.incarnation() || !unissued(op.relation.release))
return false;
322 if (suspension_ && (suspension_->hold || suspension_->replacement))
return false;
323 if (!suspension_) suspension_ = Suspension{action.cause, {
Leg::Stop,
Leg::Limit}, {}, {}, {}, {}};
324 suspension_->revival_definition = op.relation.revival_definition;
325 suspension_->replacement = op.relation;
326 suspension_->replacement->release = issue(op.relation.release);
328 }
else if constexpr (std::is_same_v<T, CancelDeferredActivation>) {
329 if (!suspension_)
return false;
330 suspension_->replacement.reset();
return true;
331 }
else if constexpr (std::is_same_v<T, Restore>) {
return restore(op.legs); }
332 else if constexpr (std::is_same_v<T, CompleteBarrier>) {
334 if (!expected || !op.requested || !same_barrier(*expected, *op.requested))
return false;
335 const auto& origin = expected->requested;
343 if (!not_after(origin, op.completed)
344 || !not_after(op.completed, action.cause)
345 || !not_after(origin, action.cause))
return false;
347 suspension_->hold.reset();
349 }
else if constexpr (std::is_same_v<T, Observe>) {
350 if (!suspension_ || !suspension_->window || (op.direction != 1 && op.direction != -1))
return false;
351 auto& w = *suspension_->window;
355 if (action.cause.event <= w.excluded.event
356 || (action.cause.domain == w.excluded.domain
357 && action.cause.bar <= w.excluded.bar))
return false;
359 const double price = op.direction > 0 ? op.high : op.low;
360 if (std::isnan(w.best) || (op.direction > 0 ?
price > w.best :
price < w.best)) w.best =
price;
362 }
else if constexpr (std::is_same_v<T, Cancel>) {
363 if (!valid_legs(op.legs))
return false;
364 for (
Leg leg : op.legs)
if (!retired_[index(leg)])
365 retired_[index(leg)] = Retirement{generations_[index(leg)], action.cause};
369 }, action.operation);
371 template<
class S>
static void visit_frame(S& f,
const Frame& v) {
372 f.u(v.event); f.i(v.bar); f.u(
static_cast<uint64_t
>(v.domain)); f.u(
static_cast<uint64_t
>(v.phase));
374 template<
class S>
static void visit_prices(S& f,
const Prices& p) {
375 f.d(p.limit_price); f.d(p.stop_price); f.d(p.trail_points); f.d(p.trail_price);
376 f.d(p.trail_offset); f.d(p.profit_ticks); f.d(p.loss_ticks);
378 template<
class S>
static void visit_definition(S& f,
const Definition& d) {
379 f.u(d.incarnation_); f.u(d.revision_); f.b(d.value_ !=
nullptr); visit_prices(f, d.prices());
381 template<
class S>
static void visit_legs(S& f,
const std::vector<Leg>& legs) {
382 f.u(legs.size());
for (
Leg leg : legs) f.u(
static_cast<uint64_t
>(leg));
384 template<
class S>
static void visit_barrier_value(S& f,
const Barrier& b) {
385 visit_frame(f,
b.requested); f.u(
b.target.incarnation); f.i(
b.target.owner); f.u(
b.revision);
387 template<
class S>
static void visit_barrier(S& f,
const std::optional<Barrier>& b) {
388 f.b(
b.has_value());
if (b) visit_barrier_value(f, *b);
390 template<
class S>
static void visit_window(S& f,
const std::optional<ObservationWindow>& w) {
391 f.b(w.has_value());
if (w) { visit_frame(f, w->excluded); f.d(w->best); f.d(w->prefix); }
393 template<
class S>
static void visit_replacement(S& f,
const Replacement& r) {
394 f.u(
r.queue_predecessor); visit_definition(f,
r.revival_definition); visit_barrier_value(f,
r.release);
398 template<
class S>
struct ReplaySink {
400 void u(uint64_t v) { sink.u(v); }
401 void i(int64_t v) { sink.i(v); }
402 void b(
bool v) { sink.b(v); }
403 void d(
double v) { uint64_t bits; std::memcpy(&bits, &v,
sizeof(bits)); sink.u(bits); }
405 template<
class S>
static void visit_action(S& f,
const Action& a) {
406 ReplaySink<S> raw{f};
407 visit_action_fields(raw, a);
409 template<
class S>
static void visit_action_fields(S& f,
const Action& a) {
410 f.u(a.target.incarnation); f.i(a.target.owner); f.u(a.expected_revision); visit_frame(f, a.cause);
411 f.u(a.operation.index());
412 std::visit([&](
const auto& op) {
413 using T = std::decay_t<
decltype(op)>;
414 if constexpr (std::is_same_v<T, BindOwner>) f.i(op.owner);
415 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); }
416 else if constexpr (std::is_same_v<T, StageReplacement>) visit_replacement(f, op.relation);
417 else if constexpr (std::is_same_v<T, Restore> || std::is_same_v<T, Cancel>) visit_legs(f, op.legs);
418 else if constexpr (std::is_same_v<T, CompleteBarrier>) { visit_frame(f, op.completed); visit_barrier(f, op.requested); }
419 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)); }
423 std::vector<uint64_t> words;
424 void u(uint64_t v) { words.push_back(v); }
425 void i(int64_t v) { u(
static_cast<uint64_t
>(v)); }
426 void b(
bool v) { u(v ? 1 : 0); }
427 void d(
double v) { uint64_t bits; std::memcpy(&bits, &v,
sizeof(bits)); u(bits); }
429 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; }