PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
compat/pine/market_admission.cpp
Go to the documentation of this file.
3#include <algorithm>
4#include <cmath>
5#include <set>
6
8namespace {
9constexpr double qty_epsilon = 1e-10;
10bool entry_like(int type) { return type == 0 || type == 1 || type == 3; }
11bool unpriced(const admission::CommandObservation& o) { return std::isnan(o.prices.limit) && std::isnan(o.prices.stop); }
12bool ordinary_terminal_call(const admission::CommandObservation& o) {
13 return !std::isnan(o.requested_quantity) && unpriced(o)
14 && o.configuration.process_on_close && o.configuration.calc_on_fills
15 && !o.configuration.fill_recalculation && o.placement_side == 0;
16}
17bool fixed_market_call(const admission::CommandObservation& o) {
18 return std::isfinite(o.requested_quantity) && o.requested_quantity > qty_epsilon
19 && unpriced(o) && o.oca_name.empty() && (o.quantity_type < 0 || o.quantity_type == 0);
20}
21bool all_in(const admission::CommandObservation& o) {
22 if (!o.original_sizing) return false;
23 const auto& c=o.configuration;const auto& s=*o.original_sizing;
24 const double margin=o.buy?c.long_margin:c.short_margin;
25 return c.default_quantity_type==1 && std::abs(c.default_quantity_value-100.0)<1e-12
26 && std::isfinite(margin) && std::abs(margin/100.0-1.0)<1e-12
27 && std::isfinite(s.quantity) && std::isfinite(s.equity) && std::isfinite(s.price)
28 && std::isfinite(s.mark) && std::isfinite(s.fx) && s.fx>0.0;
29}
30bool removed(const admission::CommandEvent& e,uint64_t incarnation) {
31 return std::find(e.removed.begin(),e.removed.end(),incarnation)!=e.removed.end();
32}
33}
35 return !c.process_on_close && !c.calc_on_fills && c.slippage==0 && c.pyramiding==2
36 && std::abs(c.long_margin-100.0)<1e-12 && std::abs(c.short_margin-100.0)<1e-12
37 && c.risk_direction==0 && c.loss_days_limit==0 && c.drawdown_limit<=0
39}
42 && c.pyramiding==1 && c.slippage==0 && c.commission_value==0.0
43 && c.default_quantity_type==1 && std::abs(c.default_quantity_value-100.0)<1e-12
44 && std::abs(c.long_margin-100.0)<1e-12 && std::abs(c.short_margin-100.0)<1e-12
45 && c.risk_direction==0 && c.loss_days_limit==0 && c.drawdown_limit<=0
47}
49 return o.kind==admission::CommandKind::Entry && fixed_market_call(o)
51}
53 return o.kind==admission::CommandKind::Entry && default_gross_scope(o.configuration)
54 && std::isnan(o.requested_quantity) && unpriced(o) && o.oca_name.empty();
55}
57 const auto& o=d.observation();return o && o->kind==admission::CommandKind::Entry
58 && unpriced(*o) && o->placement_side==0 && !(o->prior_close_quantity>qty_epsilon) && all_in(*o);
59}
61 const auto& o=d.observation();return o && o->kind==admission::CommandKind::Entry
62 && unpriced(*o) && !std::isnan(o->requested_quantity) && !std::isnan(o->signal_close)
63 && o->placement_side==0 && !(o->prior_close_quantity>qty_epsilon)
64 && std::isfinite(o->buy?o->configuration.long_margin:o->configuration.short_margin)
65 && (o->buy?o->configuration.long_margin:o->configuration.short_margin)>0.0
66 && std::isfinite(o->explicit_equity) && std::isfinite(o->explicit_price);
67}
70 if(!d.observation()||d.review())return false;
71 const auto& o=*d.observation();if(!original_default_call(o)||!all_in(o))return false;
72 const auto& s=*o.original_sizing;return s.quantity>qty_epsilon && s.equity>0 && s.mark>0;
73}
74inline const admission::Event& event_ref(const admission::Event& event) { return event; }
75inline const admission::Event& event_ref(const admission::Event* event) {
76 if (!event) {
78 "null admission history event");
79 }
80 return *event;
81}
82template<class Range>
83static History fold_admission_history(const Range& events, bool apply_reviews = true) {
84 using namespace admission;
85 History h;
86 for(const auto& held:events) {
87 const auto& event = event_ref(held);
88 if(const auto* command=std::get_if<CommandEvent>(&event)) {
89 const auto& e=*command;const auto& o=*e.observation;const auto seq=o.command;
90 if(e.outcome==Outcome::IgnoredTradingWindow || e.outcome==Outcome::IgnoredIntradayLoss
91 || e.outcome==Outcome::RejectedIntradayCap)continue;
92 if(o.kind==CommandKind::Entry) {
93 if(default_gross_scope(o.configuration)) {
94 int count=0;bool noncandidate=false;
95 for(const auto& old:e.before)if(old.bar==o.bar && entry_like(old.type)) {
96 ++count;if(!awaits_default_review(old.draft))noncandidate=true;
97 }
98 if(!original_default_call(o)||noncandidate||count>=2)h.default_causes[o.bar]=seq;
99 }
100 for(const auto& old:e.before)if(removed(e,old.incarnation)) {
101 const bool is_entry=entry_like(old.type);
102 if((original_pair_call(o)&&is_entry)||(fixed_market_call(o)&&ordinary_terminal_call(o)))h.pair_causes[o.bar]=seq;
103 if(original_default_call(o)&&is_entry)h.default_causes[o.bar]=seq;
104 if(is_entry&&old.placement_side==0)h.pair_causes[old.bar]=seq;
105 if(awaits_default_review(old.draft))h.default_causes[old.bar]=seq;
106 }
107 if((e.outcome==Outcome::RejectedAffordability||e.outcome==Outcome::OpeningRejectedReductionAdmitted)
108 && ordinary_terminal_call(o))h.pair_causes[o.bar]=seq;
109 } else if(o.kind==CommandKind::Raw) {
110 if(default_gross_scope(o.configuration))h.default_causes[o.bar]=seq;
111 for(const auto& old:e.before)if(removed(e,old.incarnation)&&entry_like(old.type)) {
112 if(awaits_default_review(old.draft))h.default_causes[old.bar]=seq;
113 if(old.placement_side==0)h.pair_causes[old.bar]=seq;
114 if(o.configuration.process_on_close&&o.configuration.calc_on_fills&&!o.configuration.fill_recalculation)h.pair_causes[o.bar]=seq;
115 }
116 } else {
117 for(const auto& old:e.before) {
118 if(awaits_default_review(old.draft))h.default_causes[old.bar]=seq;
119 if(removed(e,old.incarnation)&&entry_like(old.type)) {
120 if(old.placement_side==0)h.pair_causes[old.bar]=seq;
121 if(o.configuration.process_on_close&&o.configuration.calc_on_fills&&!o.configuration.fill_recalculation)h.pair_causes[o.bar]=seq;
122 }
123 }
124 }
125 } else if(const auto* review=std::get_if<ReviewEvent>(&event)) {
126 if (!apply_reviews) continue;
127 const auto cp=review->receipt.checkpoint;
128 if(cp!=Checkpoint::TerminalGross)for(const auto& order:review->reviewed) {
129 (cp==Checkpoint::DefaultGross?h.default_causes:h.pair_causes).erase(order.bar);
130 }
131 if(cp!=Checkpoint::DefaultGross || review->reviewed.empty()) {
132 auto& causes=cp==Checkpoint::DefaultGross?h.default_causes:h.pair_causes;
133 for(auto it=causes.begin();it!=causes.end();) {
134 if(it->first<review->receipt.bar)it=causes.erase(it);else ++it;
135 }
136 }
137 }
138 }
139 return h;
140}
142 return journal.last_rejected_command_bar();
143}
145} // namespace pineforge::compat::pine
const std::shared_ptr< const CommandObservation > & observation() const
const std::optional< ReviewReceipt > & review() const
Base with a code: throw coded<std::invalid_argument>(code, {args}, text).
std::variant< CommandEvent, ReviewEvent, SizingEvent > Event
bool explicit_qualification(const admission::Draft &draft)
const admission::Event & event_ref(const admission::Event &event)
int last_rejected_command_bar(const admission::Journal &journal)
bool awaits_pair_review(const admission::Draft &draft)
bool original_default_call(const admission::CommandObservation &o)
bool default_gross_scope(const admission::Configuration &c)
bool opening_qualification(const admission::Draft &draft)
History admission_history(const admission::Journal &journal)
bool awaits_default_review(const admission::Draft &draft)
bool original_pair_call(const admission::CommandObservation &o)
static History fold_admission_history(const Range &events, bool apply_reviews=true)
bool explicit_pair_scope(const admission::Configuration &c)