This commit is contained in:
薇薇安 2026-02-14 01:27:47 +08:00
parent f8058083e3
commit d97363e3d4
11 changed files with 305 additions and 300 deletions

View File

@ -101,7 +101,7 @@ class GlobalStrategyConfigManager:
self._cache = {}
self._redis_client: Optional[redis.Redis] = None
self._redis_connected = False
self._redis_hash_key = "global_strategy_config_v3" # 独立的Redis键 (v3: 强制刷新缓存 - 2025-02-13)
self._redis_hash_key = "global_strategy_config_v5" # 独立的Redis键 (v5: 强制刷新缓存 - 2025-02-14)
self._init_redis()
self._load_from_db()

View File

@ -1,93 +0,0 @@
import asyncio
import sys
import os
from datetime import datetime
# Add path
sys.path.insert(0, '/Users/vivian/work/python/auto_trade_sys')
sys.path.insert(0, '/Users/vivian/work/python/auto_trade_sys/backend')
from trading_system.binance_client import BinanceClient
from config_manager import ConfigManager
async def check_account(account_id):
print(f"\n{'='*20} Checking Account {account_id} {'='*20}")
try:
# Initialize ConfigManager for this account
config_manager = ConfigManager(account_id=account_id)
api_key = config_manager.get('BINANCE_API_KEY')
api_secret = config_manager.get('BINANCE_API_SECRET')
if not api_key or not api_secret:
print(f"Skipping Account {account_id}: Missing API keys")
return
# Initialize client with specific keys
client = BinanceClient(api_key=api_key, api_secret=api_secret)
# Connect
await client.connect()
# Get account information
# Access the underlying AsyncClient
if not client.client:
print(f"Failed to connect to Binance for Account {account_id}")
return
# Check positions
print("=== Current Positions ===")
positions = await client.client.futures_position_information()
has_positions = False
for pos in positions:
symbol = pos['symbol']
amt = float(pos['positionAmt'])
if amt != 0:
has_positions = True
entry_price = float(pos['entryPrice'])
mark_price = float(pos['markPrice'])
unrealized_profit = float(pos['unRealizedProfit'])
# Calculate percentage
if entry_price > 0:
if amt > 0:
pnl_pct = (mark_price - entry_price) / entry_price * 100
else:
pnl_pct = (entry_price - mark_price) / entry_price * 100
else:
pnl_pct = 0
print(f"Symbol: {symbol}")
print(f" Amount: {amt}")
print(f" Entry Price: {entry_price}")
print(f" Mark Price: {mark_price}")
print(f" Unrealized PnL: {unrealized_profit:.2f} USDT ({pnl_pct:.2f}%)")
print("-" * 30)
if not has_positions:
print("No open positions.")
# Check open orders
print("\n=== Open Orders (for active positions) ===")
if has_positions:
for pos in positions:
if float(pos['positionAmt']) != 0:
symbol = pos['symbol']
orders = await client.get_open_orders(symbol)
if orders:
print(f"Orders for {symbol}:")
for order in orders:
print(f" Type: {order['type']}, Side: {order['side']}, Price: {order['price']}, StopPrice: {order.get('stopPrice', 'N/A')}")
else:
print(f"No open orders for {symbol}")
await client.close()
except Exception as e:
print(f"Error checking Account {account_id}: {e}")
async def main():
for account_id in [1, 2, 3, 4]:
await check_account(account_id)
if __name__ == "__main__":
asyncio.run(main())

View File

@ -1,35 +0,0 @@
import os
import sys
import pymysql
from pathlib import Path
from dotenv import load_dotenv
# Load env
backend_dir = Path(__file__).parent / 'backend'
load_dotenv(backend_dir / '.env', override=True)
host = os.getenv('DB_HOST', 'localhost')
port = int(os.getenv('DB_PORT', 3306))
user = os.getenv('DB_USER', 'root')
password = os.getenv('DB_PASSWORD', '')
print(f"Connecting to {host}:{port} as {user}")
try:
conn = pymysql.connect(
host=host,
port=port,
user=user,
password=password,
charset='utf8mb4'
)
cursor = conn.cursor()
cursor.execute("SHOW DATABASES")
dbs = cursor.fetchall()
print("Databases:")
for db in dbs:
print(db)
conn.close()
except Exception as e:
print(f"Error: {e}")

