Exemplo n.º 1
0
class KuCoin:
    lastTimestamp = None

    def __init__(self, Config):
        self.publicKey = Config.getPublicKey()
        self.secretKey = Config.getSecretKey()
        self.client = Client(self.publicKey, self.secretKey)

    def checkKeys(self):
        try:
            b = self.client.get_api_keys()
            if 'code' not in b:  # If code is there, it means an error code so wrong.
                return True
            #else:
            #    self.errorMessage(b)
            #    return False
        except (Exception):  #TO DO: extract kucoin exception message directly
            self.errorMessage("KuCoin API error.")
            return False

    def getBalance(self, limit=None, page=None):
        try:
            jsonD = self.client.get_all_balances()
        except Exception as e:
            print("Error getting balance.")
            return

        balances = []
        for x in jsonD:
            if x['balance'] != 0.0:
                balances.append(x)  #TO DO, MAKE IT SAME NAMES AS HITBTC
        for b in balances:
            for key in b.keys():
                if (key == "coinType"):
                    newKey = key.replace("coinType", "currency")
                    b[newKey] = b[key]
                    del b[key]
                if (key == "balanceStr"):
                    newKey = key.replace("balanceStr", "available")
                    b[newKey] = b[key]
                    del b[key]

        return balances

    def getSymbols(self):

        jsonD = self.client.get_trading_symbols()
        symbols = []
        for x in jsonD:
            symbols.append(x['symbol'])

        return symbols

    def getOrderHistory(self, Config):
        try:
            orders = self.client.get_dealt_orders(symbol=Config.getCurrency())
            return orders
        except Exception as e:
            print("ERROR GETTING ORDER HISTORY:")
            print(e)
            return None

    def getActiveOrders(self, Config):
        try:
            orders = self.client.get_active_orders(symbol=Config.getCurrency())
            return orders
        except Exception as e:
            print("ERROR GETTING ACTIVE ORDERS")
            print(e)
            return None

    def getOrderBook(self, Config, request):
        try:
            orders = self.client.get_order_book(Config.getCurrency())

            if (request == 'SELL'):
                return orders['SELL']

            if (request == 'BUY'):
                return orders['BUY']

        except Exception as e:
            print(e)
            return None

    def cancelBuyOrders(self, Config):
        try:
            cancelAttempt = self.client.cancel_all_orders(
                Config.getCurrency(), 'BUY')
            if cancelAttempt is None:
                return True
        except Exception as e:
            print(e)
        return False

    def cancelSellOrders(self, Config):
        try:
            cancelAttempt = self.client.cancel_all_orders(
                Config.getCurrency(), 'SELL')
            if cancelAttempt is None:
                return True
        except Exception as e:
            print(e)
        return False

    def cancelOrders(self, Config):
        try:
            cancelAttempt = self.client.cancel_all_orders(
                Config.getCurrency(), 'BUY')
            cancelAttempt = self.client.cancel_all_orders(
                Config.getCurrency(), 'SELL')
            if cancelAttempt is None:
                return True
        except Exception as e:
            print(e)
        return False

    def createOrder(self, symbol, side, price, amount):
        try:
            #sellPrice = str(round(float(price), 6))
            transaction = self.client.create_order(symbol, side.upper(), price,
                                                   amount)
            return transaction  # returns orderOid
        except Exception as e:
            print("Took too long.. mismatch I think??")
            print(e)

    def errorMessage(self, b):
        print(
            '\n =================== CyrCraft - Errrr Errorrrrrr ======================= \n'
        )
        print(b)
        print(
            'Keys are wrong..\nType 1 to set your keys.. or else any key to go Configuration Menu..'
        )
        print(
            '\n ======================================================================= \n'
        )
