Initial standalone crypto_okx with one-click deploy.
Add deploy/manage.sh bootstrap for git.bz121.com/dekun/crypto_okx and point docs at this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
{# 实例数据看板:只读活跃监控总览 #}
|
||||
<div class="card full inst-dash-card" id="instance-dashboard" data-inst-dashboard="1">
|
||||
<div class="inst-dash-head">
|
||||
<div>
|
||||
<h2 style="margin-bottom:4px">数据看板</h2>
|
||||
<p class="muted inst-dash-desc">本户活跃监控总览 · 只读 · 后台快照 + SSE · 无数据的区块不显示</p>
|
||||
</div>
|
||||
<div class="inst-dash-head-actions">
|
||||
<span class="muted inst-dash-updated" id="inst-dash-updated">—</span>
|
||||
<button type="button" class="btn-sm" id="inst-dash-refresh">刷新</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="inst-dash-status muted" id="inst-dash-status"></p>
|
||||
<div class="inst-dash-sections" id="inst-dash-sections"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
{# 系统设置 · 导航显示开关(SSR 预渲染,保存仍走 API) #}
|
||||
<div class="settings-tab-inner" id="display-prefs-card">
|
||||
<h2>导航显示</h2>
|
||||
<p class="settings-env-hint">以下开关控制顶栏导航与系统设置内区块是否显示,保存后立即生效.系统设置为固定项.</p>
|
||||
<div id="display-prefs-form" class="display-prefs-form" data-prefs-ssr="1">
|
||||
{% if display_meta %}
|
||||
{% for group in display_meta %}
|
||||
<div class="display-prefs-group">
|
||||
<h3 class="settings-subcard-title">{{ group.group }}</h3>
|
||||
<div class="display-prefs-checks">
|
||||
{% for item in group.entries %}
|
||||
<label class="chk-label">
|
||||
<input type="checkbox" data-pref-key="{{ item.key }}"{% if item.key in ('show_nav_dashboard', 'show_nav_account_ledger', 'show_nav_system_guide') %}{% if display.get(item.key) %} checked{% endif %}{% elif display.get(item.key, true) %} checked{% endif %}>
|
||||
{{ item.label }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="display-prefs-loading muted">加载中…</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="settings-actions-row">
|
||||
<button type="button" class="btn-primary btn-sm" id="display-prefs-save">保存导航设置</button>
|
||||
<span class="settings-status-line" id="display-prefs-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,152 @@
|
||||
{# Hub iframe tab fragment — shared via embed_templates #}
|
||||
{% macro period_stats_pane(period_key, s) %}
|
||||
{% set win_pct = s.win_rate_pct if s.win_rate_pct is not none else 0 %}
|
||||
{% set profit_sum = (s.net_pnl_u + s.loss_sum_u) if s.closed_count else 0 %}
|
||||
{% set loss_sum = s.loss_sum_u %}
|
||||
{% set pnl_total = profit_sum + loss_sum %}
|
||||
{% set profit_bar_w = (profit_sum / pnl_total * 100) if pnl_total > 0 else 0 %}
|
||||
{% set loss_bar_w = (loss_sum / pnl_total * 100) if pnl_total > 0 else 0 %}
|
||||
{% set net_cls = 'pos-pnl-profit' if s.net_pnl_u > 0 else ('pos-pnl-loss' if s.net_pnl_u < 0 else '') %}
|
||||
<div class="stats-period-pane" data-stats-period="{{ period_key }}" role="tabpanel"{% if period_key != 'day' %} hidden{% endif %}>
|
||||
<div class="stats-period-range">{{ s.range_label }}</div>
|
||||
<div class="inst-stats-viz">
|
||||
{% if s.closed_count %}
|
||||
<div class="inst-stats-kpis">
|
||||
<div class="inst-stats-kpi inst-stats-kpi--pnl">
|
||||
<span class="inst-stats-kpi-val {{ net_cls }}">{% if s.net_pnl_u > 0 %}+{% endif %}{{ funds_fmt(s.net_pnl_u) }}U</span>
|
||||
<span class="inst-stats-kpi-lbl">净盈亏</span>
|
||||
</div>
|
||||
<div class="inst-stats-kpi inst-stats-kpi--win">
|
||||
<div class="inst-stats-ring" style="--win-pct: {{ win_pct }}">
|
||||
<span class="inst-stats-ring-label">{% if s.win_rate_pct is not none %}{{ win_pct|round(0)|int }}%{% else %}—{% endif %}</span>
|
||||
</div>
|
||||
<span class="inst-stats-kpi-lbl">{{ s.win_count }}胜 {{ s.loss_count }}负</span>
|
||||
</div>
|
||||
<div class="inst-stats-kpi inst-stats-kpi--trades">
|
||||
<span class="inst-stats-kpi-val">{{ s.opens_count }} / {{ s.closed_count }}</span>
|
||||
<span class="inst-stats-kpi-lbl">开单 / 平仓</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inst-stats-block">
|
||||
<div class="inst-stats-block-title">盈亏构成</div>
|
||||
<div class="inst-stats-stacked-bar">
|
||||
<div class="inst-stats-stacked-fill inst-stats-stacked-fill--profit" style="width: {{ '%.1f'|format(profit_bar_w) }}%"></div>
|
||||
<div class="inst-stats-stacked-fill inst-stats-stacked-fill--loss" style="width: {{ '%.1f'|format(loss_bar_w) }}%"></div>
|
||||
</div>
|
||||
<div class="inst-stats-bar-labels">
|
||||
<span class="pos-pnl-profit">盈利 {{ funds_fmt(profit_sum) }}U</span>
|
||||
<span class="pos-pnl-loss">亏损 {{ funds_fmt(loss_sum) }}U</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inst-stats-block inst-stats-block--risk">
|
||||
<div class="inst-stats-risk-grid">
|
||||
<div class="inst-stats-risk-item">
|
||||
<span class="k">最大回撤</span>
|
||||
<span class="v pos-pnl-loss">{{ funds_fmt(s.max_drawdown_u) }}U</span>
|
||||
</div>
|
||||
<div class="inst-stats-risk-item">
|
||||
<span class="k">连续亏损</span>
|
||||
<span class="v">{{ s.consecutive_losses }} 笔</span>
|
||||
</div>
|
||||
<div class="inst-stats-risk-item">
|
||||
<span class="k">最长连亏日</span>
|
||||
<span class="v">{{ s.max_loss_streak_days }} 天</span>
|
||||
</div>
|
||||
<div class="inst-stats-risk-item">
|
||||
<span class="k">最大亏损日</span>
|
||||
<span class="v">{% if s.worst_day %}{{ s.worst_day }} ({{ funds_fmt(s.worst_day_pnl) }}U){% else %}—{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="inst-stats-empty">当前区间暂无平仓数据</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<details class="inst-stats-details" open>
|
||||
<summary>详细指标</summary>
|
||||
<div class="stats-detail">
|
||||
<div class="stat-item"><div class="label">开单次数</div><div class="value">{{ s.opens_count }}</div></div>
|
||||
<div class="stat-item"><div class="label">平仓笔数</div><div class="value">{{ s.closed_count }}</div></div>
|
||||
<div class="stat-item"><div class="label">胜率</div><div class="value">{% if s.win_rate_pct is not none %}{{ s.win_rate_pct }}%{% else %}-{% endif %}</div></div>
|
||||
<div class="stat-item"><div class="label">净盈亏(U)</div><div class="value">{{ funds_fmt(s.net_pnl_u) }}</div></div>
|
||||
<div class="stat-item"><div class="label">亏损额合计(U)</div><div class="value">{{ funds_fmt(s.loss_sum_u) }}</div></div>
|
||||
<div class="stat-item"><div class="label">单笔最大亏损(U)</div><div class="value">{% if s.max_single_loss is not none %}{{ funds_fmt(s.max_single_loss) }}{% else %}-{% endif %}</div></div>
|
||||
<div class="stat-item"><div class="label">单笔最大盈利(U)</div><div class="value">{% if s.max_single_profit is not none %}{{ funds_fmt(s.max_single_profit) }}{% else %}-{% endif %}</div></div>
|
||||
<div class="stat-item"><div class="label">最大回撤(U)</div><div class="value">{{ funds_fmt(s.max_drawdown_u) }}</div></div>
|
||||
<div class="stat-item"><div class="label">当前连续亏损笔数</div><div class="value">{{ s.consecutive_losses }}</div></div>
|
||||
<div class="stat-item"><div class="label">最长连续亏损(交易日)</div><div class="value">{{ s.max_loss_streak_days }} 天</div></div>
|
||||
<div class="stat-item"><div class="label">期内最大亏损日</div><div class="value">{% if s.worst_day %}{{ s.worst_day }}({{ funds_fmt(s.worst_day_pnl) }}U){% else %}-{% endif %}</div></div>
|
||||
</div>
|
||||
</details>
|
||||
{% if period_key == 'all' %}
|
||||
<div class="inst-stats-block inst-stats-monthly" style="margin-top:14px">
|
||||
<div class="inst-stats-block-title">按月统计</div>
|
||||
{% if s.monthly_rows %}
|
||||
<div class="inst-stats-month-table-wrap">
|
||||
<table class="inst-stats-month-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>月份</th>
|
||||
<th>开单</th>
|
||||
<th>平仓</th>
|
||||
<th>胜率</th>
|
||||
<th>净盈亏</th>
|
||||
<th>最大回撤</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for m in s.monthly_rows %}
|
||||
{% set m_net_cls = 'pos-pnl-profit' if m.net_pnl_u > 0 else ('pos-pnl-loss' if m.net_pnl_u < 0 else '') %}
|
||||
<tr>
|
||||
<td>{{ m.month_key }}</td>
|
||||
<td>{{ m.opens_count }}</td>
|
||||
<td>{{ m.closed_count }}</td>
|
||||
<td>{% if m.win_rate_pct is not none %}{{ m.win_rate_pct }}%{% else %}—{% endif %}</td>
|
||||
<td class="{{ m_net_cls }}">{% if m.net_pnl_u > 0 %}+{% endif %}{{ funds_fmt(m.net_pnl_u) }}</td>
|
||||
<td class="pos-pnl-loss">{{ funds_fmt(m.max_drawdown_u) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="inst-stats-empty" style="margin-top:0">暂无按月平仓数据</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
<div class="grid">
|
||||
{% if page == 'dashboard' %}
|
||||
{% include 'dashboard_panel.html' %}
|
||||
{% elif page == 'account_ledger' %}
|
||||
{% include 'account_ledger_panel.html' %}
|
||||
{% elif page == 'key_monitor' %}
|
||||
{% include 'key_monitor_panel.html' %}
|
||||
{% elif page == 'options' %}
|
||||
{% include 'options_panel.html' %}
|
||||
{% elif page == 'options_review' %}
|
||||
{% include 'options_review_panel.html' %}
|
||||
{% elif page == 'hedge_plan' %}
|
||||
{% include 'hedge_plan_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{% if page == 'env_config' %}
|
||||
{% include 'env_config_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'risk_policy' %}
|
||||
{% include 'risk_policy_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'system_guide' %}
|
||||
{% include 'system_guide_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'settings' %}
|
||||
{% include 'settings_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<script src="/static/instance_theme.js?v=50"></script>
|
||||
<script src="/static/autofill_guard.js?v=1"></script>
|
||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=13">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=117">
|
||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||
<script src="/static/open_submit_gate.js?v=1"></script>
|
||||
<meta name="theme-color" content="#0b0d14">
|
||||
<title>{{ pwa_app_name }}</title>
|
||||
</head>
|
||||
<body
|
||||
data-embed-shell="1"
|
||||
data-intraday-discipline="{% if intraday_discipline %}1{% else %}0{% endif %}"
|
||||
data-risk-percent="{{ risk_percent }}"
|
||||
data-page="{{ initial_tab }}"
|
||||
data-position-sizing-mode="{{ position_sizing_mode }}"
|
||||
data-btc-leverage="{{ btc_leverage }}"
|
||||
data-alt-leverage="{{ alt_leverage }}"
|
||||
data-full-margin-buffer="{{ full_margin_buffer_ratio }}"
|
||||
data-balance-refresh-ms="{{ balance_refresh_seconds * 1000 }}"
|
||||
data-price-refresh-ms="{{ price_refresh_seconds * 1000 }}"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>加密货币|OKX 期权与对冲</h1>
|
||||
</div>
|
||||
<nav class="top-nav embed-top-nav" aria-label="实例导航">
|
||||
<a href="/dashboard" data-embed-tab="dashboard" class="{% if initial_tab == 'dashboard' %}active{% endif %}"{% if not display.show_nav_dashboard %} style="display:none"{% endif %}>数据看板</a>
|
||||
<a href="/account_ledger" data-embed-tab="account_ledger" class="{% if initial_tab == 'account_ledger' %}active{% endif %}"{% if not display.show_nav_account_ledger %} style="display:none"{% endif %}>账户流水</a>
|
||||
<a href="/key_monitor" data-embed-tab="key_monitor" class="{% if initial_tab == 'key_monitor' %}active{% endif %}"{% if not display.show_nav_key_monitor %} style="display:none"{% endif %}>关键位监控</a>
|
||||
{% if options_nav_visible and display.show_nav_options %}
|
||||
<a href="/options" data-embed-tab="options" class="{% if initial_tab == 'options' %}active{% endif %}">期权</a>
|
||||
{% endif %}
|
||||
{% if options_nav_visible and display.show_nav_options_review %}
|
||||
<a href="/options/review" data-embed-tab="options_review" class="{% if initial_tab == 'options_review' %}active{% endif %}">期权复盘</a>
|
||||
{% endif %}
|
||||
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
|
||||
<a href="/hedge-plan" data-embed-tab="hedge_plan" class="{% if initial_tab == 'hedge_plan' %}active{% endif %}">对冲计划</a>
|
||||
{% endif %}
|
||||
{% if display.show_nav_risk_policy %}
|
||||
<a href="/risk_policy" data-embed-tab="risk_policy" class="{% if initial_tab == 'risk_policy' %}active{% endif %}">风控说明</a>
|
||||
{% endif %}
|
||||
<a href="/system_guide" data-embed-tab="system_guide" class="{% if initial_tab == 'system_guide' %}active{% endif %}"{% if not display.show_nav_system_guide %} style="display:none"{% endif %}>系统说明</a>
|
||||
{% if display.show_nav_env_config %}
|
||||
<a href="/env_config" data-embed-tab="env_config" class="{% if initial_tab == 'env_config' %}active{% endif %}">env配置</a>
|
||||
{% endif %}
|
||||
<a href="/settings" data-embed-tab="settings" class="{% if initial_tab == 'settings' %}active{% endif %}">系统设置</a>
|
||||
</nav>
|
||||
<div id="embed-flash" class="flash" style="display:none" role="status"></div>
|
||||
|
||||
{% include 'instance_header_panel.html' %}
|
||||
{% if initial_tab not in ('settings', 'risk_policy', 'system_guide', 'env_config') and include_transfer_block %}
|
||||
{% include 'instance_top_bar.html' %}
|
||||
{% endif %}
|
||||
|
||||
<div id="embed-page-root">
|
||||
{% include 'embed_page_fragment.html' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="imgModal" onclick="closeModal()">
|
||||
<img id="bigImg" src="" alt="screenshot">
|
||||
</div>
|
||||
<div class="detail-modal" id="detailModal" onclick="closeDetailModal(event)">
|
||||
<div class="panel" onclick="event.stopPropagation()">
|
||||
<div class="panel-head">
|
||||
<div class="panel-title" id="detailTitle">详情</div>
|
||||
<div class="panel-actions">
|
||||
<button type="button" class="panel-fs" onclick="expandDetailToFullscreen()">全屏</button>
|
||||
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body" id="detailBody"></div>
|
||||
<div id="detailImages" class="journal-detail-images" style="display:none"></div>
|
||||
<img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手机端主导航(≤720px);桌面不显示 -->
|
||||
<nav id="inst-mobile-tabbar" class="inst-mobile-tabbar" aria-label="手机主导航">
|
||||
{% if options_nav_visible and display.show_nav_options %}
|
||||
<a href="/options" class="inst-m-tab{% if initial_tab == 'options' %} active{% endif %}" data-embed-tab="options">期权</a>
|
||||
{% endif %}
|
||||
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
|
||||
<a href="/hedge-plan" class="inst-m-tab{% if initial_tab == 'hedge_plan' %} active{% endif %}" data-embed-tab="hedge_plan">对冲</a>
|
||||
{% endif %}
|
||||
<a href="/key_monitor" class="inst-m-tab{% if initial_tab == 'key_monitor' %} active{% endif %}" data-embed-tab="key_monitor"{% if not display.show_nav_key_monitor %} style="display:none"{% endif %}>关键位</a>
|
||||
<button type="button" class="inst-m-tab" data-embed-tab="more" id="inst-m-tab-more" aria-haspopup="dialog" aria-expanded="false">更多</button>
|
||||
</nav>
|
||||
<div id="inst-mobile-more" class="inst-mobile-more" aria-hidden="true">
|
||||
<div class="inst-mobile-more-backdrop" id="inst-mobile-more-backdrop"></div>
|
||||
<div class="inst-mobile-more-sheet" role="dialog" aria-modal="true" aria-labelledby="inst-mobile-more-title">
|
||||
<div class="inst-mobile-more-handle" aria-hidden="true"></div>
|
||||
<h2 id="inst-mobile-more-title" class="inst-mobile-more-title">更多</h2>
|
||||
<p class="inst-mobile-more-hint">次要页面 · 完整界面请用电脑</p>
|
||||
<nav class="inst-mobile-more-nav" aria-label="更多页面">
|
||||
<a href="/dashboard" data-embed-tab="dashboard"{% if not display.show_nav_dashboard %} style="display:none"{% endif %}>数据看板</a>
|
||||
<a href="/account_ledger" data-embed-tab="account_ledger"{% if not display.show_nav_account_ledger %} style="display:none"{% endif %}>账户流水</a>
|
||||
{% if options_nav_visible and display.show_nav_options_review %}
|
||||
<a href="/options/review" data-embed-tab="options_review">期权复盘</a>
|
||||
{% endif %}
|
||||
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
|
||||
<a href="/hedge-plan" data-embed-tab="hedge_plan">对冲计划</a>
|
||||
{% endif %}
|
||||
{% if display.show_nav_risk_policy %}
|
||||
<a href="/risk_policy" data-embed-tab="risk_policy">风控说明</a>
|
||||
{% endif %}
|
||||
<a href="/system_guide" data-embed-tab="system_guide"{% if not display.show_nav_system_guide %} style="display:none"{% endif %}>系统说明</a>
|
||||
{% if display.show_nav_env_config %}
|
||||
<a href="/env_config" data-embed-tab="env_config">env配置</a>
|
||||
{% endif %}
|
||||
<a href="/settings" data-embed-tab="settings">系统设置</a>
|
||||
</nav>
|
||||
<button type="button" class="inst-mobile-more-close" id="inst-mobile-more-close">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=10"></script>
|
||||
<script src="/static/journal_upload_slots.js?v=4"></script>
|
||||
<script src="/static/instance_records_mobile.js?v=2"></script>
|
||||
<script src="/static/time_close_ui.js?v=3"></script>
|
||||
<script src="/static/form_submit_guard.js?v=2"></script>
|
||||
<script src="/static/journal_form_save.js?v=3"></script>
|
||||
<script>
|
||||
const ORDER_ENTRY_MODEL_TRADE_STYLE = {{ entry_model_trade_style_map | tojson }};
|
||||
const ORDER_ENTRY_MODEL_CATEGORIES = {{ entry_model_categories | tojson }};
|
||||
const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | tojson }};
|
||||
</script>
|
||||
<script src="/static/order_entry_model.js?v=5"></script>
|
||||
<script src="/static/manual_order_rr_preview.js?v=5"></script>
|
||||
<script src="/static/symbol_live_price.js?v=2"></script>
|
||||
<script src="/static/key_monitor_form.js?v=2"></script>
|
||||
<script src="/static/instance_stats.js?v=5"></script>
|
||||
{% include 'embed_boot_scripts.html' %}
|
||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/static/instance_dashboard.js?v=5"></script>
|
||||
<script src="/static/account_ledger.js?v=1"></script>
|
||||
<script>
|
||||
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=21"></script>
|
||||
<script src="/static/instance_live.js?v=6"></script>
|
||||
<script src="/static/instance_embed.js?v=31"></script>
|
||||
<script src="/static/instance_mobile_nav.js?v=2"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,108 @@
|
||||
{# env配置:CSS Tab(无需 JS)+ 双列表单 #}
|
||||
<div class="env-config-page full">
|
||||
<div class="env-config-head card">
|
||||
<div class="env-config-head-row">
|
||||
<div>
|
||||
<h2>env 配置</h2>
|
||||
<p class="settings-env-hint env-config-head-hint">按分类修改,改完点保存.含「需重启」的项请用「保存并重启」.<strong>AI 配置</strong>请在中控 → 系统设置 → AI 配置统一维护.</p>
|
||||
</div>
|
||||
<div class="env-config-toolbar">
|
||||
<button type="button" class="btn-primary btn-sm" id="env-config-save">保存</button>
|
||||
<button type="button" class="btn-secondary btn-sm" id="env-config-save-restart">保存并重启</button>
|
||||
<button type="button" class="btn-secondary btn-sm" id="env-config-reload">重新加载</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="settings-status-line" id="env-config-status"></span>
|
||||
</div>
|
||||
|
||||
{% if env_config_groups %}
|
||||
{% set ns = namespace(mode_idx=0) %}
|
||||
{% for group in env_config_groups %}
|
||||
{% if '期权/对冲模式' in (group.title or '') %}{% set ns.mode_idx = loop.index0 %}{% endif %}
|
||||
{% endfor %}
|
||||
<div class="env-config-body card" data-env-ssr="1" id="env-config-body" data-env-mode-section-idx="{{ ns.mode_idx }}">
|
||||
{% for group in env_config_groups %}
|
||||
<input type="radio" name="env-section" id="env-sec-{{ loop.index0 }}" class="env-tab-radio"{% if loop.first %} checked{% endif %}>
|
||||
{% endfor %}
|
||||
<div class="env-config-tabs" role="tablist" aria-label="配置分类">
|
||||
{% for group in env_config_groups %}
|
||||
<label for="env-sec-{{ loop.index0 }}" class="env-tab-btn" role="tab">{{ group.title }}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="env-config-panels" id="env-config-grid">
|
||||
{% for group in env_config_groups %}
|
||||
<section class="env-panel env-panel--{{ loop.index0 }}" role="tabpanel">
|
||||
{% if group.has_restart %}
|
||||
<p class="env-panel-hint">本组含需重启项,修改后请点「保存并重启」.</p>
|
||||
{% endif %}
|
||||
<div class="env-form-grid">
|
||||
{% for field in group.fields %}
|
||||
<div class="env-field-row{% if field.restart_required %} env-field-row--restart{% endif %}" data-env-key="{{ field.key }}"{% if field.hidden %} hidden style="display:none"{% endif %}>
|
||||
<label class="env-field-label" for="env-f-{{ field.key }}">
|
||||
{{ field.label or field.key }}
|
||||
{% if field.restart_required %}<span class="env-restart-mark" title="需重启">*</span>{% endif %}
|
||||
</label>
|
||||
{% if field.note %}
|
||||
<div class="env-field-note muted">{{ field.note }}</div>
|
||||
{% endif %}
|
||||
{% if field.type == 'bool' %}
|
||||
<select class="env-field-input" id="env-f-{{ field.key }}" data-env-key="{{ field.key }}">
|
||||
{% set cur = (field.current or field.default or 'false')|lower %}
|
||||
<option value="true"{% if cur in ('true', '1', 'yes', 'on') %} selected{% endif %}>开启</option>
|
||||
<option value="false"{% if cur not in ('true', '1', 'yes', 'on') %} selected{% endif %}>关闭</option>
|
||||
</select>
|
||||
{% elif field.type == 'select' and field.options %}
|
||||
{% set cur = (field.current or field.default or '') %}
|
||||
<select class="env-field-input" id="env-f-{{ field.key }}" data-env-key="{{ field.key }}">
|
||||
{% for opt in field.options %}
|
||||
<option value="{{ opt.value }}"{% if cur == opt.value %} selected{% endif %}>{{ opt.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% elif field.sensitive %}
|
||||
{% if field.has_value %}
|
||||
<div class="env-sensitive-current muted">已配置 <span class="env-masked-value">{{ field.masked }}</span></div>
|
||||
{% endif %}
|
||||
<input
|
||||
class="env-field-input"
|
||||
id="env-f-{{ field.key }}"
|
||||
type="password"
|
||||
data-env-key="{{ field.key }}"
|
||||
placeholder="{% if field.has_value %}修改时填写新值,留空不修改{% else %}请输入{% endif %}"
|
||||
autocomplete="new-password"
|
||||
data-lpignore="true"
|
||||
data-1p-ignore="true"
|
||||
data-bwignore="true"
|
||||
data-form-type="other"
|
||||
readonly
|
||||
onfocus="this.removeAttribute('readonly')"
|
||||
>
|
||||
{% else %}
|
||||
<input
|
||||
class="env-field-input"
|
||||
id="env-f-{{ field.key }}"
|
||||
type="text"
|
||||
data-env-key="{{ field.key }}"
|
||||
value="{{ field.current or field.default or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
data-lpignore="true"
|
||||
data-1p-ignore="true"
|
||||
data-bwignore="true"
|
||||
data-form-type="other"
|
||||
>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div id="env-config-grid" class="env-config-loading-wrap card">
|
||||
<div class="env-config-loading muted">加载配置中…</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
{% if force_close.enabled %}
|
||||
<span class="force-close-badge" id="force-close-header-badge" role="status"
|
||||
title="北京时间 {{ force_close.hour_label }} 整点未平仓将市价强制清仓(result=强制清仓)"
|
||||
data-force-close-at-ms="{{ force_close.next_at_ms or '' }}"
|
||||
data-force-close-active="{{ '1' if force_close.active else '0' }}">
|
||||
{{ force_close.label }} 已开启 · <span class="force-close-header-cd">{{ force_close.countdown or '--:--:--' }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% if force_close.enabled %}
|
||||
<span class="pos-symbol-force-close pos-force-close-meta" id="order-force-close-wrap-{{ o.id }}"
|
||||
data-force-close-at-ms="{{ o.force_close_at_ms or force_close.next_at_ms or '' }}"
|
||||
data-force-close-active="{{ '1' if (o.force_close_active or force_close.active) else '0' }}">
|
||||
<span class="pos-force-close-label">{{ o.force_close_label or force_close.label }}</span>
|
||||
· <span class="pos-force-close-cd" id="order-force-close-cd-{{ o.id }}">{{ o.force_close_countdown or force_close.countdown or '--:--:--' }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,26 @@
|
||||
<details class="tip-collapse gate-top-tips-collapse">
|
||||
<summary class="tip-collapse-summary">
|
||||
实时价格更新:<span id="price-last-updated">--</span>(北京时间 UTC+8)
|
||||
<span class="tip-collapse-hint">· 划转规则</span>
|
||||
</summary>
|
||||
<div class="tip-collapse-body rule-tip gate-transfer-tip">
|
||||
划转:自动划转 {{ '开启' if auto_transfer_enabled else '关闭' }}(每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong>起该整点小时内尝试;账簿按 <strong>UTC 自然日</strong>去重;将 {{ auto_transfer_to }} 调整至 {{ transfer_amount_fmt|default(funds_fmt(auto_transfer_amount)) }}U:不足从 {{ auto_transfer_from }} 划入,超出划回 {{ auto_transfer_from }};<strong>持仓中不划转</strong>并微信通知)
|
||||
</div>
|
||||
</details>
|
||||
<form action="/manual_transfer" method="post" class="form-row gate-transfer-form" autocomplete="off">
|
||||
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
|
||||
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
|
||||
<input name="amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额U" required
|
||||
autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-bwignore="true" data-form-type="other" readonly>
|
||||
<select name="from_account" autocomplete="off">
|
||||
<option value="funding" {% if auto_transfer_from == 'funding' %}selected{% endif %}>from: funding</option>
|
||||
<option value="swap" {% if auto_transfer_from == 'swap' %}selected{% endif %}>from: swap</option>
|
||||
<option value="spot" {% if auto_transfer_from == 'spot' %}selected{% endif %}>from: spot</option>
|
||||
</select>
|
||||
<select name="to_account" autocomplete="off">
|
||||
<option value="swap" {% if auto_transfer_to == 'swap' %}selected{% endif %}>to: swap</option>
|
||||
<option value="funding" {% if auto_transfer_to == 'funding' %}selected{% endif %}>to: funding</option>
|
||||
<option value="spot" {% if auto_transfer_to == 'spot' %}selected{% endif %}>to: spot</option>
|
||||
</select>
|
||||
<button type="submit">手动划转</button>
|
||||
</form>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
{# 统一顶栏:状态 + 筛选(上)· 统计条(下) #}
|
||||
<div class="instance-header-panel card">
|
||||
<div class="instance-header-toolbar">
|
||||
<div class="instance-header-toolbar-filter instance-desktop-only">
|
||||
<span class="list-window-label" title="列表按 UTC 时间筛选,默认本月">UTC {{ list_window.label }}</span>
|
||||
<label class="list-window-preset">预设
|
||||
<select id="win-preset-select" onchange="toggleListWindowCustom()">
|
||||
<option value="utc_this_month" {% if list_window.preset == 'utc_this_month' %}selected{% endif %}>本月</option>
|
||||
<option value="utc_last3m" {% if list_window.preset == 'utc_last3m' %}selected{% endif %}>近3月</option>
|
||||
<option value="utc_last6m" {% if list_window.preset == 'utc_last6m' %}selected{% endif %}>近6月</option>
|
||||
<option value="all" {% if list_window.preset == 'all' %}selected{% endif %}>全部</option>
|
||||
<option value="utc_today" {% if list_window.preset == 'utc_today' %}selected{% endif %}>UTC 当日</option>
|
||||
<option value="utc_last24h" {% if list_window.preset == 'utc_last24h' %}selected{% endif %}>近 24 小时</option>
|
||||
<option value="utc_last7d" {% if list_window.preset == 'utc_last7d' %}selected{% endif %}>近 7 天</option>
|
||||
<option value="custom" {% if list_window.preset == 'custom' %}selected{% endif %}>自定义</option>
|
||||
</select>
|
||||
</label>
|
||||
<span id="win-custom-range" class="list-window-custom" style="{% if list_window.preset != 'custom' %}display:none{% endif %}">
|
||||
<label>起 <input type="datetime-local" id="win-from-utc" value="{{ list_window.start_utc.strftime('%Y-%m-%dT%H:%M') }}"></label>
|
||||
<label>止 <input type="datetime-local" id="win-to-utc" value="{{ list_window.end_utc.strftime('%Y-%m-%dT%H:%M') }}"></label>
|
||||
</span>
|
||||
<button type="button" class="list-window-apply" onclick="applyListWindow()">应用</button>
|
||||
<span class="list-window-hint" title="统计分析页按北京时间 {{ stats_bundle.stats_reset_hour|default(reset_hour) }}:00 切交易日">统计切日 {{ stats_bundle.stats_reset_hour|default(reset_hour) }}:00</span>
|
||||
</div>
|
||||
<div class="instance-header-toolbar-end">
|
||||
<div class="instance-toolbar-status">
|
||||
<div class="exchange-tag">{{ exchange_display }}</div>
|
||||
{% if trade_policy.badge_text %}
|
||||
<span class="trade-policy-badge" title="账户交易限制(.env)">{{ trade_policy.badge_text }}</span>
|
||||
{% endif %}
|
||||
{% include 'force_close_header_badge.html' %}
|
||||
<span class="risk-status-badge risk-status-{{ risk_status.status|default('normal') }}" id="account-risk-badge" role="status" title="{{ risk_status.reason|default('', true) }}" data-status-label="{{ risk_status.status_label|default('正常') }}"{% if risk_status.freeze_until_ms %} data-freeze-until-ms="{{ risk_status.freeze_until_ms }}"{% endif %}>{{ risk_status.status_label|default('正常') }}</span>
|
||||
</div>
|
||||
{% include 'instance_theme_toggle.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="instance-header-stats-wrap instance-desktop-only">
|
||||
{% include 'instance_header_stats.html' %}
|
||||
</div>
|
||||
<div class="instance-header-phone-strip instance-phone-only" aria-label="手机资金摘要">
|
||||
<span class="inst-phone-chip"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<em>交易</em>
|
||||
<b data-funds-field="current-capital">{{ funds_fmt(current_capital) }}U</b>
|
||||
</span>
|
||||
<span class="inst-phone-chip"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<em>资金</em>
|
||||
<b data-funds-field="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</b>
|
||||
</span>
|
||||
<span class="inst-phone-chip">
|
||||
<em>总资</em>
|
||||
<b data-funds-field="total-funds">{% if total_funds is not none %}{{ funds_fmt(total_funds) }}U{% else %}—{% endif %}</b>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
{# 资金与统计条(顶栏 / 系统设置共用,单行展示) #}
|
||||
<div class="instance-header-stats{% if options_enabled %} instance-header-stats--options{% endif %}">
|
||||
<div class="stat-strip-item stat-strip-item--primary">
|
||||
<div class="label">交易所</div>
|
||||
<div class="value">{{ exchange_display }}{% if is_sim_mode|default(false) %} <span class="sim-mode-badge" title="本地模拟资金撮合,不下真实交易所单">模拟</span>{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">交易日</div>
|
||||
<div class="value">{{ trading_day }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item stat-strip-item--primary">
|
||||
<div class="label">总交易</div>
|
||||
<div class="value" id="stat-total" data-funds-field="stat-total">{{ total }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">胜率</div>
|
||||
<div class="value" id="stat-rate" data-funds-field="stat-rate">{{ rate }}%</div>
|
||||
</div>
|
||||
<div class="stat-strip-item" title="平均盈利 ÷ 平均亏损(当前列表窗口)">
|
||||
<div class="label">盈亏比</div>
|
||||
<div class="value" id="stat-pl-ratio" data-funds-field="stat-pl-ratio">{% if profit_loss_ratio is not none %}{{ profit_loss_ratio }}{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">总资金</div>
|
||||
<div class="value" id="total-funds" data-funds-field="total-funds">{% if total_funds is not none %}{{ funds_fmt(total_funds) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<div class="label">资金账户</div>
|
||||
<div class="value" id="total-capital" data-funds-field="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item"{% if not (show_perp_funds|default(true)) %} style="display:none"{% endif %} data-perp-funds="1">
|
||||
<div class="label">交易账户</div>
|
||||
<div class="value" id="current-capital" data-funds-field="current-capital">{{ funds_fmt(current_capital) }}U</div>
|
||||
</div>
|
||||
{% if options_enabled %}
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权资金账户</div>
|
||||
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc) }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权交易账户</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc) }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stat-strip-item stat-strip-item--pnl">
|
||||
<div class="label">实时盈亏</div>
|
||||
<div class="value" id="realtime-pnl" data-funds-field="realtime-pnl">—</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
|
||||
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12.1 3a9 9 0 1 0 8.9 11 6.5 6.5 0 1 1-8.9-11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="theme-toggle-btn" data-theme-value="light" aria-pressed="false" title="亮色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
{# 三所统一顶栏:实时价(划转已移至系统设置;切点前开仓说明见风控说明·交易执行) #}
|
||||
<div class="rule-tip instance-price-bar">
|
||||
实时价格更新:<span id="price-last-updated">--</span>(北京时间 UTC+8)
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
{# 系统设置 · 资金划转(三所共用) #}
|
||||
<div class="settings-transfer-panel">
|
||||
<p class="rule-tip settings-transfer-auto">
|
||||
自动划转 <strong>{{ '开启' if auto_transfer_enabled else '关闭' }}</strong>:
|
||||
每天<strong>北京时间 {{ auto_transfer_bj_hour }}:00</strong> 起该整点小时内尝试;
|
||||
账簿按 <strong>UTC 自然日</strong> 去重;
|
||||
将 <code>{{ auto_transfer_to }}</code> 调整至 <strong>{{ transfer_amount_fmt|default(funds_fmt(auto_transfer_amount)) }}U</strong>:
|
||||
不足从 <code>{{ auto_transfer_from }}</code> 划入,超出划回 <code>{{ auto_transfer_from }}</code>;
|
||||
<strong>持仓中不划转</strong>并微信通知.
|
||||
</p>
|
||||
<form action="/manual_transfer" method="post" class="form-row gate-transfer-form settings-transfer-form" autocomplete="off">
|
||||
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
|
||||
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
|
||||
<input name="amount" id="manual-xfer-amount" type="number" min="0.01" step="0.01" placeholder="手动划转金额 U" required
|
||||
autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-bwignore="true" data-form-type="other" readonly>
|
||||
<select name="from_account" aria-label="划出账户" autocomplete="off">
|
||||
<option value="funding" {% if auto_transfer_from == 'funding' %}selected{% endif %}>from: funding</option>
|
||||
<option value="swap" {% if auto_transfer_from == 'swap' %}selected{% endif %}>from: swap</option>
|
||||
<option value="spot" {% if auto_transfer_from == 'spot' %}selected{% endif %}>from: spot</option>
|
||||
</select>
|
||||
<select name="to_account" aria-label="划入账户" autocomplete="off">
|
||||
<option value="swap" {% if auto_transfer_to == 'swap' %}selected{% endif %}>to: swap</option>
|
||||
<option value="funding" {% if auto_transfer_to == 'funding' %}selected{% endif %}>to: funding</option>
|
||||
<option value="spot" {% if auto_transfer_to == 'spot' %}selected{% endif %}>to: spot</option>
|
||||
</select>
|
||||
<button type="submit">执行手动划转</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,44 @@
|
||||
{# 复盘表单:首行按字段宽度比例;下单类型/开仓类型与离场触发同一行 #}
|
||||
{% macro journal_form_fields(entry_reason_options, order_type_options) -%}
|
||||
<div class="form-grid journal-form-row1">
|
||||
<input type="datetime-local" name="open_datetime" class="journal-field-datetime" required autocomplete="off">
|
||||
<input type="datetime-local" name="close_datetime" class="journal-field-datetime" required autocomplete="off">
|
||||
<input name="coin" class="journal-field-coin" placeholder="BTC" required autocomplete="off" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
<input name="tf" class="journal-field-tf" placeholder="5m" required autocomplete="off" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
<input name="pnl" class="journal-field-num" placeholder="盈亏(U)" required autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
<input name="expect_rr" class="journal-field-num" placeholder="预期RR" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
<input name="real_rr" class="journal-field-num" placeholder="实际RR" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
</div>
|
||||
<div class="form-grid journal-form-row2">
|
||||
<select name="direction" id="journal-direction" class="journal-field-direction" required title="做多/做空">
|
||||
<option value="">方向(必选)</option>
|
||||
<option value="long">做多</option>
|
||||
<option value="short">做空</option>
|
||||
</select>
|
||||
<select name="order_type" id="journal-order-type" class="journal-field-order-type" required title="下单来源:下单监控/关键位/趋势回调/顺势加仓">
|
||||
<option value="">下单类型(必选)</option>
|
||||
{% for ot in order_type_options %}
|
||||
<option value="{{ ot }}">{{ ot }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select name="entry_reason" id="journal-entry-reason" class="journal-field-entry-reason" required title="日内:假破/结构突破/回调触价/突破触价;趋势户:反转/顺势/波段小分歧等">
|
||||
<option value="">开仓类型(必选)</option>
|
||||
{% for er in entry_reason_options %}
|
||||
<option value="{{ er }}">{{ er }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select name="early_exit_trigger" required title="平仓如何触发">
|
||||
<option value="">离场触发(必选)</option>
|
||||
<option value="止盈">止盈</option>
|
||||
<option value="保本止盈">保本止盈</option>
|
||||
<option value="移动止盈">移动止盈</option>
|
||||
<option value="时间平仓">时间平仓</option>
|
||||
<option value="强制清仓">强制清仓</option>
|
||||
<option value="手动平仓">手动平仓</option>
|
||||
<option value="止损">止损</option>
|
||||
<option value="其他">其他</option>
|
||||
</select>
|
||||
<input name="early_exit_note" id="early-exit-note" placeholder="离场补充(仅手工平仓必填)" autocomplete="off" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
<select name="post_breakeven_stare"><option value="否">保本后盯盘:否</option><option value="是">保本后盯盘:是</option></select>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{# 复盘四周期截图槽位(须加载 journal_upload_slots.js) #}
|
||||
{% macro journal_upload_slots() -%}
|
||||
<input type="hidden" name="journal_draft_id" id="journal-draft-id" value="">
|
||||
<div class="journal-upload-slots" id="journal-upload-slots">
|
||||
{% for tf in ['5m', '15m', '1h', '4h'] %}
|
||||
<div class="journal-upload-row" data-tf="{{ tf }}">
|
||||
<span class="journal-upload-slot-label">{{ tf }}</span>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
class="journal-upload-slot-input"
|
||||
data-tf="{{ tf }}"
|
||||
>
|
||||
<input type="hidden" name="uploaded_screenshot_{{ tf }}" class="journal-upload-hidden-file" data-tf="{{ tf }}" value="">
|
||||
<span class="journal-upload-status" data-tf="{{ tf }}" aria-live="polite"></span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="sub journal-upload-hint">可只传部分周期;选文件后即时上传,保存后详情页四宫格查看</p>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,182 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script src="/static/instance_theme.js?v=5"></script>
|
||||
<title>{{ exchange_display }} | 关键位放大</title>
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=50">
|
||||
<script src="/static/symbol_live_price.js?v=2"></script>
|
||||
<link rel="stylesheet" href="/static/focus_chart_page.css?v=1">
|
||||
</head>
|
||||
<body class="focus-page" data-price-refresh-ms="{{ price_refresh_seconds * 1000 }}">
|
||||
{% if trade_policy is not defined %}
|
||||
{% set trade_policy = {'symbol_restrict_enabled': false, 'direction_restrict_enabled': false, 'symbol_whitelist': [], 'allows_long': true, 'allows_short': true, 'badge_text': ''} %}
|
||||
{% endif %}
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="row" style="justify-content:space-between">
|
||||
<div class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
|
||||
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12.1 3a9 9 0 1 0 8.9 11 6.5 6.5 0 1 1-8.9-11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="theme-toggle-btn" data-theme-value="light" aria-pressed="false" title="亮色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a class="btn" href="/">返回首页</a>
|
||||
<strong class="focus-title">关键位放大{% if trade_policy.symbol_restrict_enabled %}(选择币种){% else %}(可输入币种){% endif %}</strong><span class="exchange-tag">{{ exchange_display }}</span>
|
||||
</div>
|
||||
<div class="status">最近刷新:<span id="updated-at">--</span></div>
|
||||
</div>
|
||||
<div class="row" style="margin-top:10px">
|
||||
<label>币种</label>
|
||||
{% from 'trade_policy_fields.html' import trade_policy_symbol with context %}
|
||||
{{ trade_policy_symbol('symbol', 'symbol-input', default_symbol, placeholder='BTC/USDT') }}
|
||||
{% from 'symbol_live_price_snippet.html' import symbol_live_price_hint %}
|
||||
{{ symbol_live_price_hint('key-focus-symbol-live-price', 'symbol-input') }}
|
||||
<label>关键位</label>
|
||||
<select id="key-id">
|
||||
<option value="">无(仅看K线)</option>
|
||||
{% for k in key_list %}
|
||||
<option value="{{ k.id }}" {% if selected_key and k.id == selected_key.id %}selected{% endif %}>#{{ k.id }} {{ k.symbol }} {{ k.monitor_type }} {{ '做多' if k.direction == 'long' else '做空' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label>周期</label>
|
||||
<select id="timeframe">
|
||||
{% for tf in ['1m','3m','5m','15m','30m','1h','4h','1d'] %}
|
||||
<option value="{{ tf }}" {% if tf == default_timeframe %}selected{% endif %}>{{ tf }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label>K线数</label>
|
||||
<select id="kline-limit">
|
||||
<option value="100" {% if default_kline_limit == 100 %}selected{% endif %}>100</option>
|
||||
<option value="200" {% if default_kline_limit == 200 %}selected{% endif %}>200</option>
|
||||
</select>
|
||||
<button id="manual-refresh" type="button">刷新</button>
|
||||
<span id="load-status" class="status"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="meta">
|
||||
<div class="meta-item meta-item--emph"><div class="k">交易对</div><div class="v" id="m-symbol">-</div></div>
|
||||
<div class="meta-item"><div class="k">监控类型</div><div class="v" id="m-type">-</div></div>
|
||||
<div class="meta-item meta-item--emph"><div class="k">方向</div><div class="v" id="m-direction">-</div></div>
|
||||
<div class="meta-item"><div class="k">上沿/阻力</div><div class="v" id="m-upper">-</div></div>
|
||||
<div class="meta-item"><div class="k">下沿/支撑</div><div class="v" id="m-lower">-</div></div>
|
||||
<div class="meta-item"><div class="k">现价</div><div class="v" id="m-price">-</div></div>
|
||||
<div class="meta-item"><div class="k">距上沿</div><div class="v" id="m-updiff">-</div></div>
|
||||
<div class="meta-item"><div class="k">距下沿</div><div class="v" id="m-lowdiff">-</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card"><div id="chart-wrap"><div id="chart"></div></div></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/static/focus_chart_page.js?v=2"></script>
|
||||
<script>
|
||||
const refreshMs = Math.max({{ price_refresh_seconds * 1000 }}, 5000);
|
||||
const keySelect = document.getElementById("key-id");
|
||||
const symbolInput = document.getElementById("symbol-input");
|
||||
const tfSelect = document.getElementById("timeframe");
|
||||
const limitSelect = document.getElementById("kline-limit");
|
||||
const statusEl = document.getElementById("load-status");
|
||||
const updatedAtEl = document.getElementById("updated-at");
|
||||
const chartHost = document.getElementById("chart");
|
||||
const FCP = window.FocusChartPage;
|
||||
const keyMap = {};
|
||||
{% for k in key_list %}
|
||||
keyMap["{{ k.id }}"] = "{{ k.symbol }}";
|
||||
{% endfor %}
|
||||
let fc = null;
|
||||
|
||||
function ensureChart(){
|
||||
if(fc && fc.ensureSeries()) return true;
|
||||
if(!window.LightweightCharts){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "图表库加载失败";
|
||||
return false;
|
||||
}
|
||||
fc = FCP.createFocusChart(chartHost);
|
||||
if(!fc || !fc.ensureSeries()){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "K线序列初始化失败";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function syncSymbolByKey(){
|
||||
const keyId = keySelect.value;
|
||||
if(keyId && keyMap[keyId]) symbolInput.value = keyMap[keyId];
|
||||
}
|
||||
|
||||
async function loadKeyKline(){
|
||||
if(!ensureChart()) return;
|
||||
const keyId = keySelect.value;
|
||||
const symbol = (symbolInput.value || "").trim().toUpperCase();
|
||||
const timeframe = tfSelect.value;
|
||||
const limit = limitSelect.value;
|
||||
if(!symbol && !keyId){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "请先输入币种或选择关键位";
|
||||
return;
|
||||
}
|
||||
statusEl.className = "status";
|
||||
statusEl.innerText = "加载中...";
|
||||
try{
|
||||
const qs = new URLSearchParams();
|
||||
if(keyId) qs.set("key_id", keyId);
|
||||
if(symbol) qs.set("symbol", symbol);
|
||||
qs.set("timeframe", timeframe);
|
||||
qs.set("limit", limit);
|
||||
const resp = await fetch(`/api/key_kline?${qs.toString()}`);
|
||||
const data = await resp.json();
|
||||
if(!resp.ok || !data.ok) throw new Error(data.msg || "请求失败");
|
||||
if(fc && typeof fc.setPriceTick === "function") fc.setPriceTick(data.price_tick);
|
||||
else FCP.setActivePriceTick(data.price_tick);
|
||||
const candles = Array.isArray(data.candles) ? data.candles : [];
|
||||
if(!candles.length){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "暂无K线数据";
|
||||
return;
|
||||
}
|
||||
fc.candleSeries.setData(candles);
|
||||
fc.resetPriceLines();
|
||||
fc.addLine(data.current_price, FCP.lineTitle("现价", data.current_price_display), "#42a5f5");
|
||||
if(data.key_monitor){
|
||||
const km = data.key_monitor;
|
||||
fc.addLine(km.upper, FCP.lineTitle("上沿", km.upper_display), "#ffb84d");
|
||||
fc.addLine(km.lower, FCP.lineTitle("下沿", km.lower_display), "#4cd97f");
|
||||
}
|
||||
fc.chart.timeScale().fitContent();
|
||||
FCP.paintKeyMeta(data);
|
||||
updatedAtEl.innerText = data.updated_at || "--";
|
||||
statusEl.className = "status";
|
||||
statusEl.innerText = `已加载 ${candles.length} 根K线`;
|
||||
}catch(err){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = err && err.message ? err.message : "加载失败";
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("manual-refresh").addEventListener("click", loadKeyKline);
|
||||
keySelect.addEventListener("change", ()=>{ syncSymbolByKey(); loadKeyKline(); });
|
||||
symbolInput.addEventListener("change", ()=>{
|
||||
if(symbolInput.value.trim()) keySelect.value = "";
|
||||
loadKeyKline();
|
||||
});
|
||||
tfSelect.addEventListener("change", loadKeyKline);
|
||||
limitSelect.addEventListener("change", loadKeyKline);
|
||||
syncSymbolByKey();
|
||||
loadKeyKline();
|
||||
setInterval(loadKeyKline, refreshMs);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,296 @@
|
||||
<style>
|
||||
.key-monitor-dual-grid{align-items:stretch}
|
||||
.key-monitor-dual-grid>.card{
|
||||
height:100%;
|
||||
min-height:0;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
.key-panel-scroll.panel-scroll.pos-list{
|
||||
display:block;
|
||||
flex:1 1 auto;
|
||||
min-height:0;
|
||||
overflow-x:hidden;
|
||||
overflow-y:auto;
|
||||
padding-bottom:6px;
|
||||
-webkit-overflow-scrolling:touch;
|
||||
scrollbar-gutter:stable;
|
||||
}
|
||||
.key-monitor-panel-scroll{min-height:200px}
|
||||
.key-history-panel-scroll{
|
||||
flex:0 0 auto;
|
||||
max-height:calc(8 * 42px + 7 * 8px);
|
||||
min-height:calc(3 * 42px + 2 * 8px);
|
||||
}
|
||||
.key-panel-scroll.panel-scroll.pos-list .key-row-collapse{flex-shrink:0}
|
||||
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar{width:8px}
|
||||
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar-thumb{background:#3a4660;border-radius:4px}
|
||||
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar-track{background:transparent}
|
||||
.key-row-collapse{border:1px solid #2a3348;border-radius:10px;background:#141923}
|
||||
.key-row-collapse:not([open]){overflow:hidden}
|
||||
.key-row-collapse[open]{overflow:visible}
|
||||
.key-row-collapse+.key-row-collapse{margin-top:8px}
|
||||
.key-row-collapse-summary{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;cursor:pointer;list-style:none;font-size:.8rem;color:#c5cde0;line-height:1.45}
|
||||
.key-row-collapse-summary::-webkit-details-marker{display:none}
|
||||
.key-row-collapse-summary::before{content:"▸";flex:0 0 auto;color:#6d7a99;transition:transform .15s ease}
|
||||
.key-row-collapse[open]>.key-row-collapse-summary::before{transform:rotate(90deg)}
|
||||
.key-row-summary-main{flex:1;min-width:0;display:flex;align-items:center;justify-content:space-between;gap:10px}
|
||||
.key-row-summary-title{display:flex;align-items:center;gap:6px;flex:0 1 auto;flex-wrap:wrap;min-width:0}
|
||||
.key-row-summary-title strong{font-size:.88rem;color:#fff}
|
||||
.key-row-summary-line{color:#9aa8c4;font-size:.76rem;word-break:break-word}
|
||||
.key-row-summary-live{
|
||||
flex:1 1 auto;
|
||||
min-width:0;
|
||||
color:#8fc8ff;
|
||||
font-size:.72rem;
|
||||
text-align:right;
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.key-row-summary-live.key-row-summary-pending{color:#4cd97f;font-weight:600}
|
||||
.key-history-panel-scroll .key-row-summary-main{justify-content:flex-start}
|
||||
.key-row-summary-actions{flex:0 0 auto;display:flex;gap:6px;align-items:center}
|
||||
.key-row-collapse-body{padding:0 12px 16px;border-top:1px solid #232b3d}
|
||||
.key-row-collapse-body .pos-meta{margin-top:10px;margin-bottom:10px}
|
||||
.key-row-collapse-body .pos-grid{margin-bottom:8px}
|
||||
.key-history-alert{font-size:.75rem;color:#aab;margin-top:8px;margin-bottom:2px;padding-bottom:4px;white-space:pre-wrap;word-break:break-word;line-height:1.5}
|
||||
.key-history-outcome-badge{font-size:.7rem;font-weight:600;padding:1px 7px;border-radius:4px;line-height:1.35}
|
||||
.key-row-collapse.key-history-success{border-color:rgba(76,217,127,.42);background:rgba(18,32,26,.92)}
|
||||
.key-row-collapse.key-history-success .key-row-collapse-summary{color:#c8f0d6}
|
||||
.key-row-collapse.key-history-success .key-row-summary-title strong{color:#e8fff0}
|
||||
.key-row-collapse.key-history-success .key-history-brief,.key-row-collapse.key-history-success .key-history-outcome-badge{color:#4cd97f;background:rgba(76,217,127,.12);border:1px solid rgba(76,217,127,.28)}
|
||||
.key-row-collapse.key-history-manual{border-color:rgba(136,146,176,.45);background:rgba(22,24,32,.95)}
|
||||
.key-row-collapse.key-history-manual .key-history-brief,.key-row-collapse.key-history-manual .key-history-outcome-badge{color:#9aa8c4;background:rgba(136,146,176,.12);border:1px solid rgba(136,146,176,.28)}
|
||||
.key-row-collapse.key-history-failed{border-color:rgba(232,160,144,.4);background:rgba(36,22,24,.95)}
|
||||
.key-row-collapse.key-history-failed .key-row-collapse-summary{color:#e8cfc8}
|
||||
.key-row-collapse.key-history-failed .key-history-brief,.key-row-collapse.key-history-failed .key-history-outcome-badge{color:#e8a090;background:rgba(232,160,144,.1);border:1px solid rgba(232,160,144,.28)}
|
||||
.key-rule-table-wrap{overflow-x:auto;margin:0 -2px}
|
||||
.key-rule-table{width:100%;min-width:620px;border-collapse:collapse;font-size:.6rem;line-height:1.3}
|
||||
.key-rule-table th,.key-rule-table td{border:1px solid #2a3348;padding:4px 6px;vertical-align:top;text-align:left}
|
||||
.key-rule-table th{background:rgba(0,0,0,.28);color:#9ab;font-weight:600;white-space:nowrap;font-size:.58rem}
|
||||
.key-rule-table td{color:#c5cde0}
|
||||
.key-rule-table .key-rule-type{color:#fff;font-weight:600;line-height:1.25;white-space:nowrap}
|
||||
.key-rule-table .key-rule-sub{color:#8fc8ff;font-size:.54rem;font-weight:500}
|
||||
.key-rule-cell{word-break:break-word}
|
||||
.key-rule-foot{margin:6px 0 0;font-size:.56rem;color:#8892b0;line-height:1.35}
|
||||
.key-rule-foot code{font-size:.54rem;color:#8fc8ff}
|
||||
</style>
|
||||
|
||||
{% macro key_monitor_type_label(k) -%}
|
||||
{%- if k.monitor_type in ['关键阻力位','关键支撑位','关键支撑阻力'] -%}关键支撑阻力{%- else -%}{{ k.monitor_type }}{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_direction_label(k) -%}
|
||||
{% if k.direction == 'watch' %}双向{% elif k.direction == 'long' %}做多{% else %}做空{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_sl_tp_mode_label(k) -%}
|
||||
{% if (k.sl_tp_mode or 'standard') == 'standard' %}标准突破{% elif k.sl_tp_mode == 'box_1p5' %}箱体1R·止盈1.5H{% else %}趋势单{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_monitor_brief(k) -%}
|
||||
上{{ k.upper }} / 下{{ k.lower }} · 提醒 {{ k.notification_count or 0 }}/{{ k.max_notify or 3 }}
|
||||
{%- if k.monitor_type in ['箱体突破','收敛突破'] %} · {{ key_sl_tp_mode_label(k) }}{% endif %}
|
||||
{%- if k.breakeven_enabled %} · 保本开{% else %} · 保本关{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_history_outcome_kind(h) -%}
|
||||
{%- set r = (h.close_reason or '')|trim -%}
|
||||
{%- if r in ['fib_filled', 'false_breakout_filled', 'trigger_entry_filled', 'key_level_alert_done', 'alerts_complete', 'auto_opened'] -%}success
|
||||
{%- elif r == 'manual' -%}manual
|
||||
{%- elif r -%}failed
|
||||
{%- else -%}neutral
|
||||
{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_history_outcome_label(h) -%}
|
||||
{%- set r = (h.close_reason or '')|trim -%}
|
||||
{%- if r == 'fib_filled' -%}斐波成交
|
||||
{%- elif r == 'false_breakout_filled' -%}假突破成交
|
||||
{%- elif r == 'trigger_entry_filled' -%}触价成交
|
||||
{%- elif r == 'key_level_alert_done' -%}提醒完成
|
||||
{%- elif r == 'alerts_complete' -%}提醒已满
|
||||
{%- elif r == 'auto_opened' -%}自动开仓
|
||||
{%- elif r == 'manual' -%}手动删除
|
||||
{%- elif r == 'fib_invalidate' -%}斐波失效
|
||||
{%- elif r == 'box_opposite_break' -%}反向突破失效
|
||||
{%- elif r == 'trigger_tp_invalidate' -%}触价止盈失效
|
||||
{%- elif r == 'trigger_sl_invalidate' -%}触价止损失效
|
||||
{%- elif r == 'trigger_entry_expired' -%}触价过期
|
||||
{%- elif r == 'trigger_exchange_failed' -%}触价下单失败
|
||||
{%- elif r == 'false_breakout_expired' -%}假突破过期
|
||||
{%- elif r == 'fib_plan_invalid' -%}计划无效
|
||||
{%- elif r == 'rr_insufficient' -%}盈亏比不足
|
||||
{%- elif r == 'exchange_failed' -%}下单失败
|
||||
{%- else -%}{{ r or '—' }}
|
||||
{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro key_history_brief(h) -%}
|
||||
{{ key_history_outcome_label(h) }} · {{ (h.closed_at or '-')[:16] }} · 上{{ h.upper }} / 下{{ h.lower }} · 提醒 {{ h.notification_count or 0 }}
|
||||
{%- endmacro %}
|
||||
|
||||
<div class="dual-panel-grid key-monitor-dual-grid" style="grid-column:1/-1">
|
||||
<div class="card">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin-bottom:8px">
|
||||
<h2 style="margin-bottom:0">关键位监控</h2>
|
||||
{% if focus_key_id %}
|
||||
<a href="/key_focus?key_id={{ focus_key_id }}" class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff">放大查看K线(默认200根)</a>
|
||||
{% else %}
|
||||
<a href="/key_focus" class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff">输入币种查看K线</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<form id="key-form" action="/add_key" method="post" class="form-row">
|
||||
{% from 'trade_policy_fields.html' import trade_policy_symbol with context %}
|
||||
{{ trade_policy_symbol('symbol', 'key-symbol') }}
|
||||
<select name="type" id="key-type-select" required>
|
||||
<option value="关键支撑阻力" selected>关键支撑阻力</option>
|
||||
</select>
|
||||
<input type="hidden" name="direction" id="key-direction" value="watch">
|
||||
{% from 'symbol_live_price_snippet.html' import symbol_live_price_hint %}
|
||||
{{ symbol_live_price_hint('key-symbol-live-price', 'key-symbol', '') }}
|
||||
<input name="upper" id="key-upper" step="0.0001" placeholder="上沿/阻力" required>
|
||||
<input name="lower" id="key-lower" step="0.0001" placeholder="下沿/支撑" required>
|
||||
<button type="submit">添加提醒</button>
|
||||
</form>
|
||||
<details class="tip-collapse key-rule-collapse">
|
||||
<summary class="tip-collapse-summary">关键位监控规则说明</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
{% include 'key_monitor_rule_tips.html' %}
|
||||
</div>
|
||||
</details>
|
||||
<div class="panel-scroll pos-list key-panel-scroll key-monitor-panel-scroll">
|
||||
{% for k in key %}
|
||||
<details class="key-row-collapse" id="key-row-{{ k.id }}">
|
||||
<summary class="key-row-collapse-summary">
|
||||
<span class="key-row-summary-main">
|
||||
<span class="key-row-summary-title">
|
||||
<strong>{{ k.symbol }}</strong>
|
||||
{% if k.time_close_enabled and k.time_close_hours %}
|
||||
<span class="pos-symbol-time-close pos-meta-on">时间平仓 {{ k.time_close_hours }}h</span>
|
||||
{% endif %}
|
||||
{% if k.direction == 'watch' %}
|
||||
<span class="pos-side-badge" style="background:#2a3152;color:#9ab">双向</span>
|
||||
{% else %}
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if k.direction == 'long' else 'pos-side-short' }}">{{ key_direction_label(k) }}</span>
|
||||
{% endif %}
|
||||
<span class="badge direction">{{ key_monitor_type_label(k) }}</span>
|
||||
</span>
|
||||
<span class="key-row-summary-live" id="key-summary-live-{{ k.id }}">现价 — · 门控 —</span>
|
||||
</span>
|
||||
<span class="key-row-summary-actions">
|
||||
<button type="button" class="table-del" onclick="event.preventDefault(); event.stopPropagation(); deleteKeyMonitor({{ k.id }})">删</button>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="key-row-collapse-body">
|
||||
<div class="key-row-summary-line">{{ key_monitor_brief(k) }}</div>
|
||||
<div class="pos-meta">
|
||||
<span class="pos-meta-item">上沿: {{ k.upper }}</span>
|
||||
<span class="pos-meta-item">下沿: {{ k.lower }}</span>
|
||||
{% if k.fib_entry_price and k.monitor_type in ['回调触价开仓','突破触价开仓','触价开仓'] %}<span class="pos-meta-item">E: {{ k.fib_entry_price }} / SL: {{ k.fib_stop_loss }} / TP: {{ k.fib_take_profit }}</span>{% elif k.fib_entry_price %}<span class="pos-meta-item">挂E: {{ k.fib_entry_price }}</span>{% endif %}
|
||||
{% if k.monitor_type == '假突破' and k.fib_stop_loss %}<span class="pos-meta-item">SL: {{ k.fib_stop_loss }} / TP: {{ k.fib_take_profit }}</span>{% endif %}
|
||||
<span class="pos-meta-item">已提醒: {{ k.notification_count or 0 }}/{{ k.max_notify or 3 }}</span>
|
||||
{% if k.monitor_type in ['箱体突破','收敛突破'] %}
|
||||
<span class="pos-meta-item">方案: {{ key_sl_tp_mode_label(k) }}</span>
|
||||
{% endif %}
|
||||
<span class="pos-meta-item">保本: {{ '开' if k.breakeven_enabled else '关' }}</span>
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
<div class="pos-cell"><span class="pos-label">现价</span><span class="pos-value" id="key-price-{{ k.id }}">-</span></div>
|
||||
<div class="pos-cell"><span class="pos-label">距上沿</span><span class="pos-value" id="key-up-diff-{{ k.id }}">-</span></div>
|
||||
<div class="pos-cell"><span class="pos-label">距下沿</span><span class="pos-value" id="key-low-diff-{{ k.id }}">-</span></div>
|
||||
<div class="pos-cell"><span class="pos-label">门控</span><span class="pos-value" id="key-gate-{{ k.id }}" style="color:#9aa">-</span></div>
|
||||
</div>
|
||||
<div class="pos-meta"><span class="pos-meta-item" id="key-gate-metrics-{{ k.id }}" style="color:#8fc8ff"></span></div>
|
||||
</div>
|
||||
</details>
|
||||
{% else %}
|
||||
<div class="pos-empty">暂无监控中的关键位</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2 style="margin-bottom:8px">关键位历史</h2>
|
||||
<div class="sub" style="font-size:.72rem;color:#8892b0;margin-bottom:8px">失效或已结案的关键位 · 点击展开详情</div>
|
||||
<div class="panel-scroll pos-list key-panel-scroll key-history-panel-scroll">
|
||||
{% for h in key_history %}
|
||||
<details class="key-row-collapse key-history-{{ key_history_outcome_kind(h) }}">
|
||||
<summary class="key-row-collapse-summary">
|
||||
<span class="key-row-summary-main">
|
||||
<span class="key-row-summary-title">
|
||||
<strong>{{ h.symbol }}</strong>
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if h.direction == 'long' else 'pos-side-short' }}">{{ key_direction_label(h) }}</span>
|
||||
<span class="badge direction">{{ key_monitor_type_label(h) }}</span>
|
||||
<span class="key-history-outcome-badge">{{ key_history_outcome_label(h) }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="key-row-summary-actions">
|
||||
<button type="button" class="table-del" onclick="event.preventDefault(); event.stopPropagation(); deleteKeyHistory({{ h.id }})">删除</button>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="key-row-collapse-body">
|
||||
<div class="key-row-summary-line key-history-brief">{{ key_history_brief(h) }}</div>
|
||||
<div class="pos-meta">
|
||||
<span class="pos-meta-item">类型: {{ key_monitor_type_label(h) }}</span>
|
||||
<span class="pos-meta-item">结案: {{ key_history_outcome_label(h) }}{% if h.close_reason %} ({{ h.close_reason }}){% endif %}</span>
|
||||
<span class="pos-meta-item">时间: {{ h.closed_at or '—' }}</span>
|
||||
</div>
|
||||
<div class="pos-meta">
|
||||
<span class="pos-meta-item">上沿: {{ h.upper }}</span>
|
||||
<span class="pos-meta-item">下沿: {{ h.lower }}</span>
|
||||
<span class="pos-meta-item">提醒次数: {{ h.notification_count or 0 }}</span>
|
||||
</div>
|
||||
{% if h.last_alert_message %}
|
||||
<div class="key-history-alert">{{ h.last_alert_message }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
{% else %}
|
||||
<div class="pos-empty">暂无历史</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function keySummaryIsPending(snap){
|
||||
if(!snap) return false;
|
||||
const gs = String(snap.gate_summary || "");
|
||||
if(gs.includes("标记价将失效")) return false;
|
||||
const gm = String(snap.gate_metrics || "");
|
||||
if(gm.includes("限价单") || gm.includes("挂单")) return true;
|
||||
if(/等待成交/.test(gs)) return true;
|
||||
if(/触价待触发/.test(gs)) return true;
|
||||
if(/挂E=/.test(gs) && !gs.includes("将失效")) return true;
|
||||
return false;
|
||||
}
|
||||
function paintKeyMonitorSummary(id, snap){
|
||||
const el = document.getElementById(`key-summary-live-${id}`);
|
||||
if(!el || !snap) return;
|
||||
const px = snap.price_display || (Number.isFinite(Number(snap.price)) ? Number(snap.price).toFixed(6) : "—");
|
||||
const gate = snap.gate_summary || "—";
|
||||
el.innerText = `现价 ${px} · 门控 ${gate}`;
|
||||
el.classList.toggle("key-row-summary-pending", keySummaryIsPending(snap));
|
||||
}
|
||||
document.querySelectorAll(".key-row-collapse").forEach((row)=>{
|
||||
row.addEventListener("toggle", ()=>{
|
||||
if(!row.open) return;
|
||||
requestAnimationFrame(()=>{
|
||||
const body = row.querySelector(".key-row-collapse-body");
|
||||
const panel = row.closest(".key-panel-scroll");
|
||||
if(body && panel){
|
||||
const bodyRect = body.getBoundingClientRect();
|
||||
const panelRect = panel.getBoundingClientRect();
|
||||
if(bodyRect.bottom > panelRect.bottom - 8){
|
||||
panel.scrollTop += bodyRect.bottom - panelRect.bottom + 16;
|
||||
} else if(bodyRect.top < panelRect.top + 8){
|
||||
panel.scrollTop -= panelRect.top - bodyRect.top + 16;
|
||||
}
|
||||
} else {
|
||||
row.scrollIntoView({block:"nearest", behavior:"smooth"});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="/static/key_monitor_form.js?v=2"></script>
|
||||
@@ -0,0 +1,59 @@
|
||||
{% set r = key_rule_ctx %}
|
||||
<div class="key-rule-table-wrap">
|
||||
<table class="key-rule-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>类型</th>
|
||||
<th>填写</th>
|
||||
<th>门控</th>
|
||||
<th>止盈止损</th>
|
||||
<th>执行</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="key-rule-type">箱体突破<br><span class="key-rule-sub">收敛突破</span></td>
|
||||
<td class="key-rule-cell">方向必选;填 H/L<br>方案:标准 / 1R·1.5H / 趋势<br>可勾移动保本</td>
|
||||
<td class="key-rule-cell">{{ r.tf }} 两根闭合 K({{ r.breakout_bar }}/{{ r.confirm_bar }})<br>突破 >{{ r.amp_min_pct }}%;确认在箱外<br>量 >前{{ r.vol_ma_bars }}均×{{ r.vol_ratio_min }}<br>成交 Top{{ r.vol_rank_max }};RR >{{ r.min_rr }}<br>标记价先破反向边界→失效</td>
|
||||
<td class="key-rule-cell">标准:SL 极值外{{ r.stop_outside_pct }}%,TP=E±H<br>1R:SL=E∓H,TP=E∓1.5H<br>趋势:SL 极值外{{ r.trend_stop_outside_pct }}%,TP 自填</td>
|
||||
<td class="key-rule-cell">门控过→市价开仓→下单监控<br>满仓不可再加</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key-rule-type">斐波回调<br><span class="key-rule-sub">0.618 / 0.786</span></td>
|
||||
<td class="key-rule-cell">方向 + H/L 波段<br>系统算 E/SL/TP</td>
|
||||
<td class="key-rule-cell">多:E=H−rΔ,SL=L,TP=H<br>空:E=L+rΔ,SL=H,TP=L<br>RR >{{ r.min_rr }};先触 TP 侧失效</td>
|
||||
<td class="key-rule-cell">公式固定 SL/TP<br>成交后挂所</td>
|
||||
<td class="key-rule-cell">挂限价等成交<br>成交→下单监控</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key-rule-type">假突破<br><span class="key-rule-sub">BTC / ETH</span></td>
|
||||
<td class="key-rule-cell">空填高点 / 多填低点<br>同币仅 1 条</td>
|
||||
<td class="key-rule-cell">外侧 {{ r.fb_offset_pct }}% 限价<br>SL {{ r.fb_sl_pct }}%;RR {{ r.fb_rr }}<br>有效 {{ r.fb_valid_hours }}h</td>
|
||||
<td class="key-rule-cell">自动 E/SL/TP<br>可保本</td>
|
||||
<td class="key-rule-cell">即挂限价<br>成交/过期→历史</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key-rule-type">回调触价开仓</td>
|
||||
<td class="key-rule-cell">方向 + 入场 E / 止损 SL / 止盈 TP<br>可勾移动保本,时间平仓</td>
|
||||
<td class="key-rule-cell">RR >{{ r.min_rr }};做多 SL<E<TP<br>标记价回调触 E(多≤E / 空≥E)后下一轮询市价开<br>先触 TP 侧失效;有效 {{ r.trigger_entry_validity_hours }}h</td>
|
||||
<td class="key-rule-cell">程序盯价,无交易所挂单<br>成交后挂所 TP/SL → 下单监控</td>
|
||||
<td class="key-rule-cell">占当日开仓意图<br>全仓模式可用</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key-rule-type">突破触价开仓</td>
|
||||
<td class="key-rule-cell">方向 + 突破价 E / 止损 SL / 止盈 TP<br>可勾移动保本,时间平仓</td>
|
||||
<td class="key-rule-cell">RR >{{ r.min_rr }};做多 SL<E<TP<br>标记价<strong>穿越</strong> E 立即市价开(多向上 / 空向下)<br>先触 TP 或 SL 侧失效;有效 {{ r.trigger_entry_validity_hours }}h</td>
|
||||
<td class="key-rule-cell">程序盯价,无交易所挂单<br>成交后挂所 TP/SL → 下单监控</td>
|
||||
<td class="key-rule-cell">占当日开仓意图<br>全仓模式可用</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key-rule-type">关键支撑阻力</td>
|
||||
<td class="key-rule-cell">双向;填上/下沿</td>
|
||||
<td class="key-rule-cell">{{ r.tf }} 收盘破上沿或下沿<br>上沿优先</td>
|
||||
<td class="key-rule-cell">无(仅提醒)</td>
|
||||
<td class="key-rule-cell">微信 ≤{{ r.alert_max }} 次<br>间隔 ≥{{ r.alert_interval_min }} 分</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="key-rule-foot">阈值来自 <code>.env</code>,修改后重启实例.</p>
|
||||
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<script src="/static/instance_theme.js?v=4"></script>
|
||||
<meta name="theme-color" content="#0b0d14">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ pwa_app_name }}">
|
||||
<link rel="icon" href="/static/icons/favicon.ico" sizes="32x32">
|
||||
<link rel="icon" href="/static/icons/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/icons/apple-touch-icon.png">
|
||||
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
||||
<title>登录 · {{ pwa_app_name }}</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #0a0a10;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
.login-theme-bar {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.login-box {
|
||||
background: #12121a;
|
||||
padding: 2.5rem;
|
||||
border-radius: 16px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
border: 1px solid #242435;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
|
||||
}
|
||||
.login-box h2 {
|
||||
margin-bottom: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
background: linear-gradient(90deg, #4cc2ff, #7b42ff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #a9a9ff;
|
||||
}
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 0.85rem 1rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #2e2e45;
|
||||
background: #1a1a29;
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
}
|
||||
.form-group input:focus {
|
||||
border-color: #4cc2ff;
|
||||
}
|
||||
.login-box > form > button {
|
||||
width: 100%;
|
||||
padding: 0.9rem;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
background: linear-gradient(90deg, #4285f4, #7b42ff);
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.login-box > form > button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.flash {
|
||||
padding: 0.8rem;
|
||||
margin-bottom: 1rem;
|
||||
background: #331e24;
|
||||
color: #ff6666;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.exchange-line {
|
||||
text-align: center;
|
||||
font-size: 0.82rem;
|
||||
color: #8892b0;
|
||||
margin: 0 0 1.5rem;
|
||||
}
|
||||
.exchange-line strong {
|
||||
color: #b8f5d0;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=5">
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-theme-bar">
|
||||
<div class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
|
||||
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12.1 3a9 9 0 1 0 8.9 11 6.5 6.5 0 1 1-8.9-11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="theme-toggle-btn" data-theme-value="light" aria-pressed="false" title="亮色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<h2>{{ pwa_app_name }}</h2>
|
||||
<p class="exchange-line">登录 · <strong>{{ exchange_display }}</strong></p>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="flash">{{ messages[0] }}</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label>账号</label>
|
||||
<input type="text" name="username" required placeholder="请输入账号" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" name="password" required placeholder="请输入密码" autocomplete="new-password">
|
||||
</div>
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,72 @@
|
||||
{# 趋势户:两级开仓类型 → 自动 trade_style;日内户:假破 / 结构突破 #}
|
||||
|
||||
{% macro order_entry_type_fields() -%}
|
||||
|
||||
{% if order_entry_profile == 'trend_div' %}
|
||||
|
||||
<div class="order-entry-model-row">
|
||||
|
||||
<select id="order-entry-category" class="order-entry-category" required title="反转 / 顺势 / 波段" aria-label="开仓性质">
|
||||
|
||||
<option value="">性质</option>
|
||||
|
||||
{% for cat in entry_model_categories %}
|
||||
|
||||
<option value="{{ cat.key }}">{{ cat.label }}</option>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
|
||||
<select name="entry_model" id="order-entry-model" class="order-entry-model-sub" required disabled title="启动A/B,大分歧A/B,小分歧" aria-label="开仓类型">
|
||||
|
||||
<option value="">类型</option>
|
||||
|
||||
{% for cat in entry_model_categories %}
|
||||
|
||||
{% for opt in cat.options %}
|
||||
|
||||
<option value="{{ opt.code }}" data-entry-category="{{ cat.key }}" data-trade-style="{{ opt.trade_style }}"{% if opt.help %} title="{{ opt.help }}"{% endif %} hidden disabled>{{ opt.label }}</option>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
|
||||
<input type="hidden" name="trade_style" id="order-trade-style-hidden" value="trend">
|
||||
|
||||
<span id="order-trade-style-hint" class="order-trade-style-hint" title="由开仓类型自动设定">趋势单</span>
|
||||
|
||||
</div>
|
||||
|
||||
{% elif order_entry_profile == 'intraday' %}
|
||||
|
||||
<select name="entry_model" id="order-entry-model" class="order-entry-intraday" required title="日内开仓类型" aria-label="开仓类型">
|
||||
|
||||
<option value="">开仓类型</option>
|
||||
|
||||
{% for opt in intraday_entry_model_options %}
|
||||
|
||||
<option value="{{ opt.code }}"{% if opt.help %} title="{{ opt.help }}"{% endif %}>{{ opt.label }}</option>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
|
||||
<input type="hidden" name="trade_style" value="trend">
|
||||
|
||||
{% else %}
|
||||
|
||||
<select name="trade_style" required>
|
||||
|
||||
<option value="trend">趋势单</option>
|
||||
|
||||
<option value="swing">波段单</option>
|
||||
|
||||
</select>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{%- endmacro %}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script src="/static/instance_theme.js?v=5"></script>
|
||||
<title>{{ exchange_display }} | 实盘下单放大</title>
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=5">
|
||||
<link rel="stylesheet" href="/static/focus_chart_page.css?v=1">
|
||||
</head>
|
||||
<body class="focus-page">
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="row" style="justify-content:space-between">
|
||||
<div class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
|
||||
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12.1 3a9 9 0 1 0 8.9 11 6.5 6.5 0 1 1-8.9-11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="theme-toggle-btn" data-theme-value="light" aria-pressed="false" title="亮色主题">
|
||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a class="btn" href="/">返回首页</a>
|
||||
<strong class="focus-title">实盘下单放大(100根K线)</strong><span class="exchange-tag">{{ exchange_display }}</span>
|
||||
</div>
|
||||
<div class="status">最近刷新:<span id="updated-at">--</span></div>
|
||||
</div>
|
||||
{% if orders %}
|
||||
<div class="row" style="margin-top:10px">
|
||||
<label>订单</label>
|
||||
<select id="order-id">
|
||||
{% for o in orders %}
|
||||
<option value="{{ o.id }}" {% if selected_order and o.id == selected_order.id %}selected{% endif %}>
|
||||
#{{ o.id }} {{ o.symbol }} {{ '做多' if o.direction == 'long' else '做空' }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label>周期</label>
|
||||
<select id="timeframe">
|
||||
{% for tf in ['1m','3m','5m','15m','30m','1h','4h','1d'] %}
|
||||
<option value="{{ tf }}" {% if tf == default_timeframe %}selected{% endif %}>{{ tf }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button id="manual-refresh" type="button">刷新</button>
|
||||
<span id="load-status" class="status"></span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">当前没有激活订单,无法展示放大K线.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if orders %}
|
||||
<div class="card">
|
||||
<div class="meta">
|
||||
<div class="meta-item meta-item--emph"><div class="k">交易对</div><div class="v" id="m-symbol">-</div></div>
|
||||
<div class="meta-item meta-item--emph"><div class="k">方向</div><div class="v" id="m-direction">-</div></div>
|
||||
<div class="meta-item"><div class="k">成交价</div><div class="v" id="m-entry">-</div></div>
|
||||
<div class="meta-item"><div class="k">止损</div><div class="v" id="m-sl">-</div></div>
|
||||
<div class="meta-item"><div class="k">止盈</div><div class="v" id="m-tp">-</div></div>
|
||||
<div class="meta-item"><div class="k">盈亏比</div><div class="v" id="m-rr">-</div></div>
|
||||
<div class="meta-item"><div class="k">移动保本</div><div class="v" id="m-breakeven">-</div></div>
|
||||
<div class="meta-item"><div class="k">现价</div><div class="v" id="m-price">-</div></div>
|
||||
<div class="meta-item meta-item--emph meta-item--pnl"><div class="k">浮盈亏</div><div class="v" id="m-pnl">-</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div id="chart-wrap"><div id="chart"></div></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if orders %}
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/static/focus_chart_page.js?v=2"></script>
|
||||
<script>
|
||||
const refreshMs = Math.max({{ price_refresh_seconds * 1000 }}, 5000);
|
||||
const orderSelect = document.getElementById("order-id");
|
||||
const tfSelect = document.getElementById("timeframe");
|
||||
const statusEl = document.getElementById("load-status");
|
||||
const updatedAtEl = document.getElementById("updated-at");
|
||||
const chartHost = document.getElementById("chart");
|
||||
const FCP = window.FocusChartPage;
|
||||
let fc = null;
|
||||
|
||||
function ensureChart(){
|
||||
if(fc && fc.ensureSeries()) return true;
|
||||
if(!window.LightweightCharts){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "图表库加载失败";
|
||||
return false;
|
||||
}
|
||||
fc = FCP.createFocusChart(chartHost);
|
||||
if(!fc || !fc.ensureSeries()){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "K线序列初始化失败";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function loadOrderKline(){
|
||||
if(!ensureChart()) return;
|
||||
const orderId = orderSelect.value;
|
||||
const timeframe = tfSelect.value;
|
||||
if(!orderId) return;
|
||||
statusEl.className = "status";
|
||||
statusEl.innerText = "加载中...";
|
||||
try{
|
||||
const resp = await fetch(`/api/order_kline?order_id=${encodeURIComponent(orderId)}&timeframe=${encodeURIComponent(timeframe)}`);
|
||||
const data = await resp.json();
|
||||
if(!resp.ok || !data.ok) throw new Error(data.msg || "请求失败");
|
||||
if(fc && typeof fc.setPriceTick === "function") fc.setPriceTick(data.price_tick);
|
||||
else FCP.setActivePriceTick(data.price_tick);
|
||||
const candles = Array.isArray(data.candles) ? data.candles : [];
|
||||
if(!candles.length){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = "暂无K线数据";
|
||||
return;
|
||||
}
|
||||
fc.candleSeries.setData(candles);
|
||||
fc.resetPriceLines();
|
||||
const o = data.order || {};
|
||||
fc.addLine(o.trigger_price, FCP.lineTitle("成交价", o.trigger_price_display), "#42a5f5");
|
||||
fc.addLine(o.stop_loss, FCP.lineTitle("止损", o.stop_loss_display), "#ff6666");
|
||||
fc.addLine(o.take_profit, FCP.lineTitle("止盈", o.take_profit_display), "#4cd97f");
|
||||
const markPx = o.current_price;
|
||||
if(markPx) fc.addLine(markPx, FCP.lineTitle("现价", o.current_price_display), "#ffb74d");
|
||||
fc.chart.timeScale().fitContent();
|
||||
FCP.paintOrderMeta(o);
|
||||
updatedAtEl.innerText = data.updated_at || "--";
|
||||
statusEl.className = "status";
|
||||
statusEl.innerText = `已加载 ${candles.length} 根K线`;
|
||||
}catch(err){
|
||||
statusEl.className = "status err";
|
||||
statusEl.innerText = err && err.message ? err.message : "加载失败";
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("manual-refresh").addEventListener("click", loadOrderKline);
|
||||
orderSelect.addEventListener("change", loadOrderKline);
|
||||
tfSelect.addEventListener("change", loadOrderKline);
|
||||
loadOrderKline();
|
||||
setInterval(loadOrderKline, refreshMs);
|
||||
</script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
{# 以损定仓:杠杆按币种默认(BTC/ETH 10x,其它 5x),不可选手输 #}
|
||||
{% macro order_leverage_fields() -%}
|
||||
{% if position_sizing_mode != 'full_margin' %}
|
||||
<input type="hidden" id="order-leverage" name="leverage" value="">
|
||||
<span id="order-leverage-hint" class="order-leverage-hint" title="BTC/ETH 默认10x,其它默认5x">杠杆 —</span>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,77 @@
|
||||
{# 实盘下单监控 · 开仓表单(实例页与中控嵌入共用) #}
|
||||
<form id="add-order-form" action="/add_order" method="post" class="order-monitor-form" data-risk-percent="{{ risk_percent }}">
|
||||
<div class="om-row om-row-policy">
|
||||
{% from 'trade_policy_fields.html' import trade_policy_symbol, trade_policy_direction with context %}
|
||||
{{ trade_policy_symbol('symbol', 'order-symbol') }}
|
||||
{{ trade_policy_direction('direction', 'order-direction') }}
|
||||
<select id="sltp-mode" name="sltp_mode" title="止盈止损模式">
|
||||
<option value="fixed_rr" selected>止盈止损:固定盈亏比</option>
|
||||
<option value="price">止盈止损:价格模式</option>
|
||||
<option value="pct">止盈止损:百分比模式</option>
|
||||
</select>
|
||||
{% from 'order_entry_model_fields.html' import order_entry_type_fields with context %}
|
||||
{{ order_entry_type_fields() }}
|
||||
{% from 'order_leverage_fields.html' import order_leverage_fields with context %}
|
||||
{{ order_leverage_fields() }}
|
||||
</div>
|
||||
|
||||
<div class="om-row om-row-levels">
|
||||
<label class="om-field" id="om-field-sl">
|
||||
<span class="om-field-lab">止损价格</span>
|
||||
<input id="order-sl" name="sl" step="any" placeholder="必填" required>
|
||||
</label>
|
||||
<label class="om-field" id="om-field-rr">
|
||||
<span class="om-field-lab">盈亏比</span>
|
||||
<input id="order-fixed-rr" name="fixed_rr" type="number" min="0.01" step="0.01" placeholder="默认1.5" value="1.5" title="止盈距离=止损距离×盈亏比">
|
||||
</label>
|
||||
<label class="om-field" id="om-field-tp" style="display:none">
|
||||
<span class="om-field-lab">止盈价格</span>
|
||||
<input id="order-tp" name="tgt" step="any" placeholder="止盈价格">
|
||||
</label>
|
||||
<label class="om-field" id="om-field-sl-pct" style="display:none">
|
||||
<span class="om-field-lab">止损%</span>
|
||||
<input id="order-sl-pct" name="sl_pct" type="number" min="0.01" step="0.01" placeholder="止损%">
|
||||
</label>
|
||||
<label class="om-field" id="om-field-tp-pct" style="display:none">
|
||||
<span class="om-field-lab">止盈%</span>
|
||||
<input id="order-tp-pct" name="tp_pct" type="number" min="0.01" step="0.01" placeholder="止盈%">
|
||||
</label>
|
||||
<div class="om-live-meta">
|
||||
{% from 'symbol_live_price_snippet.html' import symbol_live_price_hint %}
|
||||
{{ symbol_live_price_hint('order-symbol-live-price', 'order-symbol', 'order-direction') }}
|
||||
<span class="symbol-live-price-note">成交价以交易所回报为准</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="om-row om-row-opts">
|
||||
{% if not intraday_discipline %}
|
||||
<label class="om-check">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" checked>
|
||||
<span>移动保本</span>
|
||||
</label>
|
||||
<span id="order-time-close-wrap" class="order-time-close-wrap om-time-close">
|
||||
<label class="om-check" style="margin:0">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="order-time-close-cb">
|
||||
<span>时间平仓</span>
|
||||
</label>
|
||||
<select name="time_close_hours" id="order-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</span>
|
||||
{% else %}
|
||||
<input type="hidden" name="breakeven_enabled" value="0">
|
||||
{% endif %}
|
||||
<label class="om-check" title="开仓后生成多周期K线图(各周期100根,含开平仓标记)">
|
||||
<input type="checkbox" name="order_chart" value="true">
|
||||
<span>开仓后生成多周期K线</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="om-row om-row-action">
|
||||
<button type="submit" class="om-submit{% if not can_trade %} is-blocked{% endif %}" id="om-submit-btn"{% if not can_trade %} disabled aria-disabled="true"{% endif %}>{{ open_position_button_label }}</button>
|
||||
<span id="om-open-block-note" class="om-open-block-note"{% if can_trade or not (open_block_note|default('')) %} hidden{% endif %}>{% if not can_trade and (open_block_note|default('')) %}{{ open_block_note }}{% endif %}</span>
|
||||
</div>
|
||||
</form>
|
||||
{% include 'order_plan_preview_bar.html' %}
|
||||
@@ -0,0 +1,38 @@
|
||||
<details class="tip-collapse order-rule-collapse">
|
||||
<summary class="tip-collapse-summary">开仓规则说明</summary>
|
||||
<div class="tip-collapse-body rule-tip" id="order-rule-tip">
|
||||
规则:最多 {{ max_active_positions }} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;
|
||||
本交易日开仓 {{ opens_today }}{% if daily_open_hard_limit > 0 %} / 硬上限 {{ daily_open_hard_limit }}{% endif %}(AI 提醒 {{ daily_open_alert_threshold }});
|
||||
{% if can_trade %}可开仓{% else %}不可开仓(持仓已满,单日开仓达上限,或未到北京时间 {{ reset_hour }}:00){% endif %};
|
||||
人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-exit-collapse">
|
||||
<summary class="tip-collapse-summary">平仓 / 委托 / 强制清仓</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
<ul style="margin:0;padding-left:1.1em;line-height:1.55">
|
||||
<li><strong>止盈止损委托</strong>:开仓后挂交易所条件止盈/止损;持仓卡可查看状态,「委托」可重挂,「撤单」只撤对应条件单、不平仓。</li>
|
||||
<li><strong>手动平仓</strong>:持仓卡「平仓」会市价平仓并撤销该合约条件单,交易记录记为「手动平仓」。</li>
|
||||
<li><strong>强制清仓</strong>:
|
||||
{% if force_close is defined and force_close.enabled %}
|
||||
已开启 · 北京时间 <strong>{{ force_close.hour_label }}</strong> 整点起 <strong>{{ force_close.grace_minutes|default(5) }}</strong> 分钟内,本地状态为 active 的下单监控会被市价清仓,结果记「强制清仓」;该窗口内开仓按钮灰显不可点。
|
||||
{% else %}
|
||||
当前<strong>已关闭</strong>(<code>FORCE_CLOSE_ENABLED=false</code>);开启后按 <code>FORCE_CLOSE_BJ_HOUR</code> 在北京时间该整点起 <code>FORCE_CLOSE_GRACE_MINUTES</code>(默认 5)分钟内清掉 active 监控仓。
|
||||
{% endif %}
|
||||
冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓。
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-sizing-collapse">
|
||||
<summary class="tip-collapse-summary">计仓与保本说明</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
计仓模式:<strong>{{ position_sizing_mode_label }}</strong>(仅 .env <code>POSITION_SIZING_MODE</code>,须无仓后重启)
|
||||
{% if position_sizing_mode == 'full_margin' %}
|
||||
|全仓:合约可用×{{ full_margin_buffer_ratio }},BTC/ETH {{ btc_leverage }}x,其它 {{ alt_leverage }}x,单仓;张数按交易所精度
|
||||
{% else %}
|
||||
|以损定仓:风险 {{ risk_percent }}%
|
||||
{% endif %}
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,38 @@
|
||||
<details class="tip-collapse order-rule-collapse">
|
||||
<summary class="tip-collapse-summary">开仓规则说明</summary>
|
||||
<div class="tip-collapse-body rule-tip" id="order-rule-tip">
|
||||
规则:最多 {{ max_active_positions }} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;
|
||||
本交易日开仓 {{ opens_today }}{% if daily_open_hard_limit > 0 %} / 硬上限 {{ daily_open_hard_limit }}{% endif %}(AI 提醒 {{ daily_open_alert_threshold }});
|
||||
{% if can_trade %}可开仓{% else %}不可开仓(持仓已满,单日开仓达上限,或未到北京时间 {{ reset_hour }}:00){% endif %};
|
||||
人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-exit-collapse">
|
||||
<summary class="tip-collapse-summary">平仓 / 委托 / 强制清仓</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
<ul style="margin:0;padding-left:1.1em;line-height:1.55">
|
||||
<li><strong>止盈止损委托</strong>:开仓后挂交易所条件止盈/止损;持仓卡可查看状态,「委托」可重挂,「撤单」只撤对应条件单、不平仓。</li>
|
||||
<li><strong>手动平仓</strong>:持仓卡「平仓」会市价平仓并撤销该合约条件单,交易记录记为「手动平仓」。</li>
|
||||
<li><strong>强制清仓</strong>:
|
||||
{% if force_close is defined and force_close.enabled %}
|
||||
已开启 · 北京时间 <strong>{{ force_close.hour_label }}</strong> 整点起 <strong>{{ force_close.grace_minutes|default(5) }}</strong> 分钟内,本地状态为 active 的下单监控会被市价清仓,结果记「强制清仓」;该窗口内开仓按钮灰显不可点。
|
||||
{% else %}
|
||||
当前<strong>已关闭</strong>(<code>FORCE_CLOSE_ENABLED=false</code>);开启后按 <code>FORCE_CLOSE_BJ_HOUR</code> 在北京时间该整点起 <code>FORCE_CLOSE_GRACE_MINUTES</code>(默认 5)分钟内清掉 active 监控仓。
|
||||
{% endif %}
|
||||
冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓;交易所裸仓且无本地监控时不会被此逻辑平掉。
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-sizing-collapse">
|
||||
<summary class="tip-collapse-summary">计仓与保本说明</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
计仓模式:<strong>{{ position_sizing_mode_label }}</strong>(仅 .env <code>POSITION_SIZING_MODE</code>,须无仓后重启)
|
||||
{% if position_sizing_mode == 'full_margin' %}
|
||||
|全仓:合约可用×{{ full_margin_buffer_ratio }},BTC/ETH {{ btc_leverage }}x,其它 {{ alt_leverage }}x,单仓;张数按交易所精度
|
||||
{% else %}
|
||||
|以损定仓:风险 {{ risk_percent }}%
|
||||
{% endif %}
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,21 @@
|
||||
<details class="tip-collapse order-rule-collapse">
|
||||
<summary class="tip-collapse-summary">开仓规则说明</summary>
|
||||
<div class="tip-collapse-body rule-tip" id="order-rule-tip">
|
||||
规则:最大同时持仓 {{ max_active_positions }}(当前 active {{ active_count }});与「趋势回调」计划互斥;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;
|
||||
本交易日开仓 {{ opens_today }}{% if daily_open_hard_limit > 0 %} / 硬上限 {{ daily_open_hard_limit }}{% endif %}(AI 提醒 {{ daily_open_alert_threshold }});
|
||||
{% if can_trade %}可开仓{% else %}不可开仓(持仓达上限,单日开仓达上限,有趋势回调计划,或未到北京时间 {{ reset_hour }}:00){% endif %};
|
||||
人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-sizing-collapse">
|
||||
<summary class="tip-collapse-summary">计仓与保本说明</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
计仓模式:<strong>{{ position_sizing_mode_label }}</strong>(仅 .env <code>POSITION_SIZING_MODE</code>,须无仓后重启)
|
||||
{% if position_sizing_mode == 'full_margin' %}
|
||||
|全仓:合约可用×{{ full_margin_buffer_ratio }},BTC/ETH {{ btc_leverage }}x,其它 {{ alt_leverage }}x,单仓;张数按交易所精度
|
||||
{% else %}
|
||||
|以损定仓:风险 {{ risk_percent }}%
|
||||
{% endif %}
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,38 @@
|
||||
<details class="tip-collapse order-rule-collapse">
|
||||
<summary class="tip-collapse-summary">开仓规则说明</summary>
|
||||
<div class="tip-collapse-body rule-tip" id="order-rule-tip">
|
||||
规则:最多 {{ max_active_positions }} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;
|
||||
本交易日开仓 {{ opens_today }}{% if daily_open_hard_limit > 0 %} / 硬上限 {{ daily_open_hard_limit }}{% endif %}(AI 提醒 {{ daily_open_alert_threshold }});
|
||||
{% if can_trade %}可开仓{% else %}不可开仓{% if active_count >= max_active_positions %}(持仓 {{ active_count }}/{{ max_active_positions }}){% endif %}{% if daily_open_hard_limit > 0 and opens_today >= daily_open_hard_limit %}(单日开仓达上限){% endif %}{% if open_guard_blocks_now %}(未到北京时间 {{ reset_hour }}:00){% endif %}{% endif %};
|
||||
人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-exit-collapse">
|
||||
<summary class="tip-collapse-summary">平仓 / 委托 / 强制清仓</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
<ul style="margin:0;padding-left:1.1em;line-height:1.55">
|
||||
<li><strong>止盈止损委托</strong>:开仓后挂交易所条件止盈/止损;持仓卡可查看状态,「委托」可重挂,「撤单」只撤对应条件单、不平仓。</li>
|
||||
<li><strong>手动平仓</strong>:持仓卡「平仓」会市价平仓并撤销该合约条件单,交易记录记为「手动平仓」。</li>
|
||||
<li><strong>强制清仓</strong>:
|
||||
{% if force_close is defined and force_close.enabled %}
|
||||
已开启 · 北京时间 <strong>{{ force_close.hour_label }}</strong> 整点起 <strong>{{ force_close.grace_minutes|default(5) }}</strong> 分钟内,本地状态为 active 的下单监控会被市价清仓,结果记「强制清仓」;该窗口内开仓按钮灰显不可点。
|
||||
{% else %}
|
||||
当前<strong>已关闭</strong>(<code>FORCE_CLOSE_ENABLED=false</code>);开启后按 <code>FORCE_CLOSE_BJ_HOUR</code> 在北京时间该整点起 <code>FORCE_CLOSE_GRACE_MINUTES</code>(默认 5)分钟内清掉 active 监控仓。
|
||||
{% endif %}
|
||||
冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓。
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
<details class="tip-collapse order-sizing-collapse">
|
||||
<summary class="tip-collapse-summary">计仓与保本说明</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
计仓模式:<strong>{{ position_sizing_mode_label }}</strong>(仅 .env <code>POSITION_SIZING_MODE</code>,须无仓后重启)
|
||||
{% if position_sizing_mode == 'full_margin' %}
|
||||
|全仓:合约可用×{{ full_margin_buffer_ratio }},BTC/ETH {{ btc_leverage }}x,其它 {{ alt_leverage }}x,单仓;张数按交易所精度
|
||||
{% else %}
|
||||
|以损定仓:风险 {{ risk_percent }}%
|
||||
{% endif %}
|
||||
|移动保本:下单可勾选关闭;开启时 {{ breakeven_rr_trigger }}R 触发(每 1R 阶梯上移),偏移 {{ breakeven_offset_pct }}%
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div id="order-plan-preview" class="order-plan-preview">
|
||||
<span id="order-risk-preview" class="order-preview-risk">预估风险:<strong>—</strong></span>
|
||||
<span id="order-profit-preview" class="order-preview-profit">预估盈利:<strong>—</strong></span>
|
||||
<span id="order-rr-preview" class="order-preview-rr">预估盈亏比:<strong>—</strong></span>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
{# 系统设置 · 账户密码(外层 card 由 settings_panel 提供) #}
|
||||
<h2>账户密码修改</h2>
|
||||
<p class="settings-subcard-desc">修改网页登录账号密码,写入 <code>.env</code> 后需重启实例生效.</p>
|
||||
<div class="settings-password-form password-settings" data-password-settings="1">
|
||||
<label>当前密码 <input type="password" id="pwd-old" autocomplete="current-password"></label>
|
||||
<label>新用户名(可选) <input type="text" id="pwd-new-username" autocomplete="username"></label>
|
||||
<label>新密码 <input type="password" id="pwd-new" autocomplete="new-password"></label>
|
||||
<label>确认新密码 <input type="password" id="pwd-confirm" autocomplete="new-password"></label>
|
||||
</div>
|
||||
<div class="settings-actions-row">
|
||||
<button type="button" class="btn-primary btn-sm" id="pwd-save-btn">保存密码</button>
|
||||
<span class="settings-status-line" id="pwd-save-status"></span>
|
||||
</div>
|
||||
@@ -0,0 +1,153 @@
|
||||
{# 三所共用:交易记录(5/页) → 填入复盘出表单 → 交易复盘记录 / AI历史复盘 #}
|
||||
<style>
|
||||
.records-panel-wrap{display:flex;flex-direction:column;gap:14px}
|
||||
.records-panel-wrap .rr-pager{display:flex;align-items:center;gap:8px;margin-top:8px;font-size:.74rem}
|
||||
.records-panel-wrap .rr-list-loading{opacity:.55;pointer-events:none;transition:opacity .12s ease}
|
||||
.records-panel-wrap .rr-trades-wrap{min-height:9.5rem}
|
||||
.records-panel-wrap .journal-card.hidden{display:none!important}
|
||||
.records-panel-wrap .rr-hint{font-size:.72rem;color:#8892b0;margin:0 0 8px}
|
||||
</style>
|
||||
<div class="records-panel-wrap" id="records-panel-root" style="grid-column:1/-1">
|
||||
<div class="card full records-card">
|
||||
<h2>交易记录</h2>
|
||||
<p class="rr-hint">每页5条.点「填入复盘」打开下方复盘表单.</p>
|
||||
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||
<input id="review-mode-toggle" type="checkbox">
|
||||
修改/核对开关(开启后可编辑关键字段)
|
||||
</label>
|
||||
</div>
|
||||
<div class="table-wrap rr-trades-wrap" id="rr-trades-wrap">
|
||||
<table id="rr-trades-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>盈亏U</th><th>结果</th><th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="rr-trades-tbody">
|
||||
<tr><td colspan="15" class="muted">加载中…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="rr-pager" id="rr-trades-pager">
|
||||
<button type="button" class="btn-secondary" id="rr-trades-prev" style="font-size:.72rem;padding:2px 8px">上一页</button>
|
||||
<span class="muted" id="rr-trades-page-label">第 1 / 1 页</span>
|
||||
<button type="button" class="btn-secondary" id="rr-trades-next" style="font-size:.72rem;padding:2px 8px">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card full journal-card hidden" id="journal-card">
|
||||
<div class="form-row" style="align-items:center;gap:8px;margin-bottom:6px">
|
||||
<h2 style="margin:0;margin-right:auto">交易复盘记录上传(含截图)</h2>
|
||||
<button type="button" class="btn-secondary" id="rr-journal-hide-btn" style="font-size:.76rem;padding:4px 10px">收起</button>
|
||||
</div>
|
||||
<p class="rr-hint" id="rr-journal-fill-hint" style="display:none">已从交易记录填入,请补充主观原因后保存.</p>
|
||||
<form id="journal-form" action="/add_journal" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="risk_amount_hint" id="risk-amount-hint">
|
||||
<input type="hidden" name="entry_price_hint" id="entry-price-hint">
|
||||
<input type="hidden" name="stop_loss_hint" id="stop-loss-hint">
|
||||
<input type="hidden" name="exit_price_hint" id="exit-price-hint">
|
||||
<input type="hidden" name="direction_hint" id="direction-hint">
|
||||
{% from 'journal_form_fields.html' import journal_form_fields %}
|
||||
{{ journal_form_fields(entry_reason_options, order_type_options) }}
|
||||
{% from 'journal_upload_slots.html' import journal_upload_slots %}
|
||||
{{ journal_upload_slots() }}
|
||||
<div class="form-row journal-chart-options" style="flex-wrap:wrap;align-items:center">
|
||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="journal_exchange_chart" value="true">
|
||||
保存后后台生成 K 线图并作为截图(不阻塞保存)
|
||||
</label>
|
||||
<label style="font-size:.82rem;color:#9aa">周期1</label>
|
||||
<select name="journal_chart_tf1" style="min-width:72px">
|
||||
{% for tf in journal_chart_tf_choices %}
|
||||
<option value="{{ tf }}" {% if tf == journal_chart_default_tf1 %}selected{% endif %}>{{ tf }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label style="font-size:.82rem;color:#9aa">周期2</label>
|
||||
<select name="journal_chart_tf2" style="min-width:72px">
|
||||
{% for tf in journal_chart_tf_choices %}
|
||||
<option value="{{ tf }}" {% if tf == journal_chart_default_tf2 %}selected{% endif %}>{{ tf }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label style="font-size:.82rem;color:#9aa">K线数</label>
|
||||
<select name="journal_chart_limit" style="min-width:72px">
|
||||
{% for n in [100, 150, 200, 250, 300, 400, 500] %}
|
||||
<option value="{{ n }}" {% if n == journal_chart_default_limit %}selected{% endif %}>{{ n }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label style="font-size:.82rem;color:#9aa">K线截止</label>
|
||||
<select name="journal_chart_anchor" id="journal-chart-anchor" style="min-width:96px" title="K线窗口右端对齐的时间">
|
||||
<option value="close" {% if journal_chart_default_anchor == 'close' %}selected{% endif %}>平仓时间</option>
|
||||
<option value="now" {% if journal_chart_default_anchor == 'now' %}selected{% endif %}>当前时间</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sub" id="journal-chart-anchor-hint" style="font-size:.72rem;color:#8892b0;margin-top:2px;margin-bottom:0">双周期上下排列;截止=平仓时间:开仓前背景至平仓;截止=当前时间:最近 N 根至此刻(可看平仓后走势);标注开仓,平仓与止损位</div>
|
||||
<div class="mood-grid">
|
||||
<label><input type="checkbox" name="mood_issues" value="怕踏空">怕踏空</label>
|
||||
<label><input type="checkbox" name="mood_issues" value="报复开仓">报复开仓</label>
|
||||
<label><input type="checkbox" name="mood_issues" value="盈利飘了">盈利飘了</label>
|
||||
<label><input type="checkbox" name="mood_issues" value="拿不住单">拿不住单</label>
|
||||
<label><input type="checkbox" name="mood_issues" value="扛单">扛单</label>
|
||||
<label><input type="checkbox" name="mood_issues" value="重仓违规">重仓违规</label>
|
||||
</div>
|
||||
<textarea name="note" rows="2" placeholder="备注" autocomplete="off" data-lpignore="true" data-1p-ignore="true" data-form-type="other"></textarea>
|
||||
<button type="submit" style="margin-top:8px">保存复盘记录</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card full review-card" id="review-card">
|
||||
<div class="review-card-head">
|
||||
<h2>AI复盘(按交易记录)</h2>
|
||||
<button type="button" class="review-card-fs-btn" id="review-card-fs-btn" onclick="toggleReviewCardFullscreen()">全屏</button>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="date" id="day_date">
|
||||
<button type="button" id="gen-daily-btn" onclick="genDaily()">生成日复盘</button>
|
||||
<button type="button" onclick="exportDailyBundleMd()" style="background:#1f3a5a">导出当日日复盘MD</button>
|
||||
<input type="date" id="week_start">
|
||||
<input type="date" id="week_end">
|
||||
<button type="button" id="gen-weekly-btn" onclick="genWeekly()">生成周复盘</button>
|
||||
<button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button>
|
||||
</div>
|
||||
<div class="ai-result-wrap" id="daily_result_wrap" style="display:none">
|
||||
<div id="daily_result" class="ai-result"></div>
|
||||
<div class="ai-result-toolbar">
|
||||
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('日复盘结果', 'daily_result')">全屏查看</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-result-wrap" id="weekly_result_wrap" style="display:none">
|
||||
<div id="weekly_result" class="ai-result"></div>
|
||||
<div class="ai-result-toolbar">
|
||||
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('周复盘结果', 'weekly_result')">全屏查看</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card full" id="rr-journals-card">
|
||||
<h3 style="margin-top:0">交易复盘记录</h3>
|
||||
<p class="rr-hint" id="rr-journals-hint">已保存的复盘(每页5条).</p>
|
||||
<div id="journal-list-wrap" class="rr-list-wrap rr-journals-wrap">
|
||||
<div id="journal-list"></div>
|
||||
</div>
|
||||
<div class="rr-pager" id="rr-journals-pager">
|
||||
<button type="button" class="btn-secondary" id="rr-journals-prev" style="font-size:.72rem;padding:2px 8px">上一页</button>
|
||||
<span class="muted" id="rr-journals-page-label">第 1 / 1 页</span>
|
||||
<button type="button" class="btn-secondary" id="rr-journals-next" style="font-size:.72rem;padding:2px 8px">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card full" id="rr-ai-history-card">
|
||||
<h3 style="margin-top:0">AI历史复盘</h3>
|
||||
<p class="rr-hint">日/周 AI 复盘历史(每页5条).</p>
|
||||
<div id="review-list-wrap" class="rr-list-wrap">
|
||||
<div id="review-list"></div>
|
||||
</div>
|
||||
<div class="rr-pager" id="rr-reviews-pager">
|
||||
<button type="button" class="btn-secondary" id="rr-reviews-prev" style="font-size:.72rem;padding:2px 8px">上一页</button>
|
||||
<span class="muted" id="rr-reviews-page-label">第 1 / 1 页</span>
|
||||
<button type="button" class="btn-secondary" id="rr-reviews-next" style="font-size:.72rem;padding:2px 8px">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
{# 风控说明:只读展示 .env 风控参数与当前账户状态 #}
|
||||
<div class="risk-policy-page full">
|
||||
<div class="card settings-card settings-card--risk">
|
||||
<h2>风控说明</h2>
|
||||
<p class="settings-live-status">
|
||||
当前账户状态:
|
||||
<span class="risk-status-badge risk-status-{{ risk_status.status|default('normal') }}">
|
||||
{{ instance_settings.risk_status_label }}
|
||||
</span>
|
||||
{% if instance_settings.risk_status_reason %}
|
||||
<span class="settings-status-reason">{{ instance_settings.risk_status_reason }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if instance_settings.trade_policy_note %}
|
||||
<p class="settings-policy-note">账户限制:{{ instance_settings.trade_policy_note }}</p>
|
||||
{% endif %}
|
||||
<p class="settings-env-hint">以下参数读取自本实例 <code>.env</code>,修改后需重启进程生效.</p>
|
||||
<div class="settings-risk-sections">
|
||||
{% for section in instance_settings.sections %}
|
||||
<div class="card settings-subcard">
|
||||
<h3 class="settings-subcard-title">{{ section.title }}</h3>
|
||||
<dl class="settings-kv settings-kv--compact">
|
||||
{% for row in section.rows %}
|
||||
<div class="settings-kv-row">
|
||||
<dt>{{ row.label }}</dt>
|
||||
<dd>
|
||||
<span class="settings-kv-value">{{ row.value }}</span>
|
||||
{% if row.note %}
|
||||
<span class="settings-kv-note">{{ row.note }}</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
{# 系统设置:CSS Tab(与 env 配置同方案) #}
|
||||
<div class="settings-page full">
|
||||
<div class="env-config-head card">
|
||||
<h2>系统设置</h2>
|
||||
<p class="settings-env-hint env-config-head-hint">各区块说明见 <code>docs/系统设置说明.md</code>.</p>
|
||||
</div>
|
||||
|
||||
{% if settings_tabs %}
|
||||
{% set _sub = (request.args.get('settings_tab') or '').strip() %}
|
||||
{% set _legacy_tab = (request.args.get('tab') or '').strip() %}
|
||||
{% set ns = namespace(active_idx=0, active_key='') %}
|
||||
{% for tab in settings_tabs %}
|
||||
{% if _sub and tab.key == _sub %}
|
||||
{% set ns.active_idx = loop.index0 %}
|
||||
{% set ns.active_key = tab.key %}
|
||||
{% elif (not _sub) and _legacy_tab and tab.key == _legacy_tab %}
|
||||
{% set ns.active_idx = loop.index0 %}
|
||||
{% set ns.active_key = tab.key %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="env-config-body card settings-config-body" data-settings-active-tab="{{ ns.active_key }}">
|
||||
{% for tab in settings_tabs %}
|
||||
<input type="radio" name="settings-section" id="settings-sec-{{ loop.index0 }}" class="env-tab-radio" data-settings-tab="{{ tab.key }}"{% if loop.index0 == ns.active_idx %} checked{% endif %}>
|
||||
{% endfor %}
|
||||
<div class="env-config-tabs" role="tablist" aria-label="系统设置分类">
|
||||
{% for tab in settings_tabs %}
|
||||
<label for="settings-sec-{{ loop.index0 }}" class="env-tab-btn" role="tab">{{ tab.title }}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="env-config-panels">
|
||||
{% for tab in settings_tabs %}
|
||||
<section class="env-panel env-panel--{{ loop.index0 }} settings-tab-panel" role="tabpanel">
|
||||
{% if tab.key == 'nav' %}
|
||||
{% include 'display_prefs_panel.html' %}
|
||||
{% elif tab.key == 'sim_funds' %}
|
||||
{% include 'sim_funds_panel.html' %}
|
||||
{% elif tab.key == 'password' %}
|
||||
{% include 'password_settings_panel.html' %}
|
||||
{% elif tab.key == 'transfer' %}
|
||||
<h2>永续资金划转</h2>
|
||||
<p class="settings-subcard-desc">账户内:资金账户与交易账户之间划转 USDT.</p>
|
||||
{% include 'instance_transfer_panel.html' %}
|
||||
{% elif tab.key == 'export' %}
|
||||
<h2>数据导出</h2>
|
||||
<p class="settings-subcard-desc muted">CSV · v{{ instance_settings.data_export_version }}</p>
|
||||
<div class="settings-export-links-inline settings-export-links-block">
|
||||
<a href="/export/key_monitors">关键位(当前)</a>
|
||||
<a href="/export/key_monitor_history">关键位历史</a>
|
||||
</div>
|
||||
{% elif tab.key == 'options_swap' %}
|
||||
<h2>币种兑换</h2>
|
||||
{% include 'options_settings_swap.html' %}
|
||||
{% elif tab.key == 'options_transfer' %}
|
||||
<h2>期权资金划转</h2>
|
||||
{% include 'options_settings_transfer.html' %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if instance_settings.options_settings_enabled %}
|
||||
{% include 'options_settings_panel.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
var q = new URLSearchParams(window.location.search || "");
|
||||
var key = (q.get("settings_tab") || "").trim();
|
||||
if (!key) return;
|
||||
var radio = document.querySelector('input.env-tab-radio[data-settings-tab="' + key + '"]');
|
||||
if (radio) radio.checked = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
{# 币种输入旁实时现价(须加载 symbol_live_price.js) #}
|
||||
{% macro symbol_live_price_hint(price_id, symbol_input_id, direction_input_id='') -%}
|
||||
<span
|
||||
id="{{ price_id }}"
|
||||
class="symbol-live-price"
|
||||
data-symbol-input="{{ symbol_input_id }}"
|
||||
{% if direction_input_id %}data-direction-input="{{ direction_input_id }}"{% endif %}
|
||||
aria-live="polite"
|
||||
>现价:—</span>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,87 @@
|
||||
{# 系统说明: docs/系统说明.md + h2 目录 #}
|
||||
<div class="system-guide-page full">
|
||||
<div class="card system-guide-card">
|
||||
<div class="system-guide-head">
|
||||
<h2 style="margin:0">系统说明</h2>
|
||||
<p class="muted" style="margin:6px 0 0;font-size:.85rem">操作与逻辑按章节混排。默认不在顶栏显示;可在系统设置 → 导航显示中打开。</p>
|
||||
</div>
|
||||
<div class="system-guide-layout">
|
||||
{% if system_guide_toc %}
|
||||
<aside class="system-guide-toc" aria-label="章节目录">
|
||||
<div class="system-guide-toc-title">目录</div>
|
||||
<nav>
|
||||
{% for item in system_guide_toc %}
|
||||
<a href="#{{ item.id }}">{{ item.title }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</aside>
|
||||
{% endif %}
|
||||
<article class="system-guide-body prose">
|
||||
{{ system_guide_html|safe }}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.system-guide-page { grid-column: 1 / -1; }
|
||||
.system-guide-card { padding: 16px 18px 28px; }
|
||||
.system-guide-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(160px, 220px) minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
margin-top: 14px;
|
||||
align-items: start;
|
||||
}
|
||||
.system-guide-toc {
|
||||
position: sticky;
|
||||
top: 8px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(127,127,127,.25);
|
||||
border-radius: 8px;
|
||||
background: rgba(127,127,127,.06);
|
||||
max-height: calc(100vh - 120px);
|
||||
overflow: auto;
|
||||
}
|
||||
.system-guide-toc-title {
|
||||
font-size: .78rem;
|
||||
font-weight: 600;
|
||||
opacity: .75;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.system-guide-toc a {
|
||||
display: block;
|
||||
font-size: .84rem;
|
||||
line-height: 1.35;
|
||||
padding: 5px 0;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
opacity: .9;
|
||||
}
|
||||
.system-guide-toc a:hover { opacity: 1; text-decoration: underline; }
|
||||
.system-guide-body { min-width: 0; line-height: 1.65; font-size: .92rem; }
|
||||
.system-guide-body h1 { font-size: 1.35rem; margin: 0 0 12px; }
|
||||
.system-guide-body h2 { font-size: 1.12rem; margin: 22px 0 10px; padding-top: 4px; scroll-margin-top: 12px; }
|
||||
.system-guide-body h3 { font-size: 1rem; margin: 16px 0 8px; }
|
||||
.system-guide-body p, .system-guide-body li { margin: 0 0 8px; }
|
||||
.system-guide-body ul, .system-guide-body ol { padding-left: 1.35em; margin: 0 0 10px; }
|
||||
.system-guide-body table { border-collapse: collapse; width: 100%; margin: 10px 0 14px; font-size: .86rem; }
|
||||
.system-guide-body th, .system-guide-body td {
|
||||
border: 1px solid rgba(127,127,127,.35);
|
||||
padding: 7px 9px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.system-guide-body code {
|
||||
font-family: ui-monospace, Consolas, monospace;
|
||||
font-size: .86em;
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
background: rgba(127,127,127,.12);
|
||||
}
|
||||
.system-guide-body hr { border: 0; border-top: 1px solid rgba(127,127,127,.28); margin: 18px 0; }
|
||||
@media (max-width: 820px) {
|
||||
.system-guide-layout { grid-template-columns: 1fr; }
|
||||
.system-guide-toc { position: static; max-height: none; }
|
||||
.system-guide-toc nav { display: flex; flex-wrap: wrap; gap: 4px 12px; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
{# 方向 / 币种:env 账户级限制(三所共用宏);调用方须 with context #}
|
||||
{% if trade_policy is not defined %}
|
||||
{% set trade_policy = {'symbol_restrict_enabled': false, 'direction_restrict_enabled': false, 'symbol_whitelist': [], 'allows_long': true, 'allows_short': true, 'direction_mode': 'both', 'badge_text': ''} %}
|
||||
{% endif %}
|
||||
{% macro trade_policy_symbol(name, id, value='', required=true, placeholder='BTC 或 BTC/USDT') -%}
|
||||
{% if trade_policy.symbol_restrict_enabled and trade_policy.symbol_whitelist %}
|
||||
{% set wl = trade_policy.symbol_whitelist %}
|
||||
{% set sole_sym = wl[0] if (wl|length) == 1 else '' %}
|
||||
{% set effective = value if value else sole_sym %}
|
||||
<select name="{{ name }}" id="{{ id }}" {% if required %}required{% endif %} class="trade-policy-symbol-select"{% if sole_sym %} data-sole-symbol="{{ sole_sym }}"{% endif %}>
|
||||
{% if not sole_sym %}<option value="">选择币种</option>{% endif %}
|
||||
{% for sym in wl %}
|
||||
<option value="{{ sym }}" {% if effective and ((effective|upper) == sym or (effective|upper).startswith(sym ~ '/')) %}selected{% endif %}>{{ sym }}/USDT</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input id="{{ id }}" name="{{ name }}" placeholder="{{ placeholder }}" {% if required %}required{% endif %} value="{{ value }}" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" data-lpignore="true" data-1p-ignore="true" data-form-type="other">
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro trade_policy_direction(name, id, required=true, include_empty=true) -%}
|
||||
{% if trade_policy.direction_restrict_enabled and trade_policy.direction_mode == 'long_only' %}
|
||||
<span class="trade-policy-dir-lock" title="账户配置:仅做多">做多</span>
|
||||
<input type="hidden" name="{{ name }}" id="{{ id }}" value="long">
|
||||
{% elif trade_policy.direction_restrict_enabled and trade_policy.direction_mode == 'short_only' %}
|
||||
<span class="trade-policy-dir-lock" title="账户配置:仅做空">做空</span>
|
||||
<input type="hidden" name="{{ name }}" id="{{ id }}" value="short">
|
||||
{% else %}
|
||||
<select name="{{ name }}" id="{{ id }}" {% if required %}required{% endif %}>
|
||||
{% if include_empty %}<option value="">方向</option>{% endif %}
|
||||
{% if trade_policy.allows_long %}<option value="long">做多</option>{% endif %}
|
||||
{% if trade_policy.allows_short %}<option value="short">做空</option>{% endif %}
|
||||
</select>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
Reference in New Issue
Block a user