Example #1
0
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)
Example #3
0
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")
Example #5
0
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
Example #6
0
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'])
Example #7
0
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))