Add lean review issues: up to two problem tags plus short note.
Persist review_issues, enforce max two on save, and show issue breakdown in stats. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,8 +28,8 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>入场形式标注</h3>
|
||||
<form method="post" action="{{ url_for('record_annotate', tid=trade.id) }}">
|
||||
<h3>复盘标注</h3>
|
||||
<form method="post" action="{{ url_for('record_annotate', tid=trade.id) }}" id="annotate-form">
|
||||
<div class="form-row">
|
||||
<label>入场形式</label>
|
||||
<select name="entry_style">
|
||||
@@ -55,11 +55,23 @@
|
||||
</optgroup>
|
||||
{% endif %}
|
||||
</select>
|
||||
<p class="muted" style="margin:8px 0 0">顺势/反转均含:窄幅区间突破、宽幅区间假突破、波段高低点假突破;对不上的选「乱作」。</p>
|
||||
<p class="muted" style="margin:8px 0 0">管「按什么形进的」;对不上选乱作。</p>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>复盘备注</label>
|
||||
<textarea name="review_note" rows="5" placeholder="位不对 / 形不对 / 出场违纪 …">{{ trade.review_note or '' }}</textarea>
|
||||
<label>主要问题 <span class="muted">(最多 {{ review_issues_max }} 个,做对可不勾)</span></label>
|
||||
<div class="issue-checks" id="issue-checks" data-max="{{ review_issues_max }}">
|
||||
{% for iss in review_issues %}
|
||||
<label class="issue-chip">
|
||||
<input type="checkbox" name="review_issues" value="{{ iss }}"
|
||||
{% if iss in trade.review_issues_list %}checked{% endif %}>
|
||||
<span>{{ iss }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>短备注</label>
|
||||
<textarea name="review_note" rows="3" placeholder="补一句细节即可,如「假突破没等回踩」">{{ trade.review_note or '' }}</textarea>
|
||||
</div>
|
||||
<button class="btn" type="submit">保存标注</button>
|
||||
</form>
|
||||
@@ -103,4 +115,19 @@
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/charts.js') }}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var box = document.getElementById("issue-checks");
|
||||
if (!box) return;
|
||||
var maxN = parseInt(box.dataset.max || "2", 10) || 2;
|
||||
box.addEventListener("change", function (e) {
|
||||
var t = e.target;
|
||||
if (!t || t.type !== "checkbox") return;
|
||||
var checked = box.querySelectorAll('input[type="checkbox"]:checked');
|
||||
if (checked.length <= maxN) return;
|
||||
t.checked = false;
|
||||
alert("主要问题最多选 " + maxN + " 个");
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<th>账户</th>
|
||||
<th>机器</th>
|
||||
<th>入场形式</th>
|
||||
<th>主要问题</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -67,10 +68,17 @@
|
||||
<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="14" class="muted">暂无数据。{% if selected_machine_id %}当前机器下没有成交。{% else %}客户端上报成交后出现在此。{% endif %}</td></tr>
|
||||
<tr><td colspan="15" class="muted">暂无数据。{% if selected_machine_id %}当前机器下没有成交。{% else %}客户端上报成交后出现在此。{% endif %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="card">
|
||||
<div class="muted">入场形式标注</div>
|
||||
<div class="stat" style="font-size:22px">{{ stats.annotated }} <span class="muted" style="font-size:14px">/ {{ stats.total }}</span></div>
|
||||
<div class="muted" style="margin-top:6px">未标注 {{ stats.unannotated }}</div>
|
||||
<div class="muted" style="margin-top:6px">未标注 {{ stats.unannotated }} · 问题标注 {{ stats.issue_tagged }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -185,6 +185,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>按主要问题</h3>
|
||||
<p class="muted" style="margin:0 0 10px">一笔可勾 1~2 个问题,会分别计入对应行。</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>问题</th><th>笔数</th><th>胜率</th><th>盈亏</th><th>平均持仓</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for g in stats.by_issue %}
|
||||
<tr>
|
||||
<td>{{ g.label }}</td>
|
||||
<td>{{ g.count }}</td>
|
||||
<td>{{ '%.1f'|format(g.win_rate) }}%</td>
|
||||
<td style="color:{% if g.profit>0 %}var(--green){% elif g.profit<0 %}var(--red){% else %}inherit{% endif %}">
|
||||
{{ '%.2f'|format(g.profit) }}
|
||||
</td>
|
||||
<td class="muted">{{ g.avg_hold }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="muted">暂无问题标注</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>按机器</h3>
|
||||
<table>
|
||||
|
||||
Reference in New Issue
Block a user