输入/搜索内容
2
关注
480
关注者
量化分析助力网格策略(二):网格数和收益率关系
原创教程
创建于 2025-02-13 16:22:36  更新于 2025-02-17 17:38:52
 0
 1652

img

在上一篇文章中,我们使用量化分析的方法通过振幅和涨跌幅对币种进行了初步筛选。接下来,我们将探讨网格交易中的一个关键因素——网格数的设置。网格数决定了交易的频率和每次交易的资金量,进而影响网格交易的总收益和风险水平。因此,如何合理设置网格数以达到最佳的平衡点,是每个网格交易者都需要考虑的重要问题。

网格数设置的挑战

  • 网格数过少:如果网格数设置得过少,网格之间的间隔较大,意味着价格需要较大的波动才会触及下一网格。虽然每个网格的交易额较大,可以捕获较大的波动利润,但由于交易机会较少,可能错失一些较小波动带来的盈利。因此,整体盈利能力可能低于预期。

  • 网格数过多:当网格数设置得过多时,每个网格的价格区间较小,交易机会增多,能够更频繁地进行买卖。然而,由于每次交易的资金较小,这样的策略往往需要高频的交易来获取利润。这就容易导致较高的手续费支出,并且过于频繁的交易可能使得市场的小幅波动成为主要盈利来源,收益的上限有限。

  • 合理的网格数:合适的网格数需要综合考虑市场的波动性、账户资金规模以及期望的交易频率。市场波动性较大时,适当增加网格数可以更好地捕捉波动,而资金较多时,设置较少的网格数可能带来更高的单次交易额并减少手续费压力。通过平衡网格间距和频繁交易的成本,可以最大化策略的收益与风险控制。

网格数的设置依据

1. 网格间隔与资金分配

网格交易的一个核心特点是通过设定多个网格区间,将资金分配到每个区间。在确定网格数时,首先需要计算每个网格的间隔和每个网格的资金量。网格数的设置不仅影响到每个网格的资金量,还决定了每个网格的买卖价格区间。

  • 网格间隔:网格过于密集时,虽然交易机会更多,但每次交易的资金量较小,可能导致总收益的上限较低;相反,网格过于稀疏时,虽然每次交易的资金量较大,但由于成交频率低,可能错失盈利机会。
  • 资金分配:在设置网格数时,我们需要平衡资金分配,避免过多的资金分散在每个网格中,无法有效获取盈利。

2. 账户资金与风险管理

账户资金是决定网格数的重要因素。较大的账户资金允许设置更多的网格,而较小的账户资金则需要限制网格数,以避免资金分配过于分散,导致每个网格的资金量过小,无法有效盈利。

此外,网格交易的风险管理策略也需要考虑到网格数的设置。尤其在市场剧烈波动时,过多的网格可能导致更多的亏损,因此要合理控制网格数,以避免过度交易。

网格交易策略实现

在实际的网格交易策略应用中,特别是在优化网格数和收益率之间的平衡时,【数据探索】模块的DQL(DATA Query Language)提供了极大的灵活性。DQL不仅支持高效的数据查询、处理和分析,还能帮助交易者模拟和回测网格交易策略的表现,以便找到最适合的网格数和其它参数配置。

通过DQL,我们可以轻松地从多个交易所(如Binance)获取历史K线数据,并根据这些数据调整网格交易策略。这样可以根据不同市场环境和具体币种的波动情况,精确地筛选出最优的网格交易策略。

1. 获取数据

在开始网格交易策略的回测之前,我们首先需要获取目标币种的市场数据。以下是从数据库查询指定币种的K线数据的代码:

dql
# 获取目标币种的K线数据 data = query("select * from klines.spot_1d where Exchange = 'Binance' and Symbol = '???_usdt' order by Time")

说明:

  • 通过查询数据库,我们可以获得特定币种(例如 ???_usdt)在Binance交易平台的日线数据(包括时间、开盘价、最高价、最低价和收盘价等)。这为策略执行提供基础数据。

2. 网格策略函数

网格交易策略的核心在于利用预设的网格数量和价格区间进行交易。具体的步骤如下:

  1. 计算网格间隔(gap)与每格交易额(notional)

    • 网格间隔(gap):根据市场的最高价和最低价之间的比例来计算。公式如下:
      \[ \text{gap} = \frac{\log(\text{max_p} / \text{min_p})}{\text{grid_num}} \]
      其中,max_p为最高价,min_p为最低价,grid_num为网格数量。

    • 每格交易额(notional):每个网格的交易额通过总资金和网格数来计算。公式为:
      \[ \text{notional} = \frac{\text{balance}}{\text{grid_num}} \]

  2. 网格起始与结束价格

    • 每个网格的起始价格和结束价格是基于网格间隔动态计算的。例如,第一网格的起始价格是最低价格,第二网格的起始价格是最低价格乘以 exp(gap),依此类推。
  3. 交易操作

    • 开盘条件:当价格下跌到某个网格的起始价格时,执行买入操作。
    • 平仓条件:当价格上涨到某个网格的结束价格时,执行卖出操作。
  4. 交易费用:假设每笔交易的手续费为0.0001,我们需要在每次交易时计算并累计手续费。

