fix: remove 0.7 ATR multiplier, store risk_distance in DB
- Removed 0.7*ATR multiplier from signal_engine and paper_monitor - risk_distance now stored per-trade in paper_trades table - paper_monitor/signal_engine read risk_distance from DB (no hardcoded values) - V5.1 TP/SL: 2.0/1.5/3.0 -> 1.4/1.05/2.1 (same actual distance) - V5.2 TP/SL: 3.0/2.0/4.5 -> 2.1/1.4/3.15 (same actual distance) - Fixed V5.2 pnl_r: all historical values corrected by *2/3 - API unrealized_pnl_r also reads from DB risk_distance
This commit is contained in:
parent
02a769f513
commit
72ea0ffd0e
@ -753,13 +753,12 @@ async def paper_positions(
|
|||||||
d["current_price"] = current_price
|
d["current_price"] = current_price
|
||||||
# 浮动盈亏(R)
|
# 浮动盈亏(R)
|
||||||
entry = r["entry_price"]
|
entry = r["entry_price"]
|
||||||
atr = r["atr_at_entry"] or 1
|
rd = r.get("risk_distance") or abs(entry - r["sl_price"]) or 1
|
||||||
risk_distance = 2.0 * 0.7 * atr
|
if rd > 0 and entry > 0:
|
||||||
if risk_distance > 0 and entry > 0:
|
|
||||||
if r["direction"] == "LONG":
|
if r["direction"] == "LONG":
|
||||||
d["unrealized_pnl_r"] = round((current_price - entry) / risk_distance, 2)
|
d["unrealized_pnl_r"] = round((current_price - entry) / rd, 2)
|
||||||
else:
|
else:
|
||||||
d["unrealized_pnl_r"] = round((entry - current_price) / risk_distance, 2)
|
d["unrealized_pnl_r"] = round((entry - current_price) / rd, 2)
|
||||||
# 浮动盈亏(USDT) — 假设1R = risk_per_trade
|
# 浮动盈亏(USDT) — 假设1R = risk_per_trade
|
||||||
d["unrealized_pnl_usdt"] = round(d["unrealized_pnl_r"] * 200, 2) # 2% of 10000
|
d["unrealized_pnl_usdt"] = round(d["unrealized_pnl_r"] * 200, 2) # 2% of 10000
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -49,20 +49,20 @@ def check_and_close(symbol_upper: str, price: float):
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id, direction, entry_price, tp1_price, tp2_price, sl_price, "
|
"SELECT id, direction, entry_price, tp1_price, tp2_price, sl_price, "
|
||||||
"tp1_hit, entry_ts, atr_at_entry "
|
"tp1_hit, entry_ts, atr_at_entry, risk_distance "
|
||||||
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit')",
|
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit')",
|
||||||
(symbol_upper,)
|
(symbol_upper,)
|
||||||
)
|
)
|
||||||
positions = cur.fetchall()
|
positions = cur.fetchall()
|
||||||
|
|
||||||
for pos in positions:
|
for pos in positions:
|
||||||
pid, direction, entry_price, tp1, tp2, sl, tp1_hit, entry_ts, atr_entry = pos
|
pid, direction, entry_price, tp1, tp2, sl, tp1_hit, entry_ts, atr_entry, rd_db = pos
|
||||||
closed = False
|
closed = False
|
||||||
new_status = None
|
new_status = None
|
||||||
pnl_r = 0.0
|
pnl_r = 0.0
|
||||||
|
|
||||||
# 统一计算risk_distance (1R基准距离)
|
# 从DB读risk_distance,fallback用|entry-sl|
|
||||||
risk_distance = 2.0 * 0.7 * atr_entry if atr_entry > 0 else 1
|
risk_distance = rd_db if rd_db and rd_db > 0 else abs(entry_price - sl)
|
||||||
|
|
||||||
# === 实盘模拟:TP/SL视为限价单,以挂单价成交(非市价) ===
|
# === 实盘模拟:TP/SL视为限价单,以挂单价成交(非市价) ===
|
||||||
if direction == "LONG":
|
if direction == "LONG":
|
||||||
@ -128,7 +128,6 @@ def check_and_close(symbol_upper: str, price: float):
|
|||||||
|
|
||||||
if closed:
|
if closed:
|
||||||
# 扣手续费
|
# 扣手续费
|
||||||
risk_distance = 2.0 * 0.7 * atr_entry if atr_entry > 0 else 1
|
|
||||||
fee_r = (2 * FEE_RATE * entry_price) / risk_distance if risk_distance > 0 else 0
|
fee_r = (2 * FEE_RATE * entry_price) / risk_distance if risk_distance > 0 else 0
|
||||||
pnl_r -= fee_r
|
pnl_r -= fee_r
|
||||||
|
|
||||||
|
|||||||
@ -736,26 +736,26 @@ def paper_open_trade(
|
|||||||
):
|
):
|
||||||
"""模拟开仓"""
|
"""模拟开仓"""
|
||||||
import json as _json3
|
import json as _json3
|
||||||
risk_atr = 0.7 * atr
|
if atr <= 0:
|
||||||
if risk_atr <= 0:
|
|
||||||
return
|
return
|
||||||
sl_multiplier = float((tp_sl or {}).get("sl_multiplier", 2.0))
|
sl_multiplier = float((tp_sl or {}).get("sl_multiplier", 2.0))
|
||||||
tp1_multiplier = float((tp_sl or {}).get("tp1_multiplier", 1.5))
|
tp1_multiplier = float((tp_sl or {}).get("tp1_multiplier", 1.5))
|
||||||
tp2_multiplier = float((tp_sl or {}).get("tp2_multiplier", 3.0))
|
tp2_multiplier = float((tp_sl or {}).get("tp2_multiplier", 3.0))
|
||||||
|
risk_distance = sl_multiplier * atr # 1R = SL距离 = sl_multiplier × ATR
|
||||||
if direction == "LONG":
|
if direction == "LONG":
|
||||||
sl = price - sl_multiplier * risk_atr
|
sl = price - sl_multiplier * atr
|
||||||
tp1 = price + tp1_multiplier * risk_atr
|
tp1 = price + tp1_multiplier * atr
|
||||||
tp2 = price + tp2_multiplier * risk_atr
|
tp2 = price + tp2_multiplier * atr
|
||||||
else:
|
else:
|
||||||
sl = price + sl_multiplier * risk_atr
|
sl = price + sl_multiplier * atr
|
||||||
tp1 = price - tp1_multiplier * risk_atr
|
tp1 = price - tp1_multiplier * atr
|
||||||
tp2 = price - tp2_multiplier * risk_atr
|
tp2 = price - tp2_multiplier * atr
|
||||||
|
|
||||||
with get_sync_conn() as conn:
|
with get_sync_conn() as conn:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"INSERT INTO paper_trades (symbol,direction,score,tier,entry_price,entry_ts,tp1_price,tp2_price,sl_price,atr_at_entry,score_factors,strategy) "
|
"INSERT INTO paper_trades (symbol,direction,score,tier,entry_price,entry_ts,tp1_price,tp2_price,sl_price,atr_at_entry,score_factors,strategy,risk_distance) "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
symbol,
|
symbol,
|
||||||
direction,
|
direction,
|
||||||
@ -769,6 +769,7 @@ def paper_open_trade(
|
|||||||
atr,
|
atr,
|
||||||
_json3.dumps(factors) if factors else None,
|
_json3.dumps(factors) if factors else None,
|
||||||
strategy,
|
strategy,
|
||||||
|
risk_distance,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@ -783,18 +784,18 @@ def paper_check_positions(symbol: str, current_price: float, now_ms: int):
|
|||||||
with get_sync_conn() as conn:
|
with get_sync_conn() as conn:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id, direction, entry_price, tp1_price, tp2_price, sl_price, tp1_hit, entry_ts, atr_at_entry "
|
"SELECT id, direction, entry_price, tp1_price, tp2_price, sl_price, tp1_hit, entry_ts, atr_at_entry, risk_distance "
|
||||||
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit') ORDER BY id",
|
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit') ORDER BY id",
|
||||||
(symbol,)
|
(symbol,)
|
||||||
)
|
)
|
||||||
positions = cur.fetchall()
|
positions = cur.fetchall()
|
||||||
|
|
||||||
for pos in positions:
|
for pos in positions:
|
||||||
pid, direction, entry_price, tp1, tp2, sl, tp1_hit, entry_ts, atr_entry = pos
|
pid, direction, entry_price, tp1, tp2, sl, tp1_hit, entry_ts, atr_entry, rd_db = pos
|
||||||
closed = False
|
closed = False
|
||||||
new_status = None
|
new_status = None
|
||||||
pnl_r = 0.0
|
pnl_r = 0.0
|
||||||
risk_distance = 2.0 * 0.7 * atr_entry if atr_entry > 0 else 1
|
risk_distance = rd_db if rd_db and rd_db > 0 else abs(entry_price - sl)
|
||||||
|
|
||||||
# === 实盘模拟:TP/SL视为限价单,以挂单价成交 ===
|
# === 实盘模拟:TP/SL视为限价单,以挂单价成交 ===
|
||||||
if direction == "LONG":
|
if direction == "LONG":
|
||||||
@ -859,7 +860,6 @@ def paper_check_positions(symbol: str, current_price: float, now_ms: int):
|
|||||||
|
|
||||||
if closed:
|
if closed:
|
||||||
# 扣除手续费(开仓+平仓各Taker 0.05%)
|
# 扣除手续费(开仓+平仓各Taker 0.05%)
|
||||||
risk_distance = 2.0 * 0.7 * atr_entry if atr_entry > 0 else 1
|
|
||||||
fee_r = (2 * PAPER_FEE_RATE * entry_price) / risk_distance if risk_distance > 0 else 0
|
fee_r = (2 * PAPER_FEE_RATE * entry_price) / risk_distance if risk_distance > 0 else 0
|
||||||
pnl_r -= fee_r
|
pnl_r -= fee_r
|
||||||
|
|
||||||
@ -910,20 +910,20 @@ def paper_close_by_signal(symbol: str, current_price: float, now_ms: int, strate
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
if strategy:
|
if strategy:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id, direction, entry_price, tp1_hit, atr_at_entry "
|
"SELECT id, direction, entry_price, tp1_hit, atr_at_entry, risk_distance "
|
||||||
"FROM paper_trades WHERE symbol=%s AND strategy=%s AND status IN ('active','tp1_hit')",
|
"FROM paper_trades WHERE symbol=%s AND strategy=%s AND status IN ('active','tp1_hit')",
|
||||||
(symbol, strategy),
|
(symbol, strategy),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT id, direction, entry_price, tp1_hit, atr_at_entry "
|
"SELECT id, direction, entry_price, tp1_hit, atr_at_entry, risk_distance "
|
||||||
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit')",
|
"FROM paper_trades WHERE symbol=%s AND status IN ('active','tp1_hit')",
|
||||||
(symbol,),
|
(symbol,),
|
||||||
)
|
)
|
||||||
positions = cur.fetchall()
|
positions = cur.fetchall()
|
||||||
for pos in positions:
|
for pos in positions:
|
||||||
pid, direction, entry_price, tp1_hit, atr_entry = pos
|
pid, direction, entry_price, tp1_hit, atr_entry, rd_db = pos
|
||||||
risk_distance = 2.0 * 0.7 * atr_entry if atr_entry > 0 else 1
|
risk_distance = rd_db if rd_db and rd_db > 0 else abs(entry_price * 0.01)
|
||||||
if direction == "LONG":
|
if direction == "LONG":
|
||||||
pnl_r = (current_price - entry_price) / risk_distance if risk_distance > 0 else 0
|
pnl_r = (current_price - entry_price) / risk_distance if risk_distance > 0 else 0
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -11,9 +11,9 @@
|
|||||||
},
|
},
|
||||||
"accel_bonus": 5,
|
"accel_bonus": 5,
|
||||||
"tp_sl": {
|
"tp_sl": {
|
||||||
"sl_multiplier": 2.0,
|
"sl_multiplier": 1.4,
|
||||||
"tp1_multiplier": 1.5,
|
"tp1_multiplier": 1.05,
|
||||||
"tp2_multiplier": 3.0
|
"tp2_multiplier": 2.1
|
||||||
},
|
},
|
||||||
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium"]
|
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,9 @@
|
|||||||
},
|
},
|
||||||
"accel_bonus": 0,
|
"accel_bonus": 0,
|
||||||
"tp_sl": {
|
"tp_sl": {
|
||||||
"sl_multiplier": 3.0,
|
"sl_multiplier": 2.1,
|
||||||
"tp1_multiplier": 2.0,
|
"tp1_multiplier": 1.4,
|
||||||
"tp2_multiplier": 4.5
|
"tp2_multiplier": 3.15
|
||||||
},
|
},
|
||||||
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium", "funding_rate", "liquidation"]
|
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium", "funding_rate", "liquidation"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user