refactor: A1 hot-reload strategy configs, A4 remove dead paper_check_positions, A6 signals/history query signal_indicators

This commit is contained in:
root 2026-03-03 13:00:54 +00:00
parent cf7756b4e5
commit 8280aaf6ea
2 changed files with 25 additions and 92 deletions

View File

@ -226,11 +226,30 @@ async def get_stats_ytd(user: dict = Depends(get_current_user)):
@app.get("/api/signals/history") @app.get("/api/signals/history")
async def get_signals_history(limit: int = 100, user: dict = Depends(get_current_user)): async def get_signals_history(
limit: int = 100,
symbol: str = None,
strategy: str = None,
user: dict = Depends(get_current_user)
):
try: try:
conditions = []
args = []
idx = 1
if symbol:
conditions.append(f"symbol = ${idx}")
args.append(symbol.upper())
idx += 1
if strategy:
conditions.append(f"strategy = ${idx}")
args.append(strategy)
idx += 1
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
args.append(limit)
rows = await async_fetch( rows = await async_fetch(
"SELECT id, symbol, rate, annualized, sent_at, message FROM signal_logs ORDER BY sent_at DESC LIMIT $1", f"SELECT id, ts, symbol, strategy, score, signal, price, factors "
limit f"FROM signal_indicators {where} ORDER BY ts DESC LIMIT ${idx}",
*args
) )
return {"items": rows} return {"items": rows}
except Exception as e: except Exception as e:

View File

@ -785,95 +785,6 @@ def paper_open_trade(
) )
def paper_check_positions(symbol: str, current_price: float, now_ms: int):
"""检查模拟盘持仓的止盈止损"""
with get_sync_conn() as conn:
with conn.cursor() as cur:
cur.execute(
"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",
(symbol,)
)
positions = cur.fetchall()
for pos in positions:
pid, direction, entry_price, tp1, tp2, sl, tp1_hit, entry_ts, atr_entry, rd_db = pos
closed = False
new_status = None
pnl_r = 0.0
risk_distance = rd_db if rd_db and rd_db > 0 else abs(entry_price - sl)
# === 实盘模拟TP/SL视为限价单以挂单价成交 ===
if direction == "LONG":
if current_price <= sl:
closed = True
exit_price = sl
if tp1_hit:
new_status = "sl_be"
tp1_r = (tp1 - entry_price) / risk_distance if risk_distance > 0 else 0
pnl_r = 0.5 * tp1_r
else:
new_status = "sl"
pnl_r = (exit_price - entry_price) / risk_distance if risk_distance > 0 else -1.0
elif not tp1_hit and current_price >= tp1:
# TP1触发移动止损到成本价
new_sl = entry_price * 1.0005
cur.execute("UPDATE paper_trades SET tp1_hit=TRUE, sl_price=%s, status='tp1_hit' WHERE id=%s", (new_sl, pid))
logger.info(f"[{symbol}] 📝 TP1触发 LONG @ {current_price:.2f}, SL移至成本{new_sl:.2f}")
elif tp1_hit and current_price >= tp2:
closed = True
exit_price = tp2
new_status = "tp"
tp1_r = (tp1 - entry_price) / risk_distance if risk_distance > 0 else 0
tp2_r = (tp2 - entry_price) / risk_distance if risk_distance > 0 else 0
pnl_r = 0.5 * tp1_r + 0.5 * tp2_r
else: # SHORT
if current_price >= sl:
closed = True
exit_price = sl
if tp1_hit:
new_status = "sl_be"
tp1_r = (entry_price - tp1) / risk_distance if risk_distance > 0 else 0
pnl_r = 0.5 * tp1_r
else:
new_status = "sl"
pnl_r = (entry_price - exit_price) / risk_distance if risk_distance > 0 else -1.0
elif not tp1_hit and current_price <= tp1:
new_sl = entry_price * 0.9995
cur.execute("UPDATE paper_trades SET tp1_hit=TRUE, sl_price=%s, status='tp1_hit' WHERE id=%s", (new_sl, pid))
logger.info(f"[{symbol}] 📝 TP1触发 SHORT @ {current_price:.2f}, SL移至成本{new_sl:.2f}")
elif tp1_hit and current_price <= tp2:
closed = True
exit_price = tp2
new_status = "tp"
tp1_r = (entry_price - tp1) / risk_distance if risk_distance > 0 else 0
tp2_r = (entry_price - tp2) / risk_distance if risk_distance > 0 else 0
pnl_r = 0.5 * tp1_r + 0.5 * tp2_r
# 时间止损60分钟市价平仓
if not closed and (now_ms - entry_ts > 60 * 60 * 1000):
closed = True
exit_price = current_price
new_status = "timeout"
if direction == "LONG":
move = current_price - entry_price
else:
move = entry_price - current_price
pnl_r = move / risk_distance if risk_distance > 0 else 0
if tp1_hit:
tp1_r = abs(tp1 - entry_price) / risk_distance if risk_distance > 0 else 0
pnl_r = max(pnl_r, 0.5 * tp1_r)
if closed:
# 扣除手续费(开仓+平仓各Taker 0.05%
fee_r = (2 * PAPER_FEE_RATE * entry_price) / risk_distance if risk_distance > 0 else 0
pnl_r -= fee_r
cur.execute(
"UPDATE paper_trades SET status=%s, exit_price=%s, exit_ts=%s, pnl_r=%s WHERE id=%s",
(new_status, exit_price, now_ms, round(pnl_r, 4), pid)
)
logger.info(f"[{symbol}] 📝 模拟平仓: {direction} @ {exit_price:.2f} status={new_status} pnl={pnl_r:+.2f}R (fee={fee_r:.3f}R)")
conn.commit() conn.commit()
@ -1065,6 +976,9 @@ def main():
if cycle % 60 == 0: if cycle % 60 == 0:
old_strategies = list(PAPER_ENABLED_STRATEGIES) old_strategies = list(PAPER_ENABLED_STRATEGIES)
load_paper_config() load_paper_config()
strategy_configs = load_strategy_configs() # A1: 热重载权重/阈值/TP/SL
strategy_names = [cfg.get("name", "unknown") for cfg in strategy_configs]
primary_strategy_name = "v52_8signals" if any(cfg.get("name") == "v52_8signals" for cfg in strategy_configs) else strategy_names[0]
if list(PAPER_ENABLED_STRATEGIES) != old_strategies: if list(PAPER_ENABLED_STRATEGIES) != old_strategies:
logger.info(f"📋 配置热加载: enabled_strategies={PAPER_ENABLED_STRATEGIES}") logger.info(f"📋 配置热加载: enabled_strategies={PAPER_ENABLED_STRATEGIES}")
for sym, state in states.items(): for sym, state in states.items():