-
Notifications
You must be signed in to change notification settings - Fork 1
/
crypto-pairs-trading-distance-method
135 lines (116 loc) · 5.45 KB
/
crypto-pairs-trading-distance-method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import aiohttp
import asyncio
import pandas as pd
import numpy as np
import datetime as dt
import json
import nest_asyncio
from binance.client import Client
import matplotlib.pyplot as plt
from itertools import combinations
# Put your API key and Secret key into the variables below.
api_key = ''
api_secret = ''
pairs_binance = []
client = Client(api_key, api_secret)
exchange_info = client.get_exchange_info()
stablecoins_and_pegged = ['USDC', 'TUSD', 'USDP', 'EUR', 'AEUR', 'FDUSD', 'DAI', 'USTC', 'WBTC', 'WBETH']
for s in exchange_info['symbols']:
if s['symbol'][-4:] == 'USDT' and s['symbol'][:-4] not in stablecoins_and_pegged:
pairs_binance.append(s['symbol'])
nest_asyncio.apply()
async def fetch_data(session, url):
async with session.get(url) as response:
return await response.text()
async def create_df(data):
root_url = 'https://api.binance.com/api/v3/klines'
interval = '1h'
close_prices = pd.DataFrame()
async with aiohttp.ClientSession() as session:
tasks = [fetch_data(session, f'{root_url}?symbol={symbol}&interval={interval}') for symbol in data]
responses = await asyncio.gather(*tasks)
for symbol, response in zip(data, responses):
data = json.loads(response)
if 'msg' in data:
continue
df = pd.DataFrame(data, columns=['open_time', 'o', 'h', 'l', 'c', 'v', 'close_time', 'qav', 'num_trades', 'taker_base_vol', 'taker_quote_vol', 'ignore'])
df.index = [dt.datetime.fromtimestamp(x / 1000.0) for x in df.close_time]
close_prices[symbol] = pd.to_numeric(df['c'])
close_prices_cleaned = close_prices.dropna(axis=1)
return close_prices_cleaned
# Run the asynchronous function in an existing event loop
close_prices = asyncio.run(create_df(pairs_binance))
def normalize(df):
return df / df.iloc[0]
split_index = int(len(close_prices) * 2 / 3)
training = normalize(close_prices[:split_index])
testing = normalize(close_prices[split_index:])
def ssd(df):
return {f'{c1}-{c2}': np.sum((df[c1] - df[c2]) ** 2) for c1, c2 in combinations(df.columns, 2)}
ssd_results = ssd(training)
sorted_ssd = {k: v for k, v in sorted(ssd_results.items(), key=lambda item: item[1])}
top10_pairs = list(sorted_ssd)[:10]
# visualize a pair
pair = top10_pairs[0]
asset1, asset2 = pair.split('-')
training_time = training.index
plt.plot(training_time, training[asset1], label=asset1)
plt.plot(training_time, training[asset2], label=asset2)
# Add a legend
plt.legend()
# Display the plot
plt.show()
def pairs_df(data, pair_list, threshold=2):
pairs_dict = {}
for pair in pair_list:
asset1, asset2 = pair.split('-')
pairs = pd.DataFrame({
'asset1': data[asset1],
'asset2': data[asset2]
})
pairs['diff'] = pairs['asset1'] - pairs['asset2']
pairs['z_score'] = (pairs['diff'] - pairs['diff'].mean()) / pairs['diff'].std()
long_m1 = pairs['z_score'].lt(-threshold)
long_m2 = pairs['z_score'].gt(0)
pairs['long_positions'] = long_m1.where(long_m1|long_m2).ffill().fillna(False)
pairs['buy'] = pairs['long_positions'] & pairs['long_positions'].diff()
pairs['long_exit'] = long_m2 & pairs['long_positions'].shift()
pairs[['long_positions', 'buy', 'long_exit']] = pairs[['long_positions', 'buy', 'long_exit']].astype(int)
short_m1 = pairs['z_score'].gt(threshold)
short_m2 = pairs['z_score'].lt(0)
pairs['short_positions'] = short_m1.where(short_m1|short_m2).ffill().fillna(False)
pairs['sell'] = pairs['short_positions'] & pairs['short_positions'].diff()
pairs['short_exit'] = short_m2 & pairs['short_positions'].shift()
pairs[['short_positions', 'sell', 'short_exit']] = pairs[['short_positions', 'sell', 'short_exit']].astype(int)
# change index from time to the range of integers. It makes it easier to refer to the index.
pairs['time'] = pairs.index
pairs.reset_index(drop=True, inplace=True)
pairs_dict[pair] = pairs
return pairs_dict
pairs_dict = pairs_df(testing, top10_pairs, 2)
def strategy_return(data):
pnl = 0
for df in data.values():
long_entries = df[df['buy'] == 1].index
short_entries = df[df['sell'] == 1].index
for idx in long_entries:
exit_idx = df[(df.index > idx) & (df['long_exit'])].index
if not exit_idx.empty:
ret = (df['asset1'][exit_idx[0]] / df['asset1'][idx] - 1) + (1- df['asset2'][exit_idx[0]] / df['asset2'][idx])
pnl += ret
# if there is no mean reversion until the end of period, we close the position.
else:
ret = (df['asset1'].iloc[-1] / df['asset1'][idx] - 1) + (1- df['asset2'].iloc[-1] / df['asset2'][idx])
pnl += ret
for idx in short_entries:
exit_idx = df[(df.index > idx) & (df['short_exit'])].index
if not exit_idx.empty:
ret = (1- df['asset1'][exit_idx[0]] / df['asset1'][idx]) + (df['asset2'][exit_idx[0]] / df['asset2'][idx] - 1)
pnl += ret
# if there is no mean reversion until the end of period, we close the position.
else:
ret = (1- df['asset1'].iloc[-1] / df['asset1'][idx]) + (df['asset2'].iloc[-1] / df['asset2'][idx] - 1)
pnl += ret
return pnl
testing_data_return = strategy_return(pairs_dict)
print("testing data return is " + "{:.2%}".format(testing_data_return))