View File

@ -1,24 +0,0 @@
import sys
import os
from pathlib import Path
# Add backend to sys.path
backend_dir = Path(__file__).parent / 'backend'
sys.path.insert(0, str(backend_dir))
from database.models import GlobalStrategyConfig, TradingConfig
from database.connection import db
print("Checking DB values...")
keys = ['TAKE_PROFIT_PERCENT', 'TAKE_PROFIT_1_PERCENT', 'STOP_LOSS_PERCENT']
print("--- GlobalStrategyConfig ---")
for key in keys:
val = GlobalStrategyConfig.get_value(key)
print(f"{key}: {val} (Type: {type(val)})")
print("\n--- TradingConfig (All Accounts) ---")
rows = db.execute_query("SELECT account_id, config_key, config_value FROM trading_config WHERE config_key IN ('TAKE_PROFIT_PERCENT', 'TAKE_PROFIT_1_PERCENT', 'STOP_LOSS_PERCENT') ORDER BY account_id")
for row in rows:
print(f"Account {row['account_id']} - {row['config_key']}: {row['config_value']}")

View File

@ -1,29 +0,0 @@
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

@ -1,52 +0,0 @@
import pymysql
import os
from datetime import datetime
# DB Config
DB_HOST = 'localhost'
DB_USER = 'root'
DB_PASSWORD = '12345678'
DB_NAME = 'auto_trade_sys_new'
def connect_db():
return pymysql.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME,
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
def main():
try:
conn = connect_db()
cursor = conn.cursor()
print(f"Connected to {DB_NAME}")
# Search for Trade ID 3507
print("\n=== Search for Trade ID 3507 ===")
cursor.execute("SELECT * FROM trades WHERE id = 3507")
trade = cursor.fetchone()
if trade:
print(f"Found Trade 3507:")
for k, v in trade.items():
print(f" {k}: {v}")
else:
print("Trade 3507 not found in DB.")
# Check latest trades across all accounts
print("\n=== Latest 5 Trades (All Accounts) ===")
cursor.execute("SELECT id, account_id, symbol, status, pnl, pnl_percent, exit_reason, created_at FROM trades ORDER BY id DESC LIMIT 5")
trades = cursor.fetchall()
for t in trades:
print(t)
conn.close()
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()

View File

@ -1,51 +0,0 @@
import os
import sys
from sqlalchemy import create_engine, text
import json
from datetime import datetime
# Database connection
DB_USER = os.getenv('DB_USER', 'root')
DB_PASSWORD = os.getenv('DB_PASSWORD', '12345678')
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_PORT = os.getenv('DB_PORT', '3306')
DB_NAME = 'auto_trade_sys_new'
DATABASE_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
def check_today_trades():
try:
engine = create_engine(DATABASE_URL)
with engine.connect() as conn:
# Query trades from today (2026-02-13)
query = text("""
SELECT id, symbol, side, entry_price, exit_price, pnl, pnl_percent, exit_reason, exit_time, entry_time
FROM trades
WHERE DATE(entry_time) = '2026-02-13' OR DATE(exit_time) = '2026-02-13'
ORDER BY exit_time DESC
LIMIT 10
""")
result = conn.execute(query)
trades = []
for row in result:
trades.append({
"id": row[0],
"symbol": row[1],
"side": row[2],
"entry_price": float(row[3]) if row[3] else None,
"exit_price": float(row[4]) if row[4] else None,
"pnl": float(row[5]) if row[5] else None,
"pnl_percent": float(row[6]) if row[6] else None,
"exit_reason": row[7],
"exit_time": str(row[8]) if row[8] else None,
"entry_time": str(row[9]) if row[9] else None
})
print(json.dumps(trades, indent=2))
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
check_today_trades()

View File

