86 lines
2.7 KiB
C++
86 lines
2.7 KiB
C++
#pragma once
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
#include "portfolio.hpp"
|
|
|
|
struct PerfReport {
|
|
double total_return;
|
|
double cagr;
|
|
double sharpe;
|
|
double max_drawdown;
|
|
double volatility;
|
|
double calmar;
|
|
};
|
|
|
|
inline PerfReport compute_performance(
|
|
const std::vector<Portfolio::EquityPoint>& eq,
|
|
double initial_capital)
|
|
{
|
|
int n = static_cast<int>(eq.size());
|
|
if (n < 2) return {};
|
|
|
|
// daily returns
|
|
std::vector<double> rets;
|
|
rets.reserve(n - 1);
|
|
for (int i = 1; i < n; ++i)
|
|
rets.push_back((eq[i].value - eq[i-1].value) / eq[i-1].value);
|
|
|
|
double mean_r = 0.0;
|
|
for (double r : rets) mean_r += r;
|
|
mean_r /= rets.size();
|
|
|
|
double var = 0.0;
|
|
for (double r : rets) var += (r - mean_r) * (r - mean_r);
|
|
var /= rets.size();
|
|
double std_r = std::sqrt(var);
|
|
|
|
double total_return = (eq.back().value / initial_capital) - 1.0;
|
|
|
|
// approximate years from bar count (252 trading days/year)
|
|
double years = static_cast<double>(n) / 252.0;
|
|
double cagr = std::pow(eq.back().value / initial_capital, 1.0 / years) - 1.0;
|
|
|
|
double sharpe = (std_r > 0) ? (mean_r / std_r) * std::sqrt(252.0) : 0.0;
|
|
|
|
// max drawdown
|
|
double peak = eq[0].value;
|
|
double max_dd = 0.0;
|
|
for (const auto& p : eq) {
|
|
peak = std::max(peak, p.value);
|
|
double dd = (p.value - peak) / peak;
|
|
max_dd = std::min(max_dd, dd);
|
|
}
|
|
|
|
double vol = std_r * std::sqrt(252.0);
|
|
double calmar = (max_dd != 0.0) ? cagr / std::abs(max_dd) : 0.0;
|
|
|
|
return { total_return, cagr, sharpe, max_dd, vol, calmar };
|
|
}
|
|
|
|
inline void print_report(const PerfReport& r) {
|
|
auto pct = [](double v) {
|
|
std::ostringstream s;
|
|
s << std::fixed << std::setprecision(2) << v * 100.0 << "%";
|
|
return s.str();
|
|
};
|
|
auto f2 = [](double v) {
|
|
std::ostringstream s;
|
|
s << std::fixed << std::setprecision(2) << v;
|
|
return s.str();
|
|
};
|
|
|
|
std::printf("── Performance ──────────────────────────\n");
|
|
std::printf(" %-22s %s\n", "Total return", pct(r.total_return).c_str());
|
|
std::printf(" %-22s %s\n", "CAGR", pct(r.cagr).c_str());
|
|
std::printf(" %-22s %s\n", "Sharpe ratio", f2(r.sharpe).c_str());
|
|
std::printf(" %-22s %s\n", "Max drawdown", pct(r.max_drawdown).c_str());
|
|
std::printf(" %-22s %s\n", "Volatility ann.", pct(r.volatility).c_str());
|
|
std::printf(" %-22s %s\n", "Calmar ratio", f2(r.calmar).c_str());
|
|
std::printf("─────────────────────────────────────────\n");
|
|
}
|