Exemplo n.º 2
0
class Bot:
    def __init__(self):
        # Output parameters, debug is not useful to normal users
        self.debug = True
        self.verbose = True

        # Modify this according to your personal parameters
        # Keys are top secret, delete them from your account if ever published
        self.simulate = True
        self.play_with_gains = True
        self.api_key = "thereisakeyhere"
        self.secret_key = "thereisakeyherebutitsasecret"
        self.api_client = Client(self.api_key, self.secret_key)
        # Fund rules are the limit you are allowing the bot to trade with, usd estimated with USDT
        self.fund_rules = {
            'BTC': {
                'no_touch_coins': 0,
                'max_coins': 0.01,
                'max_percent': 100
            },
            'ETH': {
                'no_touch_coins': 0,
                'max_coins': 0.1,
                'max_percent': 100
            },
            'NEO': {
                'no_touch_coins': 0,
                'max_coins': 0.5,
                'max_percent': 100
            },
            'USDT': {
                'no_touch_coins': 0,
                'max_coins': 0,
                'max_percent': 100
            },
            'KCS': {
                'no_touch_coins': 0,
                'max_coins': 0,
                'max_percent': 100
            },
            'BCH': {
                'no_touch_coins': 0,
                'max_coins': 0,
                'max_percent': 100
            },
        }
        self.time_limit = {  # Longest time allowed for api calls
            'get_book': 0.4,
            'get_prices': 1
        }
        self.gap_limit_percent = 0.001  # Minimum path earnings default at 0.1%

        # Don't touch this unless you know what you are doing
        self.rate_limits, self.call_count, self.last_timestamp = 1200, 0, 0  # Request limiter per minute, burst distribution
        self.trade_fee = 1 - 0.001  # Deduced from bought asset
        if self.simulate:
            self.balances = {'BTC': 0.1, 'ETH': 1, 'NEO': 0.5}
        else:
            self.balances = {}
        self.liquidations, self.liquidation_limit = [], 0.02  # Max tolerable loss default at 1%, fee not included
        self.tradePrecision, self.pairs, self.gains, self.min_amount, self.path_blacklist = {}, {}, {}, {}, {}
        self.paths, self.order_ids = [], ["", "", "", ""]
        self.last_min_calculation = 0  # Last time the minimum amount for a trade was calculated

        signal.signal(signal.SIGINT, self.exit_program)
        signal.signal(signal.SIGTERM, self.exit_program)
        self.program_running = True
        self.start_engine()

    def loop(self):
        while self.program_running:
            self.log_debug("Starting new loop")
            # Refresh min amount if necessary
            if time.time() - 600 > self.last_min_calculation:
                self.calculate_min_amount()
            # Refresh blacklist
            self.refresh_blacklist()
            # Get symbols sell price
            success = self.reload_prices()
            if not success:
                continue
            # Calculate path coefficients
            self.log_debug("get_paths_data")
            path_prices = self.get_paths_data()
            self.log_verbose("Found {} potential path".format(
                len(path_prices)))
            self.log_verbose("Gains since started = {}".format(self.gains))

            # Iterate over path in descending values
            for path in sorted(path_prices,
                               key=lambda p: p["path_value"],
                               reverse=True):
                path_value, coin1 = path["path_value"], path["coin1"]
                buy = [None, path["buy1"], path["buy2"], path["buy3"]]
                sym = [None, path["sym1"], path["sym2"], path["sym3"]]
                unique_string = "%.8f%s%s%s" % (path_value, sym[1], sym[2],
                                                sym[3])
                if unique_string in self.path_blacklist:
                    self.log_verbose("Skipping path of the blacklist")
                    continue
                self.log_verbose(
                    "Path val=%.8f;Buy(%r) %s;Buy(%r) %s;Buy(%r) %s" %
                    (path_value, buy[1], sym[1], buy[2], sym[2], buy[3],
                     sym[3]))
                # Get books details and check quantity
                books = [None, [], [], []]
                error, success = False, 0

                # Call order books
                for i in range(1, 4):
                    success, books[i] = self.get_order_book(buy[i], sym[i])
                    if not success or books[i] is None or len(books[i]) < 1:
                        success = False
                        break

                # Skip the rest if an error happened
                if not success:
                    self.log_verbose("Order books could not be retrieved")
                    break
                else:
                    self.log_debug("Received all order books")

                # Calculate optimized quantity and price, factor in fees
                balance = self.balances[coin1]
                order1, order2, order3 = books[1].pop(), books[2].pop(
                ), books[3].pop()
                self.calc_path(balance, order1, order2, order3, path)
                earned, spent = path["earned"], path["spent"]
                # Check if the path is worth walking
                gains = self.apply_precision(earned - spent, coin1)
                min_gains = 0.1**(self.tradePrecision[coin1] - 2)
                if spent <= 0 or earned / spent < 1 + self.gap_limit_percent or gains < min_gains:
                    self.log_verbose(
                        "Path is not profitable anymore : spend %.8f get %.8f"
                        % (spent, earned))
                    self.path_blacklist["%.8f%s%s%s" %
                                        (path_value, sym[1], sym[2],
                                         sym[3])] = time.time()
                    break
                if False and gains < self.tradePrecision[path["coin1"]]:
                    self.log_verbose(
                        "Gains are too small (%.8f), skipping path" % gains)
                    break
                self.log_info("Spending %.8f to earn %.8f : %s >> %s >> %s " %
                              (spent, earned, sym[1], sym[2], sym[3]))

                if not self.simulate:
                    self.program_running = False

                # Everything is okay close the path
                self.order_ids = ["", "", "", ""]
                warm_time = 0.01
                success = self.execute_order(1, path)  # Execute trade 1 A-B
                if success:
                    time.sleep(warm_time)
                    success = self.execute_order(2,
                                                 path)  # Execute trade 2 B-C
                    if success:
                        time.sleep(warm_time)
                        success = self.execute_order(
                            3, path)  # Execute trade 3 C-B
                        if success:
                            # Update available balances and gains
                            if coin1 not in self.gains:
                                self.gains[coin1] = 0
                            self.gains[coin1] = self.apply_precision(
                                self.gains[coin1] + gains, coin1)
                            if self.play_with_gains:
                                self.balances[coin1] = self.apply_precision(
                                    self.balances[coin1] + gains, coin1)
                break

            # Liquidate stuck balances
            self.liquidate()
            # Wait if necessary
            if len(path_prices) < 1:
                self.log_debug("No path found, forced wait of 100ms")
                time.sleep(0.1)
            current_time = time.time()
            loop_wait = self.call_count * (60 / self.rate_limits) - (
                current_time - self.last_timestamp)
            self.call_count = 0
            if loop_wait > 0:
                self.log_verbose("Waiting %.6f seconds before next iteration" %
                                 loop_wait)
                time.sleep(loop_wait)
            self.last_timestamp = current_time
        self.exit_message()

    # SERIOUS BUSINESS, TRIPLE CHECK !
    def execute_order(self,
                      order_num,
                      path,
                      force_price=0,
                      force_qty=0,
                      is_liquidation=False):
        buy, symbol, quantity, price = path["buy%d" % order_num], path[
            "sym%d" % order_num], path["qty%d" % order_num], path["price%d" %
                                                                  order_num]
        order_placed, order_executed, limit_reached, order_canceled = False, False, False, False
        if force_price > 0:
            price = force_price
        if force_qty > 0:
            quantity = force_qty
        if buy:
            deal_type = 'SELL'
        else:
            deal_type = 'BUY'
        if self.simulate:
            while not order_executed and not order_canceled:
                # Place order
                while not order_placed:
                    self.log_verbose(
                        "Trying to place %s order on %s : [Price: %.8f - Quantity: %.8f]"
                        % (deal_type, symbol, price, quantity))
                    try:
                        if buy:
                            self.api_call("create_sell_order")
                            time.sleep(0.05)
                            self.log_verbose("Successfully placed sell order")
                        else:
                            self.api_call("create_buy_order")
                            time.sleep(0.05)
                            self.log_verbose("Successfully placed buy order")
                        order_placed = True
                    except KucoinAPIException as e:  # No use in simulation
                        if e.code is "NO_BALANCE":  # Previous order not or partially filled, only order 2 or 3
                            self.log_verbose(
                                "Previous order not filled, cancel it and liquidate"
                            )
                            if not is_liquidation:
                                self.feed_liquidator(order_num - 1, path)
                        else:
                            self.log_error(
                                "Error while placing %s order. Verifying if order has been placed"
                                % deal_type)
                warm_time = 0.3
                time.sleep(warm_time)

                # Check order status
                if is_liquidation and self.simulate:  # Too shit to calculate
                    return True
                ret, loaded_book = None, False
                while not loaded_book:
                    try:
                        # Load books
                        if buy:
                            self.api_call("get_buy_orders")
                            ret = self.api_client.get_buy_orders(symbol,
                                                                 limit=1)
                        else:
                            self.api_call("get_sell_orders")
                            ret = self.api_client.get_sell_orders(symbol,
                                                                  limit=1)
                        # Check if loaded
                        if ret is not None and len(ret) > 0:
                            loaded_book = True
                        elif not is_liquidation:
                            self.feed_liquidator(order_num, path)
                    except KucoinAPIException as e:
                        self.log_error(
                            "Error during API call : error {} : {}".format(
                                e.status_code, e.response))
                if (buy and ret[0][0] >= price) or (not buy
                                                    and ret[0][0] <= price):
                    self.log_verbose("%s order filled : %.8f %s for %.8f " %
                                     (deal_type, quantity, symbol, price))
                    return True

                # Cancel order
                else:
                    order_canceled = False
                    while not order_canceled:
                        try:
                            self.log_verbose(
                                "Order not executed, trying to cancel")
                            order_canceled = True
                            self.log_verbose("Order canceled")
                        except:
                            self.log_verbose(
                                "Failed to cancel order, trying again")
            if not is_liquidation:
                self.feed_liquidator(order_num, path)
            return False

        # WARNING : Not a simulation anymore! Real money to be lost
        # Real money : cancel order to check execution, check dealt orders for remaining money, liquidate remaining
        else:
            # Place order
            while not order_placed:
                self.log_verbose(
                    "Trying to place %s order on %s : [Price: %.8f - Quantity: %.8f]"
                    % (deal_type, symbol, price, quantity))
                try:
                    if buy:
                        self.api_call("create_sell_order")
                    else:
                        self.api_call("create_buy_order")
                    ret = self.api_client.create_order(symbol=symbol,
                                                       order_type=deal_type,
                                                       price="%.8f" % price,
                                                       amount="%.8f" %
                                                       quantity)
                    self.order_ids[order_num] = ret["orderOid"]
                    self.log_verbose("Successfully placed order")
                    order_placed = True
                except KucoinAPIException as e:
                    if e.code == "NO_BALANCE":  # Previous order not or partially filled, only order 2 or 3
                        self.log_verbose(
                            "No balance, previous order may not be filled yet, checking"
                        )
                        if not is_liquidation:
                            # Check and cancel + liquidate previous order if necessary
                            success = self.check_order(order_num - 1, path)
                            if not success:
                                return False
                            self.log_verbose(
                                "No problem with the previous order, continuing"
                            )
                            order_placed = True
                    else:
                        self.log_error(
                            "Error during API call : error {} : code {} : {}".
                            format(e.status_code, e.code, e.response))
                        self.log_error(
                            "Exception while placing %s order. Stop EVERYTHING"
                            % deal_type)
                        self.program_running = False
                        return False

            # Check if order has been dealt
            success = self.check_order(order_num, path)
            return success

    def check_order(self, order_num, path):
        buy = path["buy%d" % order_num]
        symbol = path["sym%d" % order_num]
        quantity = path["qty%d" % order_num]
        price = path["price%d" % order_num]
        spent, earned = path["spent"], path["earned"]
        if buy:
            deal_type = 'SELL'
            precision_coin = path["coin%d" % order_num]
        else:
            deal_type = 'BUY'
            precision_coin = path["coin%d" % ((order_num % 3) + 1)]
        oid = self.order_ids[order_num]
        check_cancel = True

        # Check order execution
        order_failed, book_is_valid = False, True
        while not order_failed:
            time.sleep(0.020)
            try:
                self.api_call("get_dealt_orders")
                ret = self.api_client.get_symbol_dealt_orders(
                    symbol=symbol, order_type=deal_type, limit=20)
            except KucoinAPIException as e:
                self.log_error("Error {}: {}".format(e.status_code,
                                                     e.response))
                time.sleep(0.05)  # Limit spam if failure
            # Check if order active
            dealt_amount = 0
            for order in ret["datas"]:
                if order["orderOid"] == oid:
                    dealt_amount = dealt_amount + order["amount"]
            # Check the book
            ret = None
            filled_rate = 100 * dealt_amount / quantity
            self.log_debug("Completed %.1f percent of order" % filled_rate)
            if filled_rate > 99.9:
                self.log_debug(
                    "Dealt threshold reached, order successfully executed")
                return True
            else:
                if book_is_valid:
                    # Load order book
                    try:
                        if deal_type == "SELL":
                            self.api_call("get_buy_orders")
                            ret = self.api_client.get_buy_orders(symbol,
                                                                 limit=1)
                        else:
                            self.api_call("get_sell_orders")
                            ret = self.api_client.get_sell_orders(symbol,
                                                                  limit=1)
                        if ret and len(ret) > 0:
                            book_line = ret.pop()
                            if not ((deal_type == "SELL"
                                     and book_line[0] >= price) or
                                    (deal_type == "BUY"
                                     and book_line[0] <= price)):
                                self.log_debug(
                                    "No more order in the book to close")
                                book_is_valid = False
                        else:
                            self.log_debug("The book contains nothing")
                            book_is_valid = False
                    except KucoinAPIException as e:
                        self.log_error("Error {}: {}".format(
                            e.status_code, e.response))
                else:
                    order_failed = True

        # Try to cancel, if order is dealt it will not work
        while check_cancel:
            try:
                self.api_call("cancel_order")
                self.api_client.cancel_order(order_id=oid,
                                             order_type=deal_type)
                self.log_verbose(
                    "Order %d not dealt or partially, cancelled." % order_num)
                check_cancel = False
            except KucoinAPIException as e:
                if e.status_code != 404:
                    self.log_error("Error {}: {}".format(
                        e.status_code, e.response))
                    time.sleep(0.1)  # Limit spam if failure
                else:
                    self.log_verbose(
                        "Order not found, already dealt or never created")
                    return True  # Order can't be cancelled, it has been dealt

        # Handle failure
        # Fetch dealt orders
        loaded_orders = False
        ret = None
        while not loaded_orders:
            try:
                self.api_call("get_symbol_dealt_orders")
                ret = self.api_client.get_symbol_dealt_orders(
                    symbol=symbol, order_type=deal_type, limit=20)
                loaded_orders = True
            except KucoinAPIException as e:
                self.log_error("Error {}: {}".format(e.status_code,
                                                     e.response))
                time.sleep(0.1)
        # Get dealt balance for order
        dealt_amount = 0
        for deal in ret["datas"]:
            dealt_amount = self.apply_precision(dealt_amount + deal["amount"],
                                                precision_coin)
        spent_amount = self.apply_precision(
            dealt_amount * (2 - self.trade_fee), precision_coin)
        remaining = self.apply_precision("qty%d" % order_num - spent_amount,
                                         precision_coin)
        self.log_verbose(
            "Cancelled order detail : %.8f dealt; %.8f remaining" %
            (dealt_amount, remaining))

        # Refactor the path to match the quantities
        new_spent = self.apply_precision(spent * remaining / quantity,
                                         path["coin1"])
        new_earned = self.apply_precision(earned * remaining / quantity,
                                          path["coin1"])
        path["spent"], path["earned"] = new_spent, new_earned
        path["qty%d" % order_num] = remaining

        # Create new path if order 1 or 2 partially dealt
        if order_num != 3 and dealt_amount > 0:
            if buy:
                new_precision = path["coin%d" % (order_num + 1)]
            else:
                new_precision = path["coin%d" % (((order_num + 1) % 3) + 1)]
            artificial_spent = self.apply_precision(
                spent * dealt_amount / quantity, path["coin1"])
            artificial_earned = self.apply_precision(
                earned * dealt_amount / quantity, path["coin1"])
            new_qty = self.apply_precision(
                path["qty%d" % order_num + 1] * dealt_amount / quantity,
                path[new_precision])
            if new_qty > self.min_amount[new_precision]:
                # Create a new path only for liquidation
                artificial_path = dict(path_value=path["path_value"],
                                       coin1=path["coin1"],
                                       coin2=path["coin2"],
                                       coin3=path["coin3"],
                                       buy1=path["buy1"],
                                       buy2=path["buy2"],
                                       buy3=path["buy3"],
                                       sym1=path["sym1"],
                                       sym2=path["sym2"],
                                       sym3=path["sym3"],
                                       spent=artificial_spent,
                                       earned=artificial_earned,
                                       qty1=path["qty1"],
                                       qty2=path["qty2"],
                                       qty3=path["qty3"],
                                       price1=path["price1"],
                                       price2=path["price2"],
                                       price3=path["price3"])
                # Mod quantity
                artificial_path["qty%d" % order_num + 1] = new_qty
                self.feed_liquidator(order_num + 1, artificial_path)

        if order_num != 1:
            # Add to liquidation if it failed
            if remaining > self.min_amount[precision_coin]:
                self.log_verbose(
                    "Order %d failed, add to liquidation : Qty %.8f; Price %.8f; Sym %s; Buy%s"
                    %
                    (order_num, path["qty%d" % order_num], price, symbol, buy))
                self.feed_liquidator(order_num, path)
            else:
                self.log_verbose(
                    "Insufficient quantity remaining, no need to liquidate %s"
                    % precision_coin)
        else:
            self.log_verbose("Order %d failed, no need to liquidate %s" %
                             (order_num, path["coin1"]))
        return False

    def feed_liquidator(self, order_num, path, force_down=False):
        if order_num is 1:
            return
        coin1, spent = path["coin1"], path["spent"]
        buy, symbol, quantity, price = path["buy%d" % order_num], path[
            "sym%d" % order_num], path["qty%d" % order_num], path["price%d" %
                                                                  order_num]
        self.log_verbose(
            "Order %d failed, add to liquidation : Qty %.8f; Price %.8f; Sym %s; Buy%s"
            % (order_num, quantity, price, symbol, buy))
        self.liquidations.append({
            "force_down": force_down,
            "failed_sym": symbol,
            "max_loss": self.liquidation_limit,
            "spent": spent,
            "path": path
        })
        # Update available balances and gains
        if coin1 not in self.gains:
            self.gains[coin1] = 0
        self.balances[coin1] = self.apply_precision(
            self.balances[coin1] - spent, coin1)
        self.gains[coin1] = self.apply_precision(self.gains[coin1] - spent,
                                                 coin1)

    def liquidate(self):
        if not self.simulate:
            return
        liquidated = []
        list_len, index = len(self.liquidations), 0
        while index < list_len:
            entry = self.liquidations[index]
            index = index + 1

            # Define the coins to deal with
            force_down, path, spent, max_loss, failed_sym = entry[
                "force_down"], entry["path"], entry["spent"], entry[
                    "max_loss"], entry["failed_sym"]
            earned = path["earned"]
            if failed_sym is path[
                    "sym2"]:  # Try trade up first then down if not profitable
                order_num = 2
                if force_down:  # Trade down
                    coin, coin1 = path["coin2"], path["coin3"]
                    buy = path["buy2"]
                    qty = path["qty2"]
                    if buy:
                        price = path["price2"] * spent / earned
                    else:
                        price = path["price2"] * earned / spent
                else:  # Trade up, default
                    coin, coin1 = path["coin2"], path["coin1"]
                    buy = not path["buy1"]
                    qty = self.apply_precision(path["qty1"] * self.trade_fee,
                                               coin1)
                    price = path["price1"]
            else:  # Trade down
                order_num = 3
                coin, coin1 = path["coin3"], path["coin1"]
                buy = path["buy3"]
                qty = path["qty3"]
                if buy:
                    price = path["price3"] * spent / earned
                else:
                    price = path["price3"] * earned / spent
            self.log_debug(
                "Initial values : price %.8f; qty %.8f; spent %.8f" %
                (price, qty, spent))

            # Define the symbol
            if coin in self.pairs and coin1 in self.pairs[coin]:
                symbol = self.pairs[coin][coin1]["symbol"]
            else:
                symbol = self.pairs[coin1][coin]["symbol"]

            self.log_debug("Trying to liquidate %s on symbol %s" %
                           (coin, symbol))
            # Get books for coin to coin1
            success, book = self.get_order_book(buy, symbol, limit=25)
            if not success or book is None or len(book) < 1:
                self.log_error("Error during api call for liquidation of %s" %
                               coin)
                continue

            # Check if prices and quantity

            if buy:
                book.sort(key=lambda order: order[0], reverse=True)
                limit_price, max_qty = book[0][0], book[0][1]
                book.pop()
                while max_qty < qty and len(
                        book
                ) > 0:  # Consume orders until qty and price are good
                    order = book.pop()
                    limit_price = order[0]
                    max_qty = max_qty + order[1]
                self.log_debug(
                    "Calculated : qty %.8f; max_qty %.8f; price %.8f; limit_price %.8f"
                    % (qty, max_qty, price, limit_price))
                if max_qty < qty or limit_price < price * (1 - max_loss):
                    if failed_sym is path["sym2"]:
                        self.log_verbose(
                            "Order 2 liquidation failed, changing trade order")
                        entry["force_down"] = not force_down
                    self.log_verbose(
                        "Couldn't liquidate sell, buy price is too low : %.8f at quantity %.8f"
                        % (limit_price, max_qty))
                    continue  # Too low to sell back
            else:
                book.sort(key=lambda order: order[0])
                limit_price, max_qty = book[0][0], book[0][1]
                book.pop()
                min_qty = qty * (1 - max_loss)
                max_price = price * (qty / min_qty)
                while max_qty < min_qty and len(
                        book
                ) > 0:  # Consume orders until qty and price are good
                    order = book.pop()
                    limit_price = order[0]
                    max_qty = max_qty + order[1]
                self.log_debug(
                    "Calculated : min_qty %.8f; max_qty %.8f; max_price %.8f; limit_price %.8f; price %.8f"
                    % (min_qty, max_qty, max_price, limit_price, price))
                if max_qty < min_qty or limit_price > max_price:
                    if failed_sym is path["sym2"]:
                        entry["force_down"] = not force_down
                    self.log_verbose(
                        "Couldn't liquidate buy, sell price is too high : %.8f at quantity %.8f"
                        % (limit_price, min_qty))
                    continue  # Too expensive to buy back
                dust_qty = 0
                leftover_balance = (price * qty) - (min_qty * limit_price)
                if leftover_balance > 0:
                    dust_qty = leftover_balance / limit_price
                self.log_debug(
                    "dust_qty %.8f;leftover %.8f;qty %.8f; price %.8f;min_qty %.8f;limit_price %.8f"
                    % (dust_qty, leftover_balance, qty, price, min_qty,
                       limit_price))
                qty = self.apply_precision(min_qty + dust_qty, coin)

            # Execute order
            success = self.execute_order(order_num,
                                         path,
                                         force_price=limit_price,
                                         force_qty=qty,
                                         is_liquidation=True)
            if not success:
                self.log_error(
                    "Error during order execution for liquidation of %s" %
                    coin)
            else:
                self.log_verbose("Successfully liquidated %.8f %s for %.8f" %
                                 (qty, symbol, limit_price))
                liquidated.append(index - 1)
                if failed_sym is path[
                        "sym2"] and force_down:  # Add the third step to liquidation
                    path["earned"] = path["spent"]
                    if buy:
                        overspend_ratio = price / limit_price
                        path["qty3"] = self.apply_precision(
                            path["qty3"] / overspend_ratio, path["coin3"])
                    else:
                        overspend_ratio = path["qty2"] / qty
                        path["qty3"] = self.apply_precision(
                            path["qty3"] / overspend_ratio, path["coin1"])
                    new_max_loss = max_loss - (overspend_ratio - 1)
                    self.liquidations.append({
                        "force_down": False,
                        "failed_sym": path["sym3"],
                        "max_loss": new_max_loss,
                        "spent": spent,
                        "path": path
                    })
                    list_len = list_len + 1
                else:
                    if buy:
                        total = qty * limit_price * self.trade_fee
                    else:
                        total = qty * self.trade_fee
                    self.balances[coin1] = self.apply_precision(
                        self.balances[coin1] + total, coin1)
                    self.gains[coin1] = self.apply_precision(
                        self.gains[coin1] + total, coin1)

        for index in list(reversed(liquidated)):  # Remove solved liquidations
            self.log_debug("Removing liquidation entry : {}".format(
                self.liquidations[index]))
            del self.liquidations[index]

    def refresh_blacklist(self):
        for unique_string in list(self.path_blacklist.keys()):
            if self.path_blacklist[unique_string] < time.time() - 10:
                self.log_debug("Remove path from blacklist : %s" %
                               unique_string)
                del self.path_blacklist[unique_string]

    def get_order_book(self, buy, sym, limit=1):
        self.api_call("get_buy/sell_orders")
        start_get_book = time.time()
        book, success = [], False
        # Call the book
        try:
            if buy:
                book = self.api_client.get_buy_orders(sym, limit=limit)
            else:
                book = self.api_client.get_sell_orders(sym, limit=limit)
            success = True
        except:
            success = False
            self.log_error("Failed to load the order book for %s" % sym)
        # Handle max allowed time
        if time.time() - start_get_book > self.time_limit['get_book']:
            success = False
            self.log_verbose("Book request took too long")
        return success, book

    def calc_path(self, balance1, order1, order2, order3,
                  path):  # Data format : {balance, book1, book2, book3}
        self.log_debug("Calculating path prices and quantity")
        coin1, coin2, coin3 = path["coin1"], path["coin2"], path["coin3"]
        buy1, buy2, buy3 = path["buy1"], path["buy2"], path["buy3"]
        # Down the path
        # Hop 1
        if buy1:
            balance1 = self.apply_precision(balance1, coin1)
            qty = min(order1[1], balance1)  # Max I can sell
            balance2 = (qty * order1[0]) * self.trade_fee
        else:
            max_price = (order1[0] * order1[1])
            order_buy_price = min(max_price, balance1)  # Max I can spend
            balance2 = (order_buy_price / order1[0]) * self.trade_fee
        balance2 = self.apply_precision(balance2, coin2)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance1, order1, buy1))
        # Hop 2
        if buy2:
            qty = min(order2[1], balance2)  # Max I can sell
            balance3 = (qty * order2[0]) * self.trade_fee
        else:
            max_price = (order2[0] * order2[1])
            order_buy_price = min(max_price, balance2)  # Max I can spend
            balance3 = (order_buy_price / order2[0]) * self.trade_fee
        balance3 = self.apply_precision(balance3, coin3)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance2, order2, buy2))
        # Hop 3
        if buy3:
            qty = min(order3[1], balance3)  # Max I can sell
            earned = (qty * order3[0]) * self.trade_fee
        else:
            max_price = (order3[0] * order3[1])
            order_buy_price = min(max_price, balance3)  # Max I can spend
            earned = (order_buy_price / order3[0]) * self.trade_fee
        earned = self.apply_precision(earned, coin1)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance3, order3, buy3))
        self.log_debug("Down path final balance : %.8f" % earned)

        # Up the path
        balance1, balance2, balance3 = earned, 0, 0
        # Hop 3
        if buy3:
            qty3 = self.apply_precision(
                (balance1 / self.trade_fee) / order3[0], coin3)  # Qty sold
            balance3 = qty3
        else:
            qty3 = self.apply_precision(balance1 / self.trade_fee,
                                        coin1)  # Qty bought
            balance3 = qty3 * order3[0]
            balance3 = self.apply_precision(balance3, coin3)
        self.log_debug("Up path : balance %.8f; Order %r; isBuy %s" %
                       (balance1, order3, buy3))
        # Hop 2
        if buy2:
            qty2 = self.apply_precision(
                (balance3 / self.trade_fee) / order2[0], coin2)  # Qty sold
            balance2 = qty2
        else:
            qty2 = self.apply_precision(balance3 / self.trade_fee,
                                        coin3)  # Qty bought
            balance2 = qty2 * order2[0]
            balance2 = self.apply_precision(balance2, coin2)
        self.log_debug("Up path : balance %.8f; Order %r; isBuy %s" %
                       (balance3, order2, buy2))
        # Hop 1
        if buy1:
            qty1 = self.apply_precision(
                (balance2 / self.trade_fee) / order1[0], coin1)  # Qty sold
            spent = qty1
        else:
            qty1 = self.apply_precision(balance2 / self.trade_fee,
                                        coin2)  # Qty bought
            spent = qty1 * order1[0]
            spent = self.apply_precision(spent, coin1)
        self.log_debug("Up path : balance %.8f; Order %r; isBuy %s" %
                       (balance2, order1, buy1))
        self.log_debug("Up path final balance is %.8f" % spent)

        # Down the path again, adjusting precision
        self.log_debug("Adjusting precision and values")
        balance1, balance2, balance3 = spent, 0, 0
        # Hop 1
        if buy1:
            qty1 = balance1  # Max I can sell
            balance2 = (qty1 * order1[0]) * self.trade_fee
        else:
            qty1 = self.apply_precision(balance1 / order1[0], coin2)
            balance2 = qty1 * self.trade_fee
        balance2 = self.apply_precision(balance2, coin2)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance1, order1, buy1))
        # Hop 2
        if buy2:
            qty2 = balance2  # Max I can sell
            balance3 = (qty2 * order2[0]) * self.trade_fee
        else:
            qty2 = self.apply_precision(balance2 / order2[0], coin3)
            balance3 = qty2 * self.trade_fee
        balance3 = self.apply_precision(balance3, coin3)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance2, order2, buy2))
        # Hop 3
        if buy3:
            qty3 = balance3  # Max I can sell
            earned = (qty3 * order3[0]) * self.trade_fee
        else:
            qty3 = self.apply_precision(balance3 / order3[0], coin1)
            earned = qty3 * self.trade_fee
        earned = self.apply_precision(earned, coin1)
        self.log_debug("Down path : balance %.8f; Order %r; isBuy %s" %
                       (balance3, order3, buy3))
        self.log_debug("Down path final balance : %.8f" % earned)

        path["qty1"], path["qty2"], path["qty3"] = qty1, qty2, qty3
        path["price1"], path["price2"], path["price3"] = order1[0], order2[
            0], order3[0]
        path["spent"], path["earned"] = spent, earned

    def get_paths_data(self):
        path_prices = []
        trade_fee = self.trade_fee**3
        pairs = self.pairs
        for path in self.paths:
            p0, p1, p2 = path[0], path[1], path[2]
            if p0 in self.balances and self.balances[p0] > 0:
                path_value, buy1, buy2, buy3 = 1, False, False, False
                # First hop
                if path[1] in pairs[p0]:
                    if pairs[p0][path[1]]["sell"] <= 0:
                        continue
                    path_value = path_value / pairs[p0][p1]["sell"]
                    sym1 = "%s-%s" % (p1, p0)
                else:
                    path_value = path_value * pairs[p1][p0]["buy"]
                    sym1 = "%s-%s" % (p0, p1)
                    buy1 = True
                # Second hop
                if path[1] in pairs and p2 in pairs[p1]:
                    if pairs[p1][p2]["sell"] <= 0:
                        continue
                    path_value = path_value / pairs[p1][p2]["sell"]
                    sym2 = "%s-%s" % (p2, p1)
                else:
                    path_value = path_value * pairs[p2][p1]["buy"]
                    sym2 = "%s-%s" % (p1, p2)
                    buy2 = True
                # Third hop
                if p2 in pairs and p0 in pairs[p2]:
                    if pairs[p2][p0]["sell"] <= 0:
                        continue
                    path_value = path_value / pairs[p2][p0]["sell"]
                    sym3 = "%s-%s" % (p0, p2)
                else:
                    path_value = path_value * pairs[p0][p2]["buy"]
                    sym3 = "%s-%s" % (p2, p0)
                    buy3 = True
                path_value = path_value * trade_fee
                if path_value - self.gap_limit_percent > 1:
                    path_prices.append({
                        "path_value": path_value,
                        "coin1": p0,
                        "coin2": p1,
                        "coin3": p2,
                        "buy1": buy1,
                        "buy2": buy2,
                        "buy3": buy3,
                        "sym1": sym1,
                        "sym2": sym2,
                        "sym3": sym3
                    })
        self.log_debug(path_prices)
        return path_prices

    def apply_precision(self, amount, coin):
        return float(
            format(amount - (0.1**self.tradePrecision[coin]),
                   '.%df' % self.tradePrecision[coin]))

    def reload_prices(self):
        self.log_debug("reload_prices")
        start_get_prices = time.time()
        req = None
        try:
            self.api_call("get_trading_symbols")
            req = self.api_client.get_trading_symbols()
            success = True
            if time.time() - start_get_prices > self.time_limit[
                    'get_prices']:  # Error if call too long
                success = False
        except KucoinAPIException as e:
            print("Error during request : error {} : {} ".format(
                e.status_code, e.message))
            success = False
        if success:
            pairs = self.pairs
            # prices = self.prices
            for symbol in req:
                coin_type, coin_type_pair, sell, buy = symbol[
                    "coinType"], symbol["coinTypePair"], symbol.get(
                        "sell", 0), symbol.get("buy", 0)
                if coin_type_pair in pairs and coin_type in pairs[
                        coin_type_pair]:
                    if sell is None:
                        sell = 0
                    if buy is None:
                        buy = 0
                    if buy == 0 or 1 - sell / buy >= 0.03:
                        sell, buy = 0, 0
                    pairs[coin_type_pair][coin_type]["sell"] = sell
                    pairs[coin_type_pair][coin_type]["buy"] = buy
            self.log_debug(pairs)
        return success

    def calculate_min_amount(self):
        self.reload_prices()
        margin = 1.1
        usd_btc = self.pairs['USDT']['BTC']['sell']
        for coinType in self.pairs['BTC']:
            usd_price = self.pairs['BTC'][coinType]['sell'] * usd_btc * margin
            if usd_price == 0:
                usd_price = 1000000  # No data, play it safe
            self.min_amount['coinType'] = 1 / math.log10(usd_price)
        self.min_amount['BTC'] = 1 / math.log10(usd_btc * margin)
        self.last_min_calculation = time.time()

    def start_engine(self):
        # Load list of pairs
        self.get_pairs_list()
        # Get list of coins
        self.get_coins_info()
        # Load funds available for trading based on balances, rules and volume
        self.get_avail_funds()
        self.log_info("Funds available for trading are : {}".format(
            self.balances))
        # load prices
        self.calculate_min_amount()
        # Start analysis & trade loop
        self.loop()

    def get_pairs_list(self):
        try:
            self.api_call("get_trading_symbols")
            req = self.api_client.get_trading_symbols()
        except KucoinAPIException as e:
            print("Error during request : error {} : {} ".format(
                e.status_code, e.message))
            self.get_pairs_list()
            return
        # Add all pairs to the list
        for pair in req:
            if pair["trading"]:
                if pair["coinTypePair"] not in self.pairs:
                    self.pairs[pair["coinTypePair"]] = {}
                # self.prices["{}-{}".format(pair["coinType"], pair["coinTypePair"])] = { "buy": 0, "sell": 0 }
                self.pairs[pair["coinTypePair"]][pair["coinType"]] = {
                    "buy": 0,
                    "sell": 0,
                    "symbol": pair["symbol"]
                }
        # Remove symbol with only one pair
        for QC1 in list(self.pairs.keys()):
            count = 0
            coin = QC1
            for AC1 in list(self.pairs[QC1].keys()):
                coin = AC1
                if AC1 in self.pairs:  # AC1 is a quote coin
                    for QC2 in list(self.pairs[AC1].keys()):
                        if QC2 in self.pairs[QC1] or (QC2 in self.pairs and QC1
                                                      in self.pairs[QC2]):
                            self.paths.append([QC1, AC1, QC2])
                            count = count + 1
                else:  # AC1 is not a quote coin
                    for QC2 in list(self.pairs.keys()):
                        if AC1 in self.pairs[QC2].keys():
                            if QC1 != QC2:
                                if QC1 in self.pairs[QC2] or QC2 in self.pairs[
                                        QC1]:
                                    self.paths.append([QC1, AC1, QC2])
                                    count = count + 1
            if count == 0:
                del (self.pairs[QC1][coin])
        # Count pairs
        pairs_count = 0
        for QC1 in self.pairs.keys():
            pairs_count = pairs_count + len(self.pairs[QC1])
        self.log_debug("All tradable pairs ({}) are : \n{}".format(
            pairs_count, self.pairs))

    def get_coins_info(self):
        try:
            self.api_call("get_coin_list")
            coins = self.api_client.get_coin_list()
            for coin in coins:
                self.tradePrecision[coin["coin"]] = coin["tradePrecision"]
        except KucoinAPIException as e:
            print("Error during request : error {} : {} ".format(
                e.status_code, e.message))
            self.get_coins_info()
            return

    def get_avail_funds(self):  # No real money for now
        # Get account balance
        try:
            self.api_call("get_all_balances")
            all_balances = self.api_client.get_all_balances()
        except KucoinAPIException as e:
            print("Error during request : error {} : {} ".format(
                e.status_code, e.message))
            self.get_avail_funds()
            return
        for coin in all_balances:
            free_balance = coin["balance"]
            if free_balance > 0:
                self.balances[coin["coinType"]] = free_balance
        self.log_debug("All balances in account : {}".format(self.balances))

        # Apply manual rules
        for symbol in list(self.balances.keys()):
            # Remove
            if symbol not in self.fund_rules:
                del (self.balances[symbol])
                continue
            initial_balance = self.balances[symbol]
            # Remove no touch coins from balance
            if self.balances[symbol] > self.fund_rules[symbol][
                    'no_touch_coins']:
                self.balances[symbol] = self.balances[
                    symbol] - self.fund_rules[symbol]['no_touch_coins']
            else:
                del (self.balances[symbol])
            # limit the available balance to the max percentage allowed
            percent_limit = initial_balance * self.fund_rules[symbol][
                'max_percent'] / 100
            if self.balances[symbol] > percent_limit:
                self.balances[symbol] = percent_limit
                self.log_verbose("{} limited to {}% of total : {}".format(
                    symbol, self.fund_rules[symbol]["max_percent"],
                    "%.2f" % self.balances[symbol]))
            # limit to the max of coins allowed
            if self.balances[symbol] > self.fund_rules[symbol]['max_coins']:
                self.balances[symbol] = self.fund_rules[symbol]['max_coins']
                self.log_verbose(
                    "{} balance exceed max_coins rule, limiting to {}{}".
                    format(symbol, self.balances[symbol], symbol))

    def api_call(self, command):
        self.call_count = self.call_count + 1
        if self.call_count > 200:
            sys.exit()
        self.log_debug("API call : {} , counter: {}".format(
            command, self.call_count))

    def exit_message(self):
        self.log_info("Program interrupted, session stats :")
        self.log_info("Gains : \n{}".format(self.gains))
        self.log_verbose("Pending liquidations : \n{}".format(
            self.liquidations))
        self.log_verbose(
            "Final trading balance (Depends on balance rules) : \n{}\n".format(
                self.balances))

    def exit_program(self, signum, frame):
        self.log_info("Interrupt signal received : SIG %d" % signum)
        self.program_running = False

    @staticmethod
    def log_error(*text):
        print('%.6f [ERR]' % time.time(), *text)

    @staticmethod
    def log_info(*text):
        print('%.6f [INF]' % time.time(), *text)

    def log_debug(self, *text):
        if self.debug:
            print('%.6f [DBG]' % time.time(), *text)

    def log_verbose(self, *text):
        if self.verbose:
            print('%.6f [VRB]' % time.time(), *text)
