修改交易记录问题
This commit is contained in:
+228
-64
@@ -36,13 +36,23 @@ if _REPO_ROOT not in sys.path:
|
||||
sys.path.insert(0, _REPO_ROOT)
|
||||
from fib_key_monitor_lib import (
|
||||
FIB_KEY_MONITOR_TYPES,
|
||||
KEY_ENTRY_REASON_BY_SIGNAL,
|
||||
calc_fib_plan,
|
||||
entry_reason_from_key_signal,
|
||||
fib_invalidate_by_mark,
|
||||
fib_ratio_from_type,
|
||||
is_fib_key_monitor_type,
|
||||
key_signal_type_for_trade_record,
|
||||
stored_key_signal_type,
|
||||
)
|
||||
from history_window_lib import (
|
||||
PRESET_CUSTOM,
|
||||
PRESET_UTC_LAST24H,
|
||||
PRESET_UTC_LAST7D,
|
||||
PRESET_UTC_TODAY,
|
||||
resolve_window,
|
||||
utc_window_to_bj_sql_strings,
|
||||
)
|
||||
|
||||
|
||||
def load_env_file(path):
|
||||
@@ -723,6 +733,41 @@ def _render_candles_subplot(rows, title, width, height, bg_rgb=(255, 255, 255),
|
||||
return img
|
||||
|
||||
|
||||
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 _fetch_ohlcv_ending_at(exchange_symbol, timeframe, limit, end_ts_ms):
|
||||
"""以 end_ts_ms 为终点向前取 K 线(无 end 则拉最近 limit 根)。"""
|
||||
lim = max(2, int(limit or ORDER_CHART_LIMIT))
|
||||
if not end_ts_ms:
|
||||
return exchange.fetch_ohlcv(exchange_symbol, timeframe=timeframe, limit=lim)
|
||||
period = _timeframe_period_ms(timeframe)
|
||||
since = int(end_ts_ms) - period * (lim + 5)
|
||||
ohlcv = exchange.fetch_ohlcv(exchange_symbol, timeframe=timeframe, since=max(0, since), limit=lim + 10)
|
||||
rows = _ohlcv_to_rows(ohlcv)
|
||||
filtered = [r for r in rows if int(r[0]) <= int(end_ts_ms)]
|
||||
if len(filtered) >= lim:
|
||||
return [[r[0], r[1], r[2], r[3], r[4]] for r in filtered[-lim:]]
|
||||
return ohlcv[-lim:] if ohlcv else []
|
||||
|
||||
|
||||
def generate_multi_timeframe_chart_png(
|
||||
exchange_symbol,
|
||||
title_prefix,
|
||||
@@ -751,9 +796,15 @@ def generate_multi_timeframe_chart_png(
|
||||
ensure_markets_loaded()
|
||||
panels = []
|
||||
cell_w, cell_h = 980, 520
|
||||
end_ts_ms = None
|
||||
if marker_payload:
|
||||
try:
|
||||
end_ts_ms = int(marker_payload.get("exit_ts_ms") or marker_payload.get("entry_ts_ms") or 0) or None
|
||||
except (TypeError, ValueError):
|
||||
end_ts_ms = None
|
||||
for tf in timeframes:
|
||||
try:
|
||||
ohlcv = exchange.fetch_ohlcv(exchange_symbol, timeframe=tf, limit=limit)
|
||||
ohlcv = _fetch_ohlcv_ending_at(exchange_symbol, tf, limit, end_ts_ms)
|
||||
except Exception:
|
||||
ohlcv = []
|
||||
rows = _ohlcv_to_rows(ohlcv)[-limit:]
|
||||
@@ -845,6 +896,7 @@ def journal_coin_from_symbol(symbol):
|
||||
|
||||
EARLY_EXIT_TRIGGERS = (
|
||||
"",
|
||||
"止盈",
|
||||
"保本止盈",
|
||||
"移动止盈",
|
||||
"手动平仓",
|
||||
@@ -852,13 +904,26 @@ EARLY_EXIT_TRIGGERS = (
|
||||
"其他",
|
||||
)
|
||||
|
||||
# 与用户约定的固定开仓类型(仅做这几类单子)
|
||||
# 与用户约定的固定开仓类型
|
||||
ENTRY_REASON_OPTIONS = (
|
||||
"趋势多头:4h大结构突破前进场,确认条件:三次探顶,5m收敛不创新低",
|
||||
"趋势空头:4h大结构突破前进场,确认条件:三次探底,5m收敛不创新高",
|
||||
"趋势多头:小分歧低吸入场(左侧),确认条件:二次探底",
|
||||
"趋势空头:小分歧高吸入场(左侧),确认条件:二次探顶",
|
||||
"波段单:5m顺势突破,确认条件:2根k线+成交量放大+4h同向+日成交量前20",
|
||||
"关键位箱体突破",
|
||||
"关键位收敛突破",
|
||||
"关键位斐波0.618",
|
||||
"关键位斐波0.786",
|
||||
)
|
||||
|
||||
STATS_SEGMENT_DEFS = (
|
||||
("all", "全部已平仓", {"segment": "all"}),
|
||||
("manual", "人工·下单监控", {"segment": "manual"}),
|
||||
("key_box", "关键位箱体突破", {"segment": "key_box"}),
|
||||
("key_conv", "关键位收敛突破", {"segment": "key_conv"}),
|
||||
("key_fib618", "关键位斐波0.618", {"segment": "key_fib618"}),
|
||||
("key_fib786", "关键位斐波0.786", {"segment": "key_fib786"}),
|
||||
)
|
||||
# 复盘表单「其他」选项的 value(非入库值;自定义文本走 entry_reason_custom)
|
||||
ENTRY_REASON_OTHER = "__OTHER__"
|
||||
@@ -1403,11 +1468,61 @@ def _count_opens_between(conn, start_td, end_td):
|
||||
).fetchone()[0]
|
||||
|
||||
|
||||
def _load_completed_live_pnls(conn):
|
||||
q = """SELECT pnl_amount, reviewed_pnl_amount, closed_at, reviewed_closed_at, created_at,
|
||||
result, reviewed_result
|
||||
def _list_window_from_request():
|
||||
return resolve_window(request.args, default_preset=PRESET_UTC_TODAY)
|
||||
|
||||
|
||||
def _pnl_row_matches_segment(row, segment_key):
|
||||
try:
|
||||
mt = (row["monitor_type"] or "").strip()
|
||||
kst = (row["key_signal_type"] or "").strip()
|
||||
except Exception:
|
||||
return False
|
||||
if segment_key == "all":
|
||||
return True
|
||||
if segment_key == "manual":
|
||||
return mt == ORDER_MONITOR_TYPE_MANUAL and not kst
|
||||
if segment_key == "key_box":
|
||||
return kst == "箱体突破"
|
||||
if segment_key == "key_conv":
|
||||
return kst == "收敛突破"
|
||||
if segment_key == "key_fib618":
|
||||
return kst == "斐波回调0.618"
|
||||
if segment_key == "key_fib786":
|
||||
return kst == "斐波回调0.786"
|
||||
return False
|
||||
|
||||
|
||||
def _count_opens_for_segment(conn, start_td, end_td, segment_key):
|
||||
if segment_key == "manual":
|
||||
return conn.execute(
|
||||
"SELECT COUNT(*) FROM order_monitors WHERE session_date >= ? AND session_date <= ? "
|
||||
"AND (monitor_type IS NULL OR monitor_type=? OR TRIM(monitor_type)='') "
|
||||
"AND (key_signal_type IS NULL OR TRIM(key_signal_type)='')",
|
||||
(start_td, end_td, ORDER_MONITOR_TYPE_MANUAL),
|
||||
).fetchone()[0]
|
||||
kst_map = {
|
||||
"key_box": "箱体突破",
|
||||
"key_conv": "收敛突破",
|
||||
"key_fib618": "斐波回调0.618",
|
||||
"key_fib786": "斐波回调0.786",
|
||||
}
|
||||
kst = kst_map.get(segment_key)
|
||||
if kst:
|
||||
return conn.execute(
|
||||
"SELECT COUNT(*) FROM order_monitors WHERE session_date >= ? AND session_date <= ? AND key_signal_type=?",
|
||||
(start_td, end_td, kst),
|
||||
).fetchone()[0]
|
||||
return conn.execute(
|
||||
"SELECT COUNT(*) FROM order_monitors WHERE session_date >= ? AND session_date <= ?",
|
||||
(start_td, end_td),
|
||||
).fetchone()[0]
|
||||
|
||||
|
||||
def _load_completed_trade_pnls(conn):
|
||||
q = """SELECT pnl_amount, reviewed_pnl_amount, closed_at, reviewed_closed_at, created_at, opened_at,
|
||||
result, reviewed_result, monitor_type, key_signal_type
|
||||
FROM trade_records
|
||||
WHERE monitor_type = '下单监控'
|
||||
ORDER BY COALESCE(closed_at, created_at, opened_at) ASC, id ASC"""
|
||||
rows = conn.execute(q).fetchall()
|
||||
out = []
|
||||
@@ -1421,7 +1536,7 @@ def _load_completed_live_pnls(conn):
|
||||
p = 0.0
|
||||
t = parse_dt_for_trading_day(r["reviewed_closed_at"]) or parse_dt_for_trading_day(r["closed_at"]) or parse_dt_for_trading_day(r["created_at"])
|
||||
td = get_trading_day(t) if t else None
|
||||
out.append((p, t, td))
|
||||
out.append((p, t, td, r))
|
||||
return out
|
||||
|
||||
|
||||
@@ -1490,34 +1605,41 @@ def _compute_period_metrics(trades):
|
||||
|
||||
|
||||
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
||||
"""日 / 周 / 月 统计:平仓按平仓时间所在交易日计入。"""
|
||||
"""日 / 周 / 月 统计:平仓按北京时间交易日(默认 8:00 切日)计入。"""
|
||||
now_dt = now_dt or app_now()
|
||||
pnls = _load_completed_live_pnls(conn)
|
||||
pnls = _load_completed_trade_pnls(conn)
|
||||
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
||||
w_start, w_end = _session_week_bounds(trading_day)
|
||||
m_start, m_end = _calendar_month_bounds(now_dt)
|
||||
|
||||
def in_week(tr):
|
||||
_p, _t, td = tr
|
||||
return td and w_start <= td <= w_end
|
||||
return tr[2] and w_start <= tr[2] <= w_end
|
||||
|
||||
def in_month(tr):
|
||||
_p, _t, td = tr
|
||||
return td and m_start <= td <= m_end
|
||||
return tr[2] and m_start <= tr[2] <= m_end
|
||||
|
||||
day_trades = [tr for tr in pnls if tr[2] == trading_day]
|
||||
week_trades = [tr for tr in pnls if in_week(tr)]
|
||||
month_trades = [tr for tr in pnls if in_month(tr)]
|
||||
def slice_metrics(seg_key):
|
||||
seg_rows = [tr for tr in pnls if _pnl_row_matches_segment(tr[3], seg_key)]
|
||||
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
||||
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
||||
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
||||
dm = _compute_period_metrics(day_tr)
|
||||
wm = _compute_period_metrics(week_tr)
|
||||
mm = _compute_period_metrics(month_tr)
|
||||
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
||||
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
||||
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
||||
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
||||
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
||||
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
||||
return dm, wm, mm
|
||||
|
||||
dm = _compute_period_metrics(day_trades)
|
||||
wm = _compute_period_metrics(week_trades)
|
||||
mm = _compute_period_metrics(month_trades)
|
||||
dm["opens_count"] = _count_opens_between(conn, trading_day, trading_day)
|
||||
wm["opens_count"] = _count_opens_between(conn, w_start, w_end)
|
||||
mm["opens_count"] = _count_opens_between(conn, m_start, m_end)
|
||||
dm["range_label"] = f"北京时间交易日 {trading_day}"
|
||||
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天窗口)"
|
||||
mm["range_label"] = f"{m_start} ~ {m_end}(北京时间自然月)"
|
||||
segments = []
|
||||
for seg_key, seg_title, _meta in STATS_SEGMENT_DEFS:
|
||||
dm, wm, mm = slice_metrics(seg_key)
|
||||
segments.append({"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm})
|
||||
|
||||
dm, wm, mm = slice_metrics("all")
|
||||
|
||||
return {
|
||||
"trading_day": trading_day,
|
||||
@@ -1525,6 +1647,8 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
||||
"day": dm,
|
||||
"week": wm,
|
||||
"month": mm,
|
||||
"segments": segments,
|
||||
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
||||
}
|
||||
|
||||
|
||||
@@ -1709,7 +1833,11 @@ def to_effective_trade_dict(row):
|
||||
base_stop = item.get("initial_stop_loss") if item.get("initial_stop_loss") not in (None, "") else item.get("stop_loss")
|
||||
item["effective_opened_at"] = get_effective_trade_field(row, "reviewed_opened_at", "opened_at", item.get("opened_at"))
|
||||
item["effective_closed_at"] = get_effective_trade_field(row, "reviewed_closed_at", "closed_at", item.get("closed_at"))
|
||||
item["effective_stop_loss"] = get_effective_trade_field(row, "reviewed_stop_loss", "stop_loss", base_stop)
|
||||
open_stop = item.get("initial_stop_loss")
|
||||
if open_stop in (None, ""):
|
||||
open_stop = base_stop
|
||||
item["display_open_stop_loss"] = open_stop
|
||||
item["effective_stop_loss"] = get_effective_trade_field(row, "reviewed_stop_loss", "stop_loss", open_stop)
|
||||
item["effective_take_profit"] = get_effective_trade_field(row, "reviewed_take_profit", "take_profit", item.get("take_profit"))
|
||||
item["effective_result"] = get_effective_trade_field(row, "reviewed_result", "result", item.get("result"))
|
||||
item["effective_miss_reason"] = get_effective_trade_field(row, "reviewed_miss_reason", "miss_reason", item.get("miss_reason"))
|
||||
@@ -1999,6 +2127,7 @@ def insert_trade_record(
|
||||
closed_at_ms=None,
|
||||
exchange_trade_id=None,
|
||||
key_signal_type=None,
|
||||
entry_reason=None,
|
||||
):
|
||||
hold_minutes = calc_hold_minutes(hold_seconds)
|
||||
open_ts = opened_at or app_now_str()
|
||||
@@ -2006,13 +2135,15 @@ def insert_trade_record(
|
||||
open_ts_ms = _to_ms_with_fallback(opened_at_ms, open_ts)
|
||||
close_ts_ms = _to_ms_with_fallback(closed_at_ms, close_ts)
|
||||
kst = key_signal_type_for_trade_record(key_signal_type, KEY_MONITOR_AUTO_TYPES)
|
||||
snap_sl = initial_stop_loss if initial_stop_loss not in (None, "") else stop_loss
|
||||
er = (entry_reason or "").strip() or entry_reason_from_key_signal(kst) or ""
|
||||
conn.execute(
|
||||
"INSERT INTO trade_records (symbol,monitor_type,key_signal_type,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,margin_capital,leverage,pnl_amount,hold_seconds,trade_style,risk_amount,planned_rr,actual_rr,hold_minutes,opened_at,opened_at_ms,closed_at,closed_at_ms,result,miss_reason,exchange_trade_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
"INSERT INTO trade_records (symbol,monitor_type,key_signal_type,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,margin_capital,leverage,pnl_amount,hold_seconds,trade_style,risk_amount,planned_rr,actual_rr,hold_minutes,opened_at,opened_at_ms,closed_at,closed_at_ms,result,miss_reason,exchange_trade_id,entry_reason) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
(
|
||||
symbol, monitor_type, kst, direction, trigger_price, stop_loss, initial_stop_loss, take_profit,
|
||||
symbol, monitor_type, kst, direction, trigger_price, snap_sl, snap_sl, take_profit,
|
||||
margin_capital, leverage, pnl_amount, hold_seconds,
|
||||
trade_style, risk_amount, planned_rr, actual_rr, hold_minutes,
|
||||
open_ts, open_ts_ms, close_ts, close_ts_ms, result, miss_reason, exchange_trade_id
|
||||
open_ts, open_ts_ms, close_ts, close_ts_ms, result, miss_reason, exchange_trade_id, er or None
|
||||
)
|
||||
)
|
||||
|
||||
@@ -5313,6 +5444,8 @@ def sync_trade_records_from_exchange(conn, force=False):
|
||||
def render_main_page(page="trade"):
|
||||
now = app_now()
|
||||
trading_day = get_trading_day(now)
|
||||
list_window = _list_window_from_request()
|
||||
start_bj, end_bj = utc_window_to_bj_sql_strings(list_window["start_utc"], list_window["end_utc"], APP_TZ)
|
||||
conn = get_db()
|
||||
session_row = ensure_session(conn, trading_day)
|
||||
local_current_capital = float(session_row["current_capital"])
|
||||
@@ -5322,7 +5455,10 @@ def render_main_page(page="trade"):
|
||||
current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2)
|
||||
recommended_capital = round(float(get_recommended_capital(current_capital)), 2)
|
||||
key_list = conn.execute("SELECT * FROM key_monitors").fetchall()
|
||||
key_history = conn.execute("SELECT * FROM key_monitor_history ORDER BY id DESC LIMIT 80").fetchall()
|
||||
key_history = conn.execute(
|
||||
"SELECT * FROM key_monitor_history WHERE closed_at >= ? AND closed_at <= ? ORDER BY id DESC LIMIT 500",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
stats_bundle = compute_stats_bundle(conn, trading_day, now)
|
||||
raw_order_list = conn.execute("SELECT * FROM order_monitors WHERE status='active'").fetchall()
|
||||
order_list = []
|
||||
@@ -5334,7 +5470,11 @@ def render_main_page(page="trade"):
|
||||
exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {}
|
||||
except Exception as e:
|
||||
exchange_pnl_sync = {"ok": False, "reason": str(e)}
|
||||
raw_records = conn.execute("SELECT * FROM trade_records ORDER BY id DESC").fetchall()
|
||||
raw_records = conn.execute(
|
||||
"SELECT * FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id DESC LIMIT 1000",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
records = [to_effective_trade_dict(r) for r in raw_records]
|
||||
total = len(records)
|
||||
miss_count = sum(1 for r in records if (r.get("effective_result") or "") == "错过")
|
||||
@@ -5386,7 +5526,14 @@ def render_main_page(page="trade"):
|
||||
can_trade=can_trade,
|
||||
focus_key_id=(key_list[0]["id"] if key_list else None),
|
||||
focus_order_id=(order_list[0]["id"] if order_list else None),
|
||||
data_export_version=2,
|
||||
data_export_version=3,
|
||||
list_window=list_window,
|
||||
list_window_presets={
|
||||
"utc_today": PRESET_UTC_TODAY,
|
||||
"utc_last24h": PRESET_UTC_LAST24H,
|
||||
"utc_last7d": PRESET_UTC_LAST7D,
|
||||
"custom": PRESET_CUSTOM,
|
||||
},
|
||||
key_alert_max_times=KEY_ALERT_MAX_TIMES,
|
||||
risk_percent=RISK_PERCENT,
|
||||
breakeven_rr_trigger=BREAKEVEN_RR_TRIGGER,
|
||||
@@ -6521,43 +6668,45 @@ def _md_response(filename, content):
|
||||
@app.route("/export/trade_records")
|
||||
@login_required
|
||||
def export_trade_records():
|
||||
win = _list_window_from_request()
|
||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||
conn = get_db()
|
||||
rows = conn.execute(
|
||||
"SELECT id,symbol,monitor_type,direction,trigger_price,stop_loss,take_profit,margin_capital,leverage,"
|
||||
"pnl_amount,hold_seconds,hold_minutes,opened_at,closed_at,result,miss_reason,"
|
||||
"entry_reason,reviewed_entry_reason,created_at FROM trade_records ORDER BY id ASC"
|
||||
"SELECT id,symbol,monitor_type,key_signal_type,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,"
|
||||
"margin_capital,leverage,pnl_amount,hold_seconds,hold_minutes,planned_rr,actual_rr,risk_amount,"
|
||||
"opened_at,closed_at,result,miss_reason,entry_reason,reviewed_entry_reason,"
|
||||
"exchange_realized_pnl,exchange_opened_at,exchange_closed_at,created_at "
|
||||
"FROM trade_records WHERE COALESCE(closed_at, created_at, opened_at) >= ? "
|
||||
"AND COALESCE(closed_at, created_at, opened_at) <= ? ORDER BY id ASC",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
head_base = [
|
||||
"id",
|
||||
"symbol",
|
||||
"monitor_type",
|
||||
"direction",
|
||||
"trigger_price",
|
||||
"stop_loss",
|
||||
"take_profit",
|
||||
"margin_capital",
|
||||
"leverage",
|
||||
"pnl_amount",
|
||||
"hold_seconds",
|
||||
"hold_minutes",
|
||||
"opened_at",
|
||||
"closed_at",
|
||||
"result",
|
||||
"miss_reason",
|
||||
"entry_reason",
|
||||
"reviewed_entry_reason",
|
||||
"created_at",
|
||||
head = [
|
||||
"id", "symbol", "monitor_type", "key_signal_type", "direction", "trigger_price",
|
||||
"stop_loss_open_snapshot", "initial_stop_loss", "take_profit", "margin_capital", "leverage",
|
||||
"pnl_amount", "hold_seconds", "hold_minutes", "planned_rr", "actual_rr", "risk_amount",
|
||||
"opened_at", "closed_at", "result", "miss_reason", "entry_reason", "reviewed_entry_reason",
|
||||
"exchange_realized_pnl", "exchange_opened_at", "exchange_closed_at", "created_at", "开仓类型",
|
||||
]
|
||||
head = head_base + ["开仓类型"]
|
||||
data = []
|
||||
for r in rows:
|
||||
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
|
||||
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_entry_reason"] else ""
|
||||
eff = er1 or er0
|
||||
data.append(tuple(r[h] for h in head_base) + (eff,))
|
||||
kst = (r["key_signal_type"] or "").strip() if "key_signal_type" in r.keys() else ""
|
||||
eff = er1 or er0 or entry_reason_from_key_signal(kst) or ""
|
||||
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
|
||||
data.append((
|
||||
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
||||
snap, r["initial_stop_loss"], r["take_profit"], r["margin_capital"], r["leverage"],
|
||||
r["pnl_amount"], r["hold_seconds"], r["hold_minutes"], r["planned_rr"], r["actual_rr"], r["risk_amount"],
|
||||
r["opened_at"], r["closed_at"], r["result"], r["miss_reason"], r["entry_reason"], r["reviewed_entry_reason"],
|
||||
r["exchange_realized_pnl"] if "exchange_realized_pnl" in r.keys() else None,
|
||||
r["exchange_opened_at"] if "exchange_opened_at" in r.keys() else None,
|
||||
r["exchange_closed_at"] if "exchange_closed_at" in r.keys() else None,
|
||||
r["created_at"], eff,
|
||||
))
|
||||
day = app_now().strftime("%Y%m%d")
|
||||
return _csv_response(f"trade_records_v2_{day}.csv", data, head)
|
||||
return _csv_response(f"trade_records_v3_{day}.csv", data, head)
|
||||
|
||||
|
||||
@app.route("/export/journal_entries")
|
||||
@@ -6629,10 +6778,13 @@ def export_key_monitors():
|
||||
@app.route("/export/key_monitor_history")
|
||||
@login_required
|
||||
def export_key_monitor_history():
|
||||
win = _list_window_from_request()
|
||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||
conn = get_db()
|
||||
rows = conn.execute(
|
||||
"SELECT id,symbol,monitor_type,direction,upper,lower,notification_count,last_alert_message,close_reason,closed_at "
|
||||
"FROM key_monitor_history ORDER BY id ASC"
|
||||
"FROM key_monitor_history WHERE closed_at >= ? AND closed_at <= ? ORDER BY id ASC",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
head = [
|
||||
@@ -6862,9 +7014,10 @@ def add_journal():
|
||||
symbol_guess = normalize_symbol_input(coin) or coin
|
||||
exchange_symbol = normalize_exchange_symbol(symbol_guess)
|
||||
title_prefix = f"{symbol_guess} journal {entry_id[:8]}"
|
||||
close_ms = _local_input_datetime_to_ms(d.get("close_datetime"))
|
||||
marker_payload = {
|
||||
"entry_ts_ms": _local_input_datetime_to_ms(d.get("open_datetime")),
|
||||
"exit_ts_ms": _local_input_datetime_to_ms(d.get("close_datetime")),
|
||||
"exit_ts_ms": close_ms,
|
||||
"entry_ts_ms": close_ms,
|
||||
"entry_price": d.get("entry_price_hint"),
|
||||
"exit_price": None,
|
||||
}
|
||||
@@ -6929,8 +7082,14 @@ def add_journal():
|
||||
@app.route("/api/journals")
|
||||
@login_required
|
||||
def api_journals():
|
||||
win = _list_window_from_request()
|
||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||
conn = get_db()
|
||||
rows = conn.execute("SELECT * FROM journal_entries ORDER BY created_at DESC").fetchall()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM journal_entries WHERE COALESCE(close_datetime, created_at, open_datetime) >= ? "
|
||||
"AND COALESCE(close_datetime, created_at, open_datetime) <= ? ORDER BY created_at DESC LIMIT 500",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
result = []
|
||||
for r in rows:
|
||||
@@ -6978,8 +7137,13 @@ def delete_journal(jid):
|
||||
@app.route("/api/reviews")
|
||||
@login_required
|
||||
def api_reviews():
|
||||
win = _list_window_from_request()
|
||||
start_bj, end_bj = utc_window_to_bj_sql_strings(win["start_utc"], win["end_utc"], APP_TZ)
|
||||
conn = get_db()
|
||||
rows = conn.execute("SELECT * FROM ai_reviews ORDER BY created_at DESC").fetchall()
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM ai_reviews WHERE created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT 200",
|
||||
(start_bj, end_bj),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return jsonify([row_to_dict(r) for r in rows])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user