-
Notifications
You must be signed in to change notification settings - Fork 0
/
ccxt_lib v6
284 lines (242 loc) · 12 KB
/
ccxt_lib v6
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
import ccxt.async_support as ccxt
import asyncio
from collections import defaultdict
from requests import Request, Session
from requests.exceptions import ConnectionError, Timeout, TooManyRedirects
import json
import requests
import time
import statistics
def cmc(starting_rank, number_of_tokens):
url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest"
parameters = {
'start':'1',
'limit': str(number_of_tokens),
'convert':'USD'
}
headers = {
'Accepts': 'application/json',
'X-CMC_PRO_API_KEY': '',
}
session = Session()
session.headers.update(headers)
try:
response = session.get(url, params=parameters)
data = json.loads(response.text)
#print(data)
except (ConnectionError, Timeout, TooManyRedirects) as e:
print(e)
# exclude stablecoins
cmc_coin_list = []
for coin in data['data']:
if starting_rank <= coin['cmc_rank'] < number_of_tokens and 'stablecoin' not in coin['tags']:
cmc_coin_list.append(coin['symbol'])
return cmc_coin_list
coins = cmc(0, 500)
async def fetch_all_fees_and_statuses(exchanges, symbols):
withdrawal_fees = {}
trading_fees = defaultdict(lambda: defaultdict(dict))
coin_statuses = defaultdict(lambda: defaultdict(dict))
async def fetch_fees_and_statuses_for_exchange(exchange_id, exchange):
try:
currencies = await exchange.fetch_currencies()
withdrawal_fees[exchange_id] = {
coin: currencies[coin]['fee'] if coin in currencies and 'fee' in currencies[coin] else None
for coin in coins
}
for coin in coins:
if coin in currencies:
coin_statuses[exchange_id][coin] = {
'withdrawable': currencies[coin].get('withdraw', False),
'depositable': currencies[coin].get('deposit', False)
}
else:
coin_statuses[exchange_id][coin] = {
'withdrawable': False,
'depositable': False
}
for symbol in symbols:
try:
fees = await exchange.fetch_trading_fee(symbol)
trading_fees[exchange_id][symbol] = fees
except Exception as e:
print(f"Error fetching trading fees for {exchange_id} {symbol}: {str(e)}")
trading_fees[exchange_id][symbol] = {'maker': 0.001, 'taker': 0.001}
except Exception as e:
print(f"Error fetching fees and statuses for {exchange_id}: {str(e)}")
withdrawal_fees[exchange_id] = {coin: None for coin in coins}
coin_statuses[exchange_id] = {coin: {'withdrawable': False, 'depositable': False} for coin in coins}
await asyncio.gather(*[fetch_fees_and_statuses_for_exchange(exchange_id, exchange) for exchange_id, exchange in exchanges.items()])
return withdrawal_fees, dict(trading_fees), dict(coin_statuses)
async def fetch_order_books(exchanges, symbols):
order_books = defaultdict(dict)
latencies = defaultdict(list)
async def fetch_order_book_for_symbol(exchange_id, exchange, symbol):
try:
start_time = time.time()
order_book = await exchange.fetch_order_book(symbol)
end_time = time.time()
latency = (end_time - start_time) * 1000 # Convert to milliseconds
latencies[exchange_id].append(latency)
order_books[symbol][exchange_id] = order_book
except Exception as e:
print(f"Error fetching order book for {exchange_id} {symbol}: {str(e)}")
await asyncio.gather(*[fetch_order_book_for_symbol(exchange_id, exchange, symbol)
for exchange_id, exchange in exchanges.items()
for symbol in symbols])
return dict(order_books), dict(latencies)
def estimate_slippage(order_book, amount, side='buy'):
"""
Estimate slippage based on the order book depth.
"""
total_amount = 0
weighted_price = 0
target_amount = amount
if side == 'buy':
for price, size in order_book['asks']:
if total_amount < target_amount:
fill_amount = min(size, target_amount - total_amount)
total_amount += fill_amount
weighted_price += price * fill_amount
else:
break
else: # sell
for price, size in order_book['bids']:
if total_amount < target_amount:
fill_amount = min(size, target_amount - total_amount)
total_amount += fill_amount
weighted_price += price * fill_amount
else:
break
if total_amount > 0:
average_price = weighted_price / total_amount
if side == 'buy':
slippage = (average_price - order_book['asks'][0][0]) / order_book['asks'][0][0]
else:
slippage = (order_book['bids'][0][0] - average_price) / order_book['bids'][0][0]
return slippage
return 0
def calculate_profit_with_fees_and_slippage(buy_exchange, sell_exchange, buy_price, sell_price, amount, withdrawal_fees, trading_fees, symbol, buy_order_book, sell_order_book):
# Estimate slippage
buy_slippage = estimate_slippage(buy_order_book, amount, 'buy')
sell_slippage = estimate_slippage(sell_order_book, amount, 'sell')
# Adjust prices for slippage
adjusted_buy_price = buy_price * (1 + buy_slippage)
adjusted_sell_price = sell_price * (1 - sell_slippage)
buy_fee_rate = trading_fees[buy_exchange][symbol].get('taker', 0.001)
buy_fee = amount * buy_fee_rate
coins_bought = (amount - buy_fee) / adjusted_buy_price
coin = symbol.split('/')[0]
withdrawal_fee = withdrawal_fees[buy_exchange].get(coin, 0) or 0
coins_after_withdrawal = coins_bought - withdrawal_fee
sell_fee_rate = trading_fees[sell_exchange][symbol].get('maker', 0.001)
sale_amount_before_fee = coins_after_withdrawal * adjusted_sell_price
sell_fee = sale_amount_before_fee * sell_fee_rate
sale_amount = sale_amount_before_fee - sell_fee
profit = sale_amount - amount
profit_percentage = (profit / amount) * 100
return profit, profit_percentage, buy_slippage, sell_slippage
def find_arbitrage_opportunities(all_order_books, initial_capital, withdrawal_fees, trading_fees, coin_statuses, min_profit_percentage):
opportunities = []
for symbol, order_books in all_order_books.items():
exchange_names = list(order_books.keys())
for i in range(len(exchange_names)):
for j in range(i + 1, len(exchange_names)):
buy_exchange = exchange_names[i]
sell_exchange = exchange_names[j]
if not order_books[buy_exchange]['asks'] or not order_books[sell_exchange]['bids']:
continue
coin = symbol.split('/')[0]
buy_price = order_books[buy_exchange]['asks'][0][0]
sell_price = order_books[sell_exchange]['bids'][0][0]
profit, profit_percentage, buy_slippage, sell_slippage = calculate_profit_with_fees_and_slippage(
buy_exchange, sell_exchange, buy_price, sell_price,
initial_capital, withdrawal_fees, trading_fees, symbol,
order_books[buy_exchange], order_books[sell_exchange]
)
if profit_percentage > min_profit_percentage:
opportunities.append({
'symbol': symbol,
'buy_exchange': buy_exchange,
'sell_exchange': sell_exchange,
'buy_price': buy_price,
'sell_price': sell_price,
'profit': profit,
'profit_percentage': profit_percentage,
'buy_withdrawable': coin_statuses[buy_exchange][coin]['withdrawable'],
'sell_depositable': coin_statuses[sell_exchange][coin]['depositable'],
'buy_slippage': buy_slippage,
'sell_slippage': sell_slippage
})
return opportunities
async def main():
exchanges = {
'binance': ccxt.binance({
'apiKey': '',
'secret': '',
}),
'huobi': ccxt.huobi({
'apiKey': '',
'secret': ''
}),
'kucoin': ccxt.kucoin({
'apiKey': '',
'secret': ''
}),
'bitget': ccxt.bitget({
'apiKey': '',
'secret': ''
}),
}
coins = cmc(0, 500)
symbols = [f"{coin}/USDT" for coin in coins]
initial_capital = 5000 # $5000 initial capital
min_profit_percentage = 0.1 # 0.1% minimum profit
# Fetch all fees and statuses once at the start
withdrawal_fees, trading_fees, coin_statuses = await fetch_all_fees_and_statuses(exchanges, symbols)
try:
while True:
start_time = time.time()
all_order_books, latencies = await fetch_order_books(exchanges, symbols)
fetch_time = time.time() - start_time
opportunities = find_arbitrage_opportunities(all_order_books, initial_capital, withdrawal_fees, trading_fees, coin_statuses, min_profit_percentage)
if opportunities:
print("Arbitrage opportunities found:")
for opportunity in opportunities:
print(f"Symbol: {opportunity['symbol']}")
print(f"Buy from {opportunity['buy_exchange']} at {opportunity['buy_price']}")
print(f"Sell on {opportunity['sell_exchange']} at {opportunity['sell_price']}")
print(f"Potential profit: ${opportunity['profit']:.2f} ({opportunity['profit_percentage']:.2f}%)")
print(f"Estimated buy slippage: {opportunity['buy_slippage']:.2%}")
print(f"Estimated sell slippage: {opportunity['sell_slippage']:.2%}")
status_messages = []
if not opportunity['buy_withdrawable']:
status_messages.append(f"Cannot withdraw from {opportunity['buy_exchange']}")
if not opportunity['sell_depositable']:
status_messages.append(f"Cannot deposit to {opportunity['sell_exchange']}")
if status_messages:
print("Status: " + ", ".join(status_messages))
else:
print("Status: Fully executable")
# Add latency information
buy_exchange_latency = statistics.mean(latencies[opportunity['buy_exchange']])
sell_exchange_latency = statistics.mean(latencies[opportunity['sell_exchange']])
print(f"Average latency: Buy exchange: {buy_exchange_latency:.2f}ms, Sell exchange: {sell_exchange_latency:.2f}ms")
print("---")
else:
print("No profitable arbitrage opportunities found.")
# Print overall latency statistics
print("\nLatency Statistics:")
for exchange, exchange_latencies in latencies.items():
avg_latency = statistics.mean(exchange_latencies)
max_latency = max(exchange_latencies)
min_latency = min(exchange_latencies)
print(f"{exchange}: Avg: {avg_latency:.2f}ms, Max: {max_latency:.2f}ms, Min: {min_latency:.2f}ms")
print(f"Total fetch time: {fetch_time:.2f} seconds")
print("---")
await asyncio.sleep(10) # Wait for 10 seconds before checking again
finally:
# Close all exchange instances
await asyncio.gather(*[exchange.close() for exchange in exchanges.values()])
if __name__ == "__main__":
asyncio.run(main())