This commit is contained in:
dekun
2026-05-27 15:32:46 +08:00
parent 7bb3f942ed
commit 6093bf6b94
9 changed files with 269 additions and 24 deletions
+117
View File
@@ -15,6 +15,9 @@ JOURNAL_CHART_DEFAULT_TF2 = "1h"
JOURNAL_CHART_DEFAULT_LIMIT = 300
JOURNAL_CHART_LIMIT_MIN = 50
JOURNAL_CHART_LIMIT_MAX = 500
JOURNAL_CHART_ANCHOR_CLOSE = "close"
JOURNAL_CHART_ANCHOR_NOW = "now"
JOURNAL_CHART_DEFAULT_ANCHOR = JOURNAL_CHART_ANCHOR_CLOSE
def _load_font(size):
@@ -90,6 +93,13 @@ def parse_positive_price(raw):
return None
def parse_journal_chart_anchor(raw):
s = str(raw or "").strip().lower()
if s in (JOURNAL_CHART_ANCHOR_NOW, "current", "当前", "当前时间"):
return JOURNAL_CHART_ANCHOR_NOW
return JOURNAL_CHART_ANCHOR_CLOSE
def parse_journal_chart_limit(raw, fallback=None):
fb = int(fallback if fallback is not None else JOURNAL_CHART_DEFAULT_LIMIT)
try:
@@ -106,6 +116,113 @@ def normalize_chart_timeframe(raw):
return ""
def timeframe_period_ms(tf):
s = (tf or "").strip().lower()
if s.endswith("m"):
try:
return int(s[:-1]) * 60 * 1000
except ValueError:
pass
if s.endswith("h"):
try:
return int(s[:-1]) * 3600 * 1000
except ValueError:
pass
if s.endswith("d"):
try:
return int(s[:-1]) * 86400 * 1000
except ValueError:
pass
return 300000
def _to_int_ms(value):
if value is None:
return None
try:
v = int(value)
return v if v > 0 else None
except (TypeError, ValueError):
return None
def trade_review_fetch_window(entry_ts_ms, exit_ts_ms, timeframe, limit, anchor=None, now_ms=None):
"""
复盘 K 线窗口(anchor=close):
- 有开/平仓:从开仓前若干根起,到平仓 K 线止(覆盖整笔交易 + 入场前背景)
- 仅开仓:以开仓时间为终点向前 limit 根
- 仅平仓:以平仓时间为终点向前 limit 根
anchor=now:以当前时间为终点向前 limit 根(可看平仓后走势)
"""
period = timeframe_period_ms(timeframe)
lim = max(2, int(limit))
entry_ms = _to_int_ms(entry_ts_ms)
exit_ms = _to_int_ms(exit_ts_ms)
anch = (anchor or JOURNAL_CHART_DEFAULT_ANCHOR).strip().lower()
if anch == JOURNAL_CHART_ANCHOR_NOW:
end_ms = _to_int_ms(now_ms)
if not end_ms:
return None
since_ms = end_ms - period * (lim + 10)
return {
"since_ms": since_ms,
"end_ms": end_ms,
"window_start_ms": since_ms,
"fetch_limit": lim + 20,
"display_limit": lim,
}
if entry_ms and exit_ms:
if exit_ms < entry_ms:
entry_ms, exit_ms = exit_ms, entry_ms
span_bars = max(1, (exit_ms - entry_ms) // period + 1)
pre_bars = max(40, min(120, lim // 3))
need = span_bars + pre_bars
fetch_limit = min(JOURNAL_CHART_LIMIT_MAX, max(lim, need + 15))
since_ms = entry_ms - period * pre_bars
return {
"since_ms": since_ms,
"end_ms": exit_ms,
"window_start_ms": since_ms,
"fetch_limit": fetch_limit,
"display_limit": lim,
}
if entry_ms:
end_ms = entry_ms
since_ms = end_ms - period * (lim + 10)
return {
"since_ms": since_ms,
"end_ms": end_ms,
"window_start_ms": since_ms,
"fetch_limit": lim + 20,
"display_limit": lim,
}
if exit_ms:
end_ms = exit_ms
since_ms = end_ms - period * (lim + 10)
return {
"since_ms": since_ms,
"end_ms": end_ms,
"window_start_ms": since_ms,
"fetch_limit": lim + 20,
"display_limit": lim,
}
return None
def trim_rows_for_trade_review(rows, window):
if not window:
return list(rows or [])
start_ms = int(window["window_start_ms"])
end_ms = int(window["end_ms"])
lim = int(window["display_limit"])
filt = [r for r in (rows or []) if start_ms <= int(r["ts"]) <= end_ms]
if len(filt) > lim:
filt = filt[-lim:]
return filt
def parse_journal_chart_timeframes(tf1, tf2, fallback_tfs=None):
"""复盘表单:最多两个周期,去重保序。"""
out = []