1
This commit is contained in:
parent
f8058083e3
commit
d97363e3d4
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
35
check_db.py
35
check_db.py
|
|
@ -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}")
|
||||
|
|
@ -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']}")
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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.0(2026-02-12:减少噪音止损,配合止盈拉远)
|
||||
'ATR_STOP_LOSS_MULTIPLIER': 0.5, # ATR止损倍数0.5(2026-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小时,过滤噪音
|
||||
|
|
|
|||
|
|
@ -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
52
update_atr_config.py
Normal 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
214
update_stop_loss_orders.py
Normal 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())
|
||||
Loading…
Reference in New Issue
Block a user