This commit is contained in:
薇薇安 2026-02-08 09:27:49 +08:00
parent 3609bddace
commit 262ee661a5
6 changed files with 189 additions and 1304 deletions

View 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
View 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()

View File

@ -65,7 +65,6 @@ const ConfigPanel = () => {
name: '⭐山寨币狙击',
desc: '合理盈亏比3:1+ 宽止损2.0×ATR+ 移动止损保护 + 严格流动性筛选。2026-01-27优化让收益率真实胜率正常化。期望胜率40%+盈亏比1.5:1+。',
configs: {
// 2026-01-27
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,
USE_FIXED_RISK_SIZING: true, FIXED_RISK_PERCENT: 1.0,

View File

@ -1150,6 +1150,13 @@ class BinanceClient:
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:
# 获取交易对精度信息
symbol_info = await self.get_symbol_info(symbol)
@ -1445,6 +1452,12 @@ class BinanceClient:
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:
await self.client.futures_cancel_order(symbol=symbol, orderId=order_id)
logger.info(f"取消订单成功: {symbol} {order_id}")
@ -1794,6 +1807,12 @@ class BinanceClient:
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:
await self.client.futures_change_leverage(symbol=symbol, leverage=leverage)
logger.info(f"设置杠杆成功: {symbol} {leverage}x")

View File

@ -228,9 +228,17 @@ class RiskManager:
logger.error(f"检查总仓位失败: {e}", exc_info=True)
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:
symbol: 交易对
@ -238,6 +246,7 @@ class RiskManager:
stop_loss_price: 止损价格
atr: ATR值
side: 交易方向
signal_strength: 信号强度 (0-10)
Returns:
建议杠杆倍数
@ -248,10 +257,40 @@ class RiskManager:
if not config.TRADING_CONFIG.get('USE_DYNAMIC_LEVERAGE', False):
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
# 如果没有传入止损价尝试使用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':
@ -267,29 +306,40 @@ class RiskManager:
# 获取最大单笔亏损率限制 (默认20%)
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
risk_safe_leverage = int(theoretical_leverage)
# 向下取整
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" ⚖️ 动态杠杆计算 ({symbol}):")
logger.info(f" 止损价格: {target_stop_loss:.4f} (宽度: {stop_loss_width*100:.2f}%)")
logger.info(f" 🛡️ 风险安全杠杆 ({symbol}):")
logger.info(f" 止损宽度: {stop_loss_width*100:.2f}%")
logger.info(f" 最大单笔亏损限制: {max_loss_pct*100:.1f}%")
logger.info(f" 理论最大杠杆: {theoretical_leverage:.2f}x")
logger.info(f" -> 最终建议杠杆: {final_dynamic_leverage}x")
return final_dynamic_leverage
return default_leverage
logger.info(f" -> 理论最大杠杆: {theoretical_leverage:.2f}x")
# 4. 综合计算最终杠杆 (取最小值)
final_leverage = min(int(signal_leverage), int(atr_limit_leverage), int(risk_safe_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(
self,

File diff suppressed because it is too large Load Diff