চিরস্থায়ী গ্রিড কৌশলটি এফএমজেড প্ল্যাটফর্মে একটি জনপ্রিয় ক্লাসিক কৌশল। স্পট গ্রিডের তুলনায়, মুদ্রার প্রয়োজন নেই এবং লিভারেজ যুক্ত করা যেতে পারে, যা স্পট গ্রিডের চেয়ে অনেক বেশি সুবিধাজনক। তবে, এফএমজেড কোয়ান্ট প্ল্যাটফর্মে সরাসরি ব্যাকটেস্ট করা সম্ভব না হওয়ায়, এটি মুদ্রা স্ক্রিনিং এবং প্যারামিটার অপ্টিমাইজেশান নির্ধারণের পক্ষে অনুকূল নয়। এই নিবন্ধে, আমরা ডেটা সংগ্রহ, ব্যাকটেস্টিং ফ্রেমওয়ার্ক, ব্যাকটেস্টিং ফাংশন, প্যারামিটার অপ্টিমাইজেশন ইত্যাদি সহ সম্পূর্ণ পাইথন ব্যাকটেস্টিং প্রক্রিয়াটি পরিচয় করিয়ে দেব। আপনি এটি নিজেকে juypter নোটবুকে চেষ্টা করতে পারেন।
সাধারণভাবে, কে-লাইন ডেটা ব্যবহার করা যথেষ্ট। নির্ভুলতার জন্য, কে-লাইন সময়কাল যত ছোট, তত ভাল। তবে, ব্যাকটেস্ট সময় এবং ডেটা ভলিউম ভারসাম্য বজায় রাখতে, এই নিবন্ধে, আমরা ব্যাকটেস্টিংয়ের জন্য গত দুই বছরের 5 মিনিট ডেটা ব্যবহার করি। চূড়ান্ত ডেটা ভলিউম 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 এর ক্ষতি হয়েছিল।
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
প্রাথমিক মূল্যের সেটিং কৌশলটির প্রাথমিক অবস্থানের উপর প্রভাব ফেলে। ব্যাকটেস্টের জন্য ডিফল্ট প্রাথমিক মূল্য এখনই স্টার্টআপের প্রাথমিক মূল্য, অর্থাৎ স্টার্টআপের সময় কোনও অবস্থান অনুষ্ঠিত হয় না। এবং আমরা জানি যে গ্রিড কৌশলটি যখন দাম প্রাথমিক পর্যায়ে ফিরে আসে তখন সমস্ত লাভ অর্জন করবে, সুতরাং কৌশলটি চালু হওয়ার সময় ভবিষ্যতের বাজারটি সঠিকভাবে পূর্বাভাস দিতে পারে তবে আয় উল্লেখযোগ্যভাবে উন্নত হবে। এখানে, আমরা প্রাথমিক মূল্যটি 3U এ সেট করি এবং তারপরে ব্যাকটেস্ট করি। শেষ পর্যন্ত, সর্বাধিক ড্রডাউন ছিল 9200U, এবং চূড়ান্ত মুনাফা ছিল 13372U। চূড়ান্ত কৌশলটি অবস্থানগুলি ধরে রাখে না। মুনাফা হ'ল সমস্ত ওঠানামা মুনাফা, এবং ডিফল্ট পরামিতিগুলির মুনাফার মধ্যে পার্থক্যটি চূড়ান্ত মূল্যের ভুল বিচারের কারণে অবস্থানের ক্ষতি।
যাইহোক, যদি প্রাথমিক মূল্য 3U এ সেট করা হয়, তবে কৌশলটি শুরুতে শর্ট হবে এবং প্রচুর সংখ্যক শর্ট পজিশন ধরে রাখবে। এই উদাহরণে, 17,000 ইউ এর একটি শর্ট অর্ডার সরাসরি রাখা হয়, তাই এটি বৃহত্তর ঝুঁকিগুলির মুখোমুখি হয়।
গ্রিড স্পেসিং অপেক্ষমান অর্ডারগুলির মধ্যে দূরত্ব নির্ধারণ করে। স্পষ্টতই, স্পেসিং যত ছোট, লেনদেনগুলি তত ঘন ঘন হয়, একক লেনদেনের মুনাফা তত কম হয় এবং হ্যান্ডলিং ফি তত বেশি হয়। তবে, এটি লক্ষণীয় যে গ্রিড স্পেসিং যত ছোট হয় এবং গ্রিডের মান অপরিবর্তিত থাকে, যখন দাম পরিবর্তন হয়, তখন মোট অবস্থানগুলি বৃদ্ধি পাবে এবং সম্মুখীন ঝুঁকিগুলি সম্পূর্ণ আলাদা। অতএব, গ্রিড স্পেসিংয়ের প্রভাব ব্যাকটেস্ট করার জন্য, গ্রিডের মানটি রূপান্তর করা প্রয়োজন।
যেহেতু ব্যাকটেস্টটি 5m কে-লাইন ডেটা ব্যবহার করে এবং প্রতিটি কে-লাইন কেবল একবারই লেনদেন করা হয়, যা অবশ্যই অবাস্তব, বিশেষত যেহেতু ডিজিটাল মুদ্রার অস্থিরতা খুব বেশি। লাইভ ট্রেডিংয়ের তুলনায় একটি ছোট ব্যবধান ব্যাকটেস্টিংয়ে অনেক লেনদেন মিস করবে। কেবলমাত্র একটি বৃহত্তর ব্যবধানের রেফারেন্স মান থাকবে। এই ব্যাকটেস্টিং প্রক্রিয়াতে, উপসংহারগুলি সঠিক নয়। টিক-স্তরের অর্ডার প্রবাহ ডেটা ব্যাকটেস্টিংয়ের মাধ্যমে, সর্বোত্তম গ্রিড স্পেসিং 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% বেশিরভাগ বাজারের অবস্থার সাথে মোকাবিলা করতে সক্ষম হবে। এই ডিওয়াইডিএক্স উদাহরণে, প্রায় 90% হ্রাসও তরলীকরণ শুরু করে। তবে, এটি লক্ষ করা উচিত যে ডিওয়াইডিএক্স মূলত পড়ে। যখন গ্রিড কৌশলটি পড়ে যায়, তখন এটি সর্বাধিক 100% হ্রাস পাবে, যখন উত্থানের কোনও সীমা নেই, এবং ঝুঁকি অনেক বেশি। অতএব, গ্রিড কৌশল ব্যবহারকারীদের তাদের বিশ্বাসযোগ্য সম্ভাব্য মুদ্রার জন্য কেবল দীর্ঘ অবস্থানের মোডটি বেছে নেওয়ার পরামর্শ দেয়।