Files
finance-talk/ft-app/app/templates/positions.html
T
2026-07-26 10:20:54 +08:00

214 lines
9.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}仓位管理{% endblock %}
{% block heading %}仓位管理{% endblock %}
{% block breadcrumb %}振幅锁仓 · 仓位跟踪{% endblock %}
{% block content %}
{% set error = request.query_params.get('error', '') %}
{% if error %}
<div style="background:var(--danger-bg);color:var(--danger-fg);padding:10px 16px;border-radius:6px;margin-bottom:16px;font-size:0.88rem;">{{ error }}</div>
{% endif %}
{% if not active_round %}
{# ═══════════════ 新建轮次 ═══════════════ #}
<div class="section-title">新建轮次</div>
<div class="form-card" style="margin-bottom:28px;">
<form method="post" action="/positions/round">
<div style="display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;">
<div class="form-group" style="margin-bottom:0;flex:1;min-width:140px;">
<label>合约</label>
<select name="contract_code" required>
<option value="">-- 选择合约 --</option>
{% for c in contracts %}
<option value="{{ c }}">{{ c }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="margin-bottom:0;flex:1;min-width:100px;">
<label>每日手数 N</label>
<input type="number" name="daily_hands" value="1" min="1" max="10" required>
</div>
<button type="submit" class="btn btn-primary">开始新轮</button>
</div>
</form>
</div>
<div style="text-align:center;padding:60px;color:var(--sub);">
<p style="font-size:1.1rem;margin-bottom:8px;">暂无活跃轮次</p>
<p style="font-size:0.82rem;">创建一轮新的交易,开始跟踪仓位和锁仓状态</p>
</div>
{% else %}
{# ═══════════════ 活跃轮次信息 ═══════════════ #}
{% set locks = active_round.total_locks %}
<div class="stat-grid">
<div class="stat-card">
<div class="label">合约</div>
<div class="value">{{ active_round.contract_code }}</div>
</div>
<div class="stat-card">
<div class="label">每日手数</div>
<div class="value">{{ active_round.daily_hands }} 手/天</div>
</div>
<div class="stat-card">
<div class="label">已开仓天数</div>
<div class="value">{{ active_round.positions|length }} 天</div>
</div>
<div class="stat-card" style="{% if locks >= 3 %}border-color:var(--danger);background:var(--danger-bg);{% endif %}">
<div class="label">累计锁仓</div>
<div class="value" style="{% if locks >= 3 %}color:var(--danger-fg);{% endif %}">
{{ locks }} / 3
{% if locks >= 3 %}⚠ 熔断{% endif %}
</div>
</div>
<div class="stat-card">
<div class="label">开始日期</div>
<div class="value" style="font-size:1rem;">{{ active_round.started_at }}</div>
</div>
</div>
{# ═══════════════ 止盈阈值提示 ═══════════════ #}
{% if active_round.positions|length > 0 %}
{% set latest = active_round.positions[-1] %}
<div style="background:var(--accent-light);border:1px solid var(--accent);border-radius:8px;padding:14px 18px;margin-bottom:24px;display:flex;align-items:center;gap:24px;flex-wrap:wrap;">
<div>
<span style="font-size:0.78rem;color:var(--sub);">止盈阈值</span>
<span style="font-weight:700;font-size:1.1rem;margin-left:8px;">{{ active_round.daily_hands }} × {{ latest.amp_threshold }} = {{ active_round.daily_hands * latest.amp_threshold }} 点</span>
</div>
<div>
<span style="font-size:0.78rem;color:var(--sub);">当前振幅 A</span>
<span style="font-weight:700;font-size:1.1rem;margin-left:8px;">{{ latest.amp_threshold }} 点</span>
</div>
<div>
<span style="font-size:0.78rem;color:var(--sub);">总手数</span>
<span style="font-weight:700;font-size:1.1rem;margin-left:8px;">{{ active_round.daily_hands * active_round.positions|length }}</span>
</div>
</div>
{% endif %}
{# ═══════════════ 添加仓位 ═══════════════ #}
<div class="section-title">添加当日仓位</div>
<div class="form-card" style="margin-bottom:28px;">
<form method="post" action="/positions/add" id="addForm">
<div style="display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;">
<div class="form-group" style="margin-bottom:0;flex:1;min-width:120px;">
<label>开仓价</label>
<input type="number" name="open_price" id="openPrice" required placeholder="如 1300" style="font-size:1rem;">
</div>
<div class="form-group" style="margin-bottom:0;flex:1;min-width:120px;">
<label>振幅阈值 A</label>
<input type="number" name="amp_threshold" id="ampThreshold" required placeholder="如 18" style="font-size:1rem;">
</div>
<div class="form-group" style="margin-bottom:0;flex:1;min-width:80px;">
<label>手数</label>
<input type="number" name="hands" value="1" min="1" max="10" required>
</div>
<div style="display:flex;flex-direction:column;align-items:center;min-width:100px;">
<span style="font-size:0.75rem;color:var(--sub);margin-bottom:4px;">锁仓价位</span>
<span id="lockPricePreview" style="font-size:1.3rem;font-weight:700;color:var(--danger-fg);"></span>
</div>
<button type="submit" class="btn btn-primary">添加仓位</button>
</div>
</form>
</div>
{# ═══════════════ 仓位列表 ═══════════════ #}
<div class="section-title">本轮仓位</div>
{% if active_round.positions %}
<div class="table-wrap">
<table>
<tr>
<th>日期</th>
<th>开仓价</th>
<th>振幅 A</th>
<th>锁仓价位</th>
<th>手数</th>
<th>已锁</th>
<th>状态</th>
<th>操作</th>
</tr>
{% for p in active_round.positions|reverse %}
<tr>
<td><strong>{{ p.opened_at }}</strong></td>
<td>{{ p.open_price }}</td>
<td>{{ p.amp_threshold }}</td>
<td style="color:var(--danger-fg);font-weight:600;">{{ p.lock_price }}</td>
<td>{{ p.hands }}</td>
<td>
{% if p.locked_count > 0 %}
<span class="badge badge-warn">{{ p.locked_count }}/{{ p.hands }}</span>
{% else %}
<span class="na">0/{{ p.hands }}</span>
{% endif %}
</td>
<td>
{% if p.locked_count >= p.hands %}
<span class="badge badge-down">已全锁</span>
{% elif p.locked_count > 0 %}
<span class="badge badge-warn">部分锁仓</span>
{% else %}
<span class="badge badge-up">活跃</span>
{% endif %}
</td>
<td>
<div style="display:flex;gap:8px;justify-content:center;">
<form method="post" action="/positions/{{ p.id }}/lock" style="display:inline;">
<button style="background:var(--warn-bg);color:var(--warn-fg);border:1px solid var(--warn);padding:3px 10px;border-radius:4px;cursor:pointer;font-size:0.78rem;font-weight:600;"
{% if p.locked_count >= p.hands %}disabled style="opacity:0.4;cursor:default;"{% endif %}>
🔒 锁
</button>
</form>
<form method="post" action="/positions/{{ p.id }}/unlock" style="display:inline;">
<button style="background:var(--surface);color:var(--sub);border:1px solid var(--border);padding:3px 10px;border-radius:4px;cursor:pointer;font-size:0.78rem;"
{% if p.locked_count == 0 %}disabled style="opacity:0.4;cursor:default;"{% endif %}>
撤销
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div style="text-align:center;padding:40px;color:var(--sub);">暂无仓位,请添加当日仓位。</div>
{% endif %}
{# ═══════════════ 结束轮次 ═══════════════ #}
<div style="margin-top:28px;padding:20px;background:var(--surface);border:1px solid var(--border);border-radius:10px;display:flex;align-items:center;gap:16px;">
<span style="font-size:0.88rem;font-weight:600;">结束本轮:</span>
<form method="post" action="/positions/close" style="display:inline;">
<input type="hidden" name="result" value="profit_taken">
<button type="submit" class="btn" style="background:var(--success);color:#fff;" onclick="return confirm('确认止盈清仓?')">🟢 止盈</button>
</form>
<form method="post" action="/positions/close" style="display:inline;">
<input type="hidden" name="result" value="meltdown">
<button type="submit" class="btn" style="background:var(--danger);color:#fff;" onclick="return confirm('确认熔断清仓?')">🔴 熔断</button>
</form>
</div>
{% endif %}
<script>
var openPrice = document.getElementById('openPrice');
var ampThreshold = document.getElementById('ampThreshold');
var preview = document.getElementById('lockPricePreview');
function updatePreview() {
var op = parseInt(openPrice.value) || 0;
var at = parseInt(ampThreshold.value) || 0;
if (op > 0 && at > 0) {
preview.textContent = (op + at) + ' 点';
} else {
preview.textContent = '—';
}
}
if (openPrice && ampThreshold) {
openPrice.addEventListener('input', updatePreview);
ampThreshold.addEventListener('input', updatePreview);
}
</script>
{% endblock %}