
টেকসই গ্রিড কৌশল প্ল্যাটফর্মের একটি জনপ্রিয় ক্লাসিক কৌশল। স্পট গ্রিডের সাথে তুলনা করে, কয়েন রাখার দরকার নেই এবং লিভারেজ যোগ করা যেতে পারে, যা স্পট গ্রিডের তুলনায় অনেক বেশি সুবিধাজনক। যাইহোক, যেহেতু ইনভেনটর কোয়ান্টিটেটিভ প্ল্যাটফর্মে সরাসরি ব্যাকটেস্ট করা সম্ভব নয়, এটি মুদ্রার স্ক্রীনিং এবং প্যারামিটার অপ্টিমাইজেশান নির্ধারণের জন্য উপযুক্ত নয়, এই নিবন্ধটি ডেটা সংগ্রহ, ব্যাকটেস্টিং ফ্রেমওয়ার্ক, ব্যাকটেস্টিং ফাংশন, প্যারামিটার অপ্টিমাইজেশন সহ সম্পূর্ণ পাইথন ব্যাকটেস্টিং প্রক্রিয়া চালু করবে। এবং অন্যান্য দিক আপনি juypter নোটবুকে চেষ্টা করতে পারেন।
সাধারণভাবে, সঠিকতার জন্য, কে-লাইন সময় যত কম হবে, তত ভাল, ব্যাকটেস্টিং সময় এবং ডেটার পরিমাণের ভারসাম্য বজায় রাখতে, এই নিবন্ধটি 5 মিনিটের ডেটা ব্যবহার করে ব্যাকটেস্টিং-এর জন্য গত দুই বছরে ডাটা 200,000 লাইন ছাড়িয়ে গেছে। অবশ্যই, নির্দিষ্ট মুদ্রা এবং কে-লাইন চক্র আপনার নিজের আগ্রহ অনুযায়ী নির্বাচন করা যেতে পারে।
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()
ব্যাকটেস্টিংয়ের জন্য, আমরা সাধারণভাবে ব্যবহৃত ফ্রেমওয়ার্ক বেছে নিতে থাকি যা একাধিক মুদ্রায় USDT স্থায়ী চুক্তি সমর্থন করে, যা সহজ এবং ব্যবহার করা সহজ।
class Exchange:
def __init__(self, trade_symbols, fee=0.0004, initial_balance=10000):
self.initial_balance = initial_balance #初始的资产
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 #扣除手续费
self.account['USDT']['fee'] += price*amount*self.fee
self.account[symbol]['fee'] += price*amount*self.fee
if cover_amount > 0: #先平仓
self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount #利润
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): #对资产进行更新
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)
গ্রিড কৌশলের নীতিটি খুব সহজ যখন দাম বেড়ে যায় তখন কিনুন এবং এতে তিনটি প্যারামিটার অন্তর্ভুক্ত থাকে: প্রাথমিক মূল্য, গ্রিড ব্যবধান এবং লেনদেনের মূল্য৷ DYDX-এর বাজার 8.6U-এর প্রাথমিক নিম্ন থেকে 1U-এ নেমে এসেছে এবং তারপরে সাম্প্রতিক ষাঁড়ের বাজারে 3U-এ পৌঁছেছে, যা গ্রিড কৌশলের জন্য খুবই প্রতিকূল , কিন্তু ডিফল্ট প্যারামিটারগুলি ব্যাকটেস্ট করা হয়েছে দুই বছরে মোট 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 row in df.iterrows():
kline = row[1] #这样会测一根K线只会产生一个买单或一个卖单,不是特别精确
buy_price = (value / pct - value) / ((value / pct) / init_price + e.account[symbol]['amount']) #买单价格,由于是挂单成交,也是最终的撮合价格
sell_price = (value / pct + value) / ((value / pct) / init_price + e.account[symbol]['amount'])
if kline.low < buy_price: #K线最低价低于当前挂单价,买单成交
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 U-এর একটি সংক্ষিপ্ত অর্ডার সরাসরি রাখা হবে, তাই এটি আরও বেশি ঝুঁকির সম্মুখীন হবে।

