Ankit3445/options_pricing
0
1#include "PortfolioManager.hpp"2 3#include <iostream>4#include <iomanip>5#include <sstream>6#include <fstream>7#include <cstdlib>8#include <ctime>9#include <cmath>10#include <algorithm>11#include <numeric>12#include <nlohmann/json.hpp>13 14PortfolioManager::PortfolioManager() {15 load();16}17 18void PortfolioManager::clear_screen() {19#if defined(_WIN32)20 std::system("cls");21#else22 std::system("clear");23#endif24}25 26double PortfolioManager::compute_hv() const {27 if (log_returns_.size() < 5) return 0.0;28 29 double sum = 0.0;30 for (double r : log_returns_) sum += r;31 double mean = sum / static_cast<double>(log_returns_.size());32 33 double sq_sum = 0.0;34 for (double r : log_returns_) {35 double dev = r - mean;36 sq_sum += dev * dev;37 }38 39 double variance = sq_sum / static_cast<double>(log_returns_.size() - 1);40 double daily_vol = std::sqrt(variance);41 return daily_vol * std::sqrt(252.0);42}43 44TickerSummary PortfolioManager::compute_summary(const std::string& ticker) const {45 TickerSummary s;46 int count = 0;47 for (const auto& p : positions_) {48 if (p.ticker != ticker) continue;49 ++count;50 s.mcDelta += p.mc.delta;51 s.mcGamma += p.mc.gamma;52 s.mcVega += p.mc.vega;53 s.mcTheta += p.mc.theta;54 s.mcRho += p.mc.rho;55 s.mcValue += p.mc.price;56 s.mcAvgIv += p.mc.impliedVol;57 s.bsDelta += p.bs.delta;58 s.bsGamma += p.bs.gamma;59 s.bsVega += p.bs.vega;60 s.bsTheta += p.bs.theta;61 s.bsRho += p.bs.rho;62 s.bsValue += p.bs.price;63 s.bsAvgIv += p.bs.impliedVol;64 s.totalCost += p.entryMarketPrice;65 }66 s.count = count;67 if (count > 0) {68 s.mcAvgIv /= static_cast<double>(count);69 s.bsAvgIv /= static_cast<double>(count);70 }71 return s;72}73 74void PortfolioManager::update(const Greeks& mc_greeks, const Greeks& bs_greeks, const MarketTick& tick) {75 if (!active_.load()) return;76 77 {78 std::lock_guard<std::mutex> lock(mutex_);79 80 Position pos;81 pos.ticker = tick.ticker;82 pos.spotPrice = tick.spotPrice;83 pos.strikePrice = tick.strikePrice;84 pos.timeToExpiry = tick.timeToExpiry;85 pos.riskFreeRate = tick.riskFreeRate;86 pos.optionType = tick.optionType;87 pos.entryMarketPrice = tick.optionMarketPrice;88 pos.mc = mc_greeks;89 pos.bs = bs_greeks;90 91 positions_.push_back(std::move(pos));92 93 lastSpot_ = tick.spotPrice;94 lastTicker_ = tick.ticker;95 96 running_mc_value_ += tick.optionMarketPrice;97 running_total_cost_ += tick.optionMarketPrice;98 99 // Track P&L history every 10 positions100 if (positions_.size() % 10 == 0) {101 double mc_sum = 0.0, cost_sum = 0.0;102 for (const auto& pos : positions_) {103 mc_sum += pos.mc.price;104 cost_sum += pos.entryMarketPrice;105 }106 double pl = mc_sum - cost_sum;107 pl_history_.push_back(pl);108 if (pl_history_.size() > 100) pl_history_.pop_front();109 }110 111 history_.push_back({tick.spotPrice});112 if (history_.size() > max_history_) {113 history_.pop_front();114 }115 116 if (history_.size() >= 2) {117 double prev = history_[history_.size() - 2].spot;118 double curr = history_.back().spot;119 if (prev > 0.0) {120 double log_ret = std::log(curr / prev);121 log_returns_.push_back(log_ret);122 if (log_returns_.size() > max_history_) {123 log_returns_.pop_front();124 }125 }126 }127 }128 129 if (positions_.size() % 30 == 0) {130 save();131 }132}133 134void PortfolioManager::reset() {135 {136 std::lock_guard<std::mutex> lock(mutex_);137 positions_.clear();138 history_.clear();139 log_returns_.clear();140 pl_history_.clear();141 lastSpot_ = 0.0;142 lastTicker_.clear();143 }144 save();145 std::cout << "[PortfolioManager] Reset complete" << std::endl;146}147 148void PortfolioManager::print_ticker_section(const std::string& ticker, const TickerSummary& s) const {149 std::ostringstream oss;150 oss << "| " << std::left << std::setw(7) << ticker151 << "| " << std::right << std::setw(4) << s.count152 << " | " << std::setw(9) << std::fixed << std::setprecision(2) << s.mcDelta153 << " | " << std::setw(9) << std::fixed << std::setprecision(4) << s.mcGamma154 << " | " << std::setw(9) << std::fixed << std::setprecision(1) << s.mcVega155 << " | " << std::setw(9) << std::fixed << std::setprecision(1) << s.mcTheta156 << " | " << std::setw(6) << std::fixed << std::setprecision(2) << s.mcAvgIv * 100.0 << "%"157 << " |\n";158 std::cout << oss.str();159}160 161void PortfolioManager::print_dashboard() const {162 std::lock_guard<std::mutex> lock(mutex_);163 164 clear_screen();165 166 double hv = compute_hv();167 168 double totalMCValue = 0.0, totalBSValue = 0.0, totalCost = 0.0;169 double mcDelta = 0.0, mcGamma = 0.0, mcVega = 0.0, mcTheta = 0.0, mcRho = 0.0, mcAvgIv = 0.0;170 double bsDelta = 0.0, bsGamma = 0.0, bsVega = 0.0, bsTheta = 0.0, bsRho = 0.0, bsAvgIv = 0.0;171 int totalCount = static_cast<int>(positions_.size());172 173 std::unordered_map<std::string, TickerSummary> summaries;174 175 for (const auto& p : positions_) {176 mcDelta += p.mc.delta; mcGamma += p.mc.gamma; mcVega += p.mc.vega;177 mcTheta += p.mc.theta; mcRho += p.mc.rho; mcAvgIv += p.mc.impliedVol;178 bsDelta += p.bs.delta; bsGamma += p.bs.gamma; bsVega += p.bs.vega;179 bsTheta += p.bs.theta; bsRho += p.bs.rho; bsAvgIv += p.bs.impliedVol;180 totalCost += p.entryMarketPrice;181 totalMCValue += p.mc.price;182 totalBSValue += p.bs.price;183 184 auto& s = summaries[p.ticker];185 s.count += 1;186 s.mcDelta += p.mc.delta; s.mcGamma += p.mc.gamma; s.mcVega += p.mc.vega;187 s.mcTheta += p.mc.theta; s.mcRho += p.mc.rho; s.mcAvgIv += p.mc.impliedVol;188 s.mcValue += p.mc.price;189 s.bsDelta += p.bs.delta; s.bsGamma += p.bs.gamma; s.bsVega += p.bs.vega;190 s.bsTheta += p.bs.theta; s.bsRho += p.bs.rho; s.bsAvgIv += p.bs.impliedVol;191 s.bsValue += p.bs.price;192 s.totalCost += p.entryMarketPrice;193 }194 195 if (totalCount > 0) {196 mcAvgIv /= totalCount;197 bsAvgIv /= totalCount;198 }199 for (auto& [_, s] : summaries) {200 if (s.count > 0) {201 s.mcAvgIv /= s.count;202 s.bsAvgIv /= s.count;203 }204 }205 206 double unrealizedPl = totalMCValue - totalCost;207 208 std::cout << "+======================================================================+\n"209 << "| REAL-TIME PORTFOLIO RISK DASHBOARD |\n"210 << "+======================================================================+\n"211 << "| Total Positions: " << std::setw(4) << totalCount212 << " | Last Spot: $" << std::right << std::setw(8) << std::fixed << std::setprecision(2) << lastSpot_213 << " | HV: " << std::setw(6) << hv * 100.0 << "% |\n"214 << "+------------------------------------------+-----------------------------+------------------+\n";215 216 auto print_row = [&](const std::string& name, double mc, double bs, const std::string& unit) {217 std::cout << "| " << std::left << std::setw(40) << name218 << "| MC:" << std::right << std::setw(10) << std::fixed << std::setprecision(4) << mc219 << " BS:" << std::setw(10) << std::fixed << std::setprecision(4) << bs220 << " | " << std::left << std::setw(16) << unit << "|\n";221 };222 223 std::cout << "| " << std::left << std::setw(40) << "Greek"224 << "| MC BS | Status |\n"225 << "+------------------------------------------+-----------------------------+------------------+\n";226 print_row("Delta", mcDelta, bsDelta, mcDelta > 0.5 ? "LONG" : mcDelta < -0.5 ? "SHORT" : "NEUTRAL");227 print_row("Gamma", mcGamma, bsGamma, mcGamma > 0.01 ? "CONVEX" : mcGamma < -0.01 ? "CONCAVE" : "FLAT");228 print_row("Vega", mcVega, bsVega, mcVega > 1.0 ? "LONG VOL" : mcVega < -1.0 ? "SHORT VOL" : "FLAT");229 print_row("Theta", mcTheta, bsTheta, mcTheta < -1.0 ? "TIME DECAY" : mcTheta > 1.0 ? "TIME GAIN" : "FLAT");230 print_row("Rho", mcRho, bsRho, mcRho > 1.0 ? "LONG RATE" : mcRho < -1.0 ? "SHORT RATE" : "FLAT");231 232 std::cout << "+------------------------------------------+-----------------------------+------------------+\n"233 << "| MC Implied Vol (avg) | " << std::right << std::setw(26) << std::fixed << std::setprecision(2)234 << mcAvgIv * 100.0 << "%"235 << " | |\n"236 << "| BS Implied Vol (avg) | " << std::right << std::setw(26) << std::fixed << std::setprecision(2)237 << bsAvgIv * 100.0 << "%"238 << " | |\n"239 << "| Historical Volatility (annual) | " << std::right << std::setw(26) << std::fixed << std::setprecision(2)240 << hv * 100.0 << "%"241 << " | |\n"242 << "+------------------------------------------+-----------------------------+------------------+\n"243 << "| P&L Summary |\n"244 << "+------------------------------------------+-----------------------------+------------------+\n"245 << "| Total Cost Basis (entry) | " << std::right << std::setw(24) << std::fixed << std::setprecision(2)246 << totalCost247 << " | |\n"248 << "| Portfolio Value (MC) | " << std::right << std::setw(24) << std::fixed << std::setprecision(2)249 << totalMCValue250 << " | |\n"251 << "| Portfolio Value (BS) | " << std::right << std::setw(24) << std::fixed << std::setprecision(2)252 << totalBSValue253 << " | |\n"254 << "| Unrealized P&L (MC) | " << std::right << std::setw(24) << std::fixed << std::setprecision(2)255 << unrealizedPl256 << " | " << (unrealizedPl >= 0.0 ? "PROFIT " : "LOSS ") << "|\n"257 << "+==========================================+=============================+==================+\n"258 << std::endl;259 260 if (!summaries.empty()) {261 std::cout << " GREEKS SURFACE (MC) - Position Summary by Ticker\n"262 << " " << std::string(76, '=') << "\n"263 << " " << std::left << std::setw(7) << "Ticker"264 << "| " << std::right << std::setw(4) << "#Pos"265 << " | " << std::setw(9) << "Delta"266 << " | " << std::setw(9) << "Gamma"267 << " | " << std::setw(9) << "Vega"268 << " | " << std::setw(9) << "Theta"269 << " | " << std::setw(6) << "IV"270 << " |\n"271 << " " << std::string(76, '-') << "\n";272 273 for (const auto& [ticker, s] : summaries) {274 std::cout << " ";275 print_ticker_section(ticker, s);276 }277 278 std::cout << " " << std::string(76, '-') << "\n"279 << " " << std::left << std::setw(7) << "TOTAL"280 << "| " << std::right << std::setw(4) << totalCount281 << " | " << std::setw(9) << std::fixed << std::setprecision(2) << mcDelta282 << " | " << std::setw(9) << std::fixed << std::setprecision(4) << mcGamma283 << " | " << std::setw(9) << std::fixed << std::setprecision(1) << mcVega284 << " | " << std::setw(9) << std::fixed << std::setprecision(1) << mcTheta285 << " | " << std::setw(6) << std::fixed << std::setprecision(2) << mcAvgIv * 100.0 << "%"286 << " |\n"287 << " " << std::string(76, '=') << "\n"288 << std::endl;289 }290 291 if (!history_.empty()) {292 std::cout << " Price History (last " << std::setw(2) << history_.size() << " ticks, spot):\n ";293 size_t count = 0;294 for (const auto& rec : history_) {295 std::cout << std::fixed << std::setprecision(2) << std::setw(7) << rec.spot;296 if (++count % 10 == 0 && count < history_.size()) {297 std::cout << "\n ";298 } else if (count < history_.size()) {299 std::cout << " ";300 }301 }302 std::cout << "\n" << std::endl;303 }304 305 // Analytics section306 if (!pl_history_.empty()) {307 std::vector<double> pl_vals(pl_history_.begin(), pl_history_.end());308 double sum = std::accumulate(pl_vals.begin(), pl_vals.end(), 0.0);309 double mean = sum / pl_vals.size();310 double sq_sum = 0.0;311 for (double v : pl_vals) sq_sum += (v - mean) * (v - mean);312 double stddev = std::sqrt(sq_sum / pl_vals.size());313 314 std::sort(pl_vals.begin(), pl_vals.end());315 double var95 = pl_vals[static_cast<size_t>(pl_vals.size() * 0.05)];316 double var99 = pl_vals[static_cast<size_t>(pl_vals.size() * 0.01)];317 318 double sharpe = stddev > 0.0 ? (mean - 0.04) / stddev * std::sqrt(252.0) : 0.0;319 320 std::cout << "+======================================================================+\n"321 << "| PORTFOLIO ANALYTICS |\n"322 << "+======================================================================+\n"323 << "| Sharpe Ratio (annual) | " << std::right << std::setw(12) << std::fixed << std::setprecision(2) << sharpe324 << " |\n"325 << "| VaR (95%%) | " << std::right << std::setw(12) << std::fixed << std::setprecision(2) << var95326 << " |\n"327 << "| VaR (99%%) | " << std::right << std::setw(12) << std::fixed << std::setprecision(2) << var99328 << " |\n"329 << "+======================================================================+\n" << std::endl;330 }331 332 std::cout << "+======================================================================+\n" << std::endl;333}334 335nlohmann::json PortfolioManager::to_json() const {336 std::lock_guard<std::mutex> lock(mutex_);337 338 double mcDelta = 0.0, mcGamma = 0.0, mcVega = 0.0, mcTheta = 0.0, mcRho = 0.0;339 double bsDelta = 0.0, bsGamma = 0.0, bsVega = 0.0, bsTheta = 0.0, bsRho = 0.0;340 double totalCost = 0.0, mcValue = 0.0, bsValue = 0.0, mcAvgIv = 0.0, bsAvgIv = 0.0;341 int totalCount = static_cast<int>(positions_.size());342 343 std::unordered_map<std::string, nlohmann::json> tickerData;344 std::unordered_map<std::string, int> tickerPosCount;345 std::unordered_map<std::string, std::vector<nlohmann::json>> tickerPositions;346 static constexpr int MAX_POS_PER_TICKER = 20;347 348 for (const auto& p : positions_) {349 mcDelta += p.mc.delta; mcGamma += p.mc.gamma; mcVega += p.mc.vega;350 mcTheta += p.mc.theta; mcRho += p.mc.rho; mcAvgIv += p.mc.impliedVol;351 bsDelta += p.bs.delta; bsGamma += p.bs.gamma; bsVega += p.bs.vega;352 bsTheta += p.bs.theta; bsRho += p.bs.rho; bsAvgIv += p.bs.impliedVol;353 totalCost += p.entryMarketPrice;354 mcValue += p.mc.price;355 bsValue += p.bs.price;356 357 nlohmann::json td = nlohmann::json::object();358 auto it = tickerData.find(p.ticker);359 if (it != tickerData.end()) td = it->second;360 td["count"] = td.value("count", 0) + 1;361 td["mc_delta"] = td.value("mc_delta", 0.0) + p.mc.delta;362 td["mc_gamma"] = td.value("mc_gamma", 0.0) + p.mc.gamma;363 td["mc_vega"] = td.value("mc_vega", 0.0) + p.mc.vega;364 td["mc_theta"] = td.value("mc_theta", 0.0) + p.mc.theta;365 td["mc_rho"] = td.value("mc_rho", 0.0) + p.mc.rho;366 td["mc_value"] = td.value("mc_value", 0.0) + p.mc.price;367 td["mc_iv_sum"] = td.value("mc_iv_sum", 0.0) + p.mc.impliedVol;368 td["bs_delta"] = td.value("bs_delta", 0.0) + p.bs.delta;369 td["bs_gamma"] = td.value("bs_gamma", 0.0) + p.bs.gamma;370 td["bs_vega"] = td.value("bs_vega", 0.0) + p.bs.vega;371 td["bs_theta"] = td.value("bs_theta", 0.0) + p.bs.theta;372 td["bs_rho"] = td.value("bs_rho", 0.0) + p.bs.rho;373 td["bs_value"] = td.value("bs_value", 0.0) + p.bs.price;374 td["bs_iv_sum"] = td.value("bs_iv_sum", 0.0) + p.bs.impliedVol;375 td["cost"] = td.value("cost", 0.0) + p.entryMarketPrice;376 tickerData[p.ticker] = td;377 378 int& pcnt = tickerPosCount[p.ticker];379 if (pcnt < MAX_POS_PER_TICKER) {380 tickerPositions[p.ticker].push_back({381 {"spot", p.spotPrice},382 {"strike", p.strikePrice},383 {"expiry", p.timeToExpiry},384 {"type", p.optionType == OptionType::CALL ? "CALL" : "PUT"},385 {"mc_price", p.mc.price}, {"mc_delta", p.mc.delta},386 {"mc_gamma", p.mc.gamma}, {"mc_vega", p.mc.vega},387 {"mc_theta", p.mc.theta}, {"mc_iv", p.mc.impliedVol},388 {"bs_price", p.bs.price}, {"bs_delta", p.bs.delta},389 {"bs_gamma", p.bs.gamma}, {"bs_vega", p.bs.vega},390 {"bs_theta", p.bs.theta}, {"bs_iv", p.bs.impliedVol},391 {"entry_price", p.entryMarketPrice}392 });393 }394 ++pcnt;395 }396 397 if (totalCount > 0) {398 mcAvgIv /= static_cast<double>(totalCount);399 bsAvgIv /= static_cast<double>(totalCount);400 }401 double hv = compute_hv();402 double unrealizedPl = mcValue - totalCost;403 404 nlohmann::json tickers;405 for (auto& [sym, td] : tickerData) {406 int cnt = td["count"].get<int>();407 tickers[sym] = {408 {"count", cnt},409 {"mc_delta", td["mc_delta"]}, {"mc_gamma", td["mc_gamma"]},410 {"mc_vega", td["mc_vega"]}, {"mc_theta", td["mc_theta"]},411 {"mc_rho", td["mc_rho"]}, {"mc_value", td["mc_value"]},412 {"mc_iv", cnt > 0 ? td["mc_iv_sum"].get<double>() / cnt : 0.0},413 {"bs_delta", td["bs_delta"]}, {"bs_gamma", td["bs_gamma"]},414 {"bs_vega", td["bs_vega"]}, {"bs_theta", td["bs_theta"]},415 {"bs_rho", td["bs_rho"]}, {"bs_value", td["bs_value"]},416 {"bs_iv", cnt > 0 ? td["bs_iv_sum"].get<double>() / cnt : 0.0},417 {"cost", td["cost"]}418 };419 }420 421 nlohmann::json priceHistory = nlohmann::json::array();422 for (const auto& rec : history_) {423 priceHistory.push_back(rec.spot);424 }425 426 nlohmann::json positions_json = nlohmann::json::array();427 int start = std::max(0, totalCount - 10);428 for (int i = start; i < totalCount; ++i) {429 const auto& p = positions_[i];430 positions_json.push_back({431 {"ticker", p.ticker},432 {"spot", p.spotPrice},433 {"strike", p.strikePrice},434 {"expiry", p.timeToExpiry},435 {"type", p.optionType == OptionType::CALL ? "CALL" : "PUT"},436 {"mc_price", p.mc.price}, {"mc_delta", p.mc.delta},437 {"mc_gamma", p.mc.gamma}, {"mc_vega", p.mc.vega},438 {"mc_theta", p.mc.theta}, {"mc_iv", p.mc.impliedVol},439 {"bs_price", p.bs.price}, {"bs_delta", p.bs.delta},440 {"bs_gamma", p.bs.gamma}, {"bs_vega", p.bs.vega},441 {"bs_theta", p.bs.theta}, {"bs_iv", p.bs.impliedVol},442 {"entry_price", p.entryMarketPrice}443 });444 }445 446 // ── Analytics ──447 double sharpe = 0.0, var95 = 0.0, var99 = 0.0, max_dd = 0.0;448 double stress_crash = 0.0, stress_vol = 0.0, stress_rate = 0.0;449 450 if (pl_history_.size() >= 5) {451 std::vector<double> pl_vals(pl_history_.begin(), pl_history_.end());452 double sum = std::accumulate(pl_vals.begin(), pl_vals.end(), 0.0);453 double mean = sum / pl_vals.size();454 double sq_sum = 0.0;455 for (double v : pl_vals) sq_sum += (v - mean) * (v - mean);456 double stddev = std::sqrt(sq_sum / pl_vals.size());457 458 std::sort(pl_vals.begin(), pl_vals.end());459 var95 = pl_vals[static_cast<size_t>(pl_vals.size() * 0.05)];460 var99 = pl_vals[static_cast<size_t>(pl_vals.size() * 0.01)];461 462 sharpe = stddev > 0.0 ? (mean - 0.04) / stddev * std::sqrt(252.0) : 0.0;463 464 // Max drawdown465 double peak = -1e18;466 for (double v : pl_vals) {467 if (v > peak) peak = v;468 double dd = (peak - v) / std::max(std::abs(peak), 1.0);469 if (dd > max_dd) max_dd = dd;470 }471 472 // Stress scenarios473 double crash_pnl = 0.0, vol_pnl = 0.0, rate_pnl = 0.0;474 for (const auto& p : positions_) {475 double crash_spot = p.spotPrice * 0.9;476 double intrinsic = p.optionType == OptionType::CALL477 ? std::max(crash_spot - p.strikePrice, 0.0)478 : std::max(p.strikePrice - crash_spot, 0.0);479 crash_pnl += intrinsic - p.mc.price;480 vol_pnl += p.mc.vega * 0.2;481 rate_pnl += p.mc.rho * 0.01;482 }483 stress_crash = crash_pnl;484 stress_vol = vol_pnl;485 stress_rate = rate_pnl;486 }487 488 nlohmann::json analytics = {489 {"sharpe", sharpe},490 {"var95", var95},491 {"var99", var99},492 {"max_drawdown", max_dd},493 {"stress_crash", stress_crash},494 {"stress_vol", stress_vol},495 {"stress_rate", stress_rate},496 {"pl_history", std::vector<double>(pl_history_.begin(), pl_history_.end())}497 };498 499 return {500 {"positions_total", totalCount},501 {"last_spot", lastSpot_},502 {"last_ticker", lastTicker_},503 {"mc_delta", mcDelta}, {"mc_gamma", mcGamma}, {"mc_vega", mcVega},504 {"mc_theta", mcTheta}, {"mc_rho", mcRho},505 {"bs_delta", bsDelta}, {"bs_gamma", bsGamma}, {"bs_vega", bsVega},506 {"bs_theta", bsTheta}, {"bs_rho", bsRho},507 {"total_cost", totalCost},508 {"mc_value", mcValue},509 {"bs_value", bsValue},510 {"unrealized_pl", unrealizedPl},511 {"mc_avg_iv", mcAvgIv},512 {"bs_avg_iv", bsAvgIv},513 {"hv", hv},514 {"tickers", tickers},515 {"ticker_positions", tickerPositions},516 {"price_history", priceHistory},517 {"recent_positions", positions_json},518 {"analytics", analytics}519 };520}521 522nlohmann::json PortfolioManager::chain_for_ticker(const std::string& ticker) const {523 std::lock_guard<std::mutex> lock(mutex_);524 nlohmann::json positions_json = nlohmann::json::array();525 for (const auto& p : positions_) {526 if (p.ticker != ticker) continue;527 positions_json.push_back({528 {"ticker", p.ticker},529 {"spot", p.spotPrice},530 {"strike", p.strikePrice},531 {"expiry", p.timeToExpiry},532 {"type", p.optionType == OptionType::CALL ? "CALL" : "PUT"},533 {"mc_price", p.mc.price}, {"mc_delta", p.mc.delta},534 {"mc_gamma", p.mc.gamma}, {"mc_vega", p.mc.vega},535 {"mc_theta", p.mc.theta}, {"mc_iv", p.mc.impliedVol},536 {"bs_price", p.bs.price}, {"bs_delta", p.bs.delta},537 {"bs_gamma", p.bs.gamma}, {"bs_vega", p.bs.vega},538 {"bs_theta", p.bs.theta}, {"bs_iv", p.bs.impliedVol},539 {"entry_price", p.entryMarketPrice}540 });541 }542 return positions_json;543}544 545nlohmann::json PortfolioManager::export_json() const {546 nlohmann::json j = to_json();547 j["export_version"] = "2.0";548 j["export_timestamp"] = std::to_string(std::time(nullptr));549 return j;550}551 552std::string PortfolioManager::export_csv() const {553 std::lock_guard<std::mutex> lock(mutex_);554 std::ostringstream csv;555 csv << "Ticker,Spot,Strike,Expiry,Type,"556 << "MC_Price,MC_Delta,MC_Gamma,MC_Vega,MC_Theta,MC_IV,"557 << "BS_Price,BS_Delta,BS_Gamma,BS_Vega,BS_Theta,BS_IV,"558 << "EntryPrice\n";559 for (const auto& p : positions_) {560 csv << p.ticker << ","561 << p.spotPrice << ","562 << p.strikePrice << ","563 << p.timeToExpiry << ","564 << (p.optionType == OptionType::CALL ? "CALL" : "PUT") << ","565 << p.mc.price << "," << p.mc.delta << "," << p.mc.gamma << ","566 << p.mc.vega << "," << p.mc.theta << "," << p.mc.impliedVol << ","567 << p.bs.price << "," << p.bs.delta << "," << p.bs.gamma << ","568 << p.bs.vega << "," << p.bs.theta << "," << p.bs.impliedVol << ","569 << p.entryMarketPrice << "\n";570 }571 return csv.str();572}573 574void PortfolioManager::save(const std::string& path) const {575 std::lock_guard<std::mutex> lock(mutex_);576 try {577 nlohmann::json j = nlohmann::json::array();578 for (const auto& p : positions_) {579 j.push_back({580 {"ticker", p.ticker},581 {"spot", p.spotPrice},582 {"strike", p.strikePrice},583 {"expiry", p.timeToExpiry},584 {"rate", p.riskFreeRate},585 {"type", p.optionType == OptionType::CALL ? "CALL" : "PUT"},586 {"entry_price", p.entryMarketPrice},587 {"mc", {{"price", p.mc.price}, {"delta", p.mc.delta}, {"gamma", p.mc.gamma},588 {"vega", p.mc.vega}, {"theta", p.mc.theta}, {"rho", p.mc.rho},589 {"iv", p.mc.impliedVol}}},590 {"bs", {{"price", p.bs.price}, {"delta", p.bs.delta}, {"gamma", p.bs.gamma},591 {"vega", p.bs.vega}, {"theta", p.bs.theta}, {"rho", p.bs.rho},592 {"iv", p.bs.impliedVol}}}593 });594 }595 std::ofstream ofs(path);596 ofs << j.dump(2);597 std::cout << "[PortfolioManager] Saved " << positions_.size() << " positions to " << path << std::endl;598 } catch (const std::exception& e) {599 std::cerr << "[PortfolioManager] Save error: " << e.what() << std::endl;600 }601}602 603void PortfolioManager::load(const std::string& path) {604 std::lock_guard<std::mutex> lock(mutex_);605 try {606 std::ifstream ifs(path);607 if (!ifs.good()) return;608 nlohmann::json j;609 ifs >> j;610 if (!j.is_array()) return;611 positions_.clear();612 for (const auto& item : j) {613 Position p;614 p.ticker = item.value("ticker", "");615 p.spotPrice = item.value("spot", 0.0);616 p.strikePrice = item.value("strike", 0.0);617 p.timeToExpiry = item.value("expiry", 0.0);618 p.riskFreeRate = item.value("rate", 0.04);619 p.entryMarketPrice = item.value("entry_price", 0.0);620 p.optionType = item.value("type", "CALL") == "CALL" ? OptionType::CALL : OptionType::PUT;621 if (item.contains("mc")) {622 p.mc.price = item["mc"].value("price", 0.0);623 p.mc.delta = item["mc"].value("delta", 0.0);624 p.mc.gamma = item["mc"].value("gamma", 0.0);625 p.mc.vega = item["mc"].value("vega", 0.0);626 p.mc.theta = item["mc"].value("theta", 0.0);627 p.mc.rho = item["mc"].value("rho", 0.0);628 p.mc.impliedVol = item["mc"].value("iv", 0.0);629 p.mc.valid = true;630 }631 if (item.contains("bs")) {632 p.bs.price = item["bs"].value("price", 0.0);633 p.bs.delta = item["bs"].value("delta", 0.0);634 p.bs.gamma = item["bs"].value("gamma", 0.0);635 p.bs.vega = item["bs"].value("vega", 0.0);636 p.bs.theta = item["bs"].value("theta", 0.0);637 p.bs.rho = item["bs"].value("rho", 0.0);638 p.bs.impliedVol = item["bs"].value("iv", 0.0);639 p.bs.valid = true;640 }641 positions_.push_back(std::move(p));642 }643 if (!positions_.empty()) {644 lastSpot_ = positions_.back().spotPrice;645 lastTicker_ = positions_.back().ticker;646 }647 std::cout << "[PortfolioManager] Loaded " << positions_.size() << " positions from " << path << std::endl;648 } catch (const std::exception& e) {649 std::cerr << "[PortfolioManager] Load error: " << e.what() << std::endl;650 }651}652 653void PortfolioManager::stop() {654 save();655 active_.store(false);656}657 658bool PortfolioManager::is_active() const {659 return active_.load();660}661 