828038a5ae
Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
5.9 KiB
HTML
149 lines
5.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}交易复盘 · {{ app_name }}{% endblock %}
|
|
{% block content %}
|
|
<h2 style="margin:0 0 6px;display:flex;align-items:center;gap:10px;flex-wrap:wrap">
|
|
交易记录与复盘
|
|
<span id="records-live-hint" class="tag" style="font-size:12px;font-weight:500">自动刷新中…</span>
|
|
</h2>
|
|
<p class="muted" style="margin:0 0 10px">按机器筛选 · 每页 {{ per_page }} 条 · 北京时间 · 成交变化会自动刷新 · 已隐藏对账/去重影子记录</p>
|
|
|
|
<div style="display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap">
|
|
<a class="btn" href="{{ url_for('records_page', machine_id=selected_machine_id) if selected_machine_id else url_for('records_page') }}">交易列表</a>
|
|
<a class="btn secondary" href="{{ url_for('records_stats_page', machine_id=selected_machine_id) if selected_machine_id else url_for('records_stats_page') }}">统计看板</a>
|
|
</div>
|
|
|
|
<div class="card" style="margin-bottom:14px">
|
|
<form method="get" action="{{ url_for('records_page') }}" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">
|
|
<label class="muted" for="machine_id" style="margin:0">机器</label>
|
|
<select name="machine_id" id="machine_id" style="min-width:200px;width:auto">
|
|
<option value="">全部机器</option>
|
|
{% for m in machines %}
|
|
<option value="{{ m.id }}" {% if selected_machine_id == m.id %}selected{% endif %}>
|
|
{{ m.name }} #{{ m.id }}{% if m.online %} · 在线{% endif %}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<input type="hidden" name="page" value="1">
|
|
<button type="submit" class="btn">筛选</button>
|
|
{% if selected_machine_id %}
|
|
<a class="btn secondary" href="{{ url_for('records_page') }}">清除</a>
|
|
{% endif %}
|
|
<span class="muted">共 {{ total }} 条 · 第 {{ page }}/{{ total_pages }} 页</span>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card" style="overflow-x:auto">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>品种</th>
|
|
<th>方向</th>
|
|
<th>手数</th>
|
|
<th>开仓价</th>
|
|
<th>平仓价</th>
|
|
<th>盈亏</th>
|
|
<th>开仓时间</th>
|
|
<th>平仓时间</th>
|
|
<th>持仓时间</th>
|
|
<th>账户</th>
|
|
<th>机器</th>
|
|
<th>入场形式</th>
|
|
<th>主要问题</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for t in trades %}
|
|
<tr>
|
|
<td>{{ t.id }}</td>
|
|
<td>{{ t.symbol }}</td>
|
|
<td><span class="tag {% if t.side=='buy' %}green{% else %}red{% endif %}">{% if t.side=='buy' %}买入{% elif t.side=='sell' %}卖出{% else %}{{ t.side }}{% endif %}</span></td>
|
|
<td>{{ t.volume }}</td>
|
|
<td>{{ t.open_price|fmt_price }}</td>
|
|
<td>{{ t.close_price|fmt_price }}</td>
|
|
<td style="color:{% if t.profit and t.profit>0 %}var(--green){% elif t.profit and t.profit<0 %}var(--red){% else %}inherit{% endif %}">
|
|
{{ t.profit|fmt_money }}
|
|
</td>
|
|
<td class="muted" style="white-space:nowrap">{{ t.open_time_fmt }}</td>
|
|
<td class="muted" style="white-space:nowrap">{{ t.close_time_fmt }}</td>
|
|
<td style="white-space:nowrap;font-weight:600;color:var(--accent)">{{ t.hold_duration }}</td>
|
|
<td>{{ t.account_name or '—' }}</td>
|
|
<td>{{ t.machine_name or '—' }}</td>
|
|
<td>{{ t.entry_style or '—' }}</td>
|
|
<td>
|
|
{% if t.review_issues_list %}
|
|
{% for iss in t.review_issues_list %}<span class="tag red" style="margin:1px 2px">{{ iss }}</span>{% endfor %}
|
|
{% else %}
|
|
<span class="muted">—</span>
|
|
{% endif %}
|
|
</td>
|
|
<td><a href="{{ url_for('record_detail', tid=t.id) }}">打开复盘</a></td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="15" class="muted">暂无数据。{% if selected_machine_id %}当前机器下没有成交。{% else %}客户端上报成交后出现在此。{% endif %}</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
{% if total_pages > 1 %}
|
|
<div style="display:flex;gap:10px;align-items:center;justify-content:flex-end;margin-top:14px;flex-wrap:wrap">
|
|
{% if page > 1 %}
|
|
<a class="btn secondary" href="{{ url_for('records_page', page=page-1, machine_id=selected_machine_id) if selected_machine_id else url_for('records_page', page=page-1) }}">上一页</a>
|
|
{% else %}
|
|
<span class="btn secondary" style="opacity:.4;pointer-events:none">上一页</span>
|
|
{% endif %}
|
|
<span class="muted">{{ page }} / {{ total_pages }}</span>
|
|
{% if page < total_pages %}
|
|
<a class="btn secondary" href="{{ url_for('records_page', page=page+1, machine_id=selected_machine_id) if selected_machine_id else url_for('records_page', page=page+1) }}">下一页</a>
|
|
{% else %}
|
|
<span class="btn secondary" style="opacity:.4;pointer-events:none">下一页</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
(function () {
|
|
var fpUrl = {{ url_for('records_fingerprint')|tojson }};
|
|
var machineId = {{ (selected_machine_id or '')|tojson }};
|
|
var hint = document.getElementById('records-live-hint');
|
|
var lastFp = null;
|
|
var q = machineId ? ('?machine_id=' + encodeURIComponent(machineId)) : '';
|
|
|
|
function setHint(text, kind) {
|
|
if (!hint) return;
|
|
hint.textContent = text;
|
|
hint.className = 'tag' + (kind ? ' ' + kind : '');
|
|
}
|
|
|
|
function tick() {
|
|
fetch(fpUrl + q, { credentials: 'same-origin' })
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (d) {
|
|
if (!d || !d.ok) return;
|
|
if (lastFp === null) {
|
|
lastFp = d.fp;
|
|
setHint('自动刷新 · 监听中', 'green');
|
|
return;
|
|
}
|
|
if (d.fp !== lastFp) {
|
|
setHint('有新成交,刷新中…', 'green');
|
|
location.reload();
|
|
return;
|
|
}
|
|
setHint('自动刷新 · 监听中', 'green');
|
|
})
|
|
.catch(function () {
|
|
setHint('自动刷新暂时不可用', '');
|
|
});
|
|
}
|
|
|
|
tick();
|
|
setInterval(tick, 3000);
|
|
})();
|
|
</script>
|
|
{% endblock %}
|