11 return a.sequence ==
b.sequence && a.checkpoint ==
b.checkpoint &&
12 a.bar ==
b.bar && a.target_command ==
b.target_command;
15 return a.sequence ==
b.sequence && a.cause_fill ==
b.cause_fill &&
16 a.bar ==
b.bar && a.target_command ==
b.target_command;
22void require_origin(
const std::shared_ptr<const CommandObservation>& origin,
23 uint64_t target, uint64_t event,
int bar) {
24 if (!origin || target != origin->command || event <= origin->command ||
26 throw std::invalid_argument(
"admission receipt requires its exact earlier command");
31 throw std::logic_error(
"market draft requires one original command observation");
37 throw std::invalid_argument(
"review requires a known checkpoint");
39 if (same(*review_, receipt))
return;
40 throw std::logic_error(
"first admission review cannot be replaced");
47 throw std::invalid_argument(
"sizing revision requires committed cause");
48 if (sizing_revision_) {
49 if (same(*sizing_revision_, receipt))
return;
50 if (receipt.
sequence <= sizing_revision_->sequence ||
51 receipt.
bar < sizing_revision_->bar || receipt.
cause_fill < sizing_revision_->cause_fill)
52 throw std::logic_error(
"sizing revision cannot rewrite or precede its latest cause");
54 sizing_revision_ = receipt;
57 return std::visit([](
const auto& value)->uint64_t {
58 using T=std::decay_t<
decltype(value)>;
59 if constexpr(std::is_same_v<T,CommandEvent>) {
60 if(!value.observation)
throw std::invalid_argument(
"command event requires an observation");
61 return value.observation->command;
63 else return value.receipt.sequence;
67 if (other.active_allocations_ != 0)
68 throw std::logic_error(
"cannot copy admission journal with active captures");
69 next_sequence_ = other.next_sequence_;
70 outstanding_sequences_ = other.outstanding_sequences_;
71 events_ = other.events_;
74 if (
this == &other)
return *
this;
75 if (active_allocations_ != 0)
76 throw std::logic_error(
"cannot replace admission journal with active captures");
78 std::swap(next_sequence_, copy.next_sequence_);
79 outstanding_sequences_.swap(copy.outstanding_sequences_);
80 events_.swap(copy.events_);
84 if (other.active_allocations_ != 0)
85 throw std::logic_error(
"cannot move admission journal with active captures");
86 next_sequence_ = other.next_sequence_;
87 outstanding_sequences_ = std::move(other.outstanding_sequences_);
88 events_ = std::move(other.events_);
89 other.outstanding_sequences_.clear();
90 other.events_.clear();
91 other.next_sequence_ = 1;
94 if (
this == &other)
return *
this;
95 if (active_allocations_ != 0)
96 throw std::logic_error(
"cannot replace admission journal with active captures");
97 Journal moved(std::move(other));
98 std::swap(next_sequence_, moved.next_sequence_);
99 outstanding_sequences_.swap(moved.outstanding_sequences_);
100 events_.swap(moved.events_);
104 if(next_sequence_==std::numeric_limits<uint64_t>::max())
throw std::overflow_error(
"admission event sequence exhausted");
105 outstanding_sequences_.push_back(next_sequence_);
106 return next_sequence_++;
110 ++active_allocations_;
114 const auto at = std::find(outstanding_sequences_.begin(), outstanding_sequences_.end(),
id);
115 if (at != outstanding_sequences_.end()) outstanding_sequences_.erase(at);
118 : journal_(other.journal_), sequence_(other.sequence_) {
119 other.journal_ =
nullptr;
122void Journal::release_allocation(uint64_t
sequence)
noexcept {
124 --active_allocations_;
129 const auto pending = std::find(outstanding_sequences_.begin(), outstanding_sequences_.end(),
id);
130 if (pending == outstanding_sequences_.end())
131 throw std::logic_error(
"admission event has no outstanding allocation");
132 auto at=std::lower_bound(events_.begin(),events_.end(),
id,[](
const auto& e,uint64_t n){return sequence(e)<n;});
133 events_.insert(at,std::move(event));
136 outstanding_sequences_.erase(pending);
139 events_.erase(std::remove_if(events_.begin(),events_.end(),[&](
const auto& e){
140 return std::find(retained.begin(),retained.end(),sequence(e))==retained.end();
144 if (!outstanding_sequences_.empty() || active_allocations_ != 0)
145 throw std::logic_error(
"cannot reset admission journal with unfinished events");
146 events_.clear();next_sequence_=1;
149 std::function<
void(
CommandEvent)> complete):allocation_(
std::move(allocation)),input_(
std::move(
input)),before_(
std::move(before)),complete_(
std::move(complete)) {
150 if (input_.command != allocation_.sequence())
151 throw std::invalid_argument(
"command capture requires its allocated event");
156 if (std::uncaught_exceptions() != 0)
return;
157 complete_({std::make_shared<const CommandObservation>(input_),outcome_,0,std::move(before_),{}});
160 input_.original_sizing=std::move(sizing);input_.explicit_equity=equity;input_.explicit_price=
price;
161 draft.
bind(std::make_shared<const CommandObservation>(input_));
166 : allocation_(
std::move(allocation)),event_(
std::move(event)),complete_(
std::move(complete)) {
167 if (event_.receipt.sequence != allocation_.sequence() || event_.receipt.target_command != 0 ||
168 !known_checkpoint(event_.receipt.checkpoint))
169 throw std::invalid_argument(
"review capture requires an allocated batch checkpoint");
172 if (std::uncaught_exceptions() != 0)
return;
173 complete_(std::move(event_));
177 if (!origin || std::none_of(event_.reviewed.begin(), event_.reviewed.end(), [&](
const auto& order) {
178 return order.draft.observation() && order.draft.observation()->command == origin->command;
180 throw std::invalid_argument(
"draft does not belong to this admission review");
181 auto receipt = event_.receipt;
182 receipt.target_command = origin->command;
183 require_origin(origin, receipt.target_command, receipt.sequence, receipt.bar);
190 template<
class T>
void field(
const std::string& path,
const char* name,
const T& value)
const {
191 if constexpr(std::is_same_v<T,bool>)visit({path+
"."+name,uint64_t(value?1:0)});
192 else if constexpr(std::is_enum_v<T>||std::is_same_v<T,int>)visit({path+
"."+name,int64_t(value)});
193 else visit({path+
"."+name,value});
195 void birth(
const OrderBirth& o,
const std::string& p)
const {
196 field(p,
"cause",o.cause());field(p,
"bar",o.bar());field(p,
"timestamp",o.timestamp());
197 field(p,
"cursor_domain",o.cursor().domain());field(p,
"cursor_position",o.cursor().position());
198 field(p,
"cursor_index",o.cursor().index());field(p,
"cursor_count",o.cursor().count());
199 field(p,
"cursor_price",o.cursor_price());field(p,
"first_fill",o.first_fill());
200 field(p,
"last_fill",o.last_fill());field(p,
"evaluation_ordinal",o.evaluation_ordinal());
202 void config(
const Configuration& o,
const std::string& p)
const {
203#define F(name) field(p,#name,o.name)
204 F(process_on_close);
F(calc_on_fills);
F(magnifier);
F(fill_recalculation);
F(scheduler);
205 F(slippage);
F(pyramiding);
F(default_quantity_type);
F(default_quantity_value);
206 F(long_margin);
F(short_margin);
F(commission_value);
F(commission_type);
F(pointvalue);
F(fx);
F(quantity_step);
F(mintick);
F(risk_direction);
F(loss_days_limit);
207 F(drawdown_limit);
F(intraday_loss_limit);
F(position_limit);
F(fill_cap_active);
F(risk_halted);
210 void sizing(
const SizingObservation& o,
const std::string& p)
const {
211#define F(name) field(p,#name,o.name)
215 void command(
const CommandObservation& o,
const std::string& p)
const {
216#define F(name) field(p,#name,o.name)
217 F(command);
F(kind);birth(o.birth,p+
".birth");
F(
id);
F(requested_quantity);
F(quantity_type);
F(buy);
218 field(p+
".prices",
"limit",o.prices.limit);field(p+
".prices",
"stop",o.prices.stop);
219 F(oca_name);
F(oca_type);config(o.configuration,p+
".configuration");
F(bar);
F(placement_side);
F(placement_cycle);
220 F(prior_close_quantity);
F(held_quantity);
F(held_entries);
F(realized_equity);
F(placement_equity);
F(signal_close);
F(quantized_fixed_quantity);
221 field(p,
"original_sizing_present",o.original_sizing.has_value());
222 if(o.original_sizing)sizing(*o.original_sizing,p+
".original_sizing");
223 F(explicit_equity);
F(explicit_price);
226 void review(
const ReviewReceipt& o,
const std::string& p)
const {
227 field(p,
"sequence",o.sequence);field(p,
"checkpoint",o.checkpoint);field(p,
"bar",o.bar);
228 field(p,
"target_command",o.target_command);
230 void revision(
const SizingRevision& o,
const std::string& p)
const {
231 field(p,
"sequence",o.sequence);field(p,
"cause_fill",o.cause_fill);field(p,
"bar",o.bar);
232 field(p,
"target_command",o.target_command);
234 void draft(
const Draft& o,
const std::string& p)
const {
235 field(p,
"observation_present",
bool(o.observation()));
236 if(o.observation())command(*o.observation(),p+
".observation");
237 field(p,
"review_present",o.review().has_value());
if(o.review())review(*o.review(),p+
".review");
238 field(p,
"sizing_revision_present",o.sizing_revision().has_value());
if(o.sizing_revision())revision(*o.sizing_revision(),p+
".sizing_revision");
240 void book(
const BookObservation& o,
const std::string& p)
const {
241 field(p,
"incarnation",o.incarnation);field(p,
"priority",o.priority);field(p,
"bar",o.bar);
242 field(p,
"type",o.type);field(p,
"placement_side",o.placement_side);field(p,
"buy",o.buy);field(p,
"id",o.id);
243 field(p,
"oca_name",o.oca_name);field(p,
"oca_type",o.oca_type);birth(o.birth,p+
".birth");
244 field(p+
".prices",
"limit",o.prices.limit);field(p+
".prices",
"stop",o.prices.stop);
245 field(p+
".prices",
"trail_points",o.prices.trail_points);field(p+
".prices",
"trail_price",o.prices.trail_price);
246 field(p+
".prices",
"trail_offset",o.prices.trail_offset);draft(o.draft,p+
".draft");
248 void resolution(
const InstructionResolution& o,
const std::string& p)
const {
249 field(p,
"incarnation",o.incarnation);field(p,
"kind",o.kind);field(p,
"peer_incarnation",o.peer_incarnation);
250 field(p,
"own_priority",o.own_priority);field(p,
"peer_priority",o.peer_priority);field(p,
"transaction_quantity",o.transaction_quantity);
252 template<
class T,
class Fn>
void array(
const std::vector<T>& values,
const std::string& p,Fn fn)
const {
253 field(p,
"size",uint64_t(values.size()));
for(
size_t i=0;i<values.size();++i)fn(values[i],p+
"["+std::to_string(i)+
"]");
255 void event(
const Event& value,
const std::string& p)
const {
256 field(p,
"kind",uint64_t(value.index()));
257 std::visit([&](
const auto& o){
using T=std::decay_t<
decltype(o)>;
258 if constexpr(std::is_same_v<T,CommandEvent>){
259 command(*o.observation,p+
".observation");field(p,
"outcome",o.outcome);field(p,
"admitted_incarnation",o.admitted_incarnation);
260 array(o.before,p+
".before",[&](
const auto& x,
const auto& q){book(x,q);});
261 array(o.removed,p+
".removed",[&](
auto x,
const auto& q){field(q,
"incarnation",x);});
262 }
else if constexpr(std::is_same_v<T,ReviewEvent>){
263 review(o.receipt,p+
".receipt");config(o.configuration,p+
".configuration");field(p,
"open_price",o.open_price);field(p,
"position_side",o.position_side);field(p,
"position_cycle",o.position_cycle);array(o.book,p+
".book",[&](
const auto& x,
const auto& q){book(x,q);});array(o.reviewed,p+
".reviewed",[&](
const auto& x,
const auto& q){book(x,q);});
264 array(o.resolutions,p+
".resolutions",[&](
const auto& x,
const auto& q){resolution(x,q);});
265 array(o.causes,p+
".causes",[&](
auto x,
const auto& q){field(q,
"sequence",x);});
267 revision(o.receipt,p+
".receipt");field(p,
"incarnation",o.incarnation);
268 sizing(o.before,p+
".before");sizing(o.after,p+
".after");
269 field(p,
"affordability_equity_before",o.affordability_equity_before);field(p,
"affordability_equity_after",o.affordability_equity_after);
278 Reflect r{visit};r.field(path,
"next_sequence",next_sequence_);
279 r.field(path,
"active_allocations",active_allocations_);
280 r.array(outstanding_sequences_,path+
".outstanding_sequences",[&](
auto sequence,
const auto& p){r.field(p,
"sequence",
sequence);});
281 r.array(events_,path+
".events",[&](
const auto& event,
const auto& p){r.event(event,p);});
284 std::vector<Field> result;
reflect(draft,
"draft",[&](
const auto& field){result.push_back(field);});
return result;
287template<
class T>T read_field(
const std::vector<Field>&
fields,
const std::string& path) {
288 for(
const auto& field:
fields)
if(field.path==path)
return std::get<T>(field.value);
292uint64_t
read_unsigned(
const std::vector<Field>& f,
const std::string& p){
return read_field<uint64_t>(f,p);}
293int64_t
read_integer(
const std::vector<Field>& f,
const std::string& p){
return read_field<int64_t>(f,p);}
294double read_double(
const std::vector<Field>& f,
const std::string& p){
return read_field<double>(f,p);}
295std::string
read_string(
const std::vector<Field>& f,
const std::string& p){
return read_field<std::string>(f,p);}
Allocation(const Allocation &)=delete
CommandCapture(Allocation allocation, CommandObservation input, std::vector< BookObservation > before, std::function< void(CommandEvent)> complete)
void bind(Draft &draft, std::optional< SizingObservation > sizing, double explicit_equity, double explicit_price)
~CommandCapture() noexcept(false)
const CommandObservation & input() const
const std::shared_ptr< const CommandObservation > & observation() const
void reviewed(ReviewReceipt receipt)
void bind(std::shared_ptr< const CommandObservation > observation)
void sizing_revised(SizingRevision receipt)
void abandon(uint64_t sequence) noexcept
Journal & operator=(const Journal &other)
void reflect(const std::string &path, const FieldVisitor &visit) const
void retain(const std::vector< uint64_t > &sequences)
~ReviewCapture() noexcept(false)
ReviewCapture(Allocation allocation, ReviewEvent event, std::function< void(ReviewEvent)> complete)
ReviewReceipt receipt_for(const Draft &draft) const
uint64_t read_unsigned(const std::vector< Field > &fields, const std::string &path)
std::string read_string(const std::vector< Field > &fields, const std::string &path)
int64_t read_integer(const std::vector< Field > &fields, const std::string &path)
std::vector< Field > fields(const Draft &draft)
std::function< void(const Field &)> FieldVisitor
uint64_t sequence(const Event &event)
double read_double(const std::vector< Field > &fields, const std::string &path)
void reflect(const Draft &value, const std::string &path, const FieldVisitor &visit)
std::variant< CommandEvent, ReviewEvent, SizingEvent > Event
@ OpeningRejectedReductionAdmitted