Split journal order type from entry reason and fix review edit toggle in embed shell.
Adds order_type to journal uploads, removes legacy swing/trend labels from entry reason options, and ensures review-edit buttons sync when the records tab loads dynamically. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,14 +67,16 @@ from lib.key_monitor.false_breakout_key_monitor_lib import (
|
||||
storage_bounds_from_key_price,
|
||||
)
|
||||
from lib.strategy.strategy_trade_labels import (
|
||||
STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
JOURNAL_ORDER_TYPE_OPTIONS,
|
||||
apply_order_monitor_source_labels,
|
||||
entry_reason_for_monitor_type,
|
||||
handoff_trade_miss_reason,
|
||||
normalize_journal_order_type,
|
||||
order_monitor_source_type,
|
||||
trade_record_monitor_type as resolve_trade_record_monitor_type,
|
||||
trend_plan_id_from_monitor_row,
|
||||
)
|
||||
from lib.instance.journal_form_lib import normalize_journal_entry_reason
|
||||
from lib.instance.journal_images_lib import (
|
||||
collect_journal_slot_images,
|
||||
enrich_journal_api_item,
|
||||
@@ -177,7 +179,7 @@ from lib.trade.position_sizing_lib import (
|
||||
from lib.trade.trade_policy_lib import load_trade_policy
|
||||
from lib.trade.entry_model_lib import (
|
||||
build_intraday_entry_reason_options,
|
||||
build_trend_div_entry_reason_options,
|
||||
build_journal_entry_reason_options,
|
||||
enrich_entry_model_display,
|
||||
hub_meta_entry_context,
|
||||
migrate_entry_model_columns,
|
||||
@@ -1123,8 +1125,8 @@ EARLY_EXIT_TRIGGERS = (
|
||||
"其他",
|
||||
)
|
||||
|
||||
# 趋势户:大分歧A/B/小分歧 + 策略(关键位本实例关闭)
|
||||
ENTRY_REASON_OPTIONS = build_trend_div_entry_reason_options(STRATEGY_ENTRY_REASON_OPTIONS)
|
||||
# 趋势户:复盘开仓类型仅 entry model;策略/风格项已拆至下单类型
|
||||
ENTRY_REASON_OPTIONS = build_journal_entry_reason_options()
|
||||
|
||||
STATS_SEGMENT_DEFS = (
|
||||
("all", "全部交易", {"segment": "all"}),
|
||||
@@ -1137,9 +1139,8 @@ STATS_SEGMENT_DEFS = (
|
||||
("key_trigger", "关键位触价开仓", {"segment": "key_trigger"}),
|
||||
)
|
||||
def normalize_entry_reason(raw, custom_text=None):
|
||||
from lib.trade.entry_model_lib import normalize_review_entry_reason
|
||||
|
||||
return normalize_review_entry_reason(raw, ENTRY_REASON_OPTIONS)
|
||||
del custom_text
|
||||
return normalize_journal_entry_reason(raw, ENTRY_REASON_OPTIONS, allow_legacy=True)
|
||||
|
||||
|
||||
def entry_reason_valid_for_storage(s):
|
||||
@@ -1429,6 +1430,9 @@ def init_db():
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN images_json TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN order_type TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE key_monitors ADD COLUMN direction TEXT DEFAULT 'long'")
|
||||
except: pass
|
||||
@@ -7326,6 +7330,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
trend_manual_count=trend_manual_entry_reason_count(TRADE_POLICY),
|
||||
)
|
||||
),
|
||||
order_type_options=list(JOURNAL_ORDER_TYPE_OPTIONS),
|
||||
key_auto_order_enabled=KEY_AUTO_ORDER_ENABLED,
|
||||
journal_chart_tf_choices=JOURNAL_CHART_TF_CHOICES,
|
||||
journal_chart_default_tf1=JOURNAL_CHART_DEFAULT_TF1,
|
||||
@@ -9256,7 +9261,13 @@ def del_order(id):
|
||||
@login_required
|
||||
def add_journal():
|
||||
d = request.form
|
||||
entry_reason_norm = normalize_entry_reason(d.get("entry_reason"))
|
||||
order_type_norm = normalize_journal_order_type(d.get("order_type"))
|
||||
if not order_type_norm:
|
||||
flash("请选择下单类型")
|
||||
return _redirect_records()
|
||||
entry_reason_norm = normalize_journal_entry_reason(
|
||||
d.get("entry_reason"), ENTRY_REASON_OPTIONS, allow_legacy=False
|
||||
)
|
||||
if not entry_reason_norm:
|
||||
flash("请选择开仓类型")
|
||||
return _redirect_records()
|
||||
@@ -9349,11 +9360,11 @@ def add_journal():
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
"""INSERT INTO journal_entries
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, entry_reason, exit_reason,
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, order_type, entry_reason, exit_reason,
|
||||
expect_rr, real_rr, early_exit, early_exit_reason, early_exit_trigger, early_exit_note,
|
||||
mood_score, mood_ai_score, mood_ai_comment, mood_issues, post_breakeven_stare,
|
||||
new_trade_while_occupied, note, image, images_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
entry_id,
|
||||
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||
@@ -9361,7 +9372,7 @@ def add_journal():
|
||||
hold_duration,
|
||||
d.get("coin"),
|
||||
d.get("tf"),
|
||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
d.get("pnl"), order_type_norm, entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||
None, None, None, mood_issues,
|
||||
d.get("post_breakeven_stare"), None, d.get("note"), image_filename,
|
||||
|
||||
@@ -68,14 +68,17 @@ from lib.key_monitor.false_breakout_key_monitor_lib import (
|
||||
storage_bounds_from_key_price,
|
||||
)
|
||||
from lib.strategy.strategy_trade_labels import (
|
||||
JOURNAL_ORDER_TYPE_OPTIONS,
|
||||
STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
apply_order_monitor_source_labels,
|
||||
entry_reason_for_monitor_type,
|
||||
handoff_trade_miss_reason,
|
||||
normalize_journal_order_type,
|
||||
order_monitor_source_type,
|
||||
trade_record_monitor_type as resolve_trade_record_monitor_type,
|
||||
trend_plan_id_from_monitor_row,
|
||||
)
|
||||
from lib.instance.journal_form_lib import normalize_journal_entry_reason
|
||||
from lib.instance.journal_images_lib import (
|
||||
collect_journal_slot_images,
|
||||
enrich_journal_api_item,
|
||||
@@ -1127,9 +1130,8 @@ STATS_SEGMENT_DEFS = (
|
||||
("key_trigger", "关键位触价开仓", {"segment": "key_trigger"}),
|
||||
)
|
||||
def normalize_entry_reason(raw, custom_text=None):
|
||||
from lib.trade.entry_model_lib import normalize_review_entry_reason
|
||||
|
||||
return normalize_review_entry_reason(raw, ENTRY_REASON_OPTIONS)
|
||||
del custom_text
|
||||
return normalize_journal_entry_reason(raw, ENTRY_REASON_OPTIONS, allow_legacy=True)
|
||||
|
||||
|
||||
def entry_reason_valid_for_storage(s):
|
||||
@@ -1424,6 +1426,9 @@ def init_db():
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN images_json TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN order_type TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE key_monitors ADD COLUMN direction TEXT DEFAULT 'long'")
|
||||
except: pass
|
||||
@@ -7122,6 +7127,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
trend_manual_count=trend_manual_entry_reason_count(TRADE_POLICY),
|
||||
)
|
||||
),
|
||||
order_type_options=list(JOURNAL_ORDER_TYPE_OPTIONS),
|
||||
key_auto_order_enabled=KEY_AUTO_ORDER_ENABLED,
|
||||
journal_chart_tf_choices=JOURNAL_CHART_TF_CHOICES,
|
||||
journal_chart_default_tf1=JOURNAL_CHART_DEFAULT_TF1,
|
||||
@@ -9122,7 +9128,13 @@ def del_order(id):
|
||||
@login_required
|
||||
def add_journal():
|
||||
d = request.form
|
||||
entry_reason_norm = normalize_entry_reason(d.get("entry_reason"))
|
||||
order_type_norm = normalize_journal_order_type(d.get("order_type"))
|
||||
if not order_type_norm:
|
||||
flash("请选择下单类型")
|
||||
return _redirect_records()
|
||||
entry_reason_norm = normalize_journal_entry_reason(
|
||||
d.get("entry_reason"), ENTRY_REASON_OPTIONS, allow_legacy=False
|
||||
)
|
||||
if not entry_reason_norm:
|
||||
flash("请选择开仓类型")
|
||||
return _redirect_records()
|
||||
@@ -9215,11 +9227,11 @@ def add_journal():
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
"""INSERT INTO journal_entries
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, entry_reason, exit_reason,
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, order_type, entry_reason, exit_reason,
|
||||
expect_rr, real_rr, early_exit, early_exit_reason, early_exit_trigger, early_exit_note,
|
||||
mood_score, mood_ai_score, mood_ai_comment, mood_issues, post_breakeven_stare,
|
||||
new_trade_while_occupied, note, image, images_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
entry_id,
|
||||
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||
@@ -9227,7 +9239,7 @@ def add_journal():
|
||||
hold_duration,
|
||||
d.get("coin"),
|
||||
d.get("tf"),
|
||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
d.get("pnl"), order_type_norm, entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||
None, None, None, mood_issues,
|
||||
d.get("post_breakeven_stare"), None, d.get("note"), image_filename,
|
||||
|
||||
+22
-11
@@ -67,14 +67,16 @@ from lib.key_monitor.false_breakout_key_monitor_lib import (
|
||||
storage_bounds_from_key_price,
|
||||
)
|
||||
from lib.strategy.strategy_trade_labels import (
|
||||
STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
JOURNAL_ORDER_TYPE_OPTIONS,
|
||||
apply_order_monitor_source_labels,
|
||||
entry_reason_for_monitor_type,
|
||||
handoff_trade_miss_reason,
|
||||
normalize_journal_order_type,
|
||||
order_monitor_source_type,
|
||||
trade_record_monitor_type as resolve_trade_record_monitor_type,
|
||||
trend_plan_id_from_monitor_row,
|
||||
)
|
||||
from lib.instance.journal_form_lib import normalize_journal_entry_reason
|
||||
from lib.exchange.okx_orders_lib import cancel_okx_all_open_orders, fetch_okx_all_open_orders
|
||||
from lib.instance.journal_images_lib import (
|
||||
collect_journal_slot_images,
|
||||
@@ -175,7 +177,7 @@ from lib.trade.position_sizing_lib import (
|
||||
from lib.trade.trade_policy_lib import load_trade_policy
|
||||
from lib.trade.entry_model_lib import (
|
||||
build_intraday_entry_reason_options,
|
||||
build_trend_div_entry_reason_options,
|
||||
build_journal_entry_reason_options,
|
||||
enrich_entry_model_display,
|
||||
hub_meta_entry_context,
|
||||
migrate_entry_model_columns,
|
||||
@@ -1134,8 +1136,8 @@ EARLY_EXIT_TRIGGERS = (
|
||||
"其他",
|
||||
)
|
||||
|
||||
# 趋势户:大分歧A/B/小分歧 + 策略(关键位本实例关闭)
|
||||
ENTRY_REASON_OPTIONS = build_trend_div_entry_reason_options(STRATEGY_ENTRY_REASON_OPTIONS)
|
||||
# 趋势户:复盘开仓类型仅 entry model;策略/风格项已拆至下单类型
|
||||
ENTRY_REASON_OPTIONS = build_journal_entry_reason_options()
|
||||
|
||||
STATS_SEGMENT_DEFS = (
|
||||
("all", "全部交易", {"segment": "all"}),
|
||||
@@ -1148,9 +1150,8 @@ STATS_SEGMENT_DEFS = (
|
||||
("key_trigger", "关键位触价开仓", {"segment": "key_trigger"}),
|
||||
)
|
||||
def normalize_entry_reason(raw, custom_text=None):
|
||||
from lib.trade.entry_model_lib import normalize_review_entry_reason
|
||||
|
||||
return normalize_review_entry_reason(raw, ENTRY_REASON_OPTIONS)
|
||||
del custom_text
|
||||
return normalize_journal_entry_reason(raw, ENTRY_REASON_OPTIONS, allow_legacy=True)
|
||||
|
||||
|
||||
def normalize_early_exit_trigger(raw):
|
||||
@@ -1430,6 +1431,9 @@ def init_db():
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN images_json TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE journal_entries ADD COLUMN order_type TEXT")
|
||||
except: pass
|
||||
try:
|
||||
c.execute("ALTER TABLE key_monitors ADD COLUMN direction TEXT DEFAULT 'long'")
|
||||
except: pass
|
||||
@@ -6722,6 +6726,7 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
trend_manual_count=trend_manual_entry_reason_count(TRADE_POLICY),
|
||||
)
|
||||
),
|
||||
order_type_options=list(JOURNAL_ORDER_TYPE_OPTIONS),
|
||||
key_auto_order_enabled=KEY_AUTO_ORDER_ENABLED,
|
||||
journal_chart_tf_choices=JOURNAL_CHART_TF_CHOICES,
|
||||
journal_chart_default_tf1=JOURNAL_CHART_DEFAULT_TF1,
|
||||
@@ -8776,7 +8781,13 @@ def del_order(id):
|
||||
@login_required
|
||||
def add_journal():
|
||||
d = request.form
|
||||
entry_reason_norm = normalize_entry_reason(d.get("entry_reason"))
|
||||
order_type_norm = normalize_journal_order_type(d.get("order_type"))
|
||||
if not order_type_norm:
|
||||
flash("请选择下单类型")
|
||||
return _redirect_records()
|
||||
entry_reason_norm = normalize_journal_entry_reason(
|
||||
d.get("entry_reason"), ENTRY_REASON_OPTIONS, allow_legacy=False
|
||||
)
|
||||
if not entry_reason_norm:
|
||||
flash("请选择开仓类型")
|
||||
return _redirect_records()
|
||||
@@ -8869,11 +8880,11 @@ def add_journal():
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
"""INSERT INTO journal_entries
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, entry_reason, exit_reason,
|
||||
(id, open_datetime, close_datetime, hold_duration, coin, tf, pnl, order_type, entry_reason, exit_reason,
|
||||
expect_rr, real_rr, early_exit, early_exit_reason, early_exit_trigger, early_exit_note,
|
||||
mood_score, mood_ai_score, mood_ai_comment, mood_issues, post_breakeven_stare,
|
||||
new_trade_while_occupied, note, image, images_json)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
entry_id,
|
||||
normalize_bj_datetime_storage(d.get("open_datetime")),
|
||||
@@ -8881,7 +8892,7 @@ def add_journal():
|
||||
hold_duration,
|
||||
d.get("coin"),
|
||||
d.get("tf"),
|
||||
d.get("pnl"), entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
d.get("pnl"), order_type_norm, entry_reason_norm, exit_reason_stored, d.get("expect_rr"), real_rr_text,
|
||||
early_exit_raw, early_exit_reason_saved, early_exit_trigger, early_exit_note,
|
||||
None, None, None, mood_issues,
|
||||
d.get("post_breakeven_stare"), None, d.get("note"), image_filename,
|
||||
|
||||
@@ -100,10 +100,14 @@
|
||||
if (!revisit && tab === "strategy" && typeof global.initStrategyRollForm === "function") {
|
||||
global.initStrategyRollForm();
|
||||
}
|
||||
if (!revisit && tab === "records") {
|
||||
if (typeof global.loadJournals === "function") global.loadJournals();
|
||||
if (typeof global.loadReviews === "function") global.loadReviews();
|
||||
if (typeof global.toggleReviewMode === "function") global.toggleReviewMode();
|
||||
if (tab === "records") {
|
||||
if (!revisit && typeof global.loadJournals === "function") global.loadJournals();
|
||||
if (!revisit && typeof global.loadReviews === "function") global.loadReviews();
|
||||
if (global.InstanceTheme && typeof global.InstanceTheme.initReviewEditModeSync === "function") {
|
||||
global.InstanceTheme.initReviewEditModeSync();
|
||||
} else if (typeof global.toggleReviewMode === "function") {
|
||||
global.toggleReviewMode();
|
||||
}
|
||||
}
|
||||
if (!revisit && tab === "stats") {
|
||||
if (typeof global.initStatsSegmentFromUrl === "function") global.initStatsSegmentFromUrl();
|
||||
|
||||
@@ -1999,6 +1999,7 @@ html[data-theme="light"] .symbol-live-price--ok {
|
||||
|
||||
.journal-card .journal-form-row2 {
|
||||
grid-template-columns:
|
||||
minmax(6.5rem, 0.85fr)
|
||||
minmax(7.5rem, 1.15fr)
|
||||
minmax(7rem, 1fr)
|
||||
minmax(0, 1.35fr)
|
||||
@@ -2006,6 +2007,7 @@ html[data-theme="light"] .symbol-live-price--ok {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.journal-card .journal-form-row2 select[name="order_type"],
|
||||
.journal-card .journal-form-row2 select[name="entry_reason"] {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.35;
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
apply(data.theme, { skipStore: true });
|
||||
}
|
||||
|
||||
/** 交易记录页:核对开关与按钮 disabled 保持同步(iframe 软导航/表单恢复后不触发 change) */
|
||||
/** 交易记录页:核对开关与按钮 disabled 保持同步(含 iframe 软导航后动态挂载的 toggle) */
|
||||
function syncReviewEditButtons() {
|
||||
const toggle = document.getElementById("review-mode-toggle");
|
||||
if (!toggle) return;
|
||||
@@ -301,13 +301,17 @@
|
||||
}
|
||||
|
||||
function initReviewEditModeSync() {
|
||||
const toggle = document.getElementById("review-mode-toggle");
|
||||
if (!toggle) return;
|
||||
if (toggle.dataset.instReviewModeBound !== "1") {
|
||||
toggle.dataset.instReviewModeBound = "1";
|
||||
toggle.addEventListener("input", () => {
|
||||
if (!global.__instReviewModeBound) {
|
||||
global.__instReviewModeBound = true;
|
||||
const onToggle = () => {
|
||||
if (typeof global.toggleReviewMode === "function") global.toggleReviewMode();
|
||||
else syncReviewEditButtons();
|
||||
};
|
||||
document.addEventListener("change", (ev) => {
|
||||
if (ev.target && ev.target.id === "review-mode-toggle") onToggle();
|
||||
});
|
||||
document.addEventListener("input", (ev) => {
|
||||
if (ev.target && ev.target.id === "review-mode-toggle") onToggle();
|
||||
});
|
||||
}
|
||||
const run = () => {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
`平仓时间:${escapeHtml(o.close_datetime || "-")}`,
|
||||
`持仓时长:${escapeHtml(o.hold_duration || "-")}`,
|
||||
`盈亏:${formatPnlSpan(o.pnl)}`,
|
||||
`下单类型:${escapeHtml(o.order_type || "无")}`,
|
||||
`开仓类型:${escapeHtml(o.entry_reason || "无")}`,
|
||||
`平仓/离场:${escapeHtml(exitText)}`,
|
||||
`预期RR:${escapeHtml(o.expect_rr || "-")}`,
|
||||
@@ -198,27 +199,28 @@
|
||||
|
||||
function parseTradeRecordRow(tr) {
|
||||
const cells = tr.querySelectorAll("td");
|
||||
if (cells.length < 14) return null;
|
||||
const dirBadge = cells[2].querySelector(".badge");
|
||||
if (cells.length < 15) return null;
|
||||
const dirBadge = cells[3].querySelector(".badge");
|
||||
return {
|
||||
rowId: tr.id,
|
||||
symbol: cells[0].textContent.trim(),
|
||||
type: cells[1].textContent.trim(),
|
||||
directionHtml: (dirBadge ? dirBadge.outerHTML : cells[2].innerHTML).trim(),
|
||||
directionText: cells[2].textContent.trim(),
|
||||
trigger: cells[3].textContent.trim(),
|
||||
stopLoss: cells[4].textContent.trim(),
|
||||
takeProfit: cells[5].textContent.trim(),
|
||||
margin: cells[6].textContent.trim(),
|
||||
leverage: cells[7].textContent.trim(),
|
||||
holdMinutes: cells[8].textContent.trim(),
|
||||
openedAt: cells[9].textContent.trim(),
|
||||
closedAt: cells[10].textContent.trim(),
|
||||
pnlHtml: cells[11].innerHTML.trim(),
|
||||
pnlText: cells[11].textContent.trim(),
|
||||
resultHtml: cells[12].innerHTML.trim(),
|
||||
resultText: cells[12].textContent.trim(),
|
||||
actionsHtml: cells[13].innerHTML,
|
||||
entryReason: cells[2].textContent.trim(),
|
||||
directionHtml: (dirBadge ? dirBadge.outerHTML : cells[3].innerHTML).trim(),
|
||||
directionText: cells[3].textContent.trim(),
|
||||
trigger: cells[4].textContent.trim(),
|
||||
stopLoss: cells[5].textContent.trim(),
|
||||
takeProfit: cells[6].textContent.trim(),
|
||||
margin: cells[7].textContent.trim(),
|
||||
leverage: cells[8].textContent.trim(),
|
||||
holdMinutes: cells[9].textContent.trim(),
|
||||
openedAt: cells[10].textContent.trim(),
|
||||
closedAt: cells[11].textContent.trim(),
|
||||
pnlHtml: cells[12].innerHTML.trim(),
|
||||
pnlText: cells[12].textContent.trim(),
|
||||
resultHtml: cells[13].innerHTML.trim(),
|
||||
resultText: cells[13].textContent.trim(),
|
||||
actionsHtml: cells[14].innerHTML,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,7 +242,8 @@
|
||||
function buildTradeRecordDetailHtml(row) {
|
||||
return `<div class="trade-record-detail">${
|
||||
tradeDetailRow("品种", escapeHtml(row.symbol)) +
|
||||
tradeDetailRow("类型", escapeHtml(row.type)) +
|
||||
tradeDetailRow("下单类型", escapeHtml(row.type)) +
|
||||
tradeDetailRow("开仓类型", escapeHtml(row.entryReason || "-")) +
|
||||
tradeDetailRow("方向", row.directionHtml) +
|
||||
tradeDetailRow("成交价", escapeHtml(row.trigger)) +
|
||||
tradeDetailRow("止损(开仓)", escapeHtml(row.stopLoss)) +
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""复盘表单:下单类型与开仓类型校验(三所共用)."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Sequence, Tuple
|
||||
|
||||
from lib.strategy.strategy_trade_labels import (
|
||||
JOURNAL_ORDER_TYPE_OPTIONS,
|
||||
STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
normalize_journal_order_type,
|
||||
)
|
||||
from lib.trade.entry_model_lib import (
|
||||
TRADE_STYLE_FALLBACK_ENTRY_REASONS,
|
||||
normalize_review_entry_reason,
|
||||
)
|
||||
|
||||
_LEGACY_JOURNAL_ENTRY_REASONS: Tuple[str, ...] = (
|
||||
*TRADE_STYLE_FALLBACK_ENTRY_REASONS,
|
||||
*STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
)
|
||||
|
||||
|
||||
def normalize_journal_entry_reason(
|
||||
raw: Optional[str],
|
||||
allowed: Sequence[str],
|
||||
*,
|
||||
allow_legacy: bool = False,
|
||||
) -> str:
|
||||
s = normalize_review_entry_reason(raw, allowed)
|
||||
if s:
|
||||
return s
|
||||
if not allow_legacy:
|
||||
return ""
|
||||
legacy = (raw or "").strip()
|
||||
if legacy in _LEGACY_JOURNAL_ENTRY_REASONS:
|
||||
return legacy
|
||||
return ""
|
||||
|
||||
|
||||
def journal_entry_reason_valid(raw: Optional[str], allowed: Sequence[str]) -> bool:
|
||||
return bool(normalize_journal_entry_reason(raw, allowed, allow_legacy=False))
|
||||
|
||||
|
||||
def journal_order_type_valid(raw: Optional[str]) -> bool:
|
||||
return bool(normalize_journal_order_type(raw))
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||
const JOURNAL_ORDER_TYPE_OPTIONS = {{ order_type_options | tojson }};
|
||||
|
||||
function reloadInstancePage(){
|
||||
if(document.body && document.body.getAttribute("data-embed-shell") === "1" && window.InstanceEmbed){
|
||||
@@ -12,6 +13,11 @@ function reloadInstancePage(){
|
||||
function validateJournalEntryReason(){
|
||||
const form = document.getElementById("journal-form");
|
||||
if(!form) return true;
|
||||
const orderSel = form.querySelector('[name="order_type"]');
|
||||
if(!orderSel || !orderSel.value){
|
||||
alert("请选择下单类型");
|
||||
return false;
|
||||
}
|
||||
const sel = form.querySelector('[name="entry_reason"]');
|
||||
if(!sel || !sel.value){
|
||||
alert("请选择开仓类型");
|
||||
@@ -147,6 +153,10 @@ function normalizeBeijingDatetimeString(v){
|
||||
}
|
||||
|
||||
function toggleReviewMode(){
|
||||
if(window.InstanceTheme && typeof InstanceTheme.syncReviewEditButtons === "function"){
|
||||
InstanceTheme.syncReviewEditButtons();
|
||||
return;
|
||||
}
|
||||
const on = !!(document.getElementById("review-mode-toggle") || {}).checked;
|
||||
document.querySelectorAll(".review-edit-btn").forEach(btn=>{
|
||||
btn.disabled = !on;
|
||||
@@ -537,11 +547,18 @@ function fillJournalFromTrade(t){
|
||||
setJournalField("early_exit_note", "");
|
||||
const kst = String(t.key_signal_type || "").trim();
|
||||
const mt = String(t.monitor_type || "").trim();
|
||||
if(mt === "趋势回调" && JOURNAL_ENTRY_REASON_OPTIONS.includes("趋势回调")){
|
||||
setJournalField("entry_reason", "趋势回调");
|
||||
} else if(mt === "顺势加仓" && JOURNAL_ENTRY_REASON_OPTIONS.includes("顺势加仓")){
|
||||
setJournalField("entry_reason", "顺势加仓");
|
||||
} else if(t.effective_entry_reason && JOURNAL_ENTRY_REASON_OPTIONS.includes(t.effective_entry_reason)){
|
||||
const orderType = (function(){
|
||||
if(mt === "趋势回调" && JOURNAL_ORDER_TYPE_OPTIONS.includes("趋势回调")) return "趋势回调";
|
||||
if(mt === "顺势加仓" && JOURNAL_ORDER_TYPE_OPTIONS.includes("顺势加仓")) return "顺势加仓";
|
||||
if(mt === "关键位监控" || mt.includes("关键位")) return "关键位监控";
|
||||
return "下单监控";
|
||||
})();
|
||||
if(JOURNAL_ORDER_TYPE_OPTIONS.includes(orderType)){
|
||||
setJournalField("order_type", orderType);
|
||||
} else {
|
||||
setJournalField("order_type", "");
|
||||
}
|
||||
if(t.effective_entry_reason && JOURNAL_ENTRY_REASON_OPTIONS.includes(t.effective_entry_reason)){
|
||||
setJournalField("entry_reason", t.effective_entry_reason);
|
||||
} else {
|
||||
const erFromKey = KEY_ENTRY_REASON_BY_SIGNAL[kst] || "";
|
||||
@@ -554,7 +571,7 @@ function fillJournalFromTrade(t){
|
||||
const er = String(t.result || "").trim();
|
||||
const exitTrigMap = { 止盈: "止盈", 保本止盈: "保本止盈", 移动止盈: "移动止盈", 时间平仓: "时间平仓", 强制清仓: "强制清仓", 手动平仓: "手动平仓", 止损: "止损" };
|
||||
if(exitTrigMap[er]) setJournalField("early_exit_trigger", exitTrigMap[er]);
|
||||
const note = `来自交易记录自动填充:${t.symbol || "-"} ${t.direction || "-"} | 入场:${entryPx || "-"} 止损:${slPx || "-"} 止盈:${tpPx || "-"} | 类型:${t.monitor_type || "-"}`;
|
||||
const note = `来自交易记录自动填充:${t.symbol || "-"} ${t.direction || "-"} | 入场:${entryPx || "-"} 止损:${slPx || "-"} 止盈:${tpPx || "-"} | 下单类型:${orderType || t.monitor_type || "-"}`;
|
||||
setJournalField("note", note);
|
||||
const form = document.getElementById("journal-form");
|
||||
if(form && typeof form.scrollIntoView === "function"){
|
||||
@@ -632,8 +649,12 @@ if(document.getElementById("review-list")) loadReviews();
|
||||
const reviewToggle = document.getElementById("review-mode-toggle");
|
||||
if(reviewToggle){
|
||||
reviewToggle.addEventListener("change", toggleReviewMode);
|
||||
reviewToggle.addEventListener("input", toggleReviewMode);
|
||||
toggleReviewMode();
|
||||
}
|
||||
if(window.InstanceTheme && typeof InstanceTheme.initReviewEditModeSync === "function"){
|
||||
InstanceTheme.initReviewEditModeSync();
|
||||
}
|
||||
const journalForm = document.getElementById("journal-form");
|
||||
if(journalForm){
|
||||
const pnlInput = journalForm.querySelector('[name="pnl"]');
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<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>
|
||||
<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>
|
||||
{% for r in record %}
|
||||
<tr id="trade-row-{{ r.id }}">
|
||||
{% set pnl_val = (r.pnl_amount or 0)|float %}
|
||||
@@ -309,7 +309,7 @@
|
||||
<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) }}
|
||||
{{ 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">
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<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=49"></script>
|
||||
<script src="/static/instance_theme.js?v=50"></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=4">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=75">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=76">
|
||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||
<meta name="theme-color" content="#0b0d14">
|
||||
<title>{{ exchange_display }} · 加密货币 | 交易监控复盘系统</title>
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=7"></script>
|
||||
<script src="/static/instance_ui.js?v=8"></script>
|
||||
<script src="/static/journal_upload_slots.js?v=3"></script>
|
||||
<script src="/static/instance_records_mobile.js?v=2"></script>
|
||||
<script src="/static/time_close_ui.js?v=3"></script>
|
||||
@@ -105,6 +105,6 @@ window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=12"></script>
|
||||
<script src="/static/instance_live.js?v=4"></script>
|
||||
<script src="/static/instance_embed.js?v=21"></script>
|
||||
<script src="/static/instance_embed.js?v=22"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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=49"></script>
|
||||
<script src="/static/instance_theme.js?v=50"></script>
|
||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||
@@ -17,7 +17,7 @@
|
||||
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
||||
<title>{{ exchange_display }} · 加密货币 | 交易监控复盘系统</title>
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=1">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=75">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=76">
|
||||
|
||||
</head>
|
||||
<body
|
||||
@@ -309,7 +309,7 @@
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<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>
|
||||
<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>
|
||||
{% for r in record %}
|
||||
<tr id="trade-row-{{ r.id }}">
|
||||
{% set pnl_val = (r.pnl_amount or 0)|float %}
|
||||
@@ -391,7 +391,7 @@
|
||||
<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) }}
|
||||
{{ 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">
|
||||
@@ -542,7 +542,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=7"></script>
|
||||
<script src="/static/instance_ui.js?v=8"></script>
|
||||
<script src="/static/journal_upload_slots.js?v=3"></script>
|
||||
<script src="/static/instance_records_mobile.js?v=2"></script>
|
||||
<script src="/static/time_close_ui.js?v=3"></script>
|
||||
@@ -559,10 +559,16 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
||||
<script src="/static/strategy_roll.js?v=6"></script>
|
||||
<script>
|
||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||
const JOURNAL_ORDER_TYPE_OPTIONS = {{ order_type_options | tojson }};
|
||||
|
||||
function validateJournalEntryReason(){
|
||||
const form = document.getElementById("journal-form");
|
||||
if(!form) return true;
|
||||
const orderSel = form.querySelector('[name="order_type"]');
|
||||
if(!orderSel || !orderSel.value){
|
||||
alert("请选择下单类型");
|
||||
return false;
|
||||
}
|
||||
const sel = form.querySelector('[name="entry_reason"]');
|
||||
if(!sel || !sel.value){
|
||||
alert("请选择开仓类型");
|
||||
@@ -698,6 +704,10 @@ function normalizeBeijingDatetimeString(v){
|
||||
}
|
||||
|
||||
function toggleReviewMode(){
|
||||
if(window.InstanceTheme && typeof InstanceTheme.syncReviewEditButtons === "function"){
|
||||
InstanceTheme.syncReviewEditButtons();
|
||||
return;
|
||||
}
|
||||
const on = !!(document.getElementById("review-mode-toggle") || {}).checked;
|
||||
document.querySelectorAll(".review-edit-btn").forEach(btn=>{
|
||||
btn.disabled = !on;
|
||||
@@ -1088,11 +1098,18 @@ function fillJournalFromTrade(t){
|
||||
setJournalField("early_exit_note", "");
|
||||
const kst = String(t.key_signal_type || "").trim();
|
||||
const mt = String(t.monitor_type || "").trim();
|
||||
if(mt === "趋势回调" && JOURNAL_ENTRY_REASON_OPTIONS.includes("趋势回调")){
|
||||
setJournalField("entry_reason", "趋势回调");
|
||||
} else if(mt === "顺势加仓" && JOURNAL_ENTRY_REASON_OPTIONS.includes("顺势加仓")){
|
||||
setJournalField("entry_reason", "顺势加仓");
|
||||
} else if(t.effective_entry_reason && JOURNAL_ENTRY_REASON_OPTIONS.includes(t.effective_entry_reason)){
|
||||
const orderType = (function(){
|
||||
if(mt === "趋势回调" && JOURNAL_ORDER_TYPE_OPTIONS.includes("趋势回调")) return "趋势回调";
|
||||
if(mt === "顺势加仓" && JOURNAL_ORDER_TYPE_OPTIONS.includes("顺势加仓")) return "顺势加仓";
|
||||
if(mt === "关键位监控" || mt.includes("关键位")) return "关键位监控";
|
||||
return "下单监控";
|
||||
})();
|
||||
if(JOURNAL_ORDER_TYPE_OPTIONS.includes(orderType)){
|
||||
setJournalField("order_type", orderType);
|
||||
} else {
|
||||
setJournalField("order_type", "");
|
||||
}
|
||||
if(t.effective_entry_reason && JOURNAL_ENTRY_REASON_OPTIONS.includes(t.effective_entry_reason)){
|
||||
setJournalField("entry_reason", t.effective_entry_reason);
|
||||
} else {
|
||||
const erFromKey = KEY_ENTRY_REASON_BY_SIGNAL[kst] || "";
|
||||
@@ -1105,7 +1122,7 @@ function fillJournalFromTrade(t){
|
||||
const er = String(t.result || "").trim();
|
||||
const exitTrigMap = { 止盈: "止盈", 保本止盈: "保本止盈", 移动止盈: "移动止盈", 时间平仓: "时间平仓", 强制清仓: "强制清仓", 手动平仓: "手动平仓", 止损: "止损" };
|
||||
if(exitTrigMap[er]) setJournalField("early_exit_trigger", exitTrigMap[er]);
|
||||
const note = `来自交易记录自动填充:${t.symbol || "-"} ${t.direction || "-"} | 入场:${entryPx || "-"} 止损:${slPx || "-"} 止盈:${tpPx || "-"} | 类型:${t.monitor_type || "-"}`;
|
||||
const note = `来自交易记录自动填充:${t.symbol || "-"} ${t.direction || "-"} | 入场:${entryPx || "-"} 止损:${slPx || "-"} 止盈:${tpPx || "-"} | 下单类型:${orderType || t.monitor_type || "-"}`;
|
||||
setJournalField("note", note);
|
||||
const form = document.getElementById("journal-form");
|
||||
if(form && typeof form.scrollIntoView === "function"){
|
||||
@@ -1170,8 +1187,12 @@ if(document.getElementById("review-list")) loadReviews();
|
||||
const reviewToggle = document.getElementById("review-mode-toggle");
|
||||
if(reviewToggle){
|
||||
reviewToggle.addEventListener("change", toggleReviewMode);
|
||||
reviewToggle.addEventListener("input", toggleReviewMode);
|
||||
toggleReviewMode();
|
||||
}
|
||||
if(window.InstanceTheme && typeof InstanceTheme.initReviewEditModeSync === "function"){
|
||||
InstanceTheme.initReviewEditModeSync();
|
||||
}
|
||||
const journalForm = document.getElementById("journal-form");
|
||||
if(journalForm){
|
||||
const pnlInput = journalForm.querySelector('[name="pnl"]');
|
||||
|
||||
@@ -5,15 +5,48 @@ from typing import Optional
|
||||
|
||||
MONITOR_TYPE_TREND_PULLBACK = "趋势回调"
|
||||
MONITOR_TYPE_ROLL = "顺势加仓"
|
||||
ORDER_TYPE_MANUAL = "下单监控"
|
||||
ORDER_TYPE_KEY = "关键位监控"
|
||||
|
||||
ENTRY_REASON_TREND_PULLBACK = "趋势回调"
|
||||
ENTRY_REASON_ROLL = "顺势加仓"
|
||||
|
||||
JOURNAL_ORDER_TYPE_OPTIONS = (
|
||||
ORDER_TYPE_MANUAL,
|
||||
ORDER_TYPE_KEY,
|
||||
MONITOR_TYPE_TREND_PULLBACK,
|
||||
MONITOR_TYPE_ROLL,
|
||||
)
|
||||
|
||||
STRATEGY_ENTRY_REASON_OPTIONS = (
|
||||
ENTRY_REASON_TREND_PULLBACK,
|
||||
ENTRY_REASON_ROLL,
|
||||
)
|
||||
|
||||
|
||||
def normalize_journal_order_type(raw: Optional[str]) -> str:
|
||||
s = (raw or "").strip()
|
||||
if s in JOURNAL_ORDER_TYPE_OPTIONS:
|
||||
return s
|
||||
if "关键位" in s:
|
||||
return ORDER_TYPE_KEY
|
||||
return ""
|
||||
|
||||
|
||||
def order_type_from_monitor_type(
|
||||
monitor_type: Optional[str],
|
||||
key_signal_type: Optional[str] = None,
|
||||
) -> str:
|
||||
del key_signal_type
|
||||
mt = (monitor_type or "").strip()
|
||||
if mt == MONITOR_TYPE_TREND_PULLBACK:
|
||||
return MONITOR_TYPE_TREND_PULLBACK
|
||||
if mt == MONITOR_TYPE_ROLL:
|
||||
return MONITOR_TYPE_ROLL
|
||||
if mt == ORDER_TYPE_KEY or "关键位" in mt:
|
||||
return ORDER_TYPE_KEY
|
||||
return ORDER_TYPE_MANUAL
|
||||
|
||||
# 趋势回调保本移交下单监控:order_monitors.key_signal_type / 平仓备注
|
||||
TREND_HANDOFF_KEY_SIGNAL = ENTRY_REASON_TREND_PULLBACK
|
||||
TREND_HANDOFF_TRADE_NOTE = "趋势回调计划"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{# 复盘表单:首行按字段宽度比例;开仓类型与离场触发同一行 #}
|
||||
{% macro journal_form_fields(entry_reason_options) -%}
|
||||
{# 复盘表单:首行按字段宽度比例;下单类型/开仓类型与离场触发同一行 #}
|
||||
{% 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>
|
||||
<input type="datetime-local" name="close_datetime" class="journal-field-datetime" required>
|
||||
@@ -10,7 +10,13 @@
|
||||
<input name="real_rr" class="journal-field-num" placeholder="实际RR">
|
||||
</div>
|
||||
<div class="form-grid journal-form-row2">
|
||||
<select name="entry_reason" id="journal-entry-reason" class="journal-field-entry-reason" required title="日内:假破/结构突破/回调触价/突破触价;趋势户:反转/顺势/波段或策略项">
|
||||
<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>
|
||||
|
||||
@@ -312,7 +312,12 @@ _INTRADAY_JOURNAL_KEY_ENTRY_REASONS: Tuple[str, ...] = (
|
||||
def trend_manual_entry_reason_count(policy: TradePolicy) -> int:
|
||||
if is_intraday_trading_profile(policy):
|
||||
return len(intraday_entry_reason_display_options())
|
||||
return len(trend_div_entry_reason_display_options()) + len(TRADE_STYLE_FALLBACK_ENTRY_REASONS)
|
||||
return len(trend_div_entry_reason_display_options())
|
||||
|
||||
|
||||
def build_journal_entry_reason_options() -> Tuple[str, ...]:
|
||||
"""复盘开仓类型:仅 entry model,不含 trade_style 兜底与策略下单类型."""
|
||||
return trend_div_entry_reason_display_options()
|
||||
|
||||
|
||||
def build_trend_div_entry_reason_options(
|
||||
|
||||
@@ -46,7 +46,7 @@ class TestEntryModelLib(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
self.assertFalse(is_intraday_trading_profile(policy))
|
||||
self.assertEqual(trend_manual_entry_reason_count(policy), 7)
|
||||
self.assertEqual(trend_manual_entry_reason_count(policy), 5)
|
||||
|
||||
def test_entry_model_maps_trade_style(self):
|
||||
self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_LAUNCH_A), "trend")
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""journal_form_lib / strategy_trade_labels 下单类型与开仓类型拆分."""
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from lib.instance.journal_form_lib import (
|
||||
journal_entry_reason_valid,
|
||||
normalize_journal_entry_reason,
|
||||
)
|
||||
from lib.strategy.strategy_trade_labels import (
|
||||
JOURNAL_ORDER_TYPE_OPTIONS,
|
||||
normalize_journal_order_type,
|
||||
order_type_from_monitor_type,
|
||||
)
|
||||
from lib.trade.entry_model_lib import build_journal_entry_reason_options
|
||||
|
||||
|
||||
class JournalFormLibTests(unittest.TestCase):
|
||||
def test_journal_entry_reason_excludes_legacy_style_and_strategy(self):
|
||||
opts = build_journal_entry_reason_options()
|
||||
self.assertIn("反转/启动A", opts)
|
||||
self.assertNotIn("趋势单", opts)
|
||||
self.assertNotIn("波段单", opts)
|
||||
self.assertNotIn("趋势回调", opts)
|
||||
self.assertNotIn("顺势加仓", opts)
|
||||
|
||||
def test_normalize_journal_entry_reason_rejects_legacy_for_new_submit(self):
|
||||
opts = build_journal_entry_reason_options()
|
||||
self.assertEqual(
|
||||
normalize_journal_entry_reason("趋势单", opts, allow_legacy=False),
|
||||
"",
|
||||
)
|
||||
self.assertEqual(
|
||||
normalize_journal_entry_reason("趋势回调", opts, allow_legacy=False),
|
||||
"",
|
||||
)
|
||||
|
||||
def test_normalize_journal_entry_reason_accepts_legacy_when_allowed(self):
|
||||
opts = build_journal_entry_reason_options()
|
||||
self.assertEqual(
|
||||
normalize_journal_entry_reason("趋势单", opts, allow_legacy=True),
|
||||
"趋势单",
|
||||
)
|
||||
|
||||
def test_order_type_from_monitor_type(self):
|
||||
self.assertEqual(order_type_from_monitor_type("下单监控"), "下单监控")
|
||||
self.assertEqual(order_type_from_monitor_type("关键位监控"), "关键位监控")
|
||||
self.assertEqual(order_type_from_monitor_type("趋势回调"), "趋势回调")
|
||||
self.assertEqual(order_type_from_monitor_type("顺势加仓"), "顺势加仓")
|
||||
|
||||
def test_normalize_journal_order_type(self):
|
||||
self.assertEqual(normalize_journal_order_type("顺势加仓"), "顺势加仓")
|
||||
self.assertEqual(normalize_journal_order_type(""), "")
|
||||
self.assertEqual(len(JOURNAL_ORDER_TYPE_OPTIONS), 4)
|
||||
|
||||
def test_journal_entry_reason_valid(self):
|
||||
opts = build_journal_entry_reason_options()
|
||||
self.assertTrue(journal_entry_reason_valid("顺势/大分歧A", opts))
|
||||
self.assertFalse(journal_entry_reason_valid("趋势单", opts))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user