diff --git a/backend/main.py b/backend/main.py index 5d031b2..01d604b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -226,11 +226,30 @@ async def get_stats_ytd(user: dict = Depends(get_current_user)): @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: + 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( - "SELECT id, symbol, rate, annualized, sent_at, message FROM signal_logs ORDER BY sent_at DESC LIMIT $1", - limit + f"SELECT id, ts, symbol, strategy, score, signal, price, factors " + f"FROM signal_indicators {where} ORDER BY ts DESC LIMIT ${idx}", + *args ) return {"items": rows} except Exception as e: diff --git a/backend/signal_engine.py b/backend/signal_engine.py index 21e3840..1ce87d0 100644 --- a/backend/signal_engine.py +++ b/backend/signal_engine.py @@ -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() @@ -1065,6 +976,9 @@ def main(): if cycle % 60 == 0: old_strategies = list(PAPER_ENABLED_STRATEGIES) 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: logger.info(f"📋 配置热加载: enabled_strategies={PAPER_ENABLED_STRATEGIES}") for sym, state in states.items():