47 ?
static_cast<double>(
static_cast<std::uint64_t
>(
last.time_ms)
48 -
static_cast<std::uint64_t
>(
first.time_ms))
49 : -
static_cast<double>(
static_cast<std::uint64_t
>(
first.time_ms)
50 -
static_cast<std::uint64_t
>(
last.time_ms));
75constexpr double nan = std::numeric_limits<double>::quiet_NaN();
81inline double defer_nonfinite(
double value,
const char* field,
const char*& first) {
82 if (!std::isfinite(value) && first ==
nullptr) first = field;
95 template<
class BarEmit,
class MonthEmit>
113 template<
class MonthEmit>
121 for (
unsigned i = 0; i < 8; ++i) {
122 hash ^= (word >> (8 * i)) & 255u;
123 hash *= 1099511628211ULL;
128 std::memcpy(&bits, &value,
sizeof(bits));
139 if (s.point_count == 0) {
144 if (observation) ++s.observation_count;
145 if (observation && exposed) ++s.bars_in_market;
147 const auto check = [&s](
double value,
const char* field) {
150 const double eq = check(point.
equity,
"equity_curve.equity");
151 check(point.
open_profit,
"equity_curve.open_profit");
154 const double dd = check(
peak - eq,
"metrics.equity.max_equity_drawdown");
155 if (dd > s.max_drawdown) {
157 s.max_drawdown_pct =
peak > 0.0
158 ? check(dd /
peak * 100.0,
"metrics.equity.max_equity_drawdown_pct") :
nan;
160 const double ru = check(eq -
trough,
"metrics.equity.max_equity_runup");
161 if (ru > s.max_runup) {
163 s.max_runup_pct =
trough > 0.0
164 ? check(ru /
trough * 100.0,
"metrics.equity.max_equity_runup_pct") :
nan;
167 [&](
double r) { s.bar_returns.fold(r); },
168 [&](
double r) { s.month_returns.fold(r); });
172 witness_word(s.point_witness,
static_cast<std::uint64_t
>(key));
174 witness_word(s.point_witness, observation && exposed ? 1u : 0u);
185 bool observation =
true,
bool exposed =
false) {
187 prefix_.fold(held_, held_key_, held_observation_, held_exposed_);
189 held_key_ = month_key;
190 held_observation_ = observation;
191 held_exposed_ = exposed;
196 if (!have_held_)
return false;
198 held_key_ = month_key;
206 auto result = prefix_;
208 result.fold(held_, held_key_, held_observation_, held_exposed_);
209 result.returns.finish_month(
210 [&](
double r) { result.values.month_returns.fold(r); });
211 return result.values;
218 bool held_observation_ =
false, held_exposed_ =
false, have_held_ =
false;
239namespace fold_detail {
248 double check(
double value,
const char* field) {
252 bool allowed,
const char* sharpe_name,
const char* sortino_name) {
273 void finish(
double& sharpe,
double& sortino) {
274 sharpe = sortino =
nan;
288 returns.fold(point, key, [&](
double r) {
bars.fold(r); },
289 [&](
double r) {
months.fold(r); });
301 prefix_.months.prepare(first.
month_returns, 0.02 / 12.0, std::sqrt(12.0),
true,
302 "metrics.equity.sharpe_monthly",
"metrics.equity.sortino_monthly");
305 const double bars_per_year = prefix_.bars.check(
306 static_cast<double>(first.point_count - 1) / span,
"metrics.equity.sharpe_bar");
307 prefix_.bars.prepare(first.bar_returns, 0.02 / bars_per_year,
308 std::sqrt(bars_per_year), true,
309 "metrics.equity.sharpe_bar",
"metrics.equity.sortino_bar");
313 bool observation =
true,
bool exposed =
false) {
314 if (
const auto* held = replay_.held_point())
315 prefix_.fold(*held, replay_.held_month_key());
316 replay_.fold(point, month_key, observation, exposed);
319 return replay_.replace_last(point, month_key);
322 auto final = prefix_;
323 if (
const auto* held = replay_.held_point())
324 final.fold(*held, replay_.held_month_key());
325 final.returns.finish_month([&](
double r) {
final.months.fold(r); });
328 result.
replayed = replay_.first_pass();
330 &&
final.bars.complete() &&
final.months.complete();
337 ?
final.months.nonfinite_field :
final.bars.nonfinite_field;
362 result.longs = longs;
363 result.shorts = shorts;
int held_month_key() const
bool replace_last(const pf_equity_point_t &point, int month_key)
const pf_equity_point_t * held_point() const
void fold(const pf_equity_point_t &point, int month_key, bool observation=true, bool exposed=false)
EquityFoldSnapshot first_pass() const
bool held_exposed() const
bool held_observation() const
void fold(const pf_equity_point_t &point, int month_key, bool observation=true, bool exposed=false)
EquityRatioResult finish() const
EquityMetricsSecondPass(const EquityFoldSnapshot &first)
bool replace_last(const pf_equity_point_t &point, int month_key)
void witness_word(std::uint64_t &hash, std::uint64_t word)
void witness_double(std::uint64_t &hash, double value)
double defer_nonfinite(double value, const char *field, const char *&first)
int equity_month_key(std::int64_t time_ms, const std::string &chart_timezone)
pf_metrics_t finalize_metrics(const EquityFoldSnapshot &first, const EquityMetricInputs &inputs, const pf_trade_stats_t &all, const pf_trade_stats_t &longs, const pf_trade_stats_t &shorts, const EquityRatioResult *ratios=nullptr)
pf_equity_stats_t finalize_equity_metrics(const EquityFoldSnapshot &first, const EquityMetricInputs &inputs, const EquityRatioResult *ratios=nullptr)
Single per-script-bar equity point.
double open_profit
Mark-to-market open P&L at bar close.
double equity
initial_capital + net_profit + open_profit.
int64_t time_ms
Script-bar OPEN timestamp (Unix ms).
Equity-curve-derived statistics (all-trades only, like TV).
Composite metrics container: trade stats (all / long / short) + equity-curve stats.
Trade-level statistics block — computed once each for all / long / short.
std::int64_t observation_count
std::int64_t bars_in_market
const char * nonfinite_field
double span_years() const
EquityReturnSummary month_returns
bool same_input(const EquityFoldSnapshot &other) const
std::uint64_t point_witness
bool needs_second_pass() const
EquityReturnSummary bar_returns
EquityFoldSnapshot replayed
const char * nonfinite_field
EquityFoldSnapshot expected
bool matches(const EquityFoldSnapshot &first) const
void fold(const pf_equity_point_t &point, int key, bool observation, bool exposed)
EquityFoldSnapshot values
void fold(const pf_equity_point_t &point, int key, BarEmit bar, MonthEmit month)
void finish_month(MonthEmit month) const
double previous_month_end
const char * sortino_field
void finish(double &sharpe, double &sortino)
const char * nonfinite_field
const char * sharpe_field
double check(double value, const char *field)
std::size_t expected_count
void prepare(const EquityReturnSummary &first, double risk_free, double annualization, bool allowed, const char *sharpe_name, const char *sortino_name)
void fold(const pf_equity_point_t &point, int key)