32 constexpr std::int64_t span_seconds = std::int64_t{1} << 40;
33 const auto seconds = time_ms / 1000;
34 return seconds > -span_seconds && seconds < span_seconds;
127static_assert(
sizeof(
MetricsExtentV1) == 32,
"streaming-ratio extent POD");
132 "streaming-ratio final equity offset");
134 "streaming-ratio final equity validity offset");
156 if (!plan.present)
return 0;
157 std::uint64_t fields = 0;
158 if (plan.extent.empty != actual.
empty) fields |= 1ULL << 0;
159 if (plan.extent.count != actual.
count) fields |= 1ULL << 1;
160 if (!plan.extent.empty && !actual.
empty) {
161 if (plan.extent.first_time_ms != actual.
first_time_ms) fields |= 1ULL << 2;
162 if (plan.extent.last_time_ms != actual.
last_time_ms) fields |= 1ULL << 3;
164 if (plan.attempt != config.
attempt) fields |= 1ULL << 4;
166 if (plan.admitted_view != config.
admitted_view) fields |= 1ULL << 6;
167 if (plan.spec_identity != config.
spec_identity) fields |= 1ULL << 7;
186 double sharpe_bar = std::numeric_limits<double>::quiet_NaN();
258 ?
static_cast<double>(
static_cast<std::uint64_t
>(
last.time_ms)
259 -
static_cast<std::uint64_t
>(
first.time_ms))
260 : -
static_cast<double>(
static_cast<std::uint64_t
>(
first.time_ms)
261 -
static_cast<std::uint64_t
>(
last.time_ms));
285namespace fold_detail {
286constexpr double nan = std::numeric_limits<double>::quiet_NaN();
293 if (!std::isfinite(value) && first ==
nullptr) first = field;
306 template<
class BarEmit,
class MonthEmit>
324 template<
class MonthEmit>
332 for (
unsigned i = 0; i < 8; ++i) {
333 hash ^= (word >> (8 * i)) & 255u;
334 hash *= 1099511628211ULL;
339 std::memcpy(&bits, &value,
sizeof(bits));
350 if (s.point_count == 0) {
355 if (observation) ++s.observation_count;
356 if (observation && exposed) ++s.bars_in_market;
358 const auto check = [&s](
double value,
const char* field) {
361 const double eq = check(point.
equity,
"equity_curve.equity");
362 check(point.
open_profit,
"equity_curve.open_profit");
365 const double dd = check(
peak - eq,
"metrics.equity.max_equity_drawdown");
366 if (dd > s.max_drawdown) {
368 s.max_drawdown_pct =
peak > 0.0
369 ? check(dd /
peak * 100.0,
"metrics.equity.max_equity_drawdown_pct") :
nan;
371 const double ru = check(eq -
trough,
"metrics.equity.max_equity_runup");
372 if (ru > s.max_runup) {
374 s.max_runup_pct =
trough > 0.0
375 ? check(ru /
trough * 100.0,
"metrics.equity.max_equity_runup_pct") :
nan;
378 [&](
double r) { s.bar_returns.fold(r); },
379 [&](
double r) { s.month_returns.fold(r); });
383 witness_word(s.point_witness,
static_cast<std::uint64_t
>(key));
385 witness_word(s.point_witness, observation && exposed ? 1u : 0u);
396 bool observation =
true,
bool exposed =
false) {
398 prefix_.fold(held_, held_key_, held_observation_, held_exposed_);
400 held_key_ = month_key;
401 held_observation_ = observation;
402 held_exposed_ = exposed;
407 if (!have_held_)
return false;
409 held_key_ = month_key;
417 auto result = prefix_;
419 result.fold(held_, held_key_, held_observation_, held_exposed_);
420 result.returns.finish_month(
421 [&](
double r) { result.values.month_returns.fold(r); });
422 return result.values;
428 std::int64_t held_key_ = 0;
429 bool held_observation_ =
false, held_exposed_ =
false, have_held_ =
false;
450namespace fold_detail {
459 double check(
double value,
const char* field) {
463 bool allowed,
const char* sharpe_name,
const char* sortino_name) {
484 void finish(
double& sharpe,
double& sortino) {
485 sharpe = sortino =
nan;
499 returns.fold(point, key, [&](
double r) {
bars.fold(r); },
500 [&](
double r) {
months.fold(r); });
512 prefix_.months.prepare(first.
month_returns, 0.02 / 12.0, std::sqrt(12.0),
true,
513 "metrics.equity.sharpe_monthly",
"metrics.equity.sortino_monthly");
516 const double bars_per_year = prefix_.bars.check(
517 static_cast<double>(first.point_count - 1) / span,
"metrics.equity.sharpe_bar");
518 prefix_.bars.prepare(first.bar_returns, 0.02 / bars_per_year,
519 std::sqrt(bars_per_year), true,
520 "metrics.equity.sharpe_bar",
"metrics.equity.sortino_bar");
524 bool observation =
true,
bool exposed =
false) {
525 if (
const auto* held = replay_.held_point())
526 prefix_.fold(*held, replay_.held_month_key());
527 replay_.fold(point, month_key, observation, exposed);
530 return replay_.replace_last(point, month_key);
533 auto final = prefix_;
534 if (
const auto* held = replay_.held_point())
535 final.fold(*held, replay_.held_month_key());
536 final.returns.finish_month([&](
double r) {
final.months.fold(r); });
539 result.
replayed = replay_.first_pass();
541 &&
final.bars.complete() &&
final.months.complete();
548 ?
final.months.nonfinite_field :
final.bars.nonfinite_field;
573 result.longs = longs;
574 result.shorts = shorts;
std::int64_t held_month_key() const
const pf_equity_point_t * held_point() const
EquityFoldSnapshot first_pass() const
void fold(const pf_equity_point_t &point, std::int64_t month_key, bool observation=true, bool exposed=false)
bool replace_last(const pf_equity_point_t &point, std::int64_t month_key)
bool held_exposed() const
bool held_observation() const
bool replace_last(const pf_equity_point_t &point, std::int64_t month_key)
EquityRatioResult finish() const
void fold(const pf_equity_point_t &point, std::int64_t month_key, bool observation=true, bool exposed=false)
EquityMetricsSecondPass(const EquityFoldSnapshot &first)
virtual MetricsStreamingStatusV1 amend_held(std::int64_t logical_index, const pf_equity_point_t &, int month_key)=0
virtual MetricsStreamingStatusV1 finish(MetricsStreamingResultV1 &, MetricsRatioCurveV1={}) const =0
virtual std::size_t state_size_bytes() const =0
virtual MetricsStreamingStatusV1 append(const pf_equity_point_t &, int month_key)=0
virtual ~StreamingEquityRatiosV1()=default
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)
std::uint64_t extent_mismatch_v1(const MetricsExtentPlanV1 &plan, const MetricsStreamingConfigV1 &config, const MetricsExtentV1 &actual)
constexpr MetricsNeedMaskV1 kMetricsNeedAllV1
constexpr MetricsNeedMaskV1 kMetricsNeedTradeStatsV1
MetricsStopBoundaryKindV1
constexpr std::size_t kMetricsMonthEndLimitV1
constexpr MetricsNeedMaskV1 kMetricsNeedEquitySummaryV1
MetricsStreamingStatusV1 enable_streaming_monthly_replay_v1(StreamingEquityRatiosV1 &)
std::int64_t equity_month_key(std::int64_t time_ms, const std::string &chart_timezone)
std::uint64_t MetricsNeedMaskV1
constexpr MetricsNeedMaskV1 kMetricsNeedSharpeBarV1
MetricsStreamingStatusV1 make_streaming_equity_ratios_v1(const MetricsStreamingConfigV1 &, const MetricsExtentPlanV1 &, MetricsRatioTraceSinkV1, std::unique_ptr< StreamingEquityRatiosV1 > &)
MetricsStreamingStatusV1 streaming_monthly_result_v1(const StreamingEquityRatiosV1 &, MetricsMonthlyResultV1 &)
constexpr MetricsNeedMaskV1 kMetricsNeedCurveV1
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)
constexpr MetricsNeedMaskV1 kMetricsNeedSortinoBarV1
void(*)(void *, const MetricsRatioTraceV1 &) MetricsRatioTraceCallbackV1
pf_equity_stats_t finalize_equity_metrics(const EquityFoldSnapshot &first, const EquityMetricInputs &inputs, const EquityRatioResult *ratios=nullptr)
constexpr MetricsNeedMaskV1 kMetricsNeedReturnsV1
bool monthly_replay_time_supported_v1(std::int64_t time_ms)
constexpr MetricsNeedMaskV1 kMetricsNeedMonthlyRatiosV1
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
std::uint64_t spec_identity
std::uint64_t input_generation
std::uint64_t admitted_view
MetricsRouteReasonV1 route_reason
std::uint64_t actual_input_generation
MetricsRatioRouteV1 route
std::uint64_t mismatch_fields
std::uint32_t replay_attempts
std::uint32_t plain_subset
StreamingSharpeV1 streaming_sharpe
std::uint64_t failed_plain_inputs
std::uint64_t eligible_returns
std::uint32_t struct_size
std::uint64_t actual_spec_identity
MetricsExtentWitnessV1 witness
double actual_last_equity
MetricsExtentV1 actual_extent
MetricsStopBoundaryV1 stop_boundary
MetricsNeedMaskV1 metrics_needed
std::uint64_t actual_admitted_view
std::uint32_t actual_last_equity_valid
std::uint64_t actual_attempt
std::uint64_t authoritative_attempt
MetricsExtentPlanV1 extent_plan
std::int64_t last_time_ms
std::int64_t first_time_ms
pf_equity_point_t(* read)(void *, std::int64_t)
MetricsRatioTraceCallbackV1 emit
std::int64_t return_index
const char * series_stage
std::int64_t completed_marks
MetricsStopBoundaryKindV1 kind
StreamingSharpeV1 streaming_sharpe
std::uint64_t spec_identity
std::uint64_t input_generation
MetricsNeedMaskV1 metrics_needed
std::uint32_t curve_available
std::uint64_t admitted_view
std::uint32_t deterministic_replay
const char * failure_reason
MetricsExtentReceiptV1 receipt
const char * failure_code
const char * failure_field
void fold(const pf_equity_point_t &point, std::int64_t key, bool observation, bool exposed)
EquityFoldSnapshot values
void finish_month(MonthEmit month) const
void fold(const pf_equity_point_t &point, std::int64_t key, BarEmit bar, MonthEmit month)
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, std::int64_t key)