Địa chỉ báo cáo nghiên cứu gốc:https://www.fmz.com/digest-topic/5584Bạn có thể đọc nó trước, bài viết này sẽ không có nội dung trùng lặp. Bài viết này sẽ làm nổi bật quá trình tối ưu hóa của chiến lược thứ hai. Sau khi tối ưu hóa, chiến lược thứ hai được cải thiện rõ ràng, nên nâng cấp chiến lược theo bài viết này.
# 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']]
Hiệu suất của chiến lược ban đầu, sau khi lựa chọn loại tiền tệ, hoạt động tốt, nhưng vẫn còn nhiều vị trí nắm giữ, nói chung là khoảng 4 lần
Nguyên tắc:
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
Vấn đề lớn nhất ban đầu là so sánh giữa giá mới nhất và giá ban đầu bắt đầu bởi chiến lược. Khi thời gian trôi qua, nó sẽ trở nên càng ngày càng lệch. Chúng ta sẽ tích lũy rất nhiều vị trí trong các loại tiền tệ này. Vấn đề lớn nhất với việc lọc tiền tệ là chúng ta vẫn có thể có các loại tiền tệ độc đáo trong tương lai dựa trên kinh nghiệm trong quá khứ của chúng ta. Sau đây là hiệu suất của chế độ không lọc. Trên thực tế, khi trade_value = 300, ở giai đoạn giữa của chiến lược chạy, nó đã mất tất cả mọi thứ. Ngay cả khi không, LINK và XTZ cũng nắm giữ các vị trí trên 10000USDT, quá lớn. Do đó, chúng ta phải giải quyết vấn đề này trong backtest và vượt qua bài kiểm tra của tất cả các loại tiền tệ.
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
Vì nguyên nhân của vấn đề là so sánh với giá ban đầu, nó có thể ngày càng thiên vị hơn.
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())
Hiệu suất của chiến lược đã hoàn toàn đáp ứng mong đợi của chúng tôi, và lợi nhuận gần như giống nhau. Tình huống bùng nổ các vị trí tài khoản trong đồng tiền gốc của tất cả các loại tiền tệ cũng đã chuyển đổi trơn tru, và hầu như không có sự thoái lui. Kích thước vị trí mở đầu tương tự, hầu hết đòn bẩy đều dưới 1 lần, vào ngày 12 tháng 3 năm 2020 giá giảm mạnh trường hợp cực đoan, nó vẫn không vượt quá 4 lần, có nghĩa là chúng tôi có thể tăng trade_value, và dưới cùng một đòn bẩy, tăng gấp đôi lợi nhuận.
Tại sao vị trí sẽ bị hạ thấp? Hãy tưởng tượng tham gia chỉ số altcoin không thay đổi, một đồng tiền đã tăng 100% và nó sẽ được duy trì trong một thời gian dài. Chiến lược ban đầu sẽ giữ các vị trí ngắn 300 * 100 = 30000USDT trong một thời gian dài, và chiến lược mới cuối cùng sẽ theo dõi giá chuẩn Tại mức giá mới nhất, bạn sẽ không giữ bất kỳ vị trí nào vào cuối.
(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))
Điều gì sẽ xảy ra với đồng tiền với cơ chế sàng lọc, với các tham số tương tự, lợi nhuận giai đoạn trước có hiệu suất tốt hơn, sự khôi phục nhỏ hơn, nhưng lợi nhuận tổng thể thấp hơn một chút.
#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))
Càng lớn thiết lập tham số Alpha của đường trung bình chuyển động theo cấp số nhân, mức độ nhạy cảm của việc theo dõi giá chuẩn càng nhiều, số lượng giao dịch càng ít, vị trí nắm giữ cuối cùng càng thấp. khi hạ đòn bẩy, lợi nhuận cũng giảm. Giảm mức khôi phục tối đa, nó có thể làm tăng khối lượng giao dịch. Các hoạt động cân bằng cụ thể cần dựa trên kết quả backtest.
Vì backtest là một đường 1h K, nó chỉ có thể được cập nhật một lần mỗi giờ, thị trường thực tế có thể được cập nhật nhanh hơn, và cần phải cân nhắc các cài đặt cụ thể một cách toàn diện.
Đây là kết quả của việc tối ưu hóa:
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