1
This commit is contained in:
parent
6bee742413
commit
922a8f3820
45
analyze_zro.py
Normal file
45
analyze_zro.py
Normal 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()
|
||||||
|
|
@ -77,7 +77,7 @@ class Database:
|
||||||
pool_timeout=30, # 获取连接超时时间(秒)
|
pool_timeout=30, # 获取连接超时时间(秒)
|
||||||
pool_pre_ping=True, # 预检测连接是否可用
|
pool_pre_ping=True, # 预检测连接是否可用
|
||||||
connect_args={
|
connect_args={
|
||||||
'cursorclass': pymysql.cursors.DictCursor,
|
# 'cursorclass': pymysql.cursors.DictCursor, # Removed to prevent KeyError: 0 in SQLAlchemy init
|
||||||
'autocommit': False
|
'autocommit': False
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -94,6 +94,21 @@ class Database:
|
||||||
# 获取原始pymysql连接
|
# 获取原始pymysql连接
|
||||||
conn = Database._engine.raw_connection()
|
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)
|
# 设置时区为北京时间(UTC+8)
|
||||||
# 注意:raw_connection可能不自动应用connect_args中的autocommit,需确认
|
# 注意:raw_connection可能不自动应用connect_args中的autocommit,需确认
|
||||||
# SQLAlchemy的raw_connection通常返回DBAPI连接,autocommit行为取决于驱动
|
# SQLAlchemy的raw_connection通常返回DBAPI连接,autocommit行为取决于驱动
|
||||||
|
|
|
||||||
42
test_db_pool.py
Normal file
42
test_db_pool.py
Normal 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()
|
||||||
|
|
@ -256,7 +256,8 @@ def _get_trading_config():
|
||||||
'MAX_LEVERAGE_SMALL_CAP': 8, # 小众币最大杠杆8倍
|
'MAX_LEVERAGE_SMALL_CAP': 8, # 小众币最大杠杆8倍
|
||||||
'ATR_LEVERAGE_REDUCTION_THRESHOLD': 0.05, # ATR超过5%时降低杠杆
|
'ATR_LEVERAGE_REDUCTION_THRESHOLD': 0.05, # ATR超过5%时降低杠杆
|
||||||
'LEVERAGE': 8, # 基础杠杆降到8倍(山寨币波动大)
|
'LEVERAGE': 8, # 基础杠杆降到8倍(山寨币波动大)
|
||||||
'USE_DYNAMIC_LEVERAGE': False, # 不使用动态杠杆(保持简单)
|
'USE_DYNAMIC_LEVERAGE': True, # 开启动态杠杆(基于止损宽度自动调整)
|
||||||
|
'MAX_SINGLE_TRADE_LOSS_PERCENT': 20.0, # 单笔交易最大本金亏损率(20%),用于限制杠杆
|
||||||
'MAX_LEVERAGE': 12, # 最大杠杆12倍,不要超过
|
'MAX_LEVERAGE': 12, # 最大杠杆12倍,不要超过
|
||||||
# 移动止损:必须开启!山寨币利润要保护
|
# 移动止损:必须开启!山寨币利润要保护
|
||||||
'USE_TRAILING_STOP': True,
|
'USE_TRAILING_STOP': True,
|
||||||
|
|
|
||||||
|
|
@ -280,12 +280,70 @@ class RiskManager:
|
||||||
current_price = ticker['price']
|
current_price = ticker['price']
|
||||||
logger.info(f" 当前价格: {current_price:.4f} USDT")
|
logger.info(f" 当前价格: {current_price:.4f} USDT")
|
||||||
|
|
||||||
# 重要语义:MAX_POSITION_PERCENT 表示“单笔保证金占用比例”
|
# -------------------------------------------------------------------------
|
||||||
# 先确定实际杠杆(用于从保证金换算名义价值)
|
# 动态杠杆计算逻辑 (针对 ZROUSDT 亏损案例优化)
|
||||||
actual_leverage = leverage if leverage is not None else config.TRADING_CONFIG.get('LEVERAGE', 10)
|
# -------------------------------------------------------------------------
|
||||||
|
# 如果启用了动态杠杆,根据止损宽度自动调整杠杆
|
||||||
|
# 公式: 建议杠杆 = 目标最大单单亏损率 / 止损宽度
|
||||||
|
# 例如: 目标亏损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:
|
if not actual_leverage or actual_leverage <= 0:
|
||||||
actual_leverage = 10
|
actual_leverage = 10
|
||||||
|
|
||||||
|
|
||||||
# ⚠️ 优化3:固定风险百分比仓位计算(凯利公式)
|
# ⚠️ 优化3:固定风险百分比仓位计算(凯利公式)
|
||||||
# 公式:仓位大小 = (总资金 * 每笔单子承受的风险%) / (入场价 - 止损价)
|
# 公式:仓位大小 = (总资金 * 每笔单子承受的风险%) / (入场价 - 止损价)
|
||||||
use_fixed_risk = config.TRADING_CONFIG.get('USE_FIXED_RISK_SIZING', True)
|
use_fixed_risk = config.TRADING_CONFIG.get('USE_FIXED_RISK_SIZING', True)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user