Sumber daya yang dimuat... Pemuatan...

Penjelasan Rincian Optimasi Parameter Strategi Grid Kontrak Perpetual

Penulis:FMZ~Lydia, Dibuat: 2023-12-11 15:04:25, Diperbarui: 2024-01-02 21:24:31

img

Strategi grid abadi adalah strategi klasik yang populer di platform FMZ. Dibandingkan dengan grid spot, tidak perlu memiliki mata uang, dan leverage dapat ditambahkan, yang jauh lebih nyaman daripada grid spot. Namun, karena tidak mungkin melakukan backtesting di FMZ Quant Platform secara langsung, tidak kondusif untuk menyaring mata uang dan menentukan optimasi parameter.

Pengumpulan data

Secara umum, cukup untuk menggunakan data K-line. Untuk akurasi, semakin kecil periode K-line, semakin baik. Namun, untuk menyeimbangkan waktu backtest dan volume data, dalam artikel ini, kami menggunakan 5min data dari dua tahun terakhir untuk backtesting. Volume data akhir melebihi 200.000 baris. Kami memilih DYDX sebagai mata uang. Tentu saja, mata uang tertentu dan periode K-line dapat dipilih sesuai dengan minat Anda sendiri.

import requests
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests, zipfile, io
%matplotlib inline

def GetKlines(symbol='BTC',start='2020-8-10',end='2021-8-10',period='1h'):
    Klines = []
    start_time = int(time.mktime(datetime.strptime(start, "%Y-%m-%d").timetuple()))*1000
    end_time = int(time.mktime(datetime.strptime(end, "%Y-%m-%d").timetuple()))*1000
    while start_time < end_time:
        res = requests.get('https://fapi.binance.com/fapi/v1/klines?symbol=%sUSDT&interval=%s&startTime=%s&limit=1000'%(symbol,period,start_time))
        res_list = res.json()
        Klines += res_list
        start_time = res_list[-1][0]
    return pd.DataFrame(Klines,columns=['time','open','high','low','close','amount','end_time','volume','count','buy_amount','buy_volume','null']).astype('float')

df = GetKlines(symbol='DYDX',start='2022-1-1',end='2023-12-7',period='5m')
df = df.drop_duplicates()

Kerangka kerja pengujian balik

Untuk backtesting, kami terus memilih kerangka kerja yang umum digunakan yang mendukung kontrak abadi USDT dalam beberapa mata uang, yang sederhana dan mudah digunakan.

class Exchange:
    
    def __init__(self, trade_symbols, fee=0.0004, initial_balance=10000):
        self.initial_balance = initial_balance #Initial assets
        self.fee = fee
        self.trade_symbols = trade_symbols
        self.account = {'USDT':{'realised_profit':0, 'unrealised_profit':0, 'total':initial_balance, 'fee':0}}
        for symbol in trade_symbols:
            self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0,'unrealised_profit':0,'fee':0}
            
    def Trade(self, symbol, direction, price, amount):
        
        cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
        open_amount = amount - cover_amount
        self.account['USDT']['realised_profit'] -= price*amount*self.fee #Deduction of handling fee
        self.account['USDT']['fee'] += price*amount*self.fee
        self.account[symbol]['fee'] += price*amount*self.fee

        if cover_amount > 0: #Close the position first.
            self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount  #Profits
            self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
            
            self.account[symbol]['amount'] -= -direction*cover_amount
            self.account[symbol]['hold_price'] = 0 if self.account[symbol]['amount'] == 0 else self.account[symbol]['hold_price']
            
        if open_amount > 0:
            total_cost = self.account[symbol]['hold_price']*direction*self.account[symbol]['amount'] + price*open_amount
            total_amount = direction*self.account[symbol]['amount']+open_amount
            
            self.account[symbol]['hold_price'] = total_cost/total_amount
            self.account[symbol]['amount'] += direction*open_amount
                    
    
    def Buy(self, symbol, price, amount):
        self.Trade(symbol, 1, price, amount)
        
    def Sell(self, symbol, price, amount):
        self.Trade(symbol, -1, price, amount)
        
    def Update(self, close_price): #Updating of assets
        self.account['USDT']['unrealised_profit'] = 0
        for symbol in self.trade_symbols:
            self.account[symbol]['unrealised_profit'] = (close_price[symbol] - self.account[symbol]['hold_price'])*self.account[symbol]['amount']
            self.account[symbol]['price'] = close_price[symbol]
            self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*close_price[symbol]
            self.account['USDT']['unrealised_profit'] += self.account[symbol]['unrealised_profit']
        self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'],6)

Grid Backtest Fungsi

Prinsip strategi grid sangat sederhana. Jual ketika harga naik dan beli ketika harga turun. Secara khusus melibatkan tiga parameter: harga awal, jarak grid, dan nilai perdagangan. Pasar DYDX berfluktuasi sangat. Ini turun dari level terendah awal 8,6U menjadi 1U, dan kemudian naik kembali menjadi 3U di pasar bull baru-baru ini. Harga awal default strategi adalah 8,6U, yang sangat tidak menguntungkan untuk strategi grid, tetapi parameter default backtested total keuntungan 9200U dibuat dalam dua tahun, dan kerugian 7500U dibuat selama periode tersebut.

