feat: upgrade to multi-model ensemble system
- Updated DB schemas: added 'strategy' to trades, new 'hourly_model_predictions' wide table - Refactored backend: api/models.py, api/prediction_service.py to support wide format - Updated trader: auto_trade_polymarket.py now logs strategy and saves to new table - Updated frontend: index.html and app.js to display multi-model accuracy and trade strategy
This commit is contained in:
1 parent
c0a02868ac
commit
2993014fa8
5 files changed
+465
-335
No files matched your search
+131
-77
@@ -74,8 +74,8 @@
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">价格</div>
|
||||
<div class="stat-value">${{ currentTrade.price?.toFixed(3) || '-' }}</div>
|
||||
<div class="stat-label">策略</div>
|
||||
<div class="stat-value small">{{ currentTrade.strategy || 'Ensemble' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@@ -116,6 +116,7 @@
|
||||
<tr>
|
||||
<th>时间 (北京)</th>
|
||||
<th>市场</th>
|
||||
<th>策略</th>
|
||||
<th>预测</th>
|
||||
<th>概率</th>
|
||||
<th>价格</th>
|
||||
@@ -128,6 +129,9 @@
|
||||
<td class="text-truncate" style="max-width: 200px;">
|
||||
{{ trade.market_title }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ trade.strategy || 'Ensemble' }}</span>
|
||||
</td>
|
||||
<td :class="getPredictionClass(trade.prediction)">
|
||||
<i :class="trade.prediction === 'UP' ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ trade.prediction }}
|
||||
@@ -139,7 +143,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!tradeHistory.data?.length">
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
<td colspan="7" class="text-center text-muted py-4">
|
||||
暂无数据
|
||||
</td>
|
||||
</tr>
|
||||
@@ -184,66 +188,80 @@
|
||||
|
||||
<!-- 当月汇总卡片 -->
|
||||
<div class="row mb-4">
|
||||
<!-- DLinear 卡片 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 border-primary">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<i class="bi bi-graph-up-arrow"></i> DLinear 模型
|
||||
<!-- Ensemble 卡片 -->
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100 border-primary shadow-sm">
|
||||
<div class="card-header bg-primary text-white text-center">
|
||||
<i class="bi bi-diagram-3"></i> 集成策略 (投票)
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center" v-if="monthlyStats.DLinear">
|
||||
<div class="col-3">
|
||||
<div class="stat-number">{{ monthlyStats.DLinear.total }}</div>
|
||||
<div class="stat-label">预测次数</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-success">{{ monthlyStats.DLinear.correct }}</div>
|
||||
<div class="stat-label">正确</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-danger">{{ monthlyStats.DLinear.incorrect }}</div>
|
||||
<div class="stat-label">错误</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-primary">{{ monthlyStats.DLinear.accuracy }}%</div>
|
||||
<div class="stat-label">准确率</div>
|
||||
<div class="text-center" v-if="monthlyStats.Ensemble">
|
||||
<h2 class="display-6 text-primary fw-bold">{{ monthlyStats.Ensemble.accuracy }}%</h2>
|
||||
<div class="text-muted mb-2">准确率</div>
|
||||
<div class="d-flex justify-content-between px-3 border-top pt-2">
|
||||
<span class="text-success"><i class="bi bi-check-lg"></i> {{ monthlyStats.Ensemble.correct }}</span>
|
||||
<span class="text-secondary">{{ monthlyStats.Ensemble.total }} 次</span>
|
||||
<span class="text-danger"><i class="bi bi-x-lg"></i> {{ monthlyStats.Ensemble.incorrect }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-muted py-3">
|
||||
暂无数据
|
||||
<div v-else class="text-center text-muted py-3">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DLinear 卡片 -->
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100 border-secondary">
|
||||
<div class="card-header bg-secondary text-white text-center">
|
||||
<i class="bi bi-graph-up-arrow"></i> DLinear
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center" v-if="monthlyStats.DLinear">
|
||||
<h3 class="text-dark">{{ monthlyStats.DLinear.accuracy }}%</h3>
|
||||
<div class="text-muted small">准确率</div>
|
||||
<div class="mt-2 small text-muted">
|
||||
{{ monthlyStats.DLinear.correct }}/{{ monthlyStats.DLinear.total }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-muted py-3">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- N-BEATS 卡片 -->
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100 border-secondary">
|
||||
<div class="card-header bg-secondary text-white text-center">
|
||||
<i class="bi bi-soundwave"></i> N-BEATS
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center" v-if="monthlyStats['N-BEATS']">
|
||||
<h3 class="text-dark">{{ monthlyStats['N-BEATS'].accuracy }}%</h3>
|
||||
<div class="text-muted small">准确率</div>
|
||||
<div class="mt-2 small text-muted">
|
||||
{{ monthlyStats['N-BEATS'].correct }}/{{ monthlyStats['N-BEATS'].total }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-muted py-3">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TTM-R2 卡片 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 border-success">
|
||||
<div class="card-header bg-success text-white">
|
||||
<i class="bi bi-cpu"></i> TTM-R2 模型
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100 border-secondary">
|
||||
<div class="card-header bg-secondary text-white text-center">
|
||||
<i class="bi bi-cpu"></i> TTM-R2
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center" v-if="monthlyStats['TTM-R2']">
|
||||
<div class="col-3">
|
||||
<div class="stat-number">{{ monthlyStats['TTM-R2'].total }}</div>
|
||||
<div class="stat-label">预测次数</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-success">{{ monthlyStats['TTM-R2'].correct }}</div>
|
||||
<div class="stat-label">正确</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-danger">{{ monthlyStats['TTM-R2'].incorrect }}</div>
|
||||
<div class="stat-label">错误</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="stat-number text-primary">{{ monthlyStats['TTM-R2'].accuracy }}%</div>
|
||||
<div class="stat-label">准确率</div>
|
||||
<div class="text-center" v-if="monthlyStats['TTM-R2']">
|
||||
<h3 class="text-dark">{{ monthlyStats['TTM-R2'].accuracy }}%</h3>
|
||||
<div class="text-muted small">准确率</div>
|
||||
<div class="mt-2 small text-muted">
|
||||
{{ monthlyStats['TTM-R2'].correct }}/{{ monthlyStats['TTM-R2'].total }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-muted py-3">
|
||||
暂无数据
|
||||
</div>
|
||||
<div v-else class="text-center text-muted py-3">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -252,52 +270,61 @@
|
||||
<!-- 每日准确率表格 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-calendar3"></i> 每日准确率
|
||||
<i class="bi bi-calendar3"></i> 每日准确率概览
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<table class="table table-hover mb-0 text-center">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>日期 (北京)</th>
|
||||
<th>DLinear 预测</th>
|
||||
<th>DLinear 准确率</th>
|
||||
<th>TTM-R2 预测</th>
|
||||
<th>TTM-R2 准确率</th>
|
||||
<th class="text-start ps-4">日期</th>
|
||||
<th>集成策略</th>
|
||||
<th>DLinear</th>
|
||||
<th>N-BEATS</th>
|
||||
<th>TTM-R2</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="day in dailyStats.data" :key="day.date">
|
||||
<td>{{ day.date }}</td>
|
||||
<td class="text-start ps-4">{{ day.date }}</td>
|
||||
|
||||
<!-- Ensemble -->
|
||||
<td>
|
||||
<span v-if="day.DLinear">
|
||||
{{ day.DLinear.correct }}/{{ day.DLinear.total }}
|
||||
<span v-if="day.Ensemble" :class="getAccuracyClass(day.Ensemble.accuracy)">
|
||||
{{ day.Ensemble.accuracy }}%
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
|
||||
<!-- DLinear -->
|
||||
<td>
|
||||
<span v-if="day.DLinear" :class="getAccuracyClass(day.DLinear.accuracy)">
|
||||
{{ day.DLinear.accuracy }}%
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
|
||||
<!-- N-BEATS -->
|
||||
<td>
|
||||
<span v-if="day['TTM-R2']">
|
||||
{{ day['TTM-R2'].correct }}/{{ day['TTM-R2'].total }}
|
||||
<span v-if="day['N-BEATS']" :class="getAccuracyClass(day['N-BEATS'].accuracy)">
|
||||
{{ day['N-BEATS'].accuracy }}%
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
|
||||
<!-- TTM-R2 -->
|
||||
<td>
|
||||
<span v-if="day['TTM-R2']" :class="getAccuracyClass(day['TTM-R2'].accuracy)">
|
||||
{{ day['TTM-R2'].accuracy }}%
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
@click="showHourlyDetails(day.date)">
|
||||
<i class="bi bi-eye"></i> 查看
|
||||
<i class="bi bi-eye"></i> 详情
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -337,7 +364,7 @@
|
||||
|
||||
<!-- 每小时详情弹窗 -->
|
||||
<div class="modal fade" id="hourlyModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
@@ -346,43 +373,70 @@
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body p-0">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<table class="table table-sm table-hover mb-0 text-center align-middle">
|
||||
<thead class="table-light sticky-top">
|
||||
<tr>
|
||||
<th>时间 (北京)</th>
|
||||
<th>实际涨跌</th>
|
||||
<th>时间</th>
|
||||
<th>开盘</th>
|
||||
<th>收盘</th>
|
||||
<th>实际</th>
|
||||
<th class="table-primary">集成策略</th>
|
||||
<th>DLinear</th>
|
||||
<th>N-BEATS</th>
|
||||
<th>TTM-R2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="hour in hourlyModal.data" :key="hour.hour">
|
||||
<td>{{ hour.timestamp_cst || (hour.hour + ':00') }}</td>
|
||||
<td :class="hour.actual_result === 1 ? 'text-success' : 'text-danger'">
|
||||
<i :class="hour.actual_result === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ hour.actual_result === 1 ? 'UP' : 'DOWN' }}
|
||||
<td>{{ hour.timestamp_cst ? hour.timestamp_cst.split(' ')[1] : (hour.hour + ':00') }}</td>
|
||||
<td>${{ hour.open_price?.toFixed(0) || '-' }}</td>
|
||||
<td>${{ hour.close_price?.toFixed(0) || '-' }}</td>
|
||||
|
||||
<td :class="hour.actual_result === 1 ? 'text-success fw-bold' : (hour.actual_result === 0 ? 'text-danger fw-bold' : 'text-muted')">
|
||||
<i v-if="hour.actual_result !== null" :class="hour.actual_result === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ hour.actual_result === 1 ? 'UP' : (hour.actual_result === 0 ? 'DOWN' : '-') }}
|
||||
</td>
|
||||
|
||||
<!-- Ensemble -->
|
||||
<td class="table-primary bg-opacity-10">
|
||||
<span v-if="hour.models?.Ensemble"
|
||||
:class="hour.models.Ensemble.is_correct ? 'text-success' : 'text-danger'">
|
||||
<i :class="hour.models.Ensemble.prediction === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ hour.models.Ensemble.prediction === 1 ? 'UP' : 'DOWN' }}
|
||||
<i v-if="hour.actual_result !== null" :class="hour.models.Ensemble.is_correct ? 'bi-check-circle-fill' : 'bi-x-circle-fill'"></i>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<!-- DLinear -->
|
||||
<td>
|
||||
<span v-if="hour.models?.DLinear"
|
||||
:class="hour.models.DLinear.is_correct ? 'text-success' : 'text-danger'">
|
||||
<i :class="hour.models.DLinear.prediction === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ hour.models.DLinear.prediction === 1 ? 'UP' : 'DOWN' }}
|
||||
<i :class="hour.models.DLinear.is_correct ? 'bi-check-circle' : 'bi-x-circle'"></i>
|
||||
<span class="small text-muted">({{ (hour.models.DLinear.probability * 100).toFixed(0) }}%)</span>
|
||||
<i v-if="hour.actual_result !== null" :class="hour.models.DLinear.is_correct ? 'bi-check' : 'bi-x'"></i>
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
|
||||
<!-- N-BEATS -->
|
||||
<td>
|
||||
<span v-if="hour.models?.['N-BEATS']"
|
||||
:class="hour.models['N-BEATS'].is_correct ? 'text-success' : 'text-danger'">
|
||||
<i :class="hour.models['N-BEATS'].prediction === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
<i v-if="hour.actual_result !== null" :class="hour.models['N-BEATS'].is_correct ? 'bi-check' : 'bi-x'"></i>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<!-- TTM-R2 -->
|
||||
<td>
|
||||
<span v-if="hour.models?.['TTM-R2']"
|
||||
:class="hour.models['TTM-R2'].is_correct ? 'text-success' : 'text-danger'">
|
||||
<i :class="hour.models['TTM-R2'].prediction === 1 ? 'bi-arrow-up' : 'bi-arrow-down'"></i>
|
||||
{{ hour.models['TTM-R2'].prediction === 1 ? 'UP' : 'DOWN' }}
|
||||
<i :class="hour.models['TTM-R2'].is_correct ? 'bi-check-circle' : 'bi-x-circle'"></i>
|
||||
<i v-if="hour.actual_result !== null" :class="hour.models['TTM-R2'].is_correct ? 'bi-check' : 'bi-x'"></i>
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!hourlyModal.data?.length">
|
||||
<td colspan="4" class="text-center text-muted py-4">
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
暂无数据
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in new issue
Block a user