remove: 全面移除「错过机会」功能与文案
删除手动记录表单与 /add_miss;停止自动生成错过类交易记录;列表/统计/顶栏不再展示;复盘卡片改为整行。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -220,7 +220,11 @@ from lib.common.history_window_lib import (
|
|||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
utc_window_to_utc_sql_strings,
|
utc_window_to_utc_sql_strings,
|
||||||
)
|
)
|
||||||
from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
|
from lib.trade.trade_result_lib import (
|
||||||
|
count_winning_trades,
|
||||||
|
filter_trade_records_excluding_miss,
|
||||||
|
normalize_result_with_pnl,
|
||||||
|
)
|
||||||
from lib.trade.trade_exchange_stats_lib import (
|
from lib.trade.trade_exchange_stats_lib import (
|
||||||
attach_exchange_stats_to_trade,
|
attach_exchange_stats_to_trade,
|
||||||
filter_position_lifecycle_fills,
|
filter_position_lifecycle_fills,
|
||||||
@@ -7119,27 +7123,20 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = filter_trade_records_excluding_miss(
|
||||||
total = len(records)
|
[to_effective_trade_dict(r) for r in raw_records]
|
||||||
miss_count = sum(1 for r in records if (r.get("effective_result") or "") == "错过")
|
|
||||||
win = count_winning_trades(records)
|
|
||||||
occupied_miss_total = sum(
|
|
||||||
1
|
|
||||||
for r in records
|
|
||||||
if (r.get("effective_result") or "") == "错过"
|
|
||||||
and ("持仓占用" in str(r.get("effective_miss_reason") or ""))
|
|
||||||
)
|
)
|
||||||
|
total = len(records)
|
||||||
|
win = count_winning_trades(records)
|
||||||
rate = round(win / total * 100, 2) if total else 0
|
rate = round(win / total * 100, 2) if total else 0
|
||||||
elif plan.records_summary:
|
elif plan.records_summary:
|
||||||
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
||||||
records = summary["records"]
|
records = summary["records"]
|
||||||
total = summary["total"]
|
total = summary["total"]
|
||||||
miss_count = summary["miss_count"]
|
|
||||||
rate = summary["rate"]
|
rate = summary["rate"]
|
||||||
occupied_miss_total = summary["occupied_miss_total"]
|
|
||||||
else:
|
else:
|
||||||
records = []
|
records = []
|
||||||
total = miss_count = rate = occupied_miss_total = 0
|
total = rate = 0
|
||||||
active_count = len(order_list)
|
active_count = len(order_list)
|
||||||
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
||||||
|
|
||||||
@@ -7196,7 +7193,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
orphan_live_positions=orphan_live_positions,
|
orphan_live_positions=orphan_live_positions,
|
||||||
record=records,
|
record=records,
|
||||||
total=total,
|
total=total,
|
||||||
miss_count=miss_count,
|
|
||||||
rate=rate,
|
rate=rate,
|
||||||
trading_day=trading_day,
|
trading_day=trading_day,
|
||||||
funding_usdt=funding_usdt,
|
funding_usdt=funding_usdt,
|
||||||
@@ -7239,7 +7235,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
),
|
),
|
||||||
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
||||||
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
||||||
occupied_miss_total=occupied_miss_total,
|
|
||||||
price_fmt=format_price_for_symbol,
|
price_fmt=format_price_for_symbol,
|
||||||
funds_fmt=format_funds_u,
|
funds_fmt=format_funds_u,
|
||||||
entry_reason_options=list(ENTRY_REASON_OPTIONS),
|
entry_reason_options=list(ENTRY_REASON_OPTIONS),
|
||||||
@@ -8358,27 +8353,6 @@ def add_order():
|
|||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
ok, reason = precheck_risk(conn, symbol, direction)
|
ok, reason = precheck_risk(conn, symbol, direction)
|
||||||
if not ok:
|
if not ok:
|
||||||
if "已达最大持仓数" in reason:
|
|
||||||
try:
|
|
||||||
tp_raw = parse_positive_float(d.get("tp"))
|
|
||||||
sl_raw = parse_positive_float(d.get("sl"))
|
|
||||||
tgt_raw = parse_positive_float(d.get("tgt"))
|
|
||||||
except Exception:
|
|
||||||
tp_raw = sl_raw = tgt_raw = None
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=symbol,
|
|
||||||
monitor_type="下单监控",
|
|
||||||
direction=direction if direction in ("long", "short") else "long",
|
|
||||||
trigger_price=tp_raw or 0,
|
|
||||||
stop_loss=sl_raw or 0,
|
|
||||||
take_profit=tgt_raw or 0,
|
|
||||||
result="错过",
|
|
||||||
miss_reason=f"持仓占用:{reason}",
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
conn.close()
|
||||||
flash(f"风控拒绝下单:{reason}")
|
flash(f"风控拒绝下单:{reason}")
|
||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
@@ -9091,30 +9065,6 @@ def del_order(id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@app.route("/add_miss", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def add_miss():
|
|
||||||
d = request.form
|
|
||||||
direction = d.get("direction", "long")
|
|
||||||
conn = get_db()
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=d["symbol"],
|
|
||||||
monitor_type=d["type"],
|
|
||||||
direction=direction,
|
|
||||||
trigger_price=d["tp"],
|
|
||||||
stop_loss=d["sl"],
|
|
||||||
take_profit=d["tgt"],
|
|
||||||
result="错过",
|
|
||||||
miss_reason=d["reason"],
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
flash("已记录错过机会")
|
|
||||||
return _redirect_records()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/add_journal", methods=["POST"])
|
@app.route("/add_journal", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -334,7 +334,6 @@
|
|||||||
<div class="stat-box instance-desktop-only">
|
<div class="stat-box instance-desktop-only">
|
||||||
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
||||||
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
||||||
<div class="stat-item"><div class="label">错过次数</div><div class="value">{{ miss_count }}</div></div>
|
|
||||||
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
||||||
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
||||||
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
||||||
@@ -565,7 +564,7 @@
|
|||||||
|
|
||||||
{% if page == 'records' %}
|
{% if page == 'records' %}
|
||||||
<div class="card full records-card">
|
<div class="card full records-card">
|
||||||
<h2>交易记录 & 错过机会</h2>
|
<h2>交易记录</h2>
|
||||||
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
||||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||||
<input id="review-mode-toggle" type="checkbox">
|
<input id="review-mode-toggle" type="checkbox">
|
||||||
@@ -598,7 +597,7 @@
|
|||||||
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
||||||
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
||||||
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
||||||
{% else %}<span class="badge miss">{{ effective_result }}</span>{% endif %}
|
{% else %}<span class="badge">{{ effective_result or '-' }}</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -645,27 +644,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card miss-card" style="opacity:.78">
|
<div class="card full journal-card">
|
||||||
<h2>记录错过机会</h2>
|
|
||||||
<form action="/add_miss" method="post" class="form-row">
|
|
||||||
<input name="symbol" placeholder="品种" required>
|
|
||||||
<select name="type" required>
|
|
||||||
<option value="箱体突破">箱体突破</option>
|
|
||||||
<option value="收敛突破">收敛突破</option>
|
|
||||||
<option value="关键支撑阻力">关键支撑阻力</option>
|
|
||||||
</select>
|
|
||||||
<select name="direction" required>
|
|
||||||
<option value="">方向</option><option value="long">做多</option><option value="short">做空</option>
|
|
||||||
</select>
|
|
||||||
<input name="tp" step="0.0001" placeholder="入场价" required>
|
|
||||||
<input name="sl" step="any" placeholder="止损" required>
|
|
||||||
<input name="tgt" step="any" placeholder="止盈" required>
|
|
||||||
<input name="reason" placeholder="错过原因" required>
|
|
||||||
<button type="submit">记录</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card journal-card">
|
|
||||||
<h2>交易复盘记录上传(含截图)</h2>
|
<h2>交易复盘记录上传(含截图)</h2>
|
||||||
<form id="journal-form" action="/add_journal" method="post" enctype="multipart/form-data">
|
<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="risk_amount_hint" id="risk-amount-hint">
|
||||||
@@ -797,9 +776,6 @@
|
|||||||
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content" id="stats-content">
|
<div class="stats-content" id="stats-content">
|
||||||
<div class="stat-box" style="margin-bottom:10px">
|
|
||||||
<div class="stat-item"><div class="label">持仓占用导致错过(累计)</div><div class="value">{{ occupied_miss_total }}</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
||||||
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
||||||
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
||||||
|
|||||||
+10
-78
@@ -220,7 +220,11 @@ from lib.common.history_window_lib import (
|
|||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
utc_window_to_utc_sql_strings,
|
utc_window_to_utc_sql_strings,
|
||||||
)
|
)
|
||||||
from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
|
from lib.trade.trade_result_lib import (
|
||||||
|
count_winning_trades,
|
||||||
|
filter_trade_records_excluding_miss,
|
||||||
|
normalize_result_with_pnl,
|
||||||
|
)
|
||||||
from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
|
from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
|
||||||
|
|
||||||
|
|
||||||
@@ -6899,27 +6903,20 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = filter_trade_records_excluding_miss(
|
||||||
total = len(records)
|
[to_effective_trade_dict(r) for r in raw_records]
|
||||||
miss_count = sum(1 for r in records if (r.get("effective_result") or "") == "错过")
|
|
||||||
win = count_winning_trades(records)
|
|
||||||
occupied_miss_total = sum(
|
|
||||||
1
|
|
||||||
for r in records
|
|
||||||
if (r.get("effective_result") or "") == "错过"
|
|
||||||
and ("持仓占用" in str(r.get("effective_miss_reason") or ""))
|
|
||||||
)
|
)
|
||||||
|
total = len(records)
|
||||||
|
win = count_winning_trades(records)
|
||||||
rate = round(win / total * 100, 2) if total else 0
|
rate = round(win / total * 100, 2) if total else 0
|
||||||
elif plan.records_summary:
|
elif plan.records_summary:
|
||||||
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
||||||
records = summary["records"]
|
records = summary["records"]
|
||||||
total = summary["total"]
|
total = summary["total"]
|
||||||
miss_count = summary["miss_count"]
|
|
||||||
rate = summary["rate"]
|
rate = summary["rate"]
|
||||||
occupied_miss_total = summary["occupied_miss_total"]
|
|
||||||
else:
|
else:
|
||||||
records = []
|
records = []
|
||||||
total = miss_count = rate = occupied_miss_total = 0
|
total = rate = 0
|
||||||
active_count = len(order_list)
|
active_count = len(order_list)
|
||||||
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
||||||
|
|
||||||
@@ -6972,7 +6969,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
order=order_list,
|
order=order_list,
|
||||||
record=records,
|
record=records,
|
||||||
total=total,
|
total=total,
|
||||||
miss_count=miss_count,
|
|
||||||
rate=rate,
|
rate=rate,
|
||||||
trading_day=trading_day,
|
trading_day=trading_day,
|
||||||
funding_usdt=funding_usdt,
|
funding_usdt=funding_usdt,
|
||||||
@@ -7016,7 +7012,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
),
|
),
|
||||||
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
||||||
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
||||||
occupied_miss_total=occupied_miss_total,
|
|
||||||
price_fmt=format_price_for_symbol,
|
price_fmt=format_price_for_symbol,
|
||||||
funds_fmt=format_usdt,
|
funds_fmt=format_usdt,
|
||||||
usdt_fmt=format_usdt,
|
usdt_fmt=format_usdt,
|
||||||
@@ -8192,32 +8187,6 @@ def add_order():
|
|||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
ok, reason = precheck_risk(conn, symbol, direction)
|
ok, reason = precheck_risk(conn, symbol, direction)
|
||||||
if not ok:
|
if not ok:
|
||||||
if "已达最大持仓数" in reason:
|
|
||||||
try:
|
|
||||||
tp_raw = parse_positive_float(d.get("tp"))
|
|
||||||
sl_raw = parse_positive_float(d.get("sl"))
|
|
||||||
tgt_raw = parse_positive_float(d.get("tgt"))
|
|
||||||
except Exception:
|
|
||||||
tp_raw = sl_raw = tgt_raw = None
|
|
||||||
ex_miss = normalize_exchange_symbol(symbol)
|
|
||||||
try:
|
|
||||||
ensure_markets_loaded()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=symbol,
|
|
||||||
monitor_type="下单监控",
|
|
||||||
direction=direction if direction in ("long", "short") else "long",
|
|
||||||
trigger_price=round_price_to_exchange(ex_miss, tp_raw) if tp_raw else 0,
|
|
||||||
stop_loss=round_price_to_exchange(ex_miss, sl_raw) if sl_raw else 0,
|
|
||||||
take_profit=round_price_to_exchange(ex_miss, tgt_raw) if tgt_raw else 0,
|
|
||||||
result="错过",
|
|
||||||
miss_reason=f"持仓占用:{reason}",
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
conn.close()
|
||||||
flash(f"风控拒绝下单:{reason}")
|
flash(f"风控拒绝下单:{reason}")
|
||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
@@ -8918,43 +8887,6 @@ def del_order(id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@app.route("/add_miss", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def add_miss():
|
|
||||||
d = request.form
|
|
||||||
direction = d.get("direction", "long")
|
|
||||||
sym_in = normalize_symbol_input(d.get("symbol"))
|
|
||||||
ex_sym = normalize_exchange_symbol(sym_in)
|
|
||||||
try:
|
|
||||||
ensure_markets_loaded()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
tp_px = round_price_to_exchange(ex_sym, float(d["tp"]))
|
|
||||||
sl_px = round_price_to_exchange(ex_sym, float(d["sl"]))
|
|
||||||
tgt_px = round_price_to_exchange(ex_sym, float(d["tgt"]))
|
|
||||||
except Exception:
|
|
||||||
flash("价格格式错误")
|
|
||||||
return _redirect_records()
|
|
||||||
conn = get_db()
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=sym_in,
|
|
||||||
monitor_type=d["type"],
|
|
||||||
direction=direction,
|
|
||||||
trigger_price=tp_px,
|
|
||||||
stop_loss=sl_px,
|
|
||||||
take_profit=tgt_px,
|
|
||||||
result="错过",
|
|
||||||
miss_reason=d["reason"],
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
flash("已记录错过机会")
|
|
||||||
return _redirect_records()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/add_journal", methods=["POST"])
|
@app.route("/add_journal", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -334,7 +334,6 @@
|
|||||||
<div class="stat-box instance-desktop-only">
|
<div class="stat-box instance-desktop-only">
|
||||||
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
||||||
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
||||||
<div class="stat-item"><div class="label">错过次数</div><div class="value">{{ miss_count }}</div></div>
|
|
||||||
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
||||||
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
||||||
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
||||||
@@ -532,7 +531,7 @@
|
|||||||
|
|
||||||
{% if page == 'records' %}
|
{% if page == 'records' %}
|
||||||
<div class="card full records-card">
|
<div class="card full records-card">
|
||||||
<h2>交易记录 & 错过机会</h2>
|
<h2>交易记录</h2>
|
||||||
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
||||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||||
<input id="review-mode-toggle" type="checkbox">
|
<input id="review-mode-toggle" type="checkbox">
|
||||||
@@ -565,7 +564,7 @@
|
|||||||
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
||||||
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
||||||
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
||||||
{% else %}<span class="badge miss">{{ effective_result }}</span>{% endif %}
|
{% else %}<span class="badge">{{ effective_result or '-' }}</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -612,27 +611,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card miss-card" style="opacity:.78">
|
<div class="card full journal-card">
|
||||||
<h2>记录错过机会</h2>
|
|
||||||
<form action="/add_miss" method="post" class="form-row">
|
|
||||||
<input name="symbol" placeholder="品种" required>
|
|
||||||
<select name="type" required>
|
|
||||||
<option value="箱体突破">箱体突破</option>
|
|
||||||
<option value="收敛突破">收敛突破</option>
|
|
||||||
<option value="关键支撑阻力">关键支撑阻力</option>
|
|
||||||
</select>
|
|
||||||
<select name="direction" required>
|
|
||||||
<option value="">方向</option><option value="long">做多</option><option value="short">做空</option>
|
|
||||||
</select>
|
|
||||||
<input name="tp" step="0.0001" placeholder="入场价" required>
|
|
||||||
<input name="sl" step="any" placeholder="止损" required>
|
|
||||||
<input name="tgt" step="any" placeholder="止盈" required>
|
|
||||||
<input name="reason" placeholder="错过原因" required>
|
|
||||||
<button type="submit">记录</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card journal-card">
|
|
||||||
<h2>交易复盘记录上传(含截图)</h2>
|
<h2>交易复盘记录上传(含截图)</h2>
|
||||||
<form id="journal-form" action="/add_journal" method="post" enctype="multipart/form-data">
|
<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="risk_amount_hint" id="risk-amount-hint">
|
||||||
@@ -764,9 +743,6 @@
|
|||||||
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content" id="stats-content">
|
<div class="stats-content" id="stats-content">
|
||||||
<div class="stat-box" style="margin-bottom:10px">
|
|
||||||
<div class="stat-item"><div class="label">持仓占用导致错过(累计)</div><div class="value">{{ occupied_miss_total }}</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
||||||
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
||||||
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
||||||
|
|||||||
+10
-60
@@ -219,7 +219,11 @@ from lib.common.history_window_lib import (
|
|||||||
utc_window_to_bj_sql_strings,
|
utc_window_to_bj_sql_strings,
|
||||||
utc_window_to_utc_sql_strings,
|
utc_window_to_utc_sql_strings,
|
||||||
)
|
)
|
||||||
from lib.trade.trade_result_lib import count_winning_trades, normalize_result_with_pnl
|
from lib.trade.trade_result_lib import (
|
||||||
|
count_winning_trades,
|
||||||
|
filter_trade_records_excluding_miss,
|
||||||
|
normalize_result_with_pnl,
|
||||||
|
)
|
||||||
from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
|
from lib.trade.trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
|
||||||
|
|
||||||
|
|
||||||
@@ -6458,27 +6462,20 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
f"SELECT * FROM trade_records WHERE {tr_ts} >= ? AND {tr_ts} <= ? ORDER BY id DESC LIMIT 1000",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
records = filter_trade_records_excluding_miss(
|
||||||
total = len(records)
|
[to_effective_trade_dict(r) for r in raw_records]
|
||||||
miss_count = sum(1 for r in records if (r.get("effective_result") or "") == "错过")
|
|
||||||
win = count_winning_trades(records)
|
|
||||||
occupied_miss_total = sum(
|
|
||||||
1
|
|
||||||
for r in records
|
|
||||||
if (r.get("effective_result") or "") == "错过"
|
|
||||||
and ("持仓占用" in str(r.get("effective_miss_reason") or ""))
|
|
||||||
)
|
)
|
||||||
|
total = len(records)
|
||||||
|
win = count_winning_trades(records)
|
||||||
rate = round(win / total * 100, 2) if total else 0
|
rate = round(win / total * 100, 2) if total else 0
|
||||||
elif plan.records_summary:
|
elif plan.records_summary:
|
||||||
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
summary = trade_records_summary(conn, start_bj, end_bj, tr_ts)
|
||||||
records = summary["records"]
|
records = summary["records"]
|
||||||
total = summary["total"]
|
total = summary["total"]
|
||||||
miss_count = summary["miss_count"]
|
|
||||||
rate = summary["rate"]
|
rate = summary["rate"]
|
||||||
occupied_miss_total = summary["occupied_miss_total"]
|
|
||||||
else:
|
else:
|
||||||
records = []
|
records = []
|
||||||
total = miss_count = rate = occupied_miss_total = 0
|
total = rate = 0
|
||||||
active_count = len(order_list)
|
active_count = len(order_list)
|
||||||
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
from lib.strategy.strategy_trade_labels import count_position_limit_active_monitors
|
||||||
|
|
||||||
@@ -6533,7 +6530,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
order=order_list,
|
order=order_list,
|
||||||
record=records,
|
record=records,
|
||||||
total=total,
|
total=total,
|
||||||
miss_count=miss_count,
|
|
||||||
rate=rate,
|
rate=rate,
|
||||||
trading_day=trading_day,
|
trading_day=trading_day,
|
||||||
daily_start_capital=DAILY_START_CAPITAL,
|
daily_start_capital=DAILY_START_CAPITAL,
|
||||||
@@ -6577,7 +6573,6 @@ def render_main_page(page="trade", embed_mode=None):
|
|||||||
),
|
),
|
||||||
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
||||||
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
breakeven_offset_pct=BREAKEVEN_OFFSET_PCT,
|
||||||
occupied_miss_total=occupied_miss_total,
|
|
||||||
price_fmt=format_price_for_symbol,
|
price_fmt=format_price_for_symbol,
|
||||||
entry_reason_options=list(ENTRY_REASON_OPTIONS),
|
entry_reason_options=list(ENTRY_REASON_OPTIONS),
|
||||||
entry_reason_other_value=ENTRY_REASON_OTHER,
|
entry_reason_other_value=ENTRY_REASON_OTHER,
|
||||||
@@ -7768,27 +7763,6 @@ def add_order():
|
|||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
ok, reason = precheck_risk(conn, symbol, direction)
|
ok, reason = precheck_risk(conn, symbol, direction)
|
||||||
if not ok:
|
if not ok:
|
||||||
if "已达最大持仓数" in reason or "一次只能持有一个仓位" in reason:
|
|
||||||
try:
|
|
||||||
tp_raw = parse_positive_float(d.get("tp"))
|
|
||||||
sl_raw = parse_positive_float(d.get("sl"))
|
|
||||||
tgt_raw = parse_positive_float(d.get("tgt"))
|
|
||||||
except Exception:
|
|
||||||
tp_raw = sl_raw = tgt_raw = None
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=symbol,
|
|
||||||
monitor_type="下单监控",
|
|
||||||
direction=direction if direction in ("long", "short") else "long",
|
|
||||||
trigger_price=tp_raw or 0,
|
|
||||||
stop_loss=sl_raw or 0,
|
|
||||||
take_profit=tgt_raw or 0,
|
|
||||||
result="错过",
|
|
||||||
miss_reason="持仓占用:一次只能持有一个仓位",
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
conn.close()
|
||||||
flash(f"风控拒绝下单:{reason}")
|
flash(f"风控拒绝下单:{reason}")
|
||||||
return redirect("/trade")
|
return redirect("/trade")
|
||||||
@@ -8462,30 +8436,6 @@ def del_order(id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@app.route("/add_miss", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def add_miss():
|
|
||||||
d = request.form
|
|
||||||
direction = d.get("direction", "long")
|
|
||||||
conn = get_db()
|
|
||||||
insert_trade_record(
|
|
||||||
conn,
|
|
||||||
symbol=d["symbol"],
|
|
||||||
monitor_type=d["type"],
|
|
||||||
direction=direction,
|
|
||||||
trigger_price=d["tp"],
|
|
||||||
stop_loss=d["sl"],
|
|
||||||
take_profit=d["tgt"],
|
|
||||||
result="错过",
|
|
||||||
miss_reason=d["reason"],
|
|
||||||
opened_at=app_now_str(),
|
|
||||||
closed_at=app_now_str(),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
flash("已记录错过机会")
|
|
||||||
return _redirect_records()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/add_journal", methods=["POST"])
|
@app.route("/add_journal", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -334,7 +334,6 @@
|
|||||||
<div class="stat-box instance-desktop-only">
|
<div class="stat-box instance-desktop-only">
|
||||||
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
||||||
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
<div class="stat-item"><div class="label">总交易</div><div class="value">{{ total }}</div></div>
|
||||||
<div class="stat-item"><div class="label">错过次数</div><div class="value">{{ miss_count }}</div></div>
|
|
||||||
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
<div class="stat-item"><div class="label">胜率</div><div class="value">{{ rate }}%</div></div>
|
||||||
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
||||||
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
||||||
@@ -561,7 +560,7 @@
|
|||||||
|
|
||||||
{% if page == 'records' %}
|
{% if page == 'records' %}
|
||||||
<div class="card full records-card">
|
<div class="card full records-card">
|
||||||
<h2>交易记录 & 错过机会</h2>
|
<h2>交易记录</h2>
|
||||||
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
||||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||||
<input id="review-mode-toggle" type="checkbox">
|
<input id="review-mode-toggle" type="checkbox">
|
||||||
@@ -594,7 +593,7 @@
|
|||||||
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
||||||
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
||||||
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
||||||
{% else %}<span class="badge miss">{{ effective_result }}</span>{% endif %}
|
{% else %}<span class="badge">{{ effective_result or '-' }}</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -641,27 +640,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card miss-card" style="opacity:.78">
|
<div class="card full journal-card">
|
||||||
<h2>记录错过机会</h2>
|
|
||||||
<form action="/add_miss" method="post" class="form-row">
|
|
||||||
<input name="symbol" placeholder="品种" required>
|
|
||||||
<select name="type" required>
|
|
||||||
<option value="箱体突破">箱体突破</option>
|
|
||||||
<option value="收敛突破">收敛突破</option>
|
|
||||||
<option value="关键支撑阻力">关键支撑阻力</option>
|
|
||||||
</select>
|
|
||||||
<select name="direction" required>
|
|
||||||
<option value="">方向</option><option value="long">做多</option><option value="short">做空</option>
|
|
||||||
</select>
|
|
||||||
<input name="tp" step="0.0001" placeholder="入场价" required>
|
|
||||||
<input name="sl" step="any" placeholder="止损" required>
|
|
||||||
<input name="tgt" step="any" placeholder="止盈" required>
|
|
||||||
<input name="reason" placeholder="错过原因" required>
|
|
||||||
<button type="submit">记录</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card journal-card">
|
|
||||||
<h2>交易复盘记录上传(含截图)</h2>
|
<h2>交易复盘记录上传(含截图)</h2>
|
||||||
<form id="journal-form" action="/add_journal" method="post" enctype="multipart/form-data">
|
<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="risk_amount_hint" id="risk-amount-hint">
|
||||||
@@ -793,9 +772,6 @@
|
|||||||
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content" id="stats-content">
|
<div class="stats-content" id="stats-content">
|
||||||
<div class="stat-box" style="margin-bottom:10px">
|
|
||||||
<div class="stat-item"><div class="label">持仓占用导致错过(累计)</div><div class="value">{{ occupied_miss_total }}</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
||||||
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
||||||
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
||||||
|
|||||||
@@ -58,25 +58,21 @@ def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[
|
|||||||
f"""
|
f"""
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS total,
|
COUNT(*) AS total,
|
||||||
SUM(CASE WHEN result = '错过' THEN 1 ELSE 0 END) AS miss_count,
|
SUM(CASE WHEN {pnl_sql} > 0 THEN 1 ELSE 0 END) AS wins
|
||||||
SUM(CASE WHEN {pnl_sql} > 0 THEN 1 ELSE 0 END) AS wins,
|
|
||||||
SUM(CASE WHEN result = '错过' AND COALESCE(miss_reason,'') LIKE '%持仓占用%' THEN 1 ELSE 0 END) AS occupied_miss
|
|
||||||
FROM trade_records
|
FROM trade_records
|
||||||
WHERE {tr_ts} >= ? AND {tr_ts} <= ?
|
WHERE {tr_ts} >= ? AND {tr_ts} <= ?
|
||||||
|
AND COALESCE(result, '') != '错过'
|
||||||
|
AND COALESCE(reviewed_result, '') != '错过'
|
||||||
""",
|
""",
|
||||||
(start_bj, end_bj),
|
(start_bj, end_bj),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
total = int(row["total"] or 0) if row else 0
|
total = int(row["total"] or 0) if row else 0
|
||||||
miss_count = int(row["miss_count"] or 0) if row else 0
|
|
||||||
wins = int(row["wins"] or 0) if row else 0
|
wins = int(row["wins"] or 0) if row else 0
|
||||||
occupied_miss_total = int(row["occupied_miss"] or 0) if row else 0
|
|
||||||
rate = round(wins / total * 100, 2) if total else 0
|
rate = round(wins / total * 100, 2) if total else 0
|
||||||
return {
|
return {
|
||||||
"records": [],
|
"records": [],
|
||||||
"total": total,
|
"total": total,
|
||||||
"miss_count": miss_count,
|
|
||||||
"rate": rate,
|
"rate": rate,
|
||||||
"occupied_miss_total": occupied_miss_total,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@
|
|||||||
|
|
||||||
{% if page == 'records' %}
|
{% if page == 'records' %}
|
||||||
<div class="card full records-card">
|
<div class="card full records-card">
|
||||||
<h2>交易记录 & 错过机会</h2>
|
<h2>交易记录</h2>
|
||||||
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
<div class="form-row" style="margin-bottom:10px;gap:8px">
|
||||||
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
<label style="display:flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef">
|
||||||
<input id="review-mode-toggle" type="checkbox">
|
<input id="review-mode-toggle" type="checkbox">
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
{% if effective_result in ["止盈","保本止盈","移动止盈"] %}<span class="badge profit">{{ effective_result }}</span>
|
||||||
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
{% elif effective_result in ["止损","强制清仓","手动平仓"] %}<span class="badge loss">{{ effective_result }}</span>
|
||||||
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
{% elif effective_result == "时间平仓" %}<span class="badge miss">{{ effective_result }}</span>
|
||||||
{% else %}<span class="badge miss">{{ effective_result }}</span>{% endif %}
|
{% else %}<span class="badge">{{ effective_result or '-' }}</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -288,27 +288,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card miss-card" style="opacity:.78">
|
<div class="card full journal-card">
|
||||||
<h2>记录错过机会</h2>
|
|
||||||
<form action="/add_miss" method="post" class="form-row">
|
|
||||||
<input name="symbol" placeholder="品种" required>
|
|
||||||
<select name="type" required>
|
|
||||||
<option value="箱体突破">箱体突破</option>
|
|
||||||
<option value="收敛突破">收敛突破</option>
|
|
||||||
<option value="关键支撑阻力">关键支撑阻力</option>
|
|
||||||
</select>
|
|
||||||
<select name="direction" required>
|
|
||||||
<option value="">方向</option><option value="long">做多</option><option value="short">做空</option>
|
|
||||||
</select>
|
|
||||||
<input name="tp" step="0.0001" placeholder="入场价" required>
|
|
||||||
<input name="sl" step="any" placeholder="止损" required>
|
|
||||||
<input name="tgt" step="any" placeholder="止盈" required>
|
|
||||||
<input name="reason" placeholder="错过原因" required>
|
|
||||||
<button type="submit">记录</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card journal-card">
|
|
||||||
<h2>交易复盘记录上传(含截图)</h2>
|
<h2>交易复盘记录上传(含截图)</h2>
|
||||||
<form id="journal-form" action="/add_journal" method="post" enctype="multipart/form-data">
|
<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="risk_amount_hint" id="risk-amount-hint">
|
||||||
@@ -439,9 +419,6 @@
|
|||||||
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
<button type="button" class="stats-toggle" id="stats-toggle-btn" onclick="toggleStatsCard()">折叠</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content" id="stats-content">
|
<div class="stats-content" id="stats-content">
|
||||||
<div class="stat-box" style="margin-bottom:10px">
|
|
||||||
<div class="stat-item"><div class="label">持仓占用导致错过(累计)</div><div class="value">{{ occupied_miss_total }}</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
<div class="sub" style="margin-bottom:12px;color:#8892b0;font-size:.82rem">
|
||||||
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
统计分析按<strong>北京时间 {{ stats_bundle.stats_reset_hour }}:00</strong>切日计入(与顶栏 UTC 列表窗无关)。历史总开仓(累计):
|
||||||
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
<strong style="color:#cfd3ef">{{ stats_bundle.total_opens_all }}</strong> 次
|
||||||
|
|||||||
@@ -83,7 +83,6 @@
|
|||||||
<div class="stat-box instance-desktop-only">
|
<div class="stat-box instance-desktop-only">
|
||||||
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
||||||
<div class="stat-item"><div class="label">总交易</div><div class="value" id="stat-total">{{ total }}</div></div>
|
<div class="stat-item"><div class="label">总交易</div><div class="value" id="stat-total">{{ total }}</div></div>
|
||||||
<div class="stat-item"><div class="label">错过次数</div><div class="value" id="stat-miss">{{ miss_count }}</div></div>
|
|
||||||
<div class="stat-item"><div class="label">胜率</div><div class="value" id="stat-rate">{{ rate }}%</div></div>
|
<div class="stat-item"><div class="label">胜率</div><div class="value" id="stat-rate">{{ rate }}%</div></div>
|
||||||
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
<div class="stat-item"><div class="label">资金账户(USDT)</div><div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div></div>
|
||||||
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
<div class="stat-item"><div class="label">交易日</div><div class="value">{{ trading_day }}</div></div>
|
||||||
|
|||||||
@@ -28,6 +28,22 @@ def count_winning_trades(trades) -> int:
|
|||||||
return sum(1 for r in trades or [] if is_winning_pnl(r.get("effective_pnl_amount")))
|
return sum(1 for r in trades or [] if is_winning_pnl(r.get("effective_pnl_amount")))
|
||||||
|
|
||||||
|
|
||||||
|
MISS_TRADE_RESULT = "错过"
|
||||||
|
|
||||||
|
|
||||||
|
def is_miss_trade_result(result) -> bool:
|
||||||
|
return (result or "").strip() == MISS_TRADE_RESULT
|
||||||
|
|
||||||
|
|
||||||
|
def filter_trade_records_excluding_miss(records):
|
||||||
|
"""列表/统计:不展示、不计入「错过」类交易记录。"""
|
||||||
|
return [
|
||||||
|
r
|
||||||
|
for r in (records or [])
|
||||||
|
if not is_miss_trade_result(r.get("effective_result") or r.get("result"))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def normalize_result_with_pnl(result, pnl_amount):
|
def normalize_result_with_pnl(result, pnl_amount):
|
||||||
"""
|
"""
|
||||||
非手动平仓且实际盈利时,不应记为「止损」。
|
非手动平仓且实际盈利时,不应记为「止损」。
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
"""trade_result_lib:过滤「错过」记录。"""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from lib.trade.trade_result_lib import (
|
||||||
|
filter_trade_records_excluding_miss,
|
||||||
|
is_miss_trade_result,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TradeResultMissFilterTest(unittest.TestCase):
|
||||||
|
def test_is_miss_trade_result(self):
|
||||||
|
self.assertTrue(is_miss_trade_result("错过"))
|
||||||
|
self.assertFalse(is_miss_trade_result("止盈"))
|
||||||
|
|
||||||
|
def test_filter_excludes_miss(self):
|
||||||
|
rows = [
|
||||||
|
{"effective_result": "止盈", "id": 1},
|
||||||
|
{"effective_result": "错过", "id": 2},
|
||||||
|
{"result": "错过", "id": 3},
|
||||||
|
]
|
||||||
|
out = filter_trade_records_excluding_miss(rows)
|
||||||
|
self.assertEqual([r["id"] for r in out], [1])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user