2
关注
19
关注者

量化分析助力网格策略(二):网格数和收益率关系

创建于: 2025-02-13 16:22:36, 更新于: 2025-02-17 17:38:52
comments   0
hits   339

量化分析助力网格策略(二):网格数和收益率关系

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

网格数设置的挑战

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

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

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

网格数的设置依据

1. 网格间隔与资金分配

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

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

2. 账户资金与风险管理

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

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

网格交易策略实现

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

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

1. 获取数据

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

# 获取目标币种的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,我们需要在每次交易时计算并累计手续费。

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),我们可以评估哪些网格数在当前市场环境下能够带来最好的收益。以下是运行回测的代码:

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. 实验结果分析

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

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

总结

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

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

更多内容