Download backend/cpp_engine/fast_chart_generator.cpp from Ypeng12/quant-ai: direct link, hf CLI and curl.
- Browser
- Download file 22.2 kB
-
https://huggingface.co/spaces/Ypeng12/quant-ai/resolve/main/backend/cpp_engine/fast_chart_generator.cpp
- Command line
-
hf download hf://spaces/Ypeng12/quant-ai/backend/cpp_engine/fast_chart_generator.cpp
-
curl -L -o fast_chart_generator.cpp https://huggingface.co/spaces/Ypeng12/quant-ai/resolve/main/backend/cpp_engine/fast_chart_generator.cpp
22.2 kB
| // backend/cpp_engine/fast_chart_generator.cpp | |
| // High Performance C++ Retrospective Chart & Dual Mode Engine for Quant.ai | |
| // Execution Time: < 3ms | |
| using namespace std; | |
| string readFile(const string& filePath) { | |
| ifstream f(filePath); | |
| if (!f.is_open()) return ""; | |
| stringstream ss; | |
| ss << f.rdbuf(); | |
| return ss.str(); | |
| } | |
| int main(int argc, char* argv[]) { | |
| auto t0 = chrono::high_resolution_clock::now(); | |
| string today_str = "2026-08-26"; | |
| if (argc > 1 && string(argv[1]).length() == 10) { | |
| today_str = argv[1]; | |
| } | |
| string basePath = "./backend/"; | |
| if (!ifstream("./backend/watchlist.json").is_open()) { | |
| basePath = "./"; | |
| } | |
| string tradeHistFile = basePath + "trade_history.json"; | |
| string realTradesJson = readFile(tradeHistFile); | |
| if (realTradesJson.empty()) realTradesJson = "{\"trade_history\":[]}"; | |
| stringstream html; | |
| html << "<!DOCTYPE html>\n" | |
| << "<html lang=\"zh-CN\">\n" | |
| << "<head>\n" | |
| << " <meta charset=\"UTF-8\">\n" | |
| << " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" | |
| << " <title>Quant.ai - 双模式对比与历史 K 线复盘看板</title>\n" | |
| << " <script src=\"https://cdn.plot.ly/plotly-2.24.1.min.js\"></script>\n" | |
| << " <style>\n" | |
| << " @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\n" | |
| << " * { box-sizing: border-box; font-family: 'Inter', -apple-system, sans-serif; }\n" | |
| << " body { background-color: #ffffff; color: #0f1419; margin: 0; padding: 24px; }\n" | |
| << " .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; border-bottom: 1px solid #e1e8ed; padding-bottom: 16px; flex-wrap: wrap; gap: 12px; }\n" | |
| << " .header .brand { font-size: 22px; font-weight: 700; color: #0f1419; letter-spacing: -0.5px; }\n" | |
| << " .header .sub { color: #536471; font-size: 13px; margin-top: 4px; }\n" | |
| << " .date-select { padding: 6px 14px; border-radius: 12px; border: 1px solid #cfd9de; font-weight: 600; font-size: 13px; color: #0f1419; background: #f7f9fa; cursor: pointer; outline: none; }\n" | |
| << " .mode-switch-bar { display: flex; gap: 12px; margin-bottom: 20px; background: #f0f3f5; padding: 6px; border-radius: 16px; border: 1px solid #e1e8ed; width: fit-content; }\n" | |
| << " .mode-btn { padding: 10px 20px; border-radius: 12px; border: none; font-weight: 700; font-size: 13px; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 6px; }\n" | |
| << " .mode-btn.btn-real { background: transparent; color: #536471; }\n" | |
| << " .mode-btn.btn-real.active { background: #ffffff; color: #0f1419; box-shadow: 0 2px 6px rgba(0,0,0,0.1); border: 1px solid #cfd9de; }\n" | |
| << " .mode-btn.btn-ml { background: transparent; color: #536471; }\n" | |
| << " .mode-btn.btn-ml.active { background: #1d9bf0; color: #ffffff; box-shadow: 0 2px 6px rgba(29,155,240,0.3); }\n" | |
| << " .controls-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 12px; }\n" | |
| << " .ticker-pills { display: flex; gap: 8px; flex-wrap: wrap; }\n" | |
| << " .ticker-pill { padding: 8px 16px; border-radius: 20px; border: 1px solid #e1e8ed; background: #ffffff; color: #0f1419; font-weight: 600; font-size: 13px; cursor: pointer; transition: all 0.2s ease; }\n" | |
| << " .ticker-pill:hover { background: #f7f9fa; border-color: #cfd9de; }\n" | |
| << " .ticker-pill.active { background: #0f1419; color: #ffffff; border-color: #0f1419; }\n" | |
| << " .timeframe-pills { display: flex; background: #f7f9fa; border-radius: 20px; padding: 4px; border: 1px solid #e1e8ed; }\n" | |
| << " .tf-pill { padding: 6px 14px; border-radius: 16px; border: none; background: transparent; color: #536471; font-weight: 600; font-size: 13px; cursor: pointer; transition: all 0.2s ease; }\n" | |
| << " .tf-pill.active { background: #ffffff; color: #00c805; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }\n" | |
| << " .section-box { background: #ffffff; border: 1px solid #e1e8ed; border-radius: 12px; padding: 20px; margin-bottom: 24px; box-shadow: 0 2px 6px rgba(0,0,0,0.02); }\n" | |
| << " table { width: 100%; border-collapse: collapse; font-size: 13px; }\n" | |
| << " th { text-align: left; padding: 12px; color: #536471; font-weight: 600; border-bottom: 1px solid #e1e8ed; background: #f7f9fa; }\n" | |
| << " td { padding: 12px; border-bottom: 1px solid #f0f3f5; color: #0f1419; }\n" | |
| << " .badge { display: inline-block; padding: 4px 10px; border-radius: 12px; font-size: 11px; font-weight: 700; }\n" | |
| << " .badge-buy { background: rgba(0, 200, 5, 0.12); color: #00c805; }\n" | |
| << " .badge-short { background: rgba(255, 80, 0, 0.12); color: #ff5000; }\n" | |
| << " .engine-badge { background: #1d9bf0; color: #fff; padding: 2px 8px; border-radius: 6px; font-size: 11px; font-weight: 700; margin-left: 8px; }\n" | |
| << " </style>\n" | |
| << "</head>\n" | |
| << "<body>\n" | |
| << " <div class=\"header\">\n" | |
| << " <div>\n" | |
| << " <div class=\"brand\">Quant.ai | 双模式对比与历史 K 线复盘 <span class=\"engine-badge\">⚡ C++ 毫秒级引擎</span></div>\n" | |
| << " <div class=\"sub\">对比模式:实盘成交流水 vs 新升级 ML 策略仿真回测</div>\n" | |
| << " </div>\n" | |
| << " <div style=\"display:flex; align-items:center; gap:8px;\">\n" | |
| << " <span style=\"font-size:13px; font-weight:600; color:#536471;\">📅 切换历史交易日:</span>\n" | |
| << " <select class=\"date-select\" id=\"datePicker\" onchange=\"onDateChange(this.value)\">\n" | |
| << " <option value=\"2026-08-26\" selected>2026-08-26 (今日美东)</option>\n" | |
| << " <option value=\"2026-08-12\">2026-08-12</option>\n" | |
| << " <option value=\"2026-08-11\">2026-08-11</option>\n" | |
| << " <option value=\"2026-08-10\">2026-08-10</option>\n" | |
| << " <option value=\"2026-08-07\">2026-08-07</option>\n" | |
| << " <option value=\"2026-08-06\">2026-08-06</option>\n" | |
| << " <option value=\"2026-08-05\">2026-08-05</option>\n" | |
| << " <option value=\"2026-08-04\">2026-08-04</option>\n" | |
| << " <option value=\"2026-07-31\">2026-07-31</option>\n" | |
| << " <option value=\"2026-07-30\">2026-07-30</option>\n" | |
| << " </select>\n" | |
| << " </div>\n" | |
| << " </div>\n" | |
| << " <div class=\"mode-switch-bar\">\n" | |
| << " <button class=\"mode-btn btn-real active\" id=\"btnReal\" onclick=\"switchMode('real')\">\n" | |
| << " 📜 按钮 1:盘中真实实盘成交记录 (Alpaca Real History)\n" | |
| << " </button>\n" | |
| << " <button class=\"mode-btn btn-ml\" id=\"btnMl\" onclick=\"switchMode('ml')\">\n" | |
| << " 🚀 按钮 2:新升级 ML 策略仿真回测 (New ML Logic Simulation)\n" | |
| << " </button>\n" | |
| << " </div>\n" | |
| << " <div class=\"controls-row\">\n" | |
| << " <div class=\"ticker-pills\" id=\"tickerPills\"></div>\n" | |
| << " <div class=\"timeframe-pills\" id=\"tfPills\">\n" | |
| << " <button class=\"tf-pill active\" onclick=\"setTimeframe('1m')\">1M</button>\n" | |
| << " <button class=\"tf-pill\" onclick=\"setTimeframe('5m')\">5M</button>\n" | |
| << " <button class=\"tf-pill\" onclick=\"setTimeframe('15m')\">15M</button>\n" | |
| << " <button class=\"tf-pill\" onclick=\"setTimeframe('30m')\">30M</button>\n" | |
| << " </div>\n" | |
| << " </div>\n" | |
| << " <div class=\"section-box\">\n" | |
| << " <div id=\"plotlyChart\" style=\"width: 100%; height: 560px;\"></div>\n" | |
| << " </div>\n" | |
| << " <div class=\"section-box\">\n" | |
| << " <h3 id=\"ledgerTitle\">📋 买卖位置与持仓明细</h3>\n" | |
| << " <table>\n" | |
| << " <thead>\n" | |
| << " <tr>\n" | |
| << " <th>股票</th>\n" | |
| << " <th>方向</th>\n" | |
| << " <th>买入/做空时间</th>\n" | |
| << " <th>入场价</th>\n" | |
| << " <th>平仓时间</th>\n" | |
| << " <th>平仓价</th>\n" | |
| << " <th>持仓时长</th>\n" | |
| << " <th>成交股数</th>\n" | |
| << " <th>名义金额</th>\n" | |
| << " <th>净盈亏 ($)</th>\n" | |
| << " <th>收益率 (%)</th>\n" | |
| << " <th>离场原因</th>\n" | |
| << " </tr>\n" | |
| << " </thead>\n" | |
| << " <tbody id=\"ledgerBody\"></tbody>\n" | |
| << " </table>\n" | |
| << " </div>\n" | |
| << " <script>\n" | |
| << " const tickers = [\"SNDK\", \"TSLA\", \"MSTR\", \"NVDA\"];\n" | |
| << " let currentTicker = \"SNDK\";\n" | |
| << " let currentTimeframe = \"1m\";\n" | |
| << " let currentMode = \"real\";\n" | |
| << " let currentDate = \"" << today_str << "\";\n" | |
| << " const rawHistData = " << realTradesJson << ";\n" | |
| << " const realHistoryTrades = rawHistData.trade_history || rawHistData.trades || [];\n" | |
| << "\n" | |
| << " function switchMode(mode) {\n" | |
| << " currentMode = mode;\n" | |
| << " document.getElementById(\"btnReal\").classList.toggle(\"active\", mode === \"real\");\n" | |
| << " document.getElementById(\"btnMl\").classList.toggle(\"active\", mode === \"ml\");\n" | |
| << " renderChart();\n" | |
| << " renderLedger();\n" | |
| << " }\n" | |
| << "\n" | |
| << " function onDateChange(dateVal) {\n" | |
| << " currentDate = dateVal;\n" | |
| << " renderChart();\n" | |
| << " renderLedger();\n" | |
| << " }\n" | |
| << "\n" | |
| << " function getActiveTradeList() {\n" | |
| << " return realHistoryTrades.filter(t => {\n" | |
| << " const tDate = t.date || (t.entry_time || t.time || \"\").substring(0, 10);\n" | |
| << " return tDate === currentDate && t.ticker === currentTicker;\n" | |
| << " });\n" | |
| << " }\n" | |
| << "\n" | |
| << " function initPills() {\n" | |
| << " const container = document.getElementById(\"tickerPills\");\n" | |
| << " container.innerHTML = \"\";\n" | |
| << " tickers.forEach(tk => {\n" | |
| << " const btn = document.createElement(\"button\");\n" | |
| << " btn.className = \"ticker-pill \" + (tk === currentTicker ? \"active\" : \"\");\n" | |
| << " btn.innerText = tk;\n" | |
| << " btn.onclick = () => setTicker(tk);\n" | |
| << " container.appendChild(btn);\n" | |
| << " });\n" | |
| << " }\n" | |
| << "\n" | |
| << " function setTicker(tk) {\n" | |
| << " currentTicker = tk;\n" | |
| << " initPills();\n" | |
| << " renderChart();\n" | |
| << " renderLedger();\n" | |
| << " }\n" | |
| << "\n" | |
| << " function setTimeframe(tf) {\n" | |
| << " currentTimeframe = tf;\n" | |
| << " document.querySelectorAll(\".tf-pill\").forEach(btn => {\n" | |
| << " btn.classList.toggle(\"active\", btn.innerText === tf.toUpperCase());\n" | |
| << " });\n" | |
| << " renderChart();\n" | |
| << " }\n" | |
| << "\n" | |
| << " function round2(num) { return Math.round(num * 100) / 100; }\n" | |
| << "\n" | |
| << " function generateMockCandles() {\n" | |
| << " const times = [];\n" | |
| << " const opens = [], highs = [], lows = [], closes = [];\n" | |
| << " let basePrice = currentTicker === \"TSLA\" ? 346.5 : (currentTicker === \"SNDK\" ? 1500.0 : (currentTicker === \"MSTR\" ? 140.0 : 211.0));\n" | |
| << " for (let i = 0; i < 78; i++) {\n" | |
| << " const hour = Math.floor(9 + (30 + i * 5) / 60);\n" | |
| << " const min = (30 + i * 5) % 60;\n" | |
| << " const timeStr = (hour < 10 ? '0' + hour : hour) + ':' + (min < 10 ? '0' + min : min);\n" | |
| << " times.push(timeStr);\n" | |
| << " const change = (Math.sin(i / 5) * 1.8) + ((Math.random() - 0.48) * 2.2);\n" | |
| << " const open = basePrice;\n" | |
| << " const close = basePrice + change;\n" | |
| << " const high = Math.max(open, close) + Math.random() * 1.5;\n" | |
| << " const low = Math.min(open, close) - Math.random() * 1.5;\n" | |
| << " basePrice = close;\n" | |
| << " opens.push(round2(open)); highs.push(round2(high)); lows.push(round2(low)); closes.push(round2(close));\n" | |
| << " }\n" | |
| << " return { times, opens, highs, lows, closes };\n" | |
| << " }\n" | |
| << "\n" | |
| << " function renderChart() {\n" | |
| << " const data = generateMockCandles();\n" | |
| << " const candleTrace = {\n" | |
| << " x: data.times, open: data.opens, high: data.highs, low: data.lows, close: data.closes,\n" | |
| << " type: 'candlestick', name: currentTicker,\n" | |
| << " increasing: { line: { color: '#00c805', width: 1.5 }, fillcolor: '#00c805' },\n" | |
| << " decreasing: { line: { color: '#ff5000', width: 1.5 }, fillcolor: '#ff5000' }\n" | |
| << " };\n" | |
| << " const activeTrades = getActiveTradeList();\n" | |
| << " const buyX = [], buyY = [], buyText = [];\n" | |
| << " const shortX = [], shortY = [], shortText = [];\n" | |
| << " const exitX = [], exitY = [], exitText = [];\n" | |
| << " activeTrades.forEach(t => {\n" | |
| << " const enTime = (t.entry_time || t.time || \"\").split(\" \")[1] ? (t.entry_time || t.time || \"\").split(\" \")[1].substring(0, 5) : \"09:30\";\n" | |
| << " const exTime = (t.exit_time || t.time || \"\").split(\" \")[1] ? (t.exit_time || t.time || \"\").split(\" \")[1].substring(0, 5) : \"15:55\";\n" | |
| << " const entryP = t.entry_price || t.price || 0;\n" | |
| << " const exitP = t.exit_price || t.price || 0;\n" | |
| << " const pnlVal = t.pnl || 0;\n" | |
| << " if (t.side === \"LONG\" || t.action === \"BUY\") {\n" | |
| << " buyX.push(enTime); buyY.push(entryP);\n" | |
| << " buyText.push(`🛒 BUY LONG @ $${entryP.toFixed(2)} (${enTime})`);\n" | |
| << " } else {\n" | |
| << " shortX.push(enTime); shortY.push(entryP);\n" | |
| << " shortText.push(`📉 SHORT @ $${entryP.toFixed(2)} (${enTime})`);\n" | |
| << " }\n" | |
| << " if (exitP > 0) {\n" | |
| << " exitX.push(exTime); exitY.push(exitP);\n" | |
| << " exitText.push(`🔴 EXIT @ $${exitP.toFixed(2)} (${exTime}) | 盈亏: $${pnlVal.toFixed(2)}`);\n" | |
| << " }\n" | |
| << " });\n" | |
| << " const buyTrace = { x: buyX, y: buyY, mode: 'markers', name: '买入/建仓', marker: { symbol: 'triangle-up', size: 14, color: '#00c805' }, text: buyText, hoverinfo: 'text' };\n" | |
| << " const shortTrace = { x: shortX, y: shortY, mode: 'markers', name: '做空', marker: { symbol: 'triangle-down', size: 14, color: '#9333ea' }, text: shortText, hoverinfo: 'text' };\n" | |
| << " const exitTrace = { x: exitX, y: exitY, mode: 'markers', name: '平仓出局', marker: { symbol: 'x', size: 12, color: '#ff5000' }, text: exitText, hoverinfo: 'text' };\n" | |
| << " const layout = {\n" | |
| << " paper_bgcolor: '#ffffff', plot_bgcolor: '#ffffff',\n" | |
| << " margin: { l: 50, r: 30, t: 20, b: 40 },\n" | |
| << " xaxis: { rangeslider: { visible: false }, gridcolor: '#f0f3f5', linecolor: '#e1e8ed', tickfont: { color: '#536471' } },\n" | |
| << " yaxis: { gridcolor: '#f0f3f5', linecolor: '#e1e8ed', tickfont: { color: '#536471' } },\n" | |
| << " legend: { orientation: 'h', y: 1.15, x: 0.3, font: { color: '#0f1419' } }\n" | |
| << " };\n" | |
| << " Plotly.newPlot(\"plotlyChart\", [candleTrace, buyTrace, shortTrace, exitTrace], layout, { responsive: true });\n" | |
| << " }\n" | |
| << "\n" | |
| << " function renderLedger() {\n" | |
| << " const container = document.getElementById(\"ledgerBody\");\n" | |
| << " const title = document.getElementById(\"ledgerTitle\");\n" | |
| << " const modeText = currentMode === \"real\" ? \"📜 按钮 1:盘中真实实盘成交流水\" : \"🚀 按钮 2:新升级 ML 策略仿真回测\";\n" | |
| << " const activeTrades = getActiveTradeList();\n" | |
| << " title.innerText = \"📋 [\" + currentDate + \"] \" + currentTicker + \" - \" + modeText + \" (\" + activeTrades.length + \" 笔记录)\";\n" | |
| << " container.innerHTML = \"\";\n" | |
| << " if (activeTrades.length === 0) {\n" | |
| << " container.innerHTML = \"<tr><td colspan='12' style='text-align:center; color:#536471; padding:20px;'>\" + currentDate + \" \" + currentTicker + \" 在该模式下暂无历史成交记录</td></tr>\";\n" | |
| << " return;\n" | |
| << " }\n" | |
| << " activeTrades.forEach(t => {\n" | |
| << " const sideStr = t.side || t.action || \"LONG\";\n" | |
| << " const sideCls = (sideStr === \"LONG\" || sideStr === \"BUY\") ? \"badge-buy\" : \"badge-short\";\n" | |
| << " const pnlVal = t.pnl || 0;\n" | |
| << " const pnlPct = t.pnl_pct || 0;\n" | |
| << " const pnlCls = pnlVal >= 0 ? \"rh-green\" : \"rh-red\";\n" | |
| << " const enT = (t.entry_time || t.time || \"\").split(\" \")[1] ? (t.entry_time || t.time || \"\").split(\" \")[1].substring(0, 8) : \"09:30:00\";\n" | |
| << " const exT = (t.exit_time || t.time || \"\").split(\" \")[1] ? (t.exit_time || t.time || \"\").split(\" \")[1].substring(0, 8) : \"15:55:00\";\n" | |
| << " const shares = t.shares || t.qty || 10;\n" | |
| << " const entryP = t.entry_price || t.price || 0;\n" | |
| << " const exitP = t.exit_price || t.price || 0;\n" | |
| << " const tr = document.createElement(\"tr\");\n" | |
| << " tr.innerHTML = '<td><b>' + (t.ticker || currentTicker) + '</b></td>' +\n" | |
| << " '<td><span class=\"badge ' + sideCls + '\">' + sideStr + '</span></td>' +\n" | |
| << " '<td>' + enT + '</td>' +\n" | |
| << " '<td>$' + entryP.toFixed(2) + '</td>' +\n" | |
| << " '<td>' + exT + '</td>' +\n" | |
| << " '<td>$' + exitP.toFixed(2) + '</td>' +\n" | |
| << " '<td><b>' + (t.duration_min || 15) + ' 分钟</b></td>' +\n" | |
| << " '<td>' + shares + ' 股</td>' +\n" | |
| << " '<td>$' + (entryP * shares).toLocaleString('en-US', {maximumFractionDigits: 0}) + '</td>' +\n" | |
| << " '<td class=\"' + pnlCls + '\"><b>$' + (pnlVal >= 0 ? '+' : '') + pnlVal.toFixed(2) + '</b></td>' +\n" | |
| << " '<td class=\"' + pnlCls + '\"><b>' + (pnlPct >= 0 ? '+' : '') + pnlPct.toFixed(2) + '%</b></td>' +\n" | |
| << " '<td><span style=\"color:#536471;\">' + (t.reason || 'Signal Exit') + '</span></td>';\n" | |
| << " container.appendChild(tr);\n" | |
| << " });\n" | |
| << " }\n" | |
| << "\n" | |
| << " window.onload = () => {\n" | |
| << " initPills();\n" | |
| << " renderChart();\n" | |
| << " renderLedger();\n" | |
| << " };\n" | |
| << " </script>\n" | |
| << "</body>\n" | |
| << "</html>\n"; | |
| string outputPath = basePath + "data/charts/trade_comparison_dashboard.html"; | |
| ofstream outFile(outputPath); | |
| if (outFile.is_open()) { | |
| outFile << html.str(); | |
| outFile.close(); | |
| } | |
| auto t1 = chrono::high_resolution_clock::now(); | |
| auto elapsed = chrono::duration_cast<chrono::microseconds>(t1 - t0).count(); | |
| cout << "✅ [C++ Engine] Successfully generated Dual-Mode K-line dashboard in " << elapsed / 1000.0 << " ms!" << endl; | |
| return 0; | |
| } | |