32 std::optional<no::RequestHandle> opening_;
33 std::optional<no::RequestHandle> reversal_;
34 std::optional<no::RequestHandle> selected_;
35 std::optional<std::int64_t> opening_cycle_;
37 bool child_submitted_ =
false;
39 struct AppliedMetrics {
40 int host_sized_applied = 0;
41 int reversal_applied = 0;
42 int selected_applied = 0;
43 double host_sized_opened_units = 0.0;
44 double reversal_opened_units = 0.0;
45 double selected_ticket = 0.0;
50 no::ExecutionTerms resolve_execution_terms(
51 const pineforge::NativeExecutionTermsFacts& facts)
const override {
52 if (std::holds_alternative<no::RemainingDeferred>(facts.
remaining)) {
59 const pineforge::NativePrecommitView&)
const override {
60 return pineforge::NativePrecommitVerdict::Proceed;
63 void on_native_run_begin()
override {
67 opening_cycle_.reset();
69 child_submitted_ =
false;
72 void on_native_bar(
const pineforge::Bar&,
73 const pineforge::NativeDecisionContext&)
override {
76 submit(no::Request{no::Transact{1.0},
"baseline-open",
""});
78 open.intent = no::HostSized{no::HostSizedKind::Open, no::Side::Long};
79 open.label =
"sized-limit";
80 open.trigger = no::Limit{101.0};
81 opening_ =
submit(open).handle;
85 if (!child_submitted_ && opening_ && opening_cycle_) {
87 child.intent = no::Reduce{no::OwnerOpenedUnits{}};
88 child.label =
"bound-stop";
89 child.trigger = no::Stop{99.0};
90 child.owner = no::BindOpening{*opening_, *opening_cycle_};
92 child_submitted_ =
true;
95 if (bars_ == 3 && opening_ && opening_cycle_ && !selected_) {
96 no::Request selected{no::Flatten{},
"selected-flatten",
""};
97 selected.owner = no::BindOpenings{{*opening_}, *opening_cycle_};
98 const auto accepted =
submit(selected);
99 selected_ = accepted.handle;
101 const pineforge::NativeCurrentExecution current{
102 *selected_, pineforge::NativeCurrentPriceRule::NearestTick};
103 const auto preview = inspect_current_execution(current);
106 execute_current(current);
111 if (bars_ == 4 && !reversal_) {
113 reverse.intent = no::ReverseTo{-1.0};
114 reverse.label =
"exact-reverse";
115 reversal_ =
submit(reverse).handle;
119 void on_native_applied(
const no::ExecutionAppliedEvent& event,
120 const pineforge::NativeDecisionContext&)
override {
121 if (opening_ && event.handle() == *opening_) {
122 opening_cycle_ =
event.cycle_after;
126 AppliedMetrics applied_metrics()
const {
127 AppliedMetrics result;
128 for (
const auto& event : native_events(0)) {
129 if (!event.command)
continue;
130 const auto* applied = std::get_if<no::ExecutionAppliedEvent>(&*event.command);
131 if (!applied)
continue;
132 if (opening_ && applied->handle() == *opening_) {
133 ++result.host_sized_applied;
134 result.host_sized_opened_units = applied->opened_units;
136 if (reversal_ && applied->handle() == *reversal_) {
137 ++result.reversal_applied;
138 result.reversal_opened_units = applied->opened_units;
140 if (selected_ && applied->handle() == *selected_) {
141 ++result.selected_applied;
142 result.selected_ticket = applied->current_ticket;
149 int host_sized_applied()
const {
return applied_metrics().host_sized_applied; }
150 int reversal_applied()
const {
return applied_metrics().reversal_applied; }
151 int selected_applied()
const {
return applied_metrics().selected_applied; }
152 double host_sized_opened_units()
const {
return applied_metrics().host_sized_opened_units; }
153 double reversal_opened_units()
const {
return applied_metrics().reversal_opened_units; }
154 double selected_ticket()
const {
return applied_metrics().selected_ticket; }
156 int reversal_terminal_kind()
const {
157 if (!reversal_)
return -1;
159 for (
const auto& event : native_events(0)) {
160 if (!event.command)
continue;
161 const auto* command = &*
event.command;
162 if (
const auto* applied = std::get_if<no::ExecutionAppliedEvent>(command)) {
163 if (applied->handle() == *reversal_)
return 1;
165 if (
const auto* rejected = std::get_if<no::MatchRejectedEvent>(command)) {
166 if (rejected->handle() == *reversal_) {
167 return 100 +
static_cast<int>(rejected->reason);
170 if (
const auto* cancelled = std::get_if<no::CancelledEvent>(command)) {
171 if (cancelled->handle() == *reversal_)
return 200 +
static_cast<int>(cancelled->reason);
173 if (
const auto* no_effect = std::get_if<no::NoEffectEvent>(command)) {
174 if (no_effect->handle() == *reversal_)
return 300;
176 if (
const auto* accepted = std::get_if<no::AcceptedEvent>(command)) {
177 if (accepted->handle() == *reversal_) observed = 400;
186 ?
dynamic_cast<NativeSelectedExample*
>(
192 const auto* example = as_example(strategy);
193 return example ?
static_cast<int>(example->native_state().kind) : -1;
197 const auto* example = as_example(strategy);
198 return example ?
static_cast<int>(example->native_state().failure.code) : -1;
202 const auto* example = as_example(strategy);
203 return example ? example->native_state().failure.discriminator : 0;
213 spec.
tickerid =
"EXCHANGE:SELECTED";
214 spec.
type =
"crypto";
227 spec.
fee_kind = pineforge::NativeFeeKind::CashPerExecution;
229 spec.
close_execution = pineforge::NativeCloseExecution::NextEligiblePoint;
235 {100.0, 102.0, 100.0, 101.0, 1.0, 0},
236 {102.0, 103.0, 101.0, 102.5, 1.0, 300000},
237 {103.0, 104.0, 102.0, 103.5, 1.0, 600000},
238 {104.0, 105.0, 103.0, 104.5, 1.0, 900000},
239 {105.0, 106.0, 104.0, 105.5, 1.0, 1200000},
243bool completed(
const NativeSelectedExample& host,
const char* where) {
244 if (host.native_state().kind == pineforge::NativeLifecycleKind::Completed)
return true;
245 std::cerr << where <<
": lifecycle="
246 <<
static_cast<int>(host.native_state().kind)
247 <<
" code=" <<
static_cast<int>(host.native_state().failure.code)
248 <<
" " << host.last_error() <<
'\n';
266 return lifecycle_kind(strategy);
270 return failure_code(strategy);
274 return failure_discriminator(strategy);
278 const auto* example = as_example(strategy);
279 return example ? example->host_sized_applied() : -1;
283 const auto* example = as_example(strategy);
284 return example ? example->host_sized_opened_units() : 0.0;
288 const auto* example = as_example(strategy);
289 return example ? example->reversal_applied() : -1;
293 const auto* example = as_example(strategy);
294 return example ? example->reversal_opened_units() : 0.0;
298 const auto* example = as_example(strategy);
299 return example ? example->reversal_terminal_kind() : -1;
303 const auto* example = as_example(strategy);
304 return example ? example->selected_applied() : -1;
308 const auto* example = as_example(strategy);
309 return example ? example->selected_ticket() : 0.0;
317 NativeSelectedExample batch;
318 if (batch.configure_native(spec).status != pineforge::NativeSetupStatus::Applied) {
319 std::cerr <<
"configure: " << batch.last_error() <<
'\n';
323 if (!completed(batch,
"run(tf)"))
return 1;
325 NativeSelectedExample stream;
326 if (stream.configure_native(spec).status != pineforge::NativeSetupStatus::Applied) {
327 std::cerr <<
"configure(stream): " << stream.last_error() <<
'\n';
330 if (!stream.stream_begin(
kBars, 1,
"5",
"5")) {
331 std::cerr <<
"stream_begin: " << stream.last_error() <<
'\n';
335 if (!stream.stream_push_bar(
kBars[i])) {
336 std::cerr <<
"stream_push_bar: " << stream.last_error() <<
'\n';
340 if (!stream.stream_end()) {
341 std::cerr <<
"stream_end: " << stream.last_error() <<
'\n';
344 if (!completed(stream,
"stream"))
return 1;
346 std::cout <<
"host-sized applied: " << batch.host_sized_applied()
347 <<
" units=" << batch.host_sized_opened_units()
348 <<
" selected-close applied: " << batch.selected_applied()
349 <<
" closed trades: " << batch.trade_count() <<
'\n';
The public native host: an abstract subclass of BacktestEngine with no PineScript on it.
static const pf_bar_t kBars[]
static pf_native_run_spec_v1 make_spec(void)
NativePrecommitVerdict
The host is consulted before generic opening-margin admission.
BacktestEngine * as_engine(pf_strategy_t strategy)
One-line C ABI export for a hand-written NativeStrategyHost.
#define PINEFORGE_EXPORT_NATIVE_STRATEGY(Class)
Define the loadable-module C ABI for one NativeStrategyHost subclass.
static void submit(struct host_state *state, uint32_t intent, double value, uint32_t trigger, double price, const char *label)
int native_selected_example_host_epoch()
int native_selected_example_lifecycle(pf_strategy_t strategy)
int native_selected_example_host_sized_applied(pf_strategy_t strategy)
int native_selected_example_selected_applied(pf_strategy_t strategy)
int native_selected_example_failure_code(pf_strategy_t strategy)
double native_selected_example_reversal_opened_units(pf_strategy_t strategy)
double native_selected_example_selected_ticket(pf_strategy_t strategy)
int native_selected_example_reversal_applied(pf_strategy_t strategy)
double native_selected_example_host_sized_opened_units(pf_strategy_t strategy)
std::uint32_t native_selected_example_failure_discriminator(pf_strategy_t strategy)
int native_selected_example_reversal_terminal_kind(pf_strategy_t strategy)
void * pf_strategy_t
Opaque handle to a compiled strategy instance.
native_order::Remaining remaining
double default_resolved_price
One complete setup value, staged/copied by NativeStrategyHost before it is applied at begin.
NativeCloseExecution close_execution
NativeOpenDirections allowed_open_directions
std::string chart_timezone
native_order::RunIdentity identity
std::uint32_t slippage_ticks