50 "pf_bar_t / pineforge::Bar size mismatch");
51static_assert(offsetof(
pf_bar_t, open) == offsetof(
Bar, open),
52 "pf_bar_t::open offset mismatch");
53static_assert(offsetof(
pf_bar_t, high) == offsetof(
Bar, high),
54 "pf_bar_t::high offset mismatch");
55static_assert(offsetof(
pf_bar_t, low) == offsetof(
Bar, low),
56 "pf_bar_t::low offset mismatch");
57static_assert(offsetof(
pf_bar_t, close) == offsetof(
Bar, close),
58 "pf_bar_t::close offset mismatch");
59static_assert(offsetof(
pf_bar_t, volume) == offsetof(
Bar, volume),
60 "pf_bar_t::volume offset mismatch");
61static_assert(offsetof(
pf_bar_t, timestamp) == offsetof(
Bar, timestamp),
62 "pf_bar_t::timestamp offset mismatch");
65 "pf_report_t / pineforge::ReportC size mismatch");
67 "pf_report_t::total_trades offset mismatch");
69 "pf_report_t::trades offset mismatch");
71 "pf_report_t::net_profit offset mismatch");
73 "pf_report_t::security_diag offset mismatch");
75 "pf_report_t::metrics offset mismatch");
77 "pf_report_t::equity_curve offset mismatch");
78static_assert(offsetof(
pf_report_t, equity_curve_len) == offsetof(
ReportC, equity_curve_len),
79 "pf_report_t::equity_curve_len offset mismatch");
80static_assert(offsetof(
pf_report_t, broker_state_hash) == offsetof(
ReportC, broker_state_hash),
81 "pf_report_t::broker_state_hash offset mismatch");
86template <
typename Host>
88 static_assert(std::is_base_of<NativeStrategyHost, Host>::value,
89 "PINEFORGE_EXPORT_NATIVE_STRATEGY requires a NativeStrategyHost subclass");
96 this->last_error_ = text ? text :
"";
106template <
typename Host>
111template <
typename Host>
113 if (
auto* module =
as_module<Host>(strategy))
module->note_error(text);
118template <
typename Host,
typename Fn>
120 if (!strategy)
return;
123 }
catch (
const std::exception& error) {
146 return bar_magnifier != 0 || magnifier_samples != 4
153 if (!engine || !out)
return;
157 std::memcpy(out, &local,
sizeof(local));
159 BacktestEngine::free_report(&local);
164template <
typename Host>
182template <
typename Host>
189template <
typename Host>
200template <
typename Host>
207template <
typename Host>
209 const char* input_tf,
const char* script_tf,
bool with_timeframes,
211 if (!strategy)
return;
214 std::vector<Bar> copied;
216 if (count > 0 && bars !=
nullptr) {
217 copied.resize(
static_cast<std::size_t
>(count));
218 for (
int i = 0; i < count; ++i) {
219 copied[
static_cast<std::size_t
>(i)] =
copy_bar(bars[i]);
223 if (with_timeframes) {
224 engine->run(
source, count, input_tf ? input_tf :
"",
225 script_tf ? script_tf :
"");
227 engine->run(
source, count);
230 }
catch (
const std::exception& error) {
237template <
typename Host>
242template <
typename Host>
244 const char* input_tf,
const char* script_tf,
245 int bar_magnifier,
int magnifier_samples,
248 if (!strategy)
return;
252 note_error<Host>(strategy,
"native module refuses unsupported magnifier arguments");
255 run_batch<Host>(strategy, bars, count, input_tf, script_tf,
true, out);
261 std::memcpy(&local, report,
sizeof(local));
262 BacktestEngine::free_report(&local);
263 std::memcpy(report, &local,
sizeof(local));
271#define PINEFORGE_EXPORT_NATIVE_STRATEGY(Class) \
273 PF_API pf_strategy_t strategy_create(const char*) { \
274 return ::pineforge::native_module::create<Class>(); \
276 PF_API void strategy_free(pf_strategy_t s) { \
277 ::pineforge::native_module::destroy(s); \
279 PF_API void strategy_set_input(pf_strategy_t s, const char* key, \
280 const char* value) { \
281 ::pineforge::native_module::set_input<Class>(s, key, value); \
283 PF_API void strategy_set_override(pf_strategy_t s, const char* key, \
284 const char* value) { \
285 ::pineforge::native_module::set_override<Class>(s, key, value); \
287 PF_API void strategy_set_magnifier_volume_weighted(pf_strategy_t s, int on) { \
288 ::pineforge::native_module::set_magnifier_volume_weighted<Class>(s, on); \
290 PF_API void run_backtest(pf_strategy_t s, pf_bar_t* bars, int n, \
291 pf_report_t* out) { \
292 ::pineforge::native_module::run_backtest<Class>(s, bars, n, out); \
294 PF_API void run_backtest_full(pf_strategy_t s, pf_bar_t* bars, int n, \
295 const char* input_tf, const char* script_tf, \
296 int bar_magnifier, int magnifier_samples, \
297 pf_magnifier_distribution_t magnifier_dist, \
298 pf_report_t* out) { \
299 ::pineforge::native_module::run_backtest_full<Class>( \
300 s, bars, n, input_tf, script_tf, bar_magnifier, magnifier_samples, \
301 magnifier_dist, out); \
303 PF_API void report_free(pf_report_t* report) { \
304 ::pineforge::native_module::report_free(report); \
307 static_assert(true, "PINEFORGE_EXPORT_NATIVE_STRATEGY expects a trailing ';'")
void set_magnifier_volume_weighted(bool on)
void fill_report(ReportC *out) const
void set_input(const std::string &key, const std::string &value)
The one class the macro instantiates: the host plus the module boundary.
void note_error(const char *text) noexcept
Presentation text for strategy_get_last_error.
pf_magnifier_distribution_t
Bar-magnifier sub-bar sampling distribution.
@ PF_MAGNIFIER_ENDPOINTS
Default — exact O,H,L,C points plus uniform fill between.
void run_backtest_full(pf_strategy_t strategy, pf_bar_t *bars, int count, const char *input_tf, const char *script_tf, int bar_magnifier, int magnifier_samples, pf_magnifier_distribution_t magnifier_distribution, pf_report_t *out)
Bar copy_bar(const pf_bar_t &in)
BacktestEngine * as_engine(pf_strategy_t strategy)
void publish_report(BacktestEngine *engine, pf_report_t *out)
Fills a local report first: the caller's struct is written only once the engine has produced a comple...
void set_override(pf_strategy_t strategy, const char *key, const char *value)
void set_input(pf_strategy_t strategy, const char *key, const char *value)
void note_error(pf_strategy_t strategy, const char *text)
void guarded(pf_strategy_t strategy, Fn &&fn)
Runs fn and converts any escaping exception into presentation text.
void destroy(pf_strategy_t strategy)
void report_free(pf_report_t *report)
void run_backtest(pf_strategy_t strategy, pf_bar_t *bars, int count, pf_report_t *out)
void run_batch(pf_strategy_t strategy, pf_bar_t *bars, int count, const char *input_tf, const char *script_tf, bool with_timeframes, pf_report_t *out)
void set_magnifier_volume_weighted(pf_strategy_t strategy, int on)
Module< Host > * as_module(pf_strategy_t strategy)
bool magnifier_unsupported(int bar_magnifier, int magnifier_samples, pf_magnifier_distribution_t distribution)
A native host owns its own intrabar path through NativeRunSpec::intrabar, so the C magnifier argument...
void * pf_strategy_t
Opaque handle to a compiled strategy instance.
Single OHLCV bar pushed into the engine.
int64_t timestamp
Bar open time, Unix milliseconds.
Backtest report filled by run_backtest / run_backtest_full.