Alamat laporan penyelidikan asal:https://www.fmz.com/digest-topic/5584Anda boleh membacanya terlebih dahulu, artikel ini tidak akan mempunyai kandungan berganda. Artikel ini akan menyerlahkan proses pengoptimuman strategi kedua. Selepas pengoptimuman, strategi kedua dipertingkatkan jelas, disyorkan untuk menaik taraf strategi mengikut artikel ini. Enjin backtest menambah statistik yuran penanganan.
# 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']]
Prestasi strategi asal, selepas pemilihan jenis mata wang, berjalan dengan baik, tetapi masih ada banyak kedudukan pegangan, umumnya sekitar 4 kali
Prinsip:
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
Masalah terbesar asal adalah perbandingan antara harga terkini dan harga awal yang dimulakan oleh strategi. Seiring berjalannya masa, ia akan menjadi lebih dan lebih menyimpang. Kita akan mengumpulkan banyak kedudukan dalam mata wang ini. Masalah terbesar dengan menapis mata wang adalah bahawa kita mungkin masih mempunyai mata wang yang unik pada masa akan datang berdasarkan pengalaman masa lalu kita. Berikut adalah prestasi mod bukan penapis. Sebenarnya, apabila trade_value = 300, pada peringkat pertengahan strategi berjalan, ia sudah kehilangan segalanya. Walaupun tidak, LINK dan XTZ juga memegang kedudukan di atas 10000USDT, yang terlalu besar. Oleh itu, kita mesti menyelesaikan masalah ini dalam backtest dan lulus ujian semua mata wang.
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
Oleh kerana penyebab masalah adalah untuk membandingkan dengan harga awal, ia mungkin lebih dan lebih berat sebelah. kita boleh membandingkannya dengan purata bergerak dari tempoh masa yang lalu, backtest mata wang penuh dan melihat hasil di bawah.
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())
Prestasi strategi telah memenuhi sepenuhnya jangkaan kami, dan pulangan hampir sama. Situasi posisi akaun pecah dalam mata wang asal semua mata wang juga telah beralih dengan lancar, dan hampir tidak ada retracement. Saiz kedudukan pembukaan yang sama, hampir semua leverage adalah di bawah 1 kali, pada 12 Mac 2020 harga terjun kes melampau, ia masih tidak melebihi 4 kali, yang bermaksud bahawa kita boleh meningkatkan trade_value, dan di bawah leverage yang sama, menggandakan keuntungan. Posisi pegangan akhir hanya BCH melebihi 1000USDT, yang sangat baik.
Mengapa kedudukan akan diturunkan? Bayangkan menyertai indeks altcoin tidak berubah, satu syiling telah meningkat sebanyak 100%, dan ia akan dikekalkan untuk masa yang lama. Strategi asal akan memegang kedudukan pendek 300 * 100 = 30000USDT untuk masa yang lama, dan strategi baru akhirnya akan mengesan harga penanda aras Pada harga terkini, anda tidak akan memegang mana-mana kedudukan pada akhirnya.
(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))
Apa yang akan berlaku kepada mata wang dengan mekanisme penyaringan, dengan parameter yang sama, keuntungan peringkat awal berprestasi lebih baik, retracement lebih kecil, tetapi pulangan keseluruhan sedikit lebih rendah.
#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))
Semakin besar tetapan parameter Alpha purata bergerak eksponen, semakin sensitif penjejakan harga penanda aras, semakin sedikit transaksi, semakin rendah kedudukan pegangan akhir. apabila menurunkan leverage, pulangan juga berkurangan. Menurunkan retracement maksimum, ia boleh meningkatkan jumlah transaksi. Operasi baki khusus perlu berdasarkan hasil backtest.
Oleh kerana backtest adalah garis 1h K, ia hanya boleh dikemas kini sekali sejam, pasaran sebenar boleh dikemas kini lebih cepat, dan adalah perlu untuk menimbang tetapan khusus secara komprehensif.
Ini adalah hasil pengoptimuman:
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