#量化交易 #策略研究 #Python学习 #DeepSeek --- 这是我现在写的一个关于数字货币的一个交易脚本。请帮我完善:一是因为网络原因,如果中断执行,加入重新运行的异常处理模块。 ![[My_strategy.py]] ```python import time import pandas as pd import ccxt from datetime import timedelta # import pandas_ta import talib # ==================================================================================================== # =====格式设置 # ==================================================================================================== pd.set_option('display.max_rows', 1000) pd.set_option('expand_frame_repr', False) # 当列太多时不换行 # 设置命令行输出时的列对齐功能 pd.set_option('display.unicode.ambiguous_as_wide', True) pd.set_option('display.unicode.east_asian_width', True) # ==================================================================================================== # =====创建ccxt交易所 # ==================================================================================================== BINANCE_CONFIG = { 'apiKey': '', 'secret': '', 'proxies': {'http': '127.0.0.1:7890', 'https': '127.0.0.1:7890'} } exchange = ccxt.binance(BINANCE_CONFIG) while True: # list = ['DOGEUSDT', 'ETHUSDT', 'BTCUSDT'] # # # for symbol in list: symbol = 'ETHUSDT' time_interval = '1m' # 其他可以尝试的值:'1m', '5m', '15m', '30m', '1h', '2h', '1d', '1w', '1M', '1y',并不是每个交易所都支持 bar_num = 1000 # 获取K线的数量 params = {'symbol': symbol, # 交易币对 'interval': time_interval, # 时间间隔 'limit': bar_num} # 数据条数 # ==================================================================================================== # =====获取K线数据 # ==================================================================================================== response = exchange.fapiPublicGetKlines(params=params) k_lines = pd.DataFrame(response) # print(k_lines) # =====整理K线数据 df = pd.DataFrame(response, dtype=float) # 将数据转换为dataframe df.rename(columns={0: 'MTS', 1: 'Open', 2: 'High', 3: 'Low', 4: 'Close', 5: 'Volume'}, inplace=True) # 重命名 df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms') # 整理时间 df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8) # 北京时间 df = df[['candle_begin_time_GMT8', 'Open', 'High', 'Low', 'Close', 'Volume']] # 整理列的顺序 # ==================================================================================================== # =====获取币对的最新价格 # ==================================================================================================== data = exchange.fapiPublicGetTickerPrice(params={'symbol': "ETHUSDT"}) price_ETH = data['price'] # ==================================================================================================== # =====通过pandas-ta计算指标并加入相关指标计算列 # ==================================================================================================== ## 计算均线 # df.ta.sma(length=5, append=True, col_names="SMA_5") # pandas-ta 计算SM5指标 df['MA5'] = talib.MA(df['Close'], timeperiod=5) # ta-lib计算MA5指标 # # 计算14日相对强弱指数(RSI) # df.ta.rsi(length=14, append=True, col_names="RSI_14") # pandas-ta计算RSI指标 df['RSI_14'] = talib.RSI(df['Close'], timeperiod=14) # ta-lib计算RSI指标 # # 计算MACD(12/26/9周期) # df.ta.macd(fast=12, slow=26, signal=9, append=True) # 默认列名:MACD_12_26_9, MACDs_12_26_9, MACDh_12_26_9 [2,7](@ref) # # 计算布林带(20日,2倍标准差) # df.ta.bbands(length=20, std=2, append=True) # pandas-ta计算布林带指标 # 默认列名:BBL_20_2.0, BBM_20_2.0, BBU_20_2.0 ## ta-lib 计算布林带指标 upper_band, middle_band, lower_band = talib.BBANDS( df['Close'], timeperiod=20, nbdevup=2, nbdevdn=2, matype=0 # SMA ) df['BB_Upper'] = upper_band df['BB_Middle'] = middle_band df['BB_Lower'] = lower_band ## 计算ADX指标 # df.ta.adx(length=20, append=True) # pandas-ta计算adx df['ADX_14'] = talib.ADX(df['High'], df['Low'], df['Close'], timeperiod=14) # ta-lib计算ADX指标 ## 计算ATR指标 # df.ta.atr(length=16, append=True) # pandas-ta计算atr,默认参数14,length也可以调整 df['ATR_14'] = talib.ATR(df['High'], df['Low'], df['Close'], timeperiod=14) print(df) # df.to_csv('Ta4.csv') # ==================================================================================================== # =====设置下单条件并执行 # ==================================================================================================== # if float(price_ETH) > float(df['Close'].iloc[-2]): # print('价格上升') # else: # print('价格下降') # # print(price_ETH) # print(df['Close'].iloc[-2]) # print(df['RSI_14'].iloc[-2] > 30) # 以上为dataframe取数格式的测试,目前使用还不太熟练 ## 调整杠杆倍率 # params = {'symbol': 'ETHUSDT', # 交易币对 # 'leverage': 10, # 'timestamp': int(time.time() * 1000)} # leverage = exchange.fapiPrivatePostLeverage(params=params) # print('调整开仓杠杆\n', leverage, '\n') ## 下单操作 num = 0 max_num = 3 # 使用变量来控制下单次数 params = {'side': 'BUY', 'symbol': 'ETHUSDT', # 交易币对 'type': 'LIMIT', # 'price': price_ETH, 'price': df['Close'].iloc[-1], # 下单价格,在限价单的时候 # 启用,将type换成LIMIT # 此处用变量取代了课程中的数值。以后下单的价格可能需要复杂计算再储存在变量中,做测试。 'quantity': 0.01, # 下单数量 'timestamp': int(time.time() * 1000), 'timeInForce': 'GTC'} # https://academy.binance.com/zh/articles/understanding-the-different-order-types if df['RSI_14'].iloc[-2] > 30 and df['RSI_14'].iloc[-1] < 30: print('向下穿越rsi30,执行买入') response = exchange.fapiPrivatePostOrder(params=params) order_info = pd.DataFrame(response, index=['symbol']) order_id = order_info['orderId'].iloc[0] print('下单\n', order_info, '\n') num += 1 # 此后这里还可以多个条件,组合多个指标的值 # RSI的使用作为一个参考 # 通过现有数据(开高收低量),已经可以计算出不少指标了。 else: print('不符合交易条件,监控中') time.sleep(30) if num >= max_num: break ## 这是我在办公室电脑上敲的注释 ## 测试能否通过iCloud同步成功,在家也可已经iCloud对同一个项目进行编辑。 ``` --- 我将为您的数字货币交易脚本添加异常处理模块,确保在网络中断或API错误时能够自动重试。以下是完善后的代码,重点增强了健壮性和可靠性: ```python import time import pandas as pd import ccxt from datetime import timedelta import talib import logging import random import os # ==================================================================================================== # =====配置日志系统 # ==================================================================================================== logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('crypto_trading.log'), logging.StreamHandler() ] ) logger = logging.getLogger('CryptoTrader') # ==================================================================================================== # =====格式设置 # ==================================================================================================== pd.set_option('display.max_rows', 1000) pd.set_option('expand_frame_repr', False) pd.set_option('display.unicode.ambiguous_as_wide', True) pd.set_option('display.unicode.east_asian_width', True) # ==================================================================================================== # =====创建ccxt交易所 # ==================================================================================================== def create_exchange(): BINANCE_CONFIG = { 'apiKey': os.getenv('BINANCE_API_KEY', ''), 'secret': os.getenv('BINANCE_API_SECRET', ''), 'proxies': {'http': '127.0.0.1:7890', 'https': '127.0.0.1:7890'}, 'options': { 'defaultType': 'future', 'adjustForTimeDifference': True }, 'timeout': 15000, # 增加超时时间 'enableRateLimit': True # 启用速率限制 } return ccxt.binance(BINANCE_CONFIG) # ==================================================================================================== # =====重试装饰器 # ==================================================================================================== def retry(max_attempts=5, delay=2, backoff=2, exceptions=(Exception,)): """自动重试装饰器""" def decorator(func): def wrapper(*args, **kwargs): attempts = 0 current_delay = delay while attempts < max_attempts: try: return func(*args, **kwargs) except exceptions as e: attempts += 1 logger.warning(f"操作失败: {str(e)}. 尝试 #{attempts}/{max_attempts}. {current_delay}秒后重试...") # 指数退避 + 随机抖动 sleep_time = current_delay + random.uniform(0, 1) time.sleep(sleep_time) current_delay *= backoff logger.error(f"操作失败 {max_attempts} 次后放弃") raise return wrapper return decorator # ==================================================================================================== # =====核心函数 # ==================================================================================================== @retry(max_attempts=5, delay=3, backoff=2, exceptions=(ccxt.NetworkError, ccxt.ExchangeNotAvailable)) def fetch_klines(exchange, symbol, time_interval, bar_num): """获取K线数据""" params = { 'symbol': symbol, 'interval': time_interval, 'limit': bar_num } response = exchange.fapiPublicGetKlines(params=params) return pd.DataFrame(response, dtype=float) @retry(max_attempts=5, delay=2, backoff=2, exceptions=(ccxt.NetworkError, ccxt.ExchangeNotAvailable)) def fetch_price(exchange, symbol): """获取最新价格""" data = exchange.fapiPublicGetTickerPrice(params={'symbol': symbol}) return float(data['price']) @retry(max_attempts=3, delay=5, backoff=2, exceptions=(ccxt.NetworkError, ccxt.ExchangeNotAvailable)) def place_order(exchange, params): """下订单""" return exchange.fapiPrivatePostOrder(params=params) # ==================================================================================================== # =====数据处理函数 # ==================================================================================================== def process_data(df): """处理K线数据并计算技术指标""" # 重命名列 df.rename(columns={ 0: 'MTS', 1: 'Open', 2: 'High', 3: 'Low', 4: 'Close', 5: 'Volume' }, inplace=True) # 处理时间 df['candle_begin_time'] = pd.to_datetime(df['MTS'], unit='ms') df['candle_begin_time_GMT8'] = df['candle_begin_time'] + timedelta(hours=8) # 保留必要列 df = df[['candle_begin_time_GMT8', 'Open', 'High', 'Low', 'Close', 'Volume']] # 计算技术指标 df['MA5'] = talib.MA(df['Close'], timeperiod=5) df['RSI_14'] = talib.RSI(df['Close'], timeperiod=14) # 计算布林带 upper_band, middle_band, lower_band = talib.BBANDS( df['Close'], timeperiod=20, nbdevup=2, nbdevdn=2, matype=0 ) df['BB_Upper'] = upper_band df['BB_Middle'] = middle_band df['BB_Lower'] = lower_band # 计算ADX和ATR df['ADX_14'] = talib.ADX(df['High'], df['Low'], df['Close'], timeperiod=14) df['ATR_14'] = talib.ATR(df['High'], df['Low'], df['Close'], timeperiod=14) return df # ==================================================================================================== # =====主交易逻辑 # ==================================================================================================== def main_trading_loop(): """主交易循环""" exchange = create_exchange() logger.info("交易所连接成功") symbol = 'ETHUSDT' time_interval = '1m' bar_num = 1000 max_num = 3 num = 0 while num < max_num: try: # 获取数据 kline_df = fetch_klines(exchange, symbol, time_interval, bar_num) df = process_data(kline_df) # 获取最新价格 price_ETH = fetch_price(exchange, symbol) logger.info(f"ETH当前价格: {price_ETH}") # 交易条件判断 if len(df) >= 2: # 确保有足够的数据 rsi_prev = df['RSI_14'].iloc[-2] rsi_current = df['RSI_14'].iloc[-1] if rsi_prev > 30 and rsi_current < 30: logger.info("向下穿越RSI30,执行买入") # 准备订单参数 order_params = { 'side': 'BUY', 'symbol': symbol, 'type': 'LIMIT', 'price': df['Close'].iloc[-1], 'quantity': 0.01, 'timestamp': int(time.time() * 1000), 'timeInForce': 'GTC' } # 下订单 response = place_order(exchange, order_params) logger.info(f"订单执行成功: {response}") num += 1 else: logger.info("不符合交易条件,继续监控") time.sleep(30) # 常规等待 else: logger.warning("数据不足,跳过本次循环") time.sleep(10) except ccxt.BaseError as e: logger.error(f"交易所API错误: {str(e)}") # 交易所相关错误,可能重新创建连接 exchange = create_exchange() logger.info("已重新创建交易所连接") time.sleep(10) except Exception as e: logger.error(f"意外错误: {str(e)}", exc_info=True) time.sleep(30) except KeyboardInterrupt: logger.info("用户中断,程序退出") break # ==================================================================================================== # =====程序入口 # ==================================================================================================== if __name__ == "__main__": logger.info("程序启动") while True: try: main_trading_loop() logger.info("交易次数达到上限,程序正常退出") break except Exception as e: logger.critical(f"主循环崩溃: {str(e)}", exc_info=True) logger.info("30秒后重启主循环...") time.sleep(30) ``` ### 主要改进说明: 1. ​**异常处理架构**​: - 三层异常处理:函数级、主循环级、程序级 - 使用装饰器自动重试网络操作 - 针对不同错误类型采取不同恢复策略 2. ​**健壮性增强**​: - 指数退避 + 随机抖动重试机制 - 交易所连接重建功能 - 数据完整性检查(确保有足够数据) - 速率限制和超时设置 3. ​**日志系统**​: - 详细记录所有操作和错误 - 同时输出到文件和终端 - 包含时间戳和日志级别 4. ​**配置优化**​: - 使用环境变量存储API密钥 - 增加API超时时间 - 启用ccxt内置速率限制 5. ​**代码结构**​: - 模块化设计,分离数据获取、处理和交易逻辑 - 函数单一职责原则 - 清晰的错误处理流程 ### 使用建议: 1. 将API密钥存储在环境变量中更安全: ``` export BINANCE_API_KEY='your_api_key' export BINANCE_API_SECRET='your_api_secret' ``` 2. 监控日志文件`crypto_trading.log`了解程序运行情况 3. 对于高频交易,考虑: - 增加`bar_num`减少API调用频率 - 使用WebSocket替代轮询获取实时数据 - 添加仓位管理和风险管理模块 这个完善版本能够在网络中断、API限制或临时错误后自动恢复,大大提高了脚本的稳定性和可靠性。