feat: 新增外部因素分析模块+综合评分引擎+算法文档重构

新增模块:
- fund_flow_analyzer.py: 主力资金流向分析(P0, ±20)
- market_sentiment.py: 市场情绪指标(P1, ±10)
- external_factors.py: 北向资金/美股/大宗商品/汇率(P2-P4,P7)
- news_analyzer.py: 公告/并购/政策面LLM分析(P5-P6)
- score_engine.py: 综合评分引擎,整合技术面+外部因素

路由更新:
- analysis.py: deep_analyze接入综合评分,根据最终评级修正买卖建议
- market.py: 新增4个外部因素API端点
- trades.py: 交易路由更新

算法文档重构:
- 章节重排: 技术面(二三)→外部因素(四)→买卖决策(五)→数据源(六)→性能(七)
- 架构图更新为五层,标注章节对应
- 5.1/5.2标注纯技术面,5.3整合外部因素修正推荐
This commit is contained in:
selfrelease
2026-07-17 23:50:42 +08:00
parent 04f8b9f951
commit 2b5a32ca1e
23 changed files with 4191 additions and 268 deletions
+345 -97
View File
@@ -24,7 +24,7 @@
<link rel="stylesheet" href="/static/css/base.css?v=20260318v2">
<link rel="stylesheet" href="/static/css/components.css?v=20260318v2">
<link rel="stylesheet" href="/static/css/auth.css?v=20260318v1">
<link rel="stylesheet" href="/static/css/pages.css?v=20260318v2">
<link rel="stylesheet" href="/static/css/pages.css?v=20260511v3">
<link rel="stylesheet" href="/static/css/scan.css?v=20260318v2">
<link rel="stylesheet" href="/static/css/responsive.css?v=20260318v2">
</head>
@@ -668,11 +668,11 @@
<button :class="['sub-tab', { active: analysisSubTab === 'scan' }]" @click="analysisSubTab = 'scan'; fetchFullScanResults(1)">
全景扫描
</button>
<button :class="['sub-tab', { active: analysisSubTab === 'bull' }]" @click="analysisSubTab = 'bull'; fetchBullStocks()">
找牛股
<button :class="['sub-tab', { active: analysisSubTab === 'bull' }]" @click="analysisSubTab = 'bull'">
个股深析
</button>
<button :class="['sub-tab', { active: analysisSubTab === 'sim' }]" @click="analysisSubTab = 'sim'; loadSimData()">
模拟交易
<button :class="['sub-tab', { active: analysisSubTab === 'buyanalysis' }]" @click="analysisSubTab = 'buyanalysis'; fetchBuyAnalysis()">
买入分析
</button>
</div>
@@ -1100,114 +1100,272 @@
</div><!-- 全景扫描子页面结束 -->
<!-- 找牛股子页面 -->
<div v-show="analysisSubTab === 'bull'" class="bull-stocks-page">
<!-- 个股深析子页面 -->
<div v-show="analysisSubTab === 'bull'" class="deep-analysis-page">
<!-- 牛股流程说明 -->
<div class="bull-flow-card">
<div class="bull-flow-title">标准牛股启动信号流程</div>
<div class="bull-flow-steps">
<span class="bull-step step-divergence" :class="{ active: bullActiveStage === 1 }" @click="bullActiveStage = 1">
<span class="step-label">底部探测</span>
<span class="step-action">关注</span>
<span class="step-count" v-if="bullStocksData">{{ getBullStageStocks(1).length }}</span>
</span>
<span class="bull-flow-arrow"></span>
<span class="bull-step step-dragon" :class="{ active: bullActiveStage === 2 }" @click="bullActiveStage = 2">
<span class="step-label">资金进场</span>
<span class="step-action">买入</span>
<span class="step-count" v-if="bullStocksData">{{ getBullStageStocks(2).length }}</span>
</span>
<span class="bull-flow-arrow"></span>
<span class="bull-step step-true-dragon" :class="{ active: bullActiveStage === 3 }" @click="bullActiveStage = 3">
<span class="step-label">趋势确立</span>
<span class="step-action">持有</span>
<span class="step-count" v-if="bullStocksData">{{ getBullStageStocks(3).length }}</span>
</span>
<span class="bull-flow-arrow"></span>
<span class="bull-step step-main-wave" :class="{ active: bullActiveStage === 4 }" @click="bullActiveStage = 4">
<span class="step-label">加速拉升</span>
<span class="step-action">加仓</span>
<span class="step-count" v-if="bullStocksData">{{ getBullStageStocks(4).length }}</span>
</span>
<span class="bull-flow-arrow"></span>
<span class="bull-step step-rebound" :class="{ active: bullActiveStage === 5 }" @click="bullActiveStage = 5">
<span class="step-label">回调补涨</span>
<span class="step-action">观察</span>
<span class="step-count" v-if="bullStocksData">{{ getBullStageStocks(5).length }}</span>
</span>
</div>
<div class="bull-flow-hint">
<template v-if="bullStocksData"><b>{{ bullStocksData.total }}</b> 只有信号股票 · 数据来自最新全景扫描</template>
<template v-else>点击各阶段查看对应股票列表 · 数据来自最新全景扫描</template>
<!-- 输入区 -->
<div class="deep-input-card">
<div class="deep-input-row">
<input v-model="deepCode" placeholder="输入股票代码,如 601609"
class="deep-code-input" @keyup.enter="fetchDeepAnalysis()" maxlength="6">
<button class="deep-analyze-btn" @click="fetchDeepAnalysis()" :disabled="deepLoading">
{{ deepLoading ? '分析中...' : '深度分析' }}
</button>
</div>
</div>
<!-- 加载状态 -->
<div v-if="bullStocksLoading" class="bull-loading">
<span class="loading-dot"></span> 正在分析牛股阶段...
<div v-if="deepLoading" class="bull-loading">
<span class="loading-dot"></span> 正在进行深度分析...
</div>
<!-- 阶段股票列表 -->
<div v-if="bullStocksData && !bullStocksLoading" class="bull-stage-section">
<div class="bull-stage-header">
<template v-if="getBullStageInfo(bullActiveStage)">
<span class="bull-stage-icon">{{ getBullStageInfo(bullActiveStage).icon }}</span>
<span class="bull-stage-name">{{ getBullStageInfo(bullActiveStage).name }} — {{ getBullStageInfo(bullActiveStage).desc }}</span>
</template>
<span class="bull-stage-count">{{ getBullStageStocks(bullActiveStage).length }} 只</span>
</div>
<!-- 分析报告 -->
<div v-if="deepReport && !deepLoading" class="deep-report">
<div v-if="getBullStageStocks(bullActiveStage).length === 0" class="bull-empty">
当前阶段暂无符合条件的股票
<!-- 头部:股票信息+综合评分 -->
<div class="deep-header-card">
<div class="deep-header-left">
<div class="deep-stock-name">{{ deepReport.stock_name }}</div>
<div class="deep-stock-code">{{ deepReport.stock_code }}</div>
</div>
<div class="deep-header-center" v-if="deepReport.realtime">
<div class="deep-price">¥{{ deepReport.realtime.price.toFixed(2) }}</div>
<div class="deep-change" :class="deepReport.realtime.change_pct >= 0 ? 'up' : 'down'">
{{ deepReport.realtime.change_pct >= 0 ? '+' : '' }}{{ deepReport.realtime.change_pct.toFixed(2) }}%
</div>
</div>
<div class="deep-header-right">
<div class="deep-score-circle" :class="'score-' + (deepReport.deep_score >= 65 ? 'high' : deepReport.deep_score >= 50 ? 'mid' : 'low')">
<span class="score-num">{{ deepReport.deep_score }}</span>
</div>
<div class="deep-verdict">{{ deepReport.verdict }}</div>
</div>
</div>
<div v-else class="bull-stock-list">
<div v-for="stock in getBullStageStocks(bullActiveStage)" :key="stock.code"
class="bull-stock-card" :class="'stage-s' + bullActiveStage"
@click="techSignalCode = stock.code; analysisSubTab = 'scan'; fetchTechSignals()">
<div class="bull-card-left">
<div class="bull-card-code">{{ stock.code }}</div>
<div class="bull-card-name">{{ stock.name }}</div>
<!-- AI通俗解说 -->
<div v-if="deepReport.ai_summary" class="deep-ai-summary">
<div class="deep-ai-title">💡 AI 解读</div>
<div class="deep-ai-text">
<p>{{ deepReport.ai_summary.text }}</p>
</div>
<div class="ai-action-tip">
<span class="ai-action-label">操作建议:</span>{{ deepReport.ai_summary.action_tip }}
</div>
</div>
<!-- 推荐操作 -->
<div class="deep-section">
<div class="deep-section-title">系统推荐</div>
<div class="deep-recommend-bar" :class="'rec-' + deepReport.recommend.signal_type">
<span class="rec-display">{{ deepReport.recommend.display }}</span>
<span class="rec-rate">{{ deepReport.recommend.rate }}分</span>
<span class="rec-reason-text">{{ deepReport.recommend.reason }}</span>
</div>
</div>
<!-- 信号列表 -->
<div class="deep-section" v-if="deepReport.signals && deepReport.signals.length">
<div class="deep-section-title">触发信号 ({{ deepReport.signals.length }}个)</div>
<div class="deep-signal-list">
<div v-for="sig in deepReport.signals" :key="sig.type" class="deep-signal-item">
<span class="deep-sig-name">{{ sig.name }}</span>
<span class="deep-sig-strength">{{ sig.strength }}%</span>
<span class="deep-sig-desc">{{ sig.description }}</span>
</div>
<div class="bull-card-center">
<div class="bull-card-signals">
<span v-for="sig in stock.signals_active" :key="sig" class="bull-signal-tag">{{ sig }}</span>
</div>
<div class="bull-card-recommend">
<span :class="['rec-tag', 'rec-' + stock.recommend_type]">{{ stock.recommend_text }}</span>
<span class="rec-reason">{{ stock.recommend_reason }}</span>
</div>
</div>
<div class="bull-card-right">
<div class="bull-card-price" v-if="stock.price">¥{{ stock.price.toFixed(2) }}</div>
<div class="bull-card-change" v-if="stock.change_pct != null"
:class="stock.change_pct >= 0 ? 'up' : 'down'">
{{ stock.change_pct >= 0 ? '+' : '' }}{{ stock.change_pct.toFixed(2) }}%
</div>
<div class="bull-card-progress">
<div class="mini-progress-bar">
<div class="mini-progress-fill" :style="{ width: stock.progress + '%' }"></div>
</div>
</div>
<!-- 价格位置 -->
<div class="deep-section">
<div class="deep-section-title">价格位置</div>
<div class="deep-position-grid">
<div v-for="(pos, key) in deepReport.position" :key="key" class="deep-pos-item">
<div class="pos-label">{{ key.replace('d','') }}日</div>
<div class="pos-range">{{ pos.low }} ~ {{ pos.high }}</div>
<div class="pos-bar-wrap">
<div class="pos-bar-bg">
<div class="pos-bar-fill" :style="{ width: pos.range_pct + '%' }"
:class="pos.range_pct > 80 ? 'high' : pos.range_pct < 30 ? 'low' : 'mid'"></div>
</div>
<span class="progress-label">{{ stock.progress }}%</span>
<span class="pos-pct">{{ pos.range_pct }}%</span>
</div>
<div class="pos-space">
<span class="space-up">↑{{ pos.up_space }}%</span>
<span class="space-down">↓{{ pos.down_risk }}%</span>
</div>
</div>
</div>
</div>
<!-- 支撑压力 -->
<div class="deep-section">
<div class="deep-section-title">支撑与压力</div>
<div class="deep-sr-grid">
<div class="sr-col">
<div class="sr-col-title support-title">支撑位</div>
<div v-for="s in deepReport.supports" :key="s.name" class="sr-item support">
<span class="sr-name">{{ s.name }}</span>
<span class="sr-level">{{ s.level }}</span>
</div>
<div v-if="!deepReport.supports.length" class="sr-empty"></div>
</div>
<div class="sr-col">
<div class="sr-col-title resist-title">压力位</div>
<div v-for="r in deepReport.resistances" :key="r.name" class="sr-item resist">
<span class="sr-name">{{ r.name }}</span>
<span class="sr-level">{{ r.level }}</span>
</div>
<div v-if="!deepReport.resistances.length" class="sr-empty"></div>
</div>
</div>
</div>
<!-- 量价与形态 -->
<div class="deep-section">
<div class="deep-section-title">量价分析</div>
<div class="deep-vol-info">
<span class="vol-tag" :class="deepReport.volume.ratio >= 1.3 ? 'vol-up' : deepReport.volume.ratio < 0.7 ? 'vol-dn' : ''">
{{ deepReport.volume.trend }}
</span>
<span class="vol-detail">量比 {{ deepReport.volume.ratio }}x · 5日均量 {{ (deepReport.volume.avg_5/10000).toFixed(0) }}万</span>
</div>
<div v-if="deepReport.patterns.length" class="deep-patterns">
<span v-for="p in deepReport.patterns" :key="p.name"
class="pattern-tag" :class="p.bullish === true ? 'bullish' : p.bullish === false ? 'bearish' : ''">
{{ p.name }} <small>{{ p.desc }}</small>
</span>
</div>
</div>
<!-- 均线与基本面 -->
<div class="deep-section">
<div class="deep-section-title">技术指标</div>
<div class="deep-ma-info">
<span class="ma-trend-tag" :class="deepReport.ma_trend">{{ deepReport.ma_trend_label }}</span>
<span v-for="(val, key) in deepReport.ma" :key="key" class="ma-val">
{{ key.toUpperCase() }}={{ val }}
</span>
</div>
<div v-if="deepReport.realtime" class="deep-fundamental">
<span v-if="deepReport.realtime.pe > 0">PE {{ deepReport.realtime.pe.toFixed(1) }}</span>
<span v-if="deepReport.realtime.pb > 0">PB {{ deepReport.realtime.pb.toFixed(1) }}</span>
<span v-if="deepReport.realtime.total_market_cap > 0">
市值 {{ (deepReport.realtime.total_market_cap/100000000).toFixed(0) }}亿
</span>
</div>
</div>
<!-- 评分明细 -->
<div class="deep-section">
<div class="deep-section-title">评分明细</div>
<div class="deep-score-details">
<span v-for="r in deepReport.score_reasons" :key="r" class="score-reason-tag"
:class="r.includes('+') ? 'positive' : 'negative'">{{ r }}</span>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-if="!bullStocksData && !bullStocksLoading" class="bull-empty-tip">
<div class="empty-icon">找牛股</div>
<p>找牛股 — 按信号流程筛选潜力股</p>
<p class="sub-tip">基于全景扫描数据,按标准牛股启动信号流程分阶段筛选</p>
<button class="scan-action-btn" @click="fetchBullStocks()">开始找牛股</button>
<div v-if="!deepReport && !deepLoading" class="bull-empty-tip">
<div class="empty-icon" style="font-size:28px">个股深析</div>
<p>输入股票代码,获取深度技术分析报告</p>
<p class="sub-tip">包含价格位置、压力支撑、量价分析、空间估算、综合评分</p>
</div>
</div><!-- 找牛股子页面结束 -->
</div><!-- 个股深析子页面结束 -->
<!-- 模拟交易子页面 -->
<div v-show="analysisSubTab === 'sim'" class="sim-trade-page">
<!-- 买入分析子页面 -->
<div v-show="analysisSubTab === 'buyanalysis'" class="buy-analysis-page">
<div class="buy-analysis-header">
<div class="buy-analysis-title">买入股深度分析</div>
<div class="buy-analysis-desc">对全景扫描中推荐买入的股票批量执行深度分析</div>
<button class="deep-analyze-btn" @click="fetchBuyAnalysis()" :disabled="buyAnalysisLoading" style="margin-top:8px;">
{{ buyAnalysisLoading ? '分析中...' : '刷新分析' }}
</button>
</div>
<div v-if="buyAnalysisLoading" class="bull-loading">
<span class="loading-dot"></span> 正在批量分析买入推荐股...{{ buyAnalysisProgress }}/{{ buyAnalysisTotal }}
</div>
<div v-if="!buyAnalysisLoading && buyAnalysisList.length === 0" class="bull-empty-tip">
<div class="empty-icon" style="font-size:28px">📊</div>
<p>暂无买入推荐股票</p>
<p class="sub-tip">当全景扫描中有买入推荐时,会自动进行深度分析</p>
</div>
<div v-if="buyAnalysisList.length > 0" class="buy-analysis-list">
<div v-for="(item, idx) in buyAnalysisList" :key="item.stock_code" class="buy-analysis-card">
<div class="buy-card-header" @click="item._expanded = !item._expanded">
<div class="buy-card-left">
<span class="buy-card-name">{{ item.stock_name }}</span>
<span class="buy-card-code">{{ item.stock_code }}</span>
</div>
<div class="buy-card-center">
<span class="buy-card-price" v-if="item.realtime">¥{{ item.realtime.price.toFixed(2) }}</span>
<span class="buy-card-change" v-if="item.realtime" :class="item.realtime.change_pct >= 0 ? 'up' : 'down'">
{{ item.realtime.change_pct >= 0 ? '+' : '' }}{{ item.realtime.change_pct.toFixed(2) }}%
</span>
</div>
<div class="buy-card-right">
<span class="buy-card-score" :class="'score-' + (item.deep_score >= 65 ? 'high' : item.deep_score >= 50 ? 'mid' : 'low')">
{{ item.deep_score }}分
</span>
<span class="buy-card-verdict">{{ item.verdict }}</span>
</div>
<span class="buy-card-arrow" :class="{ expanded: item._expanded }"></span>
</div>
<div v-show="item._expanded" class="buy-card-detail">
<div v-if="item.ai_summary" class="deep-ai-summary" style="margin-bottom:8px;">
<div class="deep-ai-title">💡 AI 解读</div>
<div class="deep-ai-text"><p>{{ item.ai_summary.text }}</p></div>
<div class="ai-action-tip">
<span class="ai-action-label">操作建议:</span>{{ item.ai_summary.action_tip }}
</div>
</div>
<div class="buy-detail-grid">
<div class="buy-detail-item">
<span class="buy-detail-label">系统推荐</span>
<span class="buy-detail-value" style="color:#f44336;font-weight:600;">{{ item.recommend.display }} {{ item.recommend.rate }}分</span>
</div>
<div class="buy-detail-item">
<span class="buy-detail-label">推荐理由</span>
<span class="buy-detail-value">{{ item.recommend.reason }}</span>
</div>
<div class="buy-detail-item" v-if="item.signals && item.signals.length">
<span class="buy-detail-label">触发信号</span>
<span class="buy-detail-value">
<span v-for="sig in item.signals" :key="sig.type" class="deep-sig-name" style="margin-right:6px;">{{ sig.name }}</span>
</span>
</div>
<div class="buy-detail-item" v-if="item.supports && item.supports.length">
<span class="buy-detail-label">支撑位</span>
<span class="buy-detail-value">{{ item.supports.map(s => s.level.toFixed(2)).join(' / ') }}</span>
</div>
<div class="buy-detail-item" v-if="item.resistances && item.resistances.length">
<span class="buy-detail-label">压力位</span>
<span class="buy-detail-value">{{ item.resistances.map(r => r.level.toFixed(2)).join(' / ') }}</span>
</div>
<div class="buy-detail-item">
<span class="buy-detail-label">量能</span>
<span class="buy-detail-value">{{ item.volume ? item.volume.trend + ' (量比' + item.volume.ratio + 'x)' : '-' }}</span>
</div>
<div class="buy-detail-item">
<span class="buy-detail-label">均线</span>
<span class="buy-detail-value">{{ item.ma_trend_label }}</span>
</div>
</div>
<button class="deep-analyze-btn" style="margin-top:8px;font-size:12px;padding:6px 12px;"
@click="deepCode = item.stock_code; analysisSubTab = 'bull'; fetchDeepAnalysis();">
查看完整报告 →
</button>
</div>
</div>
</div>
</div><!-- 买入分析子页面结束 -->
<!-- (模拟交易已移至模型tab) -->
<div style="display:none">
<!-- 模拟交易统计卡片 -->
<div class="sim-stats-card">
<div class="sim-stats-header">
@@ -1533,14 +1691,24 @@
</div>
</div>
</div><!-- 模拟交易子页面结束 -->
</div><!-- 模拟交易占位结束 -->
</div><!-- 分析页面结束 -->
<!-- 模型页面 - 交易信号实战体系 -->
<!-- 模型页面 -->
<div v-show="activeTab === 'model'" class="model-page">
<!-- 模型页子导航 -->
<div class="sub-tabs">
<button :class="['sub-tab', { active: modelSubTab === 'system' }]" @click="modelSubTab = 'system'">
信号体系
</button>
<button :class="['sub-tab', { active: modelSubTab === 'sim' }]" @click="modelSubTab = 'sim'; loadSimData()">
模拟交易
</button>
</div>
<!-- 信号体系子页面 -->
<div v-show="modelSubTab === 'system'">
<!-- 信号胜率排行 -->
<div class="signal-system-section">
<h4 class="signal-system-title">交易信号胜率排行</h4>
@@ -1652,6 +1820,86 @@
<div class="signal-summary-item"><span class="summary-tag aux">短底背离 / 反弹</span>仅作辅助参考,不单独作为核心决策依据</div>
</div>
</div>
</div><!-- 信号体系子页面结束 -->
<!-- 模拟交易子页面 -->
<div v-show="modelSubTab === 'sim'" class="sim-trade-page">
<!-- 模拟交易统计卡片 -->
<div class="sim-stats-card">
<div class="sim-stats-header">
<h4>智能交易 <span v-if="smartAlgoConfig" style="font-size:12px;color:#888;">{{ smartAlgoConfig.algo_name }}</span></h4>
<div class="sim-actions">
<button class="sim-btn auto" @click="executeAutoTrade" :disabled="simAutoTrading">
{{ simAutoTrading ? '执行中...' : '执行' }}
</button>
<button class="sim-btn reset" @click="confirmResetSim">重置</button>
</div>
</div>
<div class="sim-stats-grid">
<div class="sim-stat-item">
<div class="sim-stat-value">{{ formatMoney(smartAlgoConfig?.total_capital || simStats.initial_capital || 200000) }}</div>
<div class="sim-stat-label">总本金</div>
</div>
<div class="sim-stat-item">
<div class="sim-stat-value">{{ formatMoney(simStats.total_market_value || 0) }}</div>
<div class="sim-stat-label">持仓市值</div>
</div>
<div class="sim-stat-item">
<div class="sim-stat-value">{{ formatMoney(simStats.cash || (smartAlgoConfig?.total_capital || 200000)) }}</div>
<div class="sim-stat-label">可用资金</div>
</div>
<div class="sim-stat-item" :class="(simStats.total_profit || 0) >= 0 ? 'profit' : 'loss'">
<div class="sim-stat-value">{{ (simStats.total_profit || 0) >= 0 ? '+' : '' }}{{ formatMoney(simStats.total_profit || 0) }}</div>
<div class="sim-stat-label">总盈亏</div>
</div>
<div class="sim-stat-item" :class="(simStats.profit_rate || 0) >= 0 ? 'profit' : 'loss'">
<div class="sim-stat-value">{{ (simStats.profit_rate || 0) >= 0 ? '+' : '' }}{{ (simStats.profit_rate || 0).toFixed(2) }}%</div>
<div class="sim-stat-label">收益率</div>
</div>
<div class="sim-stat-item">
<div class="sim-stat-value">{{ simStats.total_trades || 0 }}</div>
<div class="sim-stat-label">交易次数</div>
</div>
</div>
<div class="sim-stats-detail">
<span :class="(simStats.unrealized_profit || 0) >= 0 ? 'profit' : 'loss'">
浮动 {{ (simStats.unrealized_profit || 0) >= 0 ? '+' : '' }}{{ formatMoney(simStats.unrealized_profit || 0) }}
</span>
<span :class="(simStats.realized_profit || 0) >= 0 ? 'profit' : 'loss'">
已实现 {{ (simStats.realized_profit || 0) >= 0 ? '+' : '' }}{{ formatMoney(simStats.realized_profit || 0) }}
</span>
<button class="sim-btn refresh" @click="updateSimPrices" :disabled="simPositions.length === 0 || simPriceRefreshing" v-if="simPositions.length > 0" style="margin-left:auto;font-size:11px;padding:2px 8px;">
{{ simPriceRefreshing ? '刷新中...' : '刷新现价' }}
</button>
</div>
</div>
<!-- 模拟持仓和交易历史直接复用现有逻辑 -->
<div class="sim-section collapsible-section" v-if="simPositions.length > 0">
<div class="sim-section-header" style="cursor:pointer;" @click="simPositionsExpanded = !simPositionsExpanded">
<h4 class="section-title">持仓 <span class="badge">{{ simPositions.length }}</span></h4>
<span class="expand-arrow" :class="{ expanded: simPositionsExpanded }"></span>
</div>
<div v-show="simPositionsExpanded" class="sim-positions-list">
<div v-for="pos in simPositions" :key="pos.stock_code" class="sim-position-item" @click="loadFundamental(pos.stock_code, pos.stock_name)" style="cursor:pointer;">
<div class="sim-pos-header">
<span class="sim-pos-stock">{{ pos.stock_code }} {{ pos.stock_name }}</span>
<span class="sim-pos-profit" :class="(pos.unrealized_profit || 0) >= 0 ? 'profit' : 'loss'">
{{ (pos.unrealized_profit || 0) >= 0 ? '+' : '' }}{{ formatMoney(pos.unrealized_profit || 0) }}
({{ (pos.profit_rate || 0) >= 0 ? '+' : '' }}{{ (pos.profit_rate || 0).toFixed(2) }}%)
</span>
</div>
<div class="sim-pos-detail">
<span>{{ pos.quantity }}股 @ ¥{{ (pos.avg_cost || 0).toFixed(2) }}</span>
<span v-if="pos.current_price">现价 ¥{{ pos.current_price.toFixed(2) }}</span>
</div>
</div>
</div>
</div>
</div><!-- 模拟交易子页面结束 -->
</div><!-- 模型页面结束 -->
<!-- 交易记录页面 -->
@@ -1944,6 +2192,6 @@
</div>
{% endraw %}
<script src="/static/js/app.js?v=20260318v2"></script>
<script src="/static/js/app.js?v=20260511v3"></script>
</body>
</html>