이전 기사에서는 DATADATA 플랫폼에서 진폭과 상승 및 하락을 기준으로 통화에 대한 예비 검토를 수행했습니다. 다음으로, 그리드 거래의 핵심 요소인 그리드 수 설정에 대해 살펴보겠습니다. 그리드의 수는 거래 빈도와 각 거래에 필요한 자금 규모를 결정하며, 이는 그리드 거래의 총 수익과 위험 수준에 영향을 미칩니다. 따라서 최적의 균형점을 달성하기 위해 그리드 수를 합리적으로 설정하는 방법은 모든 그리드 거래자가 고려해야 할 중요한 문제입니다.
그리드가 너무 적습니다: 그리드의 개수를 너무 작게 설정하면 그리드 간 간격이 커지게 되며, 이는 다음 그리드에 도달하기 위해 가격 변동이 더 커야 한다는 것을 의미합니다. 각 그리드의 거래 금액이 더 크고 더 큰 변동 수익을 포착할 수 있지만, 거래 기회가 적기 때문에 작은 변동으로 얻는 수익 중 일부가 놓칠 수 있습니다. 따라서 전반적인 수익성은 예상보다 낮을 수 있습니다.
그리드가 너무 많습니다: 그리드의 개수를 너무 많이 설정하면, 각 그리드의 가격 범위가 작아지고, 거래 기회가 늘어나며, 매수매도를 더 자주 수행할 수 있습니다. 그러나 각 거래에 관련된 자본 금액이 적기 때문에 이러한 전략은 수익을 내기 위해 고빈도 거래가 필요한 경우가 많습니다. 이로 인해 거래 수수료가 쉽게 높아질 수 있으며, 거래가 너무 잦으면 작은 시장 변동이 주요 수익원이 될 수 있으며 수익의 상한선도 제한됩니다.
적당한 수의 그리드: 적절한 그리드 수는 시장 변동성, 계좌 규모, 예상 거래 빈도를 고려해야 합니다. 시장 변동성이 높은 경우, 그리드의 수를 적절히 늘리면 변동을 더 잘 포착할 수 있고, 자금이 큰 경우, 그리드의 수를 줄이면 단일 거래 금액이 늘어나 거래 수수료 압박이 줄어들 수 있습니다. 그리드 간격과 빈번한 거래 비용을 균형 있게 조절함으로써 이 전략의 수익과 위험 관리를 극대화할 수 있습니다.
그리드 거래의 핵심 특징은 여러 개의 그리드 간격을 설정하여 각 간격에 자금을 할당하는 것입니다. 그리드의 개수를 결정할 때는 먼저 각 그리드 간의 간격과 각 그리드에 포함된 자금의 양을 계산해야 합니다. 그리드 개수의 설정은 각 그리드의 자금 규모에 영향을 미칠 뿐만 아니라, 각 그리드의 매수 및 매도 가격 범위도 결정합니다.
계정 자금은 그리드 수를 결정하는 중요한 요소입니다. 계좌 자금이 클수록 더 많은 그리드를 설정할 수 있는 반면, 계좌 자금이 작을수록 자금을 너무 분산시켜 각 그리드에 있는 자금이 너무 적어지고 효과적인 수익을 창출할 수 없게 되는 것을 피하기 위해 그리드 수에 제한이 필요합니다.
또한, 그리드 거래의 위험 관리 전략에서는 그리드 수의 설정도 고려해야 합니다. 특히 시장이 격렬하게 변동할 때, 그리드가 너무 많으면 손실이 커질 수 있으므로, 과도한 거래를 피하기 위해 그리드 수를 합리적으로 조절해야 합니다.
그리드 거래 전략을 실제로 적용할 때, 특히 그리드 수와 수익률 간의 균형을 최적화할 때 Datadata 플랫폼의 DQL(Datadata Query Language)은 큰 유연성을 제공합니다. DQL은 효율적인 데이터 쿼리, 처리, 분석을 지원할 뿐만 아니라, 트레이더가 그리드 트레이딩 전략의 성과를 시뮬레이션하고 백테스트하여 가장 적합한 그리드 번호와 기타 매개변수 구성을 찾는 데 도움이 됩니다.
DQL을 사용하면 여러 거래소(예: 바이낸스)의 과거 K-라인 데이터를 쉽게 얻고, 이 데이터를 기반으로 그리드 거래 전략을 조정할 수 있습니다. 이런 방식으로 다양한 시장 환경과 특정 통화의 변동성에 따라 최적의 그리드 거래 전략을 정확하게 선별할 수 있습니다.
그리드 트레이딩 전략의 백테스트를 시작하기에 앞서, 먼저 목표 통화에 대한 시장 데이터를 얻어야 합니다. 다음은 데이터베이스에서 지정된 통화의 K-라인 데이터를 쿼리하는 코드입니다.
# 获取目标币种的K线数据
data = query("select * from klines.spot_1d where Exchange = 'Binance' and Symbol = '???_usdt' order by Time")
설명:
???_usdt
) 바이낸스 거래 플랫폼의 정보(시간, 시작 가격, 최고 가격, 최저가, 종가 등 포함). 이는 전략 실행을 위한 기본 데이터를 제공합니다.그리드 거래 전략의 핵심은 사전에 설정된 그리드 수량과 가격 범위를 사용하여 거래하는 것입니다. 구체적인 단계는 다음과 같습니다.
그리드 간격(갭)과 그리드당 거래 금액(명목)을 계산합니다.:
그리드 간격(갭): 시장에서 가장 높은 가격과 가장 낮은 가격의 비율을 기준으로 계산됩니다. 공식은 다음과 같습니다.
[
\text{gap} = \frac{\log(\text{max_p} / \text{min_p})}{\text{grid_num}}
]
안에,max_p
가장 높은 가격으로,min_p
가장 낮은 가격으로,grid_num
격자의 수입니다.
그리드당 거래 금액(명목):각 그리드의 거래금액은 총자금과 그리드수에 의해 계산됩니다. 공식은 다음과 같습니다. [ \text{notional} = \frac{\text{balance}}{\text{grid_num}} ]
그리드 시작 및 종료 가격:
exp(gap)
, 등등.거래 운영:
거래 수수료: 각 거래에 대한 처리 수수료가 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
대상 통화로 ‘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
다양한 그리드 번호 구성으로 백테스트를 실시한 결과 다음과 같은 결과를 얻었습니다.
분석:
그리드 거래 전략에서 그리드의 수를 적절히 설정하는 것은 중요한 작업입니다. 그리드 수를 최적화함으로써 그리드 거래 전략의 수익 성과를 효과적으로 개선하고 위험을 보다 효과적으로 통제할 수 있습니다. 이 글에서는 그리드 번호 설정을 분석하고 구체적인 계산 방법과 샘플 코드를 제공하여 모든 사람이 그리드 거래 전략을 최적화하고 전략의 안정성과 수익성을 개선하는 데 도움이 되기를 바랍니다.
참고: 이 기사의 그리드 백테스팅 코드는 Zhihu Da Shen에서 가져왔습니다. Halcyon, 소스코드 설명은 해당 문서를 참고해주세요알고리즘 트레이딩 연습 일기(XVIII) - 그리드 트레이딩의 세부 사항: 그리드 번호와 장기 수익률의 관계。