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