This commit is contained in:
薇薇安 2026-02-04 13:45:30 +08:00
parent 6bee742413
commit 922a8f3820
5 changed files with 166 additions and 5 deletions

45
analyze_zro.py Normal file
View File

@ -0,0 +1,45 @@
from backend.database.connection import db
import json
from datetime import datetime
import time
def analyze_zro_trades():
print("Querying ZROUSDT trades...")
# Get all columns
query = "SELECT * FROM trades WHERE symbol='ZROUSDT' ORDER BY id DESC LIMIT 5"
trades = db.execute_query(query)
if not trades:
print("No ZROUSDT trades found.")
return
# Print first trade keys to understand schema
print("Schema keys:", list(trades[0].keys()))
for trade in trades:
print("-" * 50)
ts = trade.get('created_at')
dt = datetime.fromtimestamp(ts) if ts else "N/A"
print(f"ID: {trade.get('id')}")
print(f"Time: {dt} ({ts})")
print(f"Symbol: {trade.get('symbol')}")
print(f"Side: {trade.get('side')}")
print(f"Entry: {trade.get('entry_price')}")
print(f"Exit: {trade.get('exit_price')}")
print(f"Qty: {trade.get('quantity')}")
print(f"Leverage: {trade.get('leverage')}") # Guessing key
print(f"Realized PnL: {trade.get('realized_pnl')}")
print(f"PnL % (DB): {trade.get('pnl_percent')}")
# Calculate approximate ROE if leverage is known
leverage = trade.get('leverage', 1)
if leverage is None: leverage = 1
pnl_pct = trade.get('pnl_percent')
if pnl_pct:
roe = float(pnl_pct) * float(leverage)
print(f"Calc ROE (est): {roe:.2f}% (assuming leverage {leverage})")
if __name__ == "__main__":
analyze_zro_trades()

View File

@ -77,7 +77,7 @@ class Database:
pool_timeout=30, # 获取连接超时时间(秒)
pool_pre_ping=True, # 预检测连接是否可用
connect_args={
'cursorclass': pymysql.cursors.DictCursor,
# 'cursorclass': pymysql.cursors.DictCursor, # Removed to prevent KeyError: 0 in SQLAlchemy init
'autocommit': False
}
)
@ -94,6 +94,21 @@ class Database:
# 获取原始pymysql连接
conn = Database._engine.raw_connection()
# Explicitly set cursor class to DictCursor since we removed it from create_engine
# We need to set it on the underlying DBAPI connection
try:
if hasattr(conn, 'driver_connection'):
# SQLAlchemy 2.0+
conn.driver_connection.cursorclass = pymysql.cursors.DictCursor
elif hasattr(conn, 'connection'):
# Older SQLAlchemy
conn.connection.cursorclass = pymysql.cursors.DictCursor
else:
# Fallback
conn.cursorclass = pymysql.cursors.DictCursor
except Exception as e:
logger.warning(f"设置DictCursor失败: {e}")
# 设置时区为北京时间UTC+8
# 注意raw_connection可能不自动应用connect_args中的autocommit需确认
# SQLAlchemy的raw_connection通常返回DBAPI连接autocommit行为取决于驱动

42
test_db_pool.py Normal file
View File

@ -0,0 +1,42 @@
import os
import sys
import logging
import traceback
import pymysql
from backend.database.connection import Database
# Configure logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def test_connection():
print("Initializing Database...")
db = Database()
print("Testing get_connection...")
try:
with db.get_connection() as conn:
print(f"Connection type: {type(conn)}")
# Try to force DictCursor here if the fix in connection.py isn't enough,
# but we want to test if connection.py does it automatically.
with conn.cursor() as cursor:
cursor.execute("SELECT 1 as val")
result = cursor.fetchone()
print(f"Query result: {result}")
print(f"Result type: {type(result)}")
if isinstance(result, dict):
print("SUCCESS: Result is a dictionary")
else:
print("FAILURE: Result is NOT a dictionary")
# Debug: try to find how to set it
if hasattr(conn, 'connection'):
print(f"Inner connection type: {type(conn.connection)}")
except Exception as e:
print("Caught exception:")
traceback.print_exc()
if __name__ == "__main__":
test_connection()

View File