গ্রিড স্পেসিং মুলতুবি অর্ডারগুলির মধ্যে দূরত্ব নির্ধারণ করে, ব্যবধান যত কম হবে, লেনদেন তত বেশি হবে, একটি একক লেনদেনের লাভ তত কম হবে এবং হ্যান্ডলিং ফি তত বেশি হবে৷ যাইহোক, এটি লক্ষণীয় যে গ্রিডের ব্যবধান ছোট হয়ে যাওয়ার সাথে সাথে এবং গ্রিডের মান অপরিবর্তিত থাকে, যখন মূল্য পরিবর্তন হয়, তখন মোট অবস্থান বৃদ্ধি পাবে এবং ঝুঁকিগুলি সম্পূর্ণ ভিন্ন। অতএব, গ্রিড ব্যবধানের প্রভাবের ব্যাক-টেস্ট করার জন্য, গ্রিড মান রূপান্তর করা প্রয়োজন।
যেহেতু ব্যাকটেস্ট 5mK লাইন ডেটা ব্যবহার করে, এবং একটি 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% হ্রাস পাবে, যখন বৃদ্ধির কোন সীমা নেই, এবং ঝুঁকি অনেক বেশি। অতএব, গ্রিড স্ট্র্যাটেজি ব্যবহারকারীদের সুপারিশ করে যে তারা যে মুদ্রার সম্ভাবনা আছে বলে মনে করেন তার জন্য শুধুমাত্র দীর্ঘ মোড বেছে নিন।
রিটার্ন মূল্য হল প্রারম্ভিক মূল্য এবং গ্রিডের আকারের মধ্যে পার্থক্য কতগুলি অবস্থানে থাকা উচিত যদি রিটার্ন মূল্য বর্তমান মূল্যের চেয়ে বেশি হয়, গ্রিড কৌশলটি দীর্ঘ হবে বিপরীতভাবে এটি ছোট হবে। কৌশলটি শুরু করার সময় ডিফল্ট রিটার্ন মূল্য হল মূল্য। ঝুঁকি কমানোর জন্য, শুধুমাত্র লং পজিশন করার জন্য সুপারিশ করা হয় একটি স্বাভাবিক ধারণা হল যে রিটার্ন মূল্য পরিবর্তন করা যেতে পারে যাতে দাম বাড়লেও, স্ব-সামঞ্জস্যের প্রয়োজন ছাড়াই লং পজিশন রাখা হয়। উদাহরণ হিসেবে এই বছরের BTC-এর দাম ধরুন, বছরের শুরুতে এটি 15,000 থেকে বেড়ে বছরের শেষে 43,000-এ পৌঁছেছে। আপনি যদি বছরের শুরু থেকে গ্রিড কৌশলটি চালান, তাহলে রিটার্ন মূল্য ডিফল্ট হয়ে যাবে 15,000, এবং আপনি দীর্ঘ এবং ছোট উভয় অবস্থানই করবেন যা নীচে দেখানো হয়েছে, এবং BTC বৃদ্ধির সাথে সাথে আপনি অর্থ হারাবেন। এমনকি আপনি শুধুমাত্র লং মোড করলেও, যদি সেটিংটি সঠিক না হয়, তাহলে দাম দ্রুত মূল্যকে ছাড়িয়ে যাবে এবং কোন অবস্থান থাকবে না।

প্রথমত, যখন কৌশলটি শুরু করা হয়, তখন রিগ্রেশন মূল্য শুরুর সময় মূল্যের ১.৬ গুণে সেট করা হয়। এইভাবে, গ্রিড কৌশল মূল্যকে বর্তমান মূল্যের ১.৬ গুণ থেকে হ্রাস হিসাবে বিবেচনা করবে এবং দীর্ঘ সময় ধরে ধরে রাখতে শুরু করবে মূল্যের পার্থক্যের এই অংশের কারণে সৃষ্ট অবস্থান। যদি পরবর্তী মূল্য অতিক্রম করে। যখন রিটার্ন মূল্য /1.6 হয়, তখন প্রাথমিক মূল্য পুনরায় সেট করা হয়, যাতে দীর্ঘ অবস্থানের জন্য কমপক্ষে 60% পার্থক্য সর্বদা উপলব্ধ থাকে। ব্যাকটেস্টের ফলাফল নিম্নরূপ:

অবশ্যই, যদি আপনি বাজার সম্পর্কে আরও আশাবাদী হন, তাহলে আপনি এই অনুপাতটিকে আরও বড় মূল্যে সেট করতে পারেন, এবং সেই অনুযায়ী চূড়ান্ত মুনাফা বৃদ্ধি পাবে। অবশ্যই, যদি বাজারের পতন হয়, তাহলে এই সেটিং পজিশন ধরে রাখার ঝুঁকিও বাড়িয়ে দেবে।