202 lines
8.1 KiB
Python
202 lines
8.1 KiB
Python
"""
|
||
主程序 - 币安自动交易系统入口
|
||
"""
|
||
import asyncio
|
||
import logging
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
# 支持直接运行和作为模块导入
|
||
if __name__ == '__main__':
|
||
# 直接运行时,使用相对导入
|
||
from binance_client import BinanceClient
|
||
from market_scanner import MarketScanner
|
||
from risk_manager import RiskManager
|
||
from position_manager import PositionManager
|
||
from strategy import TradingStrategy
|
||
import config
|
||
else:
|
||
# 作为模块导入时,使用绝对导入
|
||
from .binance_client import BinanceClient
|
||
from .market_scanner import MarketScanner
|
||
from .risk_manager import RiskManager
|
||
from .position_manager import PositionManager
|
||
from .strategy import TradingStrategy
|
||
from . import config
|
||
|
||
# 配置日志(支持相对路径)
|
||
log_file = config.LOG_FILE
|
||
if not Path(log_file).is_absolute():
|
||
# 如果是相对路径,相对于项目根目录
|
||
project_root = Path(__file__).parent.parent
|
||
log_file = project_root / log_file
|
||
|
||
logging.basicConfig(
|
||
level=getattr(logging, config.LOG_LEVEL),
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler(str(log_file), encoding='utf-8'),
|
||
logging.StreamHandler(sys.stdout)
|
||
]
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def main():
|
||
"""主函数"""
|
||
logger.info("=" * 60)
|
||
logger.info("币安自动交易系统启动")
|
||
logger.info("=" * 60)
|
||
|
||
# 检查配置管理器状态
|
||
logger.info("检查配置管理器状态...")
|
||
if config.USE_DB_CONFIG:
|
||
logger.info("✓ 使用数据库配置")
|
||
# 尝试重新加载配置(确保获取最新值)
|
||
try:
|
||
config.reload_config()
|
||
logger.info("配置已重新加载")
|
||
except Exception as e:
|
||
logger.warning(f"重新加载配置失败: {e}")
|
||
else:
|
||
logger.warning("⚠ 未使用数据库配置,将使用环境变量和默认配置")
|
||
logger.warning("如果前端已配置API密钥,请检查:")
|
||
logger.warning("1. backend目录是否存在")
|
||
logger.warning("2. 数据库连接是否正常")
|
||
logger.warning("3. config_manager模块是否可以正常导入")
|
||
|
||
# 检查API密钥(重新获取,确保是最新值)
|
||
api_key = config._get_config_value('BINANCE_API_KEY', '')
|
||
api_secret = config._get_config_value('BINANCE_API_SECRET', '')
|
||
|
||
logger.info(f"API密钥检查: KEY存在={bool(api_key)}, SECRET存在={bool(api_secret)}")
|
||
|
||
if not api_key or not api_secret:
|
||
logger.error("=" * 60)
|
||
logger.error("API密钥未配置!")
|
||
logger.error("=" * 60)
|
||
if config.USE_DB_CONFIG:
|
||
logger.error("配置管理器已启用,但未从数据库读取到API密钥")
|
||
logger.error("请检查:")
|
||
logger.error("1. 前端配置界面是否已设置BINANCE_API_KEY和BINANCE_API_SECRET")
|
||
logger.error("2. 数据库trading_config表中是否有这些配置项")
|
||
logger.error("3. 数据库连接是否正常")
|
||
else:
|
||
logger.error("请设置 BINANCE_API_KEY 和 BINANCE_API_SECRET 环境变量")
|
||
logger.error("或在 config.py 中直接配置")
|
||
logger.error("或确保backend目录存在,以便从数据库读取配置")
|
||
logger.error("=" * 60)
|
||
return
|
||
|
||
# 更新config模块的API密钥(确保使用最新值)
|
||
config.BINANCE_API_KEY = api_key
|
||
config.BINANCE_API_SECRET = api_secret
|
||
|
||
# 初始化组件
|
||
client = None
|
||
try:
|
||
# 1. 初始化币安客户端
|
||
logger.info("初始化币安客户端...")
|
||
|
||
# 再次确认API密钥(使用最新值)
|
||
api_key = config._get_config_value('BINANCE_API_KEY', config.BINANCE_API_KEY)
|
||
api_secret = config._get_config_value('BINANCE_API_SECRET', config.BINANCE_API_SECRET)
|
||
use_testnet = config._get_config_value('USE_TESTNET', config.USE_TESTNET)
|
||
if isinstance(use_testnet, str):
|
||
use_testnet = use_testnet.lower() in ('true', '1', 'yes', 'on')
|
||
elif not isinstance(use_testnet, bool):
|
||
use_testnet = bool(use_testnet)
|
||
|
||
logger.info(f"测试网模式: {use_testnet}")
|
||
logger.info(f"连接超时: {config.CONNECTION_TIMEOUT}秒")
|
||
logger.info(f"重试次数: {config.CONNECTION_RETRIES}次")
|
||
|
||
client = BinanceClient(
|
||
api_key=api_key,
|
||
api_secret=api_secret,
|
||
testnet=use_testnet
|
||
)
|
||
await client.connect()
|
||
|
||
# 2. 检查账户余额
|
||
logger.info("检查账户余额...")
|
||
balance = await client.get_account_balance()
|
||
|
||
if balance['total'] == 0 and balance['available'] == 0:
|
||
logger.error("=" * 60)
|
||
logger.error("无法获取账户余额,可能是API权限问题")
|
||
logger.error("请检查:")
|
||
logger.error("1. API密钥是否正确")
|
||
logger.error("2. API密钥是否启用了'合约交易'权限")
|
||
logger.error("3. IP地址是否在白名单中(如果设置了IP限制)")
|
||
logger.error("4. 测试网/生产网环境是否匹配")
|
||
logger.error("=" * 60)
|
||
return
|
||
|
||
logger.info(
|
||
f"账户余额: 总余额 {balance['total']:.2f} USDT, "
|
||
f"可用余额 {balance['available']:.2f} USDT"
|
||
)
|
||
|
||
if balance['available'] <= 0:
|
||
logger.error("账户可用余额不足,无法交易")
|
||
logger.error(f"总余额: {balance['total']:.2f} USDT")
|
||
logger.error("请先充值到合约账户")
|
||
return
|
||
|
||
|
||
# 4. 初始化各个模块
|
||
logger.info("初始化交易模块...")
|
||
scanner = MarketScanner(client)
|
||
risk_manager = RiskManager(client)
|
||
position_manager = PositionManager(client, risk_manager)
|
||
|
||
# 初始化推荐器(用于自动生成推荐)
|
||
recommender = None
|
||
try:
|
||
from trade_recommender import TradeRecommender
|
||
recommender = TradeRecommender(client, scanner, risk_manager)
|
||
logger.info("✓ 推荐器已初始化,将自动生成交易推荐")
|
||
except Exception as e:
|
||
logger.warning(f"⚠ 推荐器初始化失败: {e},将不生成推荐")
|
||
logger.debug(f"错误详情: {type(e).__name__}: {e}")
|
||
|
||
strategy = TradingStrategy(client, scanner, risk_manager, position_manager, recommender=recommender)
|
||
|
||
# 4. 打印配置信息
|
||
logger.info("交易配置:")
|
||
logger.info(f" 单笔最大仓位: {config.TRADING_CONFIG['MAX_POSITION_PERCENT']*100:.1f}%")
|
||
logger.info(f" 总仓位上限: {config.TRADING_CONFIG['MAX_TOTAL_POSITION_PERCENT']*100:.1f}%")
|
||
logger.info(f" 最小涨跌幅阈值: {config.TRADING_CONFIG['MIN_CHANGE_PERCENT']:.1f}%")
|
||
logger.info(f" 扫描间隔: {config.TRADING_CONFIG['SCAN_INTERVAL']} 秒")
|
||
logger.info(f" 扫描交易对数量: {config.TRADING_CONFIG.get('MAX_SCAN_SYMBOLS', 500)} (0=全部)")
|
||
logger.info(f" 处理交易对数量: {config.TRADING_CONFIG['TOP_N_SYMBOLS']} 个")
|
||
logger.info(f" 止损: {config.TRADING_CONFIG['STOP_LOSS_PERCENT']*100:.1f}%")
|
||
logger.info(f" 止盈: {config.TRADING_CONFIG['TAKE_PROFIT_PERCENT']*100:.1f}%")
|
||
logger.info(f" 测试网模式: {config.USE_TESTNET}")
|
||
logger.info("=" * 60)
|
||
|
||
# 5. 启动交易策略
|
||
logger.info("启动交易策略...")
|
||
await strategy.execute_strategy()
|
||
|
||
except KeyboardInterrupt:
|
||
logger.info("收到停止信号,正在关闭...")
|
||
except Exception as e:
|
||
logger.error(f"程序运行出错: {e}", exc_info=True)
|
||
finally:
|
||
# 清理资源
|
||
if client:
|
||
await client.disconnect()
|
||
logger.info("程序已退出")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
try:
|
||
asyncio.run(main())
|
||
except KeyboardInterrupt:
|
||
logger.info("程序被用户中断")
|
||
except Exception as e:
|
||
logger.error(f"程序异常退出: {e}", exc_info=True)
|