40 struct Filled {
double units = 0.0;
double price = 0.0; };
41 std::optional<Filled> cash_sized;
42 std::optional<Filled> equity_sized;
46 std::optional<no::RequestHandle> cash_entry_;
47 std::optional<no::RequestHandle> equity_entry_;
49 void on_native_run_begin()
override {
52 equity_entry_.reset();
57 void on_native_bar(
const pineforge::Bar&,
58 const pineforge::NativeDecisionContext&)
override {
64 sized.side = no::Side::Long;
65 sized.basis = no::CashValue{2500.0};
66 sized.time = no::SizeTime::AtAcceptance;
67 sized.price = no::SizePrice::SignalOnTick;
68 cash_entry_ =
submit({sized,
"cash-sized",
"sized-report"}).handle;
69 }
else if (bars_ == 3) {
70 submit({no::Flatten{},
"flat",
"sized-report"});
71 }
else if (bars_ == 4) {
75 sized.side = no::Side::Long;
76 sized.basis = no::EquityFraction{0.5};
77 sized.time = no::SizeTime::AtMatch;
78 sized.price = no::SizePrice::Resolved;
79 equity_entry_ =
submit({sized,
"equity-sized",
"sized-report"}).handle;
83 void on_native_applied(
const no::ExecutionAppliedEvent& event,
84 const pineforge::NativeDecisionContext&)
override {
85 const Filled filled{
event.opened_units,
event.resolved_price};
86 if (cash_entry_ && event.handle() == *cash_entry_) cash_sized = filled;
87 if (equity_entry_ && event.handle() == *equity_entry_) equity_sized = filled;
108 spec.
fee_kind = pineforge::NativeFeeKind::Percent;
112 spec.
report_policy = pineforge::NativeReportPolicy::KernelRecorded;
119 { 99.50, 100.50, 99.00, 100.00, 4.0, 0},
120 {100.00, 102.50, 99.75, 102.00, 4.0, 300000},
121 {102.00, 103.50, 101.50, 103.00, 4.0, 600000},
122 {103.00, 103.75, 100.50, 100.75, 4.0, 900000},
123 {100.75, 102.00, 100.50, 101.75, 4.0, 1200000},
129 pineforge::ReportC c{};
130 explicit Report(
const pineforge::BacktestEngine& engine) { engine.
fill_report(&c); }
132 Report(
const Report&) =
delete;
133 Report& operator=(
const Report&) =
delete;
136bool near(
double value,
double expected) {
return std::fabs(value - expected) < 1e-9; }
141 SizedReportExample host;
142 if (host.configure_native(
make_spec()).status
143 != pineforge::NativeSetupStatus::Applied) {
144 std::cerr <<
"configure: " << host.last_error() <<
'\n';
149 if (host.native_state().kind != pineforge::NativeLifecycleKind::Completed) {
150 std::cerr <<
"run: " << host.last_error() <<
'\n';
155 if (!host.cash_sized || !host.equity_sized) {
156 std::cerr <<
"a Sized entry did not fill\n";
159 std::printf(
"cash-sized: CashValue 2500 at the 100.00 signal -> %.4f units filled at %.2f\n",
160 host.cash_sized->units, host.cash_sized->price);
161 std::printf(
"equity-sized: EquityFraction 0.5 of 10075 at %.2f -> %.4f units\n",
162 host.equity_sized->price, host.equity_sized->units);
163 if (!near(host.cash_sized->units, 25.0) || !near(host.equity_sized->units, 50.0)) {
164 std::cerr <<
"unexpected sized units\n";
170 std::printf(
"equity points: %lld (script bars %lld)\n",
175 std::cerr <<
"expected one equity point per script bar\n";
180 if (!std::isfinite(point.equity) || !std::isfinite(point.open_profit)) {
181 std::cerr <<
"non-finite equity point\n";
184 std::printf(
" t=%lld equity=%.2f open_profit=%.2f\n",
185 static_cast<long long>(point.time_ms), point.equity, point.open_profit);
187 std::printf(
"max drawdown %.2f, max run-up %.2f, open P&L at the end %.2f\n",
193 std::cerr <<
"non-finite equity metrics\n";
200 std::printf(
"report rows: %d (%d closed, %d open at run end, marked at the last close)\n",
201 report.c.
trades_len, host.trade_count(), open_at_end);
202 if (report.c.
trades_len != host.trade_count() + 1 || open_at_end != 1
203 || report.c.
trades_len != host.report_trade_count()) {
204 std::cerr <<
"expected exactly one range-end report row\n";
208 std::cout <<
"closed trades: " << host.trade_count() <<
'\n';
209 for (
int i = 0; i < host.trade_count(); ++i) {
210 const auto& trade = host.get_trade(i);
211 std::cout <<
" " << (trade.is_long ?
"long " :
"short")
212 <<
" qty=" << trade.qty
213 <<
" entry=" << trade.entry_price
214 <<
" exit=" << trade.exit_price
215 <<
" pnl=" << trade.pnl <<
'\n';
217 return host.trade_count() > 0 ? 0 : 1;
static void free_report(ReportC *report)
void fill_report(ReportC *out) const
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)
static void submit(struct host_state *state, uint32_t intent, double value, uint32_t trigger, double price, const char *label)
double max_equity_drawdown
Peak-to-trough equity drop, positive currency magnitude.
double open_pl
Mark-to-market open profit at the final bar.
double max_equity_runup
Trough-to-peak rise where the trough resets on each new equity peak (mirrors the engine's intra-run e...
pf_equity_point_t * equity_curve
int64_t script_bars_processed
One complete setup value, staged/copied by NativeStrategyHost before it is applied at begin.
native_order::RunIdentity identity
NativeReportPolicy report_policy
bool report_open_position_at_end
Report a position still open at run end as a mark-to-market closed row at the last close.