@ -256,7 +256,8 @@ def _get_trading_config():
'MAX_LEVERAGE_SMALL_CAP': 8, # 小众币最大杠杆8倍
'ATR_LEVERAGE_REDUCTION_THRESHOLD': 0.05, # ATR超过5%时降低杠杆
'LEVERAGE': 8, # 基础杠杆降到8倍山寨币波动大
'USE_DYNAMIC_LEVERAGE': False, # 不使用动态杠杆(保持简单)
'USE_DYNAMIC_LEVERAGE': True, # 开启动态杠杆(基于止损宽度自动调整)
'MAX_SINGLE_TRADE_LOSS_PERCENT': 20.0, # 单笔交易最大本金亏损率20%),用于限制杠杆
'MAX_LEVERAGE': 12, # 最大杠杆12倍不要超过
# 移动止损:必须开启!山寨币利润要保护
'USE_TRAILING_STOP': True,

View File

@ -280,12 +280,70 @@ class RiskManager:
current_price = ticker['price']
logger.info(f" 当前价格: {current_price:.4f} USDT")
# 重要语义MAX_POSITION_PERCENT 表示“单笔保证金占用比例”
# 先确定实际杠杆(用于从保证金换算名义价值)
actual_leverage = leverage if leverage is not None else config.TRADING_CONFIG.get('LEVERAGE', 10)
# -------------------------------------------------------------------------
# 动态杠杆计算逻辑 (针对 ZROUSDT 亏损案例优化)
# -------------------------------------------------------------------------
# 如果启用了动态杠杆,根据止损宽度自动调整杠杆
# 公式: 建议杠杆 = 目标最大单单亏损率 / 止损宽度
# 例如: 目标亏损20%止损宽度10%,则建议杠杆 = 20% / 10% = 2倍
# -------------------------------------------------------------------------
calculated_leverage = None
if config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False):
# 尝试获取止损价格(如果没有传入,尝试估算)
target_stop_loss = stop_loss_price
# 如果没有传入止损价尝试使用ATR估算仅用于计算杠杆建议
if target_stop_loss is None and atr and atr > 0 and entry_price:
atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 2.5)
if side == 'BUY':
target_stop_loss = entry_price - (atr * atr_multiplier)
else:
target_stop_loss = entry_price + (atr * atr_multiplier)
if target_stop_loss and entry_price:
# 计算止损宽度比例
stop_loss_width = abs(entry_price - target_stop_loss) / entry_price
if stop_loss_width > 0:
# 获取最大单笔亏损率限制 (默认20%)
max_loss_pct = config.TRADING_CONFIG.get('MAX_SINGLE_TRADE_LOSS_PERCENT', 20.0) / 100.0
# 计算建议杠杆
# 理论杠杆 = 最大允许亏损比例 / 止损宽度比例
theoretical_leverage = max_loss_pct / stop_loss_width
# 向下取整
suggested_leverage = int(theoretical_leverage)
# 限制在最大杠杆范围内
max_config_leverage = config.TRADING_CONFIG.get('MAX_LEVERAGE', 10)
final_dynamic_leverage = min(suggested_leverage, max_config_leverage)
# 至少为1倍
final_dynamic_leverage = max(1, final_dynamic_leverage)
logger.info(f" ⚖️ 动态杠杆计算:")
logger.info(f" 止损价格: {target_stop_loss:.4f} (宽度: {stop_loss_width*100:.2f}%)")
logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%")
logger.info(f" 理论最大杠杆: {theoretical_leverage:.2f}x")
logger.info(f" 配置最大杠杆: {max_config_leverage}x")
logger.info(f" -> 最终建议杠杆: {final_dynamic_leverage}x")
calculated_leverage = final_dynamic_leverage
# 确定最终使用的杠杆
# 优先级: 动态计算杠杆 > 传入的leverage参数 > 配置的默认LEVERAGE
if calculated_leverage is not None:
actual_leverage = calculated_leverage
if leverage is not None and leverage != actual_leverage:
logger.warning(f" ⚠️ 覆盖传入的杠杆 {leverage}x -> 动态调整为 {actual_leverage}x")
else:
actual_leverage = leverage if leverage is not None else config.TRADING_CONFIG.get('LEVERAGE', 10)
if not actual_leverage or actual_leverage <= 0:
actual_leverage = 10
# ⚠️ 优化3固定风险百分比仓位计算凯利公式
# 公式:仓位大小 = (总资金 * 每笔单子承受的风险%) / (入场价 - 止损价)
use_fixed_risk = config.TRADING_CONFIG.get('USE_FIXED_RISK_SIZING', True)