@ -203,13 +203,13 @@ DEFAULT_TRADING_CONFIG = {
'MIN_STOP_LOSS_PRICE_PCT': 0.025, # 最小止损价格变动2.5%
'MIN_TAKE_PROFIT_PRICE_PCT': 0.02, # 最小止盈价格变动2%
'USE_ATR_STOP_LOSS': True, # 使用ATR动态止损
'ATR_STOP_LOSS_MULTIPLIER': 3.0, # ATR止损倍数3.02026-02-12减少噪音止损配合止盈拉远
'ATR_STOP_LOSS_MULTIPLIER': 0.5, # ATR止损倍数0.52026-02-14收紧止损与ATR_MULTIPLIER_MAX一致
'ATR_TAKE_PROFIT_MULTIPLIER': 6.0, # ATR止盈倍数6.0(追求更高盈亏比)
'RISK_REWARD_RATIO': 3.0, # 盈亏比3:1
'ATR_PERIOD': 14, # ATR计算周期14
'USE_DYNAMIC_ATR_MULTIPLIER': False, # 不使用动态ATR
'ATR_MULTIPLIER_MIN': 1.5, # 动态ATR倍数最小值
'ATR_MULTIPLIER_MAX': 2.5, # 动态ATR倍数最大值
'ATR_MULTIPLIER_MIN': 0.5, # 动态ATR倍数最小值
'ATR_MULTIPLIER_MAX': 0.5, # 动态ATR倍数最大值
'SCAN_INTERVAL': 900, # 扫描间隔15分钟900秒快速验证模式提高扫描频率以增加交易机会
'KLINE_INTERVAL': '1h',
'PRIMARY_INTERVAL': '4h', # 主周期4小时过滤噪音

View File

@ -213,20 +213,43 @@ class MarketScanner:
logger.info(f"扫描完成,找到 {len(top_n)} 个符合条件的交易对")
# 打印结果(包含技术指标)
for i, symbol_info in enumerate(top_n, 1):
rsi_str = f"RSI:{symbol_info.get('rsi', 0):.1f}" if symbol_info.get('rsi') else "RSI:N/A"
regime_str = symbol_info.get('marketRegime', 'unknown')
# ⚠️ 2026-01-27优化显示真实的signal_strength而不是signalScore
strength_str = f"信号:{symbol_info.get('signal_strength', symbol_info.get('signalScore', 0))}"
logger.info(
f"{i}. {symbol_info['symbol']}: "
f"{symbol_info['changePercent']:.2f}% | "
f"{rsi_str} | {regime_str} | {strength_str} | "
f"价格: {symbol_info['price']:.4f}"
)
# 分组打印:优先展示有信号的交易对,然后是高波动/成交量的观察对象
signals_found = [s for s in top_n if s.get('signal_strength', 0) > 0 or s.get('signalScore', 0) > 0]
others = [s for s in top_n if s not in signals_found]
if signals_found:
logger.info(f"===== 发现潜在交易信号 ({len(signals_found)}) =====")
for i, symbol_info in enumerate(signals_found, 1):
self._log_single_symbol(i, symbol_info)
if others:
logger.info(f"===== 其它活跃交易对 (高波动/无明确信号) ({len(others)}) =====")
for i, symbol_info in enumerate(others, 1):
self._log_single_symbol(i, symbol_info)
return top_n
def _log_single_symbol(self, index: int, symbol_info: Dict):
"""打印单个交易对信息"""
rsi_str = f"RSI:{symbol_info.get('rsi', 0):.1f}" if symbol_info.get('rsi') else "RSI:N/A"
regime_str = symbol_info.get('marketRegime', 'unknown')
strength = symbol_info.get('signal_strength', symbol_info.get('signalScore', 0))
strength_str = f"信号:{strength}" if strength > 0 else ""
log_parts = [
f"{index}. {symbol_info['symbol']}:",
f"{symbol_info['changePercent']:.2f}%",
rsi_str,
regime_str
]
if strength_str:
log_parts.append(strength_str)
log_parts.append(f"价格: {symbol_info['price']:.4f}")
logger.info(" | ".join(log_parts))
async def _get_symbol_change(self, symbol: str, ticker_data: Optional[Dict] = None) -> Optional[Dict]:
"""

52
update_atr_config.py Normal file
View File

@ -0,0 +1,52 @@
import sys
import os
# Add project root to path
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from backend.database.connection import db
def update_atr_config():
print("Updating ATR configuration...")
# 1. Update ATR_STOP_LOSS_MULTIPLIER
sql_check = "SELECT config_value FROM global_strategy_config WHERE config_key = 'ATR_STOP_LOSS_MULTIPLIER'"
try:
rows = db.execute_query(sql_check)
if rows:
print(f"Current ATR_STOP_LOSS_MULTIPLIER: {rows[0]['config_value']}")
sql_update = "UPDATE global_strategy_config SET config_value = '0.5' WHERE config_key = 'ATR_STOP_LOSS_MULTIPLIER'"
db.execute_update(sql_update)
print("Updated ATR_STOP_LOSS_MULTIPLIER to 0.5")
else:
sql_insert = "INSERT INTO global_strategy_config (config_key, config_value, config_type, category) VALUES ('ATR_STOP_LOSS_MULTIPLIER', '0.5', 'float', 'risk')"
db.execute_update(sql_insert)
print("Created ATR_STOP_LOSS_MULTIPLIER: 0.5")
except Exception as e:
print(f"Error updating ATR_STOP_LOSS_MULTIPLIER: {e}")
import traceback
traceback.print_exc()
# 2. Ensure USE_ATR_STOP_LOSS is True
try:
sql_check_use = "SELECT config_value FROM global_strategy_config WHERE config_key = 'USE_ATR_STOP_LOSS'"
rows_use = db.execute_query(sql_check_use)
if rows_use:
if rows_use[0]['config_value'] != 'true':
sql_update_use = "UPDATE global_strategy_config SET config_value = 'true' WHERE config_key = 'USE_ATR_STOP_LOSS'"
db.execute_update(sql_update_use)
print("Enabled USE_ATR_STOP_LOSS")
else:
sql_insert_use = "INSERT INTO global_strategy_config (config_key, config_value, config_type, category) VALUES ('USE_ATR_STOP_LOSS', 'true', 'boolean', 'risk')"
db.execute_update(sql_insert_use)
print("Created USE_ATR_STOP_LOSS: true")
except Exception as e:
print(f"Error updating USE_ATR_STOP_LOSS: {e}")
print("Database update complete.")
if __name__ == "__main__":
update_atr_config()

214
update_stop_loss_orders.py Normal file
View File

@ -0,0 +1,214 @@
import asyncio
import logging
import sys
import os
from pathlib import Path
from typing import List, Dict, Optional
# Add project root to sys.path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'trading_system'))
sys.path.insert(0, str(project_root / 'backend'))
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("UpdateSL")
# Import system modules
try:
from trading_system.binance_client import BinanceClient
from trading_system.risk_manager import RiskManager
from trading_system import config
from backend.database.connection import Database
from backend.security.crypto import decrypt_str
# Ensure config is loaded (defaults updated to 0.5)
from trading_system.config import _init_config_manager
_init_config_manager()
except ImportError as e:
logger.error(f"Import failed: {e}")
sys.exit(1)
async def process_account(account_id, name, api_key, api_secret):
logger.info(f"=== Processing Account {account_id}: {name} ===")
if not api_key or not api_secret:
logger.warning(f"Account {name} missing keys. Skipping.")
return
# Initialize Client with explicit keys
client = BinanceClient(api_key=api_key, api_secret=api_secret)
# Connect to Binance
try:
# Debug proxy settings
logger.info(f"Proxy Settings - HTTP: {os.environ.get('HTTP_PROXY')}, HTTPS: {os.environ.get('HTTPS_PROXY')}")
# Increase timeout to 60s and retries
await client.connect(timeout=60, retries=5)
except Exception as e:
logger.error(f"Failed to connect to Binance for account {name}: {e}")
return
# Initialize RiskManager
# Note: RiskManager uses global config for ATR settings.
# Since we updated config.py default ATR_STOP_LOSS_MULTIPLIER to 0.5,
# and account 1 DB config is also 0.5, this is safe.
risk_manager = RiskManager(client)
try:
atr_multiplier = float(config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 0.5))
logger.info(f"Target ATR Multiplier: {atr_multiplier}")
# Get Open Positions
try:
positions = await client.get_open_positions()
logger.info(f"Found {len(positions)} open positions.")
except Exception as e:
logger.error(f"Failed to get positions: {e}")
return
for position in positions:
symbol = position['symbol']
amt = float(position['positionAmt'])
if amt == 0:
continue
# Determine side from amount
if amt > 0:
real_side = 'BUY'
else:
real_side = 'SELL'
entry_price = float(position['entryPrice'])
mark_price = float(position['markPrice'])
leverage = int(position['leverage'])
quantity = abs(amt)
logger.info(f"Processing {symbol} ({real_side}): Entry={entry_price}, Mark={mark_price}, Amt={amt}")
# Fetch Klines for ATR
try:
# Get 1h klines for ATR calculation
klines = await client.get_klines(symbol, '1h', limit=100)
if not klines:
logger.warning(f"{symbol} - Failed to get klines, skipping.")
continue
new_stop_loss = risk_manager.get_stop_loss_price(
entry_price=entry_price,
side=real_side,
quantity=quantity,
leverage=leverage,
stop_loss_pct=None, # Force ATR usage
klines=klines
)
if not new_stop_loss:
logger.warning(f"{symbol} - Could not calculate new stop loss.")
continue
logger.info(f"{symbol} - New ATR Stop Loss Price: {new_stop_loss}")
# Check if triggered
triggered = False
if real_side == 'BUY':
if mark_price <= new_stop_loss:
triggered = True
else: # SELL
if mark_price >= new_stop_loss:
triggered = True
if triggered:
logger.warning(f"{symbol} - Price already beyond new stop loss! Executing MARKET CLOSE.")
try:
await client.place_order(
symbol=symbol,
side='SELL' if real_side == 'BUY' else 'BUY',
type='MARKET',
quantity=quantity,
reduceOnly=True
)
logger.info(f"{symbol} - Market Close Order Placed.")
await client.cancel_all_orders(symbol)
logger.info(f"{symbol} - Cancelled all open orders.")
except Exception as e:
logger.error(f"{symbol} - Failed to close position: {e}")
else:
# Update Stop Loss Order
logger.info(f"{symbol} - Updating Stop Loss Order to {new_stop_loss}")
# Cancel existing STOP_MARKET orders
try:
open_orders = await client.get_open_orders(symbol)
for order in open_orders:
if order['type'] == 'STOP_MARKET':
await client.cancel_order(symbol, order['orderId'])
logger.info(f"{symbol} - Cancelled old STOP_MARKET order {order['orderId']}")
except Exception as e:
logger.error(f"{symbol} - Failed to cancel orders: {e}")
# Place new STOP_MARKET order
try:
stop_side = 'SELL' if real_side == 'BUY' else 'BUY'
await client.place_order(
symbol=symbol,
side=stop_side,
type='STOP_MARKET',
stopPrice=new_stop_loss,
quantity=quantity,
reduceOnly=True,
workingType='MARK_PRICE'
)
logger.info(f"{symbol} - Placed new STOP_MARKET order at {new_stop_loss}")
except Exception as e:
logger.error(f"{symbol} - Failed to place new stop loss: {e}")
except Exception as e:
logger.error(f"Error processing {symbol}: {e}")
import traceback
traceback.print_exc()
finally:
await client.close()
async def main():
logger.info("Starting Multi-Account Stop Loss Update Script...")
db = Database()
with db.get_connection() as conn:
with conn.cursor() as cursor:
# Get active accounts
cursor.execute("SELECT id, name, api_key_enc, api_secret_enc FROM accounts WHERE status='active'")
accounts = cursor.fetchall()
logger.info(f"Found {len(accounts)} active accounts.")
for acc in accounts:
# Assuming fetchall returns dicts because Database uses DictCursor usually,
# or we check type. backend/database/connection.py ensures DictCursor if possible?
# Let's handle both dict and tuple/list just in case, but based on previous tools, it returns dicts.
# But wait, previous `check_atr_db` output showed dicts.
acc_id = acc['id']
name = acc['name']
api_key_enc = acc['api_key_enc']
api_secret_enc = acc['api_secret_enc']
api_key = decrypt_str(api_key_enc)
api_secret = decrypt_str(api_secret_enc)
await process_account(acc_id, name, api_key, api_secret)
logger.info("All accounts processed.")
if __name__ == "__main__":
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())