Add personal license agreement and rename product section to tradable symbols.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
|
||||
+48
-47
@@ -1,47 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}品种简介 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card profile-page">
|
||||
<h2>品种简介</h2>
|
||||
<div class="card-body">
|
||||
<form id="contract-search-form" class="form-row" method="get" action="{{ url_for('contract_profile_page') }}">
|
||||
<div class="symbol-wrap" style="flex:1;min-width:220px;max-width:360px">
|
||||
<input type="text" class="symbol-input" id="contract-symbol-input"
|
||||
placeholder="输入品种或合约,如 螺纹钢 / rb2510" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" id="contract-symbol-hidden" value="{{ symbol or '' }}">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">查询</button>
|
||||
</form>
|
||||
<p class="hint">展示交易所合约规格:交易单位、最小变动、保证金、交割规则等(数据来源:东方财富 / 新浪)。</p>
|
||||
|
||||
{% if error %}
|
||||
<div class="flash" style="margin-top:1rem">{{ error }}</div>
|
||||
{% elif profile %}
|
||||
<div class="profile-head">
|
||||
<strong>{{ profile.symbol_name or profile.ths_code }}</strong>
|
||||
<span class="text-muted">{{ profile.ths_code }}</span>
|
||||
{% if profile.exchange %}<span class="badge active">{{ profile.exchange }}</span>{% endif %}
|
||||
<span class="profile-source">来源:{{ profile.source }}</span>
|
||||
</div>
|
||||
<div class="profile-spec">
|
||||
{% for row in profile.rows %}
|
||||
<div class="profile-row">
|
||||
<div class="profile-label">{{ row.label }}</div>
|
||||
<div class="profile-value">
|
||||
{{ row.value }}
|
||||
{% if row.hint %}<div class="profile-hint">{{ row.hint }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif symbol %}
|
||||
<p class="text-muted" style="margin-top:1rem">未查询到该合约简介,请检查合约代码是否正确。</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/contract.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}品种简介 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card profile-page">
|
||||
<h2>品种简介</h2>
|
||||
<div class="card-body">
|
||||
<form id="contract-search-form" class="form-row" method="get" action="{{ url_for('contract_profile_page') }}">
|
||||
<div class="symbol-wrap" style="flex:1;min-width:220px;max-width:360px">
|
||||
<input type="text" class="symbol-input" id="contract-symbol-input"
|
||||
placeholder="输入品种或合约,如 螺纹钢 / rb2510" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" id="contract-symbol-hidden" value="{{ symbol or '' }}">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">查询</button>
|
||||
</form>
|
||||
<p class="hint">展示交易所合约规格:交易单位、最小变动、保证金、交割规则等(数据来源:东方财富 / 新浪)。</p>
|
||||
|
||||
{% if error %}
|
||||
<div class="flash" style="margin-top:1rem">{{ error }}</div>
|
||||
{% elif profile %}
|
||||
<div class="profile-head">
|
||||
<strong>{{ profile.symbol_name or profile.ths_code }}</strong>
|
||||
<span class="text-muted">{{ profile.ths_code }}</span>
|
||||
{% if profile.exchange %}<span class="badge active">{{ profile.exchange }}</span>{% endif %}
|
||||
<span class="profile-source">来源:{{ profile.source }}</span>
|
||||
</div>
|
||||
<div class="profile-spec">
|
||||
{% for row in profile.rows %}
|
||||
<div class="profile-row">
|
||||
<div class="profile-label">{{ row.label }}</div>
|
||||
<div class="profile-value">
|
||||
{{ row.value }}
|
||||
{% if row.hint %}<div class="profile-hint">{{ row.hint }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif symbol %}
|
||||
<p class="text-muted" style="margin-top:1rem">未查询到该合约简介,请检查合约代码是否正确。</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/contract.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
+121
-120
@@ -1,120 +1,121 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}手续费配置 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.fees-status-card .card-body{display:flex;flex-wrap:wrap;gap:.75rem 1.25rem;align-items:center}
|
||||
.fees-status-card .fees-meta{font-size:.85rem;color:var(--text-muted)}
|
||||
.fees-table-card .card-body{padding:.75rem 1rem 1rem}
|
||||
.fees-table-card .trade-table-wrap{
|
||||
max-height:min(70vh,560px);
|
||||
width:100%;
|
||||
overflow:auto;
|
||||
-webkit-overflow-scrolling:touch;
|
||||
border:1px solid var(--table-border);
|
||||
border-radius:10px;
|
||||
background:var(--card-inner);
|
||||
}
|
||||
.fees-table-card .trade-table{
|
||||
width:100%;
|
||||
min-width:0;
|
||||
table-layout:fixed;
|
||||
font-size:.8rem;
|
||||
}
|
||||
.fees-table-card .trade-table thead th{
|
||||
position:sticky;
|
||||
top:0;
|
||||
z-index:2;
|
||||
background:var(--card-inner);
|
||||
box-shadow:0 1px 0 var(--table-border);
|
||||
}
|
||||
.fees-table-card .trade-table th,
|
||||
.fees-table-card .trade-table td{
|
||||
padding:.5rem .4rem;
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.fees-table-card .trade-table th:last-child,
|
||||
.fees-table-card .trade-table td:last-child{
|
||||
position:static;
|
||||
box-shadow:none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card fees-status-card">
|
||||
<h2>CTP 手续费</h2>
|
||||
<div class="card-body">
|
||||
<p class="fees-meta" style="margin:0;flex:1;min-width:220px">
|
||||
费率由后台从 <strong>CTP 柜台</strong> 同步写入数据库,<strong>每日自动更新一次</strong>,本页只读展示。
|
||||
</p>
|
||||
{% if ctp_connected %}
|
||||
<span class="badge profit">CTP 已连接</span>
|
||||
{% else %}
|
||||
<span class="badge planned">CTP 未连接</span>
|
||||
{% endif %}
|
||||
{% if fee_synced_today %}
|
||||
<span class="badge profit">今日已同步</span>
|
||||
{% else %}
|
||||
<span class="badge planned">今日未同步</span>
|
||||
{% endif %}
|
||||
{% if fee_last_sync %}
|
||||
<span class="text-muted" style="font-size:.8rem">上次:{{ fee_last_sync[:16] }}</span>
|
||||
{% endif %}
|
||||
{% if fee_sync_running %}
|
||||
<span class="badge planned">同步中…</span>
|
||||
{% endif %}
|
||||
{% if fee_counts.get('ctp') %}
|
||||
<span class="text-muted" style="font-size:.8rem">共 {{ fee_counts.ctp }} 个品种</span>
|
||||
{% endif %}
|
||||
<form action="{{ url_for('fees') }}" method="post" style="display:inline">
|
||||
<input type="hidden" name="action" value="sync_ctp">
|
||||
<input type="hidden" name="force" value="1">
|
||||
<button type="submit" class="btn-primary" {% if not ctp_connected %}disabled title="请先连接 CTP"{% endif %}>立即同步</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card fees-table-card">
|
||||
<h2>品种费率表</h2>
|
||||
<div class="card-body">
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品种</th><th>交易所</th><th>乘数</th>
|
||||
<th>开仓(元/手)</th><th>开仓(比例)</th>
|
||||
<th>平昨(元/手)</th><th>平昨(比例)</th>
|
||||
<th>平今(元/手)</th><th>平今(比例)</th>
|
||||
<th>更新</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rates %}
|
||||
<tr>
|
||||
<td><strong>{{ r.product }}</strong></td>
|
||||
<td class="cell-readonly">{{ r.exchange or '—' }}</td>
|
||||
<td class="cell-readonly">{{ r.mult }}</td>
|
||||
<td class="cell-readonly">{{ r.open_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.open_ratio }}</td>
|
||||
<td class="cell-readonly">{{ r.close_yesterday_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.close_yesterday_ratio }}</td>
|
||||
<td class="cell-readonly">{{ r.close_today_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.close_today_ratio }}</td>
|
||||
<td class="text-muted" style="font-size:.72rem">{{ (r.updated_at or '')[:16] }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="10" class="text-muted">暂无 CTP 费率,请连接 CTP 后等待自动同步或点击「立即同步」</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top:.75rem;padding:0 1rem 1rem">
|
||||
公式:单边 = 固定(元/手)×手数 + 比例×价格×乘数×手数;往返 = 开仓 + 平仓(平今/平昨自动判断)。
|
||||
{% if ctp_connected and not fee_counts.get('ctp') %}
|
||||
<br><strong class="text-loss">数据库尚无 CTP 费率,请点击「立即同步」或等待后台每日任务。</strong>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}手续费配置 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.fees-status-card .card-body{display:flex;flex-wrap:wrap;gap:.75rem 1.25rem;align-items:center}
|
||||
.fees-status-card .fees-meta{font-size:.85rem;color:var(--text-muted)}
|
||||
.fees-table-card .card-body{padding:.75rem 1rem 1rem}
|
||||
.fees-table-card .trade-table-wrap{
|
||||
max-height:min(70vh,560px);
|
||||
width:100%;
|
||||
overflow:auto;
|
||||
-webkit-overflow-scrolling:touch;
|
||||
border:1px solid var(--table-border);
|
||||
border-radius:10px;
|
||||
background:var(--card-inner);
|
||||
}
|
||||
.fees-table-card .trade-table{
|
||||
width:100%;
|
||||
min-width:0;
|
||||
table-layout:fixed;
|
||||
font-size:.8rem;
|
||||
}
|
||||
.fees-table-card .trade-table thead th{
|
||||
position:sticky;
|
||||
top:0;
|
||||
z-index:2;
|
||||
background:var(--card-inner);
|
||||
box-shadow:0 1px 0 var(--table-border);
|
||||
}
|
||||
.fees-table-card .trade-table th,
|
||||
.fees-table-card .trade-table td{
|
||||
padding:.5rem .4rem;
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.fees-table-card .trade-table th:last-child,
|
||||
.fees-table-card .trade-table td:last-child{
|
||||
position:static;
|
||||
box-shadow:none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card fees-status-card">
|
||||
<h2>CTP 手续费</h2>
|
||||
<div class="card-body">
|
||||
<p class="fees-meta" style="margin:0;flex:1;min-width:220px">
|
||||
费率由后台从 <strong>CTP 柜台</strong> 同步写入数据库,<strong>每日自动更新一次</strong>,本页只读展示。
|
||||
</p>
|
||||
{% if ctp_connected %}
|
||||
<span class="badge profit">CTP 已连接</span>
|
||||
{% else %}
|
||||
<span class="badge planned">CTP 未连接</span>
|
||||
{% endif %}
|
||||
{% if fee_synced_today %}
|
||||
<span class="badge profit">今日已同步</span>
|
||||
{% else %}
|
||||
<span class="badge planned">今日未同步</span>
|
||||
{% endif %}
|
||||
{% if fee_last_sync %}
|
||||
<span class="text-muted" style="font-size:.8rem">上次:{{ fee_last_sync[:16] }}</span>
|
||||
{% endif %}
|
||||
{% if fee_sync_running %}
|
||||
<span class="badge planned">同步中…</span>
|
||||
{% endif %}
|
||||
{% if fee_counts.get('ctp') %}
|
||||
<span class="text-muted" style="font-size:.8rem">共 {{ fee_counts.ctp }} 个品种</span>
|
||||
{% endif %}
|
||||
<form action="{{ url_for('fees') }}" method="post" style="display:inline">
|
||||
<input type="hidden" name="action" value="sync_ctp">
|
||||
<input type="hidden" name="force" value="1">
|
||||
<button type="submit" class="btn-primary" {% if not ctp_connected %}disabled title="请先连接 CTP"{% endif %}>立即同步</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card fees-table-card">
|
||||
<h2>品种费率表</h2>
|
||||
<div class="card-body">
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品种</th><th>交易所</th><th>乘数</th>
|
||||
<th>开仓(元/手)</th><th>开仓(比例)</th>
|
||||
<th>平昨(元/手)</th><th>平昨(比例)</th>
|
||||
<th>平今(元/手)</th><th>平今(比例)</th>
|
||||
<th>更新</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rates %}
|
||||
<tr>
|
||||
<td><strong>{{ r.product }}</strong></td>
|
||||
<td class="cell-readonly">{{ r.exchange or '—' }}</td>
|
||||
<td class="cell-readonly">{{ r.mult }}</td>
|
||||
<td class="cell-readonly">{{ r.open_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.open_ratio }}</td>
|
||||
<td class="cell-readonly">{{ r.close_yesterday_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.close_yesterday_ratio }}</td>
|
||||
<td class="cell-readonly">{{ r.close_today_fixed }}</td>
|
||||
<td class="cell-readonly">{{ r.close_today_ratio }}</td>
|
||||
<td class="text-muted" style="font-size:.72rem">{{ (r.updated_at or '')[:16] }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="10" class="text-muted">暂无 CTP 费率,请连接 CTP 后等待自动同步或点击「立即同步」</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top:.75rem;padding:0 1rem 1rem">
|
||||
公式:单边 = 固定(元/手)×手数 + 比例×价格×乘数×手数;往返 = 开仓 + 平仓(平今/平昨自动判断)。
|
||||
{% if ctp_connected and not fee_counts.get('ctp') %}
|
||||
<br><strong class="text-loss">数据库尚无 CTP 费率,请点击「立即同步」或等待后台每日任务。</strong>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
+86
-85
@@ -1,85 +1,86 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}关键位监控 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>新增监控</h2>
|
||||
<div class="card-body">
|
||||
<form action="{{ url_for('add_key') }}" method="post" class="form-compact">
|
||||
<div class="form-line line-3">
|
||||
<div class="symbol-wrap symbol-mains">
|
||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" required>
|
||||
<input type="hidden" name="symbol_name">
|
||||
<input type="hidden" name="market_code" required>
|
||||
<input type="hidden" name="sina_code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<select name="type" required>
|
||||
<option value="箱体突破">箱体突破</option>
|
||||
<option value="收敛突破">收敛突破</option>
|
||||
<option value="关键阻力位">关键阻力位</option>
|
||||
<option value="关键支撑位">关键支撑位</option>
|
||||
</select>
|
||||
<select name="direction" required>
|
||||
<option value="">方向</option>
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="upper" type="number" step="0.0001" placeholder="上沿/阻力" required>
|
||||
<input name="lower" type="number" step="0.0001" placeholder="下沿/支撑" required>
|
||||
<button type="submit" class="btn-primary">添加</button>
|
||||
</div>
|
||||
</form>
|
||||
<h3 class="section-label">监控列表</h3>
|
||||
<div class="list card-scroll" id="key-monitor-list">
|
||||
{% for k in keys %}
|
||||
<div class="list-item key-item" data-key-id="{{ k.id }}" style="padding:.75rem;font-size:.85rem">
|
||||
<div>
|
||||
<strong>{{ k.symbol_name or k.symbol }}</strong> {{ k.monitor_type }}
|
||||
<span class="badge dir">{{ '多' if k.direction == 'long' else '空' }}</span>
|
||||
</div>
|
||||
<div class="key-live">
|
||||
<span class="live-price-line">现价:<span class="live-price">--</span></span>
|
||||
<span class="live-dist">距上<span class="dist-up">--</span> 距下<span class="dist-down">--</span></span>
|
||||
</div>
|
||||
<div>上{{ k.upper }} 下{{ k.lower }}</div>
|
||||
<a href="{{ url_for('del_key', pid=k.id) }}" class="btn-del" onclick="return confirm('移入历史?')">删</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-hint">暂无监控</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>监控历史</h2>
|
||||
<div class="card-body card-scroll">
|
||||
<table>
|
||||
<thead><tr><th>品种</th><th>类型</th><th>方向</th><th>上沿</th><th>下沿</th><th>归档</th></tr></thead>
|
||||
<tbody>
|
||||
{% for k in history %}
|
||||
<tr>
|
||||
<td>{{ k.symbol_name or k.symbol }}</td>
|
||||
<td>{{ k.monitor_type }}</td>
|
||||
<td><span class="badge dir">{{ '多' if k.direction == 'long' else '空' }}</span></td>
|
||||
<td>{{ k.upper }}</td>
|
||||
<td>{{ k.lower }}</td>
|
||||
<td>{{ k.archived_at[:16] if k.archived_at else '' }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="text-muted">暂无历史</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/keys.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}关键位监控 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>新增监控</h2>
|
||||
<div class="card-body">
|
||||
<form action="{{ url_for('add_key') }}" method="post" class="form-compact">
|
||||
<div class="form-line line-3">
|
||||
<div class="symbol-wrap symbol-mains">
|
||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" required>
|
||||
<input type="hidden" name="symbol_name">
|
||||
<input type="hidden" name="market_code" required>
|
||||
<input type="hidden" name="sina_code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<select name="type" required>
|
||||
<option value="箱体突破">箱体突破</option>
|
||||
<option value="收敛突破">收敛突破</option>
|
||||
<option value="关键阻力位">关键阻力位</option>
|
||||
<option value="关键支撑位">关键支撑位</option>
|
||||
</select>
|
||||
<select name="direction" required>
|
||||
<option value="">方向</option>
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="upper" type="number" step="0.0001" placeholder="上沿/阻力" required>
|
||||
<input name="lower" type="number" step="0.0001" placeholder="下沿/支撑" required>
|
||||
<button type="submit" class="btn-primary">添加</button>
|
||||
</div>
|
||||
</form>
|
||||
<h3 class="section-label">监控列表</h3>
|
||||
<div class="list card-scroll" id="key-monitor-list">
|
||||
{% for k in keys %}
|
||||
<div class="list-item key-item" data-key-id="{{ k.id }}" style="padding:.75rem;font-size:.85rem">
|
||||
<div>
|
||||
<strong>{{ k.symbol_name or k.symbol }}</strong> {{ k.monitor_type }}
|
||||
<span class="badge dir">{{ '多' if k.direction == 'long' else '空' }}</span>
|
||||
</div>
|
||||
<div class="key-live">
|
||||
<span class="live-price-line">现价:<span class="live-price">--</span></span>
|
||||
<span class="live-dist">距上<span class="dist-up">--</span> 距下<span class="dist-down">--</span></span>
|
||||
</div>
|
||||
<div>上{{ k.upper }} 下{{ k.lower }}</div>
|
||||
<a href="{{ url_for('del_key', pid=k.id) }}" class="btn-del" onclick="return confirm('移入历史?')">删</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-hint">暂无监控</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>监控历史</h2>
|
||||
<div class="card-body card-scroll">
|
||||
<table>
|
||||
<thead><tr><th>品种</th><th>类型</th><th>方向</th><th>上沿</th><th>下沿</th><th>归档</th></tr></thead>
|
||||
<tbody>
|
||||
{% for k in history %}
|
||||
<tr>
|
||||
<td>{{ k.symbol_name or k.symbol }}</td>
|
||||
<td>{{ k.monitor_type }}</td>
|
||||
<td><span class="badge dir">{{ '多' if k.direction == 'long' else '空' }}</span></td>
|
||||
<td>{{ k.upper }}</td>
|
||||
<td>{{ k.lower }}</td>
|
||||
<td>{{ k.archived_at[:16] if k.archived_at else '' }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="text-muted">暂无历史</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/keys.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
|
||||
+140
-139
@@ -1,139 +1,140 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}行情K线 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="card market-card">
|
||||
<h2>行情 K 线</h2>
|
||||
<form class="market-toolbar" id="market-form" onsubmit="return false;">
|
||||
<div class="symbol-wrap market-symbol-wrap">
|
||||
<input type="text" class="symbol-input" id="market-symbol-input" placeholder="点击选择主力合约,或输入搜索" autocomplete="off" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol" id="market-symbol-hidden" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol_name" id="market-symbol-name">
|
||||
<input type="hidden" name="market_code" id="market-market-code">
|
||||
<input type="hidden" name="sina_code" id="market-sina-code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected" id="market-symbol-selected"></div>
|
||||
</div>
|
||||
<div class="market-period-tabs" id="market-period-tabs">
|
||||
{% for p in market_periods %}
|
||||
<button type="button" class="period-tab{% if p.key == period %} active{% endif %}" data-period="{{ p.key }}">{{ p.label }}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" class="btn-primary" id="market-load-btn">查看</button>
|
||||
</form>
|
||||
<div class="market-quote" id="market-quote">
|
||||
<span class="market-quote-name" id="market-quote-name">—</span>
|
||||
<span class="market-quote-price" id="market-quote-price">—</span>
|
||||
<span class="market-quote-prev text-muted" id="market-quote-prev"></span>
|
||||
<span class="market-quote-meta text-muted" id="market-quote-meta"></span>
|
||||
</div>
|
||||
<div class="market-chart-toolbar">
|
||||
<div class="market-chart-options">
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-prev-close">昨收线</label>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-ma">均线 21/55</label>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-gap-day">间隔日</label>
|
||||
</div>
|
||||
<div class="market-chart-zoom">
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-in" title="放大">+</button>
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-out" title="缩小">-</button>
|
||||
<button type="button" class="chart-zoom-btn chart-zoom-reset" id="chart-zoom-reset">重置</button>
|
||||
</div>
|
||||
<span class="market-refresh-hint text-muted" id="market-refresh-hint"></span>
|
||||
</div>
|
||||
<div class="market-chart-wrap" id="market-chart-wrap">
|
||||
<div id="market-chart" class="market-chart" aria-label="K线图"></div>
|
||||
<div class="market-chart-empty" id="market-chart-empty">请选择合约并点击「查看」</div>
|
||||
<div class="market-chart-loading" id="market-chart-loading">连接中…</div>
|
||||
</div>
|
||||
<p class="hint">图表引擎:TradingView Lightweight Charts(红跌绿涨)。数据来源:{% if ctp_connected %}报价 CTP;K 线历史新浪补齐、最新 bar 由 CTP tick 更新{% else %}CTP 未连接时回退新浪{% endif %}。滚轮缩放、拖拽平移;勾选「间隔日」可压缩夜盘空白。</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.market-card{overflow:visible}
|
||||
.market-card h2{margin-bottom:.75rem}
|
||||
.market-toolbar{
|
||||
display:flex;flex-wrap:wrap;gap:.65rem;align-items:flex-end;
|
||||
margin-bottom:.75rem;position:relative;z-index:2;
|
||||
min-height:2.5rem;
|
||||
}
|
||||
.market-symbol-wrap{flex:1;min-width:200px;max-width:360px;z-index:3}
|
||||
.market-symbol-wrap .symbol-dropdown{max-height:min(70vh,420px)}
|
||||
.market-symbol-wrap .symbol-selected{display:none}
|
||||
.market-period-tabs{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
|
||||
.period-tab{
|
||||
padding:.4rem .65rem;border-radius:999px;
|
||||
border:1px solid var(--input-border);
|
||||
background:var(--toggle-bg);color:var(--text-muted);
|
||||
font-size:.78rem;cursor:pointer;width:auto;white-space:nowrap;
|
||||
}
|
||||
.period-tab:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.period-tab.active{
|
||||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||||
border-color:transparent;color:#fff;
|
||||
}
|
||||
#market-load-btn{width:auto;padding:.55rem 1.25rem;font-size:.85rem}
|
||||
.market-quote{
|
||||
display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem 1rem;
|
||||
margin-bottom:.75rem;padding:.65rem .85rem;
|
||||
background:var(--card-inner);border-radius:10px;border:1px solid var(--card-border);
|
||||
min-height:2.75rem;
|
||||
}
|
||||
.market-quote-name{font-weight:600;color:var(--text-title)}
|
||||
.market-quote-price{font-size:1.35rem;font-weight:700;color:var(--accent);font-variant-numeric:tabular-nums}
|
||||
.market-quote-prev{font-size:.78rem}
|
||||
.market-chart-toolbar{
|
||||
display:flex;align-items:center;justify-content:space-between;gap:.75rem;
|
||||
margin-bottom:.5rem;flex-wrap:wrap;
|
||||
min-height:2rem;
|
||||
}
|
||||
.market-chart-options{display:flex;flex-wrap:wrap;gap:.5rem .85rem;align-items:center}
|
||||
.chart-opt{
|
||||
display:flex;align-items:center;gap:.35rem;font-size:.78rem;
|
||||
color:var(--text-muted);cursor:pointer;user-select:none;
|
||||
}
|
||||
.chart-opt input{width:auto;margin:0;cursor:pointer}
|
||||
.market-chart-zoom{display:flex;gap:.35rem;align-items:center}
|
||||
.chart-zoom-btn{
|
||||
width:32px;height:32px;padding:0;border-radius:8px;
|
||||
border:1px solid var(--input-border);background:var(--toggle-bg);
|
||||
color:var(--text-primary);font-size:1rem;line-height:1;cursor:pointer;
|
||||
}
|
||||
.chart-zoom-btn:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.chart-zoom-reset{width:auto;padding:0 .65rem;font-size:.75rem}
|
||||
.market-refresh-hint{font-size:.72rem}
|
||||
.market-chart-wrap{
|
||||
position:relative;border-radius:12px;border:1px solid var(--card-border);
|
||||
background:var(--card-inner);
|
||||
height:min(68vh,560px);min-height:420px;
|
||||
}
|
||||
.market-chart{width:100%;height:100%}
|
||||
.market-chart-empty,
|
||||
.market-chart-loading{
|
||||
position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
|
||||
color:var(--text-muted);font-size:.9rem;pointer-events:none;
|
||||
}
|
||||
.market-chart-loading{display:none}
|
||||
html[data-theme="light"] .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.has-data .market-chart-empty{display:none}
|
||||
.market-chart-wrap.loading .market-chart-loading{
|
||||
display:flex;background:rgba(10,12,20,.35);
|
||||
}
|
||||
html[data-theme="light"] .market-chart-wrap.loading .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.loading .market-chart-empty{display:none}
|
||||
@media(max-width:767px){
|
||||
.market-toolbar{align-items:stretch}
|
||||
.market-symbol-wrap{max-width:none}
|
||||
.market-period-tabs{order:3;width:100%}
|
||||
#market-load-btn{order:4;width:100%}
|
||||
.market-chart-wrap{min-height:300px;height:50vh}
|
||||
.market-chart-toolbar{flex-direction:column;align-items:stretch}
|
||||
.market-chart-options{order:1}
|
||||
.market-chart-zoom{order:2}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/market.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}行情K线 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="card market-card">
|
||||
<h2>行情 K 线</h2>
|
||||
<form class="market-toolbar" id="market-form" onsubmit="return false;">
|
||||
<div class="symbol-wrap market-symbol-wrap">
|
||||
<input type="text" class="symbol-input" id="market-symbol-input" placeholder="点击选择主力合约,或输入搜索" autocomplete="off" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol" id="market-symbol-hidden" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol_name" id="market-symbol-name">
|
||||
<input type="hidden" name="market_code" id="market-market-code">
|
||||
<input type="hidden" name="sina_code" id="market-sina-code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected" id="market-symbol-selected"></div>
|
||||
</div>
|
||||
<div class="market-period-tabs" id="market-period-tabs">
|
||||
{% for p in market_periods %}
|
||||
<button type="button" class="period-tab{% if p.key == period %} active{% endif %}" data-period="{{ p.key }}">{{ p.label }}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" class="btn-primary" id="market-load-btn">查看</button>
|
||||
</form>
|
||||
<div class="market-quote" id="market-quote">
|
||||
<span class="market-quote-name" id="market-quote-name">—</span>
|
||||
<span class="market-quote-price" id="market-quote-price">—</span>
|
||||
<span class="market-quote-prev text-muted" id="market-quote-prev"></span>
|
||||
<span class="market-quote-meta text-muted" id="market-quote-meta"></span>
|
||||
</div>
|
||||
<div class="market-chart-toolbar">
|
||||
<div class="market-chart-options">
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-prev-close">昨收线</label>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-ma">均线 21/55</label>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-gap-day">间隔日</label>
|
||||
</div>
|
||||
<div class="market-chart-zoom">
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-in" title="放大">+</button>
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-out" title="缩小">-</button>
|
||||
<button type="button" class="chart-zoom-btn chart-zoom-reset" id="chart-zoom-reset">重置</button>
|
||||
</div>
|
||||
<span class="market-refresh-hint text-muted" id="market-refresh-hint"></span>
|
||||
</div>
|
||||
<div class="market-chart-wrap" id="market-chart-wrap">
|
||||
<div id="market-chart" class="market-chart" aria-label="K线图"></div>
|
||||
<div class="market-chart-empty" id="market-chart-empty">请选择合约并点击「查看」</div>
|
||||
<div class="market-chart-loading" id="market-chart-loading">连接中…</div>
|
||||
</div>
|
||||
<p class="hint">图表引擎:TradingView Lightweight Charts(红跌绿涨)。数据来源:{% if ctp_connected %}报价 CTP;K 线历史新浪补齐、最新 bar 由 CTP tick 更新{% else %}CTP 未连接时回退新浪{% endif %}。滚轮缩放、拖拽平移;勾选「间隔日」可压缩夜盘空白。</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.market-card{overflow:visible}
|
||||
.market-card h2{margin-bottom:.75rem}
|
||||
.market-toolbar{
|
||||
display:flex;flex-wrap:wrap;gap:.65rem;align-items:flex-end;
|
||||
margin-bottom:.75rem;position:relative;z-index:2;
|
||||
min-height:2.5rem;
|
||||
}
|
||||
.market-symbol-wrap{flex:1;min-width:200px;max-width:360px;z-index:3}
|
||||
.market-symbol-wrap .symbol-dropdown{max-height:min(70vh,420px)}
|
||||
.market-symbol-wrap .symbol-selected{display:none}
|
||||
.market-period-tabs{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
|
||||
.period-tab{
|
||||
padding:.4rem .65rem;border-radius:999px;
|
||||
border:1px solid var(--input-border);
|
||||
background:var(--toggle-bg);color:var(--text-muted);
|
||||
font-size:.78rem;cursor:pointer;width:auto;white-space:nowrap;
|
||||
}
|
||||
.period-tab:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.period-tab.active{
|
||||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||||
border-color:transparent;color:#fff;
|
||||
}
|
||||
#market-load-btn{width:auto;padding:.55rem 1.25rem;font-size:.85rem}
|
||||
.market-quote{
|
||||
display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem 1rem;
|
||||
margin-bottom:.75rem;padding:.65rem .85rem;
|
||||
background:var(--card-inner);border-radius:10px;border:1px solid var(--card-border);
|
||||
min-height:2.75rem;
|
||||
}
|
||||
.market-quote-name{font-weight:600;color:var(--text-title)}
|
||||
.market-quote-price{font-size:1.35rem;font-weight:700;color:var(--accent);font-variant-numeric:tabular-nums}
|
||||
.market-quote-prev{font-size:.78rem}
|
||||
.market-chart-toolbar{
|
||||
display:flex;align-items:center;justify-content:space-between;gap:.75rem;
|
||||
margin-bottom:.5rem;flex-wrap:wrap;
|
||||
min-height:2rem;
|
||||
}
|
||||
.market-chart-options{display:flex;flex-wrap:wrap;gap:.5rem .85rem;align-items:center}
|
||||
.chart-opt{
|
||||
display:flex;align-items:center;gap:.35rem;font-size:.78rem;
|
||||
color:var(--text-muted);cursor:pointer;user-select:none;
|
||||
}
|
||||
.chart-opt input{width:auto;margin:0;cursor:pointer}
|
||||
.market-chart-zoom{display:flex;gap:.35rem;align-items:center}
|
||||
.chart-zoom-btn{
|
||||
width:32px;height:32px;padding:0;border-radius:8px;
|
||||
border:1px solid var(--input-border);background:var(--toggle-bg);
|
||||
color:var(--text-primary);font-size:1rem;line-height:1;cursor:pointer;
|
||||
}
|
||||
.chart-zoom-btn:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.chart-zoom-reset{width:auto;padding:0 .65rem;font-size:.75rem}
|
||||
.market-refresh-hint{font-size:.72rem}
|
||||
.market-chart-wrap{
|
||||
position:relative;border-radius:12px;border:1px solid var(--card-border);
|
||||
background:var(--card-inner);
|
||||
height:min(68vh,560px);min-height:420px;
|
||||
}
|
||||
.market-chart{width:100%;height:100%}
|
||||
.market-chart-empty,
|
||||
.market-chart-loading{
|
||||
position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
|
||||
color:var(--text-muted);font-size:.9rem;pointer-events:none;
|
||||
}
|
||||
.market-chart-loading{display:none}
|
||||
html[data-theme="light"] .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.has-data .market-chart-empty{display:none}
|
||||
.market-chart-wrap.loading .market-chart-loading{
|
||||
display:flex;background:rgba(10,12,20,.35);
|
||||
}
|
||||
html[data-theme="light"] .market-chart-wrap.loading .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.loading .market-chart-empty{display:none}
|
||||
@media(max-width:767px){
|
||||
.market-toolbar{align-items:stretch}
|
||||
.market-symbol-wrap{max-width:none}
|
||||
.market-period-tabs{order:3;width:100%}
|
||||
#market-load-btn{order:4;width:100%}
|
||||
.market-chart-wrap{min-height:300px;height:50vh}
|
||||
.market-chart-toolbar{flex-direction:column;align-items:stretch}
|
||||
.market-chart-options{order:1}
|
||||
.market-chart-zoom{order:2}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/market.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}开单计划 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
+48
-47
@@ -1,47 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}持仓监控 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>持仓录入</h2>
|
||||
<div class="card-body">
|
||||
<form action="{{ url_for('add_position') }}" method="post" class="form-compact">
|
||||
<div class="form-line line-3">
|
||||
<div class="symbol-wrap">
|
||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" required>
|
||||
<input type="hidden" name="symbol_name">
|
||||
<input type="hidden" name="market_code" required>
|
||||
<input type="hidden" name="sina_code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<input type="datetime-local" name="open_time" required title="开仓时间">
|
||||
<input name="lots" type="number" step="1" min="1" value="1" placeholder="张数" required>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="entry_price" type="number" step="0.0001" placeholder="成交价格" required>
|
||||
<input name="stop_loss" type="number" step="0.0001" placeholder="止损" required>
|
||||
<input name="take_profit" type="number" step="0.0001" placeholder="止盈" required>
|
||||
</div>
|
||||
<div class="form-line line-btn">
|
||||
<button type="submit" class="btn-primary">添加持仓</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="hint" style="margin-top:.5rem">方向根据止损与成交价自动判断;风险比例依赖系统设置中的实盘资金。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>实时持仓</h2>
|
||||
<div class="card-body card-scroll" id="position-live-list">
|
||||
{% if not positions %}
|
||||
<div class="empty-hint">暂无持仓,左侧录入后显示</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/positions.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}持仓监控 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>持仓录入</h2>
|
||||
<div class="card-body">
|
||||
<form action="{{ url_for('add_position') }}" method="post" class="form-compact">
|
||||
<div class="form-line line-3">
|
||||
<div class="symbol-wrap">
|
||||
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||
<input type="hidden" name="symbol" required>
|
||||
<input type="hidden" name="symbol_name">
|
||||
<input type="hidden" name="market_code" required>
|
||||
<input type="hidden" name="sina_code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected"></div>
|
||||
</div>
|
||||
<input type="datetime-local" name="open_time" required title="开仓时间">
|
||||
<input name="lots" type="number" step="1" min="1" value="1" placeholder="张数" required>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="entry_price" type="number" step="0.0001" placeholder="成交价格" required>
|
||||
<input name="stop_loss" type="number" step="0.0001" placeholder="止损" required>
|
||||
<input name="take_profit" type="number" step="0.0001" placeholder="止盈" required>
|
||||
</div>
|
||||
<div class="form-line line-btn">
|
||||
<button type="submit" class="btn-primary">添加持仓</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="hint" style="margin-top:.5rem">方向根据止损与成交价自动判断;风险比例依赖系统设置中的实盘资金。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>实时持仓</h2>
|
||||
<div class="card-body card-scroll" id="position-live-list">
|
||||
{% if not positions %}
|
||||
<div class="empty-hint">暂无持仓,左侧录入后显示</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/positions.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
+33
-32
@@ -1,32 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}品种推荐 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>品种推荐 · 按资金筛选</h2>
|
||||
<p class="hint">当前权益 <strong class="text-accent">{{ '%.2f'|format(capital) }}</strong> 元({{ trading_mode_label }})。
|
||||
优先展示可开 1 手且 1% 风险规则下较友好的品种;灰色为保证金不足。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品种</th><th>交易所</th><th>参考价</th><th>1手保证金</th><th>建议最低资金</th><th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rows %}
|
||||
<tr class="rec-{{ r.status }}">
|
||||
<td><strong>{{ r.name }}</strong> <span class="text-muted">{{ r.ths }}</span></td>
|
||||
<td>{{ r.exchange }}</td>
|
||||
<td>{% if r.price %}{{ r.price }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.margin_one_lot %}{{ r.margin_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.min_capital_one_lot %}{{ r.min_capital_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge {% if r.status=='ok' %}profit{% elif r.status=='blocked' %}loss{% else %}planned{% endif %}">{{ r.status_label }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}可开仓品种 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>可开仓品种 · 按资金筛选</h2>
|
||||
<p class="hint">当前权益 <strong class="text-accent">{{ '%.2f'|format(capital) }}</strong> 元({{ trading_mode_label }})。
|
||||
优先展示可开 1 手且 1% 风险规则下较友好的品种;灰色为保证金不足。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品种</th><th>交易所</th><th>参考价</th><th>1手保证金</th><th>建议最低资金</th><th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rows %}
|
||||
<tr class="rec-{{ r.status }}">
|
||||
<td><strong>{{ r.name }}</strong> <span class="text-muted">{{ r.ths }}</span></td>
|
||||
<td>{{ r.exchange }}</td>
|
||||
<td>{% if r.price %}{{ r.price }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.margin_one_lot %}{{ r.margin_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.min_capital_one_lot %}{{ r.min_capital_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge {% if r.status=='ok' %}profit{% elif r.status=='blocked' %}loss{% else %}planned{% endif %}">{{ r.status_label }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}交易记录与复盘 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
+407
-406
@@ -1,406 +1,407 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}系统设置 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.settings-page{display:flex;flex-direction:column;gap:1.25rem}
|
||||
.settings-page .split-grid{margin-bottom:0}
|
||||
.settings-page .split-grid .card{margin-bottom:0;min-height:100%;height:100%;display:flex;flex-direction:column}
|
||||
.settings-page .split-grid .card > form,
|
||||
.settings-page .split-grid .card > .card-inner{flex:1;display:flex;flex-direction:column}
|
||||
.settings-password-form{display:grid;grid-template-columns:1fr 1fr;gap:.65rem .75rem}
|
||||
.settings-password-form .field-full{grid-column:1/-1}
|
||||
.settings-password-form .field label{font-size:.78rem}
|
||||
.settings-password-form input{padding:.55rem .7rem;font-size:.85rem}
|
||||
.settings-tips{flex:1;display:flex;flex-direction:column;justify-content:center;gap:.5rem;margin:0;padding:0;list-style:none;font-size:.85rem;color:var(--text-muted);line-height:1.55}
|
||||
.settings-tips li{padding-left:1rem;position:relative}
|
||||
.settings-tips li::before{content:"";position:absolute;left:0;top:.55em;width:5px;height:5px;border-radius:50%;background:var(--accent)}
|
||||
.settings-ctp-grid{display:grid;grid-template-columns:1fr 1fr;gap:.65rem .75rem}
|
||||
.settings-ctp-grid .field-full{grid-column:1/-1}
|
||||
.settings-ctp-wrap .card-body{padding-top:0}
|
||||
.settings-ctp-cards-row{
|
||||
display:grid;grid-template-columns:1fr 1fr;gap:.75rem;
|
||||
align-items:start;margin-bottom:.75rem;
|
||||
}
|
||||
.settings-ctp-cards-row .settings-ctp-fold.card{margin-bottom:0;height:100%}
|
||||
.settings-ctp-cards-row .settings-ctp-grid{
|
||||
grid-template-columns:repeat(6,minmax(0,1fr));
|
||||
gap:.5rem .6rem;
|
||||
}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field{grid-column:span 2}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field-ctp-front-span{grid-column:span 3}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field label{font-size:.75rem}
|
||||
.settings-ctp-cards-row .settings-ctp-grid input,
|
||||
.settings-ctp-cards-row .settings-ctp-grid select{
|
||||
padding:.45rem .55rem;font-size:.8rem;
|
||||
}
|
||||
.settings-ctp-fold.card{
|
||||
margin-bottom:.75rem;padding:0;overflow:hidden;
|
||||
border:1px solid var(--border);border-radius:8px;background:var(--card-inner);
|
||||
}
|
||||
.settings-ctp-fold.card:last-of-type{margin-bottom:0}
|
||||
.settings-ctp-fold-head{
|
||||
width:100%;display:flex;align-items:center;justify-content:space-between;gap:.75rem;
|
||||
padding:.7rem 1rem;margin:0;border:none;background:transparent;cursor:pointer;
|
||||
font-size:.92rem;font-weight:600;color:var(--text-title);text-align:left;
|
||||
}
|
||||
.settings-ctp-fold-head:hover{color:var(--accent)}
|
||||
.settings-ctp-fold-title{display:flex;align-items:center;gap:.5rem}
|
||||
.settings-ctp-fold-chevron{
|
||||
flex-shrink:0;font-size:.72rem;color:var(--text-muted);
|
||||
transition:transform .2s ease;
|
||||
}
|
||||
.settings-ctp-fold.is-collapsed .settings-ctp-fold-chevron{transform:rotate(-90deg)}
|
||||
.settings-ctp-fold-body{padding:0 1rem .85rem}
|
||||
.settings-ctp-fold.is-collapsed .settings-ctp-fold-body{display:none}
|
||||
.settings-ctp-status{font-size:.82rem;color:var(--text-muted);margin-top:.75rem;line-height:1.5}
|
||||
@media(max-width:900px){
|
||||
.settings-password-form{grid-template-columns:1fr}
|
||||
.settings-ctp-cards-row{grid-template-columns:1fr}
|
||||
.settings-ctp-grid{grid-template-columns:1fr}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field,
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field-ctp-front-span{grid-column:span 1}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="settings-page">
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>导航显示</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="nav">
|
||||
<p class="hint" style="margin-bottom:.75rem">关闭后顶栏隐藏对应入口,直接访问 URL 也会跳转回下单监控。</p>
|
||||
<div class="check-row">
|
||||
{% for key, label in nav_toggles.items() %}
|
||||
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer;white-space:nowrap">
|
||||
<input type="checkbox" name="nav_{{ key }}" {% if nav_items[key] %}checked{% endif %}>
|
||||
<span>{{ label }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top:.75rem">保存导航</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>交易模式</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="trading">
|
||||
<div class="form-grid">
|
||||
<div class="field">
|
||||
<label>交易通道</label>
|
||||
<select name="trading_mode">
|
||||
<option value="simulation" {% if trading_mode == 'simulation' %}selected{% endif %}>SimNow(vnpy CTP)</option>
|
||||
<option value="live" {% if trading_mode == 'live' %}selected{% endif %}>期货公司 CTP(后期接入)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>计仓模式</label>
|
||||
<select name="position_sizing_mode" id="position-sizing-mode">
|
||||
<option value="fixed" {% if position_sizing_mode == 'fixed' %}selected{% endif %}>固定手数</option>
|
||||
<option value="amount" {% if position_sizing_mode in ('amount', 'risk') %}selected{% endif %}>固定金额</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" id="field-fixed-lots" {% if position_sizing_mode in ('amount', 'risk') %}hidden{% endif %}>
|
||||
<label>固定手数(手)</label>
|
||||
<input name="fixed_lots" type="number" step="1" min="1" value="{{ fixed_lots }}">
|
||||
</div>
|
||||
<div class="field" id="field-fixed-amount" {% if position_sizing_mode not in ('amount', 'risk') %}hidden{% endif %}>
|
||||
<label>固定金额(元)</label>
|
||||
<input name="fixed_amount" type="number" step="1" min="1" value="{{ fixed_amount }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>保证金占用上限(%)</label>
|
||||
<input name="max_margin_pct" type="number" step="1" min="1" max="100" value="{{ max_margin_pct }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>移动保本缓冲(最小变动价位倍数)</label>
|
||||
<input name="trailing_be_tick_buffer" type="number" step="1" min="1" max="20" value="{{ trailing_be_tick_buffer }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>开仓挂单超时(分钟)</label>
|
||||
<input name="pending_order_timeout_min" type="number" step="1" min="1" max="60" value="{{ pending_order_timeout_min }}">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top:.75rem">保存交易设置</button>
|
||||
<p class="hint" style="margin-top:.75rem;margin-bottom:0">
|
||||
保证金上限用于开仓校验与品种最大手数估算(默认 30%)。<strong>移动保本</strong>:达 1R 后止损移至开仓价 ± N 跳。
|
||||
<strong>挂单超时</strong>:限价开仓未成交时,超过设定分钟数自动向柜台撤单(1~60 分钟)。CTP 账号与前置在下方「CTP 连接」中配置。
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card settings-ctp-wrap">
|
||||
<h2>CTP 连接</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint" style="margin-bottom:.85rem">
|
||||
投资者代码、密码、前置地址在此维护(优先于 <code>.env</code>)。保存后将自动断开并用新地址重连 CTP。
|
||||
{% if ctp_status.connected %}
|
||||
<span class="badge profit" style="margin-left:.35rem">已连接</span>
|
||||
{% elif ctp_status.connecting %}
|
||||
<span class="badge planned" style="margin-left:.35rem">连接中</span>
|
||||
{% elif ctp_status.last_error %}
|
||||
<span class="text-loss" style="display:block;margin-top:.35rem">{{ ctp_status.last_error }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<form action="{{ url_for('settings') }}" method="post" id="ctp-settings-form">
|
||||
<input type="hidden" name="action" value="ctp">
|
||||
|
||||
<div class="settings-ctp-cards-row">
|
||||
<div class="settings-ctp-fold card{% if trading_mode != 'simulation' %} is-collapsed{% endif %}" data-ctp-fold="simnow">
|
||||
<button type="button" class="settings-ctp-fold-head" aria-expanded="{{ 'true' if trading_mode == 'simulation' else 'false' }}">
|
||||
<span class="settings-ctp-fold-title">
|
||||
SimNow 模拟盘
|
||||
{% if trading_mode == 'simulation' %}<span class="badge planned" style="font-size:.7rem">当前通道</span>{% endif %}
|
||||
</span>
|
||||
<span class="settings-ctp-fold-chevron" aria-hidden="true">▼</span>
|
||||
</button>
|
||||
<div class="settings-ctp-fold-body">
|
||||
<div class="settings-ctp-grid">
|
||||
<div class="field">
|
||||
<label>投资者代码</label>
|
||||
<input name="simnow_user" value="{{ ctp_cfg.simnow_user }}" placeholder="非手机号">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>交易密码</label>
|
||||
<input id="simnow_password" name="simnow_password" type="password"
|
||||
autocomplete="off" spellcheck="false"
|
||||
placeholder="{% if ctp_cfg.simnow_password_set %}已设置:须重新输入才会更新{% else %}SimNow 交易密码(必填){% endif %}">
|
||||
<p class="hint" style="margin:.25rem 0 0;font-size:.75rem">
|
||||
与快期相同密码,保存前须在此<strong>手打</strong>;留空则不改。下方「修改密码」是网页登录密码,不是 SimNow。
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>经纪商代码</label>
|
||||
<input name="simnow_broker_id" value="{{ ctp_cfg.simnow_broker_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>柜台环境</label>
|
||||
<select name="simnow_env">
|
||||
<option value="实盘" {% if ctp_cfg.simnow_env == '实盘' %}selected{% endif %}>实盘(看穿式,推荐)</option>
|
||||
<option value="测试" {% if ctp_cfg.simnow_env == '测试' %}selected{% endif %}>测试</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>AppID</label>
|
||||
<input name="simnow_app_id" value="{{ ctp_cfg.simnow_app_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>授权编码</label>
|
||||
<input name="simnow_auth_code" value="{{ ctp_cfg.simnow_auth_code }}">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>行情前置</label>
|
||||
<input name="simnow_md_address" value="{{ ctp_cfg.simnow_md_address }}" placeholder="tcp://180.168.146.187:10211">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>交易前置</label>
|
||||
<input name="simnow_td_address" value="{{ ctp_cfg.simnow_td_address }}" placeholder="tcp://180.168.146.187:10201">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-ctp-fold card{% if trading_mode != 'live' %} is-collapsed{% endif %}" data-ctp-fold="live">
|
||||
<button type="button" class="settings-ctp-fold-head" aria-expanded="{{ 'true' if trading_mode == 'live' else 'false' }}">
|
||||
<span class="settings-ctp-fold-title">
|
||||
期货公司实盘
|
||||
{% if trading_mode == 'live' %}<span class="badge planned" style="font-size:.7rem">当前通道</span>{% endif %}
|
||||
</span>
|
||||
<span class="settings-ctp-fold-chevron" aria-hidden="true">▼</span>
|
||||
</button>
|
||||
<div class="settings-ctp-fold-body">
|
||||
<div class="settings-ctp-grid">
|
||||
<div class="field">
|
||||
<label>投资者代码</label>
|
||||
<input name="ctp_live_user" value="{{ ctp_cfg.ctp_live_user }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>交易密码</label>
|
||||
<input name="ctp_live_password" type="password" autocomplete="new-password"
|
||||
placeholder="{% if ctp_cfg.ctp_live_password_set %}已设置,留空不修改{% else %}实盘密码{% endif %}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>经纪商代码</label>
|
||||
<input name="ctp_live_broker_id" value="{{ ctp_cfg.ctp_live_broker_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>柜台环境</label>
|
||||
<select name="ctp_live_env">
|
||||
<option value="实盘" {% if ctp_cfg.ctp_live_env == '实盘' %}selected{% endif %}>实盘</option>
|
||||
<option value="测试" {% if ctp_cfg.ctp_live_env == '测试' %}selected{% endif %}>测试</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>AppID</label>
|
||||
<input name="ctp_live_app_id" value="{{ ctp_cfg.ctp_live_app_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>授权编码</label>
|
||||
<input name="ctp_live_auth_code" value="{{ ctp_cfg.ctp_live_auth_code }}">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>行情前置</label>
|
||||
<input name="ctp_live_md_address" value="{{ ctp_cfg.ctp_live_md_address }}" placeholder="tcp://...">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>交易前置</label>
|
||||
<input name="ctp_live_td_address" value="{{ ctp_cfg.ctp_live_td_address }}" placeholder="tcp://...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary">保存 CTP 配置</button>
|
||||
<p class="settings-ctp-status">
|
||||
官方第一套:<code>180.168.146.187:10201/10211</code>;
|
||||
第二套(云服务器常用):<code>182.254.243.31:30001/30011</code>;
|
||||
7×24:<code>182.254.243.31:40001/40011</code>(部分账号在 40001 会报「不合法登录」,与快期前置保持一致)。
|
||||
详见 <code>docs/SIMNOW.md</code>。
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>行情说明</h2>
|
||||
<div class="card-inner">
|
||||
<p class="hint" style="font-size:.88rem;line-height:1.6;margin:0">
|
||||
当前行情源:<strong class="text-accent">{{ quote_label }}</strong><br>
|
||||
CTP 已连接时使用<strong>柜台行情</strong>;未连接时回退新浪接口。<br>
|
||||
合约代码按同花顺格式(如 ag2608、IF2606)。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>企业微信推送</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="wechat">
|
||||
<div class="field" style="margin-bottom:.75rem">
|
||||
<label>Webhook 地址</label>
|
||||
<input name="wechat_webhook" type="url" placeholder="https://qyapi.weixin.qq.com/..." value="{{ webhook }}">
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">保存</button>
|
||||
<p class="hint" style="margin-top:.75rem;margin-bottom:0">在企业微信群添加机器人后,粘贴 Webhook 地址保存。</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>修改密码</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post" class="settings-password-form">
|
||||
<input type="hidden" name="action" value="password">
|
||||
<div class="field field-full">
|
||||
<label>当前账号</label>
|
||||
<input type="text" value="{{ username }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>原密码</label>
|
||||
<input name="old_password" type="password" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>新密码</label>
|
||||
<input name="new_password" type="password" required minlength="6" placeholder="至少 6 位">
|
||||
</div>
|
||||
<div class="field field-full">
|
||||
<label>确认新密码</label>
|
||||
<input name="new_password2" type="password" required minlength="6">
|
||||
</div>
|
||||
<div class="field-full">
|
||||
<button type="submit" class="btn-primary">修改密码</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>使用提示</h2>
|
||||
<ul class="settings-tips">
|
||||
<li>下单监控:连接 CTP 后下单、看持仓与品种推荐</li>
|
||||
<li>策略交易:趋势回调自动补仓;顺势加仓需先开仓</li>
|
||||
<li>手续费:默认 CTP 柜台费率,连接后点同步</li>
|
||||
<li>手机端:浏览器菜单可「添加到主屏幕」安装 App</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
(function () {
|
||||
var sel = document.getElementById('position-sizing-mode');
|
||||
var lotsField = document.getElementById('field-fixed-lots');
|
||||
var amountField = document.getElementById('field-fixed-amount');
|
||||
function syncSizingFields() {
|
||||
if (!sel) return;
|
||||
var isAmount = sel.value === 'amount';
|
||||
if (lotsField) lotsField.hidden = isAmount;
|
||||
if (amountField) amountField.hidden = !isAmount;
|
||||
}
|
||||
if (sel) sel.addEventListener('change', syncSizingFields);
|
||||
syncSizingFields();
|
||||
|
||||
var CTP_FOLD_KEY = 'qihuo_ctp_fold';
|
||||
function setCtpFold(el, collapsed) {
|
||||
if (!el) return;
|
||||
el.classList.toggle('is-collapsed', collapsed);
|
||||
var head = el.querySelector('.settings-ctp-fold-head');
|
||||
if (head) head.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||
}
|
||||
function saveCtpFoldState() {
|
||||
var state = {};
|
||||
document.querySelectorAll('[data-ctp-fold]').forEach(function (el) {
|
||||
state[el.getAttribute('data-ctp-fold')] = el.classList.contains('is-collapsed');
|
||||
});
|
||||
try { localStorage.setItem(CTP_FOLD_KEY, JSON.stringify(state)); } catch (e) { /* ignore */ }
|
||||
}
|
||||
function loadCtpFoldState() {
|
||||
try {
|
||||
var raw = localStorage.getItem(CTP_FOLD_KEY);
|
||||
if (!raw) return;
|
||||
var state = JSON.parse(raw);
|
||||
document.querySelectorAll('[data-ctp-fold]').forEach(function (el) {
|
||||
var key = el.getAttribute('data-ctp-fold');
|
||||
if (Object.prototype.hasOwnProperty.call(state, key)) {
|
||||
setCtpFold(el, !!state[key]);
|
||||
}
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
document.querySelectorAll('.settings-ctp-fold-head').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var panel = btn.closest('[data-ctp-fold]');
|
||||
if (!panel) return;
|
||||
setCtpFold(panel, !panel.classList.contains('is-collapsed'));
|
||||
saveCtpFoldState();
|
||||
});
|
||||
});
|
||||
loadCtpFoldState();
|
||||
|
||||
var ctpForm = document.getElementById('ctp-settings-form');
|
||||
if (ctpForm) {
|
||||
ctpForm.addEventListener('submit', function (ev) {
|
||||
var simnowFold = document.querySelector('[data-ctp-fold="simnow"]');
|
||||
if (simnowFold) setCtpFold(simnowFold, false);
|
||||
var pwd = document.getElementById('simnow_password');
|
||||
var pwdVal = pwd && pwd.value ? pwd.value.trim() : '';
|
||||
var pwdWasSet = {{ 'true' if ctp_cfg.simnow_password_set else 'false' }};
|
||||
if (pwdWasSet && !pwdVal) {
|
||||
var ok = window.confirm(
|
||||
'SimNow 交易密码为空,保存后不会更新密码(仍用旧密码)。\n\n'
|
||||
+ '若快期已改密,请取消后在「交易密码」框手打新密码再保存。\n\n仍要保存其他项?'
|
||||
);
|
||||
if (!ok) ev.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}系统设置 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.settings-page{display:flex;flex-direction:column;gap:1.25rem}
|
||||
.settings-page .split-grid{margin-bottom:0}
|
||||
.settings-page .split-grid .card{margin-bottom:0;min-height:100%;height:100%;display:flex;flex-direction:column}
|
||||
.settings-page .split-grid .card > form,
|
||||
.settings-page .split-grid .card > .card-inner{flex:1;display:flex;flex-direction:column}
|
||||
.settings-password-form{display:grid;grid-template-columns:1fr 1fr;gap:.65rem .75rem}
|
||||
.settings-password-form .field-full{grid-column:1/-1}
|
||||
.settings-password-form .field label{font-size:.78rem}
|
||||
.settings-password-form input{padding:.55rem .7rem;font-size:.85rem}
|
||||
.settings-tips{flex:1;display:flex;flex-direction:column;justify-content:center;gap:.5rem;margin:0;padding:0;list-style:none;font-size:.85rem;color:var(--text-muted);line-height:1.55}
|
||||
.settings-tips li{padding-left:1rem;position:relative}
|
||||
.settings-tips li::before{content:"";position:absolute;left:0;top:.55em;width:5px;height:5px;border-radius:50%;background:var(--accent)}
|
||||
.settings-ctp-grid{display:grid;grid-template-columns:1fr 1fr;gap:.65rem .75rem}
|
||||
.settings-ctp-grid .field-full{grid-column:1/-1}
|
||||
.settings-ctp-wrap .card-body{padding-top:0}
|
||||
.settings-ctp-cards-row{
|
||||
display:grid;grid-template-columns:1fr 1fr;gap:.75rem;
|
||||
align-items:start;margin-bottom:.75rem;
|
||||
}
|
||||
.settings-ctp-cards-row .settings-ctp-fold.card{margin-bottom:0;height:100%}
|
||||
.settings-ctp-cards-row .settings-ctp-grid{
|
||||
grid-template-columns:repeat(6,minmax(0,1fr));
|
||||
gap:.5rem .6rem;
|
||||
}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field{grid-column:span 2}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field-ctp-front-span{grid-column:span 3}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field label{font-size:.75rem}
|
||||
.settings-ctp-cards-row .settings-ctp-grid input,
|
||||
.settings-ctp-cards-row .settings-ctp-grid select{
|
||||
padding:.45rem .55rem;font-size:.8rem;
|
||||
}
|
||||
.settings-ctp-fold.card{
|
||||
margin-bottom:.75rem;padding:0;overflow:hidden;
|
||||
border:1px solid var(--border);border-radius:8px;background:var(--card-inner);
|
||||
}
|
||||
.settings-ctp-fold.card:last-of-type{margin-bottom:0}
|
||||
.settings-ctp-fold-head{
|
||||
width:100%;display:flex;align-items:center;justify-content:space-between;gap:.75rem;
|
||||
padding:.7rem 1rem;margin:0;border:none;background:transparent;cursor:pointer;
|
||||
font-size:.92rem;font-weight:600;color:var(--text-title);text-align:left;
|
||||
}
|
||||
.settings-ctp-fold-head:hover{color:var(--accent)}
|
||||
.settings-ctp-fold-title{display:flex;align-items:center;gap:.5rem}
|
||||
.settings-ctp-fold-chevron{
|
||||
flex-shrink:0;font-size:.72rem;color:var(--text-muted);
|
||||
transition:transform .2s ease;
|
||||
}
|
||||
.settings-ctp-fold.is-collapsed .settings-ctp-fold-chevron{transform:rotate(-90deg)}
|
||||
.settings-ctp-fold-body{padding:0 1rem .85rem}
|
||||
.settings-ctp-fold.is-collapsed .settings-ctp-fold-body{display:none}
|
||||
.settings-ctp-status{font-size:.82rem;color:var(--text-muted);margin-top:.75rem;line-height:1.5}
|
||||
@media(max-width:900px){
|
||||
.settings-password-form{grid-template-columns:1fr}
|
||||
.settings-ctp-cards-row{grid-template-columns:1fr}
|
||||
.settings-ctp-grid{grid-template-columns:1fr}
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field,
|
||||
.settings-ctp-cards-row .settings-ctp-grid .field-ctp-front-span{grid-column:span 1}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="settings-page">
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>导航显示</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="nav">
|
||||
<p class="hint" style="margin-bottom:.75rem">关闭后顶栏隐藏对应入口,直接访问 URL 也会跳转回下单监控。</p>
|
||||
<div class="check-row">
|
||||
{% for key, label in nav_toggles.items() %}
|
||||
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer;white-space:nowrap">
|
||||
<input type="checkbox" name="nav_{{ key }}" {% if nav_items[key] %}checked{% endif %}>
|
||||
<span>{{ label }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top:.75rem">保存导航</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>交易模式</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="trading">
|
||||
<div class="form-grid">
|
||||
<div class="field">
|
||||
<label>交易通道</label>
|
||||
<select name="trading_mode">
|
||||
<option value="simulation" {% if trading_mode == 'simulation' %}selected{% endif %}>SimNow(vnpy CTP)</option>
|
||||
<option value="live" {% if trading_mode == 'live' %}selected{% endif %}>期货公司 CTP(后期接入)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>计仓模式</label>
|
||||
<select name="position_sizing_mode" id="position-sizing-mode">
|
||||
<option value="fixed" {% if position_sizing_mode == 'fixed' %}selected{% endif %}>固定手数</option>
|
||||
<option value="amount" {% if position_sizing_mode in ('amount', 'risk') %}selected{% endif %}>固定金额</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" id="field-fixed-lots" {% if position_sizing_mode in ('amount', 'risk') %}hidden{% endif %}>
|
||||
<label>固定手数(手)</label>
|
||||
<input name="fixed_lots" type="number" step="1" min="1" value="{{ fixed_lots }}">
|
||||
</div>
|
||||
<div class="field" id="field-fixed-amount" {% if position_sizing_mode not in ('amount', 'risk') %}hidden{% endif %}>
|
||||
<label>固定金额(元)</label>
|
||||
<input name="fixed_amount" type="number" step="1" min="1" value="{{ fixed_amount }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>保证金占用上限(%)</label>
|
||||
<input name="max_margin_pct" type="number" step="1" min="1" max="100" value="{{ max_margin_pct }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>移动保本缓冲(最小变动价位倍数)</label>
|
||||
<input name="trailing_be_tick_buffer" type="number" step="1" min="1" max="20" value="{{ trailing_be_tick_buffer }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>开仓挂单超时(分钟)</label>
|
||||
<input name="pending_order_timeout_min" type="number" step="1" min="1" max="60" value="{{ pending_order_timeout_min }}">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top:.75rem">保存交易设置</button>
|
||||
<p class="hint" style="margin-top:.75rem;margin-bottom:0">
|
||||
保证金上限用于开仓校验与品种最大手数估算(默认 30%)。<strong>移动保本</strong>:达 1R 后止损移至开仓价 ± N 跳。
|
||||
<strong>挂单超时</strong>:限价开仓未成交时,超过设定分钟数自动向柜台撤单(1~60 分钟)。CTP 账号与前置在下方「CTP 连接」中配置。
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card settings-ctp-wrap">
|
||||
<h2>CTP 连接</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint" style="margin-bottom:.85rem">
|
||||
投资者代码、密码、前置地址在此维护(优先于 <code>.env</code>)。保存后将自动断开并用新地址重连 CTP。
|
||||
{% if ctp_status.connected %}
|
||||
<span class="badge profit" style="margin-left:.35rem">已连接</span>
|
||||
{% elif ctp_status.connecting %}
|
||||
<span class="badge planned" style="margin-left:.35rem">连接中</span>
|
||||
{% elif ctp_status.last_error %}
|
||||
<span class="text-loss" style="display:block;margin-top:.35rem">{{ ctp_status.last_error }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<form action="{{ url_for('settings') }}" method="post" id="ctp-settings-form">
|
||||
<input type="hidden" name="action" value="ctp">
|
||||
|
||||
<div class="settings-ctp-cards-row">
|
||||
<div class="settings-ctp-fold card{% if trading_mode != 'simulation' %} is-collapsed{% endif %}" data-ctp-fold="simnow">
|
||||
<button type="button" class="settings-ctp-fold-head" aria-expanded="{{ 'true' if trading_mode == 'simulation' else 'false' }}">
|
||||
<span class="settings-ctp-fold-title">
|
||||
SimNow 模拟盘
|
||||
{% if trading_mode == 'simulation' %}<span class="badge planned" style="font-size:.7rem">当前通道</span>{% endif %}
|
||||
</span>
|
||||
<span class="settings-ctp-fold-chevron" aria-hidden="true">▼</span>
|
||||
</button>
|
||||
<div class="settings-ctp-fold-body">
|
||||
<div class="settings-ctp-grid">
|
||||
<div class="field">
|
||||
<label>投资者代码</label>
|
||||
<input name="simnow_user" value="{{ ctp_cfg.simnow_user }}" placeholder="非手机号">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>交易密码</label>
|
||||
<input id="simnow_password" name="simnow_password" type="password"
|
||||
autocomplete="off" spellcheck="false"
|
||||
placeholder="{% if ctp_cfg.simnow_password_set %}已设置:须重新输入才会更新{% else %}SimNow 交易密码(必填){% endif %}">
|
||||
<p class="hint" style="margin:.25rem 0 0;font-size:.75rem">
|
||||
与快期相同密码,保存前须在此<strong>手打</strong>;留空则不改。下方「修改密码」是网页登录密码,不是 SimNow。
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>经纪商代码</label>
|
||||
<input name="simnow_broker_id" value="{{ ctp_cfg.simnow_broker_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>柜台环境</label>
|
||||
<select name="simnow_env">
|
||||
<option value="实盘" {% if ctp_cfg.simnow_env == '实盘' %}selected{% endif %}>实盘(看穿式,推荐)</option>
|
||||
<option value="测试" {% if ctp_cfg.simnow_env == '测试' %}selected{% endif %}>测试</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>AppID</label>
|
||||
<input name="simnow_app_id" value="{{ ctp_cfg.simnow_app_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>授权编码</label>
|
||||
<input name="simnow_auth_code" value="{{ ctp_cfg.simnow_auth_code }}">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>行情前置</label>
|
||||
<input name="simnow_md_address" value="{{ ctp_cfg.simnow_md_address }}" placeholder="tcp://180.168.146.187:10211">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>交易前置</label>
|
||||
<input name="simnow_td_address" value="{{ ctp_cfg.simnow_td_address }}" placeholder="tcp://180.168.146.187:10201">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-ctp-fold card{% if trading_mode != 'live' %} is-collapsed{% endif %}" data-ctp-fold="live">
|
||||
<button type="button" class="settings-ctp-fold-head" aria-expanded="{{ 'true' if trading_mode == 'live' else 'false' }}">
|
||||
<span class="settings-ctp-fold-title">
|
||||
期货公司实盘
|
||||
{% if trading_mode == 'live' %}<span class="badge planned" style="font-size:.7rem">当前通道</span>{% endif %}
|
||||
</span>
|
||||
<span class="settings-ctp-fold-chevron" aria-hidden="true">▼</span>
|
||||
</button>
|
||||
<div class="settings-ctp-fold-body">
|
||||
<div class="settings-ctp-grid">
|
||||
<div class="field">
|
||||
<label>投资者代码</label>
|
||||
<input name="ctp_live_user" value="{{ ctp_cfg.ctp_live_user }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>交易密码</label>
|
||||
<input name="ctp_live_password" type="password" autocomplete="new-password"
|
||||
placeholder="{% if ctp_cfg.ctp_live_password_set %}已设置,留空不修改{% else %}实盘密码{% endif %}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>经纪商代码</label>
|
||||
<input name="ctp_live_broker_id" value="{{ ctp_cfg.ctp_live_broker_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>柜台环境</label>
|
||||
<select name="ctp_live_env">
|
||||
<option value="实盘" {% if ctp_cfg.ctp_live_env == '实盘' %}selected{% endif %}>实盘</option>
|
||||
<option value="测试" {% if ctp_cfg.ctp_live_env == '测试' %}selected{% endif %}>测试</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>AppID</label>
|
||||
<input name="ctp_live_app_id" value="{{ ctp_cfg.ctp_live_app_id }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>授权编码</label>
|
||||
<input name="ctp_live_auth_code" value="{{ ctp_cfg.ctp_live_auth_code }}">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>行情前置</label>
|
||||
<input name="ctp_live_md_address" value="{{ ctp_cfg.ctp_live_md_address }}" placeholder="tcp://...">
|
||||
</div>
|
||||
<div class="field field-ctp-front-span">
|
||||
<label>交易前置</label>
|
||||
<input name="ctp_live_td_address" value="{{ ctp_cfg.ctp_live_td_address }}" placeholder="tcp://...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary">保存 CTP 配置</button>
|
||||
<p class="settings-ctp-status">
|
||||
官方第一套:<code>180.168.146.187:10201/10211</code>;
|
||||
第二套(云服务器常用):<code>182.254.243.31:30001/30011</code>;
|
||||
7×24:<code>182.254.243.31:40001/40011</code>(部分账号在 40001 会报「不合法登录」,与快期前置保持一致)。
|
||||
详见 <code>docs/SIMNOW.md</code>。
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>行情说明</h2>
|
||||
<div class="card-inner">
|
||||
<p class="hint" style="font-size:.88rem;line-height:1.6;margin:0">
|
||||
当前行情源:<strong class="text-accent">{{ quote_label }}</strong><br>
|
||||
CTP 已连接时使用<strong>柜台行情</strong>;未连接时回退新浪接口。<br>
|
||||
合约代码按同花顺格式(如 ag2608、IF2606)。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>企业微信推送</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post">
|
||||
<input type="hidden" name="action" value="wechat">
|
||||
<div class="field" style="margin-bottom:.75rem">
|
||||
<label>Webhook 地址</label>
|
||||
<input name="wechat_webhook" type="url" placeholder="https://qyapi.weixin.qq.com/..." value="{{ webhook }}">
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">保存</button>
|
||||
<p class="hint" style="margin-top:.75rem;margin-bottom:0">在企业微信群添加机器人后,粘贴 Webhook 地址保存。</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>修改密码</h2>
|
||||
<form action="{{ url_for('settings') }}" method="post" class="settings-password-form">
|
||||
<input type="hidden" name="action" value="password">
|
||||
<div class="field field-full">
|
||||
<label>当前账号</label>
|
||||
<input type="text" value="{{ username }}" disabled>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>原密码</label>
|
||||
<input name="old_password" type="password" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>新密码</label>
|
||||
<input name="new_password" type="password" required minlength="6" placeholder="至少 6 位">
|
||||
</div>
|
||||
<div class="field field-full">
|
||||
<label>确认新密码</label>
|
||||
<input name="new_password2" type="password" required minlength="6">
|
||||
</div>
|
||||
<div class="field-full">
|
||||
<button type="submit" class="btn-primary">修改密码</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>使用提示</h2>
|
||||
<ul class="settings-tips">
|
||||
<li>下单监控:连接 CTP 后下单、看持仓与可开仓品种</li>
|
||||
<li>策略交易:趋势回调自动补仓;顺势加仓需先开仓</li>
|
||||
<li>手续费:默认 CTP 柜台费率,连接后点同步</li>
|
||||
<li>手机端:浏览器菜单可「添加到主屏幕」安装 App</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
(function () {
|
||||
var sel = document.getElementById('position-sizing-mode');
|
||||
var lotsField = document.getElementById('field-fixed-lots');
|
||||
var amountField = document.getElementById('field-fixed-amount');
|
||||
function syncSizingFields() {
|
||||
if (!sel) return;
|
||||
var isAmount = sel.value === 'amount';
|
||||
if (lotsField) lotsField.hidden = isAmount;
|
||||
if (amountField) amountField.hidden = !isAmount;
|
||||
}
|
||||
if (sel) sel.addEventListener('change', syncSizingFields);
|
||||
syncSizingFields();
|
||||
|
||||
var CTP_FOLD_KEY = 'qihuo_ctp_fold';
|
||||
function setCtpFold(el, collapsed) {
|
||||
if (!el) return;
|
||||
el.classList.toggle('is-collapsed', collapsed);
|
||||
var head = el.querySelector('.settings-ctp-fold-head');
|
||||
if (head) head.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||
}
|
||||
function saveCtpFoldState() {
|
||||
var state = {};
|
||||
document.querySelectorAll('[data-ctp-fold]').forEach(function (el) {
|
||||
state[el.getAttribute('data-ctp-fold')] = el.classList.contains('is-collapsed');
|
||||
});
|
||||
try { localStorage.setItem(CTP_FOLD_KEY, JSON.stringify(state)); } catch (e) { /* ignore */ }
|
||||
}
|
||||
function loadCtpFoldState() {
|
||||
try {
|
||||
var raw = localStorage.getItem(CTP_FOLD_KEY);
|
||||
if (!raw) return;
|
||||
var state = JSON.parse(raw);
|
||||
document.querySelectorAll('[data-ctp-fold]').forEach(function (el) {
|
||||
var key = el.getAttribute('data-ctp-fold');
|
||||
if (Object.prototype.hasOwnProperty.call(state, key)) {
|
||||
setCtpFold(el, !!state[key]);
|
||||
}
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
document.querySelectorAll('.settings-ctp-fold-head').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var panel = btn.closest('[data-ctp-fold]');
|
||||
if (!panel) return;
|
||||
setCtpFold(panel, !panel.classList.contains('is-collapsed'));
|
||||
saveCtpFoldState();
|
||||
});
|
||||
});
|
||||
loadCtpFoldState();
|
||||
|
||||
var ctpForm = document.getElementById('ctp-settings-form');
|
||||
if (ctpForm) {
|
||||
ctpForm.addEventListener('submit', function (ev) {
|
||||
var simnowFold = document.querySelector('[data-ctp-fold="simnow"]');
|
||||
if (simnowFold) setCtpFold(simnowFold, false);
|
||||
var pwd = document.getElementById('simnow_password');
|
||||
var pwdVal = pwd && pwd.value ? pwd.value.trim() : '';
|
||||
var pwdWasSet = {{ 'true' if ctp_cfg.simnow_password_set else 'false' }};
|
||||
if (pwdWasSet && !pwdVal) {
|
||||
var ok = window.confirm(
|
||||
'SimNow 交易密码为空,保存后不会更新密码(仍用旧密码)。\n\n'
|
||||
+ '若快期已改密,请取消后在「交易密码」框手打新密码再保存。\n\n仍要保存其他项?'
|
||||
);
|
||||
if (!ok) ev.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
+77
-76
@@ -1,76 +1,77 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}统计分析 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="card stats-summary-card">
|
||||
<div class="stats-toolbar">
|
||||
<span id="stats-updated" class="hint">正在加载统计…</span>
|
||||
</div>
|
||||
<div class="stat-grid stat-grid-summary" id="stats-summary">
|
||||
<div class="stat-item"><div class="label">总交易次数</div><div class="value" data-k="total_trades">-</div></div>
|
||||
<div class="stat-item"><div class="label">胜率</div><div class="value" data-k="win_rate">-</div></div>
|
||||
<div class="stat-item"><div class="label">平均盈利</div><div class="value text-profit" data-k="avg_profit">-</div></div>
|
||||
<div class="stat-item"><div class="label">平均亏损</div><div class="value text-loss" data-k="avg_loss">-</div></div>
|
||||
<div class="stat-item"><div class="label">盈亏比</div><div class="value" data-k="profit_loss_ratio">-</div></div>
|
||||
<div class="stat-item"><div class="label">连续亏损次数</div><div class="value" data-k="consecutive_losses">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大回撤</div><div class="value" data-k="max_drawdown">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大亏损金额</div><div class="value text-loss" data-k="max_loss_amount">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大亏损占比</div><div class="value text-loss" data-k="max_loss_pct">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大盈利金额</div><div class="value text-profit" data-k="max_profit_amount">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大盈利占比</div><div class="value text-profit" data-k="max_profit_pct">-</div></div>
|
||||
<div class="stat-item"><div class="label">累计手续费</div><div class="value text-loss" data-k="total_fee">-</div></div>
|
||||
<div class="stat-item"><div class="label">情绪单数量</div><div class="value" data-k="emotion_count">-</div></div>
|
||||
<div class="stat-item"><div class="label">情绪单占比</div><div class="value" data-k="emotion_ratio">-</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="stats-card-head">
|
||||
<h2>分项统计</h2>
|
||||
<div class="field stats-view-field">
|
||||
<label for="stats-view-select">统计维度</label>
|
||||
<select id="stats-view-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-scroll">
|
||||
<table id="stats-breakdown-table">
|
||||
<thead><tr id="stats-breakdown-head"></tr></thead>
|
||||
<tbody id="stats-breakdown-body">
|
||||
<tr><td colspan="12" class="text-muted">加载中…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.stats-summary-card{margin-bottom:1.25rem}
|
||||
.stats-toolbar{display:flex;align-items:center;justify-content:flex-start;gap:1rem;margin-bottom:.75rem;flex-wrap:wrap}
|
||||
.stat-grid-summary{
|
||||
display:flex;flex-wrap:nowrap;align-items:stretch;gap:0;
|
||||
margin-bottom:0;overflow-x:auto;-webkit-overflow-scrolling:touch;
|
||||
}
|
||||
.stat-grid-summary .stat-item{
|
||||
flex:1 1 0;min-width:4.5rem;background:transparent;border:none;border-radius:0;
|
||||
padding:.35rem .2rem;text-align:center;position:relative;overflow:visible;
|
||||
border-right:1px solid var(--table-border);
|
||||
}
|
||||
.stat-grid-summary .stat-item:last-child{border-right:none}
|
||||
.stat-grid-summary .stat-item::before{display:none}
|
||||
.stat-grid-summary .stat-item:hover{transform:none;box-shadow:none}
|
||||
.stat-grid-summary .stat-item .label{
|
||||
font-size:.62rem;line-height:1.25;color:var(--text-muted);white-space:nowrap;
|
||||
}
|
||||
.stat-grid-summary .stat-item .value{
|
||||
font-size:.78rem;font-weight:600;color:var(--text-title);margin-top:.12rem;
|
||||
font-variant-numeric:tabular-nums;white-space:nowrap;
|
||||
}
|
||||
.stats-card-head{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-bottom:1rem}
|
||||
.stats-card-head h2{margin-bottom:0}
|
||||
.stats-view-field{width:auto;min-width:200px}
|
||||
.stats-view-field select{width:100%;min-width:180px}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/stats.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}统计分析 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="card stats-summary-card">
|
||||
<div class="stats-toolbar">
|
||||
<span id="stats-updated" class="hint">正在加载统计…</span>
|
||||
</div>
|
||||
<div class="stat-grid stat-grid-summary" id="stats-summary">
|
||||
<div class="stat-item"><div class="label">总交易次数</div><div class="value" data-k="total_trades">-</div></div>
|
||||
<div class="stat-item"><div class="label">胜率</div><div class="value" data-k="win_rate">-</div></div>
|
||||
<div class="stat-item"><div class="label">平均盈利</div><div class="value text-profit" data-k="avg_profit">-</div></div>
|
||||
<div class="stat-item"><div class="label">平均亏损</div><div class="value text-loss" data-k="avg_loss">-</div></div>
|
||||
<div class="stat-item"><div class="label">盈亏比</div><div class="value" data-k="profit_loss_ratio">-</div></div>
|
||||
<div class="stat-item"><div class="label">连续亏损次数</div><div class="value" data-k="consecutive_losses">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大回撤</div><div class="value" data-k="max_drawdown">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大亏损金额</div><div class="value text-loss" data-k="max_loss_amount">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大亏损占比</div><div class="value text-loss" data-k="max_loss_pct">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大盈利金额</div><div class="value text-profit" data-k="max_profit_amount">-</div></div>
|
||||
<div class="stat-item"><div class="label">最大盈利占比</div><div class="value text-profit" data-k="max_profit_pct">-</div></div>
|
||||
<div class="stat-item"><div class="label">累计手续费</div><div class="value text-loss" data-k="total_fee">-</div></div>
|
||||
<div class="stat-item"><div class="label">情绪单数量</div><div class="value" data-k="emotion_count">-</div></div>
|
||||
<div class="stat-item"><div class="label">情绪单占比</div><div class="value" data-k="emotion_ratio">-</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="stats-card-head">
|
||||
<h2>分项统计</h2>
|
||||
<div class="field stats-view-field">
|
||||
<label for="stats-view-select">统计维度</label>
|
||||
<select id="stats-view-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-scroll">
|
||||
<table id="stats-breakdown-table">
|
||||
<thead><tr id="stats-breakdown-head"></tr></thead>
|
||||
<tbody id="stats-breakdown-body">
|
||||
<tr><td colspan="12" class="text-muted">加载中…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.stats-summary-card{margin-bottom:1.25rem}
|
||||
.stats-toolbar{display:flex;align-items:center;justify-content:flex-start;gap:1rem;margin-bottom:.75rem;flex-wrap:wrap}
|
||||
.stat-grid-summary{
|
||||
display:flex;flex-wrap:nowrap;align-items:stretch;gap:0;
|
||||
margin-bottom:0;overflow-x:auto;-webkit-overflow-scrolling:touch;
|
||||
}
|
||||
.stat-grid-summary .stat-item{
|
||||
flex:1 1 0;min-width:4.5rem;background:transparent;border:none;border-radius:0;
|
||||
padding:.35rem .2rem;text-align:center;position:relative;overflow:visible;
|
||||
border-right:1px solid var(--table-border);
|
||||
}
|
||||
.stat-grid-summary .stat-item:last-child{border-right:none}
|
||||
.stat-grid-summary .stat-item::before{display:none}
|
||||
.stat-grid-summary .stat-item:hover{transform:none;box-shadow:none}
|
||||
.stat-grid-summary .stat-item .label{
|
||||
font-size:.62rem;line-height:1.25;color:var(--text-muted);white-space:nowrap;
|
||||
}
|
||||
.stat-grid-summary .stat-item .value{
|
||||
font-size:.78rem;font-weight:600;color:var(--text-title);margin-top:.12rem;
|
||||
font-variant-numeric:tabular-nums;white-space:nowrap;
|
||||
}
|
||||
.stats-card-head{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-bottom:1rem}
|
||||
.stats-card-head h2{margin-bottom:0}
|
||||
.stats-view-field{width:auto;min-width:200px}
|
||||
.stats-view-field select{width:100%;min-width:180px}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/stats.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
+102
-101
@@ -1,101 +1,102 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}策略交易 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.strategy-page .split-grid .card{min-height:420px;display:flex;flex-direction:column}
|
||||
.strategy-page .split-grid .card-body{flex:1}
|
||||
.strategy-preview{background:var(--card-inner);border:1px solid var(--card-border);border-radius:8px;padding:.65rem .85rem;font-size:.78rem;line-height:1.5;margin-top:.75rem;white-space:pre-wrap;max-height:200px;overflow:auto}
|
||||
.strategy-steps{margin:.75rem 0 0;padding-left:1.1rem;font-size:.82rem;color:var(--text-muted);line-height:1.6}
|
||||
.strategy-steps a{color:var(--accent)}
|
||||
.strategy-active-roll{margin-top:.65rem;padding:.55rem .75rem;background:var(--card-inner);border-radius:8px;font-size:.8rem;border:1px solid var(--card-border)}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="strategy-page">
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>趋势回调</h2>
|
||||
<div class="card-body">
|
||||
{% if active_trend %}
|
||||
<p class="hint">运行中 #{{ active_trend.id }} · {{ active_trend.symbol }} · {{ '做多' if active_trend.direction == 'long' else '做空' }}</p>
|
||||
<p class="hint">已开 <strong>{{ active_trend.lots_open or 0 }}</strong> / {{ active_trend.target_lots }} 手 · 止损 {{ active_trend.stop_loss }} · 止盈 {{ active_trend.take_profit }}</p>
|
||||
<form id="trend-stop-form" class="form-row" style="margin-top:.75rem">
|
||||
<input type="hidden" name="plan_id" value="{{ active_trend.id }}">
|
||||
<button type="button" class="btn-primary" id="btn-trend-stop">结束计划</button>
|
||||
</form>
|
||||
<p class="hint" style="margin-top:.75rem;font-size:.75rem">后台按档位自动补仓,触及止盈或手动结束。</p>
|
||||
{% else %}
|
||||
<p class="hint" style="margin-bottom:.65rem">设置止损/补仓边界/止盈 → 预览 → 确认执行首仓;后续自动分档加仓。</p>
|
||||
<form id="trend-form" class="form-compact">
|
||||
<div class="form-line line-2">
|
||||
<div class="symbol-wrap symbol-mains">
|
||||
<input class="symbol-input" name="symbol" placeholder="品种,输入中文或代码" autocomplete="off" required>
|
||||
<div class="symbol-dropdown"></div>
|
||||
</div>
|
||||
<select name="direction"><option value="long">做多</option><option value="short">做空</option></select>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="stop_loss" type="number" step="any" placeholder="止损" required>
|
||||
<input name="add_upper" type="number" step="any" placeholder="补仓边界" required>
|
||||
<input name="take_profit" type="number" step="any" placeholder="止盈" required>
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="risk_percent" type="number" step="0.1" value="{{ risk_percent }}" placeholder="单笔风险 %" title="单笔风险%">
|
||||
<button type="button" class="btn-primary" id="btn-trend-preview">预览计划</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="trend-preview" class="strategy-preview" hidden></div>
|
||||
<button type="button" class="btn-primary" id="btn-trend-exec" hidden style="margin-top:.65rem;width:100%">确认执行首仓</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>顺势加仓(滚仓)</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint" style="margin-bottom:.65rem">在已有持仓上扩大仓位,统一抬高止损;最多 3 腿,止盈锁定首仓。</p>
|
||||
{% if roll_groups %}
|
||||
{% for g in roll_groups %}
|
||||
<div class="strategy-active-roll">
|
||||
运行中 · 监控 #{{ g.order_monitor_id }} · {{ g.leg_count or 1 }} 腿 · 止损 {{ g.current_stop_loss }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if monitors %}
|
||||
<form id="roll-form" class="form-compact">
|
||||
<div class="field" style="margin-bottom:.5rem">
|
||||
<label class="text-label" style="font-size:.72rem">选择下单监控(开仓后生成)</label>
|
||||
<select name="monitor_id" required>
|
||||
{% for m in monitors %}
|
||||
<option value="{{ m.id }}">{{ m.symbol_name or m.symbol }} {{ m.symbol }} · {{ '多' if m.direction == 'long' else '空' }} {{ m.lots }}手 · SL {{ m.stop_loss or '—' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="new_stop_loss" type="number" step="any" placeholder="新统一止损" required>
|
||||
<input name="risk_percent" type="number" step="0.1" value="2" placeholder="总风险 %" title="总风险%">
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="add_price" type="number" step="any" placeholder="加仓参考价(可选)">
|
||||
<button type="button" class="btn-primary" id="btn-roll-preview">预览滚仓</button>
|
||||
</div>
|
||||
<div id="roll-preview" class="strategy-preview" hidden></div>
|
||||
<button type="button" class="btn-primary" id="btn-roll-exec" hidden style="margin-top:.65rem;width:100%">执行滚仓</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="empty-hint">暂无可用持仓监控</p>
|
||||
<ol class="strategy-steps">
|
||||
<li>打开 <a href="{{ url_for('positions') }}">持仓监控</a>,连接 CTP</li>
|
||||
<li>在「期货下单」填写品种、止损/止盈并<strong>开仓</strong></li>
|
||||
<li>开仓成功后会生成本页可选的监控记录,即可滚仓</li>
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top:1rem"><a href="{{ url_for('strategy_records_page') }}">策略交易记录 →</a></p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/strategy.js') }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}策略交易 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.strategy-page .split-grid .card{min-height:420px;display:flex;flex-direction:column}
|
||||
.strategy-page .split-grid .card-body{flex:1}
|
||||
.strategy-preview{background:var(--card-inner);border:1px solid var(--card-border);border-radius:8px;padding:.65rem .85rem;font-size:.78rem;line-height:1.5;margin-top:.75rem;white-space:pre-wrap;max-height:200px;overflow:auto}
|
||||
.strategy-steps{margin:.75rem 0 0;padding-left:1.1rem;font-size:.82rem;color:var(--text-muted);line-height:1.6}
|
||||
.strategy-steps a{color:var(--accent)}
|
||||
.strategy-active-roll{margin-top:.65rem;padding:.55rem .75rem;background:var(--card-inner);border-radius:8px;font-size:.8rem;border:1px solid var(--card-border)}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="strategy-page">
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<h2>趋势回调</h2>
|
||||
<div class="card-body">
|
||||
{% if active_trend %}
|
||||
<p class="hint">运行中 #{{ active_trend.id }} · {{ active_trend.symbol }} · {{ '做多' if active_trend.direction == 'long' else '做空' }}</p>
|
||||
<p class="hint">已开 <strong>{{ active_trend.lots_open or 0 }}</strong> / {{ active_trend.target_lots }} 手 · 止损 {{ active_trend.stop_loss }} · 止盈 {{ active_trend.take_profit }}</p>
|
||||
<form id="trend-stop-form" class="form-row" style="margin-top:.75rem">
|
||||
<input type="hidden" name="plan_id" value="{{ active_trend.id }}">
|
||||
<button type="button" class="btn-primary" id="btn-trend-stop">结束计划</button>
|
||||
</form>
|
||||
<p class="hint" style="margin-top:.75rem;font-size:.75rem">后台按档位自动补仓,触及止盈或手动结束。</p>
|
||||
{% else %}
|
||||
<p class="hint" style="margin-bottom:.65rem">设置止损/补仓边界/止盈 → 预览 → 确认执行首仓;后续自动分档加仓。</p>
|
||||
<form id="trend-form" class="form-compact">
|
||||
<div class="form-line line-2">
|
||||
<div class="symbol-wrap symbol-mains">
|
||||
<input class="symbol-input" name="symbol" placeholder="品种,输入中文或代码" autocomplete="off" required>
|
||||
<div class="symbol-dropdown"></div>
|
||||
</div>
|
||||
<select name="direction"><option value="long">做多</option><option value="short">做空</option></select>
|
||||
</div>
|
||||
<div class="form-line line-3">
|
||||
<input name="stop_loss" type="number" step="any" placeholder="止损" required>
|
||||
<input name="add_upper" type="number" step="any" placeholder="补仓边界" required>
|
||||
<input name="take_profit" type="number" step="any" placeholder="止盈" required>
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="risk_percent" type="number" step="0.1" value="{{ risk_percent }}" placeholder="单笔风险 %" title="单笔风险%">
|
||||
<button type="button" class="btn-primary" id="btn-trend-preview">预览计划</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="trend-preview" class="strategy-preview" hidden></div>
|
||||
<button type="button" class="btn-primary" id="btn-trend-exec" hidden style="margin-top:.65rem;width:100%">确认执行首仓</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>顺势加仓(滚仓)</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint" style="margin-bottom:.65rem">在已有持仓上扩大仓位,统一抬高止损;最多 3 腿,止盈锁定首仓。</p>
|
||||
{% if roll_groups %}
|
||||
{% for g in roll_groups %}
|
||||
<div class="strategy-active-roll">
|
||||
运行中 · 监控 #{{ g.order_monitor_id }} · {{ g.leg_count or 1 }} 腿 · 止损 {{ g.current_stop_loss }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if monitors %}
|
||||
<form id="roll-form" class="form-compact">
|
||||
<div class="field" style="margin-bottom:.5rem">
|
||||
<label class="text-label" style="font-size:.72rem">选择下单监控(开仓后生成)</label>
|
||||
<select name="monitor_id" required>
|
||||
{% for m in monitors %}
|
||||
<option value="{{ m.id }}">{{ m.symbol_name or m.symbol }} {{ m.symbol }} · {{ '多' if m.direction == 'long' else '空' }} {{ m.lots }}手 · SL {{ m.stop_loss or '—' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="new_stop_loss" type="number" step="any" placeholder="新统一止损" required>
|
||||
<input name="risk_percent" type="number" step="0.1" value="2" placeholder="总风险 %" title="总风险%">
|
||||
</div>
|
||||
<div class="form-line line-2">
|
||||
<input name="add_price" type="number" step="any" placeholder="加仓参考价(可选)">
|
||||
<button type="button" class="btn-primary" id="btn-roll-preview">预览滚仓</button>
|
||||
</div>
|
||||
<div id="roll-preview" class="strategy-preview" hidden></div>
|
||||
<button type="button" class="btn-primary" id="btn-roll-exec" hidden style="margin-top:.65rem;width:100%">执行滚仓</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="empty-hint">暂无可用持仓监控</p>
|
||||
<ol class="strategy-steps">
|
||||
<li>打开 <a href="{{ url_for('positions') }}">持仓监控</a>,连接 CTP</li>
|
||||
<li>在「期货下单」填写品种、止损/止盈并<strong>开仓</strong></li>
|
||||
<li>开仓成功后会生成本页可选的监控记录,即可滚仓</li>
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top:1rem"><a href="{{ url_for('strategy_records_page') }}">策略交易记录 →</a></p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/strategy.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}策略记录 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card card-scroll">
|
||||
<h2>趋势回调</h2>
|
||||
{% if trend_rows %}
|
||||
<ul class="list">{% for r in trend_rows %}
|
||||
<li class="list-item"><span>{{ r.symbol }} {{ r.result_label }} · {{ r.closed_at or r.created_at }}</span></li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}<p class="empty-hint">暂无记录</p>{% endif %}
|
||||
</div>
|
||||
<div class="card card-scroll">
|
||||
<h2>顺势加仓</h2>
|
||||
{% if roll_rows %}
|
||||
<ul class="list">{% for r in roll_rows %}
|
||||
<li class="list-item"><span>{{ r.symbol }} {{ r.result_label }} · {{ r.closed_at or r.created_at }}</span></li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}<p class="empty-hint">暂无记录</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}策略记录 - 国内期货监控系统{% endblock %}
|
||||
{% block content %}
|
||||
<div class="split-grid">
|
||||
<div class="card card-scroll">
|
||||
<h2>趋势回调</h2>
|
||||
{% if trend_rows %}
|
||||
<ul class="list">{% for r in trend_rows %}
|
||||
<li class="list-item"><span>{{ r.symbol }} {{ r.result_label }} · {{ r.closed_at or r.created_at }}</span></li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}<p class="empty-hint">暂无记录</p>{% endif %}
|
||||
</div>
|
||||
<div class="card card-scroll">
|
||||
<h2>顺势加仓</h2>
|
||||
{% if roll_rows %}
|
||||
<ul class="list">{% for r in roll_rows %}
|
||||
<li class="list-item"><span>{{ r.symbol }} {{ r.result_label }} · {{ r.closed_at or r.created_at }}</span></li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}<p class="empty-hint">暂无记录</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
+211
-210
@@ -1,210 +1,211 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}下单监控 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/trade.css') }}">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="trade-page">
|
||||
<div class="trade-top-bar">
|
||||
<div class="trade-top-bar-main">
|
||||
<span class="badge dir">{{ trading_mode_label }}</span>
|
||||
<span class="badge {% if ctp_status.connected %}profit{% else %}planned{% endif %}" id="ctp-badge">
|
||||
{% if ctp_status.connected %}CTP 已连接{% else %}CTP 未连接{% endif %}
|
||||
</span>
|
||||
<span class="badge {% if risk_status.can_trade %}profit{% else %}loss{% endif %}" id="risk-badge">{{ risk_status.status_label }}</span>
|
||||
<span class="text-muted">权益 <strong id="cap-display">{{ '%.2f'|format(capital) }}</strong> 元</span>
|
||||
{% if ctp_account.available is defined and ctp_status.connected %}
|
||||
<span class="text-muted">可用 <strong id="avail-display">{{ '%.2f'|format(ctp_account.available) }}</strong> 元</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="trade-top-bar-actions">
|
||||
<button type="button" class="btn-primary btn-ctp-sm" id="btn-ctp-connect">{% if ctp_status.connected %}重连 CTP{% else %}连接 CTP{% endif %}</button>
|
||||
<span class="text-muted trade-top-hint">断线自动重连 · 开盘前 30 分钟自动连接</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid trade-split">
|
||||
<div class="card trade-card" id="order">
|
||||
<h2>期货下单</h2>
|
||||
<div class="card-body">
|
||||
<div class="trade-order-status trade-order-status-compact">
|
||||
<div class="status-row">
|
||||
<span class="text-muted">计仓</span>
|
||||
<strong id="sizing-label">{{ sizing_mode_label }}</strong>
|
||||
{% if sizing_mode == 'fixed' %}
|
||||
<span class="text-muted">· {{ fixed_lots }} 手</span>
|
||||
{% elif sizing_mode in ('amount', 'risk') %}
|
||||
<span class="text-muted">· {{ '%.0f'|format(fixed_amount) }} 元</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-form-rows">
|
||||
<div class="trade-form-line line-3">
|
||||
<div class="symbol-wrap trade-field symbol-mains">
|
||||
<label class="text-label">品种</label>
|
||||
<input type="text" id="trade-symbol" class="symbol-input" placeholder="输入中文或代码,选择主力合约" autocomplete="off">
|
||||
<input type="hidden" id="trade-symbol-code" class="symbol-ths-code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected" id="sym-selected"></div>
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">方向</label>
|
||||
<select id="trade-direction">
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="trade-field" id="field-lots">
|
||||
<label class="text-label">手数</label>
|
||||
<input type="text" id="trade-lots-calc" class="lots-auto" readonly placeholder="—">
|
||||
<input type="hidden" id="trade-lots" value="{{ fixed_lots if sizing_mode == 'fixed' else '1' }}">
|
||||
<p class="hint lots-warn text-loss" id="lots-warn" hidden></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-form-line line-3">
|
||||
<div class="trade-field">
|
||||
<label class="text-label">入场价</label>
|
||||
<div class="price-type-tabs">
|
||||
<button type="button" class="price-tab active" data-type="limit">限价</button>
|
||||
<button type="button" class="price-tab" data-type="market">市价</button>
|
||||
</div>
|
||||
<input type="number" id="trade-price" step="any" placeholder="限价">
|
||||
<p class="hint market-hint" id="market-hint" hidden>市价以 FAK 即时成交报单(非限价挂单)</p>
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">止盈</label>
|
||||
<input type="number" id="trade-tp" step="any">
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">止损</label>
|
||||
<input type="number" id="trade-sl" step="any">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-action-row">
|
||||
<label class="trailing-be-toggle">
|
||||
<input type="checkbox" id="trailing-be" checked>
|
||||
<span>移动保本</span>
|
||||
</label>
|
||||
<span class="hint trade-rr-hint" id="trade-rr-hint" hidden></span>
|
||||
<button type="button" class="btn-primary btn-open" id="btn-open">开仓</button>
|
||||
<p class="hint session-hint text-muted" id="session-hint" hidden>不在交易时间段</p>
|
||||
<p class="trade-order-msg" id="order-msg" hidden></p>
|
||||
</div>
|
||||
|
||||
<div class="trade-footer" id="trade-footer">
|
||||
<p class="hint" id="trade-metrics-hint">填写品种后显示精度与每跳价值;策略自动化请用 <a href="{{ url_for('strategy_page') }}">策略交易</a>。</p>
|
||||
{% if ctp_status.last_error %}
|
||||
<p class="text-loss ctp-install-hint" style="font-size:.78rem;margin-top:.35rem">{{ ctp_status.last_error }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card trade-card" id="positions">
|
||||
<h2>当前持仓</h2>
|
||||
<p class="hint pos-hint">开仓委托先显示「挂单中」,柜台成交后写入监控;超过 <strong>{{ pending_order_timeout_min }}</strong> 分钟未成交自动撤单,可手动撤单。</p>
|
||||
<div class="card-body card-scroll" id="position-live-list">
|
||||
<div class="empty-hint" id="position-placeholder">加载本地持仓…</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card trade-card trade-card-full" id="recommend">
|
||||
<h2>品种推荐</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint">最大手数 = floor(权益 × 保证金上限 <strong>{{ max_margin_pct }}%</strong> ÷ 1手保证金);当前权益 <strong class="text-accent" id="rec-capital">{{ '%.2f'|format(capital) }}</strong> 元。
|
||||
{% if sizing_mode == 'fixed' %}仅显示最大手数 ≥ <strong>{{ fixed_lots }}</strong> 手的品种。{% endif %}
|
||||
保证金优先读取 CTP 柜台合约信息。
|
||||
{% if recommend_updated_at %}<span class="text-muted">每日后台更新 · 最近 {{ recommend_updated_at }}</span>{% else %}<span class="text-muted" id="rec-updated">等待今日后台刷新…</span>{% endif %}
|
||||
</p>
|
||||
<p class="trend-hint">走势:近一周日线,近3日重叠≥70%为震荡;跳空=今日开盘 vs 昨日收盘。成交量为昨日成交手数,成交额=成交量×昨收×合约乘数。支持按走势/跳空/成交量/振幅排序,可按行业筛选。</p>
|
||||
<div class="rec-stats" id="rec-stats"></div>
|
||||
<div class="rec-sort-bar">
|
||||
<label for="rec-industry-filter">行业</label>
|
||||
<select id="rec-industry-filter">
|
||||
<option value="" selected>全部</option>
|
||||
{% for cat in product_categories %}
|
||||
<option value="{{ cat }}">{{ cat }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="rec-sort-key">排序</label>
|
||||
<select id="rec-sort-key">
|
||||
<option value="trend" selected>走势</option>
|
||||
<option value="gap">跳空</option>
|
||||
<option value="volume">成交量</option>
|
||||
<option value="amplitude">振幅</option>
|
||||
</select>
|
||||
<button type="button" class="rec-sort-dir-btn" id="rec-sort-dir" title="切换升序/降序">↓</button>
|
||||
</div>
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table" id="recommend-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<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>1手保证金</th><th>1手手续费</th><th>最大手数</th><th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recommend-list">
|
||||
{% if recommend_rows %}
|
||||
{% for r in recommend_rows %}
|
||||
<tr class="rec-{{ r.status }}{% if r.trend_transition %} rec-trend-break{% endif %}">
|
||||
<td><strong class="{% if r.trend_transition %}trend-name{% endif %}">{{ r.name }}</strong> <span class="text-accent">{{ r.main_code or r.ths }}</span></td>
|
||||
<td>{{ r.exchange }}</td>
|
||||
<td>{{ r.category or '—' }}</td>
|
||||
<td>
|
||||
{% if r.trend_label and r.trend_label != '—' %}
|
||||
<span class="badge trend-badge {% if r.trend in ('break_long', 'break_short') %}break{% elif r.trend == 'long' %}profit{% elif r.trend == 'short' %}loss{% else %}planned{% endif %}" title="{% if r.trend_overlap_pct is not none %}近3日重叠 {{ r.trend_overlap_pct }}%{% endif %}">
|
||||
{% if r.trend_transition %}★ {% endif %}{{ r.trend_label }}
|
||||
</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if r.gap_label and r.gap_label != '—' %}
|
||||
<span class="badge gap-badge {% if r.gap == 'up' %}profit{% elif r.gap == 'down' %}loss{% else %}planned{% endif %}"{% if r.gap_pct %} title="跳空 {{ '%+.2f'|format(r.gap_pct) }}%"{% endif %}>{{ r.gap_label }}</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>{% if r.price %}{{ r.price }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.prev_close is not none %}{{ r.prev_close }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.today_open is not none %}{{ r.today_open }}{% else %}—{% endif %}</td>
|
||||
<td>
|
||||
{% if r.yesterday_change is not none %}
|
||||
<span class="{% if r.yesterday_change > 0 %}rec-change-up{% elif r.yesterday_change < 0 %}rec-change-down{% endif %}">
|
||||
{{ '%+.4f'|format(r.yesterday_change) }}{% if r.yesterday_change_pct is not none %} ({{ '%+.2f'|format(r.yesterday_change_pct) }}%){% endif %}
|
||||
</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>{% if r.yesterday_amplitude_pct is not none %}{{ '%.2f'|format(r.yesterday_amplitude_pct) }}%{% else %}—{% endif %}</td>
|
||||
<td>{% if r.volume is not none %}{{ r.volume }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.turnover is not none %}{{ '%.0f'|format(r.turnover) }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.margin_one_lot %}{{ r.margin_one_lot }}{% if r.margin_source == 'ctp' %} <span class="text-muted">(柜台)</span>{% endif %}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.open_fee_one_lot is defined and r.open_fee_one_lot is not none %}{{ r.open_fee_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.max_lots is not none and r.max_lots > 0 %}{{ r.max_lots }}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge {% if r.status=='ok' %}profit{% else %}planned{% endif %}">{{ r.status_label }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="16" class="empty-hint">等待今日后台刷新推荐…</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
window.TRADE_SIZING_MODE = {{ sizing_mode|tojson }};
|
||||
window.TRADE_FIXED_LOTS = {{ fixed_lots|tojson }};
|
||||
window.TRADE_FIXED_AMOUNT = {{ fixed_amount|tojson }};
|
||||
window.PRODUCT_CATEGORIES = {{ product_categories | default([]) | tojson }};
|
||||
window.__RECOMMEND_ROWS__ = {{ recommend_rows | default([]) | tojson }};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/trade.js') }}?v={{ asset_v }}"></script>
|
||||
{% endblock %}
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}下单监控 - 国内期货监控系统{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/trade.css') }}">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="trade-page">
|
||||
<div class="trade-top-bar">
|
||||
<div class="trade-top-bar-main">
|
||||
<span class="badge dir">{{ trading_mode_label }}</span>
|
||||
<span class="badge {% if ctp_status.connected %}profit{% else %}planned{% endif %}" id="ctp-badge">
|
||||
{% if ctp_status.connected %}CTP 已连接{% else %}CTP 未连接{% endif %}
|
||||
</span>
|
||||
<span class="badge {% if risk_status.can_trade %}profit{% else %}loss{% endif %}" id="risk-badge">{{ risk_status.status_label }}</span>
|
||||
<span class="text-muted">权益 <strong id="cap-display">{{ '%.2f'|format(capital) }}</strong> 元</span>
|
||||
{% if ctp_account.available is defined and ctp_status.connected %}
|
||||
<span class="text-muted">可用 <strong id="avail-display">{{ '%.2f'|format(ctp_account.available) }}</strong> 元</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="trade-top-bar-actions">
|
||||
<button type="button" class="btn-primary btn-ctp-sm" id="btn-ctp-connect">{% if ctp_status.connected %}重连 CTP{% else %}连接 CTP{% endif %}</button>
|
||||
<span class="text-muted trade-top-hint">断线自动重连 · 开盘前 30 分钟自动连接</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-grid trade-split">
|
||||
<div class="card trade-card" id="order">
|
||||
<h2>期货下单</h2>
|
||||
<div class="card-body">
|
||||
<div class="trade-order-status trade-order-status-compact">
|
||||
<div class="status-row">
|
||||
<span class="text-muted">计仓</span>
|
||||
<strong id="sizing-label">{{ sizing_mode_label }}</strong>
|
||||
{% if sizing_mode == 'fixed' %}
|
||||
<span class="text-muted">· {{ fixed_lots }} 手</span>
|
||||
{% elif sizing_mode in ('amount', 'risk') %}
|
||||
<span class="text-muted">· {{ '%.0f'|format(fixed_amount) }} 元</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-form-rows">
|
||||
<div class="trade-form-line line-3">
|
||||
<div class="symbol-wrap trade-field symbol-mains">
|
||||
<label class="text-label">品种</label>
|
||||
<input type="text" id="trade-symbol" class="symbol-input" placeholder="输入中文或代码,选择主力合约" autocomplete="off">
|
||||
<input type="hidden" id="trade-symbol-code" class="symbol-ths-code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected" id="sym-selected"></div>
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">方向</label>
|
||||
<select id="trade-direction">
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="trade-field" id="field-lots">
|
||||
<label class="text-label">手数</label>
|
||||
<input type="text" id="trade-lots-calc" class="lots-auto" readonly placeholder="—">
|
||||
<input type="hidden" id="trade-lots" value="{{ fixed_lots if sizing_mode == 'fixed' else '1' }}">
|
||||
<p class="hint lots-warn text-loss" id="lots-warn" hidden></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-form-line line-3">
|
||||
<div class="trade-field">
|
||||
<label class="text-label">入场价</label>
|
||||
<div class="price-type-tabs">
|
||||
<button type="button" class="price-tab active" data-type="limit">限价</button>
|
||||
<button type="button" class="price-tab" data-type="market">市价</button>
|
||||
</div>
|
||||
<input type="number" id="trade-price" step="any" placeholder="限价">
|
||||
<p class="hint market-hint" id="market-hint" hidden>市价以 FAK 即时成交报单(非限价挂单)</p>
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">止盈</label>
|
||||
<input type="number" id="trade-tp" step="any">
|
||||
</div>
|
||||
<div class="trade-field">
|
||||
<label class="text-label">止损</label>
|
||||
<input type="number" id="trade-sl" step="any">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trade-action-row">
|
||||
<label class="trailing-be-toggle">
|
||||
<input type="checkbox" id="trailing-be" checked>
|
||||
<span>移动保本</span>
|
||||
</label>
|
||||
<span class="hint trade-rr-hint" id="trade-rr-hint" hidden></span>
|
||||
<button type="button" class="btn-primary btn-open" id="btn-open">开仓</button>
|
||||
<p class="hint session-hint text-muted" id="session-hint" hidden>不在交易时间段</p>
|
||||
<p class="trade-order-msg" id="order-msg" hidden></p>
|
||||
</div>
|
||||
|
||||
<div class="trade-footer" id="trade-footer">
|
||||
<p class="hint" id="trade-metrics-hint">填写品种后显示精度与每跳价值;策略自动化请用 <a href="{{ url_for('strategy_page') }}">策略交易</a>。</p>
|
||||
{% if ctp_status.last_error %}
|
||||
<p class="text-loss ctp-install-hint" style="font-size:.78rem;margin-top:.35rem">{{ ctp_status.last_error }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card trade-card" id="positions">
|
||||
<h2>当前持仓</h2>
|
||||
<p class="hint pos-hint">开仓委托先显示「挂单中」,柜台成交后写入监控;超过 <strong>{{ pending_order_timeout_min }}</strong> 分钟未成交自动撤单,可手动撤单。</p>
|
||||
<div class="card-body card-scroll" id="position-live-list">
|
||||
<div class="empty-hint" id="position-placeholder">加载本地持仓…</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card trade-card trade-card-full" id="recommend">
|
||||
<h2>可开仓品种</h2>
|
||||
<div class="card-body">
|
||||
<p class="hint">最大手数 = floor(权益 × 保证金上限 <strong>{{ max_margin_pct }}%</strong> ÷ 1手保证金);当前权益 <strong class="text-accent" id="rec-capital">{{ '%.2f'|format(capital) }}</strong> 元。
|
||||
{% if sizing_mode == 'fixed' %}仅显示最大手数 ≥ <strong>{{ fixed_lots }}</strong> 手的品种。{% endif %}
|
||||
保证金优先读取 CTP 柜台合约信息。
|
||||
{% if recommend_updated_at %}<span class="text-muted">每日后台更新 · 最近 {{ recommend_updated_at }}</span>{% else %}<span class="text-muted" id="rec-updated">等待今日后台刷新…</span>{% endif %}
|
||||
</p>
|
||||
<p class="trend-hint">走势:近一周日线,近3日重叠≥70%为震荡;跳空=今日开盘 vs 昨日收盘。成交量为昨日成交手数,成交额=成交量×昨收×合约乘数。支持按走势/跳空/成交量/振幅排序,可按行业筛选。</p>
|
||||
<div class="rec-stats" id="rec-stats"></div>
|
||||
<div class="rec-sort-bar">
|
||||
<label for="rec-industry-filter">行业</label>
|
||||
<select id="rec-industry-filter">
|
||||
<option value="" selected>全部</option>
|
||||
{% for cat in product_categories %}
|
||||
<option value="{{ cat }}">{{ cat }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="rec-sort-key">排序</label>
|
||||
<select id="rec-sort-key">
|
||||
<option value="trend" selected>走势</option>
|
||||
<option value="gap">跳空</option>
|
||||
<option value="volume">成交量</option>
|
||||
<option value="amplitude">振幅</option>
|
||||
</select>
|
||||
<button type="button" class="rec-sort-dir-btn" id="rec-sort-dir" title="切换升序/降序">↓</button>
|
||||
</div>
|
||||
<div class="trade-table-wrap">
|
||||
<table class="trade-table" id="recommend-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<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>1手保证金</th><th>1手手续费</th><th>最大手数</th><th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recommend-list">
|
||||
{% if recommend_rows %}
|
||||
{% for r in recommend_rows %}
|
||||
<tr class="rec-{{ r.status }}{% if r.trend_transition %} rec-trend-break{% endif %}">
|
||||
<td><strong class="{% if r.trend_transition %}trend-name{% endif %}">{{ r.name }}</strong> <span class="text-accent">{{ r.main_code or r.ths }}</span></td>
|
||||
<td>{{ r.exchange }}</td>
|
||||
<td>{{ r.category or '—' }}</td>
|
||||
<td>
|
||||
{% if r.trend_label and r.trend_label != '—' %}
|
||||
<span class="badge trend-badge {% if r.trend in ('break_long', 'break_short') %}break{% elif r.trend == 'long' %}profit{% elif r.trend == 'short' %}loss{% else %}planned{% endif %}" title="{% if r.trend_overlap_pct is not none %}近3日重叠 {{ r.trend_overlap_pct }}%{% endif %}">
|
||||
{% if r.trend_transition %}★ {% endif %}{{ r.trend_label }}
|
||||
</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if r.gap_label and r.gap_label != '—' %}
|
||||
<span class="badge gap-badge {% if r.gap == 'up' %}profit{% elif r.gap == 'down' %}loss{% else %}planned{% endif %}"{% if r.gap_pct %} title="跳空 {{ '%+.2f'|format(r.gap_pct) }}%"{% endif %}>{{ r.gap_label }}</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>{% if r.price %}{{ r.price }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.prev_close is not none %}{{ r.prev_close }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.today_open is not none %}{{ r.today_open }}{% else %}—{% endif %}</td>
|
||||
<td>
|
||||
{% if r.yesterday_change is not none %}
|
||||
<span class="{% if r.yesterday_change > 0 %}rec-change-up{% elif r.yesterday_change < 0 %}rec-change-down{% endif %}">
|
||||
{{ '%+.4f'|format(r.yesterday_change) }}{% if r.yesterday_change_pct is not none %} ({{ '%+.2f'|format(r.yesterday_change_pct) }}%){% endif %}
|
||||
</span>
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>{% if r.yesterday_amplitude_pct is not none %}{{ '%.2f'|format(r.yesterday_amplitude_pct) }}%{% else %}—{% endif %}</td>
|
||||
<td>{% if r.volume is not none %}{{ r.volume }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.turnover is not none %}{{ '%.0f'|format(r.turnover) }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.margin_one_lot %}{{ r.margin_one_lot }}{% if r.margin_source == 'ctp' %} <span class="text-muted">(柜台)</span>{% endif %}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.open_fee_one_lot is defined and r.open_fee_one_lot is not none %}{{ r.open_fee_one_lot }}{% else %}—{% endif %}</td>
|
||||
<td>{% if r.max_lots is not none and r.max_lots > 0 %}{{ r.max_lots }}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge {% if r.status=='ok' %}profit{% else %}planned{% endif %}">{{ r.status_label }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="16" class="empty-hint">等待今日后台刷新推荐…</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
window.TRADE_SIZING_MODE = {{ sizing_mode|tojson }};
|
||||
window.TRADE_FIXED_LOTS = {{ fixed_lots|tojson }};
|
||||
window.TRADE_FIXED_AMOUNT = {{ fixed_amount|tojson }};
|
||||
window.PRODUCT_CATEGORIES = {{ product_categories | default([]) | tojson }};
|
||||
window.__RECOMMEND_ROWS__ = {{ recommend_rows | default([]) | tojson }};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/trade.js') }}?v={{ asset_v }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user