1
This commit is contained in:
parent
3609bddace
commit
262ee661a5
68
backend/check_db_symbols.py
Normal file
68
backend/check_db_symbols.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Add backend directory to sys.path
|
||||||
|
backend_path = Path(__file__).parent
|
||||||
|
sys.path.insert(0, str(backend_path))
|
||||||
|
|
||||||
|
try:
|
||||||
|
from database.connection import db
|
||||||
|
print("Database connection imported successfully.")
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"Error importing database connection: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def is_ascii(s):
|
||||||
|
return all(ord(c) < 128 for c in s)
|
||||||
|
|
||||||
|
def check_table(table_name, column_name):
|
||||||
|
print(f"Checking table '{table_name}' column '{column_name}'...")
|
||||||
|
try:
|
||||||
|
query = f"SELECT DISTINCT {column_name} FROM {table_name}"
|
||||||
|
rows = db.execute_query(query)
|
||||||
|
|
||||||
|
found_invalid = False
|
||||||
|
for row in rows:
|
||||||
|
symbol = row.get(column_name)
|
||||||
|
if symbol and not is_ascii(symbol):
|
||||||
|
print(f"!!! FOUND INVALID SYMBOL in {table_name}: '{symbol}'")
|
||||||
|
found_invalid = True
|
||||||
|
if symbol and "币安" in symbol:
|
||||||
|
print(f"!!! FOUND '币安' in {table_name}: '{symbol}'")
|
||||||
|
found_invalid = True
|
||||||
|
|
||||||
|
if not found_invalid:
|
||||||
|
print(f"No invalid symbols found in {table_name}.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error querying {table_name}: {e}")
|
||||||
|
|
||||||
|
def check_config(table_name):
|
||||||
|
print(f"Checking table '{table_name}' for '币安'...")
|
||||||
|
try:
|
||||||
|
query = f"SELECT config_key, config_value FROM {table_name}"
|
||||||
|
rows = db.execute_query(query)
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
key = row.get('config_key')
|
||||||
|
val = row.get('config_value')
|
||||||
|
|
||||||
|
if val and "币安" in str(val):
|
||||||
|
# Ignore expected descriptions/comments if any (usually description is separate column)
|
||||||
|
# But here we check config_value
|
||||||
|
print(f"Found '币安' in {table_name} KEY='{key}': VALUE='{val}'")
|
||||||
|
|
||||||
|
if key and "币安" in str(key):
|
||||||
|
print(f"Found '币安' in {table_name} KEY='{key}'")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error querying {table_name}: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
check_table("trades", "symbol")
|
||||||
|
check_table("trade_recommendations", "symbol")
|
||||||
|
check_config("trading_config")
|
||||||
|
check_config("global_strategy_config")
|
||||||
29
check_strange_symbols.py
Normal file
29
check_strange_symbols.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add project root to path
|
||||||
|
project_root = Path(__file__).parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
from backend.database.models import Trade, TradeRecommendation, get_db_context
|
||||||
|
|
||||||
|
def check_strange_symbols():
|
||||||
|
with get_db_context() as db:
|
||||||
|
# Check Trades
|
||||||
|
trades = db.query(Trade).all()
|
||||||
|
print(f"Checking {len(trades)} trades...")
|
||||||
|
for t in trades:
|
||||||
|
if not t.symbol.isascii() or len(t.symbol) > 15:
|
||||||
|
print(f"Suspicious Trade Symbol: {t.symbol} (ID: {t.id})")
|
||||||
|
|
||||||
|
# Check Recommendations
|
||||||
|
recs = db.query(TradeRecommendation).all()
|
||||||
|
print(f"Checking {len(recs)} recommendations...")
|
||||||
|
for r in recs:
|
||||||
|
if not r.symbol.isascii() or len(r.symbol) > 15:
|
||||||
|
print(f"Suspicious Rec Symbol: {r.symbol} (ID: {r.id})")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
check_strange_symbols()
|
||||||
|
|
@ -65,7 +65,6 @@ const ConfigPanel = () => {
|
||||||
name: '⭐山寨币狙击',
|
name: '⭐山寨币狙击',
|
||||||
desc: '合理盈亏比(3:1)+ 宽止损(2.0×ATR)+ 移动止损保护 + 严格流动性筛选。2026-01-27优化:让收益率真实,胜率正常化。期望胜率40%+,盈亏比1.5:1+。',
|
desc: '合理盈亏比(3:1)+ 宽止损(2.0×ATR)+ 移动止损保护 + 严格流动性筛选。2026-01-27优化:让收益率真实,胜率正常化。期望胜率40%+,盈亏比1.5:1+。',
|
||||||
configs: {
|
configs: {
|
||||||
// 2026-01-27优化:让收益率真实,胜率正常化
|
|
||||||
ATR_STOP_LOSS_MULTIPLIER: 1.5, STOP_LOSS_PERCENT: 12.0, RISK_REWARD_RATIO: 3.0,
|
ATR_STOP_LOSS_MULTIPLIER: 1.5, STOP_LOSS_PERCENT: 12.0, RISK_REWARD_RATIO: 3.0,
|
||||||
ATR_TAKE_PROFIT_MULTIPLIER: 2.0, TAKE_PROFIT_PERCENT: 20.0, MIN_HOLD_TIME_SEC: 0,
|
ATR_TAKE_PROFIT_MULTIPLIER: 2.0, TAKE_PROFIT_PERCENT: 20.0, MIN_HOLD_TIME_SEC: 0,
|
||||||
USE_FIXED_RISK_SIZING: true, FIXED_RISK_PERCENT: 1.0,
|
USE_FIXED_RISK_SIZING: true, FIXED_RISK_PERCENT: 1.0,
|
||||||
|
|
|
||||||
|
|
@ -1150,6 +1150,13 @@ class BinanceClient:
|
||||||
Returns:
|
Returns:
|
||||||
订单信息
|
订单信息
|
||||||
"""
|
"""
|
||||||
|
# 0. 校验 symbol 合法性(防止非法字符导致 -1022 签名错误)
|
||||||
|
if not symbol or not symbol.isascii():
|
||||||
|
logger.error(f"❌ 下单请求包含非法 Symbol: '{symbol}' (包含非ASCII字符)")
|
||||||
|
import traceback
|
||||||
|
logger.error(f" 调用堆栈:\n{traceback.format_exc()}")
|
||||||
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 获取交易对精度信息
|
# 获取交易对精度信息
|
||||||
symbol_info = await self.get_symbol_info(symbol)
|
symbol_info = await self.get_symbol_info(symbol)
|
||||||
|
|
@ -1445,6 +1452,12 @@ class BinanceClient:
|
||||||
Returns:
|
Returns:
|
||||||
是否成功
|
是否成功
|
||||||
"""
|
"""
|
||||||
|
if not symbol or not symbol.isascii():
|
||||||
|
logger.error(f"❌ 设置杠杆请求包含非法 Symbol: '{symbol}'")
|
||||||
|
import traceback
|
||||||
|
logger.error(f" 调用堆栈:\n{traceback.format_exc()}")
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.client.futures_cancel_order(symbol=symbol, orderId=order_id)
|
await self.client.futures_cancel_order(symbol=symbol, orderId=order_id)
|
||||||
logger.info(f"取消订单成功: {symbol} {order_id}")
|
logger.info(f"取消订单成功: {symbol} {order_id}")
|
||||||
|
|
@ -1794,6 +1807,12 @@ class BinanceClient:
|
||||||
Returns:
|
Returns:
|
||||||
是否成功
|
是否成功
|
||||||
"""
|
"""
|
||||||
|
if not symbol or not symbol.isascii():
|
||||||
|
logger.error(f"❌ 设置杠杆请求包含非法 Symbol: '{symbol}'")
|
||||||
|
import traceback
|
||||||
|
logger.error(f" 调用堆栈:\n{traceback.format_exc()}")
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.client.futures_change_leverage(symbol=symbol, leverage=leverage)
|
await self.client.futures_change_leverage(symbol=symbol, leverage=leverage)
|
||||||
logger.info(f"设置杠杆成功: {symbol} {leverage}x")
|
logger.info(f"设置杠杆成功: {symbol} {leverage}x")
|
||||||
|
|
|
||||||
|
|
@ -228,9 +228,17 @@ class RiskManager:
|
||||||
logger.error(f"检查总仓位失败: {e}", exc_info=True)
|
logger.error(f"检查总仓位失败: {e}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def calculate_dynamic_leverage(self, symbol: str, entry_price: float, stop_loss_price: Optional[float] = None, atr: Optional[float] = None, side: Optional[str] = None) -> int:
|
async def calculate_dynamic_leverage(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
entry_price: float,
|
||||||
|
stop_loss_price: Optional[float] = None,
|
||||||
|
atr: Optional[float] = None,
|
||||||
|
side: Optional[str] = None,
|
||||||
|
signal_strength: Optional[int] = None
|
||||||
|
) -> int:
|
||||||
"""
|
"""
|
||||||
计算动态杠杆
|
计算动态杠杆 - 综合考虑信号强度和风险控制
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
symbol: 交易对
|
symbol: 交易对
|
||||||
|
|
@ -238,6 +246,7 @@ class RiskManager:
|
||||||
stop_loss_price: 止损价格
|
stop_loss_price: 止损价格
|
||||||
atr: ATR值
|
atr: ATR值
|
||||||
side: 交易方向
|
side: 交易方向
|
||||||
|
signal_strength: 信号强度 (0-10)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
建议杠杆倍数
|
建议杠杆倍数
|
||||||
|
|
@ -248,10 +257,40 @@ class RiskManager:
|
||||||
if not config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False):
|
if not config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False):
|
||||||
return default_leverage
|
return default_leverage
|
||||||
|
|
||||||
|
# 1. 基于信号强度的杠杆计算 (进攻性)
|
||||||
|
signal_leverage = default_leverage
|
||||||
|
if signal_strength is not None:
|
||||||
|
base_leverage = default_leverage
|
||||||
|
max_leverage = config.TRADING_CONFIG.get('MAX_LEVERAGE', 20)
|
||||||
|
min_signal_strength = config.TRADING_CONFIG.get('MIN_SIGNAL_STRENGTH', 7)
|
||||||
|
|
||||||
|
if signal_strength >= min_signal_strength:
|
||||||
|
signal_range = 10 - min_signal_strength
|
||||||
|
leverage_range = max_leverage - base_leverage
|
||||||
|
if signal_range > 0:
|
||||||
|
strength_above_min = signal_strength - min_signal_strength
|
||||||
|
leverage_increase = (strength_above_min / signal_range) * leverage_range
|
||||||
|
signal_leverage = base_leverage + leverage_increase
|
||||||
|
signal_leverage = min(signal_leverage, max_leverage)
|
||||||
|
|
||||||
|
logger.info(f" 📊 信号强度杠杆 ({symbol}): 信号={signal_strength} -> {int(signal_leverage)}x")
|
||||||
|
|
||||||
|
# 2. 基于ATR波动率的限制 (小众币保护)
|
||||||
|
atr_limit_leverage = 100 # 初始设为很高
|
||||||
|
if atr and entry_price and entry_price > 0:
|
||||||
|
atr_percent = atr / entry_price
|
||||||
|
atr_leverage_reduction_threshold = config.TRADING_CONFIG.get('ATR_LEVERAGE_REDUCTION_THRESHOLD', 0.05) # 5%
|
||||||
|
max_leverage_small_cap = config.TRADING_CONFIG.get('MAX_LEVERAGE_SMALL_CAP', 5)
|
||||||
|
|
||||||
|
if atr_percent >= atr_leverage_reduction_threshold:
|
||||||
|
atr_limit_leverage = max_leverage_small_cap
|
||||||
|
logger.info(f" ⚠️ ATR波动率 {atr_percent*100:.2f}% (高波动), 限制最大杠杆为 {atr_limit_leverage}x")
|
||||||
|
|
||||||
|
# 3. 基于止损宽度的风险控制 (防御性 - 核心逻辑)
|
||||||
|
risk_safe_leverage = 100 # 初始设为很高
|
||||||
|
|
||||||
# 尝试获取止损价格(如果没有传入,尝试估算)
|
# 尝试获取止损价格(如果没有传入,尝试估算)
|
||||||
target_stop_loss = stop_loss_price
|
target_stop_loss = stop_loss_price
|
||||||
|
|
||||||
# 如果没有传入止损价,尝试使用ATR估算(仅用于计算杠杆建议)
|
|
||||||
if target_stop_loss is None and atr and atr > 0 and entry_price:
|
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)
|
atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 2.5)
|
||||||
if side == 'BUY':
|
if side == 'BUY':
|
||||||
|
|
@ -267,29 +306,40 @@ class RiskManager:
|
||||||
# 获取最大单笔亏损率限制 (默认20%)
|
# 获取最大单笔亏损率限制 (默认20%)
|
||||||
max_loss_pct = config.TRADING_CONFIG.get('MAX_SINGLE_TRADE_LOSS_PERCENT', 20.0) / 100.0
|
max_loss_pct = config.TRADING_CONFIG.get('MAX_SINGLE_TRADE_LOSS_PERCENT', 20.0) / 100.0
|
||||||
|
|
||||||
# 计算建议杠杆
|
|
||||||
# 理论杠杆 = 最大允许亏损比例 / 止损宽度比例
|
# 理论杠杆 = 最大允许亏损比例 / 止损宽度比例
|
||||||
|
# 例如:最大亏损20%,止损宽5%,则最大杠杆 = 4倍 (4 * 5% = 20%)
|
||||||
theoretical_leverage = max_loss_pct / stop_loss_width
|
theoretical_leverage = max_loss_pct / stop_loss_width
|
||||||
|
risk_safe_leverage = int(theoretical_leverage)
|
||||||
|
|
||||||
# 向下取整
|
logger.info(f" 🛡️ 风险安全杠杆 ({symbol}):")
|
||||||
suggested_leverage = int(theoretical_leverage)
|
logger.info(f" 止损宽度: {stop_loss_width*100:.2f}%")
|
||||||
|
|
||||||
# 限制在最大杠杆范围内
|
|
||||||
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" ⚖️ 动态杠杆计算 ({symbol}):")
|
|
||||||
logger.info(f" 止损价格: {target_stop_loss:.4f} (宽度: {stop_loss_width*100:.2f}%)")
|
|
||||||
logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%")
|
logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%")
|
||||||
logger.info(f" 理论最大杠杆: {theoretical_leverage:.2f}x")
|
logger.info(f" -> 理论最大杠杆: {theoretical_leverage:.2f}x")
|
||||||
logger.info(f" -> 最终建议杠杆: {final_dynamic_leverage}x")
|
|
||||||
|
# 4. 综合计算最终杠杆 (取最小值)
|
||||||
return final_dynamic_leverage
|
final_leverage = min(int(signal_leverage), int(atr_limit_leverage), int(risk_safe_leverage))
|
||||||
|
|
||||||
return default_leverage
|
# 限制在系统最大杠杆范围内
|
||||||
|
max_config_leverage = config.TRADING_CONFIG.get('MAX_LEVERAGE', 20)
|
||||||
|
final_leverage = min(final_leverage, max_config_leverage)
|
||||||
|
|
||||||
|
# 至少为1倍
|
||||||
|
final_leverage = max(1, final_leverage)
|
||||||
|
|
||||||
|
# 检查交易对最大杠杆限制
|
||||||
|
if symbol:
|
||||||
|
try:
|
||||||
|
symbol_info = await self.client.get_symbol_info(symbol)
|
||||||
|
if symbol_info and 'maxLeverage' in symbol_info:
|
||||||
|
symbol_max_leverage = symbol_info['maxLeverage']
|
||||||
|
if final_leverage > symbol_max_leverage:
|
||||||
|
logger.warning(f" ⚠️ {symbol} 交易所限制最大杠杆 {symbol_max_leverage}x, 调整 {final_leverage}x -> {symbol_max_leverage}x")
|
||||||
|
final_leverage = symbol_max_leverage
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger.info(f" ⚖️ 最终动态杠杆: {final_leverage}x (信号:{int(signal_leverage)}x, ATR限制:{int(atr_limit_leverage)}x, 风险安全:{int(risk_safe_leverage)}x)")
|
||||||
|
return final_leverage
|
||||||
|
|
||||||
async def calculate_position_size(
|
async def calculate_position_size(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user