From d97363e3d44f52171328d8fdcb77c103b259c8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sat, 14 Feb 2026 01:27:47 +0800 Subject: [PATCH] 1 --- backend/config_manager.py | 2 +- check_account_status.py | 93 -------------- check_db.py | 35 ----- check_db_values.py | 24 ---- check_strange_symbols.py | 29 ----- check_trades.py | 52 -------- check_trades_today.py | 51 -------- trading_system/config.py | 6 +- trading_system/market_scanner.py | 47 +++++-- update_atr_config.py | 52 ++++++++ update_stop_loss_orders.py | 214 +++++++++++++++++++++++++++++++ 11 files changed, 305 insertions(+), 300 deletions(-) delete mode 100644 check_account_status.py delete mode 100644 check_db.py delete mode 100644 check_db_values.py delete mode 100644 check_strange_symbols.py delete mode 100644 check_trades.py delete mode 100644 check_trades_today.py create mode 100644 update_atr_config.py create mode 100644 update_stop_loss_orders.py diff --git a/backend/config_manager.py b/backend/config_manager.py index 50e7527..a2c9f74 100644 --- a/backend/config_manager.py +++ b/backend/config_manager.py @@ -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() diff --git a/check_account_status.py b/check_account_status.py deleted file mode 100644 index ef3bc9f..0000000 --- a/check_account_status.py +++ /dev/null @@ -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()) diff --git a/check_db.py b/check_db.py deleted file mode 100644 index 4c0911b..0000000 --- a/check_db.py +++ /dev/null @@ -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}") diff --git a/check_db_values.py b/check_db_values.py deleted file mode 100644 index c8bc52a..0000000 --- a/check_db_values.py +++ /dev/null @@ -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']}") diff --git a/check_strange_symbols.py b/check_strange_symbols.py deleted file mode 100644 index ffe9afd..0000000 --- a/check_strange_symbols.py +++ /dev/null @@ -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() diff --git a/check_trades.py b/check_trades.py deleted file mode 100644 index 2660a9a..0000000 --- a/check_trades.py +++ /dev/null @@ -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() diff --git a/check_trades_today.py b/check_trades_today.py deleted file mode 100644 index dc2fc87..0000000 --- a/check_trades_today.py +++ /dev/null @@ -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() diff --git a/trading_system/config.py b/trading_system/config.py index 235770d..b43d24e 100644 --- a/trading_system/config.py +++ b/trading_system/config.py @@ -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小时,过滤噪音 diff --git a/trading_system/market_scanner.py b/trading_system/market_scanner.py index 12c6214..35ee3fd 100644 --- a/trading_system/market_scanner.py +++ b/trading_system/market_scanner.py @@ -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]: """ diff --git a/update_atr_config.py b/update_atr_config.py new file mode 100644 index 0000000..05bfc1d --- /dev/null +++ b/update_atr_config.py @@ -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() diff --git a/update_stop_loss_orders.py b/update_stop_loss_orders.py new file mode 100644 index 0000000..1065190 --- /dev/null +++ b/update_stop_loss_orders.py @@ -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())