1
+ import ccxt ,getpip
2
+ import time
3
+ from datetime import datetime
4
+ import json
5
+ import os
6
+ import sys
7
+
8
+ # Executes buying and selling
9
+ class binance_api :
10
+
11
+ # Initialize
12
+ def __init__ (self , api_keys , logfile = False , block = False , account_json = None ):
13
+ self .api_keys = {'api_key' :api_keys ['binance_keys' ]['api_key' ],'secret_key' :api_keys ['binance_keys' ]['secret_key' ]}
14
+ self .exchange = ccxt .binance ({'apiKey' :self .api_keys ['api_key' ], 'secret' :self .api_keys ['secret_key' ]})
15
+ self .logfile = logfile
16
+ self .block = block
17
+ self .started_time = datetime .now ()
18
+ self .account_json = account_json
19
+
20
+ # Set of tickers to block if specified
21
+ if self .block :
22
+ self .block_set = set ()
23
+
24
+ # Reset the exchange
25
+ def refresh_exchange (self ):
26
+ self .exchange = ccxt .binance ({'apiKey' :self .api_keys ['api_key' ], 'secret' :self .api_keys ['secret_key' ]})
27
+
28
+ # Buying of real cryto
29
+ def buy_crypto (self , ticker , buy_volume ):
30
+
31
+ # Try creating the buy order
32
+ for i in range (10 ):
33
+ try :
34
+ buy_trade = self .exchange .create_order (ticker ,'market' ,'buy' ,buy_volume )
35
+ break
36
+ except Exception as e :
37
+ print (e )
38
+ if i == 9 :
39
+ print ('Could not buy, exiting thread' )
40
+ exit ()
41
+ print ('\n Buy did not work, trying again' )
42
+
43
+ # Print buy
44
+ try :
45
+ if buy_trade .get ('status' ) != 'open' :
46
+ avg_price = sum ([float (x ['price' ]) * float (x ['qty' ]) for x in buy_trade ['info' ]['fills' ]])/ sum ([float (x ['qty' ]) for x in buy_trade ['info' ]['fills' ]])
47
+ print ('\n Bought %s of %s at %s with %s %s of fees on %s\n ' % (buy_trade ['amount' ]\
48
+ , buy_trade ['symbol' ], avg_price , buy_trade ['fee' ]['cost' ], buy_trade ['fee' ]['currency' ]\
49
+ , datetime .now ().strftime ('%b %d - %H:%M:%S' )))
50
+ else :
51
+ print ('\n Bought %.8f at %s\n ' % (buy_volume , datetime .now ().strftime ('%b %d - %H:%M:%S' )))
52
+ except Exception as e :
53
+ print (e )
54
+ print ('\n Error in print of buy' )
55
+
56
+ return buy_trade , buy_volume
57
+
58
+ # Selling of real crypto
59
+ def sell_crypto (self , ticker , buy_volume , buy_trade ):
60
+
61
+ # Try to sell 10 times
62
+ for i in range (10 ):
63
+ try :
64
+ # Fees in USDT, Buy/Sell crypto amount is equal -- check if behaviour is the same when you have BNB in the wallet
65
+ if ticker [- 4 :] == 'USDT' :
66
+ sell_volume = buy_volume
67
+ # If fees in the returned trade (filled on order)
68
+ elif buy_trade ['fee' ]:
69
+ if buy_trade ['fee' ]['currency' ] == 'BNB' :
70
+ sell_volume = buy_volume
71
+ else :
72
+ # Converting fee currency to buy currency
73
+ ticker_pair = ticker .split ('/' )
74
+ if ticker_pair [0 ] != buy_trade ['fee' ]['currency' ]:
75
+ fee_pair = [ticker_pair [0 ], buy_trade ['fee' ]['currency' ]]
76
+ fee_ticker = '/' .join (fee_pair )
77
+ if fee_ticker not in tickers :
78
+ fee_ticker = '/' .join (fee_pair [::- 1 ])
79
+ fee = self .exchange .fetchTicker (fee_ticker )
80
+ fee_price = (fee ['bid' ] + fee ['ask' ]) / 2
81
+ sell_volume = buy_volume - fee_price * buy_trade ['fee' ]['cost' ]
82
+
83
+ # When fee currency is the same as the buy currency
84
+ else :
85
+ sell_volume = buy_volume - buy_trade ['fee' ]['cost' ]
86
+ else :
87
+ sell_volume = buy_volume
88
+
89
+ sell_trade = self .exchange .create_order (ticker ,'market' ,'sell' ,sell_volume )
90
+ break
91
+
92
+ except Exception as e :
93
+ error = e
94
+ if 'MIN_NOTIONAL' in str (error ):
95
+ buy_volume = buy_volume * 1.0005
96
+ elif 'insufficient balance' in str (error ):
97
+ buy_volume = buy_volume * 0.9995
98
+ else :
99
+ self .refresh_exchange ()
100
+ print (e )
101
+ print ('\n \n Trying to sell %.10f again' % buy_volume )
102
+
103
+ # Print sell
104
+ if sell_trade ['status' ] != 'open' :
105
+ avg_price = sum ([float (x ['price' ]) * float (x ['qty' ]) for x in sell_trade ['info' ]['fills' ]])/ sum ([float (x ['qty' ]) for x in sell_trade ['info' ]['fills' ]])
106
+ print ('\n Sold %s of %s at %s with %s %s of fees on %s' % (sell_trade ['amount' ], sell_trade ['symbol' ], avg_price \
107
+ , sell_trade ['fee' ]['cost' ], sell_trade ['fee' ]['currency' ], datetime .now ().strftime ('%b %d - %H:%M:%S' )))
108
+ else :
109
+ print ('\n Sold %.8f at %s' % (sell_volume , datetime .now ().strftime ('%b %d - %H:%M:%S' )))
110
+
111
+ return sell_trade
112
+
113
+
114
+ # Get data from self.exchange and print it
115
+ def simulate_trade (self , buy , volume , ticker , conversion ):
116
+ if conversion [- 4 :] == 'USDT' and ticker [- 4 :] == 'USDT' :
117
+ usdpair = {'bid' :1 ,'ask' :1 }
118
+ else :
119
+ usdpair = self .exchange .fetchTicker (conversion )
120
+ if buy :
121
+ bid_ask , buy_sell = 'ask' , 'Buying'
122
+ else :
123
+ bid_ask , buy_sell = 'bid' , 'Selling'
124
+ try :
125
+ trade_price = self .exchange .fetchTicker (ticker )[bid_ask ]
126
+ price = (usdpair ['bid' ]+ usdpair ['ask' ])/ 2
127
+ print ('\n {} {} at {:.8f} {} = ${:.6f}' .format (buy_sell , volume , trade_price , ticker , trade_price * volume * price ))
128
+
129
+ except Exception as e :
130
+ print (e , '\n Error in fetching ticker info' )
131
+ trade = {'symbol' : ticker ,'side' :'buy' if buy else 'sell' , 'amount' :volume , 'cost' :trade_price * volume }
132
+
133
+ return trade
134
+
135
+
136
+ # Summarise trade buy and sell
137
+ def print_summary (self , simulate , ticker , buy_trade , sell_trades , conversion ):
138
+
139
+ if not simulate :
140
+ buy_id , sell_ids = buy_trade ['info' ]['orderId' ], [i ['info' ]['orderId' ] for i in sell_trades ]
141
+ buy_prices , sell_prices = [], []
142
+ for i in range (20 ):
143
+ try :
144
+ trades = self .exchange .fetchMyTrades (ticker )
145
+ break
146
+ except Exception as e :
147
+ print (e )
148
+ print ("Couldn't fetch trades, tying again" )
149
+
150
+ # Loop over trades as one order could have had multiple fills
151
+ for trade in trades [::- 1 ]:
152
+ if buy_id == trade ['info' ]['orderId' ]:
153
+ buy_prices .append ({'amount' :trade ['amount' ],'cost' :trade ['cost' ],'fee' :trade ['fee' ]})
154
+ elif trade ['info' ]['orderId' ] in sell_ids :
155
+ sell_prices .append ({'amount' :trade ['amount' ],'cost' :trade ['cost' ],'fee' :trade ['fee' ]}) # Actual return uses fills
156
+
157
+ buy_fee = sum ([x ['fee' ]['cost' ] for x in buy_prices ])
158
+ sell_fee = sum ([x ['fee' ]['cost' ] for x in sell_prices ])
159
+
160
+ # Log fees
161
+ for i in range (20 ):
162
+ try :
163
+ if buy_prices [0 ]['fee' ]['currency' ] == 'BNB' :
164
+ bnb_dollar = self .exchange .fetch_ticker ('BNB/USDT' )
165
+ bnb_price = (bnb_dollar ['bid' ] + bnb_dollar ['ask' ]) / 2
166
+ buy_fee_dollar = buy_fee * bnb_price
167
+ if sell_prices [0 ]['fee' ]['currency' ] == 'BNB' :
168
+ sell_fee_dollar = sell_fee * bnb_price
169
+ elif buy_prices [0 ]['fee' ]['currency' ] == 'USDT' :
170
+ buy_fee_dollar = buy_fee
171
+ sell_fee_dollar = sell_fee
172
+ else :
173
+ buy_crypto_dollar = self .exchange .fetch_ticker (buy_prices [0 ]['fee' ]['currency' ]+ '/USDT' )
174
+ sell_crypto_dollar = self .exchange .fetch_ticker (sell_prices [0 ]['fee' ]['currency' ]+ '/USDT' )
175
+ buy_fee_price = (buy_crypto_dollar ['bid' ]+ buy_crypto_dollar ['ask' ])/ 2
176
+ sell_fee_price = (sell_crypto_dollar ['bid' ]+ sell_crypto_dollar ['ask' ])/ 2
177
+ buy_fee_dollar = buy_fee_price * buy_fee
178
+ sell_fee_dollar = sell_fee_price * sell_fee
179
+
180
+ ticker_pair = ticker .split ('/' )
181
+ if ticker_pair [1 ] == 'USDT' :
182
+ ticker_info = {'bid' :1 ,'ask' :1 }
183
+ else :
184
+ ticker_info = self .exchange .fetch_ticker (ticker_pair [1 ]+ '/' + 'USDT' )
185
+ break
186
+ except Exception as e :
187
+ print (e )
188
+ print ('\n Error in printing executed trades' )
189
+ else :
190
+ sell_prices , buy_prices = sell_trades , [buy_trade ]
191
+ sell_fee_dollar , buy_fee_dollar = 0 , 0
192
+ if ticker [- 4 :] == 'USDT' :
193
+ ticker_info = {'bid' :1 , 'ask' :1 }
194
+ else :
195
+ ticker_info = self .exchange .fetch_ticker (ticker .split ('/' )[1 ]+ '/' + 'USDT' )
196
+
197
+ buy_total = sum (i ['cost' ] for i in buy_prices )
198
+ sell_total = sum ([i ['cost' ] for i in sell_prices ])
199
+ avg_bid_ask = (ticker_info ['bid' ] + ticker_info ['ask' ]) / 2
200
+
201
+ gain_loss = (sell_total - buy_total ) * avg_bid_ask - sell_fee_dollar - buy_fee_dollar
202
+ gain_loss_percent = gain_loss / (buy_total * avg_bid_ask - sell_fee_dollar - buy_fee_dollar ) * 100
203
+
204
+ gain_text = '\n Profit/Loss: $%.6f %.3f%%' % (gain_loss , gain_loss_percent )
205
+ print (gain_text )
206
+
207
+ return gain_text , buy_total , sell_total
208
+
209
+ # Send a telegram of the profits and losses
210
+ def send_telegram (self , ticker , buy_total , sell_total , gain_text , status , simulate ):
211
+
212
+ # Sending a telegram message to myself
213
+ import telegram
214
+ with open ('../telegram_keys.json' ) as json_file :
215
+ telegram_dict = json .load (json_file )
216
+ if type (status ) == dict :
217
+ full_info_text = '(%s) %s\n Bought %.6f and sold %.6f BTC\n \n @%s - %s:\n "%s"\n ' % (ticker , gain_text , float (buy_total ), \
218
+ float (sell_total ), status ['url' ], status ['update_time' ].strftime ('%m/%d %H:%M:%S' ), status ['update_text' ])
219
+ else :
220
+ try :
221
+ full_text = status .text
222
+ except :
223
+ full_text = status .full_text
224
+ full_info_text = '(%s) %s\n Bought %.6f and sold %.6f BTC\n \n @%s - %s:\n "%s"\n ' % (ticker , gain_text , float (buy_total ), \
225
+ float (sell_total ), status .user .screen_name , status .created_at .strftime ('%m/%d %H:%M:%S' ), full_text )
226
+
227
+ bot = telegram .Bot (token = telegram_dict ['api_key' ])
228
+ bot .send_message (chat_id = telegram_dict ['chat_id' ], text = full_info_text )
229
+
230
+
231
+ # Log the trade
232
+ def log_trade (self , ticker , buy_volume , hold_times , buy_trade , sell_trades , gain_text , status , simulate ):
233
+ # Log trade
234
+ now = datetime .now ().strftime ("%y-%m-%d_%H:%M:%S" )
235
+
236
+ # Saving name format: time_started, json_file_used, simluation/live
237
+ with open ("prev_trades/trades_%s_binance_%s_%s.txt" % (self .started_time .strftime ('%Y-%m-%d_%H-%M-%S' ), self .account_json , 'simulation' if simulate else 'live' ), "a" ) as log_name :
238
+ # If status is a dict, the message was from a web scrape
239
+ if type (status ) == dict :
240
+ json .dump ({'url' :status ['url' ],'update_text' :status ['update_text' ],'update_time' :status ['update_time' ].strftime ('%Y-%m-%d_%H:%M:%S' ),'ticker' :ticker ,'hold_times' :hold_times ,'complete_time' :now ,'buy_volume' :buy_volume ,'buy' :buy_trade ,'sell' :sell_trades ,'telegram' :gain_text }, log_name )
241
+
242
+ # If tweet from stream or query
243
+ else :
244
+ try :
245
+ full_text = status .text
246
+ except :
247
+ full_text = status .full_text
248
+ json .dump ({'user' :status .user .screen_name ,'tweet' :full_text ,'tweet_time' :status .created_at .strftime ('%Y-%m-%d_%H:%M:%S' ),'ticker' :ticker ,'hold_times' :hold_times ,'complete_time' :now ,'buy_volume' :buy_volume ,'buy' :buy_trade ,'sell' :sell_trades ,'telegram' :gain_text }, log_name )
249
+ log_name .write ('\n ' )
250
+
251
+
252
+ # Execute trade
253
+ def execute_trade (self , pair , hold_times = 60 , buy_volume = 50 , simulate = False , status = None ):
254
+
255
+ # Dealing with buy_sell volume pair or just a buy_volume
256
+ if type (buy_volume ) != list :
257
+ sell_volumes = [buy_volume / len (hold_times ) for _ in hold_times ]
258
+ else :
259
+ sell_volumes = buy_volume [1 ]
260
+ buy_volume = buy_volume [0 ]
261
+
262
+ # Ticker and convesion
263
+ ticker = pair [0 ]+ '/' + pair [1 ]
264
+ tousd1 = pair [0 ]+ '/USDT'
265
+ tousd2 = pair [1 ]+ '/USDT'
266
+
267
+ # If there is a block put on trading this ticker
268
+ if self .block :
269
+ if ticker in self .block_set :
270
+ print ('\n Trade of ' + ticker + ' blocked in ' + str (self .block_set ))
271
+ return
272
+ # When bought add and blocker flag set
273
+ self .block_set .add (ticker )
274
+ print ('Added to blockset ' + str (self .block_set ))
275
+
276
+ # Buy order
277
+ if not simulate :
278
+ buy_trade , buy_volume = self .buy_crypto (ticker , buy_volume )
279
+ else :
280
+ buy_trade = self .simulate_trade (True , buy_volume , ticker , tousd2 )
281
+
282
+
283
+ # Sell in multiple stages based on hold_times
284
+ prev_sell_time = 0
285
+ sell_trades = []
286
+ for hold , sell_volume in zip (hold_times , sell_volumes ):
287
+ time .sleep (hold - prev_sell_time )
288
+ prev_sell_time = hold
289
+
290
+ # Sell order
291
+ if not simulate :
292
+ sell_trades .append (self .sell_crypto (ticker , sell_volume , buy_trade ))
293
+ else :
294
+ sell_trades .append (self .simulate_trade (False , sell_volume , ticker , tousd2 ))
295
+
296
+ # Remove block when trade finishes
297
+ if self .block :
298
+ self .block_set .remove (ticker )
299
+ print ('Removing %s from block set' % (ticker ))
300
+
301
+ print ('\n \n TRADE FINISHED\n ' )
302
+
303
+ # Print summary and log
304
+ try :
305
+ gain_text , buy_total , sell_total = self .print_summary (simulate , ticker , buy_trade , sell_trades , tousd2 )
306
+ except Exception as e :
307
+ print ('\n Failed to print summary\n ' )
308
+ print (e )
309
+
310
+ # Send telegram message
311
+ if 'telegram_keys.json' in os .listdir ('../' ) and not simulate :
312
+ self .send_telegram (ticker , buy_total , sell_total , gain_text , status , simulate )
313
+
314
+ # Log trade
315
+ if self .logfile :
316
+ self .log_trade (ticker , buy_volume , hold_times , buy_trade , sell_trades , gain_text , status , simulate )
317
+
0 commit comments