feat: 内照明心交易日历与交易所口径成交额/手续费统计
新增按 08:00 切日的月历(盈亏、笔数、犯病日高亮与点击筛选);平仓时从交易所 fill 写入双边成交额与手续费,统计表与明细同步展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -201,6 +201,7 @@ from history_window_lib import (
|
||||
utc_window_to_utc_sql_strings,
|
||||
)
|
||||
from trade_result_lib import count_winning_trades, normalize_result_with_pnl
|
||||
from trade_exchange_stats_lib import attach_exchange_stats_to_trade, filter_position_lifecycle_fills
|
||||
|
||||
|
||||
def load_env_file(path):
|
||||
@@ -1411,6 +1412,8 @@ def init_db():
|
||||
"ALTER TABLE trade_records ADD COLUMN exchange_opened_at TEXT",
|
||||
"ALTER TABLE trade_records ADD COLUMN exchange_closed_at TEXT",
|
||||
"ALTER TABLE trade_records ADD COLUMN exchange_sync_key TEXT",
|
||||
"ALTER TABLE trade_records ADD COLUMN exchange_turnover_usdt REAL",
|
||||
"ALTER TABLE trade_records ADD COLUMN exchange_commission_usdt REAL",
|
||||
):
|
||||
try:
|
||||
c.execute(ddl)
|
||||
@@ -2254,6 +2257,8 @@ def insert_trade_record(
|
||||
key_signal_type=None,
|
||||
entry_reason=None,
|
||||
trend_plan_id=None,
|
||||
exchange_symbol=None,
|
||||
attach_exchange_stats=True,
|
||||
):
|
||||
hold_minutes = calc_hold_minutes(hold_seconds)
|
||||
open_ts = opened_at or app_now_str()
|
||||
@@ -2270,7 +2275,7 @@ def insert_trade_record(
|
||||
or entry_reason_for_monitor_type(monitor_type)
|
||||
or ""
|
||||
)
|
||||
conn.execute(
|
||||
cur = 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,entry_reason,trend_plan_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
(
|
||||
symbol, monitor_type, kst, direction, trigger_price, snap_sl, snap_sl, take_profit,
|
||||
@@ -2280,6 +2285,20 @@ def insert_trade_record(
|
||||
trend_plan_id,
|
||||
)
|
||||
)
|
||||
tid = int(cur.lastrowid or 0)
|
||||
if attach_exchange_stats and tid:
|
||||
ex_sym = (exchange_symbol or "").strip() or normalize_exchange_symbol(symbol)
|
||||
_attach_okx_trade_exchange_stats(
|
||||
conn,
|
||||
tid,
|
||||
exchange_symbol=ex_sym,
|
||||
direction=direction,
|
||||
opened_at_str=open_ts,
|
||||
closed_at_str=close_ts,
|
||||
opened_at_ms=open_ts_ms,
|
||||
closed_at_ms=close_ts_ms,
|
||||
)
|
||||
return tid
|
||||
|
||||
|
||||
def calc_duration_text(open_str, close_str):
|
||||
@@ -3392,6 +3411,55 @@ def fetch_closing_fills_for_record(exchange_symbol, direction, opened_at_str, cl
|
||||
return all_side_candidates[-20:]
|
||||
|
||||
|
||||
def fetch_all_position_fills_for_record(
|
||||
exchange_symbol, direction, opened_at_str, closed_at_str=None, opened_at_ms=None, closed_at_ms=None
|
||||
):
|
||||
if not (OKX_API_KEY and OKX_API_SECRET and OKX_API_PASSPHRASE):
|
||||
return []
|
||||
ensure_markets_loaded()
|
||||
since_ms = _to_ms_with_fallback(opened_at_ms, opened_at_str)
|
||||
closed_ms = _to_ms_with_fallback(closed_at_ms, closed_at_str) if (closed_at_str or closed_at_ms is not None) else None
|
||||
if closed_ms is not None:
|
||||
closed_ms += 6 * 60 * 60 * 1000
|
||||
try:
|
||||
trades = exchange.fetch_my_trades(exchange_symbol, since=since_ms, limit=200)
|
||||
except Exception:
|
||||
trades = []
|
||||
return filter_position_lifecycle_fills(
|
||||
trades or [],
|
||||
direction,
|
||||
since_ms,
|
||||
closed_ms,
|
||||
hedge_mode=(OKX_POS_MODE == "hedge"),
|
||||
close_buffer_ms=0,
|
||||
)
|
||||
|
||||
|
||||
def _attach_okx_trade_exchange_stats(
|
||||
conn, trade_id, *, exchange_symbol, direction, opened_at_str, closed_at_str, opened_at_ms=None, closed_at_ms=None
|
||||
):
|
||||
if not (OKX_API_KEY and OKX_API_SECRET and OKX_API_PASSPHRASE):
|
||||
return
|
||||
open_ms = _to_ms_with_fallback(opened_at_ms, opened_at_str)
|
||||
close_ms = _to_ms_with_fallback(closed_at_ms, closed_at_str)
|
||||
contract_size = 1.0
|
||||
try:
|
||||
ensure_markets_loaded()
|
||||
contract_size = float(exchange.market(exchange_symbol).get("contractSize") or 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _fetch():
|
||||
return fetch_all_position_fills_for_record(
|
||||
exchange_symbol, direction, opened_at_str, closed_at_str, opened_at_ms=open_ms, closed_at_ms=close_ms
|
||||
)
|
||||
|
||||
try:
|
||||
attach_exchange_stats_to_trade(conn, trade_id, fetch_fills=_fetch, contract_size=contract_size)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def calc_weighted_exit_price(trades):
|
||||
if not trades:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user