মূল গবেষণা রিপোর্টের ঠিকানা:https://www.fmz.com/digest-topic/5584আপনি এটি প্রথম পড়তে পারেন, এই নিবন্ধটি ডুপ্লিকেট কন্টেন্ট থাকবে না। এই নিবন্ধটি দ্বিতীয় কৌশল অপ্টিমাইজেশান প্রক্রিয়া হাইলাইট করবে। অপ্টিমাইজেশান পরে, দ্বিতীয় কৌশল স্পষ্টভাবে উন্নত হয়, এটি এই নিবন্ধ অনুযায়ী কৌশল আপগ্রেড করার পরামর্শ দেওয়া হয়। ব্যাকটেস্ট ইঞ্জিন হ্যান্ডলিং ফি পরিসংখ্যান যোগ করা হয়েছে।
# Libraries to import
import pandas as pd
import requests
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
%matplotlib inline
symbols = ['ETH', 'BCH', 'XRP', 'EOS', 'LTC', 'TRX', 'ETC', 'LINK', 'XLM', 'ADA', 'XMR', 'DASH', 'ZEC', 'XTZ', 'BNB', 'ATOM', 'ONT', 'IOTA', 'BAT', 'VET', 'NEO', 'QTUM', 'IOST']
price_usdt = pd.read_csv('https://www.fmz.com/upload/asset/20227de6c1d10cb9dd1.csv ', index_col = 0)
price_usdt.index = pd.to_datetime(price_usdt.index)
price_usdt_norm = price_usdt/price_usdt.fillna(method='bfill').iloc[0,]
price_usdt_btc = price_usdt.divide(price_usdt['BTC'],axis=0)
price_usdt_btc_norm = price_usdt_btc/price_usdt_btc.fillna(method='bfill').iloc[0,]
class Exchange:
def __init__(self, trade_symbols, leverage=20, commission=0.00005, initial_balance=10000, log=False):
self.initial_balance = initial_balance # Initial asset
self.commission = commission
self.leverage = leverage
self.trade_symbols = trade_symbols
self.date = ''
self.log = log
self.df = pd.DataFrame(columns=['margin','total','leverage','realised_profit','unrealised_profit'])
self.account = {'USDT':{'realised_profit':0, 'margin':0, 'unrealised_profit':0, 'total':initial_balance, 'leverage':0, 'fee':0}}
for symbol in trade_symbols:
self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0, 'margin':0, 'unrealised_profit':0,'fee':0}
def Trade(self, symbol, direction, price, amount, msg=''):
if self.date and self.log:
print('%-20s%-5s%-5s%-10.8s%-8.6s %s'%(str(self.date), symbol, 'buy' if direction == 1 else 'sell', price, amount, msg))
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.commission # Minus handling fee
self.account['USDT']['fee'] += price*amount*self.commission
self.account[symbol]['fee'] += price*amount*self.commission
if cover_amount > 0: # close position first
self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount # Profit
self.account['USDT']['margin'] -= cover_amount*self.account[symbol]['hold_price']/self.leverage # Free margin
self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
self.account[symbol]['amount'] -= -direction*cover_amount
self.account[symbol]['margin'] -= cover_amount*self.account[symbol]['hold_price']/self.leverage
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['USDT']['margin'] += open_amount*price/self.leverage
self.account[symbol]['hold_price'] = total_cost/total_amount
self.account[symbol]['amount'] += direction*open_amount
self.account[symbol]['margin'] += open_amount*price/self.leverage
self.account[symbol]['unrealised_profit'] = (price - self.account[symbol]['hold_price'])*self.account[symbol]['amount']
self.account[symbol]['price'] = price
self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*price
return True
def Buy(self, symbol, price, amount, msg=''):
self.Trade(symbol, 1, price, amount, msg)
def Sell(self, symbol, price, amount, msg=''):
self.Trade(symbol, -1, price, amount, msg)
def Update(self, date, close_price): # Update assets
self.date = date
self.close = close_price
self.account['USDT']['unrealised_profit'] = 0
for symbol in self.trade_symbols:
if np.isnan(close_price[symbol]):
continue
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']
if self.date.hour in [0,8,16]:
pass
self.account['USDT']['realised_profit'] += -self.account[symbol]['amount']*close_price[symbol]*0.01/100
self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'],6)
self.account['USDT']['leverage'] = round(self.account['USDT']['margin']/self.account['USDT']['total'],4)*self.leverage
self.df.loc[self.date] = [self.account['USDT']['margin'],self.account['USDT']['total'],self.account['USDT']['leverage'],self.account['USDT']['realised_profit'],self.account['USDT']['unrealised_profit']]
মুদ্রার ধরন নির্বাচন করার পর মূল কৌশলটি ভালভাবে কাজ করেছে, কিন্তু এখনও অনেকগুলি হোল্ডিং পজিশন রয়েছে, সাধারণত প্রায় 4 গুণ
নীতিঃ
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH'])) # Remaining currencies
price_usdt_btc_norm_mean = price_usdt_btc_norm[trade_symbols].mean(axis=1)
e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False)
trade_value = 300
for row in price_usdt.iloc[:].iterrows():
e.Update(row[0], row[1])
empty_value = 0
for symbol in trade_symbols:
price = row[1][symbol]
if np.isnan(price):
continue
diff = price_usdt_btc_norm.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]]
aim_value = -trade_value*round(diff/0.01,1)
now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount'])
empty_value += now_value
if aim_value - now_value > 20:
e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
if aim_value - now_value < -20:
e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
stragey_2b = e
(stragey_2b.df['total']/stragey_2b.initial_balance).plot(figsize=(17,6),grid = True);
stragey_2b.df['leverage'].plot(figsize=(18,6),grid = True); # leverage
pd.DataFrame(e.account).T.apply(lambda x:round(x,3)) # holding position
মূল বৃহত্তম সমস্যাটি হ'ল কৌশলটি শুরু করা সর্বশেষ মূল্য এবং প্রাথমিক মূল্যের মধ্যে তুলনা। সময়ের সাথে সাথে এটি আরও বেশি বিচ্যুত হয়ে উঠবে। আমরা এই মুদ্রাগুলিতে প্রচুর অবস্থান জমা করব। মুদ্রা ফিল্টার করার সবচেয়ে বড় সমস্যা হ'ল আমাদের অতীতের অভিজ্ঞতার ভিত্তিতে ভবিষ্যতে আমাদের এখনও অনন্য মুদ্রা থাকতে পারে। নিম্নলিখিতটি হল ফিল্টারিং ছাড়াই মোডের পারফরম্যান্স। আসলে, যখন trade_value = 300, কৌশল চলার মাঝামাঝি পর্যায়ে, এটি ইতিমধ্যে সবকিছু হারিয়েছে। এমনকি যদি এটি না হয় তবে লিংক এবং এক্সটিজেডও 10000USDT এর উপরে অবস্থান ধরে রাখে, যা খুব বড়। অতএব, আমাদের এই সমস্যাটি ব্যাকটেস্টে সমাধান করতে হবে এবং সমস্ত মুদ্রার পরীক্ষা পাস করতে হবে।
trade_symbols = list(set(symbols)) # Remaining currencies
price_usdt_btc_norm_mean = price_usdt_btc_norm[trade_symbols].mean(axis=1)
e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False)
trade_value = 300
for row in price_usdt.iloc[:].iterrows():
e.Update(row[0], row[1])
empty_value = 0
for symbol in trade_symbols:
price = row[1][symbol]
if np.isnan(price):
continue
diff = price_usdt_btc_norm.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]]
aim_value = -trade_value*round(diff/0.01,1)
now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount'])
empty_value += now_value
if aim_value - now_value > 20:
e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
if aim_value - now_value < -20:
e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
stragey_2c = e
(stragey_2c.df['total']/stragey_2c.initial_balance).plot(figsize=(17,6),grid = True);
pd.DataFrame(stragey_2c.account).T.apply(lambda x:round(x,3)) # Last holding position
((price_usdt_btc_norm.iloc[-1:] - price_usdt_btc_norm_mean[-1]).T) # Each currency deviates from the initial situation
যেহেতু সমস্যার কারণ হল প্রাথমিক মূল্যের সাথে তুলনা করা, এটি আরও বেশি পক্ষপাতমূলক হতে পারে। আমরা এটিকে অতীত সময়ের চলমান গড়ের সাথে তুলনা করতে পারি, পুরো মুদ্রাটি ব্যাকটেস্ট করতে পারি এবং নীচের ফলাফলগুলি দেখতে পারি।
Alpha = 0.05
#price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.rolling(20).mean() #Ordinary moving average
price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.ewm(alpha=Alpha).mean() # Here is consistent with the strategy, using EMA
trade_symbols = list(set(symbols))#All currencies
price_usdt_btc_norm_mean = price_usdt_btc_norm2[trade_symbols].mean(axis=1)
e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False)
trade_value = 300
for row in price_usdt.iloc[:].iterrows():
e.Update(row[0], row[1])
empty_value = 0
for symbol in trade_symbols:
price = row[1][symbol]
if np.isnan(price):
continue
diff = price_usdt_btc_norm2.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]]
aim_value = -trade_value*round(diff/0.01,1)
now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount'])
empty_value += now_value
if aim_value - now_value > 20:
e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
if aim_value - now_value < -20:
e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
stragey_2d = e
#print(N,stragey_2d.df['total'][-1],pd.DataFrame(stragey_2d.account).T.apply(lambda x:round(x,3))['value'].sum())
কৌশলটির পারফরম্যান্স আমাদের প্রত্যাশা পুরোপুরি পূরণ করেছে, এবং রিটার্নগুলি প্রায় একই। সমস্ত মুদ্রার মূল মুদ্রায় অ্যাকাউন্টের অবস্থানগুলি ভেঙে যাওয়ার পরিস্থিতিও মসৃণভাবে রূপান্তরিত হয়েছে, এবং প্রায় কোনও পুনরুদ্ধার নেই। একই উদ্বোধনী অবস্থানের আকার, প্রায় সমস্ত লিভারেজ 1 বারের নীচে, 12 ই মার্চ 2020 এ মূল্যের চরম ক্ষেত্রে ডুবে গেছে, এটি এখনও 4 বারের বেশি নয়, যার অর্থ আমরা trade_value বৃদ্ধি করতে পারি, এবং একই লিভারেজের অধীনে, মুনাফা দ্বিগুণ করতে পারি। চূড়ান্ত হোল্ডিং অবস্থানটি কেবলমাত্র BCH 1000USDT ছাড়িয়ে গেছে, যা খুব ভাল।
কেন পজিশনটি হ্রাস করা হবে? কল্পনা করুন অ্যাল্টকয়েন সূচকে অপরিবর্তিত যোগদান, একটি মুদ্রা 100% বৃদ্ধি পেয়েছে, এবং এটি দীর্ঘ সময়ের জন্য বজায় থাকবে। মূল কৌশলটি দীর্ঘ সময়ের জন্য 300 * 100 = 30000USDT এর শর্ট পজিশন ধরে রাখবে, এবং নতুন কৌশলটি শেষ পর্যন্ত বেঞ্চমার্ক মূল্যটি ট্র্যাক করবে। সর্বশেষ মূল্যে, আপনি শেষ পর্যন্ত কোনও অবস্থান ধরে রাখবেন না।
(stragey_2d.df['total']/stragey_2d.initial_balance).plot(figsize=(17,6),grid = True);
#(stragey_2c.df['total']/stragey_2c.initial_balance).plot(figsize=(17,6),grid = True);
stragey_2d.df['leverage'].plot(figsize=(18,6),grid = True);
stragey_2b.df['leverage'].plot(figsize=(18,6),grid = True); # Screen currency strategy leverage
pd.DataFrame(stragey_2d.account).T.apply(lambda x:round(x,3))
স্ক্রিনিং মেশিনের সাথে মুদ্রার কী হবে, একই পরামিতিগুলির সাথে, পূর্ববর্তী পর্যায়ে মুনাফা আরও ভাল সম্পাদন করে, পুনরুদ্ধারটি ছোট, তবে সামগ্রিক রিটার্নগুলি কিছুটা কম। অতএব, একটি স্ক্রিনিং মেশিন থাকার পরামর্শ দেওয়া হয়।
#price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.rolling(50).mean()
price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.ewm(alpha=0.05).mean()
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH'])) # Remaining currencies
price_usdt_btc_norm_mean = price_usdt_btc_norm2[trade_symbols].mean(axis=1)
e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False)
trade_value = 300
for row in price_usdt.iloc[:].iterrows():
e.Update(row[0], row[1])
empty_value = 0
for symbol in trade_symbols:
price = row[1][symbol]
if np.isnan(price):
continue
diff = price_usdt_btc_norm2.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]]
aim_value = -trade_value*round(diff/0.01,1)
now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount'])
empty_value += now_value
if aim_value - now_value > 20:
e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
if aim_value - now_value < -20:
e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
stragey_2e = e
#(stragey_2d.df['total']/stragey_2d.initial_balance).plot(figsize=(17,6),grid = True);
(stragey_2e.df['total']/stragey_2e.initial_balance).plot(figsize=(17,6),grid = True);
stragey_2e.df['leverage'].plot(figsize=(18,6),grid = True);
pd.DataFrame(stragey_2e.account).T.apply(lambda x:round(x,3))
এক্সপোনেন্সিয়াল মুভিং এভারেজের আলফা প্যারামিটারের সেটিং যত বড়, বেঞ্চমার্ক মূল্য ট্র্যাকিং তত বেশি সংবেদনশীল, কম লেনদেন, চূড়ান্ত হোল্ডিং অবস্থান তত কম। যখন লিভারেজ কম হয়, রিটার্নও হ্রাস পায়। সর্বাধিক পুনরুদ্ধার হ্রাস করা, এটি লেনদেনের পরিমাণ বাড়িয়ে তুলতে পারে। ব্যাকটেস্টের ফলাফলের উপর ভিত্তি করে নির্দিষ্ট ভারসাম্য অপারেশন প্রয়োজন।
যেহেতু ব্যাকটেস্টটি 1 ঘন্টা K রেখা, তাই এটি প্রতি ঘন্টায় একবার আপডেট করা যেতে পারে, বাস্তব বাজারটি দ্রুত আপডেট করা যেতে পারে এবং নির্দিষ্ট সেটিংসগুলি ব্যাপকভাবে ওজন করা প্রয়োজন।
এটি অপ্টিমাইজেশনের ফলাফলঃ
for Alpha in [i/100 for i in range(1,30)]:
#price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.rolling(20).mean() # Ordinary moving average
price_usdt_btc_norm2 = price_usdt_btc/price_usdt_btc.ewm(alpha=Alpha).mean() # Here is consistent with the strategy, using EMA
trade_symbols = list(set(symbols))# All currencies
price_usdt_btc_norm_mean = price_usdt_btc_norm2[trade_symbols].mean(axis=1)
e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False)
trade_value = 300
for row in price_usdt.iloc[:].iterrows():
e.Update(row[0], row[1])
empty_value = 0
for symbol in trade_symbols:
price = row[1][symbol]
if np.isnan(price):
continue
diff = price_usdt_btc_norm2.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]]
aim_value = -trade_value*round(diff/0.01,1)
now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount'])
empty_value += now_value
if aim_value - now_value > 20:
e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
if aim_value - now_value < -20:
e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2))
stragey_2d = e
# These are the final net value, the initial maximum backtest, the final position size, and the handling fee
print(Alpha, round(stragey_2d.account['USDT']['total'],1), round(1-stragey_2d.df['total'].min()/stragey_2d.initial_balance,2),round(pd.DataFrame(stragey_2d.account).T['value'].sum(),1),round(stragey_2d.account['USDT']['fee']))
0.01 21116.2 0.14 15480.0 2178.0
0.02 20555.6 0.07 12420.0 2184.0
0.03 20279.4 0.06 9990.0 2176.0
0.04 20021.5 0.04 8580.0 2168.0
0.05 19719.1 0.03 7740.0 2157.0
0.06 19616.6 0.03 7050.0 2145.0
0.07 19344.0 0.02 6450.0 2133.0
0.08 19174.0 0.02 6120.0 2117.0
0.09 18988.4 0.01 5670.0 2104.0
0.1 18734.8 0.01 5520.0 2090.0
0.11 18532.7 0.01 5310.0 2078.0
0.12 18354.2 0.01 5130.0 2061.0
0.13 18171.7 0.01 4830.0 2047.0
0.14 17960.4 0.01 4770.0 2032.0
0.15 17779.8 0.01 4531.3 2017.0
0.16 17570.1 0.01 4441.3 2003.0
0.17 17370.2 0.01 4410.0 1985.0
0.18 17203.7 0.0 4320.0 1971.0
0.19 17016.9 0.0 4290.0 1955.0
0.2 16810.6 0.0 4230.6 1937.0
0.21 16664.1 0.0 4051.3 1921.0
0.22 16488.2 0.0 3930.6 1902.0
0.23 16378.9 0.0 3900.6 1887.0
0.24 16190.8 0.0 3840.0 1873.0
0.25 15993.0 0.0 3781.3 1855.0
0.26 15828.5 0.0 3661.3 1835.0
0.27 15673.0 0.0 3571.3 1816.0
0.28 15559.5 0.0 3511.3 1800.0
0.29 15416.4 0.0 3481.3 1780.0