PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
trade_text_source.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <algorithm>
8#include <cstring>
9#include <stdexcept>
10#include <string>
11#include <utility>
12
13namespace pineforge {
14namespace report {
16
17// Adapters borrow a frozen generation. Existing owning accessors supply their
18// temporary values; no storage pointer or borrowed string escapes a call.
20public:
21 TradeRowV1 at(LogicalIndexV1 index) const override {
22 check_index(index);
23 const auto row = numeric_value(index);
24 TradeRowV1 result{};
25 auto& value = result.value;
26 value.entry_time = row.entry_time;
27 value.exit_time = row.exit_time;
28 value.entry_price = row.entry_price;
29 value.exit_price = row.exit_price;
30 value.pnl = row.pnl;
31 value.pnl_pct = row.pnl_pct;
32 value.is_long = row.is_long ? 1 : 0;
33 value.max_runup = row.max_runup;
34 value.max_drawdown = row.max_drawdown;
35 value.qty = row.qty;
36 value.commission = row.commission;
37 value.entry_bar_index = row.entry_bar_index;
38 value.exit_bar_index = row.exit_bar_index;
39 value.open_at_end = row.open_at_end ? 1 : 0;
40 result.entry_incarnation = row.entry_incarnation;
41 result.close_cause = BacktestEngine::classify_report_close_cause(row);
42 result.exit_from_bracket = row.exit_from_bracket ? 1 : 0;
43 return result;
44 }
45
46 std::size_t read_page(LogicalIndexV1 from, TradeRowV1* out,
47 std::size_t capacity) const override {
48 check_range(from, 0);
49 if (capacity && !out) throw std::invalid_argument("trade page buffer");
50 const auto count = static_cast<std::size_t>(
51 std::min<LogicalIndexV1>(capacity, size() - from));
52 for (std::size_t i = 0; i < count; ++i) out[i] = at(from + i);
53 return count;
54 }
55
57 void* context, TradeVisitorV1 visitor) const override {
58 return visit_values(from, count, context, visitor, false);
59 }
60
62 void* context, TradeVisitorV1 visitor) const override {
63 return visit_values(from, count, context, visitor, true);
64 }
65
66 std::uint64_t text_size(LogicalIndexV1 index, TradeTextV1 field) const override {
67 check_text(index, field);
68 return text_value(index, field).size();
69 }
70
71 std::size_t read_text(LogicalIndexV1 index, TradeTextV1 field,
72 std::uint64_t byte_offset, char* out,
73 std::size_t capacity) const override {
74 check_text(index, field);
75 const auto text = text_value(index, field);
76 if (byte_offset > text.size()) throw std::out_of_range("trade text offset");
77 if (capacity && !out) throw std::invalid_argument("trade text buffer");
78 const auto offset = static_cast<std::size_t>(byte_offset);
79 const auto count = std::min(capacity, text.size() - offset);
80 if (count) std::memcpy(out, text.data() + offset, count);
81 return count;
82 }
83
84protected:
85 virtual Trade numeric_value(LogicalIndexV1 index) const = 0;
86 virtual std::string text_value(LogicalIndexV1 index, TradeTextV1 field) const = 0;
87
88private:
89 void check_index(LogicalIndexV1 index) const {
90 if (index >= size()) throw std::out_of_range("trade source index");
91 }
92 void check_range(LogicalIndexV1 from, LogicalIndexV1 count) const {
93 const auto length = size();
94 if (from > length || count > length - from)
95 throw std::out_of_range("trade source range");
96 }
97 void check_text(LogicalIndexV1 index, TradeTextV1 field) const {
98 check_index(index);
99 if (static_cast<std::uint32_t>(field) > 3)
100 throw std::invalid_argument("trade text selector");
101 }
102 VisitResultV1 visit_values(LogicalIndexV1 from, LogicalIndexV1 count,
103 void* context, TradeVisitorV1 visitor, bool reverse) const {
104 check_range(from, count);
105 if (!visitor) throw std::invalid_argument("trade source visitor");
106 VisitResultV1 result{};
107 while (result.visited < count) {
108 const auto index = reverse ? from + (count - result.visited - 1)
109 : from + result.visited;
110 const auto row = at(index);
111 ++result.visited;
112 if (!visitor(context, index, row)) return result;
113 }
114 result.complete = true;
115 return result;
116 }
117};
118
119} // namespace trade_source_detail
120
121// No inheritance or new data member is added to the installed ledger object.
122// Reads below a physically retired boundary retain the ledger's own refusal.
124public:
125 explicit LedgerTradeSourceV1(const TradeLedger& ledger) : ledger_(ledger) {}
126 LogicalIndexV1 size() const override { return ledger_.size(); }
127
128private:
129 Trade numeric_value(LogicalIndexV1 index) const override {
130 return ledger_.numeric_at(static_cast<std::size_t>(index));
131 }
132 std::string text_value(LogicalIndexV1 index, TradeTextV1 field) const override {
133 const auto row = static_cast<std::size_t>(index);
134 switch (field) {
135 case TradeTextV1::EntryId: return ledger_.entry_id_at(row);
136 case TradeTextV1::EntryComment: return ledger_.entry_comment_at(row);
137 case TradeTextV1::ExitId: return ledger_.exit_id_at(row);
138 case TradeTextV1::ExitComment: return ledger_.exit_comment_at(row);
139 }
140 throw std::invalid_argument("trade text selector");
141 }
142 const TradeLedger& ledger_;
143};
144
145// Finished report order includes the report-only range-end suffix. The engine
146// must outlive this adapter and remain unmodified for its entire use.
148public:
149 explicit EngineReportTradeSourceV1(const BacktestEngine& engine) : engine_(engine) {}
150 LogicalIndexV1 size() const override { return engine_.report_trade_count(); }
151
152private:
153 Trade numeric_value(LogicalIndexV1 index) const override {
154 return engine_.get_report_trade(static_cast<int>(index));
155 }
156 std::string text_value(LogicalIndexV1 index, TradeTextV1 field) const override {
157 auto row = engine_.get_report_trade(static_cast<int>(index));
158 switch (field) {
159 case TradeTextV1::EntryId: return std::move(row.entry_id);
160 case TradeTextV1::EntryComment: return std::move(row.entry_comment);
161 case TradeTextV1::ExitId: return std::move(row.exit_id);
162 case TradeTextV1::ExitComment: return std::move(row.exit_comment);
163 }
164 throw std::invalid_argument("trade text selector");
165 }
166 const BacktestEngine& engine_;
167};
168
169} // namespace report
170} // namespace pineforge
std::string entry_id_at(size_type global) const
std::string exit_id_at(size_type global) const
Trade numeric_at(size_type global) const
std::string entry_comment_at(size_type global) const
std::string exit_comment_at(size_type global) const
EngineReportTradeSourceV1(const BacktestEngine &engine)
LogicalIndexV1 size() const override
LedgerTradeSourceV1(const TradeLedger &ledger)
virtual LogicalIndexV1 size() const =0
TradeRowV1 at(LogicalIndexV1 index) const override
VisitResultV1 visit(LogicalIndexV1 from, LogicalIndexV1 count, void *context, TradeVisitorV1 visitor) const override
virtual Trade numeric_value(LogicalIndexV1 index) const =0
virtual std::string text_value(LogicalIndexV1 index, TradeTextV1 field) const =0
std::uint64_t text_size(LogicalIndexV1 index, TradeTextV1 field) const override
std::size_t read_text(LogicalIndexV1 index, TradeTextV1 field, std::uint64_t byte_offset, char *out, std::size_t capacity) const override
std::size_t read_page(LogicalIndexV1 from, TradeRowV1 *out, std::size_t capacity) const override
VisitResultV1 visit_backward(LogicalIndexV1 from, LogicalIndexV1 count, void *context, TradeVisitorV1 visitor) const override
std::uint64_t LogicalIndexV1
bool(*)(void *, LogicalIndexV1, TradeRowV1) TradeVisitorV1