img

symbol = 'DYDX'
value = 100
pct = 0.01

def Grid(fee=0.0002, value=100, pct=0.01, init = df.close[0]):
    e = Exchange([symbol], fee=0.0002, initial_balance=10000)
    init_price = init
    res_list = [] #For storing intermediate results
    for row in df.iterrows():
        kline = row[1] #To backtest a K-line will only generate one buy order or one sell order, which is not particularly accurate.
        buy_price = (value / pct - value) / ((value / pct) / init_price + e.account[symbol]['amount']) #The buy order price, as it is a pending order transaction, is also the final aggregated price
        sell_price = (value / pct + value) / ((value / pct) / init_price + e.account[symbol]['amount'])
        if kline.low < buy_price: #The lowest price of the K-line is lower than the current pending order price, the buy order is filled
            e.Buy(symbol,buy_price,value/buy_price)
        if kline.high > sell_price:
            e.Sell(symbol,sell_price,value/sell_price)
        e.Update({symbol:kline.close})
        res_list.append([kline.time, kline.close, e.account[symbol]['amount'], e.account['USDT']['total']-e.initial_balance,e.account['USDT']['fee'] ])
    res = pd.DataFrame(data=res_list, columns=['time','price','amount','profit', 'fee'])
    res.index = pd.to_datetime(res.time,unit='ms')
    return res

img

Dampak Harga Awal

Pengaturan harga awal mempengaruhi posisi awal strategi. Harga awal default untuk backtest saat ini adalah harga awal saat startup, yaitu tidak ada posisi yang dipegang saat startup. Dan kita tahu bahwa strategi grid akan mewujudkan semua keuntungan ketika harga kembali ke tahap awal, jadi jika strategi dapat memprediksi pasar masa depan dengan benar saat diluncurkan, pendapatan akan meningkat secara signifikan. Di sini, kita menetapkan harga awal menjadi 3U dan kemudian backtest. Pada akhirnya, penarikan maksimum adalah 9200U, dan keuntungan akhir adalah 13372U. Strategi akhir tidak memegang posisi. Keuntungan adalah semua keuntungan fluktuasi, dan perbedaan antara keuntungan dari parameter default adalah kerugian posisi yang disebabkan oleh penilaian harga akhir yang tidak akurat.

Namun, jika harga awal ditetapkan menjadi 3U, strategi akan short di awal dan memegang sejumlah besar posisi short.

img

Pengaturan Jarak kisi

Jarak kisi menentukan jarak antara pesanan yang menunggu. Jelas, semakin kecil jarak, semakin sering transaksi, semakin rendah keuntungan dari satu transaksi, dan semakin tinggi biaya penanganan. Namun, perlu dicatat bahwa sebagai jarak kisi menjadi lebih kecil dan nilai kisi tetap tidak berubah, ketika harga berubah, total posisi akan meningkat, dan risiko yang dihadapi sama sekali berbeda. Oleh karena itu, untuk menguji kembali efek dari jarak kisi, perlu untuk mengkonversi nilai kisi.

Karena backtest menggunakan data 5m K-line, dan setiap K-line hanya diperdagangkan sekali, yang jelas tidak realistis, terutama karena volatilitas mata uang digital sangat tinggi. Jarak yang lebih kecil akan kehilangan banyak transaksi dalam backtesting dibandingkan dengan perdagangan langsung. Hanya jarak yang lebih besar yang akan memiliki nilai referensi. Dalam mekanisme backtesting ini, kesimpulan yang diambil tidak akurat. Melalui backtesting data aliran pesanan tingkat tik, jarak kisi optimal harus 0,005-0,01.

for p in [0.0005, 0.001 ,0.002 ,0.005, 0.01, 0.02, 0.05]:
    res = Grid( fee=0.0002, value=value*p/0.01, pct=p, init =3)
    print(p, round(min(res['profit']),0), round(res['profit'][-1],0), round(res['fee'][-1],0))
    
0.0005 -8378.0 144.0 237.0
0.001 -9323.0 1031.0 465.0
0.002 -9306.0 3606.0 738.0
0.005 -9267.0 9457.0 781.0
0.01 -9228.0 13375.0 550.0
0.02 -9183.0 15212.0 309.0
0.05 -9037.0 16263.0 131.0

Nilai Transaksi Grid

Seperti yang disebutkan sebelumnya, ketika fluktuasi sama, semakin besar nilai kepemilikan, risikonya proporsional. Namun, selama tidak ada penurunan yang cepat, 1% dari total dana dan 1% dari jarak grid harus dapat mengatasi sebagian besar kondisi pasar. Dalam contoh DYDX ini, penurunan hampir 90% juga memicu likuidasi. Namun, harus dicatat bahwa DYDX terutama jatuh. Ketika strategi grid pergi panjang ketika jatuh, itu akan turun maksimal 100%, sementara tidak ada batasan kenaikan, dan risikonya jauh lebih tinggi. Oleh karena itu, Strategi Grid merekomendasikan pengguna untuk memilih hanya mode posisi panjang untuk mata uang yang mereka yakini berpotensi.


Lebih banyak