Exemplo n.º 3
0
    client.get_coin_balance('ETH')['balance'])) * (1 - userFee)

# In[5]:

# BINANCE
depth = bclient.get_order_book(symbol='ETHBTC')

# In[6]:

print(orders)
print(sellorders)
print(depth)

# In[7]:

coins = client.get_trading_symbols()  # get all kucoin prices
prices = bclient.get_all_tickers()  # get all binance prices
print(prices)

# In[8]:

# this is a dict of all the fees
coinFeesDict = {
    'RPXETH': 1,
    'ARNETH': 1.8,
    'OMGETH': 0.1,
    'GASETH': 0,
    'MTHETH': 10,
    'GVTETH': 0.1,
    'PPTETH': 0.1,
    'CVCETH': 3,
Exemplo n.º 4
0
    def run(self):
        # Retrieve password
        #self.SKYPE_PWD = getpass.getpass()

        # Kucoin connection
        client = Client(self.API_KEY, self.SECRET_KEY)

        # Skype connection
        #skyper = Skyper(self.SKYPE_USERNAME, self.SKYPE_PWD, self.SKYPE_USERS)
        send_msg_skype(self.skyper, "KUCOIN", time.ctime(int(time.time())),
                       "Bot is running...")
        self.fileManagementObt.log("KUCOIN", time.ctime(int(time.time())),
                                   "Bot is running...")

        # Telegram connection
        #telegramer = Telegramer(token='521036992:AAG8pG_Gc7tmldnlpJgXGUwrPkGGKjhtCHw')
        self.telegramer.send_message(self.TELEGRAM_CHAT_ID, "KUCOIN",
                                     time.ctime(int(time.time())),
                                     "BOT is running...")

        # Get list of symbols
        try:
            if self.DEBUG:
                previousInfo = [{
                    u'sell': 10824.0,
                    u'sort': 100,
                    u'buy': 10820.0,
                    u'changeRate': 0.0417,
                    u'vol': 208.75995661,
                    u'symbol': u'BTC-USDT',
                    u'high': 10849.999999,
                    u'coinType': u'BTC',
                    u'lastDealPrice': 10824.0,
                    u'feeRate': 0.001,
                    u'volValue': 2189548.32186627,
                    u'trading': True,
                    u'low': 9999.0,
                    u'coinTypePair': u'USDT',
                    u'datetime': 1519026140000L,
                    u'change': 433.779732
                }, {
                    u'sell': 0.087433,
                    u'sort': 100,
                    u'buy': 0.08713844,
                    u'changeRate': -0.0054,
                    u'vol': 3946.72613,
                    u'symbol': u'ETH-BTC',
                    u'high': 0.089597,
                    u'coinType': u'LTC',
                    u'lastDealPrice': 0.08712844,
                    u'feeRate': 0.001,
                    u'volValue': 347.13935351,
                    u'trading': True,
                    u'low': 0.086022,
                    u'coinTypePair': u'BTC',
                    u'datetime': 1519026138000L,
                    u'change': -0.00047156
                }]
            else:
                previousInfo = client.get_trading_symbols()
                if previousInfo:
                    for symbol in previousInfo:
                        if symbol['coinType'] not in self.symbols_list:
                            self.symbols_list.append(symbol['coinType'])
        except:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            strFormatted = "{}\n{}:\n{}\n".format(exc_type, fname,
                                                  exc_tb.tb_lineno)
            self.fileManagementObt.log("KUCOIN", time.ctime(int(time.time())),
                                       strFormatted)

        # Request in a loop the list of symbols, and compare it with the initial list.
        while True:
            try:
                currentTime = time.ctime(int(time.time()))
                if self.DEBUG:
                    info = [{
                        u'sell': 10824.0,
                        u'sort': 100,
                        u'buy': 10820.0,
                        u'changeRate': 0.0417,
                        u'vol': 208.75995661,
                        u'symbol': u'BTC-USDT',
                        u'high': 10849.999999,
                        u'coinType': u'BTC',
                        u'lastDealPrice': 10824.0,
                        u'feeRate': 0.001,
                        u'volValue': 2189548.32186627,
                        u'trading': True,
                        u'low': 9999.0,
                        u'coinTypePair': u'USDT',
                        u'datetime': 1519026140000L,
                        u'change': 433.779732
                    }, {
                        u'sell': 0.087433,
                        u'sort': 100,
                        u'buy': 0.08713844,
                        u'changeRate': -0.0054,
                        u'vol': 3946.72613,
                        u'symbol': u'ETH-BTC',
                        u'high': 0.089597,
                        u'coinType': u'LTC',
                        u'lastDealPrice': 0.08712844,
                        u'feeRate': 0.001,
                        u'volValue': 347.13935351,
                        u'trading': True,
                        u'low': 0.086022,
                        u'coinTypePair': u'BTC',
                        u'datetime': 1519026138000L,
                        u'change': -0.00047156
                    }, {
                        u'sell': 944.5969,
                        u'sort': 100,
                        u'buy': 942.9,
                        u'changeRate': 0.0291,
                        u'vol': 1116.388536,
                        u'symbol': u'ETH-USDT',
                        u'high': 947.777777,
                        u'coinType': u'ETH',
                        u'lastDealPrice': 942.9,
                        u'feeRate': 0.001,
                        u'volValue': 1031605.24945772,
                        u'trading': True,
                        u'low': 892.137139,
                        u'coinTypePair': u'USDT',
                        u'datetime': 1519026140000L,
                        u'change': 26.661531
                    }]
                else:
                    info = client.get_trading_symbols()
                if info:
                    print "( ", currentTime, " ): [ KUCOIN ] get_trading_symbols"

                    # Looking for any new symbols.
                    if self.CHECK_NEW_SYMBOLS == True:
                        for symbol in info:
                            if symbol['coinType'] not in self.symbols_list:
                                # New symbol detected.
                                self.symbols_list.append(symbol['coinType'])
                                depositAddress = {}
                                print "( ", currentTime, " ): [ KUCOIN ] new symbol named ", symbol[
                                    'coinType']
                                print "( ", currentTime, " ): [ KUCOIN ] get_deposit_address"
                                try:
                                    depositAddress = client.get_deposit_address(
                                        symbol['coinType'])
                                except:
                                    pass
                                # Send a message to the skype user and telegram channel.
                                if len(depositAddress) != 0:
                                    self.telegramer.send_message(
                                        self.TELEGRAM_CHAT_ID, "KUCOIN",
                                        currentTime, depositAddress)
                                    send_msg_skype(self.skyper, "KUCOIN",
                                                   currentTime, depositAddress)
                                    print "( ", currentTime, " ): [ KUCOIN ] depositAddress ", depositAddress
                                else:
                                    self.telegramer.send_message(
                                        self.TELEGRAM_CHAT_ID, "KUCOIN",
                                        currentTime, symbol['coinType'])
                                    send_msg_skype(self.skyper, "KUCOIN",
                                                   currentTime,
                                                   symbol['coinType'])
                                    print "( ", currentTime, " ): [ KUCOIN ] depositAddress ", symbol[
                                        'coinType']
                                self.fileManagementObt.log(
                                    "KUCOIN", time.ctime(int(time.time())),
                                    depositAddress)
                                break

                    # Looking for a symbol in the list.
                    for symbol in self.SYMBOLS:
                        depositAddress = {}
                        for kucoinSymbol in info:
                            if kucoinSymbol['coinType'] == symbol:
                                # Symbol in the list detected.
                                print "( ", currentTime, " ): [ KUCOIN ] found symbol ", symbol
                                print "( ", currentTime, " ): [ KUCOIN ] get_deposit_address"
                                if self.ALARM == True:
                                    if self.alarm_function_running == False:
                                        self.alarmThread = Thread(
                                            target=self.threaded_alarm_function
                                        )
                                        self.alarmThread.start()
                                        self.alarm_function_running = True
                                try:
                                    depositAddress = client.get_deposit_address(
                                        symbol)
                                except:
                                    pass
                                if len(depositAddress) != 0:
                                    self.telegramer.send_message(
                                        self.TELEGRAM_CHAT_ID, "KUCOIN",
                                        currentTime, depositAddress)
                                    send_msg_skype(self.skyper, "KUCOIN",
                                                   currentTime, depositAddress)
                                    print "( ", currentTime, " ): [ KUCOIN ] depositAddress ", depositAddress
                                else:
                                    self.telegramer.send_message(
                                        self.TELEGRAM_CHAT_ID, "KUCOIN",
                                        currentTime, symbol)
                                    send_msg_skype(self.skyper, "KUCOIN",
                                                   currentTime, symbol)
                                    print "( ", currentTime, " ): [ KUCOIN ] depositAddress ", symbol
                                self.SYMBOLS.remove(symbol)
                                self.fileManagementObt.log(
                                    "KUCOIN", time.ctime(int(time.time())),
                                    depositAddress)
                                break
                    previousInfo = info
            except:
                exc_type, exc_obj, exc_tb = sys.exc_info()
                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                print(exc_type, fname, exc_tb.tb_lineno)
                strFormatted = "{}\n{}:\n{}\n".format(exc_type, fname,
                                                      exc_tb.tb_lineno)
                self.fileManagementObt.log("KUCOIN",
                                           time.ctime(int(time.time())),
                                           strFormatted)
            time.sleep(10)
Exemplo n.º 5
0
    # though the deposit json contains a fee
    return {"Type": "Deposit", "Buy": "{0:.8f}".format(row['amount']), "Cur.": row['coinType'], \
      "Sell": "", "sCur.": "", "Fee": "", \
      "fCur.": "", "Exchange": "KuCoin", "Group": "", "Comment": row['address'], \
      "Date": datetime.fromtimestamp(row['createdAt']/1000).strftime('%Y-%m-%d %H:%M:%S') }


def getWithdrawalRowFormat(row):
    return {"Type": "Withdrawal", "Buy": "", "Cur.": "", \
      "Sell": "{0:.8f}".format(row['amount']), "sCur.": row['coinType'], "Fee": "{0:.8f}".format(row['fee']), \
      "fCur.": row['coinType'], "Exchange": "KuCoin", "Group": "", "Comment": row['address'], \
      "Date": datetime.fromtimestamp(row['createdAt']/1000).strftime('%Y-%m-%d %H:%M:%S') }


# list all the coins on KuCoin
symbols = client.get_trading_symbols()
print("Got a list of trading symbols")

#loop through every symbol and work out if you have any trades
for row in symbols:

    # for the current symbol get your dealt orders
    symbol = row['symbol']

    retry = 0
    while (retry < maxretries):
        try:
            dealtOrders = client.get_dealt_orders(symbol='{0}'.format(symbol))
            data = dealtOrders['datas']
            break
        except:
Exemplo n.º 6
0
class KucoinWrapper(BaseExchangeWrapper):
    INTERVAL_MAP = {
        CandleTicks.one_minute : Client.RESOLUTION_1MINUTE,
        CandleTicks.five_minutes : Client.RESOLUTION_5MINUTES,
        CandleTicks.thirty_minutes : Client.RESOLUTION_30MINUTES,
        CandleTicks.one_hour : Client.RESOLUTION_1HOUR,
        CandleTicks.one_day : Client.RESOLUTION_1DAY,
    }
    TIME_SINCE_MAP = {
        CandleTicks.one_minute : "1 hour ago",
        CandleTicks.five_minutes : "5 hours ago",
        CandleTicks.thirty_minutes : "26 hours ago",
        CandleTicks.one_hour : "52 hours ago",
        CandleTicks.one_day : "60 days ago",
    }
    ORDER_TYPE_MAPPINGS = {
        'BUY' : OrderType.limit_buy,
        'SELL' : OrderType.limit_sell,
    }

    def __init__(self, api_key, api_secret):
        BaseExchangeWrapper.__init__(self)
        self._handle = Client(api_key, api_secret)
        self._filters = {}
        self._load_markets()

    def _perform_request(self, request_lambda):
        try:
            return request_lambda()
        except KucoinAPIException as ex:
            raise ExchangeAPIException('Failed to perform request: {0}'.format(ex.message))

    def _load_currencies(self):
        result = self._perform_request(lambda: self._handle.get_coin_list())
        for coin in result:
            self.add_currency(
                Currency(
                    coin['coin'],
                    coin['name'],
                    coin['confirmationCount'],
                    coin['withdrawFeeRate'] # check if this is right
                )
            )

    def _load_markets(self):
        self._load_currencies()
        result = self._perform_request(lambda: self._handle.get_trading_symbols())
        for market in result:
            self.add_market(market['coinTypePair'], market['coinType'])

    def _make_symbol(self, base_currency_code, market_currency_code):
        return '{0}-{1}'.format(market_currency_code, base_currency_code)

    def _process_paged_request(self, make_request, callback):
        page = 1
        limit = 50
        data = self._perform_request(lambda: make_request(limit=limit, page=page))
        while len(data['datas']) > 0:
            for entry in data['datas']:
                callback(entry)
            page += 1
            data = make_request(limit=limit, page=page)

    def get_market_state(self, base_currency_code, market_currency_code):
        symbol = self._make_symbol(base_currency_code, market_currency_code)
        data = self._perform_request(lambda: self._handle.get_tick(symbol))
        return MarketState(data['sell'], data['buy'], data['lastDealPrice'])

    def get_orderbook(self, base_currency_code, market_currency_code):
        symbol = self._make_symbol(base_currency_code, market_currency_code)
        data = self._perform_request(lambda: self._handle.get_order_book(symbol))
        buy_orderbook = Orderbook()
        sell_orderbook = Orderbook()
        for item in data['BUY']:
            buy_orderbook.add_order(Order(item[0], item[1]))
        for item in data['SELL']:
            sell_orderbook.add_order(Order(item[0], item[1]))
        return (buy_orderbook, sell_orderbook)

    def get_candles(self, base_currency_code, market_currency_code, interval, limit):
        symbol = self._make_symbol(base_currency_code, market_currency_code)
        since = KucoinWrapper.TIME_SINCE_MAP[interval]
        kucoin_interval = KucoinWrapper.INTERVAL_MAP[interval]
        data = self._perform_request(
            lambda: self._handle.get_historical_klines_tv(symbol, kucoin_interval, since)
        )
        output = []
        for i in data:
            output.append(Candle(
                i[3], # Low
                i[2], # High
                i[1], # Open
                i[4], # Close
                dateparser.parse(str(i[0]))
            ))
        return output

    def get_wallets(self):
        output = []
        self._process_paged_request(
            self._handle.get_all_balances,
            lambda entry: output.append(Wallet(
                entry['coinType'],
                entry['balance'],
                entry['balance'] - entry['freezeBalance'],
                entry['freezeBalance']
            ))
        )
        return output

    def get_wallet(self, currency_code):
        self.check_valid_currency(currency_code)
        data = self._perform_request(lambda: self._handle.get_coin_balance(currency_code))
        return Wallet(
            self._currencies[currency_code],
            data['balance'],
            data['balance'] - data['freezeBalance'],
            data['freezeBalance']
        )

    def get_deposit_history(self, currency_code):
        request = lambda limit, page: self._handle.get_deposits(
            currency_code,
            limit=limit,
            page=page
        )
        output = []
        self._process_paged_request(
            request,
            lambda entry: output.append(Transfer(
                self._currencies[entry['coinType']],
                entry['amount'],
                entry['outerWalletTxid'], # is this right?
                1 if entry['status'] == 'FINISHED' else 0,
                0, # cost
                entry['status'] == 'CANCEL',
                dataparser.parse(str(entry['createdAt']))
            ))
        )
        return output

    def get_withdrawal_history(self, currency_code):
        request = lambda limit, page: self._handle.get_withdrawals(
            currency_code,
            limit=limit,
            page=page
        )
        output = []
        self._process_paged_request(
            request,
            lambda entry: output.append(Transfer(
                self._currencies[entry['coinType']],
                entry['amount'],
                entry['outerWalletTxid'], # is this right?
                0,
                entry['fee'],
                entry['status'] == 'CANCEL',
                dataparser.parse(str(entry['createdAt']))
            ))
        )
        return output

    def get_open_orders(self):
        raise ExchangeAPIException('Not implemented')

    def get_order_history(self):
        output = []
        self._process_paged_request(
            self._handle.get_dealt_orders,
            lambda entry: output.append(TradeOrder(
                data['orderOid'],
                self._currencies[base_currency],
                self._currencies[market_currency],
                None, # Date open
                dateparser.parse(str(data['createdAt'])),
                data['amount'],
                0, # Amount remaining
                data['dealPrice'],
                data['dealPrice'],
                KucoinWrapper.ORDER_TYPE_MAPPINGS[data['direction']]
            ))
        )
        return output

    def buy(self, base_currency_code, market_currency_code, amount, rate):
        symbol = self._make_symbol(base_currency_code, market_currency_code)
        result = self._perform_request(lambda: self._handle.create_buy_order(symbol, rate, amount))
        return result['orderOid']

    def sell(self, base_currency_code, market_currency_code, amount, rate):
        symbol = self._make_symbol(base_currency_code, market_currency_code)
        result = self._perform_request(lambda: self._handle.create_sell_order(symbol, rate, amount))
        return result['orderOid']

    def cancel_order(self, base_currency_code, market_currency_code, order_id):
        self._perform_request(lambda: self._handle.cancel_order(order_id, None))

    def withdraw(self, currency_code, amount, address, address_tag):
        if address_tag:
            raise ExchangeAPIException('Address tag not supported')
        self._perform_request(
            lambda: self._handle.create_withdrawal(currency_code, amount, address)
        )

    def get_deposit_address(self, currency_code):
        result = self._perform_request(lambda: self._handle.get_deposit_address(currency_code))
        return CryptoAddress(
            currency_code,
            result['address'],
            None
        )

    def transfers_needs_asset(self):
        return True
Exemplo n.º 7
0
class KucoinClient(AbstractClient):
    def __init__(self, listener):
        self.exchange = 'kucoin'
        self.trade_fee = Decimal(0.001)
        self.client = Client(api_keys.kucoin[0], api_keys.kucoin[1])
        super(KucoinClient, self).__init__(listener)
        self.get_all_balances()

    def _buy(self, symbol, price, amount):
        result = self.client.create_buy_order(symbol, price, amount)
        self.open_orders[symbol] = [str(result['orderOid']), 'BUY', symbol]

    def _sell(self, symbol, price, amount):
        result = self.client.create_sell_order(symbol, price, amount)
        self.open_orders[symbol] = [str(result['orderOid']), 'SELL', symbol]

    def _cancel(self, symbol):
        order = self.open_orders.pop(symbol)
        self.client.cancel_order(order[0], order[1], symbol=order[2])
        time.sleep(1)
        if not self.is_order_fulfilled(order[0]):
            raise Exception("cancelled order not fulfilled!")

    def is_order_fulfilled(self, orderOid):
        try:
            for data in self.client.get_dealt_orders()['datas']:
                if data['orderOid'] == orderOid:
                    return True

        except (KucoinRequestException, KucoinAPIException) as e:
            print datetime.now(), "[Kucoin] is order fulfilled error", str(
                e).replace('\r\n', '')
            self.errors += 1

        return False

    def get_all_balances(self):
        # Kucoin paginates the coin balances and we can only retrieve 20 coins per page
        page = 1
        while True:
            try:
                result = self.client.get_all_balances_paged(limit=20,
                                                            page=page)

                for balance in result['datas']:
                    self.coin_balances[str(balance['coinType'])] = Decimal(
                        str(balance['balance'])
                    )  # + Decimal(balance['freezeBalance'])

                page = result['currPageNo'] + 1
                if page > result['pageNos']:
                    break

            except (KucoinRequestException, KucoinAPIException) as e:
                print datetime.now(), "[Kucoin] get all balances error:", str(
                    e).replace('\r\n', '')
                self.errors += 1
                break

        return self.coin_balances

    # Gets the buy/sell price and amount for one symbol at a time (inefficient).
    def _market_poll(self):
        result = {'success': False}
        result['client'] = self
        result['timestamp'] = int(time.time() * 1000)
        result['coinpairs'] = {}

        threads = []
        for symbol in self.exchange_symbols:
            thread = threading.Thread(target=self._symbol_poll,
                                      args=[symbol, result])
            thread.start()
            threads.append(thread)

        for thread in threads:
            thread.join()

        if len(result['coinpairs']) > 0:
            result['success'] = True

        return result

    def _symbol_poll(self, symbol, result):
        try:
            data = self.client.get_order_book(symbol, limit=1)

            if data['BUY'] is not None and data['SELL'] is not None:
                result['coinpairs'][self._get_arbitrager_coinpair(symbol)] = {
                    'buy': Decimal(str(data['BUY'][0][0])),
                    'buyAmount': Decimal(str(data['BUY'][0][1])),
                    'sell': Decimal(str(data['SELL'][0][0])),
                    'sellAmount': Decimal(str(data['SELL'][0][1]))
                }

        except (KucoinRequestException, KucoinAPIException) as e:
            print datetime.now(), "[Kucoin] market poll error", str(e).replace(
                '\r\n', '')
            self.errors += 1

    # Gets the buy/sell price for all the symbols with one query but does not contain buy/sell amount (useless).
    @DeprecationWarning
    def market_poll_no_amount(self):
        threading.Timer(Consts.POLL_INTERVAL,
                        self.market_poll_no_amount).start()
        timestamp = int(time.time() * 1000)
        try:
            coins = self.client.get_trading_symbols()
            if self.verbose:
                now = int(time.time() * 1000)
                print datetime.now(
                ), "[Kucoin] market_poll took", now - timestamp, "ms."
        except (KucoinRequestException, KucoinAPIException) as e:
            print datetime.now(), "[Kucoin] market poll error", str(e).replace(
                '\r\n', ' ')
            result = {'success': False}
            result['client'] = self
            return result

        result = {'success': True}
        result['client'] = self
        result['timestamp'] = timestamp
        result['coinpairs'] = {}

        for coin in coins:
            symbol = coin['symbol']
            if symbol in self.exchange_symbols:
                result['coinpairs'][self._get_arbitrager_coinpair(symbol)] = {
                    'buy': Decimal(str(coin['buy'])),
                    'sell': Decimal(str(coin['sell']))
                }
                # result['timestamp'] = coin['datetime']

        if self.callback is None:
            print result
        else:
            self.callback(result)