स्थायी अनुबंध ग्रिड रणनीति पैरामीटर अनुकूलन का विस्तृत स्पष्टीकरण

लेखक:लिडिया, बनाया गयाः 2023-12-11 15:04:25, अद्यतनः 2024-01-02 21:24:31

img

शाश्वत ग्रिड रणनीति एफएमजेड प्लेटफॉर्म पर एक लोकप्रिय क्लासिक रणनीति है। स्पॉट ग्रिड की तुलना में, मुद्राओं की आवश्यकता नहीं है, और लीवरेज जोड़ा जा सकता है, जो स्पॉट ग्रिड की तुलना में बहुत अधिक सुविधाजनक है। हालांकि, चूंकि एफएमजेड क्वांट प्लेटफॉर्म पर सीधे बैकटेस्ट करना संभव नहीं है, इसलिए यह मुद्राओं की स्क्रीनिंग और पैरामीटर अनुकूलन निर्धारित करने के लिए अनुकूल नहीं है। इस लेख में, हम डेटा संग्रह, बैकटेस्टिंग फ्रेमवर्क, बैकटेस्टिंग फ़ंक्शन, पैरामीटर अनुकूलन, आदि सहित पूर्ण पायथन बैकटेस्टिंग प्रक्रिया का परिचय देंगे। आप इसे जूइप्टर नोटबुक में स्वयं आज़मा सकते हैं।

डेटा संग्रह

सामान्य तौर पर, के-लाइन डेटा का उपयोग करना पर्याप्त है। सटीकता के लिए, के-लाइन अवधि जितनी छोटी होगी, उतना ही बेहतर होगा। हालांकि, बैकटेस्ट समय और डेटा वॉल्यूम को संतुलित करने के लिए, इस लेख में, हम बैकटेस्टिंग के लिए पिछले दो वर्षों के 5min डेटा का उपयोग करते हैं। अंतिम डेटा वॉल्यूम 200,000 लाइनों से अधिक है। हम मुद्रा के रूप में DYDX चुनते हैं। बेशक, विशिष्ट मुद्रा और के-लाइन अवधि को आपकी अपनी रुचियों के अनुसार चुना जा सकता है।

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()

बैकटेस्टिंग ढांचा

बैकटेस्टिंग के लिए, हम आम तौर पर इस्तेमाल किए जाने वाले ढांचे का चयन करना जारी रखते हैं जो कई मुद्राओं में यूएसडीटी स्थायी अनुबंधों का समर्थन करता है, जो सरल और उपयोग करने में आसान है।

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)

ग्रिड बैकटेस्ट फ़ंक्शन

ग्रिड रणनीति का सिद्धांत बहुत सरल है। जब कीमत बढ़ती है तो बेचें और जब कीमत गिरती है तो खरीदें। इसमें विशेष रूप से तीन मापदंड शामिल हैंः प्रारंभिक मूल्य, ग्रिड अंतराल और व्यापारिक मूल्य। डीवाईडीएक्स का बाजार बहुत उतार-चढ़ाव करता है। यह 8.6U के प्रारंभिक निम्न से 1U तक गिर गया, और फिर हाल के बुल बाजार में 3U तक वापस बढ़ गया। रणनीति का डिफ़ॉल्ट प्रारंभिक मूल्य 8.6U है, जो ग्रिड रणनीति के लिए बहुत प्रतिकूल है, लेकिन डिफ़ॉल्ट मापदंडों ने बैकटेस्ट किया है। दो वर्षों में 9200U का कुल लाभ हुआ, और इस अवधि के दौरान 7500U का नुकसान हुआ।

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

आरंभिक मूल्य प्रभाव

