feat: scaffold gold_monitor web hub, API, PM2 deploy and docs
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}账户 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 14px">账户标注</h2>
|
||||
<div class="grid cols-2">
|
||||
<div class="card">
|
||||
<h3>新增账户</h3>
|
||||
<form method="post" action="{{ url_for('accounts_create') }}">
|
||||
<div class="form-row"><label>名称</label><input name="name" required placeholder="如:模拟盘-A"></div>
|
||||
<div class="form-row"><label>券商</label><input name="broker" placeholder="MT5 券商名"></div>
|
||||
<div class="form-row"><label>登录号</label><input name="login_id" placeholder="可选"></div>
|
||||
<div class="form-row">
|
||||
<label>类型</label>
|
||||
<select name="account_type">
|
||||
<option value="demo">模拟</option>
|
||||
<option value="cent">美分</option>
|
||||
<option value="real">实盘</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row"><label>标注</label><textarea name="note" rows="2" placeholder="用途、纪律备注"></textarea></div>
|
||||
<button class="btn" type="submit">添加</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>已有账户</h3>
|
||||
<table>
|
||||
<thead><tr><th>名称</th><th>类型</th><th>标注</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for a in items %}
|
||||
<tr>
|
||||
<td>{{ a.name }}<div class="muted">{{ a.broker }} {{ a.login_id }}</div></td>
|
||||
<td><span class="tag gray">{{ a.account_type }}</span></td>
|
||||
<td style="min-width:180px">
|
||||
<form method="post" action="{{ url_for('accounts_note', aid=a.id) }}">
|
||||
<input name="note" value="{{ a.note or '' }}" placeholder="标注">
|
||||
<button class="btn secondary" style="margin-top:6px" type="submit">保存</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>#{{ a.id }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}{{ app_name }}{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
{% if session.get('user') %}
|
||||
<header class="topbar">
|
||||
<div class="brand">{{ app_name }}</div>
|
||||
<nav class="nav">
|
||||
<a href="{{ url_for('dashboard') }}" class="{% if request.endpoint=='dashboard' %}active{% endif %}">总览</a>
|
||||
<a href="{{ url_for('records_page') }}" class="{% if request.endpoint and request.endpoint.startswith('record') %}active{% endif %}">交易复盘</a>
|
||||
<a href="{{ url_for('download_page') }}" class="{% if request.endpoint and 'download' in request.endpoint %}active{% endif %}">下载中心</a>
|
||||
<a href="{{ url_for('accounts_page') }}" class="{% if request.endpoint and 'account' in request.endpoint %}active{% endif %}">账户</a>
|
||||
<a href="{{ url_for('machines_page') }}" class="{% if request.endpoint and 'machine' in request.endpoint %}active{% endif %}">机器</a>
|
||||
</nav>
|
||||
<div class="right">{{ session.get('user') }} · <a href="{{ url_for('logout') }}">退出</a></div>
|
||||
</header>
|
||||
{% endif %}
|
||||
<main class="wrap">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for cat, msg in messages %}
|
||||
<div class="flash {{ cat }}">{{ msg }}</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}总览 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 14px">总览</h2>
|
||||
<div class="grid cols-3">
|
||||
<div class="card"><div class="muted">账户</div><div class="stat">{{ accounts|length }}</div></div>
|
||||
<div class="card"><div class="muted">机器</div><div class="stat">{{ machines|length }}</div></div>
|
||||
<div class="card"><div class="muted">最近成交(展示)</div><div class="stat">{{ trades|length }}</div></div>
|
||||
</div>
|
||||
|
||||
<div class="grid cols-2" style="margin-top:14px">
|
||||
<div class="card">
|
||||
<h3>快速入口</h3>
|
||||
<p class="muted">登录后下载通用辅助包,用激活码绑定机器与账户;成交自动上报,网页复盘四周期 K 线。</p>
|
||||
<p style="margin-top:12px">
|
||||
<a class="btn" href="{{ url_for('download_page') }}">下载中心</a>
|
||||
<a class="btn secondary" href="{{ url_for('records_page') }}">交易复盘</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>服务信息</h3>
|
||||
<p class="muted">对外地址:{{ public_base }}</p>
|
||||
<p class="muted">端口:5800 · 健康检查:<a href="/health">/health</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>最近记录</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>时间</th><th>品种</th><th>方向</th><th>盈亏</th><th>账户</th><th>入场形式</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in trades %}
|
||||
<tr>
|
||||
<td class="muted">{{ t.close_time or t.open_time or t.created_at }}</td>
|
||||
<td>{{ t.symbol }}</td>
|
||||
<td><span class="tag {% if t.side=='buy' %}green{% else %}red{% endif %}">{{ t.side }}</span></td>
|
||||
<td>{{ t.profit if t.profit is not none else '—' }}</td>
|
||||
<td>{{ t.account_name or '—' }}</td>
|
||||
<td>{{ t.entry_style or '—' }}</td>
|
||||
<td><a href="{{ url_for('record_detail', tid=t.id) }}">复盘</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="7" class="muted">暂无成交。激活客户端并上报后显示。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,62 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}下载中心 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 14px">下载中心</h2>
|
||||
<div class="grid cols-2">
|
||||
<div class="card">
|
||||
<h3>通用辅助包</h3>
|
||||
<p class="muted">所有机器下载同一 exe。首次启动输入激活码,绑定账户与机器标识。</p>
|
||||
{% if exe_ready %}
|
||||
<p><span class="tag green">已上传 {{ exe_name }}</span></p>
|
||||
<a class="btn" href="{{ url_for('download_file') }}">下载 exe</a>
|
||||
{% else %}
|
||||
<p><span class="tag gray">尚未放置 exe</span></p>
|
||||
<p class="muted">请把打包文件放到服务器 <code>releases/黄金市价辅助.exe</code>。当前可下载说明文件。</p>
|
||||
<a class="btn secondary" href="{{ url_for('download_file') }}">下载说明</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>生成激活码</h3>
|
||||
<form method="post" action="{{ url_for('download_create_activation') }}">
|
||||
<div class="form-row">
|
||||
<label>绑定账户</label>
|
||||
<select name="account_id" required>
|
||||
{% for a in accounts %}
|
||||
<option value="{{ a.id }}">#{{ a.id }} {{ a.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>预绑定机器(可选)</label>
|
||||
<select name="machine_id">
|
||||
<option value="">激活时自动创建</option>
|
||||
{% for m in machines %}
|
||||
<option value="{{ m.id }}">#{{ m.id }} {{ m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn" type="submit">生成激活码</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>激活码列表</h3>
|
||||
<table>
|
||||
<thead><tr><th>激活码</th><th>账户</th><th>机器</th><th>状态</th><th>创建时间</th></tr></thead>
|
||||
<tbody>
|
||||
{% for a in activations %}
|
||||
<tr>
|
||||
<td><code>{{ a.code }}</code></td>
|
||||
<td>{{ a.account_name or a.account_id }}</td>
|
||||
<td>{{ a.machine_name or a.machine_id or '—' }}</td>
|
||||
<td><span class="tag {% if a.status=='active' %}green{% else %}gray{% endif %}">{{ a.status }}</span></td>
|
||||
<td class="muted">{{ a.created_at }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="muted">暂无激活码</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>登录 · {{ app_name }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
|
||||
<style>
|
||||
body { display:flex; align-items:center; justify-content:center; }
|
||||
.login-box { width:100%; max-width:400px; }
|
||||
.login-box h2 {
|
||||
text-align:center; margin:0 0 8px;
|
||||
background: linear-gradient(90deg, #4cc2ff, #7b42ff);
|
||||
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card login-box">
|
||||
<h2>{{ app_name }}</h2>
|
||||
<p class="muted" style="text-align:center;margin-bottom:18px">MT5 黄金 · 下单辅助 · 网页复盘</p>
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for cat, msg in messages %}
|
||||
<div class="flash {{ cat }}">{{ msg }}</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
<form method="post">
|
||||
<div class="form-row">
|
||||
<label>用户名</label>
|
||||
<input name="username" autocomplete="username" value="admin" required>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>密码</label>
|
||||
<input name="password" type="password" autocomplete="current-password" required>
|
||||
</div>
|
||||
<button class="btn" style="width:100%" type="submit">登录</button>
|
||||
</form>
|
||||
<p class="muted" style="margin-top:14px;text-align:center">默认 admin / admin123(请部署后修改 .env)</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}机器 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 14px">机器标注</h2>
|
||||
<div class="grid cols-2">
|
||||
<div class="card">
|
||||
<h3>预登记机器</h3>
|
||||
<p class="muted">也可不预登记:激活时自动创建机器并绑定指纹。</p>
|
||||
<form method="post" action="{{ url_for('machines_create') }}">
|
||||
<div class="form-row"><label>名称</label><input name="name" placeholder="如:书房台式机"></div>
|
||||
<div class="form-row"><label>标注</label><textarea name="note" rows="2"></textarea></div>
|
||||
<button class="btn" type="submit">添加</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>已有机器</h3>
|
||||
<table>
|
||||
<thead><tr><th>名称 / 指纹</th><th>最近在线</th><th>标注</th></tr></thead>
|
||||
<tbody>
|
||||
{% for m in items %}
|
||||
<tr>
|
||||
<td>
|
||||
<form method="post" action="{{ url_for('machines_note', mid=m.id) }}">
|
||||
<input name="name" value="{{ m.name }}" style="margin-bottom:6px">
|
||||
<div class="muted">{{ m.device_fingerprint or '未绑定' }}</div>
|
||||
<textarea name="note" rows="2" style="margin-top:6px">{{ m.note or '' }}</textarea>
|
||||
<button class="btn secondary" style="margin-top:6px" type="submit">保存</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="muted">{{ m.last_seen_at or '—' }}</td>
|
||||
<td>#{{ m.id }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,63 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}复盘 #{{ trade.id }} · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<p><a href="{{ url_for('records_page') }}">← 返回列表</a></p>
|
||||
<h2 style="margin:8px 0 14px">复盘 #{{ trade.id }} · {{ trade.symbol }} · {{ trade.side }}</h2>
|
||||
|
||||
<div class="grid cols-2">
|
||||
<div class="card">
|
||||
<h3>成交信息</h3>
|
||||
<table>
|
||||
<tr><th>账户</th><td>{{ trade.account_name or '—' }}</td></tr>
|
||||
<tr><th>机器</th><td>{{ trade.machine_name or '—' }}</td></tr>
|
||||
<tr><th>手数</th><td>{{ trade.volume }}</td></tr>
|
||||
<tr><th>开仓</th><td>{{ trade.open_price }} / {{ trade.open_time }}</td></tr>
|
||||
<tr><th>平仓</th><td>{{ trade.close_price }} / {{ trade.close_time }}</td></tr>
|
||||
<tr><th>盈亏</th><td>{{ trade.profit }}</td></tr>
|
||||
<tr><th>SL / TP</th><td>{{ trade.sl }} / {{ trade.tp }}</td></tr>
|
||||
<tr><th>单号</th><td>{{ trade.ticket or '—' }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>入场形式标注</h3>
|
||||
<form method="post" action="{{ url_for('record_annotate', tid=trade.id) }}">
|
||||
<div class="form-row">
|
||||
<label>入场形式</label>
|
||||
<select name="entry_style">
|
||||
{% set styles = ['', 'M15关键位+M1强吞没', '回撤接力', '突破延续', '逆势试错', '其它'] %}
|
||||
{% for s in styles %}
|
||||
<option value="{{ s }}" {% if trade.entry_style==s %}selected{% endif %}>{{ s or '(未选)' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>复盘备注</label>
|
||||
<textarea name="review_note" rows="5" placeholder="位不对 / 形不对 / 出场违纪 …">{{ trade.review_note or '' }}</textarea>
|
||||
</div>
|
||||
<button class="btn" type="submit">保存标注</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>四周期 K 线(M1 / M5 / M15 / H1)</h3>
|
||||
<p class="muted">数据来自服务器缓存;由已激活客户端通过 API 上报 bars。无数据时显示占位。</p>
|
||||
<div class="chart-grid" style="margin-top:10px">
|
||||
{% for tf in ['M1','M5','M15','H1'] %}
|
||||
<div class="chart-box">
|
||||
<h4>{{ tf }} · {{ charts[tf]|length }} 根</h4>
|
||||
{% if charts[tf] %}
|
||||
<canvas class="chart-canvas" id="chart-{{ tf }}"
|
||||
data-bars='{{ charts[tf]|tojson }}'></canvas>
|
||||
{% else %}
|
||||
<div class="empty-chart">暂无 {{ tf }} K 线缓存</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/charts.js') }}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}交易复盘 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 6px">交易记录与复盘</h2>
|
||||
<p class="muted" style="margin:0 0 14px">参考 crypto_monitor 复盘流程:列表筛选 → 点开详情 → 四周期 K 线 + 入场形式标注。</p>
|
||||
<div class="card">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th><th>品种</th><th>方向</th><th>手数</th><th>开仓价</th><th>平仓价</th>
|
||||
<th>盈亏</th><th>账户</th><th>机器</th><th>入场形式</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in trades %}
|
||||
<tr>
|
||||
<td>{{ t.id }}</td>
|
||||
<td>{{ t.symbol }}</td>
|
||||
<td><span class="tag {% if t.side=='buy' %}green{% else %}red{% endif %}">{{ t.side }}</span></td>
|
||||
<td>{{ t.volume }}</td>
|
||||
<td>{{ t.open_price }}</td>
|
||||
<td>{{ t.close_price if t.close_price is not none else '—' }}</td>
|
||||
<td style="color:{% if t.profit and t.profit>0 %}var(--green){% elif t.profit and t.profit<0 %}var(--red){% else %}inherit{% endif %}">
|
||||
{{ t.profit if t.profit is not none else '—' }}
|
||||
</td>
|
||||
<td>{{ t.account_name or '—' }}</td>
|
||||
<td>{{ t.machine_name or '—' }}</td>
|
||||
<td>{{ t.entry_style or '—' }}</td>
|
||||
<td><a href="{{ url_for('record_detail', tid=t.id) }}">打开复盘</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="11" class="muted">暂无数据。客户端上报成交后出现在此。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user