class Trader(object): """ Used for handling all trade functionality """ def __init__(self, secrets): self.trade_params = secrets["tradeParameters"] self.pause_params = secrets["pauseParameters"] self.Bittrex = Bittrex(secrets) self.Messenger = Messenger(secrets) self.Database = Database() def initialise(self): """ Fetch the initial coin pairs to track and to print the header line """ try: if len(self.Database.app_data["coinPairs"]) < 1: self.Database.store_coin_pairs(self.get_markets("BTC")) self.Messenger.print_header( len(self.Database.app_data["coinPairs"])) except ConnectionError as exception: self.Messenger.print_exception_error("connection") logger.exception(exception) exit() def analyse_pauses(self): """ Check all the paused buy and sell pairs and reactivate the necessary ones """ if self.Database.check_resume(self.pause_params["buy"]["pauseTime"], "buy"): self.Database.store_coin_pairs(self.get_markets("BTC")) self.Messenger.print_resume_pause( len(self.Database.app_data["coinPairs"]), "buy") if self.Database.check_resume(self.pause_params["sell"]["pauseTime"], "sell"): self.Messenger.print_resume_pause( self.Database.app_data["pausedTrackedCoinPairs"], "sell") self.Database.resume_sells() def analyse_buys(self): """ Analyse all the un-paused coin pairs for buy signals and apply buys """ trade_len = len(self.Database.trades["trackedCoinPairs"]) pause_trade_len = len(self.Database.app_data["pausedTrackedCoinPairs"]) if (trade_len < 1 or pause_trade_len == trade_len ) and trade_len < self.trade_params["buy"]["maxOpenTrades"]: for coin_pair in self.Database.app_data["coinPairs"]: self.buy_strategy(coin_pair) def analyse_sells(self): """ Analyse all the un-paused tracked coin pairs for sell signals and apply sells """ for coin_pair in self.Database.trades["trackedCoinPairs"]: if coin_pair not in self.Database.app_data[ "pausedTrackedCoinPairs"]: self.sell_strategy(coin_pair) def buy_strategy(self, coin_pair): """ Applies the buy checks on the coin pair and handles the results appropriately :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str """ if (len(self.Database.trades["trackedCoinPairs"]) >= self.trade_params["buy"]["maxOpenTrades"] or coin_pair in self.Database.trades["trackedCoinPairs"]): return rsi = self.calculate_RSI(coin_pair=coin_pair, period=14, unit=self.trade_params["tickerInterval"]) day_volume = self.get_current_24hr_volume(coin_pair) current_buy_price = self.get_current_price(coin_pair, "ask") if self.check_buy_parameters(rsi, day_volume, current_buy_price): buy_stats = {"rsi": rsi, "24HrVolume": day_volume} self.buy(coin_pair, self.trade_params["buy"]["btcAmount"], current_buy_price, buy_stats) elif rsi is not None and rsi <= self.pause_params["buy"][ "rsiThreshold"]: self.Messenger.print_no_buy(coin_pair, rsi, day_volume, current_buy_price) elif rsi is not None: self.Messenger.print_pause(coin_pair, rsi, self.pause_params["buy"]["pauseTime"], "buy") self.Database.pause_buy(coin_pair) def sell_strategy(self, coin_pair): """ Applies the sell checks on the coin pair and handles the results appropriately :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str """ if (coin_pair in self.Database.app_data["pausedTrackedCoinPairs"] or coin_pair not in self.Database.trades["trackedCoinPairs"]): return rsi = self.calculate_RSI(coin_pair=coin_pair, period=14, unit=self.trade_params["tickerInterval"]) current_sell_price = self.get_current_price(coin_pair, "bid") profit_margin = self.Database.get_profit_margin( coin_pair, current_sell_price) if self.check_sell_parameters(rsi, profit_margin): sell_stats = {"rsi": rsi, "profitMargin": profit_margin} self.sell(coin_pair, current_sell_price, sell_stats) elif rsi is not None and profit_margin >= self.pause_params["sell"][ "profitMarginThreshold"]: self.Messenger.print_no_sell(coin_pair, rsi, profit_margin, current_sell_price) elif rsi is not None: self.Messenger.print_pause(coin_pair, profit_margin, self.pause_params["sell"]["pauseTime"], "sell") self.Database.pause_sell(coin_pair) def check_buy_parameters(self, rsi, day_volume, current_buy_price): """ Used to check if the buy conditions have been met :param rsi: The coin pair's current RSI :type rsi: float :param day_volume: The coin pair's current 24 hour volume :type day_volume: float :param current_buy_price: The coin pair's current price :type current_buy_price: float :return: Boolean indicating if the buy conditions have been met :rtype : bool """ return ( rsi is not None and rsi <= self.trade_params["buy"]["rsiThreshold"] and day_volume >= self.trade_params["buy"]["24HourVolumeThreshold"] and current_buy_price > self.trade_params["buy"]["minimumUnitPrice"]) def check_sell_parameters(self, rsi, profit_margin): """ Used to check if the sell conditions have been met :param rsi: The coin pair's current RSI :type rsi: float :param profit_margin: The coin pair's current profit margin :type profit_margin: float :return: Boolean indicating if the sell conditions have been met :rtype : bool """ return ((rsi is not None and rsi >= self.trade_params["sell"]["rsiThreshold"] and profit_margin > self.trade_params["sell"]["minProfitMarginThreshold"]) or profit_margin > self.trade_params["sell"]["profitMarginThreshold"]) def buy(self, coin_pair, btc_quantity, price, stats, trade_time_limit=2): """ Used to place a buy order to Bittrex. Wait until the order is completed. If the order is not filled within trade_time_limit minutes cancel it. :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param btc_quantity: The amount of BTC to buy with :type btc_quantity: float :param price: The price at which to buy :type price: float :param stats: The buy stats object :type stats: dict :param trade_time_limit: The time in minutes to wait fot the order before cancelling it :type trade_time_limit: float """ buy_data = self.Bittrex.buy_limit(coin_pair, btc_quantity / price, price) if not buy_data["success"]: return logger.error( "Failed to buy on {} market.".format(coin_pair)) self.Database.store_initial_buy(coin_pair, buy_data["result"]["uuid"]) buy_order_data = self.get_order(buy_data["result"]["uuid"], trade_time_limit * 60) self.Database.store_buy(buy_order_data["result"], stats) self.Messenger.send_buy_email(buy_order_data["result"], stats) self.Messenger.print_buy(coin_pair, price, stats["rsi"], stats["24HrVolume"]) self.Messenger.play_sw_imperial_march() def sell(self, coin_pair, price, stats, trade_time_limit=2): """ Used to place a sell order to Bittrex. Wait until the order is completed. If the order is not filled within trade_time_limit minutes cancel it. :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param price: The price at which to buy :type price: float :param stats: The buy stats object :type stats: dict :param trade_time_limit: The time in minutes to wait fot the order before cancelling it :type trade_time_limit: float """ trade = self.Database.get_open_trade(coin_pair) sell_data = self.Bittrex.sell_limit(coin_pair, trade["quantity"], price) if not sell_data["success"]: return logger.error( "Failed to sell on {} market. Bittrex error message: {}". format(coin_pair, sell_data["message"])) sell_order_data = self.get_order(sell_data["result"]["uuid"], trade_time_limit * 60) # TODO: Handle partial/incomplete sales. self.Database.store_sell(sell_order_data["result"], stats) self.Messenger.send_sell_email(sell_order_data["result"], stats) self.Messenger.print_sell(coin_pair, price, stats["rsi"], stats["profitMargin"]) self.Messenger.play_sw_theme() def get_markets(self, main_market_filter=None): """ Gets all the Bittrex markets and filters them based on the main market filter :param main_market_filter: Main market to filter on (ex: BTC, ETH, USDT) :type main_market_filter: str :return: All Bittrex markets (with filter applied, if any) :rtype : list """ markets = self.Bittrex.get_markets() if not markets["success"]: logger.error("Failed to fetch Bittrex markets") exit() markets = markets["result"] if main_market_filter is not None: market_check = main_market_filter + "-" markets = py_.filter_( markets, lambda market: market_check in market["MarketName"]) markets = py_.map_(markets, lambda market: market["MarketName"]) return markets def get_current_price(self, coin_pair, price_type): """ Gets current market price for a coin pair :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str :param price_type: The type of price to get (one of: 'ask', 'bid') :type price_type: str :return: Coin pair's current market price :rtype : float """ coin_summary = self.Bittrex.get_market_summary(coin_pair) if not coin_summary["success"]: logger.error( "Failed to fetch Bittrex market summary for the {} market". format(coin_pair)) return None if price_type == "ask": return coin_summary["result"][0]["Ask"] if price_type == "bid": return coin_summary["result"][0]["Bid"] return coin_summary["result"][0]["Last"] def get_current_24hr_volume(self, coin_pair): """ Gets current 24 hour market volume for a coin pair :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str :return: Coin pair's current 24 hour market volume :rtype : float """ coin_summary = self.Bittrex.get_market_summary(coin_pair) if not coin_summary["success"]: logger.error( "Failed to fetch Bittrex market summary for the {} market". format(coin_pair)) return None return coin_summary["result"][0]["BaseVolume"] def get_closing_prices(self, coin_pair, period, unit): """ Returns closing prices within a specified time frame for a coin pair :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param period: Number of periods to query :type period: int :param unit: Ticker interval (one of: 'oneMin', 'fiveMin', 'thirtyMin', 'hour', 'week', 'day', and 'month') :type unit: str :return: Array of closing prices :rtype : list """ historical_data = self.Bittrex.get_historical_data( coin_pair, period, unit) closing_prices = [] for i in historical_data: closing_prices.append(i["C"]) return closing_prices def get_order(self, order_uuid, trade_time_limit): """ Used to get an order from Bittrex by it's UUID. First wait until the order is completed before retrieving it. If the order is not completed within trade_time_limit seconds, cancel it. :param order_uuid: The order's UUID :type order_uuid: str :param trade_time_limit: The time in seconds to wait fot the order before cancelling it :type trade_time_limit: float :return: Order object :rtype : dict """ start_time = time.time() order_data = self.Bittrex.get_order(order_uuid) while time.time() - start_time <= trade_time_limit and order_data[ "result"]["IsOpen"]: time.sleep(10) order_data = self.Bittrex.get_order(order_uuid) if order_data["result"]["IsOpen"]: error_str = self.Messenger.print_order_error( order_uuid, trade_time_limit, order_data["result"]["Exchange"]) logger.error(error_str) if order_data["result"]["Type"] == "LIMIT_BUY": self.Bittrex.cancel(order_uuid) return order_data return order_data def calculate_RSI(self, coin_pair, period, unit): """ Calculates the Relative Strength Index for a coin_pair If the returned value is above 75, it's overbought (SELL IT!) If the returned value is below 25, it's oversold (BUY IT!) :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param period: Number of periods to query :type period: int :param unit: Ticker interval (one of: 'oneMin', 'fiveMin', 'thirtyMin', 'hour', 'week', 'day', and 'month') :type unit: str :return: RSI :rtype : float """ closing_prices = self.get_closing_prices(coin_pair, period * 3, unit) count = 0 change = [] # Calculating price changes for i in closing_prices: if count != 0: change.append(i - closing_prices[count - 1]) count += 1 if count == 15: break # Calculating gains and losses advances = [] declines = [] for i in change: if i > 0: advances.append(i) if i < 0: declines.append(abs(i)) average_gain = (sum(advances) / 14) average_loss = (sum(declines) / 14) new_avg_gain = average_gain new_avg_loss = average_loss for _ in closing_prices: if 14 < count < len(closing_prices): close = closing_prices[count] new_change = close - closing_prices[count - 1] add_loss = 0 add_gain = 0 if new_change > 0: add_gain = new_change if new_change < 0: add_loss = abs(new_change) new_avg_gain = (new_avg_gain * 13 + add_gain) / 14 new_avg_loss = (new_avg_loss * 13 + add_loss) / 14 count += 1 if new_avg_loss == 0: return None rs = new_avg_gain / new_avg_loss new_rs = 100 - 100 / (1 + rs) return new_rs
class TestBittrexV20PublicAPI(unittest.TestCase): """ Integration tests for the Bittrex public API. These will fail in the absence of an internet connection or if bittrex API goes down """ def setUp(self): self.bittrex = Bittrex(None, None, api_version=API_V2_0) def test_handles_none_key_or_secret(self): self.bittrex = Bittrex(None, None, api_version=API_V2_0) # could call any public method here actual = self.bittrex.get_market_summaries() self.assertTrue(actual['success'], "failed with None key and None secret") self.bittrex = Bittrex("123", None, api_version=API_V2_0) actual = self.bittrex.get_market_summaries() self.assertTrue(actual['success'], "failed with None secret") self.bittrex = Bittrex(None, "123", api_version=API_V2_0) actual = self.bittrex.get_market_summaries() self.assertTrue(actual['success'], "failed with None key") def test_get_currencies(self): actual = self.bittrex.get_currencies() test_basic_response(self, actual, "get_currencies") def test_get_ticker(self): self.assertRaisesRegexp(Exception, 'method call not available', self.bittrex.get_ticker, market='BTC-LTC') def test_get_market_summaries(self): actual = self.bittrex.get_market_summaries() test_basic_response(self, actual, "get_market_summaries") def test_get_market_summary(self): actual = self.bittrex.get_market_summary(market='BTC-LTC') test_basic_response(self, actual, "get_market_summary") def test_get_orderbook(self): actual = self.bittrex.get_orderbook('BTC-LTC') test_basic_response(self, actual, "get_orderbook") def test_get_wallet_health(self): actual = self.bittrex.get_wallet_health() test_basic_response(self, actual, "get_wallet_health") self.assertIsInstance(actual['result'], list) @unittest.skip("Endpoint 404s. Is this still a valid 2.0 API?") def test_get_balance_distribution(self): actual = self.bittrex.get_balance_distribution() test_basic_response(self, actual, "get_balance_distribution") self.assertIsInstance(actual['result'], list) def test_get_candles(self): actual = self.bittrex.get_candles('BTC-LTC', tick_interval=TICKINTERVAL_ONEMIN) test_basic_response(self, actual, "test_get_candles") self.assertIsInstance(actual['result'], list) def test_get_latest_candle(self): actual = self.bittrex.get_latest_candle( 'BTC-LTC', tick_interval=TICKINTERVAL_ONEMIN) test_basic_response(self, actual, "test_get_latest_candle") self.assertIsInstance(actual['result'], list)
def updateBuyingScore(): my_bittrex = Bittrex('******', '#####') my_markets = [ 'BTC-ETH', 'BTC-XRP', 'BTC-RVN', 'BTC-BSV', 'BTC-ADA', 'BTC-LTC', 'BTC-BCH', 'BTC-TRX', 'BTC-XMR', 'BTC-SPND', 'BTC-XLM', 'BTC-TUSD', 'BTC-ZEC', 'BTC-NEO', 'BTC-XEM', 'BTC-SOLVE', 'BTC-BAT', 'BTC-DGB', 'BTC-XVG', ] prev_day_dict = {} volatity_dict = {} volume_dict = {} for market in my_markets: ask = price_btc = my_bittrex.get_ticker(market)['result']['Ask'] prev_day_dict.update({ market: (ask / my_bittrex.get_market_summary(market)['result'][0]['PrevDay']) }) prev_day_ascending = sorted(prev_day_dict, key=prev_day_dict.get, reverse=False) volume_dict.update({ market: (my_bittrex.get_market_summary(market)['result'][0]['Volume']) }) volume_ascending = sorted(volume_dict, key=volume_dict.get, reverse=True) volatity_dict.update({ market: (my_bittrex.get_market_summary(market)['result'][0]['High']) / (my_bittrex.get_market_summary(market)['result'][0]['Low']) }) volatility_ascending = sorted(volatity_dict, key=volatity_dict.get, reverse=True) #print('PrevDay ', {key: rank for rank, key in enumerate(prev_day_ascending, key=prev_day_ascending.get, reverse=False)}) #print('Volume ', sorted(volume_ascending, key=volume_ascending.get, reverse=True)) i = 0 new_prev_dict = {} for key in prev_day_ascending: new_prev_dict.update({key: i}) i = i + 1 #print("New Previous",new_prev_dict) i = 0 new_volume_dict = {} for key in volume_ascending: new_volume_dict.update({key: i}) i = i + 1 #print("New Volume",new_volume_dict) i = 0 new_volatility_dict = {} #print('Volatility ', sorted(volatility_ascending, key=volatility_ascending.get, reverse=True)) for key in volatility_ascending: new_volatility_dict.update({key: i}) i = i + 1 #print("New Volatility", new_volatility_dict) score_dict = {} for market in my_markets: score_dict.update({ market: (new_prev_dict.get(market) + new_volume_dict.get(market) + new_volatility_dict.get(market)) }) sorted_score = sorted(score_dict, key=score_dict.get, reverse=False) print("THE SCORE DICT", sorted_score) for x in list(sorted_score)[0:1]: print("Buy", x) return x
class CryptoArb(object): """ Integration tests for the Bittrex Account API. * These will fail in the absence of an internet connection or if bittrex API goes down. * They require a valid API key and secret issued by Bittrex. * They also require the presence of a JSON file called secrets.json. It is structured as such: { "key": "12341253456345", "secret": "3345745634234534" } """ def setUp(self): with open("secrets.json") as secrets_file: self.secrets = json.load(secrets_file) secrets_file.close() self.bittrex = Bittrex(self.secrets['key'], self.secrets['secret']) def test(self): desiredResult = .95 ## ETHEREUM btcResult = 0 LoopNumber = 1 while btcResult < desiredResult: markets = self.bittrex.get_market_summaries() marketCount = len(markets["result"]) print("Start - " + str(LoopNumber)) btcCount = 0 ethCount = 0 ### ~~ Filling the BTC-ALT Matrix ~~ ### # Counting the number of BTC Currency Pairs btcLoopCount = 0 while btcLoopCount < marketCount: if "BTC-" in markets["result"][btcLoopCount]["MarketName"]: btcCount = btcCount + 1 btcLoopCount = btcLoopCount + 1 # Creating the BTC pair-exchange matrix btcCol, btcRow = 2, btcCount BTCMatrix = [[0 for x in range(btcCol)] for y in range(btcRow)] # Filling the BTC Matrix btcLoopCount = 0 btcMatrixRowCount = 0 while btcLoopCount < marketCount: if "BTC-" in markets["result"][btcLoopCount]["MarketName"]: BTCMatrix[btcMatrixRowCount][0] = markets["result"][ btcLoopCount]["MarketName"] BTCMatrix[btcMatrixRowCount][1] = markets["result"][ btcLoopCount]["Ask"] btcMatrixRowCount = btcMatrixRowCount + 1 btcLoopCount = btcLoopCount + 1 ### ~~ Filling the ETH-ALT Matrix ~~ ### # Counting the number of ETH Currency Pairs ethLoopCount = 0 while ethLoopCount < marketCount: if "ETH-" in markets["result"][ethLoopCount]["MarketName"]: ethCount = ethCount + 1 ethLoopCount = ethLoopCount + 1 # Creating the ETH pair-exchange matrix ethCol, ethRow = 2, ethCount ETHMatrix = [[0 for x in range(ethCol)] for y in range(ethRow)] # Filling the ETH Matrix ethLoopCount = 0 ethMatrixRowCount = 0 while ethLoopCount < marketCount: if "ETH-" in markets["result"][ethLoopCount]["MarketName"]: ETHMatrix[ethMatrixRowCount][0] = markets["result"][ ethLoopCount]["MarketName"] ETHMatrix[ethMatrixRowCount][1] = markets["result"][ ethLoopCount]["Bid"] ethMatrixRowCount = ethMatrixRowCount + 1 ethLoopCount = ethLoopCount + 1 btc_ethTick = self.bittrex.get_ticker("BTC-ETH") btc_eth_BTC = btc_ethTick["result"]["Bid"] # ~~~ Comparing Bitcoin Arbitrage Returns ~~~ # arbBTCPairs = [] arbBTCReturn = [] arbBTCTestReturn = [] arbBTCRow = 0 for btcAlt in BTCMatrix: for ethAlt in ETHMatrix: if ethAlt[0][4:] == btcAlt[0][4:]: btcResult = 0 #arbBTCPairs.append(str(btcAlt[0]) + " > " + str(ethAlt[0]) + " > BTC_ETH") arbPath = str(btcAlt[0]) + " > " + str( ethAlt[0]) + " > BTC_ETH" btcAltDiff = 0.0000007 altEthDiff = -0.0000002 ethBtcDiff = -0.000001 # Forumla to check returns print("BTC -> Alt: " + str(btcAlt[1])) btc_altX = float(btcAlt[1] + btcAltDiff) print("Alt -> ETH: " + str(ethAlt[1])) eth_altX = float(ethAlt[1] + altEthDiff) print("ETH -> BTC: " + str(btc_eth_BTC)) btc_ethX = float(btc_eth_BTC + ethBtcDiff) #test1 = float(btcAlt[1] - 0.00000001) #test2 = float(ethAlt[1] - 0.00000001) #test3 = float(btc_eth_BTC - 0.0000004) print( str(btcAlt[0]) + " > " + str(ethAlt[0]) + " > BTC_ETH") btcUnits = 1 print("BTC = " + str(btcUnits)) altUnits = round(((btcUnits / 1.0025) / btc_altX), 8) #testaltUnits = round(((btcUnits / 1.0025) / test1), 8) print("Alt Units = " + str(altUnits) + " (" + str(btc_altX) + ")") #print("Test Alt Units = " + str(testaltUnits) + " (" + str(test1) + ")") ethUnits = round( ((altUnits - (altUnits * 0.0025)) * eth_altX), 8) #testethUnits = round(((testaltUnits - (testaltUnits * 0.0025)) * test2), 8) print("ETH Units = " + str(ethUnits) + " (" + str(eth_altX) + ")") #print("Test ETH Units = " + str(testethUnits) + " (" + str(test2) + ")") btcResult = round( ((ethUnits - (ethUnits * 0.0025)) * btc_ethX), 8) #testbtcResult = round(((testethUnits - (testethUnits * 0.0025)) * test3), 8) print("BTC Result = " + str(btcResult) + " (" + str(btc_ethX) + ")") #print("Test BTC Result = " + str(testbtcResult) + " (" + str(test3) + ")") print("") #arbBTCReturn.append(btcResult) #arbBTCTestReturn.append(testbtcResult) print(btcAlt[0]) if (btcResult ) >= desiredResult and btcAlt[0] != "BTC-SALT": print("!! Desired Result Reached !!") break arbBTCRow = arbBTCRow + 1 if (btcResult) >= desiredResult and btcAlt[0] != "BTC-SALT": break print("") # If desired result is not reached empty the lists to start again if btcResult <= desiredResult: BTCMatrix[:] = [] ETHMatrix[:] = [] arbBTCPairs[:] = [] arbBTCReturn[:] = [] LoopNumber = LoopNumber + 1 # Loops if return isn't good enough i.e. > 1.005 # Loop has been exited because return is good enough # If statement is final check to make sure return is good enough if float(btcResult) > desiredResult and btcAlt[0] != "BTC-SALT": print("Arb Return = " + str(btcResult)) print("begin timer") startTime = time.time() # Path of the arb which yiels return i.e. BTC -> ALT -> ETH -> BTC #print(arbPath) # Getting name of Alt if len(arbPath) == 25: alt = arbPath[4:6] print("Alt = " + alt) elif len(arbPath) == 27: alt = arbPath[4:7] print("Alt = " + alt) elif len(arbPath) == 29: alt = arbPath[4:8] print("Alt = " + alt) elif len(arbPath) == 31: alt = arbPath[4:9] print("Alt = " + alt) else: print("Wrong Number Letters " + len(arbPath)) print("Time elapsed " + str(time.time() - startTime)) # Begin Buy Process orderBTCALTBook = self.bittrex.get_orderbook( "BTC-" + str(alt), "sell") print("") #BTCBal = self.bittrex.get_balance("BTC") #if str(BTCBal["result"]["Balance"]) == "None": # principle = 0 #else: # principle = float(BTCBal["result"]["Balance"]) principle = 0.00065 print("Principle = " + str(principle)) AltQuantity = 0 ETHQuantity = 0 BTCQuantity = 0 market = "BTC-" + alt print("Market = " + market) print("") actBtcAltRate = 0 actAltEthRate = 0 actEthBtcRate = 0 btcOrderCount = 0 while principle > 0: print("BTC -> " + str(alt) + " Order " + str(btcOrderCount + 1) + ": Principle = " + str(principle)) askQuantity = orderBTCALTBook["result"][btcOrderCount][ "Quantity"] askRate = orderBTCALTBook["result"][btcOrderCount]["Rate"] askTotal = askQuantity * askRate print("-- Order Details: --") print("---- Ask Quantity = " + str(askQuantity)) print("---- Ask Rate = " + str(askRate)) print("---- Ask Total = " + str(askTotal)) print("---- Principle = " + str(principle)) print("") if askTotal > principle: print("---- Executing full final trade...") tradeQuantity = math.floor( ((principle / 1.0025) / askRate) * 100000000) / 100000000 print("---- Trade Quantity = " + str(tradeQuantity) + " (" + str(principle / 1.0025) + " / " + str(askRate) + ")") # Execute full or remainder of trade AltQuantity = AltQuantity + tradeQuantity print("---- BUY " + str(AltQuantity) + " " + str(alt) + " @ " + str(askRate) + "BTC = " + str(round((AltQuantity * askRate), 8))) altBuy = self.bittrex.buy_limit(market, AltQuantity, askRate) print("---- " + str(altBuy)) actBtcAltRate = askRate # I can delete this because I have a more accurate below from get_order_history altBuy = True break else: # Execute a portion of the trade print("---- Partial Trade - CANCEL ... ") print("---- BUY " + str(askQuantity) + str(alt) + " @ " + str(askRate) + " BTC = " + str(round((askQuantity * askRate), 8))) AltQuantity = AltQuantity + askQuantity principle = principle - askTotal break #buy = self.bittrex.buy_limit(market, askQuantity, askRate) #print(buy) #principle = (principle * 0.9975) - askTotal # execute trade btcOrderCount = btcOrderCount + 1 print("") print(str(alt) + " Quantity = " + str(AltQuantity)) firstTrade = time.time() - startTime secondTrade = 0 finalTrade = 0 print("Time since arb calc = " + str(firstTrade)) print("") if altBuy == True: orderETHALTBook = self.bittrex.get_orderbook( "ETH-" + str(alt), "buy") market = "ETH-" + alt ogAltQuantity = AltQuantity altOrderCount = 0 while AltQuantity > 0: print( str(alt) + " -> ETH Order " + str(altOrderCount + 1) + ": Principle = " + str(AltQuantity)) bidQuantity = orderETHALTBook["result"][altOrderCount][ "Quantity"] bidRate = orderETHALTBook["result"][altOrderCount]["Rate"] bidTotal = bidQuantity * bidRate print("-- Order Details: --") print("---- Bid Quantity = " + str(bidQuantity)) print("---- Bid Rate = " + str(bidRate)) print("---- Bid Total = " + str(bidTotal)) print("---- Alt Quantity = " + str(AltQuantity)) print("") if bidQuantity > AltQuantity: print("Executing full final trade...") tradeQuantity = math.floor( ((AltQuantity * 0.9975) * bidRate) * 100000000) / 100000000 print("---- Trade Quantity = " + str(tradeQuantity) + " (" + str(AltQuantity) + " * " + str(bidRate) + ")") # Execute full or remainder of trade print("---- SELL " + str(ogAltQuantity) + " " + str(alt) + " @ " + str(bidRate) + "ETH = " + str(tradeQuantity)) ETHQuantity = ETHQuantity + tradeQuantity altSell = self.bittrex.sell_limit( market, ogAltQuantity, bidRate) print("---- " + str(altSell)) actAltEthRate = bidRate # I can delete this because I have a more accurate below from get_order_history altSell = True break else: # Execute a portion of the trade print("---- Executing partial trade... " + str(bidQuantity) + str(alt) + " @ " + str(bidRate) + "ETH = " + str(bidQuantity * bidRate)) ETHQuantity = (ETHQuantity + bidTotal) * 0.9975 sell = self.bittrex.sell_limit(market, bidQuantity, bidRate) print(sell) AltQuantity = AltQuantity - bidQuantity # execute trade print("") altOrderCount = altOrderCount + 1 if altSell == True: print("") print("ETH Quantity = " + str(ETHQuantity)) secondTrade = time.time() - startTime print("Time since arb calc = " + str(secondTrade)) print("") orderBTCETHBook = self.bittrex.get_orderbook( "BTC-ETH", "buy") ogETHQuantity = ETHQuantity market = "BTC-ETH" ethOrderCount = 0 while ETHQuantity > 0: print("ETH -> BTC Order " + str(ethOrderCount + 1) + ": Principle = " + str(ETHQuantity)) bidQuantity = orderBTCETHBook["result"][ethOrderCount][ "Quantity"] bidRate = orderBTCETHBook["result"][ethOrderCount][ "Rate"] bidTotal = bidQuantity * bidRate print("-- Order Details: --") print("---- Bid Quantity = " + str(bidQuantity)) print("---- Bid Rate = " + str(bidRate)) print("---- Bid Total = " + str(bidTotal)) print("---- ETH Quantity = " + str(ETHQuantity)) print("") if bidQuantity > ETHQuantity: print("---- Executing full final trade...") tradeQuantity = math.floor( ((ETHQuantity * 0.9975) * bidRate) * 100000000) / 100000000 print("---- Trade Quantity = " + str(tradeQuantity) + " (" + str(ETHQuantity) + " * " + str(bidRate) + ")") # Execute full or remainder of trade print("---- SELL " + str(ogETHQuantity) + " ETH @ " + str(bidRate) + "BTC = " + str(tradeQuantity)) BTCQuantity = BTCQuantity + tradeQuantity sell = self.bittrex.sell_limit( market, ogETHQuantity, bidRate) print("---- " + str(sell)) actEthBtcRate = bidRate # I can delete this because I have a more accurate below from get_order_history break else: # Execute a portion of the trade print("---- Executing partial trade... " + str(bidQuantity) + "ETH @ " + str(bidRate) + "BTC = " + str(bidQuantity * bidRate)) BTCQuantity = BTCQuantity + bidTotal sell = self.bittrex.sell_limit( market, bidQuantity, bidRate) print(sell) ETHQuantity = ETHQuantity - bidQuantity # execute trade ethOrderCount = ethOrderCount + 1 print(BTCQuantity) finalTrade = time.time() - startTime print("Time since arb calc = " + str(finalTrade)) btcAltMarket = self.bittrex.get_market_summary("BTC-" + str(alt)) btcAltVolume = btcAltMarket["result"][0]["Volume"] altEthMarket = self.bittrex.get_market_summary("ETH-" + str(alt)) altEthVolume = altEthMarket["result"][0]["Volume"] ethBtcMarket = self.bittrex.get_market_summary("BTC-ETH") ethBtcVolume = ethBtcMarket["result"][0]["Volume"] # Grab bittrex Trade Details # 1 - BTC-ALT #tradeDetails = self.bittrex.get_order_history() tradeDetails = self.bittrex.get_order_history() tradeOne = tradeDetails["result"][2] tradeTwo = tradeDetails["result"][1] tradeThree = tradeDetails["result"][0] # Actual Arb Return tradeOneActualValue = tradeOne["Price"] + tradeOne[ "Commission"] tradeThreeActualValue = tradeThree["Price"] + tradeThree[ "Commission"] actualArbReturn = tradeThreeActualValue / tradeOneActualValue # Actual Rates tradeOneActualRate = tradeOne["PricePerUnit"] tradeTwoActualRate = tradeTwo["PricePerUnit"] tradeThreeActualRate = tradeThree["PricePerUnit"] with open('Trade Tracker.csv', 'a', newline='') as csvfile: tracker = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) tracker.writerow([ arbPath, datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f")[:-3], btcResult, actualArbReturn, str(btcAlt[1]), str(btcAltDiff), btcAlt[1] + btcAltDiff, tradeOneActualRate, str(btcAlt[1] - tradeOneActualRate), str(( (btcAlt[1] - tradeOneActualRate) / btcAlt[1]) * 100), str(firstTrade), str(btcAltVolume), str(ethAlt[1]), str(altEthDiff), str(ethAlt[1] + altEthDiff), tradeTwoActualRate, str(ethAlt[1] - tradeTwoActualRate), str(( (ethAlt[1] - tradeTwoActualRate) / ethAlt[1]) * 100), str(secondTrade), str(altEthVolume), str(btc_eth_BTC), str(ethBtcDiff), str(btc_eth_BTC + ethBtcDiff), tradeThreeActualRate, str(btc_eth_BTC - tradeThreeActualRate), str(((btc_eth_BTC - tradeThreeActualRate) / btc_eth_BTC) * 100), str(finalTrade), str(ethBtcVolume) ]) print("Excel Save Successful") db = firebase.database() data = { "Arb Path": arbPath, "Date Time": datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f")[:-3], "Principle": principle, "Actual Return": BTCQuantity, "Expected Arb Return": btcResult, "Actual Arb Return": actualArbReturn, "Trade 1 Quoted Rate": str(btcAlt[1]), "Trade 1 Adjustment": str(btcAltDiff), "Trade 1 Adjusted Quote": btcAlt[1] + btcAltDiff, "Trade 1 Actual Rate": tradeOneActualRate, "Trade 1 Actual-Quote Diff": str(btcAlt[1] - tradeOneActualRate), "Trade 1 Actual-Quote % Diff": str(((btcAlt[1] - tradeOneActualRate) / btcAlt[1]) * 100), "Trade 1 Time From Quote": str(firstTrade), "Trade 1 Market Volume": str(btcAltVolume), "Trade 2 Quoted Rate": str(ethAlt[1]), "Trade 2 Adjustment": str(altEthDiff), "Trade 2 Adjusted Quote": str(ethAlt[1] + altEthDiff), "Trade 2 Actual Rate": tradeTwoActualRate, "Trade 2 Actual-Quote Diff": str(ethAlt[1] - tradeTwoActualRate), "Trade 2 Actual-Quote % Diff": str(((ethAlt[1] - tradeTwoActualRate) / ethAlt[1]) * 100), "Trade 2 Time From Quote": str(secondTrade), "Trade 2 Market Volume": str(altEthVolume), "Trade 3 Quoted Rate": str(btc_eth_BTC), "Trade 3 Adjustment": str(ethBtcDiff), "Trade 3 Adjusted Quote": str(btc_eth_BTC + ethBtcDiff), "Trade 3 Actual Rate": tradeThreeActualRate, "Trade 3 Actual-Quote Diff": str(btc_eth_BTC - tradeThreeActualRate), "Trade 3 Actual-Quote % Diff Rate": str(((btc_eth_BTC - tradeThreeActualRate) / btc_eth_BTC) * 100), "Trade 3 Time From Quote": str(finalTrade), "Trade 3 Market Volume": str(ethBtcVolume) } db.child("Successful Transactions").push(data) else: print("ALT -> ETH Fail") else: print("BTC -> ALT Fail")
class Trader(object): """ Used for handling all trade functionality """ def __init__(self, secrets): self.trade_params = secrets["tradeParameters"] self.pause_params = secrets["pauseParameters"] self.Bittrex = Bittrex(secrets) self.Messenger = Messenger(secrets) self.Database = Database() def initialise(self): """ Fetch the initial coin pairs to track and to print the header line """ try: if len(self.Database.app_data["coinPairs"]) < 1: self.Database.store_coin_pairs(self.get_markets("BTC")) self.Messenger.print_header(len(self.Database.app_data["coinPairs"])) except ConnectionError as exception: self.Messenger.print_exception_error("connection") logger.exception(exception) exit() def analyse_pauses(self): """ Check all the paused buy and sell pairs and reactivate the necessary ones """ if self.Database.check_resume(self.pause_params["buy"]["pauseTime"], "buy"): self.Database.store_coin_pairs(self.get_markets("BTC")) self.Messenger.print_resume_pause(len(self.Database.app_data["coinPairs"]), "buy") if self.Database.check_resume(self.pause_params["sell"]["pauseTime"], "sell"): self.Messenger.print_resume_pause(self.Database.app_data["pausedTrackedCoinPairs"], "sell") self.Database.resume_sells() def analyse_buys(self): """ Analyse all the un-paused coin pairs for buy signals and apply buys """ trade_len = len(self.Database.trades["trackedCoinPairs"]) pause_trade_len = len(self.Database.app_data["pausedTrackedCoinPairs"]) if (trade_len < 1 or pause_trade_len == trade_len) and trade_len < self.trade_params["buy"]["maxOpenTrades"]: for coin_pair in self.Database.app_data["coinPairs"]: self.buy_strategy(coin_pair) def analyse_sells(self): """ Analyse all the un-paused tracked coin pairs for sell signals and apply sells """ for coin_pair in self.Database.trades["trackedCoinPairs"]: if coin_pair not in self.Database.app_data["pausedTrackedCoinPairs"]: self.sell_strategy(coin_pair) def buy_strategy(self, coin_pair): """ Applies the buy checks on the coin pair and handles the results appropriately :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str """ if (len(self.Database.trades["trackedCoinPairs"]) >= self.trade_params["buy"]["maxOpenTrades"] or coin_pair in self.Database.trades["trackedCoinPairs"]): return rsi = self.calculate_RSI(coin_pair=coin_pair, period=14, unit=self.trade_params["tickerInterval"]) day_volume = self.get_current_24hr_volume(coin_pair) current_buy_price = self.get_current_price(coin_pair, "ask") if self.check_buy_parameters(rsi, day_volume, current_buy_price): buy_stats = { "rsi": rsi, "24HrVolume": day_volume } self.buy(coin_pair, self.trade_params["buy"]["btcAmount"], current_buy_price, buy_stats) elif rsi is not None and rsi <= self.pause_params["buy"]["rsiThreshold"]: self.Messenger.print_no_buy(coin_pair, rsi, day_volume, current_buy_price) elif rsi is not None: self.Messenger.print_pause(coin_pair, rsi, self.pause_params["buy"]["pauseTime"], "buy") self.Database.pause_buy(coin_pair) def sell_strategy(self, coin_pair): """ Applies the sell checks on the coin pair and handles the results appropriately :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str """ if (coin_pair in self.Database.app_data["pausedTrackedCoinPairs"] or coin_pair not in self.Database.trades["trackedCoinPairs"]): return rsi = self.calculate_RSI(coin_pair=coin_pair, period=14, unit=self.trade_params["tickerInterval"]) current_sell_price = self.get_current_price(coin_pair, "bid") profit_margin = self.Database.get_profit_margin(coin_pair, current_sell_price) if self.check_sell_parameters(rsi, profit_margin): sell_stats = { "rsi": rsi, "profitMargin": profit_margin } self.sell(coin_pair, current_sell_price, sell_stats) elif rsi is not None and profit_margin >= self.pause_params["sell"]["profitMarginThreshold"]: self.Messenger.print_no_sell(coin_pair, rsi, profit_margin, current_sell_price) elif rsi is not None: self.Messenger.print_pause(coin_pair, profit_margin, self.pause_params["sell"]["pauseTime"], "sell") self.Database.pause_sell(coin_pair) def check_buy_parameters(self, rsi, day_volume, current_buy_price): """ Used to check if the buy conditions have been met :param rsi: The coin pair's current RSI :type rsi: float :param day_volume: The coin pair's current 24 hour volume :type day_volume: float :param current_buy_price: The coin pair's current price :type current_buy_price: float :return: Boolean indicating if the buy conditions have been met :rtype : bool """ return (rsi is not None and rsi <= self.trade_params["buy"]["rsiThreshold"] and day_volume >= self.trade_params["buy"]["24HourVolumeThreshold"] and current_buy_price > self.trade_params["buy"]["minimumUnitPrice"]) def check_sell_parameters(self, rsi, profit_margin): """ Used to check if the sell conditions have been met :param rsi: The coin pair's current RSI :type rsi: float :param profit_margin: The coin pair's current profit margin :type profit_margin: float :return: Boolean indicating if the sell conditions have been met :rtype : bool """ return ((rsi is not None and rsi >= self.trade_params["sell"]["rsiThreshold"] and profit_margin > self.trade_params["sell"]["minProfitMarginThreshold"]) or profit_margin > self.trade_params["sell"]["profitMarginThreshold"]) def buy(self, coin_pair, btc_quantity, price, stats, trade_time_limit=2): """ Used to place a buy order to Bittrex. Wait until the order is completed. If the order is not filled within trade_time_limit minutes cancel it. :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param btc_quantity: The amount of BTC to buy with :type btc_quantity: float :param price: The price at which to buy :type price: float :param stats: The buy stats object :type stats: dict :param trade_time_limit: The time in minutes to wait fot the order before cancelling it :type trade_time_limit: float """ buy_data = self.Bittrex.buy_limit(coin_pair, btc_quantity / price, price) if not buy_data["success"]: return logger.error("Failed to buy on {} market.".format(coin_pair)) self.Database.store_initial_buy(coin_pair, buy_data["result"]["uuid"]) buy_order_data = self.get_order(buy_data["result"]["uuid"], trade_time_limit * 60) self.Database.store_buy(buy_order_data["result"], stats) self.Messenger.print_buy(coin_pair, price, stats["rsi"], stats["24HrVolume"]) self.Messenger.send_buy_slack(coin_pair, stats["rsi"], stats["24HrVolume"]) self.Messenger.send_buy_gmail(buy_order_data["result"], stats) self.Messenger.play_sw_imperial_march() def sell(self, coin_pair, price, stats, trade_time_limit=2): """ Used to place a sell order to Bittrex. Wait until the order is completed. If the order is not filled within trade_time_limit minutes cancel it. :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param price: The price at which to buy :type price: float :param stats: The buy stats object :type stats: dict :param trade_time_limit: The time in minutes to wait fot the order before cancelling it :type trade_time_limit: float """ trade = self.Database.get_open_trade(coin_pair) sell_data = self.Bittrex.sell_limit(coin_pair, trade["quantity"], price) if not sell_data["success"]: return logger.error( "Failed to sell on {} market. Bittrex error message: {}".format(coin_pair, sell_data["message"]) ) sell_order_data = self.get_order(sell_data["result"]["uuid"], trade_time_limit * 60) # TODO: Handle partial/incomplete sales. self.Database.store_sell(sell_order_data["result"], stats) self.Messenger.print_sell(coin_pair, price, stats["rsi"], stats["profitMargin"]) self.Messenger.send_sell_slack(coin_pair, stats["rsi"], stats["profitMargin"]) self.Messenger.send_sell_gmail(sell_order_data["result"], stats) self.Messenger.play_sw_theme() def get_markets(self, main_market_filter=None): """ Gets all the Bittrex markets and filters them based on the main market filter :param main_market_filter: Main market to filter on (ex: BTC, ETH, USDT) :type main_market_filter: str :return: All Bittrex markets (with filter applied, if any) :rtype : list """ markets = self.Bittrex.get_markets() if not markets["success"]: logger.error("Failed to fetch Bittrex markets") exit() markets = markets["result"] if main_market_filter is not None: market_check = main_market_filter + "-" markets = py_.filter_(markets, lambda market: market_check in market["MarketName"]) markets = py_.map_(markets, lambda market: market["MarketName"]) return markets def get_current_price(self, coin_pair, price_type): """ Gets current market price for a coin pair :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str :param price_type: The type of price to get (one of: 'ask', 'bid') :type price_type: str :return: Coin pair's current market price :rtype : float """ coin_summary = self.Bittrex.get_market_summary(coin_pair) if not coin_summary["success"]: logger.error("Failed to fetch Bittrex market summary for the {} market".format(coin_pair)) return None if price_type == "ask": return coin_summary["result"][0]["Ask"] if price_type == "bid": return coin_summary["result"][0]["Bid"] return coin_summary["result"][0]["Last"] def get_current_24hr_volume(self, coin_pair): """ Gets current 24 hour market volume for a coin pair :param coin_pair: Coin pair market to check (ex: BTC-ETH, BTC-FCT) :type coin_pair: str :return: Coin pair's current 24 hour market volume :rtype : float """ coin_summary = self.Bittrex.get_market_summary(coin_pair) if not coin_summary["success"]: logger.error("Failed to fetch Bittrex market summary for the {} market".format(coin_pair)) return None return coin_summary["result"][0]["BaseVolume"] def get_closing_prices(self, coin_pair, period, unit): """ Returns closing prices within a specified time frame for a coin pair :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param period: Number of periods to query :type period: int :param unit: Ticker interval (one of: 'oneMin', 'fiveMin', 'thirtyMin', 'hour', 'week', 'day', and 'month') :type unit: str :return: Array of closing prices :rtype : list """ historical_data = self.Bittrex.get_historical_data(coin_pair, period, unit) closing_prices = [] for i in historical_data: closing_prices.append(i["C"]) return closing_prices def get_order(self, order_uuid, trade_time_limit): """ Used to get an order from Bittrex by it's UUID. First wait until the order is completed before retrieving it. If the order is not completed within trade_time_limit seconds, cancel it. :param order_uuid: The order's UUID :type order_uuid: str :param trade_time_limit: The time in seconds to wait fot the order before cancelling it :type trade_time_limit: float :return: Order object :rtype : dict """ start_time = time.time() order_data = self.Bittrex.get_order(order_uuid) while time.time() - start_time <= trade_time_limit and order_data["result"]["IsOpen"]: time.sleep(10) order_data = self.Bittrex.get_order(order_uuid) if order_data["result"]["IsOpen"]: error_str = self.Messenger.print_order_error(order_uuid, trade_time_limit, order_data["result"]["Exchange"]) logger.error(error_str) if order_data["result"]["Type"] == "LIMIT_BUY": self.Bittrex.cancel(order_uuid) return order_data return order_data def calculate_RSI(self, coin_pair, period, unit): """ Calculates the Relative Strength Index for a coin_pair If the returned value is above 75, it's overbought (SELL IT!) If the returned value is below 25, it's oversold (BUY IT!) :param coin_pair: String literal for the market (ex: BTC-LTC) :type coin_pair: str :param period: Number of periods to query :type period: int :param unit: Ticker interval (one of: 'oneMin', 'fiveMin', 'thirtyMin', 'hour', 'week', 'day', and 'month') :type unit: str :return: RSI :rtype : float """ closing_prices = self.get_closing_prices(coin_pair, period * 3, unit) count = 0 change = [] # Calculating price changes for i in closing_prices: if count != 0: change.append(i - closing_prices[count - 1]) count += 1 if count == 15: break # Calculating gains and losses advances = [] declines = [] for i in change: if i > 0: advances.append(i) if i < 0: declines.append(abs(i)) average_gain = (sum(advances) / 14) average_loss = (sum(declines) / 14) new_avg_gain = average_gain new_avg_loss = average_loss for _ in closing_prices: if 14 < count < len(closing_prices): close = closing_prices[count] new_change = close - closing_prices[count - 1] add_loss = 0 add_gain = 0 if new_change > 0: add_gain = new_change if new_change < 0: add_loss = abs(new_change) new_avg_gain = (new_avg_gain * 13 + add_gain) / 14 new_avg_loss = (new_avg_loss * 13 + add_loss) / 14 count += 1 if new_avg_loss == 0: return None rs = new_avg_gain / new_avg_loss new_rs = 100 - 100 / (1 + rs) return new_rs
def get_histor(market): my_bittrex = Bittrex(None, None, api_version=API_V1_1) result = my_bittrex.get_market_summary(market) return(result['result'][0]['High'],result['result'][0]['Low'])
class Market(object): """ Used to provide analysis of a market on Bittrex. """ def __init__(self, name, bittrex=None): """ :param name: String literal for the market (e.g. BTC-LTC) :type name: str :param bittrex: Instance of Bittrex, potentially with API_KEY and SECRET :type bittrex: Bittrex """ self.name = name self.basis, self.coin = self.name.split("-") if bittrex: self.bittrex = bittrex else: self.bittrex = Bittrex(None, None) @property def summary(self): response = self.bittrex.get_market_summary(self.name) if response['success']: return response['result'][0] raise Exception("Could not retrieve data from Bittrex: {:s}".format( response['message'])) @property def history(self): response = self.bittrex.get_market_history(self.name) if response['success']: df = pd.DataFrame(response['result']) df["TimeStamp"] = pd.to_datetime(df["TimeStamp"]) return df raise Exception("Could not retrieve data from Bittrex: {:s}".format( response['message'])) def _get_orderbook(self, depth_type, depth=20): response = self.bittrex.get_orderbook(self.name, depth_type, depth) if response['success']: return pd.DataFrame(response['result']) raise Exception("Could not retrieve data from Bittrex: {:s}".format( response['message'])) def get_buy_orderbook(self, depth=20): return self._get_orderbook(BUY_ORDERBOOK, depth) def get_sell_orderbook(self, depth=20): return self._get_orderbook(SELL_ORDERBOOK, depth) def get_both_orderbooks(self, depth=20): response = self.bittrex.get_orderbook(self.name, BOTH_ORDERBOOK, depth) if response['success']: return (pd.DataFrame(response['result']['buy']), pd.DataFrame(response['result']['sell'])) raise Exception("Could not retrieve data from Bittrex: {:s}".format( response['message'])) @property def ticker(self): response = self.bittrex.get_ticker(self.name) if response['success']: return response['result'] raise Exception("Could not retrieve data from Bittrex: {:s}".format( response['message'])) def get_price_time_series(self): return self.history[["TimeStamp", "Price"]] def __str__(self): return "{:s}\t{:s}".format(self.name, str(self.ticker))