dql
def grid_trading_strategy( raw, grid_num, # 网格数量 min_p, # 最低价格 max_p, # 最高价格 ): """ 执行网格交易策略的函数 """ # 初始化变量 balance = 1000 # 初始资金 raw = raw[['Time', 'Open', 'High', 'Low', 'Close']] # 只选择相关列 # 网格交易策略的设置 gap = math.log(max_p / min_p) / grid_num # 计算网格间隔 notional = balance / grid_num # 每个网格的交易额 # 初始化网格 net = [] for i in range(grid_num): net.append({ 'start_p': min_p * math.exp(i * gap), # 每个网格的起始价格 'end_p': min_p * math.exp((i + 1) * gap), # 每个网格的结束价格 'amt': notional / (min_p * math.exp(i * gap)), # 每个网格的购买量 'status': 'idle' # 初始状态为闲置 }) # 记录状态 state = { 'stock': 0, # 当前持仓 'fee': 0, # 交易费用 'longTradeVol': 0, # 长期交易量 'shortTradeVol': 0, # 短期交易量 'profitTbl': [], # 存储每个时刻的利润 'feeTbl': [], # 存储每个时刻的费用 'netCnt': 0, # 记录净交易次数 'idx': 0 # 当前数据的索引 } # 检查开盘交易 def check_open_orders(state, net): for i in range(len(net)): if net[i]['status'] == 'idle' and raw['Low'][state['idx']] <= net[i]['start_p'] and raw['Open'][state['idx']] > net[i]['start_p']: net[i]['status'] = 'taken' # 网格被占用 tradeVol = net[i]['amt'] * net[i]['start_p'] state['stock'] += net[i]['amt'] state['longTradeVol'] += tradeVol state['fee'] += tradeVol * 0.0001 # 假设手续费为0.0001 # 检查平仓交易 def check_close_orders(state, net): for i in range(len(net)): if net[i]['status'] == 'taken' and raw['High'][state['idx']] >= net[i]['end_p'] and raw['Open'][state['idx']] < net[i]['end_p']: net[i]['status'] = 'idle' # 网格状态恢复为空闲 tradeVol = net[i]['amt'] * net[i]['end_p'] state['stock'] -= net[i]['amt'] state['shortTradeVol'] += tradeVol state['fee'] += tradeVol * 0.0001 # 假设手续费为0.0001 state['netCnt'] += 1 # 日志记录利润和费用 def log(state): addVol = state['stock'] * raw['Close'][state['idx']] # 当前仓位的总价值 pl = state['shortTradeVol'] - state['longTradeVol'] + addVol # 计算利润 state['profitTbl'].append(pl) state['feeTbl'].append(state['fee']) # 主交易循环 for i in range(len(raw)): state['idx'] = i if raw['Close'][state['idx']] >= raw['Open'][state['idx']]: check_open_orders(state, net) check_close_orders(state, net) else: check_close_orders(state, net) check_open_orders(state, net) log(state) # 将利润和费用数据整理为DataFrame pl = DataFrame({'pl' : state['profitTbl'], 'fee' : state['feeTbl']}) pl['time'] = raw['Time'] pl['pl-net'] = pl['pl'] - pl['fee'] return pl

3. 运行回测

针对目标币种,我们选择'oax_usdt'币种,经过上篇代码检验,该币种在较长一段时间内,保持较高的振幅,又不呈现显著的单边趋势;我们可以尝试使用不同的网格数(例如:5、10、15等)进行模拟回测,查看不同网格数的效果,进而找到适合的网格数。例如,通过计算每个网格数下的净利润(pl-net),我们可以评估哪些网格数在当前市场环境下能够带来最好的收益。以下是运行回测的代码:

dql
grid_nums = [5*i+5 for i in range(5)] out = [] for g in grid_nums: pl = grid_trading_strategy( data, grid_num=g, min_p=min(data['Close']), max_p=max(data['Close']) ) out.append({ 'num': g, 'pl-net': pl['pl-net'][-1], 'min_pl': min(pl['pl-net']), 'max_pl': max(pl['pl-net']) }) return out

4. 实验结果分析

经过不同网格数量配置下的回测,我们得到了以下结果:

(chart/e8388af0-1055-4a3c-98d0-43e6987545ef)

分析:

  • 随着网格数量的增加,净收益呈现先增加后减少的趋势。当网格数为15时,净收益达到了最大值。继续增加网格数后,净收益出现下降。
  • 最小收益和最大收益的波动与净收益趋势相似,表明网格数的选择对收益有重要影响。

总结

合理设置网格数是网格交易策略中的一项重要任务。通过网格数的优化,可以有效提高网格交易策略的收益表现,并更好地控制风险。本文通过对网格数设置的分析,提供了具体的计算方法和示例代码,希望可以帮助大家优化网格交易策略,提高策略的稳定性和盈利性。

注:
本文网格回测代码改编自知乎大神
Halcyon,源码解释请参考文章算法交易实战日记(十八) —— 网格交易中的细节:网格数与长期收益率的关系

相关推荐
评论
全部评论 (0)
暂无数据
暂无数据
  • 1
iPhone 下载
社区
回测系统
© 2015 - ∞ INVENTOR PTE LTD (SG)