प्रारंभिक मूल्य की स्थापना रणनीति की प्रारंभिक स्थिति को प्रभावित करती है। अभी बैकटेस्ट के लिए डिफ़ॉल्ट प्रारंभिक मूल्य स्टार्टअप पर प्रारंभिक मूल्य है, यानी स्टार्टअप पर कोई स्थिति नहीं रखी जाती है। और हम जानते हैं कि ग्रिड रणनीति सभी लाभों को महसूस करेगी जब मूल्य प्रारंभिक चरण में लौटता है, इसलिए यदि रणनीति इसे लॉन्च करते समय भविष्य के बाजार की सही भविष्यवाणी कर सकती है, तो आय में काफी सुधार होगा। यहां, हम प्रारंभिक मूल्य को 3U पर सेट करते हैं और फिर बैकटेस्ट करते हैं। अंत में, अधिकतम ड्रॉडाउन 9200U था, और अंतिम लाभ 13372U था। अंतिम रणनीति पदों को नहीं रखती है। लाभ सभी उतार-चढ़ाव लाभ है, और डिफ़ॉल्ट मापदंडों के लाभों के बीच का अंतर अंतिम मूल्य के गलत निर्णय के कारण स्थिति हानि है।

हालाँकि, यदि प्रारंभिक मूल्य 3U पर सेट किया जाता है, तो रणनीति शुरुआत में छोटी हो जाएगी और बड़ी संख्या में छोटी स्थिति बनाए रखेगी। इस उदाहरण में, 17,000 U का एक छोटा आदेश सीधे रखा जाता है, इसलिए इसे अधिक जोखिमों का सामना करना पड़ता है।

img

ग्रिड अंतराल सेटिंग

ग्रिड स्पेसिंग लंबित ऑर्डर के बीच की दूरी को निर्धारित करती है। जाहिर है, जितना छोटा स्पेसिंग, अधिक बार लेनदेन, एक एकल लेनदेन का कम लाभ, और अधिक हैंडलिंग शुल्क। हालांकि, यह ध्यान देने योग्य है कि जैसे-जैसे ग्रिड स्पेसिंग छोटा हो जाता है और ग्रिड मूल्य अपरिवर्तित रहता है, जब मूल्य बदलता है, कुल पद बढ़ेंगे, और जोखिम का सामना करना पड़ रहा है पूरी तरह से अलग है। इसलिए, ग्रिड स्पेसिंग के प्रभाव को बैकटेस्ट करने के लिए, ग्रिड मूल्य को परिवर्तित करना आवश्यक है।

चूंकि बैकटेस्ट में 5m K-लाइन डेटा का उपयोग किया जाता है, और प्रत्येक K-लाइन का केवल एक बार ही कारोबार किया जाता है, जो स्पष्ट रूप से अवास्तविक है, खासकर जब से डिजिटल मुद्राओं की अस्थिरता बहुत अधिक है। लाइव ट्रेडिंग की तुलना में बैकटेस्टिंग में एक छोटा अंतर कई लेनदेन को याद करेगा। केवल एक बड़ा अंतर का संदर्भ मूल्य होगा। इस बैकटेस्टिंग तंत्र में निष्कर्ष निकाले गए सटीक नहीं हैं। टिक-स्तर ऑर्डर प्रवाह डेटा बैकटेस्टिंग के माध्यम से, इष्टतम ग्रिड अंतर 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

ग्रिड लेनदेन मूल्य

जैसा कि पहले उल्लेख किया गया है, जब उतार-चढ़ाव समान होते हैं, तो होल्डिंग का मूल्य जितना बड़ा होता है, जोखिम आनुपातिक होता है। हालांकि, जब तक कोई तेजी से गिरावट नहीं होती है, तब तक कुल धन का 1% और ग्रिड स्पेसिंग का 1% अधिकांश बाजार की स्थितियों का सामना करने में सक्षम होना चाहिए। इस DYDX उदाहरण में, लगभग 90% की गिरावट ने परिसमापन को भी ट्रिगर किया। हालांकि, यह ध्यान दिया जाना चाहिए कि DYDX मुख्य रूप से गिरता है। जब ग्रिड रणनीति लंबी होती है जब यह गिरती है, तो यह अधिकतम 100% गिर जाएगी, जबकि वृद्धि की कोई सीमा नहीं है, और जोखिम बहुत अधिक है। इसलिए, ग्रिड रणनीति उपयोगकर्ताओं को केवल उन मुद्राओं के लिए लंबी स्थिति मोड चुनने की सलाह देती है जिनकी उन्हें संभावना है।


अधिक