Example #1
0
def get_binance(key, secret):
    """Get balance from binance exchange API."""

    client = BinClient(key, secret)
    acct = client.get_account()
    df = pd.DataFrame(acct['balances'])
    df['source'] = 'binance'

    df = df[['free', 'asset', 'source']]
    df.columns = ['balance', 'coin', 'source']

    return df
class BinanceAPIManager:
    def __init__(self, config: Config, db: Database, logger: Logger):
        self.binance_client = Client(
            config.BINANCE_API_KEY,
            config.BINANCE_API_SECRET_KEY,
            tld=config.BINANCE_TLD,
        )
        self.db = db
        self.logger = logger
        self.config = config

    @cached(cache=TTLCache(maxsize=1, ttl=43200))
    def get_trade_fees(self) -> Dict[str, float]:
        return {
            ticker["symbol"]: ticker["taker"]
            for ticker in self.binance_client.get_trade_fee()["tradeFee"]
        }

    @cached(cache=TTLCache(maxsize=1, ttl=60))
    def get_using_bnb_for_fees(self):
        return self.binance_client.get_bnb_burn_spot_margin()["spotBNBBurn"]

    def get_fee(self, origin_coin: Coin, target_coin: Coin, selling: bool):
        return 0.001
        base_fee = self.get_trade_fees()[origin_coin + target_coin]
        if not self.get_using_bnb_for_fees():
            return base_fee
        # The discount is only applied if we have enough BNB to cover the fee
        amount_trading = (self._sell_quantity(origin_coin.symbol,
                                              target_coin.symbol)
                          if selling else self._buy_quantity(
                              origin_coin.symbol, target_coin.symbol))
        fee_amount = amount_trading * base_fee * 0.75
        if origin_coin.symbol == "BNB":
            fee_amount_bnb = fee_amount
        else:
            origin_price = self.get_market_ticker_price(origin_coin +
                                                        Coin("BNB"))
            if origin_price is None:
                return base_fee
            fee_amount_bnb = fee_amount * origin_price
        bnb_balance = self.get_currency_balance("BNB")
        if bnb_balance >= fee_amount_bnb:
            return base_fee * 0.75
        return base_fee

    def get_all_market_tickers(self) -> AllTickers:
        """
        Get ticker price of all coins
        """
        return AllTickers(self.binance_client.get_all_tickers())

    def get_market_ticker_price(self, ticker_symbol: str):
        """
        Get ticker price of a specific coin
        """
        for ticker in self.binance_client.get_symbol_ticker():
            if ticker["symbol"] == ticker_symbol:
                return float(ticker["price"])
        return None

    def get_currency_balance(self, currency_symbol: str):
        """
        Get balance of a specific coin
        """
        for currency_balance in self.binance_client.get_account()["balances"]:
            if currency_balance["asset"] == currency_symbol:
                return float(currency_balance["free"])
        return None

    def retry(self, func, *args, **kwargs):
        time.sleep(2)
        attempts = 0
        while attempts < 30:
            try:
                return func(*args, **kwargs)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info("Failed to Buy/Sell. Trying Again.")
                if attempts == 0:
                    self.logger.info(e)
                attempts += 1
        return None

    def get_symbol_filter(self, origin_symbol: str, target_symbol: str,
                          filter_type: str):
        return next(_filter for _filter in self.binance_client.get_symbol_info(
            origin_symbol + target_symbol)["filters"]
                    if _filter["filterType"] == filter_type)

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_alt_tick(self, origin_symbol: str, target_symbol: str):
        step_size = self.get_symbol_filter(origin_symbol, target_symbol,
                                           "LOT_SIZE")["stepSize"]
        if step_size.find("1") == 0:
            return 1 - step_size.find(".")
        return step_size.find("1") - 1

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_min_notional(self, origin_symbol: str, target_symbol: str):
        return float(
            self.get_symbol_filter(origin_symbol, target_symbol,
                                   "MIN_NOTIONAL")["minNotional"])

    def wait_for_order(self, origin_symbol, target_symbol, order_id):
        while True:
            try:
                order_status = self.binance_client.get_order(
                    symbol=origin_symbol + target_symbol, orderId=order_id)
                break
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")
                time.sleep(1)

        self.logger.info(order_status)

        while order_status["status"] != "FILLED":
            try:
                order_status = self.binance_client.get_order(
                    symbol=origin_symbol + target_symbol, orderId=order_id)

                if self._should_cancel_order(order_status):
                    cancel_order = None
                    while cancel_order is None:
                        cancel_order = self.binance_client.cancel_order(
                            symbol=origin_symbol + target_symbol,
                            orderId=order_id)
                    self.logger.info("Order timeout, canceled...")

                    # sell partially
                    if order_status[
                            "status"] == "PARTIALLY_FILLED" and order_status[
                                "side"] == "BUY":
                        self.logger.info("Sell partially filled amount")

                        order_quantity = self._sell_quantity(
                            origin_symbol, target_symbol)
                        partially_order = None
                        while partially_order is None:
                            partially_order = self.binance_client.order_market_sell(
                                symbol=origin_symbol + target_symbol,
                                quantity=order_quantity)

                    self.logger.info("Going back to scouting mode...")
                    return None

                if order_status["status"] == "CANCELED":
                    self.logger.info(
                        "Order is canceled, going back to scouting mode...")
                    return None

                time.sleep(1)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")
                time.sleep(1)

        return order_status

    def _should_cancel_order(self, order_status):
        minutes = (time.time() - order_status["time"] / 1000) / 60
        timeout = 0

        if order_status["side"] == "SELL":
            timeout = float(self.config.SELL_TIMEOUT)
        else:
            timeout = float(self.config.BUY_TIMEOUT)

        if timeout and minutes > timeout and order_status["status"] == "NEW":
            return True

        if timeout and minutes > timeout and order_status[
                "status"] == "PARTIALLY_FILLED":
            if order_status["side"] == "SELL":
                return True

            if order_status["side"] == "BUY":
                current_price = self.get_market_ticker_price(
                    order_status["symbol"])
                if float(current_price) * (1 - 0.001) > float(
                        order_status["price"]):
                    return True

        return False

    def buy_alt(self, origin_coin: Coin, target_coin: Coin,
                all_tickers: AllTickers):
        return self.retry(self._buy_alt, origin_coin, target_coin, all_tickers)

    def _buy_quantity(self,
                      origin_symbol: str,
                      target_symbol: str,
                      target_balance: float = None,
                      from_coin_price: float = None):
        target_balance = target_balance or self.get_currency_balance(
            target_symbol)
        from_coin_price = from_coin_price or self.get_all_market_tickers(
        ).get_price(origin_symbol + target_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(target_balance * 10**origin_tick /
                          from_coin_price) / float(10**origin_tick)

    def _buy_alt(self, origin_coin: Coin, target_coin: Coin, all_tickers):
        """
        Buy altcoin
        """
        #Update balance first
        trade_log = self.db.start_trade_log(origin_coin, target_coin, False)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)
        from_coin_price = all_tickers.get_price(origin_symbol + target_symbol)

        order_quantity = self._buy_quantity(origin_symbol, target_symbol,
                                            target_balance, from_coin_price)
        self.logger.info(
            f"BUY QTY {order_quantity} of {origin_symbol} for ${target_balance} {self.config.LOG_USER}"
        )

        # prevent low amount buy
        if target_balance < 100:
            self.logger.warning(
                f"Couldn't BUY with {target_balance}$ {self.config.LOG_USER}")
            time.sleep(2)
            return None

        # Try to buy until successful
        order = None
        while order is None:
            try:
                order = self.binance_client.order_market_buy(
                    symbol=origin_symbol + target_symbol,
                    quoteOrderQty=target_balance)
                self.logger.info(order)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")

        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        stat = self.wait_for_order(origin_symbol, target_symbol,
                                   order["orderId"])

        if stat is None:
            return None

        self.logger.info(f"Bought {origin_symbol} {self.config.LOG_USER}")
        trade_log.set_complete(stat["cummulativeQuoteQty"])

        return order

    def sell_alt(self, origin_coin: Coin, target_coin: Coin,
                 all_tickers: AllTickers):
        return self.retry(self._sell_alt, origin_coin, target_coin,
                          all_tickers)

    def _sell_quantity(self,
                       origin_symbol: str,
                       target_symbol: str,
                       origin_balance: float = None):
        origin_balance = origin_balance or self.get_currency_balance(
            origin_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(origin_balance * 10**origin_tick) / float(
            10**origin_tick)

    def _sell_alt(self, origin_coin: Coin, target_coin: Coin,
                  all_tickers: AllTickers):
        """
        Sell altcoin
        """
        trade_log = self.db.start_trade_log(origin_coin, target_coin, True)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)
        from_coin_price = all_tickers.get_price(origin_symbol + target_symbol)

        order_quantity = self._sell_quantity(origin_symbol, target_symbol,
                                             origin_balance)
        self.logger.info(
            f"Selling {order_quantity} of {origin_symbol} {self.config.LOG_USER}"
        )

        self.logger.info(f"Balance is {origin_balance}")

        # prevent low amount buy
        if origin_balance * from_coin_price < 100:
            self.logger.warning(
                f"Couldn't SELL with {origin_balance * from_coin_price}$ {self.config.LOG_USER}"
            )
            time.sleep(2)
            return None

        order = None
        while order is None:
            # Should sell at calculated price to avoid lost coin
            order = self.binance_client.order_market_sell(
                symbol=origin_symbol + target_symbol, quantity=order_quantity)

        self.logger.info(f"SELL order ID {order}")

        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        # Binance server can take some time to save the order
        self.logger.info("Waiting for Binance")

        stat = self.wait_for_order(origin_symbol, target_symbol,
                                   order["orderId"])

        if stat is None:
            return None

        new_balance = self.get_currency_balance(origin_symbol)
        while new_balance >= origin_balance:
            new_balance = self.get_currency_balance(origin_symbol)

        self.logger.info(f"Sold {origin_symbol} {self.config.LOG_USER}")

        trade_log.set_complete(stat["cummulativeQuoteQty"])

        return order
Example #3
0
f = open("token.txt", "r")
contents = f.read()
newline = False
secretkey = ""
apikey = ""
for x in contents:
    if x == "\n":
        newline = True
        continue
    if not newline:
        apikey += x
    else:
        secretkey += x
client = Client(apikey, secretkey)
f.close()
balances = client.get_account()['balances']
count = 0
for thing in balances:
    if thing['asset'] == "ZIL":
        print(count)
    count += 1
print(balances)
print(balances[120])

launchString = 'Backtest("' + LaunchDict["pair"] + '", strat=' + LaunchDict[
    'strat'] + ', klineint=' + LaunchDict["klineint"] + ', ss="' + LaunchDict[
        'ss'] + '"'

for thing in LaunchDict:
    if not LaunchDict[thing]:
        launchString += ", " + thing + "=False"
class BinanceAPIManager:
    def __init__(self, config: Config, db: Database, logger: Logger):
        # initializing the client class calls `ping` API endpoint, verifying the connection
        self.binance_client = Client(
            config.BINANCE_API_KEY,
            config.BINANCE_API_SECRET_KEY,
            tld=config.BINANCE_TLD,
        )
        self.db = db
        self.logger = logger
        self.config = config

        self.cache = BinanceCache()
        self.stream_manager: Optional[BinanceStreamManager] = None
        self.setup_websockets()

    def setup_websockets(self):
        self.stream_manager = BinanceStreamManager(
            self.cache,
            self.config,
            self.binance_client,
            self.logger,
        )

    @cached(cache=TTLCache(maxsize=1, ttl=43200))
    def get_trade_fees(self) -> Dict[str, float]:
        return {
            ticker["symbol"]: ticker["taker"]
            for ticker in self.binance_client.get_trade_fee()["tradeFee"]
        }

    @cached(cache=TTLCache(maxsize=1, ttl=60))
    def get_using_bnb_for_fees(self):
        return self.binance_client.get_bnb_burn_spot_margin()["spotBNBBurn"]

    def get_fee(self, origin_coin: Coin, target_coin: Coin, selling: bool):
        fees = self.get_trade_fees()
        if not fees:
            base_fee = 0.001
        else:
            base_fee = fees[origin_coin + target_coin]

        if not self.get_using_bnb_for_fees():
            return base_fee

        # The discount is only applied if we have enough BNB to cover the fee
        amount_trading = (self._sell_quantity(origin_coin.symbol,
                                              target_coin.symbol)
                          if selling else self._buy_quantity(
                              origin_coin.symbol, target_coin.symbol))

        fee_amount = amount_trading * base_fee * 0.75
        if origin_coin.symbol == "BNB":
            fee_amount_bnb = fee_amount
        else:
            origin_price = self.get_ticker_price(origin_coin + Coin("BNB"))
            if origin_price is None:
                return base_fee
            fee_amount_bnb = fee_amount * origin_price

        bnb_balance = self.get_currency_balance("BNB")

        if bnb_balance >= fee_amount_bnb:
            return base_fee * 0.75
        return base_fee

    def get_account(self):
        """
        Get account information
        """
        return self.binance_client.get_account()

    def get_ticker_price(self, ticker_symbol: str):
        """
        Get ticker price of a specific coin
        """
        price = self.cache.ticker_values.get(ticker_symbol, None)
        if price is None and ticker_symbol not in self.cache.non_existent_tickers:
            self.cache.ticker_values = {
                ticker["symbol"]: float(ticker["price"])
                for ticker in self.binance_client.get_symbol_ticker()
            }
            self.logger.debug(
                f"Fetched all ticker prices: {self.cache.ticker_values}")
            price = self.cache.ticker_values.get(ticker_symbol, None)
            if price is None:
                self.logger.debug(
                    f"Ticker does not exist: {ticker_symbol} - will not be fetched from now on"
                )
                self.cache.non_existent_tickers.add(ticker_symbol)

        return price

    def get_currency_balance(self, currency_symbol: str, force=False) -> float:
        """
        Get balance of a specific coin
        """
        with self.cache.open_balances() as cache_balances:
            balance = cache_balances.get(currency_symbol, None)
            if force or balance is None:
                cache_balances.clear()
                cache_balances.update({
                    currency_balance["asset"]: float(currency_balance["free"])
                    for currency_balance in self.binance_client.get_account()
                    ["balances"]
                })
                self.logger.debug(f"Fetched all balances: {cache_balances}")
                if currency_symbol not in cache_balances:
                    cache_balances[currency_symbol] = 0.0
                    return 0.0
                return cache_balances.get(currency_symbol, 0.0)

            return balance

    def retry(self, func, *args, **kwargs):
        time.sleep(1)
        attempts = 0
        while attempts < 20:
            try:
                return func(*args, **kwargs)
            except Exception:  # pylint: disable=broad-except
                self.logger.warning(
                    f"Failed to Buy/Sell. Trying Again (attempt {attempts}/20)"
                )
                if attempts == 0:
                    self.logger.warning(traceback.format_exc())
                attempts += 1
        return None

    def get_symbol_filter(self, origin_symbol: str, target_symbol: str,
                          filter_type: str):
        return next(_filter for _filter in self.binance_client.get_symbol_info(
            origin_symbol + target_symbol)["filters"]
                    if _filter["filterType"] == filter_type)

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_alt_tick(self, origin_symbol: str, target_symbol: str):
        step_size = self.get_symbol_filter(origin_symbol, target_symbol,
                                           "LOT_SIZE")["stepSize"]
        if step_size.find("1") == 0:
            return 1 - step_size.find(".")
        return step_size.find("1") - 1

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_min_notional(self, origin_symbol: str, target_symbol: str):
        return float(
            self.get_symbol_filter(origin_symbol, target_symbol,
                                   "MIN_NOTIONAL")["minNotional"])

    def _wait_for_order(
        self, order_id, origin_symbol: str, target_symbol: str
    ) -> Optional[BinanceOrder]:  # pylint: disable=unsubscriptable-object
        while True:
            order_status: BinanceOrder = self.cache.orders.get(order_id, None)
            if order_status is not None:
                break
            self.logger.debug(f"Waiting for order {order_id} to be created")
            time.sleep(1)

        self.logger.debug(f"Order created: {order_status}")

        while order_status.status != "FILLED":
            try:
                order_status = self.cache.orders.get(order_id, order_status)

                self.logger.debug(f"Waiting for order {order_id} to be filled")

                if self._should_cancel_order(order_status):
                    cancel_order = None
                    while cancel_order is None:
                        cancel_order = self.binance_client.cancel_order(
                            symbol=origin_symbol + target_symbol,
                            orderId=order_id)
                    self.logger.info("Order timeout, canceled...")

                    # sell partially
                    if order_status.status == "PARTIALLY_FILLED" and order_status.side == "BUY":
                        self.logger.info("Sell partially filled amount")

                        order_quantity = self._sell_quantity(
                            origin_symbol, target_symbol)
                        partially_order = None
                        while partially_order is None:
                            partially_order = self.binance_client.order_market_sell(
                                symbol=origin_symbol + target_symbol,
                                quantity=order_quantity)

                    self.logger.info("Going back to scouting mode...")
                    return None

                if order_status.status == "CANCELED":
                    self.logger.info(
                        "Order is canceled, going back to scouting mode...")
                    return None

                time.sleep(1)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected order error: {e}")
                time.sleep(1)

        self.logger.debug(f"Order filled: {order_status}")

        return order_status

    def wait_for_order(
        self, order_id, origin_symbol: str, target_symbol: str,
        order_guard: OrderGuard
    ) -> Optional[BinanceOrder]:  # pylint: disable=unsubscriptable-object
        with order_guard:
            return self._wait_for_order(order_id, origin_symbol, target_symbol)

    def _should_cancel_order(self, order_status):
        minutes = (time.time() - order_status.time / 1000) / 60
        timeout = 0

        if order_status.side == "SELL":
            timeout = float(self.config.SELL_TIMEOUT)
        else:
            timeout = float(self.config.BUY_TIMEOUT)

        if timeout and minutes > timeout and order_status.status == "NEW":
            return True

        if timeout and minutes > timeout and order_status.status == "PARTIALLY_FILLED":
            if order_status.side == "SELL":
                return True

            if order_status.side == "BUY":
                current_price = self.get_ticker_price(order_status.symbol)
                if float(current_price) * (1 - 0.001) > float(
                        order_status.price):
                    return True

        return False

    def buy_alt(self, origin_coin: Coin, target_coin: Coin) -> BinanceOrder:
        return self.retry(self._buy_alt, origin_coin, target_coin)

    def _buy_quantity(self,
                      origin_symbol: str,
                      target_symbol: str,
                      target_balance: float = None,
                      from_coin_price: float = None):
        target_balance = target_balance or self.get_currency_balance(
            target_symbol)
        from_coin_price = from_coin_price or self.get_ticker_price(
            origin_symbol + target_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(target_balance * 10**origin_tick /
                          from_coin_price) / float(10**origin_tick)

    def _buy_alt(self, origin_coin: Coin, target_coin: Coin):
        """
        Buy altcoin
        """
        trade_log = self.db.start_trade_log(origin_coin, target_coin, False)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        with self.cache.open_balances() as balances:
            balances.clear()

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)
        from_coin_price = self.get_ticker_price(origin_symbol + target_symbol)

        order_quantity = self._buy_quantity(origin_symbol, target_symbol,
                                            target_balance, from_coin_price)
        self.logger.info(f"Buying roughly {order_quantity} {origin_symbol}")

        # Try to buy until successful
        order = None
        order_guard = self.stream_manager.acquire_order_guard()
        while order is None:
            try:
                order = self.binance_client.order_limit_buy(
                    symbol=origin_symbol + target_symbol,
                    quantity=order_quantity,
                    price=from_coin_price,
                )
                self.logger.debug(order)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.warning(f"Unexpected Error: {e}")

        orderId = order["orderId"]
        self.logger.info(
            f"Placed buy order {orderId}, waiting for it to complete")
        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        order_guard.set_order(origin_symbol, target_symbol, int(orderId))
        order = self.wait_for_order(orderId, origin_symbol, target_symbol,
                                    order_guard)

        if order is None:
            return None

        newBalance = self.get_currency_balance(origin_symbol)
        self.logger.info(f"Bought {newBalance} {origin_symbol}")

        trade_log.set_complete(order.cumulative_quote_qty)

        return order

    def sell_alt(self, origin_coin: Coin, target_coin: Coin) -> BinanceOrder:
        return self.retry(self._sell_alt, origin_coin, target_coin)

    def _sell_quantity(self,
                       origin_symbol: str,
                       target_symbol: str,
                       origin_balance: float = None):
        origin_balance = origin_balance or self.get_currency_balance(
            origin_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(origin_balance * 10**origin_tick) / float(
            10**origin_tick)

    def _sell_alt(self, origin_coin: Coin, target_coin: Coin):
        """
        Sell altcoin
        """
        trade_log = self.db.start_trade_log(origin_coin, target_coin, True)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        with self.cache.open_balances() as balances:
            balances.clear()

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)
        from_coin_price = self.get_ticker_price(origin_symbol + target_symbol)

        order_quantity = self._sell_quantity(origin_symbol, target_symbol,
                                             origin_balance)
        self.logger.info(f"Selling {order_quantity} {origin_symbol}")

        self.logger.debug(f"Balance is {origin_balance}")
        order = None
        order_guard = self.stream_manager.acquire_order_guard()
        while order is None:
            # Should sell at calculated price to avoid lost coin
            self.logger.debug("Attempting to place order")
            order = self.binance_client.order_limit_sell(
                symbol=origin_symbol + target_symbol,
                quantity=order_quantity,
                price=from_coin_price)
            self.logger.debug(f"order: {order}")
        orderId = order["orderId"]

        self.logger.info(
            f"Placed sell order {orderId}, waiting for it to complete")

        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        order_guard.set_order(origin_symbol, target_symbol,
                              int(order["orderId"]))
        order = self.wait_for_order(order["orderId"], origin_symbol,
                                    target_symbol, order_guard)

        if order is None:
            return None

        new_balance = self.get_currency_balance(origin_symbol)
        while new_balance >= origin_balance:
            new_balance = self.get_currency_balance(origin_symbol, True)

        new_target_balance = self.get_currency_balance(target_symbol)
        self.logger.info(
            f"Sold {origin_symbol} for {new_target_balance} {target_symbol}")

        trade_log.set_complete(order.cumulative_quote_qty)

        return order
Example #5
0
class trade_realtime():
    def __init__(self, api_key, api_secret, total_list=None):
        self.coin_list = total_list
        self.api_key = api_key
        self.api_secret = api_secret
        self.client = Client(api_key, api_secret)

    def get_time(self):
        server_time = self.client.get_server_time()
        #  {
        #         "serverTime": 1499827319559
        # }
        return int(server_time['serverTime'])

    def get_exchange_status(self):
        return self.client.get_system_status()

    def get_coin_price(self, coin_list):
        # kwargs = {'data': coin}
        output_data = []
        for coin in coin_list:
            price_d = ast.literal_eval(
                json.dumps(self.client.get_symbol_ticker(symbol=coin)))
            print(price_d)
            price = float(price_d['price'])
            output_data.append(price)

        return output_data

    def get_trade(self, start, end):
        output_data = []
        for coin in self.coin_list:
            output_data.append(
                self.client.get_aggregate_trades(symbol=coin,
                                                 startTime=start,
                                                 endTime=end))

        return output_data

    def get_kline(self, start, end):

        output_data = []
        for coin in self.coin_list:
            output_data.append(
                self.client.get_klines(symbol=coin,
                                       interval=Client.KLINE_INTERVAL_5MINUTE,
                                       limit=500,
                                       startTime=start,
                                       endTime=end))
        return output_data

    def get_historical_klines(self, symbol, interval, start, end):

        # init our list
        output_data = []

        # setup the max limit
        limit = 500
        timeframe = interval_to_milliseconds(interval)
        start_ts = start
        idx = 0
        # it can be difficult to know when a symbol was listed on Binance so allow start time to be before list date
        symbol_existed = False
        while True:
            # fetch the klines from start_ts up to max 500 entries or the end_ts if set
            temp_data = self.client.get_klines(symbol=symbol,
                                               interval=interval,
                                               limit=limit,
                                               startTime=start_ts,
                                               endTime=end)

            # handle the case where our start date is before the symbol pair listed on Binance
            if not symbol_existed and len(temp_data):
                symbol_existed = True

            if symbol_existed:
                # append this loops data to our output data
                output_data += temp_data

                # update our start timestamp using the last value in the array and add the interval timeframe
                start_ts = temp_data[len(temp_data) - 1][0] + timeframe
            else:
                # it wasn't listed yet, increment our start date
                start_ts += timeframe

            idx += 1
            # check if we received less than the required limit and exit the loop
            if len(temp_data) < limit:
                # exit the while loop
                break

            # sleep after every 3rd call to be kind to the API
            if idx % 3 == 0:
                time.sleep(1)

        return output_data

    def get_orderbook_ticker(self):
        pass

    def order_limit_buy(self, **params):

        return self.client.order_limit_buy(**params)

    def order_limit_sell(self, **params):

        return self.client.order_limit_sell(**params)

    def order_market_sell(self, **params):
        return self.client.order_market_sell(**params)

    def order_market_buy(self, **params):
        return self.client.order_market_buy(**params)

    def get_open_orders(self, **params):
        return self.client.get_open_orders(**params)

    def create_test_order(self, **params):

        self.client.create_test_order()

    def get_order(self, **params):
        self.client.get_order(self, **params)

    def get_all_orders(self, **params):
        self.client.get_all_orders(self, **params)

    def cancel_order(self, **params):
        self.client.cancel_order(self, **params)

    def get_account(self, **params):
        return (self.client.get_account(recvWindow=self.get_time()))

    def get_asset_balance(self, asset, **params):
        bal = self.client.get_asset_balance(asset=asset,
                                            recvWindow=self.get_time())
        return ast.literal_eval(json.dumps(bal))['free']

    def start_trade():
        pass

    def get_kline_lag_time(self, coin, lookback_in_ms):

        # lookback = 2*60*1000 #5mins
        # rt.pred_coin_list=[coin]

        end_ts = self.get_time()
        #  calendar.timegm(time.gmtime()) -lookback

        start_ts = end_ts - lookback_in_ms
        #  print("start=",start_ts)
        # print("end=",end_ts)

        f = self.get_historical_klines(symbol=coin,
                                       interval=Client.KLINE_INTERVAL_30MINUTE,
                                       end=end_ts,
                                       start=start_ts)
        f = ast.literal_eval(json.dumps(f))
        return f

    def getState(self, coin_list):

        features = np.empty((LOOK_BACK, 0), float)
        coin_f_index = [2, 3, 4, 5, 7]
        for coin in coin_list:
            coin_f = np.array(self.get_kline_lag_time(coin, LOOK_BACK_IN_MS),
                              dtype=np.float).reshape(-1, 12)
            coin_f = coin_f[coin_f.shape[0] - LOOK_BACK:, coin_f_index]
            if (coin_f.shape[0] < 10):
                print("something is wrong with binance api,return shape=",
                      coin_f.shape)
                return
            #print("coin_f shape ",coin_f.shape)
            #print("features shape ",features.shape)

            # COIN_FEATURE = np.concatenate((COIN_FEATURE, tmp), axis=0)
            features = np.hstack((features, coin_f))
        # features = self.create_input(features,LOOK_BACK)
        # DF_FEATURES
        #reshape for tensorflow backend
        features = features.reshape(1, DF_FEATURES, 1, LOOK_BACK)

        print("create_predictive_input, features shape ", features.shape)

        # print("kline shape after modify",features.shape)
        # features = self.create_input(features ,LOOK_BACK)
        return features
Example #6
0
class BinanceService(ServiceInterface):
    name = 'binance'

    def __init__(self, **kwargs):
        api_key = kwargs["api_key"]
        api_secret = kwargs["api_secret"]

        if not all([api_key, api_secret]):
            raise Exception(
                f"Both api_key and api_secret are required for {name} exchange"
            )

        self.client = Client(api_key, api_secret)
        self.debug_mode = environ.get("DEBUG", False)

    def get_account(self):
        return self.client.get_account()

    def buy(self,
            coin_name,
            quantity=None,
            pair_base="BTC",
            amount=None,
            order_type="market"):
        try:
            symbol = f"{coin_name}{pair_base}".upper()
            precision = self.get_precision(symbol)
            current_price = self.get_price(symbol, precision)
            step_size = self.get_step_size(symbol)

            quantity = self.calculate_buy_qty(price=current_price,
                                              amount=amount,
                                              step_size=step_size)

            logger.info(
                f"step_size>{step_size}>price>{current_price} > Amount > {amount} > precision: {precision} > qty: {quantity}"
            )

            side = Client.SIDE_BUY
            time_in_force = None
            if order_type == "limit":
                order_type = Client.ORDER_TYPE_LIMIT
                time_in_force = "GTC"
            else:
                order_type = Client.ORDER_TYPE_MARKET

            if order_type == Client.ORDER_TYPE_LIMIT:
                logger.info(
                    f"{side} order request:{symbol}>type:{order_type}>quantity:{quantity} > {amount}"
                )
                order = self.client.create_order(symbol=symbol,
                                                 side=side,
                                                 type=order_type,
                                                 quantity=quantity,
                                                 timeInForce=time_in_force,
                                                 price=amount)
            else:
                logger.info(
                    f"{side} order request:{symbol}>type:{order_type}>quantity:{quantity} > {current_price}"
                )
                order = self.client.create_order(symbol=symbol,
                                                 side=side,
                                                 type=order_type,
                                                 quantity=quantity)
            return order
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise UserAdviceException(ex)

    def sell(self,
             coin_name,
             quantity=None,
             pair_base="BTC",
             amount=None,
             order_type="market"):
        try:
            balance = self.get_balance(coin_name)
            if not balance:
                raise UserAdviceException(
                    f"Balance not enough to execute action in {name} exchange")

            symbol = f"{coin_name}{pair_base}".upper()
            precision = self.get_precision(symbol)

            current_price = self.get_price(symbol, precision)
            step_size = self.get_step_size(symbol)
            quantity = self.calculate_sell_qty(price=current_price,
                                               amount=amount,
                                               step_size=step_size)

            side = Client.SIDE_SELL
            time_in_force = None
            if order_type == "limit":
                order_type = Client.ORDER_TYPE_LIMIT
                time_in_force = "GTC"
            else:
                order_type = Client.ORDER_TYPE_MARKET

            logger.info(
                f"{side} order request:{symbol}>type:{order_type}>quantity:{quantity} >> current_price: {current_price}"
            )

            if order_type == Client.ORDER_TYPE_LIMIT:
                order = self.client.create_order(symbol=symbol,
                                                 side=side,
                                                 type=order_type,
                                                 quantity=quantity,
                                                 timeInForce=time_in_force,
                                                 price=amount)
            else:
                order = self.client.create_order(symbol=symbol,
                                                 side=side,
                                                 type=order_type,
                                                 quantity=quantity)
            return order
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise UserAdviceException(ex)

    def get_precision(self, symbol):
        try:
            step = db_client.stepsizes.find_one({"symbol": symbol})
            if step:
                return step["baseAssetPrecision"]
            return 8
        except Exception as ex:
            logger.error("Error retriving step size")
            return 8

    def get_step_size(self, symbol):
        try:
            step = db_client.stepsizes.find_one({"symbol": symbol})
            if step:
                return float(step["lot_size"]["stepSize"])
            return float(0.0)
        except Exception as ex:
            logger.error("Error retriving step size")
            return float(0.0)

    def get_min_notional(self, symbol):
        try:
            step = db_client.stepsizes.find_one({"symbol": symbol})
            if step:
                return float(step["min_notional"]["minNotional"])
            return float(0.0)
        except Exception as ex:
            logger.error("Error retriving step size")
            return float(0.0)

    def get_price(self, symbol, precision=8):
        try:
            price_info = self.client.get_symbol_ticker(symbol=symbol)
            return round(Decimal(price_info.get("price")), precision)
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise Exception(ex)

    def calculate_sell_qty(self, price, amount, step_size):
        quantity = float(amount) / float(price)
        step_precision_decimal = str(step_size).split(".")[1]
        step_precision = len(
            step_precision_decimal) if int(step_precision_decimal) > 0 else 0

        abs_quantity = round(quantity, step_precision)
        print(
            f"price:{price}: amount:{amount} :quantity::::{quantity}: abs_quantity: {abs_quantity}:::step_precision:{step_precision}"
        )
        return abs_quantity

    def calculate_buy_qty(self, price, amount, step_size):
        quantity = float(amount) / float(price)
        step_precision_decimal = str(step_size).split(".")[1]
        step_precision = len(
            step_precision_decimal) if int(step_precision_decimal) > 0 else 0

        abs_quantity = round(quantity, step_precision)
        print(
            f"price:{price}: amount:{amount} :quantity::::{quantity}: abs_quantity: {abs_quantity}:::step_precision:{step_precision}"
        )
        return abs_quantity

    def get_balance(self, coin_name):
        """Query coin balance in exchange account

        Args:
            coin_name (string): Name of coin

        Raises:
            Exception: Raise exception if coin is not found

        Returns:
            float: Coin balance.

        """
        try:
            balance_info = self.client.get_asset_balance(coin_name)
            if not balance_info:
                raise UserAdviceException(
                    f"Coin not found in account in {self.name} exchange")
            return balance_info.get("free")
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise Exception(ex)

    def track_coin(self, payload):
        pass

    def get_prices(self):
        """Returns a list of symbols and their prices

        API: https://python-binance.readthedocs.io/en/latest/binance.html#binance.client.Client.get_symbol_ticker
        Return Example:
            [
                {
                    "symbol": "LTCBTC",
                    "price": "4.00000200"
                },
                {
                    "symbol": "ETHBTC",
                    "price": "0.07946600"
                }
            ]
        """
        return self.client.get_symbol_ticker()

    def get_symbol_info(self, coin_name=None, pair_base="BTC", symbol=None):
        """
        Reference:
            https://python-binance.readthedocs.io/en/latest/binance.html#binance.client.Client.get_symbol_info
        """
        try:
            if not symbol:
                if not coin_name and pair_base:
                    raise Exception(
                        "Both coin name and base is required where symbol not specified"
                    )
                symbol = f"{coin_name}{pair_base}".upper()
            info = self.client.get_symbol_info(symbol=symbol)
            return info
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise Exception(ex)

    def get_open_orders(self, coin_name, pair_base="BTC"):
        try:
            symbol = f"{coin_name}{pair_base}".upper()
            orders = self.client.get_open_orders(symbol=symbol)
            logger.debug(f"orders: {orders}")
            return orders
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise Exception(ex)

    def get_all_orders(self, coin_name, limit=5, pair_base="BTC"):
        try:
            symbol = f"{coin_name}{pair_base}".upper()
            orders = self.client.get_all_orders(symbol=symbol, limit=limit)
            logger.debug(f"orders: {orders}")
            return orders
        except (BinanceAPIException, BinanceRequestException) as ex:
            logger.error(ex, exc_info=True)
            raise Exception(ex)
Example #7
0
                                          startTime=deposit['insertTime'])
            depositCoinVal += deposit['amount']
            depositValBTC += float(klines[0][4]) * deposit['amount']

            nonBTCDeposit[depCoin] = {
                'isBuyer': True,
                'price': float(klines[0][4]),
                'qty': deposit['amount'],
                'commission': 0
            }

    print(depCoin + ': ' + str(depositCoinVal))

print('Overall: ' + str(depositValBTC) + ' BTC')

acctStatus = binClient.get_account()
prices = binClient.get_all_tickers()

print('\n')
print('COIN GAINS')
printLine()

symbols = [
    'XVG', 'POE', 'XLM', 'LEND', 'TRX', 'BNB', 'LTC', 'ADA', 'NEO', 'POWR',
    'ICX', 'ETH'
]

commission = 0.0

for symbol in sorted(symbols):
    pair = symbol + "BTC"
Example #8
0
import config, csv
from binance.client import Client

client = Client(config.API_KEY, config.API_SECRET, tld='us')

info = client.get_account()
my_balances = info['balances']

# my_bal = []

# for d in my_balances:
#     my_bal.append([d['asset'],float(d['free'])])
# for el in my_bal:
#     print(el[0],el[1])

# ________________________________________________________________________________________________________

# status = client.get_all_orders(symbol='ADAUSD')

# print(status)

# from binance.enums import *
# order = client.create_test_order(
#     symbol='BNBBTC',
#     side=SIDE_BUY,
#     type=ORDER_TYPE_LIMIT,
#     timeInForce=TIME_IN_FORCE_GTC,
#     quantity=100,
#     price='0.01158')

# print(order)
Example #9
0
class App:
    def __init__(self):
        self.client = Client(configData.binanceApiKey,
                             configData.binanceApiSecret, {"timeout": 20})
        self.telegramKey = configData.telegramApiKey
        self.data = DotMap()

    def klines(self, symbol, timeframe='5m', count=12):
        """ Return Klines for the given timeframe and count
            [[Open time,Open,High,Low,Close,Volume, ...]]"""
        res = re.search(r'(\d+)([mhdw])', timeframe)
        num = res.group(1) if res else "5"
        time_format = res.group(2) if res else "m"
        time_str = 'minutes'
        if time_format == 'h':
            time_str = 'hours'
        elif time_format == 'd':
            time_str = 'days'
        elif time_format == 'w':
            time_str = 'weeks'

        time_ago_str = f'{count * int(num)} {time_str} ago'
        log.debug("getting klines: %s %s '%s' %s", symbol, timeframe,
                  time_ago_str, count)
        klines = self.client.get_historical_klines(symbol,
                                                   timeframe,
                                                   time_ago_str,
                                                   limit=count)
        return klines

    def tickers(self):
        return {
            x['symbol']: float(x['price'])
            for x in self.client.get_all_tickers()
        }

    @staticmethod
    def floor(number, digits=2):
        return int(math.floor(float(number) *
                              (10**digits))) / (10**digits + 0.0)

    @staticmethod
    def floor_new(number, digits=2):
        return str(
            Decimal(number).quantize(Decimal(1) / Decimal(10**digits),
                                     rounding=ROUND_DOWN))

    def place_order(self, orderObj, priceDigits=10, qtyDigits=4, test=True):
        """ Place and order to binance with the given Order and minimum significan number for quantity and price in
        digits.If error is due to price and quantity notional value then it will be attempted again by reducing the notional digits"""
        while qtyDigits >= 0 or priceDigits >= 0:
            if 'stopPrice' in orderObj:
                orderObj['stopPrice'] = (self.floor_new(
                    orderObj['stopPrice'], priceDigits))
            orderObj['price'] = (self.floor_new(orderObj['price'],
                                                priceDigits))
            orderObj['quantity'] = (self.floor_new(orderObj['quantity'],
                                                   qtyDigits))
            try:
                if (test):
                    self.client.create_test_order(**orderObj)
                    log.info(orderObj)
                    return orderObj
                else:
                    self.client.create_order(**orderObj)
                    return orderObj
                break
            except binance.exceptions.BinanceAPIException as e:
                if ('PRICE_FILTER' in e.message or 'Precision' in e.message):
                    priceDigits = priceDigits - 1
                elif ('LOT_SIZE' in e.message):
                    qtyDigits = qtyDigits - 1
                else:
                    log.error("** cannot place Order **")
                    log.error(e)
                    orderObj['quantity'] = 0
                    return orderObj
                    break
                log.debug("retrying order %s , %s", orderObj['price'],
                          orderObj['quantity'])

    def get_free_balances_non_usdt(self, worth_threshold=10):
        """ find balances from Binance account which are non USDT 
        and the worth of them in amount meets the threshold """
        account = self.client.get_account()
        tickers = self.tickers()
        balances = [(x['asset'], float(x['free']), float(x['locked']))
                    for x in account['balances'] if x['asset'] != 'USDT']
        freebalances = []
        for x in balances:
            symbol = x[0] + self.base_token(x[0], tickers)
            if symbol in tickers:
                amomunt = x[1] * tickers[symbol] if 'USDT' in symbol else (
                    x[1] * tickers[symbol]) * tickers['BTCUSDT']
                if (amomunt) > worth_threshold:
                    freebalances.append((symbol, x[1], amomunt))

        return freebalances

    def create_stop_loss_orders(self,
                                test=True,
                                stoploss_prc=3.0,
                                quantity_prc=100,
                                symbols=None):
        """Find all Free Balances and create Stop Loss Orders"""
        free_balances = self.get_free_balances_non_usdt()
        tickers = self.tickers()
        log.debug("free balances: %s", free_balances)
        log.debug("required orders: %s", symbols)
        res = []
        for x in free_balances:
            if symbols and x[0] not in symbols:
                continue

            log.info('-- creating order for %s--', x[0])
            sym = x[0]
            action = 'SELL'
            sprice = (100 - stoploss_prc) / 100.0 * tickers[sym]
            price = (100 - stoploss_prc - 0.1) / 100.0 * tickers[sym]
            qty = x[1]
            orderObj = {
                'symbol': sym,
                'quantity': float(qty) * quantity_prc / 100,
                'side': action,
                'type': 'STOP_LOSS_LIMIT',
                'stopPrice': sprice,
                'price': price,
                'timeInForce': 'GTC'
            }
            curr_res = self.place_order(orderObj, test=test)
            res.append(curr_res)
        return res

    def send_msg(self, msg, chat_id, reply_message_id=None):
        """ 
        Sends message to Telegram group
        Emojis:
        https://apps.timwhitlock.info/emoji/tables/unicode

        """
        log.debug("sending message %s", msg)
        url = f"https://api.telegram.org/bot{self.telegramKey}/sendMessage?chat_id={chat_id}&parse_mode=Markdown&text="

        url += urllib.parse.quote('\n' + msg)
        if reply_message_id:
            url += f'&reply_to_message_id={reply_message_id}'
        with urllib.request.urlopen(url) as response:
            pass

    def send_photo(self, filename, chat_id, caption=""):
        url = f"https://api.telegram.org/bot{self.telegramKey}/sendPhoto?chat_id={chat_id}&caption={urllib.parse.quote(caption)}"
        response = requests.post(url, files={'photo': open(filename, 'rb')})

    def notify_action(self, chat_id):
        """
        Sends message to Telegram group
        """
        url = f"https://api.telegram.org/bot{self.telegramKey}/sendChatAction?chat_id={chat_id}&action=typing"
        # https://apps.timwhitlock.info/emoji/tables/unicode
        with urllib.request.urlopen(url) as response:
            pass

    def get_messages(self, offset=0, timeout=10):
        """ 
        Get notifications from Telegram after the provided offset   
        @return: Tuple( offset, author, chat_id, text, date )
        """
        urlString = f"https://api.telegram.org/bot{self.telegramKey}/getUpdates?offset={offset}&timeout={timeout}"
        try:
            with urllib.request.urlopen(urlString) as response:
                jsonObj = json.loads(response.read().decode('utf-8'))
                response = []
                for x in jsonObj['result']:
                    log.debug("From Telegram: message: %s", x)
                    if 'message' in x:
                        message = DotMap(x['message'])
                        message.update_id = x['update_id']
                        if message.entities:
                            del message.entities
                        if message['from'].first_name:
                            del message['from'].first_name
                        if message['from'].last_name:
                            del message['from'].last_name
                        if message['chat'].first_name:
                            del message['chat'].first_name
                        if message['chat'].last_name:
                            del message['chat'].last_name
                        log.info("Message from Telegram: %s", message.toDict())
                        response.append(message)
                    else:
                        response.append(DotMap({'update_id': x['update_id']}))

                    if 'callback_query' in x:
                        log.info(json.dumps(x))

                return response
        except Exception as e:
            log.error(e)
            return []

    @staticmethod
    def timestamp_to_str(ts: float, fmt: str = '%Y-%m-%d %H:%M:%S'):
        return datetime.datetime.fromtimestamp(ts).strftime(fmt)

    def to_amount(self, symbol, balance, tickers):
        if symbol == 'USDT':
            return self.floor(balance, 2)
        elif symbol == 'BTC':
            return self.floor(balance * tickers["BTCUSDT"], 2)
        elif symbol + 'BTC' in tickers:
            return self.floor(
                balance * tickers[symbol + 'BTC'] * tickers["BTCUSDT"], 2)
        else:
            return 0.0

    def get_account_balances(self):
        """
        Tuple( Symbol, Free , Locked, Free+Locked )
        """
        account = self.client.get_account()
        balances = ((x['asset'], float(x['free']), float(x['locked']),
                     float(x['free']) + float(x['locked']))
                    for x in account['balances'])
        return balances

    def get_snapshot(self):
        balances = self.get_account_balances()
        tickers = self.tickers()
        snapshot_raw = {x[0]: x[3] for x in balances if (x[3]) > 0}
        snapshot = {
            x: self.floor(snapshot_raw[x], 4)
            for x in snapshot_raw
            if self.to_amount(x, snapshot_raw[x], tickers) > 1.0
        }

        return snapshot

    def snapshot_total(self):
        tickers = self.tickers()
        snapshot = db.config(Const.SNAPSHOT, {})
        amounts = list(
            (x, self.to_amount(x, snapshot[x], tickers)) for x in snapshot)
        amounts_str = [f'{x[0]} -> {x[1]}' for x in amounts]
        total = sum(x[1] for x in amounts)
        amounts_str.append("." * 15)
        amounts_str.append(f'Snapshot: {self.floor(total)}')
        msg = "\n".join(amounts_str)
        return msg

    def account_total(self):
        """
        Returns amounts and balances of the Symbols
        :return: Tuple( List(str, float),  List(str, float))
        """
        tickers = {
            x['symbol']: float(x['price'])
            for x in self.client.get_all_tickers()
        }

        balances = self.get_account_balances()
        aggr_balances: Dict(
            str, float) = {x[0]: x[3]
                           for x in balances if (x[3]) > 0}
        amounts: List[Tuple[str, float]] = list(
            filter(lambda x: x[1] > 0,
                   ((x, self.to_amount(x, aggr_balances[x], tickers))
                    for x in aggr_balances)))
        return amounts, aggr_balances

    def price_alert(self, symbol, timeframe, count, threshold):
        # log.debug("getting price alert for : %s %s x %s", symbol, timeframe, count)
        # print(f'Symbol: {symbol}, timeframe: {timeframe} , count: {count}, threshold: {threshold}')
        klines = self.klines(symbol, timeframe, count)
        # Max and Min price in the klines
        # time, Open,High,Low,Close
        max_kline = max([(int(x[0]), float(x[2])) for x in klines],
                        key=lambda y: y[1])
        min_kline = min([(int(x[0]), float(x[3])) for x in klines],
                        key=lambda y: y[1])

        first_price = second_price = None
        if max_kline[0] < min_kline[0]:
            first_price = max_kline[1]
            second_price = min_kline[1]
        else:
            first_price = min_kline[1]
            second_price = max_kline[1]

        log.debug("Price Alert: %s, %s ->  %s", symbol, first_price,
                  second_price)

        first_price = self.floor(first_price, 8)
        second_price = self.floor(second_price, 8)
        percentage = math.fabs(
            self.floor((first_price - second_price) * 100 / first_price, 1))
        current = self.floor(klines[-1][4], 8)

        key = f"{symbol}{timeframe}{count}{threshold}"
        curr_value = f"{first_price}{second_price}"
        existing_value = self.data[key] if key in self.data else ""

        if percentage > threshold:
            self.data[key] = curr_value
            if curr_value != existing_value:
                direction = '⬆' if first_price < second_price else '⬇'
                return f"{direction} {symbol:10} {percentage}% ({first_price}, {second_price}, {current}) "

    def stop_loss_orders_percentage(self):
        """Tuple(orderId, Symbol, StopPrice, CurrentPriceUSDT, Quantity, PercentageDiff)"""
        tickers = self.tickers()
        slOrders = self.get_open_orders(type='STOP_LOSS_LIMIT')

        stats = []
        for x in slOrders:
            # log.debug(x)
            currPrice = tickers[x['symbol']]
            stopPrice = float(x['price'])
            stopPriceUSDT = float(
                x['price']) * tickers['BTCUSDT'] if x['symbol'].endswith(
                    'BTC') else float(x['price'])
            qty = float(x['origQty'])
            prcGap = (currPrice - stopPrice) * 100 / currPrice
            currPriceUSDT = currPrice * tickers['BTCUSDT'] if 'BTC' in x[
                'symbol'] else currPrice
            stats.append(
                (x['orderId'], x['symbol'], stopPriceUSDT, currPriceUSDT, qty,
                 round(prcGap, 2)))
        return stats

    def symbol_with_currency(self, symbol):
        return symbol.upper() + self.base_token(symbol.upper())

    def base_token(self, asset, token=None):
        cached_token = token if token else self.tickers()
        return 'USDT' if asset + 'USDT' in cached_token else 'BTC'

    def sell_x_percent(self,
                       symbol='ALL',
                       qty_prc=None,
                       price_prc_over_market=0,
                       test=True):
        if not qty_prc:
            raise Exception("X needs to be defined")
        if price_prc_over_market < 0:
            raise Exception("NEGATIVE VALUES NOT ALLOWED")

        tickers = self.tickers()
        balances = [(x[0], x[1]) for x in self.get_account_balances()
                    if self.to_amount(x[0], x[1], tickers) > 100]
        sell_orders = [
            (x[0] + self.base_token(x[0], tickers), x[1] * qty_prc / 100,
             tickers[x[0] + self.base_token(x[0], tickers)]) for x in balances
            if x[0] != 'USDT' and symbol == 'ALL' or x[0] in symbol
        ]
        log.debug(sell_orders)
        response = []
        for x in sell_orders:
            if float(x[2]) < 1 / (10**4):
                log.warn('Ignoring a very low priced asset sell Order: %s %s',
                         x[0], x[2])
                # continue
            orderObj = {
                'symbol': x[0],
                'quantity': x[1],
                'side': 'SELL',
                'type': 'LIMIT',
                'price': x[2] * (100 - 0.005 + price_prc_over_market) / 100,
                'timeInForce': 'GTC'
            }
            currRes = self.place_order(orderObj, test=test)

            if 'BTC' in x[0]:
                # Convert to usdt price
                currRes['price'] = float(currRes['price']) * tickers['BTCUSDT']

            response.append(currRes)
        return response

    def buy_x_prc(self, symbol, prc_usdt, prc_belowmarket, test=True):
        if not prc_usdt:
            raise Exception("Percentage of USDT amount not defined")
        if prc_belowmarket < 0:
            raise Exception("Negative values not allowed")
        if not symbol:
            raise Exception("Symbol not defined")

        balances = list(
            filter(lambda x: x[0] == 'USDT', self.get_account_balances()))
        log.debug("USDT Balances: %s", balances)
        free_balance = balances[0][1] if len(balances) > 0 else 0

        buyAmount = min(prc_usdt * free_balance / 100, free_balance)

        tickers = self.tickers()
        eff_symbol = self.symbol_with_currency(symbol)
        qty = buyAmount / tickers[eff_symbol]
        buyPrice = tickers[eff_symbol] * (100 + 0.005 - prc_belowmarket) / 100
        orderObj = {
            'symbol': eff_symbol,
            'quantity': qty,
            'side': 'BUY',
            'type': 'LIMIT',
            'price': buyPrice,
            'timeInForce': 'GTC'
        }
        currRes = self.place_order(orderObj, test=test)
        return currRes

    def get_open_orders(self, type=None, side=None):
        openOrders = self.client.get_open_orders(recvWindow=5000)
        # log.debug("open orders: %s", openOrders)
        if type:
            return list(filter(lambda x: x['type'] == type, openOrders))
        if side:
            return list(filter(lambda x: x['side'] == side, openOrders))

    def cancel_all_sl_orders(self, symbol='ALL'):
        stats = self.stop_loss_orders_percentage()
        # Cancel All Orders will high stop loss percentage
        cancelled = []
        for x in stats:
            if symbol == 'ALL' or symbol in x[1]:
                log.debug("Cancelling SL order for : %s", x[1])
                self.client.cancel_order(symbol=x[1], orderId=str(x[0]))
                cancelled.append(x)
        # send the stats for cancelled orders
        return cancelled

    def revise_sl(self, symbol='ALL', threshold=4):
        stats = self.stop_loss_orders_percentage()
        log.info(f"Trying to revise Stop Loss for {threshold}%")

        # Cancel All Orders will high stop loss percentage
        cancelled_sl_symbols = []
        for x in filter(
                lambda x:
            (x[5] >
             (threshold + 0.25) and symbol == 'ALL' or symbol in x[1]), stats):
            self.client.cancel_order(symbol=x[1], orderId=str(x[0]))
            cancelled_sl_symbols.append(x[1])

        if len(cancelled_sl_symbols) > 0:
            log.info("trying to revise %s", cancelled_sl_symbols)
            resp = self.create_stop_loss_orders(test=False,
                                                stoploss_prc=threshold,
                                                symbols=cancelled_sl_symbols)
            return resp
        else:
            return []

    @staticmethod
    def dataframe(klines):
        klines_2d_array = [
            list(map(lambda x: float(x), x[1:6])) for x in klines
        ]
        index_data = [app.timestamp_to_str(float(x[0]) / 1000) for x in klines]
        df = pd.DataFrame(np.array(klines_2d_array),
                          index=index_data,
                          columns=['open', 'high', 'low', 'close', 'volume'])
        df.index = pd.to_datetime(df.index)
        return df
class Binance(object):
    def __init__(self, api_key, secret_key):
        self.client = Client(api_key, secret_key)
        self.client.API_URL = 'https://testnet.binance.vision/api'  #only for testing

    # ---- Account Info Commands --- #
    def get_account(self):
        ''' Returns account info '''
        recvWindow = {'recvWindow': 59999}
        account_info = self.client.get_account(**recvWindow)
        return account_info

    # ---- ---- #

    # ---- Order Commands ---- #
    def send_order(self, order_type, message):
        raw_message = message.text
        if order_type == "limit":
            order_params = limit_order_message_filter(raw_message)
            order_response = self.limit_order(order_params[0], order_params[1],
                                              order_params[2], order_params[3],
                                              order_params[4], order_params[5])
            order_confirmation = order_message(
                order_response)  #Telegram message sent to user
        elif order_type == "market":
            order_params = market_order_message_filter(raw_message)
            order_response = self.market_order(order_params[0],
                                               order_params[1],
                                               order_params[2],
                                               order_params[3])
            order_confirmation = order_message(
                order_response)  #Telegram message sent to user
        elif order_type == "stoploss":
            order_params = stoploss_order_message_filter(raw_message)
            order_response = self.stoploss_order(
                order_params[0], order_params[1], order_params[2],
                order_params[3], order_params[4], order_params[5],
                order_params[6])
            order_confirmation = stopLoss_message(
                order_response)  #Telegram message sent to user
        print(order_response)
        return order_confirmation

    def market_order(self, symbol, side, order_type, quantity):
        ''' Executes market orders/Depending on type of order commands '''
        try:
            order_dictionary = {
                'symbol': symbol,
                'side': side,
                'type': order_type,
                'quantity': quantity,
                'recvWindow': 59999
            }
            order = self.client.create_order(**order_dictionary)
        except Exception as e:
            print("something went wrong - {}".format(e))
            #bot.error_message(symbol, quantity, str(e))
            return False

        return order

    def limit_order(self, symbol, side, order_type, timeInForce, quantity,
                    price):
        ''' Executes limit orders/Depending on type of order commands '''
        try:
            order_dictionary = {
                'symbol': symbol,
                'side': side,
                'type': order_type,
                'timeInForce': timeInForce,
                'quantity': quantity,
                'price': price,
                'recvWindow': 59999
            }
            order = self.client.create_order(**order_dictionary)
        except Exception as e:
            print("something went wrong - {}".format(e))
            #bot.error_message(symbol, quantity, str(e))
            return False

        return order

    def stoploss_order(self, symbol, side, order_type, timeInForce, quantity,
                       price, stopPrice):
        try:
            order_dictionary = {
                'symbol': symbol,
                'side': side,
                'type': order_type,
                'timeInForce': timeInForce,
                'quantity': quantity,
                'price': price,
                'stopPrice': stopPrice,
                'recvWindow': 59999
            }
            order = self.client.create_order(**order_dictionary)
        except Exception as e:
            print("something went wrong - {}".format(e))
            #bot.error_message(symbol, quantity, str(e))
            return False

        return order

    # ---- ---- #

    # ---- Check Orders Commands --- #
    def see_all_orders(self, symbol):
        try:
            dict = {'symbol': symbol, 'recvWindow': 59999}
            all_orders = self.client.get_all_orders(**dict)
            return all_orders
        except Exception as e:
            print(str(e))
            return 'Whoops'

    def open_orders(self, symbol):
        try:
            dict = {'symbol': symbol, 'recvWindow': 59999}
            open_orders = self.client.get_open_orders(**dict)
            print(open_orders)
            return open_orders
        except Exception as e:
            print(str(e))
            return 'Whoops'

    # ---- ---- #

    # ---- Cancel Orders ---- #
    def cancel_order(self, message):
        try:
            cancel_order_params = cancel_order_message_filter(message)
            dict = {
                'symbol': cancel_order_params[0],
                'orderId': cancel_order_params[1],
                "recvWindow": 59999
            }
            response = self.client.cancel_order(**dict)
            print(response)
            return response
        except Exception as e:
            print(str(e))
Example #11
0
class BinanceOrders:

    # Initialization with the API key and secret key
    def __init__(self, key, secret):
        self.client = Client(api_key=key, api_secret=secret)
        self.assets = self.get_assets()
        self.orders = self.get_orders()
        self.get_data()

# Get all the assets with positive balance

    def get_assets(self):
        info = self.client.get_account()
        assets_list = info['balances']
        my_assets = []
        for asset in assets_list:
            if float(asset['free']) > 0:
                my_assets.append(asset['asset'])
        return my_assets

# Get all the orders that were made

    def get_orders(self):
        orders = []
        for i in self.assets:
            if i != 'USDT':
                orders_list = self.client.get_all_orders(symbol=i + 'USDT')
                for order in orders_list:
                    orders.append(order)
        return orders


# Create a customized DataFrame with required information, only for orders that were filled

    def get_data(self):
        DF = pd.DataFrame(columns=[
            'Date', 'Asset', 'Type', 'Price', 'Amount', 'Fee', 'Total'
        ])
        for x in range(len(self.orders)):
            if self.orders[x]['status'] == 'FILLED':
                Date = datetime.fromtimestamp(
                    self.orders[x]['time'] / 1000.0).strftime('%d.%m.%Y %H:%M')
                Asset = str(self.orders[x]['symbol']).replace('USDT', '')
                Type = self.orders[x]['side']
                Price = round(
                    float(self.orders[x]['cummulativeQuoteQty']) /
                    float(self.orders[x]['executedQty']), 2)
                Amount = round(float(self.orders[x]['executedQty']), 3)
                Total = round(float(self.orders[x]['cummulativeQuoteQty']), 3)
                if self.orders[x]['time'] < 1613816037557:
                    Fee = Price * 0.001
                else:
                    Fee = Price * 0.00075
                Fee = round(Fee, 3)

                DF = DF.append(pd.Series(
                    [Date, Asset, Type, Price, Amount, Fee, Total],
                    index=DF.columns),
                               ignore_index=True)

        DF.to_csv('*PATH TO WHERE YOU WANT TO SAVE THE FILE*', index=False)
Example #12
0
from binance.client import Client

print('Running bot')

# Fetching data from config file:
config = parser.Parser()
api_key = config.api_key
secret = config.secret

client = Client(api_key, secret)

# get market depth
depth = client.get_order_book(symbol='BNBBTC')

client.verbose = True
balance = client.get_account()
print(balance)

# place a test market buy order, to place an actual order use the create_order function
order = client.create_test_order(symbol='BNBBTC',
                                 side=Client.SIDE_BUY,
                                 type=Client.ORDER_TYPE_MARKET,
                                 quantity=100)

# get all symbol prices
prices = client.get_all_tickers()

# withdraw 100 ETH
# check docs for assumptions around withdrawals
from binance.exceptions import BinanceAPIException, BinanceWithdrawException
try:
Example #13
0
class OrderClient(Thread):

    min_qnty = 0.000001
    _latest_price = {'symbol': None, 'price': 0, 'timestamp': 0}  #

    def __init__(self, params):
        Thread.__init__(self)
        self.name = params['name']
        self.uuid = params['uuid']
        self.new_side = params['side']
        self.SYMBOL = params['symbol']
        self.rest_api = Client(params['API_KEY'], params['API_SECRET'])
        self.keep_running = True

    def run(self):

        trade_count = 0
        #removed check permission, because it calls same API as get balance, it will be checked during that call

        while self.keep_running:
            try:

                if self.new_side == "BUY":  #lets find out is it is BTC or USDT we need to get balance of
                    asset = self.SYMBOL[3:]
                elif self.new_side == "SELL":
                    asset = self.SYMBOL[:3]
                else:
                    raise FatalError('side not set, either BUY or SELL')

                asset_balance = self.get_account_balance(asset)
                logger.debug(f"{self.name} fetched balance, {asset_balance}")
                #removed call to min quantity, call might be necessary later when multiple pairs are added.
                min_qnty = self.min_qnty
                if self.new_side == "BUY":
                    min_qnty = self.min_qnty * self.latest_price

                if asset_balance * 0.999 < min_qnty:
                    #this is naturally where we end our trade, report that trade is complete.
                    logger.info(
                        f"{self.name} trade has completed successfully")
                    break

                if self.new_side == "BUY":
                    amount = (asset_balance /
                              self.latest_price) * 0.995**(trade_count + 1)
                    logger.debug(f"[+]{self.name} Buying {amount}")
                    self.do_buy(amount)

                elif self.new_side == "SELL":
                    amount = asset_balance * (0.998**(trade_count + 1))
                    logger.debug(f"[+]{self.name} Selling {amount}")
                    self.do_sell(amount)

            except FatalError as e:
                logger.error(f"{self.name} fatal, {e.message}")
                break
            except ZeroDepositError as e:
                logger.error(f"{self.name}, {e.message}")
                break
            except BinanceAPIException as e:
                if int(e.code) in [-2010, -1010, -2011]:
                    logger.info(f"{self.name}, {e.message}")
                elif int(e.code) == -1013:
                    #balance below minimum allowable, should get here if we checking balances, end trade
                    logger.info(
                        f"{self.name} cannot {self.new_side} less than minimum, {e.message}"
                    )
                    break
                elif int(e.code) == -2015:
                    logger.error(f"{self.name} {e.message}, exiting")
                    break
                elif int(e.status_code) == 429:
                    logger.warning(
                        f"{self.name} hit a rate limit, backing dowm for 1 minute"
                    )
                    time.sleep(60)
                elif int(e.status_code) == 418:
                    logger.error(f"{self.name} Ooops, IP has been auto banned")
                    break
                else:
                    logger.error(
                        f"{self.name} uncaught API exception, {e.message}, {e.code}, {e.status_code}"
                    )

            except Exception as e:  #default, for all uncaught exceptions.
                logger.error(f"{self.name} Exceptiion, {e}")
                break

            if trade_count > 9:
                logger.info(
                    f"{self.name} Exhausted the number of loops, which is {trade_count}, exiting"
                )
                break

            trade_count += 1

    def get_account_balance(self, asset):
        try:
            account_info = self.rest_api.get_account()
        except Exception as e:
            raise e
        if not account_info['canTrade']:
            logger.error(f"{self.name} cannot trade")
            raise PermissionsError()
        balance_list = [
            x for x in account_info['balances'] if x['asset'] == asset
        ]
        if not balance_list:
            raise ZeroDepositError()

        balance = balance_list[0]
        return float(balance['free'])

    @property
    def latest_price(self):
        if self._latest_price['symbol'] == self.SYMBOL and self._latest_price[
                'timestamp'] - time.time() < 5:
            if self._latest_price['price']:
                return self._latest_price['price']
        price_dict = self.rest_api.get_symbol_ticker(symbol=self.SYMBOL)
        self._latest_price = {
            'symbol': self.SYMBOL,
            'timestamp': time.time(),
            'price': float(price_dict['price'])
        }
        return self._latest_price['price']

    def do_buy(self, amount):
        logger.info(f"[*]{self.name} Buying {amount}")
        order = self.rest_api.order_market_buy(
            symbol=self.SYMBOL, quantity="{0:8f}".format(amount))

        logger.info("[+]{} Order successful, ID: {}".format(
            self.name, order['orderId']))
        return {
            'status': True,
            'exception': None,
            'message': 'Order successful',
            'orderID': order['orderId']
        }

    def do_sell(self, amount):
        logger.info(f"[*]{self.name} Selling {amount}")
        order = self.rest_api.order_market_sell(
            symbol=self.SYMBOL, quantity="{0:8f}".format(amount))

        logger.info("[+]{} Order successful, ID: {}".format(
            self.name, order['orderId']))
        return {
            'status': True,
            'exception': None,
            'message': 'Order successful',
            'orderID': order['orderId']
        }
Example #14
0
class BinanceExchage(Exchange):
    def __init__(self, apiKey, apiSecret, pairs):
        super().__init__(apiKey, apiSecret, pairs)
        self.exchange_name = "Binance"
        self.connection = Client(self.api['key'], self.api['secret'])
        self.update_balance()
        self.socket = BinanceSocketManager(self.connection)
        self.socket.start_user_socket(self.on_balance_update)
        self.socket.start()

    def update_balance(self):
        account_information = self.connection.get_account()
        self.set_balance(account_information['balances'])

    def set_balance(self, balances):
        symbols = self.get_trading_symbols()
        actual_balance = list(
            filter(lambda elem: str(elem['asset']) in symbols, balances))
        self.balance = actual_balance

    def on_balance_update(self, upd_balance_ev):
        if upd_balance_ev['e'] == 'outboundAccountInfo':
            balance = []
            for ev in upd_balance_ev['B']:
                balance.append({
                    'asset': ev['a'],
                    'free': ev['f'],
                    'locked': ev['l']
                })
            self.set_balance(balance)

    def get_balance(self):
        return self.balance

    def get_open_orders(self):
        return self.connection.get_open_orders()

    def cancel_order(self, symbol, orderId):
        self.connection.cancel_order(symbol=symbol, orderId=orderId)
        print('order canceled')

    def stop(self):
        self.socket.close()

    def _cancel_order_detector(self, event):
        # detect order id which need to be canceled
        slave_open_orders = self.connection.get_open_orders()
        for ordr_open in slave_open_orders:
            if ordr_open['price'] == event['p']:
                return ordr_open['orderId']

    async def on_order_handler(self, event):
        # shortcut mean https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#order-update

        if event['x'] == 'CANCELED':
            slave_order_id = self._cancel_order_detector(event)
            self.cancel_order(event['s'], slave_order_id)
        else:
            self.create_order(event['s'], event['S'], event['o'], event['p'],
                              event['q'], event['f'], event['P'])

    def create_order(self,
                     symbol,
                     side,
                     type,
                     price,
                     quantityPart,
                     timeInForce,
                     stopPrice=0):
        """
        :param symbol:
        :param side:
        :param type: LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER
        :param price: required if limit order
        :param quantityPart: the part that becomes an order from the entire balance
        :param timeInForce: required if limit order
        :param stopPrice: required if type == STOP_LOSS or TAKE_PROFIT
        """
        # # if order[side] == sell don't need calculate quantity
        # if side == 'BUY':
        #     quantity = self.calc_quatity_from_part(symbol, quantityPart, price)
        # else:
        #     quantity = quantityPart

        quantity = self.calc_quantity_from_part(symbol, quantityPart, price,
                                                side)
        print('Slave ' + str(self.get_balance_market_by_symbol(symbol)) + ' ' +
              str(self.get_balance_coin_by_symbol(symbol)) +
              ', Create Order:' + ' amount: ' + str(quantity) + ', price: ' +
              str(price))
        try:
            if (type == 'STOP_LOSS_LIMIT' or type == "TAKE_PROFIT_LIMIT"):
                self.connection.create_order(symbol=symbol,
                                             side=side,
                                             type=type,
                                             price=price,
                                             quantity=quantity,
                                             timeInForce=timeInForce,
                                             stopPrice=stopPrice)
            if (type == 'MARKET'):
                self.connection.create_order(symbol=symbol,
                                             side=side,
                                             type=type,
                                             quantity=quantity)
            else:
                self.connection.create_order(symbol=symbol,
                                             side=side,
                                             type=type,
                                             quantity=quantity,
                                             price=price,
                                             timeInForce=timeInForce)
            print("order created")
        except Exception as e:
            print(str(e))
Example #15
0
class ArbitrageStratety:
    # 手续费率
    huobi_fee_rate = 0.0012
    binance_fee_rate = 0.0005
    bnb_price = 10.8159
    # 盈利率
    huobi_profit_rate = 0.0003
    binance_profit_rate = 0.0003
    # btc每次最大交易量
    btc_exchange_min = 0.001
    usdt_exchange_min = 10
    # 程序里有3处需要同时更改
    btc_exchange_max = 0.065
    # HUOBI API最大连续超时次数
    huobi_max_timeout = 3
    # 深度参数阈值
    STD_THD = 5
    SUM_THD = 0.5

    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.output = open('history', 'a+')
        self.huobiSpot = HuobiSpot(key_dict['HUOBI_ACCESS_KEY2'], key_dict['HUOBI_SECRET_KEY2'])
        self.binanceClient = BinanceSpot(key_dict['BINANCE_ACCESS_KEY'], key_dict['BINANCE_SECRET_KEY'])

        self.btc_mat = "BTC :\tfree:{:<20.8f}locked:{:<20.8f}"
        self.usdt_mat = "USDT:\tfree:{:<20.8f}locked:{:<20.8f}"
        self.total_format = "BTC:{:<20.8f}USDT:{:<20.8f}"

        # config logging
        self.logger = logging.getLogger("Robot")

        # 指定logger输出格式
        formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.handlers.TimedRotatingFileHandler('log', when='midnight')
        # 设置日志文件后缀,以当前时间作为日志文件后缀名。
        file_handler.suffix = "%Y-%m-%d"
        # 可以通过setFormatter指定输出格式
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter  # 也可以直接给formatter赋值

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 用于记录huobi连续响应过长时间的次数,超过两次,就退出
        self.huobi_timeout = 0

        # 收益统计
        self.huobi_usdt_inc = 0
        self.huobi_usdt_dec = 0
        self.binance_usdt_inc = 0
        self.binance_usdt_dec = 0

        self.huobi_usdt_total_change = 0
        self.binance_usdt_total_change = 0

        # 账户余额
        self.huobi_trade_btc = 0
        self.huobi_trade_usdt = 0
        self.binance_trade_btc = 0
        self.binance_trade_usdt = 0

        # 成交量统计
        self.usdt_exchange_amount = 0
        self.btc_exchange_amount = 0

        self.last_deal_time = 0

        # 由于数量小于0.001而未能成交
        # >0 表示需要卖出的
        self.untreated_btc = 0

    @staticmethod
    def sms_notify(msg):
        url = 'http://221.228.17.88:8080/sendmsg/send'
        params = {
            'phonenum': '18118999630',
            'msg': msg
        }
        requests.get(url, params=params)

    def update_profit_rate(self):
        self.logger.info('更新盈利率')
        huobi_btc_percent = float('%.2f' % (self.huobi_trade_btc / (self.huobi_trade_btc + self.binance_trade_btc)))
        binance_btc_percent = 1 - huobi_btc_percent

        self.logger.info('Huobi: %s\tBinance:%s' % (huobi_btc_percent, binance_btc_percent))
        if huobi_btc_percent < 0.1:
            self.huobi_profit_rate = 0.001
        elif huobi_btc_percent < 0.2:
            self.huobi_profit_rate = 0.0007
        elif huobi_btc_percent > 0.9:
            self.binance_profit_rate = 0.001
        elif huobi_btc_percent > 0.8:
            self.binance_profit_rate = 0.0007
        else:
            self.huobi_profit_rate = 0.0003
            self.binance_profit_rate = 0.0003
        self.logger.info('huobi_profit_rate: %s\t, binance_profit_rate: %s' % (
            self.huobi_profit_rate, self.binance_profit_rate))

    def update_account_info(self):
        # update binance
        self.logger.info('|--------------------------------------------------')
        self.logger.info('|' + '更新账户信息')
        try:
            account_info = self.binanceClient.get_account()
        except Exception as e:
            self.logger.error('Binance get_account error: %s' % e)
        else:
            freezed_btc = 0
            freezed_usdt = 0
            for info in account_info['balances']:
                if info['asset'] == 'BTC':
                    self.binance_trade_btc = float(info['free'])
                    freezed_btc = float(info['locked'])
                elif info['asset'] == 'USDT':
                    self.binance_trade_usdt = float(info['free'])
                    freezed_usdt = float(info['locked'])
            self.logger.info('|' + 'Binance:')
            self.logger.info('|' + self.btc_mat.format(self.binance_trade_btc, freezed_btc))
            self.logger.info('|' + self.usdt_mat.format(self.binance_trade_usdt, freezed_usdt))

            # update huobi
            # 修复了float进位的问题
            json_r = self.huobiSpot.get_balance()
            if json_r['status'] == 'ok':
                for item in json_r['data']['list']:
                    if item['currency'] == 'btc' and item['type'] == 'trade':
                        self.huobi_trade_btc = ArbitrageStratety.cut2_float(item['balance'], 8)
                        # self.huobi_trade_btc = float(item['balance'])
                    elif item['currency'] == 'btc' and item['type'] == 'frozen':
                        freezed_btc = float(item['balance'])
                    elif item['currency'] == 'usdt' and item['type'] == 'trade':
                        self.huobi_trade_usdt = ArbitrageStratety.cut2_float(item['balance'], 8)
                    elif item['currency'] == 'usdt' and item['type'] == 'frozen':
                        freezed_usdt = float(item['balance'])
                self.logger.info('|' + 'Huobi:')
                self.logger.info('|' + self.btc_mat.format(self.huobi_trade_btc, freezed_btc))
                self.logger.info('|' + self.usdt_mat.format(self.huobi_trade_usdt, freezed_usdt))

                self.logger.info('|' + 'Total:')
                self.logger.info('|' + self.total_format.format(self.binance_trade_btc + self.huobi_trade_btc,
                                                                self.binance_trade_usdt + self.huobi_trade_usdt))
                self.logger.info('|' + 'Untreated: %s' % self.untreated_btc)
                self.update_profit_rate()
            else:
                print json_r
                if 'fail' == json_r['status']:
                    self.logger.error('Huobi get_balance error: %s' % json_r['msg'])
                else:
                    self.logger.error('Huobi get_balance error: %s' % json_r['err-msg'])
            self.logger.info('|--------------------------------------------------')

    @staticmethod
    def merge_depth(depth):
        new_depth = []
        for d in depth:
            price = ArbitrageStratety.cut2_float(d[0], 1)
            # price = float(re.match('(\d+\.\d)\d*', '{:.8f}'.format(float(d[0]))).group(1))
            amount = float(d[1])
            if len(new_depth) == 0:
                new_depth.append([price, amount])
            else:
                if new_depth[-1][0] == price:
                    new_depth[-1] = [price, new_depth[-1][1] + amount]
                else:
                    new_depth.append([price, amount])
        return new_depth[:5]

    @staticmethod
    def cut2_float(s, n):
        if isinstance(s, float):
            s = '{:.8f}'.format(s)
        pattern = re.compile(r'(\d+\.\d{1,%d})\d*' % n)
        return float(pattern.match(s).group(1))

    def go(self):
        while True:
            # get depth info
            print '获取深度信息'
            h_depth = self.huobiSpot.get_depth('btcusdt', 'step5')
            if h_depth['status'] == 'ok':
                h_bids = h_depth['tick']['bids']
                h_asks = h_depth['tick']['asks']
            else:
                time.sleep(3)
                continue

            try:
                b_depth = self.binanceClient.get_order_book(symbol='BTCUSDT')
            except Exception as e:
                self.logger.error(u'获取Binance市场深度错误: %s' % e)
                time.sleep(3)
                continue
            # print b_depth
            # 需要合并深度
            b_bids = ArbitrageStratety.merge_depth(b_depth['bids'])
            b_asks = ArbitrageStratety.merge_depth(b_depth['asks'])
            print b_asks

            # huobi sell
            if h_bids[0][0] * 1.0 / float(
                    b_asks[0][0]) > 1 + self.huobi_fee_rate + self.binance_fee_rate + self.huobi_profit_rate:
                self.logger.info('binance买入,huobi卖出,')
                # print h_bids
                h_sum = np.sum(h_bids[:3], axis=0)
                h_std = np.std(h_bids[:3], axis=0)
                h_avg = np.mean(h_bids[:3], axis=0)

                a = [(float(i[0]), float(i[1])) for i in b_asks]
                b_sum = np.sum(a[:3], axis=0)
                b_std = np.std(a[:3], axis=0)
                b_avg = np.mean(a[:3], axis=0)
                # print a
                self.logger.info('ASKS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (h_sum[1], h_std[0], h_avg[0]))
                self.logger.info('BIDS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (b_sum[1], b_std[0], b_avg[0]))

                self.logger.info(
                    '卖出价:%s, 买入价:%s, 比例:%s' % (h_bids[0][0], float(b_asks[0][0]), h_bids[0][0] * 1.0 / float(
                        b_asks[0][0])))

                if h_std[0] > self.STD_THD or h_sum[1] < self.SUM_THD:
                    self.logger.info('标准差过大,本单取消')
                    time.sleep(0.1)
                    continue

                btc_amount = float(
                    '%.4f' % min(h_bids[0][1], float(b_asks[0][1]), self.btc_exchange_max))

                # Binance btc-amount 精度是6位,需要截取前4位,不能产生进位
                if btc_amount > float(b_asks[0][1]):
                    # btc_amount = float(re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(float(b_asks[0][1]))).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(b_asks[0][1], 4)

                if btc_amount > self.huobi_trade_btc:
                    # btc_amount = float(
                    #     re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(self.huobi_trade_btc)).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(self.huobi_trade_btc, 4)

                order_price = float(b_asks[0][0]) + 1.1
                usdt_amount = float('%.4f' % (btc_amount * order_price))
                self.logger.info('本次交易量:%s BTC, %s USDT' % (btc_amount, usdt_amount))

                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC交易数量不足: %s, 本单取消' % self.btc_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount < self.usdt_exchange_min:
                    self.logger.info('USDT交易数量不足: %s, 本单取消' % self.usdt_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount > self.binance_trade_usdt - 5:
                    self.logger.info('Binance USDT 数量: %s, 不足:%s, 本单取消' % (self.binance_trade_usdt, usdt_amount))
                    time.sleep(1)
                    continue

                # 限价买
                self.logger.info('开始限价买入')
                try:
                    buy_order = self.binanceClient.order_limit_buy(symbol='BTCUSDT', quantity=btc_amount,
                                                                   price=order_price, newOrderRespType='FULL')
                except Exception as e:
                    self.logger.error(u'Binance买入错误: %s' % e)
                    time.sleep(3)
                    continue
                print buy_order

                buy_order_id = buy_order['orderId']
                self.output.write('\n' + str(buy_order_id))
                self.output.flush()
                self.logger.info('binance buy orderId: %s, state: %s' % (buy_order_id, buy_order['status']))
                field_cash_amount = 0
                field_amount = 0
                if buy_order['status'] == 'NEW' or buy_order['status'] == 'PARTIALLY_FILLED':
                    self.logger.info('撤消未完成委托')
                    try:
                        cancel_r = self.binanceClient.cancel_order(symbol='BTCUSDT', orderId=buy_order_id)
                        print cancel_r
                    # get_my_trades有超时问题,暂时不用此函数

                    except Exception as e:
                        self.logger.error(u'撤销错误: %s' % e)
                    else:
                        self.logger.info('撤销成功')
                    # 有可能会出现撤销成功,但是撤销完成的过程中,又完成了部分委托,需要更新实际成交量
                    self.logger.info('更新成交量')
                    times = 0
                    while times < 10:
                        self.logger.info(u'第%s次查询Binance订单状态' % (times + 1))
                        try:
                            order = self.binanceClient.get_order(symbol='BTCUSDT', orderId=buy_order_id)
                            self.logger.info(u'当前订单状态为: %s', order['status'])
                            # 撤销成功,状态必定为CANCELED, 撤销成功则为FILLED
                            if order['status'] == 'CANCELED' or order['status'] == 'FILLED':
                                field_amount = float(order['executedQty'])
                                price = float(order['price'])
                                field_cash_amount = float('%.8f' % (field_amount * price))
                                break
                        except Exception as e:
                            self.logger.error(u'Binance get order error: %s' % e)
                        times += 1

                    if times == 10:
                        self.logger.info('未知错误,程序终止')
                        break
                # filled
                elif buy_order['status'] == 'FILLED':
                    fills = buy_order['fills']
                    for f in fills:
                        price = float('%.2f' % float(f['price']))
                        qty = float('%.8f' % float(f['qty']))
                        field_amount += qty
                        field_cash_amount += price * qty
                else:
                    self.logger.info('订单状态异常: %s' % buy_order['status'])

                if field_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    float(field_amount), float(field_cash_amount)))

                self.binance_trade_btc += field_amount
                self.binance_trade_usdt -= field_cash_amount

                self.binance_usdt_dec = field_cash_amount

                self.binance_usdt_total_change -= self.binance_usdt_dec

                # 市价卖
                self.logger.info('开始市价卖出')
                btc_amount = float('%.4f' % field_amount)

                # 记录总共损失的BTC数目,达到0.001时候,进行补全
                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC卖出数量低于 %s', self.btc_exchange_min)
                    self.logger.info('本次交易终止')
                    self.untreated_btc += field_amount
                    # self.rollback_binance_order(buy_order_id)
                    time.sleep(3)
                    continue
                # 买入卖出由于精度不同,会存在一定的偏差,这里进行统计调整
                else:
                    self.untreated_btc += field_amount - btc_amount

                sell_order = self.huobiSpot.send_order(btc_amount, 'api', 'btcusdt', 'sell-market')
                if sell_order['status'] != 'ok':
                    if sell_order['status'] == 'fail':
                        self.logger.error('sell failed : %s' % sell_order['msg'])
                    else:
                        self.logger.error('sell failed : %s' % sell_order['err-msg'])

                    self.logger.info('开始回滚')
                    self.rollback_binance_order(buy_order_id)
                    self.logger.info('终止程序')
                    break
                sell_order_id = sell_order['data']

                self.output.write(':' + str(sell_order_id))
                self.output.flush()
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    sell_order_info = self.huobiSpot.order_info(sell_order_id)
                    print sell_order_info
                    if sell_order_info['status'] == 'ok' and sell_order_info['data']['state'] == 'filled':
                        self.logger.info('huobi sell filled, orderId: %s' % sell_order_id)
                        field_cash_amount = sell_order_info['data']['field-cash-amount']
                        field_amount = sell_order_info['data']['field-amount']
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.huobi_timeout += 1
                    if self.huobi_timeout == self.huobi_max_timeout:
                        self.logger.info('连续%s次超时,终止程序' % self.huobi_timeout)
                        break
                    else:
                        self.logger.info('连续%s次超时,继续程序' % self.huobi_timeout)
                        if self.huobi_timeout == 1:
                            time.sleep(60)
                        else:
                            time.sleep(600)
                        self.btc_exchange_max /= 2
                        continue
                else:
                    self.huobi_timeout = 0
                    self.btc_exchange_max = 0.065

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    float(field_amount), float(field_cash_amount)))

                self.huobi_trade_btc -= float(field_amount)
                self.huobi_trade_usdt += float(field_cash_amount)
                # 更新交易量统计
                self.usdt_exchange_amount += float(field_cash_amount)
                self.btc_exchange_amount += float(field_amount)

                self.huobi_usdt_inc = float(field_cash_amount)

                self.huobi_usdt_total_change += self.huobi_usdt_inc

                usdt_inc = self.huobi_usdt_inc - self.binance_usdt_dec
                thistime_earnings = usdt_inc
                thistime_earnings_rate = thistime_earnings * 1.0 / float(field_cash_amount)

                total_usdt_earnings = self.huobi_usdt_total_change + self.binance_usdt_total_change

                self.logger.info('本次交易量: %s BTC, 盈利: %s USDT, 盈利率: %s' % (
                    float(field_amount), thistime_earnings, thistime_earnings_rate))
                self.logger.info(
                    '总BTC成交量: %s, 盈利: %s USDT, 盈利率: %s' % (
                        self.btc_exchange_amount, total_usdt_earnings,
                        total_usdt_earnings * 1.0 / self.usdt_exchange_amount))
                self.logger.info('|--------------------------------------------------')
                self.logger.info('|' + ' BTC:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                    (self.huobi_trade_btc + self.binance_trade_btc), self.huobi_trade_btc, self.binance_trade_btc))
                self.logger.info(
                    '|' + 'USDT:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                        (self.huobi_trade_usdt + self.binance_trade_usdt), self.huobi_trade_usdt,
                        self.binance_trade_usdt))
                self.logger.info('|--------------------------------------------------')
                self.update_profit_rate()

                self.last_deal_time = int(time.time())

            # binance sell
            elif float(b_bids[0][0]) / h_asks[0][
                0] > 1 + self.huobi_fee_rate + self.binance_fee_rate + self.binance_profit_rate:
                self.logger.info('binance 卖出, huobi买入')

                b = [(float(i[0]), float(i[1])) for i in b_bids]
                print b
                b_sum = np.sum(b[:3], axis=0)
                b_std = np.std(b[:3], axis=0)
                b_avg = np.mean(b[:3], axis=0)

                print h_asks
                h_sum = np.sum(h_asks[:3], axis=0)
                h_std = np.std(h_asks[:3], axis=0)
                h_avg = np.mean(h_asks[:3], axis=0)

                self.logger.info('ASKS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (b_sum[1], b_std[0], b_avg[0]))
                self.logger.info('BIDS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (h_sum[1], h_std[0], h_avg[0]))

                self.logger.info(
                    '卖出价:%s, 买入价:%s, 比例:%s' % (float(b_bids[0][0]), h_asks[0][0], float(b_bids[0][0]) / h_asks[0][0]))
                if h_std[0] > self.STD_THD or h_sum[1] < self.SUM_THD:
                    self.logger.info('标准差过大,本单取消')
                    time.sleep(0.1)
                    continue
                order_price = b_bids[0][0] - 1.1

                btc_amount = float('%.4f' % min(float(b_bids[0][1]), h_asks[0][1],
                                                self.btc_exchange_max))
                if btc_amount > float(b_bids[0][1]):
                    # = float(re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(float(b_bids[0][1]))).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(b_bids[0][1], 4)

                if btc_amount > self.binance_trade_btc:
                    # btc_amount = float(
                    #     re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(self.binance_trade_btc)).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(self.binance_trade_btc, 4)

                usdt_amount = float('%.4f' % (btc_amount * h_asks[0][0]))

                self.logger.info('本次交易量:%s BTC, %s USDT' % (btc_amount, usdt_amount))
                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC交易数量不足: %s, 本单取消' % self.btc_exchange_min)
                    time.sleep(1)
                    continue
                if float('%.4f' % (btc_amount * order_price)) < self.usdt_exchange_min:
                    self.logger.info('USDT交易数量不足: %s, 本单取消' % self.usdt_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount > self.huobi_trade_usdt - 5:
                    self.logger.info('Huobi USDT 数量: %s, 不足:%s, 本单取消' % (self.huobi_trade_usdt, usdt_amount))
                    time.sleep(1)
                    continue

                # 限价卖
                self.logger.info('开始限价卖出')
                try:
                    sell_r = self.binanceClient.order_limit_sell(symbol='BTCUSDT', quantity=btc_amount,
                                                                 price=order_price, newOrderRespType='FULL')
                except Exception as e:
                    self.logger.error(u'Binance卖出错误: %s' % e)
                    time.sleep(3)
                    continue
                print sell_r
                sell_order_id = sell_r['orderId']
                self.output.write('\n' + str(sell_order_id))
                self.output.flush()
                self.logger.info('binance sell orderId: %s, state: %s' % (sell_order_id, sell_r['status']))
                field_cash_amount = 0
                field_amount = 0
                if sell_r['status'] == 'NEW' or sell_r['status'] == 'PARTIALLY_FILLED':
                    # 撤销未完成订单
                    self.logger.info('撤消未完成委托')
                    try:
                        cancel_r = self.binanceClient.cancel_order(symbol='BTCUSDT', orderId=sell_order_id)
                        print cancel_r
                    except Exception as e:
                        self.logger.error(u'撤销错误: %s' % e)
                    else:
                        self.logger.info('撤销成功')
                    self.logger.info('更新成交量')
                    times = 0
                    while times < 10:
                        self.logger.info(u'第%s次查询Binance订单状态' % (times + 1))
                        try:
                            order = self.binanceClient.get_order(symbol='BTCUSDT', orderId=sell_order_id)
                            print order
                            self.logger.info(u'当前订单状态为: %s', order['status'])
                            # 撤销成功,状态必定为CANCELED, 撤销成功则为FILLED
                            if order['status'] == 'CANCELED' or order['status'] == 'FILLED':
                                field_amount = float(order['executedQty'])
                                price = float(order['price'])
                                field_cash_amount = float('%.8f' % (field_amount * price))
                                break
                        except Exception as e:
                            self.logger.error(u'Binance get order error: %s' % e)
                        times += 1

                    if times == 10:
                        self.logger.info('未知错误,程序终止')
                        break
                # filled
                elif sell_r['status'] == 'FILLED':
                    fills = sell_r['fills']
                    for f in fills:
                        price = float('%.2f' % float(f['price']))
                        qty = float('%.8f' % float(f['qty']))
                        field_amount += qty
                        field_cash_amount += price * qty
                else:
                    self.logger.info('订单状态异常: %s' % sell_r['status'])

                if field_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                # 更新统计数据
                self.btc_exchange_amount += float(field_amount)
                self.usdt_exchange_amount += float(field_cash_amount)

                # update income
                self.binance_usdt_inc = field_cash_amount

                self.binance_usdt_total_change += self.binance_usdt_inc

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    field_amount, field_cash_amount))
                self.binance_trade_btc -= field_amount
                self.binance_trade_usdt += field_cash_amount

                # 限价买,使买价高于市价,最后会已市价成交
                self.logger.info('开始伪市价(限价)买入')
                buy_price = h_asks[0][0] + 20
                btc_amount = float('%.4f' % field_amount)

                if btc_amount < self.btc_exchange_min:
                    self.logger.error('BTC交易数量低于 %s' % self.btc_exchange_min)
                    self.logger.info('本次交易终止,开始回滚')
                    self.untreated_btc -= field_amount
                    time.sleep(3)
                    continue
                else:
                    self.untreated_btc -= field_amount - btc_amount

                buy_r = self.huobiSpot.send_order(btc_amount, 'api', 'btcusdt', 'buy-limit', buy_price)
                print buy_r

                if buy_r['status'] != 'ok':
                    if buy_r['status'] == 'fail':
                        self.logger.error('buy failed : %s' % buy_r['msg'])
                    else:
                        self.logger.error('buy failed : %s' % buy_r['err-msg'])
                    self.logger.info('开始回滚')
                    self.rollback_binance_order(sell_order_id)
                    self.logger.info('终止程序')
                    break

                buy_order_id = buy_r['data']
                self.output.write(':' + str(buy_order_id))
                self.output.flush()
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    buy_order_result = self.huobiSpot.order_info(buy_order_id)
                    print buy_order_result
                    if buy_order_result['status'] == 'ok' and buy_order_result['data']['state'] == 'filled':
                        self.logger.info('huobi buy filled, orderId: %s' % buy_order_id)
                        field_amount = float('%.8f' % float(buy_order_result['data']['field-amount']))
                        field_cash_amount = float('%.8f' % float(buy_order_result['data']['field-cash-amount']))
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.huobi_timeout += 1
                    if self.huobi_timeout == self.huobi_max_timeout:
                        self.logger.info('连续%s次超时,终止程序' % self.huobi_timeout)
                        break
                    else:
                        self.logger.info('连续%s次超时,继续程序' % self.huobi_timeout)
                        if self.huobi_timeout == 1:
                            time.sleep(60)
                        else:
                            time.sleep(600)
                        #
                        self.btc_exchange_max /= 2
                        continue
                else:
                    self.huobi_timeout = 0
                    self.btc_exchange_max = 0.065

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    field_amount, field_cash_amount))
                # update income
                self.huobi_usdt_dec = field_cash_amount

                self.huobi_usdt_total_change -= self.huobi_usdt_dec

                self.huobi_trade_btc += field_amount
                self.huobi_trade_usdt -= field_cash_amount
                # total
                usdt_inc = self.binance_usdt_inc - self.huobi_usdt_dec
                thistime_earnings = usdt_inc
                thistime_earnings_rate = thistime_earnings * 1.0 / field_cash_amount

                # total_btc_earnings = self.huobi_btc_total_change + self.binance_btc_total_change
                total_usdt_earnings = self.huobi_usdt_total_change + self.binance_usdt_total_change

                self.logger.info('本次交易量: %s BTC, 盈利: %s USDT, 盈利率: %s' % (
                    float(field_amount), thistime_earnings, thistime_earnings_rate))
                self.logger.info(
                    '总BTC成交量: %s, 盈利: %s USDT, 盈利率: %s' % (
                        self.btc_exchange_amount, total_usdt_earnings,
                        total_usdt_earnings * 1.0 / self.usdt_exchange_amount))
                self.logger.info('|--------------------------------------------------')
                self.logger.info('|' + ' BTC:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                    (self.huobi_trade_btc + self.binance_trade_btc), self.huobi_trade_btc,
                    self.binance_trade_btc))
                self.logger.info(
                    '|' + 'USDT:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                        (self.huobi_trade_usdt + self.binance_trade_usdt), self.huobi_trade_usdt,
                        self.binance_trade_usdt))
                self.logger.info('|--------------------------------------------------')
                self.update_profit_rate()

                self.last_deal_time = int(time.time())
            time.sleep(0.1)
            nowtime = time.strftime('%H:%M:%S', time.localtime(time.time()))
            if nowtime.startswith('08:30'):
                self.order_statistics()

                self.usdt_exchange_amount = 0
                self.btc_exchange_amount = 0
                self.huobi_usdt_total_change = 0
                self.binance_usdt_total_change = 0
                time.sleep(60)

            # 每5分钟没有交易就更新账户信息
            if self.last_deal_time > 0 and int(time.time()) - self.last_deal_time > 300:
                orderid = 0
                if self.untreated_btc > 0.001:
                    sell_amount = float('%.4f' % self.untreated_btc)
                    self.logger.info('平衡账户资产,Huobi卖出: %s BTC' % sell_amount)
                    sell_r = self.huobiSpot.send_order(sell_amount, 'api', 'btcusdt', 'sell-market')
                    if sell_r['status'] != 'ok':
                        if sell_r['status'] == 'fail':
                            self.logger.error('sell failed : %s' % sell_r['msg'])
                        else:
                            self.logger.error('sell failed : %s' % sell_r['err-msg'])
                        break
                    else:
                        orderid = sell_r['data']
                        self.untreated_btc -= sell_amount
                elif self.untreated_btc < -0.001:
                    buy_price = h_asks[0][0] + 20
                    buy_amount = float('%.4f' % self.untreated_btc)
                    self.logger.info('平衡账户资产,Huobi买入: %s BTC' % buy_amount)
                    buy_r = self.huobiSpot.send_order(-1 * buy_amount, 'api', 'btcusdt', 'buy-limit', buy_price)
                    if buy_r['status'] != 'ok':
                        if buy_r['status'] == 'fail':
                            self.logger.error('sell failed : %s' % buy_r['msg'])
                        else:
                            self.logger.error('sell failed : %s' % buy_r['err-msg'])
                        break
                    else:
                        orderid = buy_r['data']
                        self.untreated_btc -= buy_amount
                print orderid
                if orderid:
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次确认订单信息' % (times + 1))
                        order_result = self.huobiSpot.order_info(orderid)
                        print order_result
                        if order_result['status'] == 'ok' and order_result['data']['state'] == 'filled':
                            self.logger.info('order filled, orderId: %s' % orderid)
                            field_amount = float('%.8f' % float(order_result['data']['field-amount']))
                            field_cash_amount = float('%.8f' % float(order_result['data']['field-cash-amount']))
                            if 'buy' in order_result['data']['type']:
                                self.huobi_trade_btc += field_amount
                                self.huobi_trade_usdt -= field_cash_amount
                            else:
                                self.huobi_trade_btc -= field_amount
                                self.huobi_trade_usdt += field_cash_amount
                            break
                        times += 1

                        if times == 9:
                            time.sleep(10)
                        if times == 19:
                            time.sleep(300)

                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break

                total_btc_amount_before = self.binance_trade_btc + self.huobi_trade_btc
                self.logger.info('before: binance:%s\thuobi: %s' % (self.binance_trade_btc, self.huobi_trade_btc))
                self.update_account_info()
                self.logger.info('after : binance:%s\thuobi: %s' % (self.binance_trade_btc, self.huobi_trade_btc))
                total_btc_amount_after = self.binance_trade_btc + self.huobi_trade_btc
                if abs(total_btc_amount_after - total_btc_amount_before) > 0.001:
                    self.logger.info('账户BTC总量发生异常,程序终止')
                    break
                self.last_deal_time = 0

    def rollback_binance_order(self, orderid):
        order_info = self.binanceClient.get_order(symbol='BTCUSDT', orderId=orderid)
        side = order_info['side'].upper()
        field_amount = float('%.6f' % float(order_info['executedQty']))
        price = float('%.2f' % float(order_info['price']))
        if side == 'BUY':
            try:
                order = self.binanceClient.order_limit_sell(symbol='BTCUSDT', quantity=field_amount,
                                                            price=price - 5, newOrderRespType='FULL')
                print order
            except Exception as e:
                self.logger.error(u'Binance卖出错误: %s, 回滚失败' % e)
        else:
            try:
                order = self.binanceClient.order_limit_buy(symbol='BTCUSDT', quantity=field_amount,
                                                           price=price + 5, newOrderRespType='FULL')
                print order
            except Exception as e:
                self.logger.error(u'Binance买入错误: %s, 回滚失败' % e)

    # HUOBI API 提供的只能获取100条,此函数为扩展,提取500条
    def get_huobi_orders(self):
        result = []
        last_order_id = None
        for i in range(5):
            orders_list = self.huobiSpot.orders_list('btcusdt', '', _from=last_order_id, size=100)
            if orders_list['status'] != 'ok':
                print ('获取火币历史委托错误')
                print orders_list
                return []
            else:
                if i == 0:
                    for item in orders_list['data']:
                        result.append(item)
                else:
                    for item in orders_list['data'][1:]:
                        result.append(item)
                last_order_id = orders_list['data'][-1]['id']
        return result

    def order_statistics(self, start=None, end=None):
        huobi_order_list = []
        binance_order_list = []
        # Huobi
        for order in self.get_huobi_orders():
            dict = {
                'id': order['id'],
                'state': order['state'].upper(),
                'amount': float('%.8f' % float(order['amount'])),
                'field-cash-amount': float('%.4f' % float(order['field-cash-amount'])),
                'field-amount': float('%.8f' % float(order['field-amount'])),
                'created-at': order['created-at'],
                'finished-at': order['finished-at'],
                'canceled-at': order['canceled-at'],
                'type': u'LIMIT' if 'limit' in order['type'] else u'MARKET',
                'side': u'BUY' if 'buy' in order['type'] else u'SELL',
            }
            # print dict
            if dict['finished-at'] == 0:
                continue
            if dict['field-amount'] > 0:
                dict['price'] = float('%.2f' % (dict['field-cash-amount'] / dict['field-amount']))
            huobi_order_list.append(dict)
        print('获取币安历史委托数据')
        binance_orders = self.binanceClient.get_all_orders(symbol='BTCUSDT')
        binance_trades = self.binanceClient.get_my_trades(symbol='BTCUSDT', recvWindow=130000)
        # print binance_trades
        for order in binance_orders:
            dict = {
                'id': order['orderId'],
                'state': order['status'].upper(),
                'amount': float('%.8f' % float(order['origQty'])),
                'created-at': order['time'],
                'finished-at': '',
                'canceled-at': '',
                'type': order['type'].upper(),
                'side': order['side'].upper(),
                'commission': 0,
                'field-amount': 0,
                'field-cash-amount': 0
            }
            binance_order_list.append(dict)
        for item in binance_order_list:
            id = item['id']
            for trade in binance_trades:
                if trade['orderId'] == id:
                    item['commission'] += float('%.8f' % float(trade['commission']))
                    item['field-amount'] += float('%.8f' % float(trade['qty']))
                    item['price'] = float('%.2f' % float(trade['price']))
                    item['field-cash-amount'] += float('%.8f' % (float(trade['qty']) * item['price']))
        huobi_order_list = sorted(huobi_order_list, key=lambda x: x['created-at'], reverse=True)
        binance_order_list = sorted(binance_order_list, key=lambda x: x['created-at'], reverse=True)

        # print huobi_order_list
        # print binance_order_list

        yestoday = datetime.date.today() + datetime.timedelta(days=-1)
        timearray = time.strptime(str(yestoday) + ' 8:30:00', "%Y-%m-%d %H:%M:%S")
        timestamp = int(round(time.mktime(timearray)) * 1000)
        print timestamp
        workbook = xlsxwriter.Workbook('output.xlsx')
        worksheet = workbook.add_worksheet(u'成功')
        worksheet2 = workbook.add_worksheet(u'失败')
        worksheet3 = workbook.add_worksheet(u'总计')
        date_format_str = 'yy/mm/dd/ hh:mm:ss'
        binance_common_format = workbook.add_format({'align': 'left', 'font_name': 'Consolas'})
        binance_date_format = workbook.add_format({'num_format': date_format_str,
                                                   'align': 'left', 'font_name': 'Consolas'})

        huobi_common_format = workbook.add_format({'align': 'left', 'font_name': 'Consolas', 'bg_color': 'yellow'})
        huobi_date_format = workbook.add_format({'num_format': date_format_str,
                                                 'align': 'left', 'font_name': 'Consolas', 'bg_color': 'yellow'})
        merged_format = workbook.add_format({'align': 'center', 'valign': 'vcenter', 'font_name': 'Consolas'})
        row_1 = 0
        row_2 = 0
        row_3 = 0
        col = 0
        header = [u'委托时间', u'方向', u'类型', u'价格', u'委托数量', u'成交数量', u'成交金额', u'状态',
                  u'盈利(USDT)', u'盈利率']

        total_usdt_earnings = 0
        total_huobi_usdt_trade = 0
        total_binance_commission = 0
        total_btc_exchange = 0
        i = 0
        for h in header:
            worksheet.write(row_1, col + i, h)
            worksheet2.write(row_2, col + i, h)
            i += 1
        row_1 += 1
        row_2 += 1
        with open('history', 'r') as f:
            for line in f.readlines():
                if len(line) < 5:
                    continue
                splited = line.strip().split(':')
                huobi_id = 0
                if len(splited) == 2:
                    binance_id = int(splited[0])
                    huobi_id = int(splited[1])
                elif len(splited) == 1:
                    binance_id = int(splited[0])
                else:
                    continue
                # print binance_id, huobi_id
                if binance_id > 0 and huobi_id > 0:
                    binance_order = None
                    huobi_order = None
                    for order in binance_order_list:
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == binance_id:
                            binance_order = order
                            break
                    for order in huobi_order_list:
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == huobi_id:
                            huobi_order = order
                            break
                    if not binance_order or not huobi_order:
                        continue
                    # print binance_id, huobi_id
                    total_huobi_usdt_trade += float('%.8f' % float(huobi_order['field-cash-amount']))
                    total_binance_commission += float('%.8f' % float(binance_order['commission']))
                    total_btc_exchange += float('%.8f' % float(binance_order['field-amount']))

                    order = huobi_order
                    worksheet.write(row_1, col, datetime.datetime.fromtimestamp(
                        float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                    huobi_date_format)
                    worksheet.write(row_1, col + 1, order['side'], huobi_common_format)
                    worksheet.write(row_1, col + 2, order['type'], huobi_common_format)
                    worksheet.write(row_1, col + 3, '%.2f' % float(order['price']), huobi_common_format)
                    worksheet.write(row_1, col + 4, '%.4f' % float(order['amount']), huobi_common_format)
                    worksheet.write(row_1, col + 5, '%.8f' % float(order['field-amount']), huobi_common_format)
                    worksheet.write(row_1, col + 6, '%.8f' % float(order['field-cash-amount']), huobi_common_format)
                    worksheet.write(row_1, col + 7, order['state'], huobi_common_format)
                    row_1 += 1

                    order = binance_order
                    worksheet.write_datetime(row_1, col, datetime.datetime.fromtimestamp(
                        float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                             binance_date_format)
                    worksheet.write(row_1, col + 1, order['side'], binance_common_format)
                    worksheet.write(row_1, col + 2, order['type'], binance_common_format)
                    worksheet.write(row_1, col + 3, '%.2f' % float(order['price']), binance_common_format)
                    worksheet.write(row_1, col + 4, '%.4f' % float(order['amount']), binance_common_format)
                    worksheet.write(row_1, col + 5, '%.8f' % float(order['field-amount']), binance_common_format)
                    worksheet.write(row_1, col + 6, '%.8f' % float(order['field-cash-amount']),
                                    binance_common_format)
                    worksheet.write(row_1, col + 7, order['state'], binance_common_format)

                    earnings = abs(float(huobi_order['field-cash-amount']) - float(binance_order['field-cash-amount']))
                    earning_rate = earnings / binance_order['field-cash-amount']

                    total_usdt_earnings += earnings

                    worksheet.merge_range(row_1 - 1, 8, row_1, 8, '%.8f' % earnings, merged_format)
                    worksheet.merge_range(row_1 - 1, 9, row_1, 9, '%.8f' % earning_rate, merged_format)
                    row_1 += 1
                else:
                    for order in binance_order_list:
                        # print order['created-at']
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == binance_id and order['field-amount'] > 0:
                            print order
                        if order['id'] == binance_id and 'CANCELED' in order['state']:
                            worksheet2.write_datetime(row_2, col, datetime.datetime.fromtimestamp(
                                float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                                      binance_date_format)
                            worksheet2.write(row_2, col + 1, order['side'], binance_common_format)
                            worksheet2.write(row_2, col + 2, order['type'], binance_common_format)
                            worksheet2.write(row_2, col + 3, 0, binance_common_format)
                            worksheet2.write(row_2, col + 4, '%.4f' % float(order['amount']), binance_common_format)
                            worksheet2.write(row_2, col + 5, 0, binance_common_format)
                            worksheet2.write(row_2, col + 6, 0, binance_common_format)
                            worksheet2.write(row_2, col + 7, order['state'], binance_common_format)
                            row_2 += 1
                            break
            total_huobi_commission = total_huobi_usdt_trade * 0.002
            total_binance_commission = total_binance_commission * 10.8159
            # print total_huobi_commission, total_binance_commission
            header = [u'BTC总量', u'BTC成交量', u'USDT总量' u'USDT盈亏', u'HUOBI手续费', u'BINANCE手续费']
            i = 0
            for h in header:
                worksheet3.write(row_3, i, h)
                i += 1
            row_3 += 1
            worksheet3.write(row_3, 0, total_btc_exchange)
            worksheet3.write(row_3, 1, total_usdt_earnings)
            worksheet3.write(row_3, 2, total_huobi_commission)
            worksheet3.write(row_3, 3, total_binance_commission)
        workbook.close()
        # 发送邮件
        self.logger.info('邮件通知')
        ArbitrageStratety.send_mail_with_attachment()

    @staticmethod
    def send_mail_with_attachment():
        from email import encoders
        from email.header import Header
        from email.mime.base import MIMEBase
        from email.mime.multipart import MIMEMultipart
        from email.utils import parseaddr, formataddr
        from email.mime.text import MIMEText
        import smtplib

        def _format_addr(s):
            name, addr = parseaddr(s)
            return formataddr(( \
                Header(name, 'utf-8').encode(), \
                addr.encode('utf-8') if isinstance(addr, unicode) else addr))

        from_addr = '*****@*****.**'
        username = '******'
        password = '******'
        to_addr = '*****@*****.**'
        smtp_server = 'smtp.163.com'

        print 'sending mail to [email protected]'
        msg = MIMEMultipart()
        msg['From'] = _format_addr(from_addr)
        msg['To'] = _format_addr(to_addr)
        msg['Subject'] = u'收益情况(%s)' % (time.strftime('%Y-%m-%d', time.localtime(time.time())))
        msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))

        # add file:
        with open('output.xlsx', 'rb') as f:
            mime = MIMEBase('text', 'txt', filename='output.xlsx')
            mime.add_header('Content-Disposition', 'attachment', filename='output.xlsx')
            mime.add_header('Content-ID', '<0>')
            mime.add_header('X-Attachment-Id', '0')
            mime.set_payload(f.read())
            encoders.encode_base64(mime)
            msg.attach(mime)

        server = smtplib.SMTP()
        server.connect(smtp_server)
        server.login(username, password)
        server.sendmail(from_addr, [to_addr], msg.as_string())
        server.quit()
import os

from binance.client import Client

# init
api_key = os.environ.get('binance_api')
api_secret = os.environ.get('binance_secret')

client = Client(api_key, api_secret)

## main

# get balances for all assets & some account information
print(client.get_account())

# get balance for a specific asset only (BTC)
print(client.get_asset_balance(asset='BTC'))

# get balances for futures account
print(client.futures_account_balance())

# get balances for margin account
# will raise an exception if margin account is not activated
#print(client.get_margin_account())
Example #17
0
class TradingAccount():
    def __init__(self, app={}):
        """Trading account object model

        Parameters
        ----------
        app : object
            PyCryptoBot object
        """

        # config needs to be a dictionary, empty or otherwise
        if not isinstance(app, object):
            raise TypeError('App is not a PyCryptoBot object.')

        if app.getExchange() == 'binance':
            self.client = Client(app.getAPIKey(), app.getAPISecret(), {
                'verify': False,
                'timeout': 20
            })

        # if trading account is for testing it will be instantiated with a balance of 1000
        self.balance = pd.DataFrame(
            [['QUOTE', 1000, 0, 1000], ['BASE', 0, 0, 0]],
            columns=['currency', 'balance', 'hold', 'available'])

        self.app = app

        if app.isLive() == 1:
            self.mode = 'live'
        else:
            self.mode = 'test'

        self.orders = pd.DataFrame()

    def __convertStatus(self, val):
        if val == 'filled':
            return 'done'
        else:
            return val

    def getOrders(self, market='', action='', status='all'):
        """Retrieves orders either live or simulation

        Parameters
        ----------
        market : str, optional
            Filters orders by market
        action : str, optional
            Filters orders by action
        status : str
            Filters orders by status, defaults to 'all'
        """

        if self.app.getExchange() == 'coinbasepro' and market != '':
            # validate market is syntactically correct
            p = re.compile(r"^[A-Z]{3,4}\-[A-Z]{3,4}$")
            if not p.match(market):
                raise TypeError('Coinbase Pro market is invalid.')
        elif self.app.getExchange() == 'binance':
            # validate market is syntactically correct
            p = re.compile(r"^[A-Z]{6,12}$")
            if not p.match(market):
                raise TypeError('Binance market is invalid.')

        if action != '':
            # validate action is either a buy or sell
            if not action in ['buy', 'sell']:
                raise ValueError('Invalid order action.')

        # validate status is open, pending, done, active or all
        if not status in [
                'open', 'pending', 'done', 'active', 'all', 'filled'
        ]:
            raise ValueError('Invalid order status.')

        if self.app.getExchange() == 'binance':
            if self.mode == 'live':
                resp = self.client.get_all_orders(symbol=market)
                if len(resp) > 0:
                    df = pd.DataFrame(resp)
                else:
                    df = pd.DataFrame()

                if len(df) == 0:
                    return pd.DataFrame()

                df = df[[
                    'time', 'symbol', 'side', 'type', 'executedQty',
                    'cummulativeQuoteQty', 'status'
                ]]
                df.columns = [
                    'created_at', 'market', 'action', 'type', 'size', 'value',
                    'status'
                ]
                df['created_at'] = df['created_at'].apply(
                    lambda x: int(str(x)[:10]))
                df['created_at'] = df['created_at'].astype("datetime64[s]")
                df['size'] = df['size'].astype(float)
                df['value'] = df['value'].astype(float)
                df['action'] = df['action'].str.lower()
                df['type'] = df['type'].str.lower()
                df['status'] = df['status'].str.lower()
                df['price'] = df['size'] * df['value']

                # pylint: disable=unused-variable
                for k, v in df.items():
                    if k == 'status':
                        df[k] = df[k].map(self.__convertStatus)

                if action != '':
                    df = df[df['action'] == action]
                    df = df.reset_index(drop=True)

                if status != 'all' and status != '':
                    df = df[df['status'] == status]
                    df = df.reset_index(drop=True)

                return df
            else:
                # return dummy orders
                if market == '':
                    return self.orders
                else:
                    if (len(self.orders) > 0):
                        return self.orders[self.orders['market'] == market]
                    else:
                        return pd.DataFrame()
        if self.app.getExchange() == 'coinbasepro':
            if self.mode == 'live':
                # if config is provided and live connect to Coinbase Pro account portfolio
                model = CBAuthAPI(self.app.getAPIKey(),
                                  self.app.getAPISecret(),
                                  self.app.getAPIPassphrase(),
                                  self.app.getAPIURL())
                # retrieve orders from live Coinbase Pro account portfolio
                self.orders = model.getOrders(market, action, status)
                return self.orders
            else:
                # return dummy orders
                if market == '':
                    return self.orders
                else:
                    return self.orders[self.orders['market'] == market]

    def getBalance(self, currency=''):
        """Retrieves balance either live or simulation

        Parameters
        ----------
        currency: str, optional
            Filters orders by currency
        """

        if self.app.getExchange() == 'binance':
            if self.mode == 'live':
                resp = self.client.get_account()
                if 'balances' in resp:
                    df = pd.DataFrame(resp['balances'])
                    df = df[(df['free'] != '0.00000000')
                            & (df['free'] != '0.00')]
                    df['free'] = df['free'].astype(float)
                    df['locked'] = df['locked'].astype(float)
                    df['balance'] = df['free'] - df['locked']
                    df.columns = ['currency', 'available', 'hold', 'balance']
                    df = df[['currency', 'balance', 'hold', 'available']]
                    df = df.reset_index(drop=True)

                    if currency == '':
                        # retrieve all balances
                        return df
                    else:
                        # retrieve balance of specified currency
                        df_filtered = df[df['currency'] ==
                                         currency]['available']
                        if len(df_filtered) == 0:
                            # return nil balance if no positive balance was found
                            return 0.0
                        else:
                            # return balance of specified currency (if positive)
                            if currency in ['EUR', 'GBP', 'USD']:
                                return float(
                                    self.app.truncate(
                                        float(df[df['currency'] == currency]
                                              ['available'].values[0]), 2))
                            else:
                                return float(
                                    self.app.truncate(
                                        float(df[df['currency'] == currency]
                                              ['available'].values[0]), 4))
                else:
                    return 0.0
            else:
                # return dummy balances
                if currency == '':
                    # retrieve all balances
                    return self.balance
                else:
                    if self.app.getExchange() == 'binance':
                        self.balance = self.balance.replace('QUOTE', currency)
                    else:
                        # replace QUOTE and BASE placeholders
                        if currency in ['EUR', 'GBP', 'USD']:
                            self.balance = self.balance.replace(
                                'QUOTE', currency)
                        else:
                            self.balance = self.balance.replace(
                                'BASE', currency)

                    if self.balance.currency[self.balance.currency.isin(
                        [currency])].empty == True:
                        self.balance.loc[len(
                            self.balance)] = [currency, 0, 0, 0]

                    # retrieve balance of specified currency
                    df = self.balance
                    df_filtered = df[df['currency'] == currency]['available']

                    if len(df_filtered) == 0:
                        # return nil balance if no positive balance was found
                        return 0.0
                    else:
                        # return balance of specified currency (if positive)
                        if currency in ['EUR', 'GBP', 'USD']:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 2))
                        else:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 4))

        else:
            if self.mode == 'live':
                # if config is provided and live connect to Coinbase Pro account portfolio
                model = CBAuthAPI(self.app.getAPIKey(),
                                  self.app.getAPISecret(),
                                  self.app.getAPIPassphrase(),
                                  self.app.getAPIURL())
                if currency == '':
                    # retrieve all balances
                    return model.getAccounts()[[
                        'currency', 'balance', 'hold', 'available'
                    ]]
                else:
                    df = model.getAccounts()
                    # retrieve balance of specified currency
                    df_filtered = df[df['currency'] == currency]['available']
                    if len(df_filtered) == 0:
                        # return nil balance if no positive balance was found
                        return 0.0
                    else:
                        # return balance of specified currency (if positive)
                        if currency in ['EUR', 'GBP', 'USD']:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 2))
                        else:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 4))

            else:
                # return dummy balances

                if currency == '':
                    # retrieve all balances
                    return self.balance
                else:
                    # replace QUOTE and BASE placeholders
                    if currency in ['EUR', 'GBP', 'USD']:
                        self.balance = self.balance.replace('QUOTE', currency)
                    elif currency in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']:
                        self.balance = self.balance.replace('BASE', currency)

                    if self.balance.currency[self.balance.currency.isin(
                        [currency])].empty == True:
                        self.balance.loc[len(
                            self.balance)] = [currency, 0, 0, 0]

                    # retrieve balance of specified currency
                    df = self.balance
                    df_filtered = df[df['currency'] == currency]['available']

                    if len(df_filtered) == 0:
                        # return nil balance if no positive balance was found
                        return 0.0
                    else:
                        # return balance of specified currency (if positive)
                        if currency in ['EUR', 'GBP', 'USD']:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 2))
                        else:
                            return float(
                                self.app.truncate(
                                    float(df[df['currency'] == currency]
                                          ['available'].values[0]), 4))

    def saveTrackerCSV(self, market='', save_file='tracker.csv'):
        """Saves order tracker to CSV

        Parameters
        ----------
        market : str, optional
            Filters orders by market
        save_file : str
            Output CSV file
        """

        if self.app.getExchange() == 'coinbasepro' and market != '':
            # validate market is syntactically correct
            p = re.compile(r"^[A-Z]{3,4}\-[A-Z]{3,4}$")
            if not p.match(market):
                raise TypeError('Coinbase Pro market is invalid.')
        elif self.app.getExchange() == 'binance':
            # validate market is syntactically correct
            p = re.compile(r"^[A-Z]{6,12}$")
            if not p.match(market):
                raise TypeError('Binance market is invalid.')

        if self.mode == 'live':
            if self.app.getExchange() == 'coinbasepro':
                # retrieve orders from live Coinbase Pro account portfolio
                df = self.getOrders(market, '', 'done')
            elif self.app.getExchange() == 'binance':
                # retrieve orders from live Binance account portfolio
                df = self.getOrders(market, '', 'done')
            else:
                df = pd.DataFrame()
        else:
            # return dummy orders
            if market == '':
                df = self.orders
            else:
                df = self.orders[self.orders['market'] == market]

        if list(df.keys()) != [
                'created_at', 'market', 'action', 'type', 'size', 'value',
                'status', 'price'
        ]:
            # no data, return early
            return False

        df_tracker = pd.DataFrame()

        last_action = ''
        for market in df['market'].sort_values().unique():
            df_market = df[df['market'] == market]

            df_buy = pd.DataFrame()
            df_sell = pd.DataFrame()

            pair = 0
            # pylint: disable=unused-variable
            for index, row in df_market.iterrows():
                if row['action'] == 'buy':
                    pair = 1

                if pair == 1 and (row['action'] != last_action):
                    if row['action'] == 'buy':
                        df_buy = row
                    elif row['action'] == 'sell':
                        df_sell = row

                if row['action'] == 'sell' and len(df_buy) != 0:
                    df_pair = pd.DataFrame([[
                        df_sell['status'], df_buy['market'],
                        df_buy['created_at'], df_buy['type'], df_buy['size'],
                        df_buy['value'], df_buy['price'],
                        df_sell['created_at'], df_sell['type'],
                        df_sell['size'], df_sell['value'], df_sell['price']
                    ]],
                                           columns=[
                                               'status', 'market', 'buy_at',
                                               'buy_type', 'buy_size',
                                               'buy_value', 'buy_price',
                                               'sell_at', 'sell_type',
                                               'sell_size', 'sell_value',
                                               'sell_price'
                                           ])
                    df_tracker = df_tracker.append(df_pair, ignore_index=True)
                    pair = 0

                last_action = row['action']

        if list(df_tracker.keys()) != [
                'status', 'market', 'buy_at', 'buy_type', 'buy_size',
                'buy_value', 'buy_price', 'sell_at', 'sell_type', 'sell_size',
                'sell_value', 'sell_price'
        ]:
            # no data, return early
            return False

        df_tracker['profit'] = np.subtract(df_tracker['sell_value'],
                                           df_tracker['buy_value'])
        df_tracker['margin'] = np.multiply(
            np.true_divide(df_tracker['profit'], df_tracker['sell_value']),
            100)
        df_sincebot = df_tracker[df_tracker['buy_at'] > '2021-02-1']

        try:
            df_sincebot.to_csv(save_file, index=False)
        except OSError:
            raise SystemExit('Unable to save: ', save_file)

    def buy(self,
            cryptoMarket,
            fiatMarket,
            fiatAmount=0,
            manualPrice=0.00000000):
        """Places a buy order either live or simulation

        Parameters
        ----------
        cryptoMarket: str
            Crypto market you wish to purchase
        fiatMarket, str
            QUOTE market funding the purchase
        fiatAmount, float
            QUOTE amount of crypto currency to purchase
        manualPrice, float
            Used for simulations specifying the live price to purchase
        """

        # fiat funding amount must be an integer or float
        if not isinstance(fiatAmount, float) and not isinstance(
                fiatAmount, int):
            raise TypeError('QUOTE amount not numeric.')

        # fiat funding amount must be positive
        if fiatAmount <= 0:
            raise Exception('Invalid QUOTE amount.')

        if self.app.getExchange() == 'binance':
            # validate crypto market is syntactically correct
            p = re.compile(r"^[A-Z]{3,8}$")
            if not p.match(cryptoMarket):
                raise TypeError('Binance crypto market is invalid.')

            # validate fiat market is syntactically correct
            p = re.compile(r"^[A-Z]{3,8}$")
            if not p.match(fiatMarket):
                raise TypeError('Binance fiat market is invalid.')
        else:
            # crypto market should be either BCH, BTC, ETH, LTC or XLM
            if cryptoMarket not in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']:
                raise Exception(
                    'Invalid crypto market: BCH, BTC, ETH, LTC, ETH, or XLM')

            # fiat market should be either EUR, GBP, or USD
            if fiatMarket not in ['EUR', 'GBP', 'USD']:
                raise Exception('Invalid QUOTE market: EUR, GBP, USD')

        # reconstruct the exchange market using crypto and fiat inputs
        if self.app.getExchange() == 'binance':
            market = cryptoMarket + fiatMarket
        else:
            market = cryptoMarket + '-' + fiatMarket

        if self.app.getExchange() == 'binance':
            if self.mode == 'live':
                # execute a live market buy
                resp = self.client.order_market_buy(symbol=market,
                                                    quantity=fiatAmount)

                # TODO: not finished
                print(resp)
            else:
                # fiat amount should exceed balance
                if fiatAmount > self.getBalance(fiatMarket):
                    raise Exception('Insufficient funds.')

                # manual price must be an integer or float
                if not isinstance(manualPrice, float) and not isinstance(
                        manualPrice, int):
                    raise TypeError('Optional manual price not numeric.')

                price = manualPrice
                # if manualPrice is non-positive retrieve the current live price
                if manualPrice <= 0:
                    if self.app.getExchange() == 'binance':
                        api = BPublicAPI()
                        price = api.getTicker(market)
                    else:
                        resp = requests.get(
                            'https://api-public.sandbox.pro.coinbase.com/products/'
                            + market + '/ticker')
                        if resp.status_code != 200:
                            raise Exception(
                                'GET /products/' + market +
                                '/ticker {}'.format(resp.status_code))
                        resp.raise_for_status()
                        json = resp.json()
                        price = float(json['price'])

                # calculate purchase fees
                fee = fiatAmount * 0.005
                fiatAmountMinusFee = fiatAmount - fee
                total = float(fiatAmountMinusFee / float(price))

                # append dummy order into orders dataframe
                ts = pd.Timestamp.now()
                price = (fiatAmountMinusFee * 100) / (total * 100)
                order = pd.DataFrame([[
                    '', market, 'buy', 'market',
                    float('{:.8f}'.format(total)), fiatAmountMinusFee, 'done',
                    '{:.8f}'.format(float(price))
                ]],
                                     columns=[
                                         'created_at', 'market', 'action',
                                         'type', 'size', 'value', 'status',
                                         'price'
                                     ],
                                     index=[ts])
                order['created_at'] = order.index
                self.orders = pd.concat(
                    [self.orders, pd.DataFrame(order)], ignore_index=False)

                # update the dummy fiat balance
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'balance'] = self.getBalance(fiatMarket) - fiatAmount
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'available'] = self.getBalance(fiatMarket) - fiatAmount

                # update the dummy crypto balance
                self.balance.loc[self.balance['currency'] == cryptoMarket,
                                 'balance'] = self.getBalance(cryptoMarket) + (
                                     fiatAmountMinusFee / price)
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'available'] = self.getBalance(cryptoMarket) + (
                        fiatAmountMinusFee / price)

        else:
            if self.mode == 'live':
                # connect to coinbase pro api (authenticated)
                model = CBAuthAPI(self.app.getAPIKey(),
                                  self.app.getAPISecret(),
                                  self.app.getAPIPassphrase(),
                                  self.app.getAPIURL())

                # execute a live market buy
                if fiatAmount > 0:
                    resp = model.marketBuy(market, fiatAmount)
                else:
                    resp = model.marketBuy(market,
                                           float(self.getBalance(fiatMarket)))

                # TODO: not finished
                print(resp)
            else:
                # fiat amount should exceed balance
                if fiatAmount > self.getBalance(fiatMarket):
                    raise Exception('Insufficient funds.')

                # manual price must be an integer or float
                if not isinstance(manualPrice, float) and not isinstance(
                        manualPrice, int):
                    raise TypeError('Optional manual price not numeric.')

                price = manualPrice
                # if manualPrice is non-positive retrieve the current live price
                if manualPrice <= 0:
                    resp = requests.get(
                        'https://api-public.sandbox.pro.coinbase.com/products/'
                        + market + '/ticker')
                    if resp.status_code != 200:
                        raise Exception('GET /products/' + market +
                                        '/ticker {}'.format(resp.status_code))
                    resp.raise_for_status()
                    json = resp.json()
                    price = float(json['price'])

                # calculate purchase fees
                fee = fiatAmount * 0.005
                fiatAmountMinusFee = fiatAmount - fee
                total = float(fiatAmountMinusFee / price)

                # append dummy order into orders dataframe
                ts = pd.Timestamp.now()
                price = (fiatAmountMinusFee * 100) / (total * 100)
                order = pd.DataFrame([[
                    '', market, 'buy', 'market',
                    float('{:.8f}'.format(total)), fiatAmountMinusFee, 'done',
                    price
                ]],
                                     columns=[
                                         'created_at', 'market', 'action',
                                         'type', 'size', 'value', 'status',
                                         'price'
                                     ],
                                     index=[ts])
                order['created_at'] = order.index
                self.orders = pd.concat(
                    [self.orders, pd.DataFrame(order)], ignore_index=False)

                # update the dummy fiat balance
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'balance'] = self.getBalance(fiatMarket) - fiatAmount
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'available'] = self.getBalance(fiatMarket) - fiatAmount

                # update the dummy crypto balance
                self.balance.loc[self.balance['currency'] == cryptoMarket,
                                 'balance'] = self.getBalance(cryptoMarket) + (
                                     fiatAmountMinusFee / price)
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'available'] = self.getBalance(cryptoMarket) + (
                        fiatAmountMinusFee / price)

    def sell(self,
             cryptoMarket,
             fiatMarket,
             cryptoAmount,
             manualPrice=0.00000000):
        """Places a sell order either live or simulation

        Parameters
        ----------
        cryptoMarket: str
            Crypto market you wish to purchase
        fiatMarket, str
            QUOTE market funding the purchase
        fiatAmount, float
            QUOTE amount of crypto currency to purchase
        manualPrice, float
            Used for simulations specifying the live price to purchase
        """
        if self.app.getExchange() == 'binance':
            # validate crypto market is syntactically correct
            p = re.compile(r"^[A-Z]{3,8}$")
            if not p.match(cryptoMarket):
                raise TypeError('Binance crypto market is invalid.')

            # validate fiat market is syntactically correct
            p = re.compile(r"^[A-Z]{3,8}$")
            if not p.match(fiatMarket):
                raise TypeError('Binance fiat market is invalid.')
        else:
            # crypto market should be either BCH, BTC, ETH, LTC or XLM
            if cryptoMarket not in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']:
                raise Exception(
                    'Invalid crypto market: BCH, BTC, ETH, LTC, ETH, or XLM')

            # fiat market should be either EUR, GBP, or USD
            if fiatMarket not in ['EUR', 'GBP', 'USD']:
                raise Exception('Invalid QUOTE market: EUR, GBP, USD')

        # reconstruct the exchange market using crypto and fiat inputs
        if self.app.getExchange() == 'binance':
            market = cryptoMarket + fiatMarket
        else:
            market = cryptoMarket + '-' + fiatMarket

        # crypto amount must be an integer or float
        if not isinstance(cryptoAmount, float) and not isinstance(
                cryptoAmount, int):
            raise TypeError('Crypto amount not numeric.')

        # crypto amount must be positive
        if cryptoAmount <= 0:
            raise Exception('Invalid crypto amount.')

        if self.app.getExchange() == 'binance':
            if self.mode == 'live':
                # execute a live market buy
                resp = self.client.order_market_sell(symbol=market,
                                                     quantity=cryptoAmount)

                # TODO: not finished
                print(resp)
            else:
                # crypto amount should exceed balance
                if cryptoAmount > self.getBalance(cryptoMarket):
                    raise Exception('Insufficient funds.')

                # manual price must be an integer or float
                if not isinstance(manualPrice, float) and not isinstance(
                        manualPrice, int):
                    raise TypeError('Optional manual price not numeric.')

                # calculate purchase fees
                fee = cryptoAmount * 0.005
                cryptoAmountMinusFee = cryptoAmount - fee

                price = manualPrice
                # if manualPrice is non-positive retrieve the current live price
                if manualPrice <= 0:
                    resp = requests.get(
                        'https://api-public.sandbox.pro.coinbase.com/products/'
                        + market + '/ticker')
                    if resp.status_code != 200:
                        raise Exception('GET /products/' + market +
                                        '/ticker {}'.format(resp.status_code))
                    resp.raise_for_status()
                    json = resp.json()
                    price = float(json['price'])

                total = price * cryptoAmountMinusFee

                # append dummy order into orders dataframe
                ts = pd.Timestamp.now()
                price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100)
                order = pd.DataFrame([[
                    '', market, 'sell', 'market', cryptoAmountMinusFee,
                    float('{:.8f}'.format(total)), 'done', '{:.8f}'.format(
                        float(price))
                ]],
                                     columns=[
                                         'created_at', 'market', 'action',
                                         'type', 'size', 'value', 'status',
                                         'price'
                                     ],
                                     index=[ts])
                order['created_at'] = order.index
                self.orders = pd.concat(
                    [self.orders, pd.DataFrame(order)], ignore_index=False)

                # update the dummy fiat balance
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'balance'] = self.getBalance(fiatMarket) + total
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'available'] = self.getBalance(fiatMarket) + total

                # update the dummy crypto balance
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'balance'] = self.getBalance(cryptoMarket) - cryptoAmount
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'available'] = self.getBalance(cryptoMarket) - cryptoAmount

        else:
            if self.mode == 'live':
                # connect to Coinbase Pro API live
                model = CBAuthAPI(self.app.getAPIKey(),
                                  self.app.getAPISecret(),
                                  self.app.getAPIPassphrase(),
                                  self.app.getAPIURL())

                # execute a live market sell
                resp = model.marketSell(market,
                                        float(self.getBalance(cryptoMarket)))

                # TODO: not finished
                print(resp)
            else:
                # crypto amount should exceed balance
                if cryptoAmount > self.getBalance(cryptoMarket):
                    raise Exception('Insufficient funds.')

                # manual price must be an integer or float
                if not isinstance(manualPrice, float) and not isinstance(
                        manualPrice, int):
                    raise TypeError('Optional manual price not numeric.')

                # calculate purchase fees
                fee = cryptoAmount * 0.005
                cryptoAmountMinusFee = cryptoAmount - fee

                price = manualPrice
                if manualPrice <= 0:
                    # if manualPrice is non-positive retrieve the current live price
                    resp = requests.get(
                        'https://api-public.sandbox.pro.coinbase.com/products/'
                        + market + '/ticker')
                    if resp.status_code != 200:
                        raise Exception('GET /products/' + market +
                                        '/ticker {}'.format(resp.status_code))
                    resp.raise_for_status()
                    json = resp.json()
                    price = float(json['price'])

                total = price * cryptoAmountMinusFee

                # append dummy order into orders dataframe
                ts = pd.Timestamp.now()
                price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100)
                order = pd.DataFrame([[
                    market, 'sell', 'market', cryptoAmountMinusFee,
                    float('{:.8f}'.format(total)), 'done', price
                ]],
                                     columns=[
                                         'market', 'action', 'type', 'size',
                                         'value', 'status', 'price'
                                     ],
                                     index=[ts])
                order['created_at'] = order.index
                self.orders = pd.concat(
                    [self.orders, pd.DataFrame(order)], ignore_index=False)

                # update the dummy fiat balance
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'balance'] = self.getBalance(fiatMarket) + total
                self.balance.loc[
                    self.balance['currency'] == fiatMarket,
                    'available'] = self.getBalance(fiatMarket) + total

                # update the dummy crypto balance
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'balance'] = self.getBalance(cryptoMarket) - cryptoAmount
                self.balance.loc[
                    self.balance['currency'] == cryptoMarket,
                    'available'] = self.getBalance(cryptoMarket) - cryptoAmount
Example #18
0
class Exchange:
    def __init__(self, default):
        self.client = Client(binance_api_key, binance_api_secret)
        self.storage_ticker = default

    def get_asset_price(self, asset_name):
        if asset_name != "USDT":
            ticker = self.client.get_ticker(symbol=asset_name +
                                            self.storage_ticker)
            return ticker["lastPrice"]
        else:
            return 1

    def buy_asset(self, asset_name):
        if asset_name != self.storage_ticker:
            # Print on console buy order
            print("Buying asset: %s" % asset_name)

            # Set asset name according mapper CoinMarketCap vs Binance
            asset_name = self.mapper_asset_name(asset_name)

            # Get free balance to trade
            result = self.client.get_asset_balance(asset=self.storage_ticker)
            free_balance = result["free"]

            # Get price asset vs storage ticker
            result = self.client.get_symbol_ticker(symbol=asset_name +
                                                   self.storage_ticker)
            price = result["price"]

            # Max quantity to buy
            quantity = (float(free_balance) / float(price))

            factor = 1 * math.pow(10, self.mapper_min_quantity(asset_name))
            quantity = math.floor(quantity * factor) / factor
            print("quantity round %s" % quantity)

            # Submit a buy order
            self.client.order_market_buy(symbol=asset_name +
                                         self.storage_ticker,
                                         quantity=quantity)

    def sell_all_assets(self):
        response = self.client.get_account()
        balance_lenght = len(response["balances"])
        for i in range(0, balance_lenght):
            item = response["balances"][i]
            if item["asset"] != self.storage_ticker and float(
                    item["free"]) > 0:
                try:
                    factor = 1 * math.pow(
                        10, self.mapper_min_quantity(item["asset"]))
                    quantity = math.floor(
                        float(item["free"]) * factor) / factor

                    if quantity > 0:
                        # Print on console buy order
                        print("Selling asset: %s" % (item["asset"]))
                        self.client.order_market_sell(symbol=item["asset"] +
                                                      self.storage_ticker,
                                                      quantity=quantity)
                except Exception as e:
                    print(e)
                    pass

    def mapper_asset_name(self, asset_name):
        if asset_name == "USDT":
            return "USDT"
        elif asset_name == "BTC":
            return "BTC"
        elif asset_name == "ETH":
            return "ETH"
        elif asset_name == "XRP":
            return "XRP"
        elif asset_name == "BCH":
            return "BCC"
        elif asset_name == "ADA":
            return "ADA"
        elif asset_name == "LTC":
            return "LTC"
        elif asset_name == "XEM":
            return "XEM"
        elif asset_name == "NEO":
            return "NEO"
        elif asset_name == "XLM":
            return "XLM"
        elif asset_name == "MIOTA":
            return "IOTA"
        elif asset_name == "BNB":
            return "BNB"

    def mapper_min_quantity(self, asset_name):
        if asset_name == "USDT":
            return 4
        elif asset_name == "BTC":
            return 6
        elif asset_name == "ETH":
            return 5
        elif asset_name == "XRP":
            return 0
        elif asset_name == "BCC":
            return 5
        elif asset_name == "ADA":
            return 0
        elif asset_name == "LTC":
            return 5
        elif asset_name == "XEM":
            return 0
        elif asset_name == "NEO":
            return 3
        elif asset_name == "XLM":
            return 1
        elif asset_name == "IOTA":
            return 0
        elif asset_name == "BNB":
            return 2
Example #19
0
class BinaceConnector():
    def __init__(self):
        self.client = Client(clientConfig.api_key,
                             clientConfig.api_secret,
                             tld='us')
        self.wsclient = None

    # setup a websocket client
    def setupWebsocket(self):
        self.wsclient = BinanceSocketManager(self.client)
        return self.wsclient

    # get a feed in real time for all prices
    def startAllBookTicker(self, callback):
        return self.client.start_book_ticker_socket(callback)

    # setup depth manager
    def setupDepthCacheManager(self, coin, callback, limit=10):
        return DepthCacheManager(self.client, coin, callback)

    def setupCandleSocket(self, coin, callback, interval):
        self.setupWebsocket()
        return self.wsclient.start_kline_socket(coin,
                                                callback,
                                                interval=interval)

    def setupSocketMultiPlex(self, coins, multimessage):
        self.wsclient.start_multiplex_socket(coins, multimessage)
        return self.wsclient

    def getHistoryCandles(self,
                          coin,
                          interval=Client.KLINE_INTERVAL_5MINUTE,
                          len='100 hour ago EST'):
        return self.client.get_historical_klines(coin, interval, len)

    # setup multiple cache managers:
    def setupDepthForSymbolsUSDT(self, symbols, callback):
        manager = self.setupWebsocket()
        streams = []
        for coin in symbols:
            streams.append(
                DepthCacheManager(self.client,
                                  coin + "USDT",
                                  callback,
                                  bm=manager,
                                  ws_interval=30))
        return streams

    # return the current coin price
    def getCoinPrice(self, coin):
        for sym in (self.client.get_all_tickers()):
            if sym['symbol'] == coin:
                return (sym['price'])

    # get all orders for a coin
    def getCoinOrder(self, coin, amount):
        return self.client.get_all_orders(coin=coin, limit=amount)

    #get current spending limit
    def getUSD(self):
        return float(self.client.get_asset_balance(asset='USD')['free'])

    #get current coin holding
    def getCoinBalance(self, coin):
        return self.client.get_asset_balance(asset=coin)

    #returns a list of what I own
    def getAccountBalances(self):
        coins = []
        for coin in self.client.get_account()['balances']:
            if float(coin['free']) > 0 or float(coin['locked']) > 0:
                coins.append(coin)
        return coins

    #gets assets, asset totals, and totalBalance
    def getAssetPrices(self):
        owned = (self.getAccountBalances())
        for x in owned:
            asset = x['asset']
            if asset != 'USD':
                price = float((self.getCoinPrice(f"{x['asset']}USD")))
                total = (float(x['free']) + float(x['locked'])) * price
                x['price'] = price
                x['total'] = total
        return owned

    # get all information about a coin
    def getCoinInfo(self, coin):
        return self.client.get_symbol_info(coin)

    # get the minimum amount of coin that can be purchased and the increments
    def getCoinPrecision(self, coin):
        return self.client.get_symbol_info(coin)['baseAssetPrecision']

    # get our current buying power
    def getPower(self):
        return self.getUSD()

    # buy a coin at the current market value
    def buyMarket(self, coin, amount):
        return self.client.order_market_buy(symbol=coin, quantity=amount)

    # sell a coin at the current market value
    def sellMarket(self, coin, amount):
        return self.client.order_market_sell(symbol=coin, quantity=amount)

    # sell a coin at the current market value
    def sellLimit(self, coin, amount, limit):
        info = self.getCoinInfo(coin)
        quotePrecision = int(info['quotePrecision'])
        limit = round(limit, quotePrecision - 2)
        return self.client.order_limit_sell(symbol=coin,
                                            quantity=amount,
                                            price=limit)

    # place a stoploss
    def stopLoss(self, coin, stop, limit, position):
        multiplierUp = 0
        multiplierDown = 0

        info = self.getCoinInfo(coin)

        quotePrecision = int(info['quotePrecision'])

        for filt in info['filters']:
            if filt['filterType'] == "PERCENT_PRICE":
                multiplierUp = float(filt['multiplierUp'])
                multiplierDown = float(filt['multiplierDown'])

        currentPrice = float(self.getCoinPrice(coin))

        upPrice = currentPrice * multiplierUp
        downPrice = currentPrice * multiplierDown

        stoper = round(stop, quotePrecision - 2)
        limiter = round(limit, quotePrecision - 2)

        print(f"up {upPrice}")
        print(f"down {downPrice}")
        print(f"stop {stoper}")
        print(f"limit {limiter}")

        if stoper > upPrice:
            print("TOHIGH")
            return None
        if stoper < downPrice:
            print("TOLOW")
            return None

        if limiter > upPrice:
            print("TOHIGH")
            return None
        if limiter < downPrice:
            print("TOLOW")
            return None

        output = self.client.create_order(symbol=coin,
                                          timeInForce='GTC',
                                          type='STOP_LOSS_LIMIT',
                                          quantity=position,
                                          side="sell",
                                          price=limiter,
                                          stopPrice=stoper)
        return output

    #test order will return {} if great success
    def testOrder(self, coin, act, amount):
        print(
            self.client.create_test_order(symbol=coin,
                                          side=act,
                                          type=ORDER_TYPE_MARKET,
                                          quantity=amount))

    # check the status of an order
    def getSellAmount(self, coin, order):
        order = self.client.get_order(symbol=coin, origClientOrderId=order)
        #add logic here for complete
        return round(float(order['cummulativeQuoteQty']), 2)

    def checkStatus(self, coin, order):
        order = self.client.get_order(symbol=coin, origClientOrderId=order)
        #add logic here for complete
        return order['status']

    # cancel an order that might get hung
    def cancelOrder(self, coin, order):
        return self.client.cancel_order(symbol=coin, origClientOrderId=order)

    def getBook(self):
        return self.client.get_orderbook_tickers()

    def getAssets(self):
        return self.client.get_account()
Example #20
0
class Binance(Exchange):

    ORDER_KEYS_MAP = {
            's': 'symbol',
            'X': 'status',
            'i': 'orderId',
            'p': 'price',
            'q': 'origQty',
            'S': 'side',
            }

    def __init__(self, *args, **kwargs):
        super(Binance, self).__init__('binance', *args, **kwargs)

        key = self.config['api_key']
        secret = self.config['api_secret']

        self.client = BinanceClient(key, secret)
        self._debpth_data_buffer = Queue()
        self._load_depth_snapshot_thread = None

    def new_order(self, amount, price):
        type = 'LIMIT'
        side = 'BUY' if amount > 0 else 'SELL'
        data = {
            'price': price,
            'quantity': abs(amount),
            'symbol': self.target_pair,
            'timeInForce': 'FOK',
            'side': side,
            'type': type,
        }
        log.critical('new_order: %s', data)
        ret = self.client.create_order(**data)
        log.critical('new_order: %s', ret)
        if ret['status'] == 'FILLED':
            return True
        else:
            return ret

    def withdraw(self, token_type, amount, address):
        ret = self.client.withdraw(token_type, amount, address)
        return ret

    def _update_order_book_list(self, book, list):
        for item in list:
            price, amount, _ = item
            price = float(price)
            amount = float(amount)
            if amount == 0:
                book.remove(price)
            else:
                book.add_or_update(price, amount)

    def _update_order_book(self, bid_list, ask_list):
        log.debug('_update_order_book')
        self._update_order_book_list(self.bids_book, bid_list)
        self._update_order_book_list(self.asks_book, ask_list)

        self.notify_order_book_update()

    def _process_order(self, data, keys=None):
        if keys is not None:
            data = utils.map_dict(data, keys)
        symbol = data['symbol']
        if symbol != self.target_pair:
            return
        status = data['status']
        remove = status in 'CANCELED FILLED EXPIRED CANCELED'
        self.update_order_list(data['orderId'], float(data['price']), float(data['origQty']), data['side'] == 'SELL', remove)

    def _process_assets(self, balances, keys=None):
        for item in balances:
            if keys is not None:
                item = utils.map_dict(item, keys)
            free_amount_str = item['free']
            if free_amount_str == '0.00000000':
                continue
            free_amount = float(free_amount_str)
            self.asset_list[item['asset']] = free_amount
        self.notify_account_update()

    def connect(self):
        self._load_init_data()
        self._new_queue_poller(self.client.queue, self.process_message)
        self.client.start_depth_socket(self.target_pair)
        self.client.start_user_socket()

    def _load_init_data(self):
        open_orders = self.client.get_open_orders()
        for open_order in open_orders:
            self._process_order(open_order)
        log.info('_load_init_data, open_orders: %s', open_orders)

        my_account = self.client.get_account()
        log.info('_load_init_data, my_account: %s', my_account)
        self._process_assets(my_account['balances'])

    def _load_depth_data(self):
        depth_data = self.client.get_order_book(symbol=self.target_pair)
        log.info('_load_init_data, depth_data: %s', depth_data)
        last_update_id = depth_data['lastUpdateId'];
        self._update_order_book(depth_data['bids'], depth_data['asks'])
        while not self._debpth_data_buffer.empty():
            item = self._debpth_data_buffer.get()
            if item['u'] <= last_update_id:
                log.info('_load_init_data, skip: %s', item)
                continue
            else:
                self._update_order_book(item['b'], item['a'])
        self.order_book_ready.set()
        log.info('_load_depth_data finish');

    def _new_queue_poller(self, queue, handler):
        poller = QueuePoller(queue, handler)
        poller.start()
        self.queue_poller_list.append(poller)

    def process_message(self, msg):
        log.debug('process_message: %s', msg)
        payload, ts = msg
        event = payload['e']
        if event == 'executionReport':
            self._process_order(payload, self.ORDER_KEYS_MAP)
        if event == 'outboundAccountInfo':
            self._process_assets(payload['B'], {'f': 'free', 'a': 'asset'})
        if event == 'depthUpdate':
            if self.order_book_ready.is_set():
                self._update_order_book(payload['b'], payload['a'])
            else:
                self._debpth_data_buffer.put(payload)
                if self._debpth_data_buffer.qsize() >= 2 and self._load_depth_snapshot_thread is None:
                    self._load_depth_snapshot_thread = Thread(target=self._load_depth_data)
                    self._load_depth_snapshot_thread.start()

    def disconnect(self):
        self.client.close()

        for poller in self.queue_poller_list:
            poller.join()
Example #21
0
from private.keys import binance_paper_keys, connection_strings
import pandas as pd
import os
from binance.websockets import BinanceSocketManager
from twisted.internet import reactor

##
binance_client = Client(binance_paper_keys.get('api'),
                        binance_paper_keys.get('secret_key'))

##
binance_client.API_URL = 'https://testnet.binance.vision/api'

##
# get balances for all assets & some account information
pprint(binance_client.get_account())

# %%
# get balance for a specific asset only (BTC)
pprint(binance_client.get_asset_balance(asset='BTC'))

# %%
# get latest price from Binance API
btc_price = binance_client.get_symbol_ticker(symbol="BTCUSDT")
# print full output (dictionary)
print(btc_price)

# %%
hist = binance_client.get_historical_klines(
    'BTCUSDT',
    binance_client.KLINE_INTERVAL_5MINUTE,
Example #22
0
class BinanceClient(object):

    def __init__(self, app=None):
        self.app = app
        if app:
            self.init_app(app)

    def init_app(self, app):
        self.app = app
        self.name = 'binance'
        app.extensions[self.name] = self
        self.api_key = app.config[self.name.upper()+'_API_KEY']
        self.api_secret = app.config[self.name.upper()+'_API_SECRET']
        self.client = Client(self.api_key, self.api_secret)

    def get_balances(self):

        def append_total(d):
            d['total'] = float(d['free']) + float(d['locked'])
            return d

        def standardize(d):
            return {
                'exchange': self.name,
                'asset': d['asset'],
                'balance': d['total']
            }
        return filter(
            lambda x: x['balance'] > 0.0,
            [standardize(append_total(x)) for x in self.client.get_account()['balances']]
        )

    def get_trades(self, pair):
        params = {
            'symbol': pair.upper(),
            'fromId': 0
        }
        out = []
        while True:
            data = self.client.get_my_trades(**params)
            if data is None or len(data) == 0:
                return out
            out.extend(data)
            params['fromId'] = data[-1]['id'] + 1

    def get_transfers(self):
        def with_type(item, type):
            item['type'] = type
            return item
        out = []
        data = self.client.get_deposit_history()
        if data and 'depositList' in data:
            out.extend([
                with_type(item, 'Deposit')
                for item in data['depositList']
            ])
        data = self.client.get_withdraw_history()
        if data and 'withdrawList' in data:
            out.extend([
                with_type(item, 'Withdraw')
                for item in data['withdrawList']
            ])
        return out
Example #23
0
class AuthAPI(AuthAPIBase):
    def __init__(self,
                 api_key: str = '',
                 api_secret: str = '',
                 api_url: str = 'https://api.binance.com') -> None:
        """Binance API object model
    
        Parameters
        ----------
        api_key : str
            Your Binance account portfolio API key
        api_secret : str
            Your Binance account portfolio API secret
        """

        # options
        self.debug = False
        self.die_on_api_error = False

        valid_urls = [
            'https://api.binance.com/', 'https://api.binance.us/',
            'https://testnet.binance.vision/api/'
        ]

        if len(api_url) > 1 and api_url[-1] != '/':
            api_url = api_url + '/'

        # validate Binance API
        if api_url not in valid_urls:
            raise ValueError('Binance API URL is invalid')

        # validates the api key is syntactically correct
        p = re.compile(r"^[A-z0-9]{64,64}$")
        if not p.match(api_key):
            self.handle_init_error('Binance API key is invalid')

        # validates the api secret is syntactically correct
        p = re.compile(r"^[A-z0-9]{64,64}$")
        if not p.match(api_secret):
            self.handle_init_error('Binance API secret is invalid')

        self.mode = 'live'  # TODO: check if this needs to be set here
        self.api_url = api_url
        self.api_key = api_key
        self.api_secret = api_secret

        for i in range(10):
            try:
                sys.tracebacklimit = 0
                if 'api.binance.us' in api_url:
                    self.client = Client(self.api_key,
                                         self.api_secret, {
                                             'verify': False,
                                             'timeout': 20
                                         },
                                         tld='us')
                else:
                    self.client = Client(self.api_key, self.api_secret, {
                        'verify': False,
                        'timeout': 20
                    })
                break
            except Exception as e:
                if i == 9:
                    raise SystemExit(
                        "Can not create instance of AuthAPI client.")
                Logger.error('Exception: ' + str(e))
                Logger.error(
                    'Error on creating instance of AuthAPI Client. Trying again... Attempt: '
                    + str(i))
                sleep(0.1)

        sys.tracebacklimit = 1

    def handle_init_error(self, err: str) -> None:
        if self.debug:
            raise TypeError(err)
        else:
            raise SystemExit(err)

    def getClient(self) -> Client:
        return self.client

    def getAccount(self):
        """Retrieves a specific account"""
        account = self.client.get_account()
        if 'balances' in account:
            df = pd.DataFrame(account['balances'])
            df = df[(df['free'] != '0.00000000') & (df['free'] != '0.00')]
            df['free'] = df['free'].astype(float)
            df['locked'] = df['locked'].astype(float)
            df['balance'] = df['free'] - df['locked']
            df.columns = ['currency', 'available', 'hold', 'balance']
            df = df[['currency', 'balance', 'hold', 'available']]
            df = df.reset_index(drop=True)
            return df
        else:
            return 0.0

    def getFees(self, market: str = '') -> pd.DataFrame:
        if market != '':
            resp = self.client.get_trade_fee(symbol=market)
            if 'tradeFee' in resp and len(resp['tradeFee']) > 0:
                df = pd.DataFrame(resp['tradeFee'][0], index=[0])
                df['usd_volume'] = None
                df.columns = [
                    'maker_fee_rate', 'market', 'taker_fee_rate', 'usd_volume'
                ]
                return df[[
                    'maker_fee_rate', 'taker_fee_rate', 'usd_volume', 'market'
                ]]
            return pd.DataFrame(
                columns=['maker_fee_rate', 'taker_fee_rate', 'market'])
        else:
            resp = self.client.get_trade_fee()
            if 'tradeFee' in resp:
                df = pd.DataFrame(resp['tradeFee'])
                df['usd_volume'] = None
                df.columns = [
                    'maker_fee_rate', 'market', 'taker_fee_rate', 'usd_volume'
                ]
                return df[[
                    'maker_fee_rate', 'taker_fee_rate', 'usd_volume', 'market'
                ]]
            return pd.DataFrame(
                columns=['maker_fee_rate', 'taker_fee_rate', 'market'])

    def getMakerFee(self, market: str = '') -> float:
        if market == '':
            fees = self.getFees()
        else:
            fees = self.getFees(market)

        if len(fees) == 0 or 'maker_fee_rate' not in fees:
            Logger.error(
                f"error: 'maker_fee_rate' not in fees (using {DEFAULT_MAKER_FEE_RATE} as a fallback)"
            )
            return DEFAULT_MAKER_FEE_RATE

        if market == '':
            return fees
        else:
            return float(fees['maker_fee_rate'].to_string(index=False).strip())

    def getTakerFee(self, market: str = '') -> float:
        if market == '':
            return DEFAULT_TAKER_FEE_RATE
        else:
            fees = self.getFees(market)

        if len(fees) == 0 or 'taker_fee_rate' not in fees:
            Logger.error(
                f"error: 'taker_fee_rate' not in fees (using {DEFAULT_TAKER_FEE_RATE} as a fallback)"
            )
            return DEFAULT_TAKER_FEE_RATE

        return float(fees['taker_fee_rate'].to_string(index=False).strip())

    def __convertStatus(self, val: str) -> str:
        if val == 'filled':
            return 'done'
        else:
            return val

    def getOrders(self,
                  market: str = '',
                  action: str = '',
                  status: str = 'all') -> pd.DataFrame:
        """Retrieves your list of orders with optional filtering"""

        # if market provided
        if market != '':
            # validates the market is syntactically correct
            if not self._isMarketValid(market):
                raise ValueError('Binance market is invalid.')

        # if action provided
        if action != '':
            # validates action is either a buy or sell
            if not action in ['buy', 'sell']:
                raise ValueError('Invalid order action.')

        # validates status is either open, pending, done, active, or all
        if not status in ['open', 'pending', 'done', 'active', 'all']:
            raise ValueError('Invalid order status.')

        resp = self.client.get_all_orders(symbol=market)
        if len(resp) > 0:
            df = pd.DataFrame(resp)
        else:
            df = pd.DataFrame()

        if len(df) == 0:
            return pd.DataFrame()

        # replace null NaN values with 0
        df.fillna(0, inplace=True)

        df = df[[
            'time', 'symbol', 'side', 'type', 'executedQty',
            'cummulativeQuoteQty', 'status'
        ]]
        df.columns = [
            'created_at', 'market', 'action', 'type', 'filled', 'size',
            'status'
        ]
        df['created_at'] = df['created_at'].apply(lambda x: int(str(x)[:10]))
        df['created_at'] = df['created_at'].astype("datetime64[s]")
        df['size'] = df['size'].astype(float)
        df['filled'] = df['filled'].astype(float)
        df['action'] = df['action'].str.lower()
        df['type'] = df['type'].str.lower()
        df['status'] = df['status'].str.lower()
        df['price'] = df['size'] / df['filled']

        # pylint: disable=unused-variable
        for k, v in df.items():
            if k == 'status':
                df[k] = df[k].map(self.__convertStatus)

        if action != '':
            df = df[df['action'] == action]
            df = df.reset_index(drop=True)

        if status != 'all' and status != '':
            df = df[df['status'] == status]
            df = df.reset_index(drop=True)

        return df

    def marketBuy(self, market: str = '', quote_quantity: float = 0) -> list:
        """Executes a market buy providing a funding amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError('Binance market is invalid.')

        # validates quote_quantity is either an integer or float
        if not isinstance(quote_quantity, int) and not isinstance(
                quote_quantity, float):
            raise TypeError('The funding amount is not numeric.')

        try:
            current_price = self.getTicker(market)[1]

            base_quantity = np.divide(quote_quantity, current_price)

            df_filters = self.getMarketInfoFilters(market)
            step_size = float(df_filters.loc[df_filters['filterType'] ==
                                             'LOT_SIZE']['stepSize'])
            precision = int(round(-math.log(step_size, 10), 0))

            # remove fees
            base_quantity = base_quantity - (base_quantity *
                                             self.getTradeFee(market))

            # execute market buy
            stepper = 10.0**precision
            truncated = math.trunc(stepper * base_quantity) / stepper
            Logger.info('Order quantity after rounding and fees: ' +
                        str(truncated))

            return self.client.order_market_buy(symbol=market,
                                                quantity=truncated)
        except Exception as err:
            ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S")
            Logger.error(ts + ' Binance ' + ' marketBuy ' + str(err))
            return []

    def marketSell(self, market: str = '', base_quantity: float = 0) -> list:
        """Executes a market sell providing a crypto amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError('Binance market is invalid.')

        if not isinstance(base_quantity, int) and not isinstance(
                base_quantity, float):
            raise TypeError('The crypto amount is not numeric.')

        try:
            df_filters = self.getMarketInfoFilters(market)
            step_size = float(df_filters.loc[df_filters['filterType'] ==
                                             'LOT_SIZE']['stepSize'])
            precision = int(round(-math.log(step_size, 10), 0))

            # remove fees
            base_quantity = base_quantity - (base_quantity *
                                             self.getTradeFee(market))

            # execute market sell
            stepper = 10.0**precision
            truncated = math.trunc(stepper * base_quantity) / stepper
            Logger.info('Order quantity after rounding and fees: ' +
                        str(truncated))
            return self.client.order_market_sell(symbol=market,
                                                 quantity=truncated)
        except Exception as err:
            ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S")
            Logger.error(ts + ' Binance ' + ' marketSell ' + str(err))
            return []

    def getTradeFee(self, market: str) -> float:
        resp = self.client.get_trade_fee(symbol=market,
                                         timestamp=self.getTime())

        if 'tradeFee' not in resp:
            Logger.info('*** getTradeFee(' + market +
                        ') - missing "tradeFee" ***')
            Logger.info(resp)
        else:
            if len(resp['tradeFee']) == 0:
                Logger.info('*** getTradeFee(' + market +
                            ') - "tradeFee" empty ***')
                Logger.info(resp)
            else:
                if 'taker' not in resp['tradeFee'][0]:
                    Logger.info('*** getTradeFee(' + market +
                                ') - missing "trader" ***')
                    Logger.info(resp)

        if 'success' in resp:
            return resp['tradeFee'][0]['taker']
        else:
            return DEFAULT_TRADE_FEE_RATE

    def getMarketInfo(self, market: str) -> dict:
        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise TypeError('Binance market required.')

        return self.client.get_symbol_info(symbol=market)

    def getMarketInfoFilters(self, market: str) -> pd.DataFrame:
        return pd.DataFrame(
            self.client.get_symbol_info(symbol=market)['filters'])

    def getTicker(self, market: str) -> tuple:
        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise TypeError('Binance market required.')

        resp = self.client.get_symbol_ticker(symbol=market)

        if 'price' in resp:
            return (self.getTime().strftime('%Y-%m-%d %H:%M:%S'),
                    float('{:.8f}'.format(float(resp['price']))))

        now = datetime.today().strftime('%Y-%m-%d %H:%M:%S')
        return (now, 0.0)

    def getTime(self) -> datetime:
        """Retrieves the exchange time"""

        try:
            resp = self.client.get_server_time()
            epoch = int(str(resp['serverTime'])[0:10])
            return datetime.fromtimestamp(epoch)
        except:
            return None
Example #24
0
class Binance(Broker):
    def __init__(self,
                 base_currency,
                 market_currency,
                 pair_code,
                 api_key=None,
                 api_secret=None):
        super().__init__(base_currency, market_currency, pair_code)

        self.client = Client(
            api_key if api_key else config.Binance_API_KEY,
            api_secret if api_secret else config.Binance_SECRET_TOKEN)

    def _place_order(self, amount, price, side):
        order = client.create_order(symbol=self.pair_code,
                                    side=side,
                                    type=ORDER_TYPE_LIMIT,
                                    timeInForce=TIME_IN_FORCE_GTC,
                                    quantity=amount,
                                    price=str(price))
        logging.verbose('_place_order: %s %s' % (side, order))

        return order['orderId']

    def _buy_limit(self, amount, price):
        """Create a buy limit order"""
        return self._place_order(amount, price, SIDE_BUY)

    def _sell_limit(self, amount, price):
        """Create a sell limit order"""
        return self._place_order(amount, price, SIDE_SELL)

    def _order_status(self, res):
        resp = {}
        resp['order_id'] = res['orderId']
        resp['amount'] = float(res['origQty'])
        resp['price'] = float(res['price'])
        resp['deal_amount'] = float(res['executedQty'])
        resp['avg_price'] = float(res['price'])

        if res['status'] == ORDER_STATUS_NEW or res[
                'status'] == ORDER_STATUS_PARTIALLY_FILLED:
            resp['status'] = 'OPEN'
        else:
            resp['status'] = 'CLOSE'

        return resp

    def _get_order(self, order_id):
        res = self.client.get_order(orderId=int(order_id),
                                    symbol=self.pair_code)
        logging.verbose('get_order: %s' % res)

        assert str(res['symbol']) == str(self.pair_code)
        assert str(res['orderId']) == str(order_id)
        return self._order_status(res['data'])

    def _cancel_order(self, order_id):
        res = self.client.cancel_order(orderId=int(order_id),
                                       symbol=self.pair_code)
        logging.verbose('cancel_order: %s' % res)

        assert str(res['orderId']) == str(order_id)
        return True

    def _get_balances(self):
        """Get balance"""
        res = self.client.get_account()
        logging.debug("get_balances: %s" % res)

        balances = res['balances']

        for entry in balances:
            currency = entry['asset'].upper()
            if currency not in ('BTC', 'BCH', 'USD'):
                continue

            if currency == 'BCH':
                self.bch_available = float(entryfree['free'])
                self.bch_balance = float(entry['amount']) + float(
                    entry['locked'])

            elif currency == 'BTC':
                self.btc_available = float(entry['free'])
                self.btc_balance = float(entry['amount']) + float(
                    entry['locked'])

        return res
Example #25
0
def get_account_balances(api_key, api_secret):
    client = Client(api_key, api_secret)
    account = client.get_account()
    balances = account['balances']
    return filter(lambda bl: float(bl['free']) > 0.0, balances)
Example #26
0
class magic_bomb:
    def __init__(self, api_key, api_secret):
        self.api_key = api_key
        self.api_secret = api_secret
        try:
            self.client = Client(self.api_key, self.api_secret)
            print("Client authenticated")
        except BinanceAPIException as e:
            print(e.status_code)
            print(e.message)

    def account(self):
        account = self.client.get_account()
        all_values = []
        account_values = []
        for value in account['balances']:
            if (float(value['free']) != float(0)) or (float(value['locked']) !=
                                                      float(0)):
                account_values.append(value)
            all_values.append(value['asset'])
        # return json.dumps({"all_values": all_values,"count_values": account_values})
        return all_values

    # def get_pairs(self):
    def get_historical_trades(self):
        trades = self.client.get_historical_trades(symbol='CHZBUSD')
        print(trades)

    def get_all_orders(self, pair):
        orders = self.client.get_all_orders(symbol=pair)
        success_orders = []
        for order in orders:
            if order['status'] != 'CANCELED':
                success_orders.append(order)
        return success_orders

    def get_balance_pair(self):
        orders = self.get_all_orders('ALL')
        print(orders)
        # BUYS=0
        # SELLS=0
        # for order in orders:
        #     if order['side'] == 'SELL':
        #         SELLS = SELLS + (float(order['executedQty'])*float(order['price']))
        #     else:
        #         if order['side'] == 'BUY':
        #             BUYS = BUYS + (float(order['executedQty'])*float(order['price']))
        #         else:
        #             print("hay mas mierda")
        # balance = SELLS - BUYS
        # return balance
    def get_coins(self):
        details = self.client.get_all_tickers()
        coins = []
        for coin in details:
            coins.append(coin['symbol'])
        return coins

    def total_balance(self):
        pairs = self.get_coins()
        # print(pairs)
        # print(values['all_values'])
        # pairs = values['all_values']
        balances = []
        for pair in pairs:
            print('pair: {}', pair)
            balances.append(self.get_balance_pair(pair))
        return balances

    def get_asset_dividend_histrory(self):
        print(self.client.get_asset_dividend_history())

    def get_my_trades(self):
        print(self.client.get_my_trades(symbol=''))

    def get_historical_trades(self, pair):
        trades = self.client.get_historical_trades(symbol=pair)
        return (trades)

    def get_historical_klines(self, pair, period):
        klines = self.client.get_historical_klines(
            pair, Client.KLINE_INTERVAL_1MINUTE, period)
        clean_klines = []
        for kline in klines:
            clean_klines.append({
                'date':
                datetime.fromtimestamp(int(kline[0]) / 1000),
                'high':
                kline[2],
                'low':
                kline[3],
                'avg': (float(kline[2]) + float(kline[3])) / 2
            })
        return clean_klines
Example #27
0
def spread_order(api_key, api_secret, symbol, amount, side,
                 first_order_percentage, spread, spread_step_size_percentage,
                 dry_mode):

    list = ['starting run']

    client = Client(api_key, api_secret)

    side_str = 'selling' if side == 'SELL' else 'buying'

    account = client.get_account()

    symbol_info = client.get_symbol_ticker(symbol=symbol)
    market_price = symbol_info['price']
    list.append('current market price: ' + market_price)

    precision = common.get_symbol_precision(client, symbol)
    quantity_first_order = round(
        Decimal((first_order_percentage / 100.0) * amount), precision)
    quantity_spread = amount - quantity_first_order

    list.append(side_str + ' ' + str(quantity_first_order) + ' ' + symbol +
                ' at MARKET price')
    list.append(side_str + ' ' + str(quantity_spread) + ' ' + symbol +
                ' at SPREAD')

    if (quantity_first_order > 0):
        #1. Place Market BUY order
        if not dry_mode:
            client.create_order(symbol=symbol,
                                type=Client.ORDER_TYPE_MARKET,
                                side=side,
                                quantity=quantity_first_order)

    if (quantity_spread > 0):

        base = quantity_spread / (spread - 1)
        curr = quantity_spread

        for i in range(1, spread):

            quantity_spread_i = base - (i * random.uniform(0.01, 0.03))
            quantity_spread_i_rounded = round(Decimal(quantity_spread_i),
                                              precision)

            if i == spread - 1:
                #last cycle - adjust quantity
                if curr != quantity_spread_i:
                    quantity_spread_i_rounded = round(Decimal(curr), precision)

            curr = curr - quantity_spread_i_rounded

            price_i = float(market_price)
            if side == Client.SIDE_SELL:
                price_i += i * (float(market_price) *
                                (spread_step_size_percentage / 100.0)
                                )  #sell above market price
            else:
                price_i -= i * (float(market_price) *
                                (spread_step_size_percentage / 100.0)
                                )  #buy below market price

            price_i_rounded = float("{0:.7f}".format(price_i))

            list.append(side_str + ' ' + str(quantity_spread_i_rounded) + ' ' +
                        symbol[0:3] + ' at ' + str(price_i_rounded) + ' price')

            if not dry_mode:
                client.create_order(symbol=symbol,
                                    quantity=quantity_spread_i_rounded,
                                    side=side,
                                    type=Client.ORDER_TYPE_LIMIT,
                                    price=price_i_rounded,
                                    timeInForce=Client.TIME_IN_FORCE_GTC)

    return list
Example #28
0
class BinanceAPIManager:
    def __init__(self, config: Config, db: Database, logger: Logger):
        self.binance_client = Client(
            config.BINANCE_API_KEY,
            config.BINANCE_API_SECRET_KEY,
            tld=config.BINANCE_TLD,
        )
        self.db = db
        self.logger = logger

    @cached(cache=TTLCache(maxsize=1, ttl=43200))
    def get_trade_fees(self) -> Dict[str, float]:
        return {
            ticker["symbol"]: ticker["taker"]
            for ticker in self.binance_client.get_trade_fee()["tradeFee"]
        }

    @cached(cache=TTLCache(maxsize=1, ttl=60))
    def get_using_bnb_for_fees(self):
        return self.binance_client.get_bnb_burn_spot_margin()["spotBNBBurn"]

    def get_fee(self, origin_coin: Coin, target_coin: Coin, selling: bool):
        base_fee = self.get_trade_fees()[origin_coin + target_coin]
        if not self.get_using_bnb_for_fees():
            return base_fee
        # The discount is only applied if we have enough BNB to cover the fee
        amount_trading = (self._sell_quantity(origin_coin.symbol,
                                              target_coin.symbol)
                          if selling else self._buy_quantity(
                              origin_coin.symbol, target_coin.symbol))
        fee_amount = amount_trading * base_fee * 0.75
        if origin_coin.symbol == "BNB":
            fee_amount_bnb = fee_amount
        else:
            origin_price = self.get_market_ticker_price(origin_coin +
                                                        Coin("BNB"))
            if origin_price is None:
                return base_fee
            fee_amount_bnb = fee_amount * origin_price
        bnb_balance = self.get_currency_balance("BNB")
        if bnb_balance >= fee_amount_bnb:
            return base_fee * 0.75
        return base_fee

    def get_all_market_tickers(self) -> AllTickers:
        """
        Get ticker price of all coins
        """
        return AllTickers(self.binance_client.get_all_tickers())

    def get_market_ticker_price(self, ticker_symbol: str):
        """
        Get ticker price of a specific coin
        """
        for ticker in self.binance_client.get_symbol_ticker():
            if ticker["symbol"] == ticker_symbol:
                return float(ticker["price"])
        return None

    def get_currency_balance(self, currency_symbol: str):
        """
        Get balance of a specific coin
        """
        for currency_balance in self.binance_client.get_account()["balances"]:
            if currency_balance["asset"] == currency_symbol:
                return float(currency_balance["free"])
        return None

    def retry(self, func, *args, **kwargs):
        time.sleep(1)
        attempts = 0
        while attempts < 20:
            try:
                return func(*args, **kwargs)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info("Failed to Buy/Sell. Trying Again.")
                if attempts == 0:
                    self.logger.info(e)
                attempts += 1
        return None

    def get_symbol_filter(self, origin_symbol: str, target_symbol: str,
                          filter_type: str):
        return next(_filter for _filter in self.binance_client.get_symbol_info(
            origin_symbol + target_symbol)["filters"]
                    if _filter["filterType"] == filter_type)

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_alt_tick(self, origin_symbol: str, target_symbol: str):
        step_size = self.get_symbol_filter(origin_symbol, target_symbol,
                                           "LOT_SIZE")["stepSize"]
        if step_size.find("1") == 0:
            return 1 - step_size.find(".")
        return step_size.find("1") - 1

    @cached(cache=TTLCache(maxsize=2000, ttl=43200))
    def get_min_notional(self, origin_symbol: str, target_symbol: str):
        return float(
            self.get_symbol_filter(origin_symbol, target_symbol,
                                   "MIN_NOTIONAL")["minNotional"])

    def wait_for_order(self, origin_symbol, target_symbol, order_id):
        while True:
            try:
                order_status = self.binance_client.get_order(
                    symbol=origin_symbol + target_symbol, orderId=order_id)
                break
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")
                time.sleep(1)

        self.logger.info(order_status)

        while order_status["status"] != "FILLED":
            try:
                order_status = self.binance_client.get_order(
                    symbol=origin_symbol + target_symbol, orderId=order_id)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")
                time.sleep(1)

        return order_status

    def buy_alt(self, origin_coin: Coin, target_coin: Coin,
                all_tickers: AllTickers):
        return self.retry(self._buy_alt, origin_coin, target_coin, all_tickers)

    def _buy_quantity(self,
                      origin_symbol: str,
                      target_symbol: str,
                      target_balance: float = None,
                      from_coin_price: float = None):
        target_balance = target_balance or self.get_currency_balance(
            target_symbol)
        from_coin_price = from_coin_price or self.get_all_market_tickers(
        ).get_price(origin_symbol + target_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(target_balance * 10**origin_tick /
                          from_coin_price) / float(10**origin_tick)

    def _buy_alt(self, origin_coin: Coin, target_coin: Coin, all_tickers):
        """
        Buy altcoin
        """
        trade_log = self.db.start_trade_log(origin_coin, target_coin, False)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)
        from_coin_price = all_tickers.get_price(origin_symbol + target_symbol)

        order_quantity = self._buy_quantity(origin_symbol, target_symbol,
                                            target_balance, from_coin_price)
        self.logger.info(f"BUY QTY {order_quantity}")

        # Try to buy until successful
        order = None
        while order is None:
            try:
                order = self.binance_client.order_limit_buy(
                    symbol=origin_symbol + target_symbol,
                    quantity=order_quantity,
                    price=from_coin_price,
                )
                self.logger.info(order)
            except BinanceAPIException as e:
                self.logger.info(e)
                time.sleep(1)
            except Exception as e:  # pylint: disable=broad-except
                self.logger.info(f"Unexpected Error: {e}")

        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        stat = self.wait_for_order(origin_symbol, target_symbol,
                                   order["orderId"])

        self.logger.info(f"Bought {origin_symbol}")

        trade_log.set_complete(stat["cummulativeQuoteQty"])

        return order

    def sell_alt(self, origin_coin: Coin, target_coin: Coin):
        return self.retry(self._sell_alt, origin_coin, target_coin)

    def _sell_quantity(self,
                       origin_symbol: str,
                       target_symbol: str,
                       origin_balance: float = None):
        origin_balance = origin_balance or self.get_currency_balance(
            origin_symbol)

        origin_tick = self.get_alt_tick(origin_symbol, target_symbol)
        return math.floor(origin_balance * 10**origin_tick) / float(
            10**origin_tick)

    def _sell_alt(self, origin_coin: Coin, target_coin: Coin):
        """
        Sell altcoin
        """
        trade_log = self.db.start_trade_log(origin_coin, target_coin, True)
        origin_symbol = origin_coin.symbol
        target_symbol = target_coin.symbol

        origin_balance = self.get_currency_balance(origin_symbol)
        target_balance = self.get_currency_balance(target_symbol)

        order_quantity = self._sell_quantity(origin_symbol, target_symbol,
                                             origin_balance)
        self.logger.info(f"Selling {order_quantity} of {origin_symbol}")

        self.logger.info(f"Balance is {origin_balance}")
        order = None
        while order is None:
            order = self.binance_client.order_market_sell(
                symbol=origin_symbol + target_symbol, quantity=order_quantity)

        self.logger.info("order")
        self.logger.info(order)

        trade_log.set_ordered(origin_balance, target_balance, order_quantity)

        # Binance server can take some time to save the order
        self.logger.info("Waiting for Binance")

        stat = self.wait_for_order(origin_symbol, target_symbol,
                                   order["orderId"])

        new_balance = self.get_currency_balance(origin_symbol)
        while new_balance >= origin_balance:
            new_balance = self.get_currency_balance(origin_symbol)

        self.logger.info(f"Sold {origin_symbol}")

        trade_log.set_complete(stat["cummulativeQuoteQty"])

        return order
class BinanceExchange(Exchange):
    exchange_name = "Binance"
    isMargin = False

    def __init__(self, apiKey, apiSecret, pairs, name):
        super().__init__(apiKey, apiSecret, pairs, name)

        self.connection = Client(self.api['key'], self.api['secret'])

        symbol_info_arr = self.connection.get_exchange_info()
        dict_symbols_info = {
            item['symbol']: item
            for item in symbol_info_arr["symbols"]
        }
        actual_symbols_info = {
            symbol: dict_symbols_info[symbol]
            for symbol in self.pairs
        }
        self.symbols_info = actual_symbols_info

        self.update_balance()
        self.socket = BinanceSocketManager(self.connection)
        self.socket.start_user_socket(self.on_balance_update)
        self.socket.start()
        self.is_last_order_event_completed = True
        self.step_sizes = {}
        self.balance_updated = True

        for symbol_info in symbol_info_arr['symbols']:
            if symbol_info['symbol'] in self.pairs:
                self.step_sizes[symbol_info['symbol']] = \
                    [f['stepSize'] for f in symbol_info['filters'] if f['filterType'] == 'LOT_SIZE'][0]

    def start(self, caller_callback):
        self.socket.start_user_socket(caller_callback)

    def update_balance(self):
        account_information = self.connection.get_account()
        self.set_balance(account_information['balances'])

    def get_trading_symbols(self):
        symbols = set()
        if not self.symbols_info:
            raise RuntimeError("Cant get exchange info")
        for key, value in self.symbols_info.items():
            symbols.add(value["quoteAsset"])
            symbols.add(value["baseAsset"])
        return symbols

    def set_balance(self, balances):
        symbols = self.get_trading_symbols()
        dict_balances = {item['asset']: item for item in balances}
        actual_balance = {symbol: dict_balances[symbol] for symbol in symbols}
        self.balance = actual_balance

    def on_balance_update(self, upd_balance_ev):
        if upd_balance_ev['e'] == 'outboundAccountInfo':
            balance = []
            for ev in upd_balance_ev['B']:
                balance.append({
                    'asset': ev['a'],
                    'free': ev['f'],
                    'locked': ev['l']
                })
            self.balance.update({item['asset']: item for item in balance})

    def get_open_orders(self):
        orders = self.connection.get_open_orders()
        general_orders = []
        for o in orders:
            quantityPart = self.get_part(o['symbol'], o["origQty"], o['price'],
                                         o['side'])
            general_orders.append(
                Order(o['price'], o["origQty"], quantityPart, o['orderId'],
                      o['symbol'], o['side'], o['type'], self.exchange_name))
        return general_orders

    def _cancel_order(self, order_id, symbol):
        self.connection.cancel_order(symbol=symbol, orderId=order_id)
        self.logger.info(f'{self.name}: Order canceled')

    async def on_cancel_handler(self, event: Actions.ActionCancel):
        try:
            slave_order_id = self._cancel_order_detector(event.price)
            self._cancel_order(slave_order_id, event.symbol)
        except BinanceAPIException as error:
            self.logger.error(f'{self.name}: error {error.message}')
        except:
            self.logger.error(
                f"{self.name}: error in action: {event.name} in slave {self.name}"
            )

    def stop(self):
        self.socket.close()

    def _cancel_order_detector(self, price):
        # detect order id which need to be canceled
        slave_open_orders = self.connection.get_open_orders()
        for ordr_open in slave_open_orders:
            if float(ordr_open['price']) == float(price):
                return ordr_open['orderId']

    def process_event(self, event):
        # return event in generic type from websocket

        # if this event in general type it was send from start function and need call firs_copy
        if 'exchange' in event:
            return event

        if event['e'] == 'outboundAccountPosition':
            self.is_last_order_event_completed = True

        if event['e'] == 'executionReport':
            if event['X'] == 'FILLED':
                return
            elif event['x'] == 'CANCELED':
                return Actions.ActionCancel(event['s'], event['p'], event['i'],
                                            self.exchange_name, event)
            self.last_order_event = event  # store event order_event coz we need in outboundAccountInfo event
            # sometimes can came event executionReport x == filled and x == new together so we need flag
            self.is_last_order_event_completed = False
            return

        elif event['e'] == 'outboundAccountInfo':
            if self.is_last_order_event_completed:
                return

            order_event = self.last_order_event

            if order_event['s'] not in self.pairs:
                return

            if order_event[
                    'o'] == 'MARKET':  # if market order, we haven't price and cant calculate quantity
                order_event['p'] = self.connection.get_ticker(
                    symbol=order_event['s'])['lastPrice']

            # part = self.get_part(order_event['s'], order_event['q'], order_event['p'], order_event['S'])

            self.on_balance_update(event)

            # shortcut mean https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#order-update
            order = Order(
                order_event['p'], order_event['q'],
                self.get_part(order_event['s'], order_event['q'],
                              order_event['p'], order_event['S']),
                order_event['i'], order_event['s'], order_event['S'],
                order_event['o'], self.exchange_name, order_event['P'])
            return Actions.ActionNewOrder(order, self.exchange_name, event)

    async def on_order_handler(self, event: Actions.ActionNewOrder):
        self.create_order(event.order)

    def create_order(self, order):
        """
        :param order:
        """
        quantity = self.calc_quantity_from_part(order.symbol,
                                                order.quantityPart,
                                                order.price, order.side)
        self.logger.info('Slave ' + self.name + ' ' +
                         str(self._get_quote_balance(order.symbol)) + ' ' +
                         str(self._get_base_balance(order.symbol)) +
                         ', Create Order:' + ' amount: ' + str(quantity) +
                         ', price: ' + str(order.price))
        try:
            if order.type == 'STOP_LOSS_LIMIT' or order.type == "TAKE_PROFIT_LIMIT":
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             price=order.price,
                                             quantity=quantity,
                                             timeInForce='GTC',
                                             stopPrice=order.stop)
            if order.type == 'MARKET':
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             quantity=quantity)
            else:
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             quantity=quantity,
                                             price=order.price,
                                             timeInForce='GTC')
            self.logger.info(f"{self.name}: order created")
        except Exception as e:
            self.logger.error(str(e))

    def _get_quote_balance(self, symbol):
        return self.balance[self.symbols_info[symbol]['quoteAsset']]

    def _get_base_balance(self, symbol):
        return self.balance[self.symbols_info[symbol]['baseAsset']]

    def get_part(self, symbol: str, quantity: float, price: float, side: str):
        # get part of the total balance of this coin

        # if order[side] == sell: need obtain coin balance
        if side == 'BUY':
            get_context_balance = self._get_quote_balance
            market_value = float(quantity) * float(price)
        else:
            get_context_balance = self._get_base_balance
            market_value = float(quantity)

        balance = float(get_context_balance(symbol)['free'])

        # if first_copy the balance was update before
        if self.balance_updated:
            balance += float(get_context_balance(symbol)['locked'])
        else:
            balance += market_value

        part = market_value / balance
        part = part * 0.99  # decrease part for 1% for avoid rounding errors in calculation
        return part

    def calc_quantity_from_part(self, symbol, quantityPart, price, side):
        # calculate quantity from quantityPart

        # if order[side] == sell: need obtain coin balance

        if side == 'BUY':
            get_context_balance = self._get_quote_balance
            buy_koef = float(price)
        else:
            get_context_balance = self._get_base_balance
            buy_koef = 1

        cur_bal = float(get_context_balance(symbol)['free'])

        if self.balance_updated:
            cur_bal += float(get_context_balance(symbol)['locked'])

        quantity = quantityPart * cur_bal / buy_koef

        stepSize = float(self.step_sizes[symbol])
        precision = int(round(-math.log(stepSize, 10), 0))
        quantity = round(quantity, precision)
        return quantity
Example #30
0
class TradingClient:
    def __init__(self, base_currency, trade_currency, key, secret):
        self.base_currency = base_currency
        self.trade_currency = trade_currency
        self.client = Client(key, secret)
        self.symbol = trade_currency + base_currency

    def get_historical_klines(self, interval, start_ts):
        return self.client.get_historical_klines(symbol=self.symbol,
                                                 interval=interval,
                                                 start_str=start_ts)

    def get_recent_trades(self):
        return self.client.get_recent_trades(symbol=self.symbol, limit=500)

    def get_order_book(self):
        return self.client.get_order_book(symbol=self.symbol, limit=1000)

    def get_order_book_ticker(self):
        return self.client.get_orderbook_ticker(symbol=self.symbol)

    def get_base_balance(self):
        return self.client.get_asset_balance(asset=self.base_currency)

    def get_trading_balance(self):
        return self.client.get_asset_balance(asset=self.trade_currency)

    @staticmethod
    def asset_balance_to_float(balance):
        return float(balance['free']) + float(balance['locked'])

    def get_all_trading_balance(self):
        trading_balance = self.get_trading_balance()
        return self.asset_balance_to_float(trading_balance)

    @staticmethod
    def trading_balance_available(trading_balance):
        return trading_balance > 1

    def get_balances(self):
        return self.client.get_account()['balances']

    def get_open_orders(self):
        return self.client.get_open_orders(symbol=self.symbol)

    def cancel_all_orders(self):
        open_orders = self.get_open_orders()
        for order in open_orders:
            self.client.cancel_order(symbol=self.symbol,
                                     orderId=order['orderId'])

    # GTC(Good-Til-Canceled) orders are effective until they are executed or canceled.
    # IOC(Immediate or Cancel) orders fills all or part of an order immediately and cancels the remaining part of the
    # order.
    def buy(self, quantity, price):
        price = '.8f' % price
        logging.info('Buying %d for %s', quantity, price)
        self.client.order_limit_buy(symbol=self.symbol,
                                    timeInForce=Client.TIME_IN_FORCE_GTC,
                                    quantity=quantity,
                                    price=price)

    def sell(self, quantity, price):
        price = '.8f' % price
        logging.info('Selling %d for %s', quantity, price)
        self.client.order_limit_sell(symbol=self.symbol,
                                     timeInForce=Client.TIME_IN_FORCE_GTC,
                                     quantity=quantity,
                                     price=price)

    def sell_market(self, quantity):
        if quantity > 0:
            logging.info('Selling to MARKET with quantity ' + str(quantity))
            self.client.order_market_sell(symbol=self.symbol,
                                          quantity=quantity)
        else:
            logging.info('Not executing - 0 quantity sell')

    def get_order(self, order_id):
        return self.client.get_order(symbol=self.symbol, orderId=order_id)

    def last_price(self):
        return float(
            self.client.get_symbol_ticker(symbol=self.symbol)['price'])

    def cancel_order(self, order_id):
        logging.info('Cancelling order ' + order_id)
        self.client.cancel_order(symbol=self.symbol, orderId=order_id)

    def panic_sell(self, last_known_amount, last_known_price):
        logging.error('!!! PANIC SELL !!!')
        logging.warn('Probably selling %.8f for %.8f', last_known_amount,
                     last_known_price)
        self.cancel_all_orders()
        self.sell_market(float(self.get_trading_balance()['free']))
Example #31
0
class Account(StoppableThread):
    def __init__(self, api_key, api_secret, master):
        StoppableThread.__init__(self)
        self.symbol_counter = 0

        try:
            self.rest_client = Client(api_key, api_secret)
            self.ws_client = BinanceSocketManager(self.rest_client)
            self.keep_running = True
            self.api_key = api_key
            self.api_secret = api_secret
            self.name = f"Account {self.api_key}"

            self.master = master

        except ConnectionError as e:
            logger.error(e)

    def get_account_trade_history(self, symbol, start_time=None, from_id=None):
        try:
            if start_time:
                trades = self.rest_client.get_my_trades(symbol=symbol,
                                                        startTime=start_time)
            elif from_id:
                trades = self.rest_client.get_my_trades(symbol=symbol,
                                                        fromId=from_id)
            else:
                trades = self.rest_client.get_my_trades(symbol=symbol)
            return trades
        except Exception as e:
            raise e

    def get_asset_balances(self):
        try:
            account_info = self.rest_client.get_account()
            portfolio = account_info['balances']
            balances = [
                bal for bal in portfolio
                if float(bal['free']) or float(bal['locked'])
            ]
            return balances
        except Exception as e:
            raise e

    def post_assets(self, balances):

        with create_session() as session:
            account = session.query(AccountModel).filter_by(
                api_key=self.api_key).first()
            account_assets = account.my_assets
            account_assets_names = [asset.name for asset in account_assets]
            for asset_params in balances:
                if not asset_params[
                        'asset'] in account_assets_names:  # asset is not yet created
                    asset = Asset(name=asset_params['asset'],
                                  free=asset_params['free'],
                                  fixed=asset_params['locked'],
                                  account=account)
                    session.add(asset)
                    continue
                for account_asset in account_assets:
                    if asset_params['asset'] == account_asset.name:
                        account_asset.free = asset_params['free']
                        account_asset.fixed = asset_params['locked']
                        session.add(account_asset)
            #do the reverse, check if there is an asset whose balance is zero
            balances_assets_names = [asset['asset'] for asset in balances]
            for asset_name in account_assets_names:
                if asset_name not in balances_assets_names:
                    asset = [
                        asset for asset in account_assets
                        if asset.name == asset_name
                    ]
                    if asset:
                        asset = asset[0]
                        session.delete(asset)
                        logger.info(f"asset {asset_name} has been deleted")
            session.commit()

    def post_error(self, e):
        error_params = {
            'exception_name': e.__class__.__name__,
            'message': e.message if hasattr(e, 'message') else f"{e}"
        }
        logger.error(f"{self.name} {e}")
        with create_session() as session:
            account = session.query(AccountModel).filter_by(
                api_key=self.api_key).first()
            error = ErrorModel(message=error_params['message'],
                               exception_name=error_params['exception_name'],
                               account=account)
            session.add(error)
            session.commit()

    def post_trades(self, trades, raw_json=None):

        with create_session() as session:
            account = session.query(AccountModel).filter_by(
                api_key=self.api_key).first()
            for trade_params in trades:
                trade_id = trade_params['id']
                trade_in_db = session.query(Trade).filter_by(
                    tradeId=trade_id).first()
                if not trade_in_db:
                    trade = Trade(
                        tradeId=trade_params['id'],
                        orderId=trade_params['orderId'],
                        symbol=trade_params['symbol'],
                        price=trade_params['price'],
                        qty=trade_params['qty'],
                        commission=trade_params['commission'],
                        commissionAsset=trade_params['commissionAsset'],
                        time=datetime.fromtimestamp(
                            float(trade_params['time']) / 1000),
                        isBuyer=trade_params['isBuyer'],
                        isMaker=trade_params['isMaker'],
                        isBestMatch=trade_params['isBestMatch'],
                        account=account,
                        raw_json=trade_params if not raw_json else raw_json)
                    session.add(trade)
            session.commit()
        return True

    def process_user_socket_message(self, msg):
        # throw it in the database
        try:
            print(msg)
            payload = msg
            if payload['e'] == "outboundAccountInfo":
                balances_all = payload['B']
                balances = [{
                    'asset': bal['a'],
                    'free': bal['f'],
                    'locked': bal['l']
                } for bal in balances_all
                            if float(bal['f']) or float(bal['l'])]

                self.post_assets(balances)

            elif payload['e'] == "executionReport":
                if payload['x'] == "TRADE":
                    logger.info(f"{self.name} received a trading event")
                    trade_params = {
                        'id': payload['t'],
                        'orderId': payload['i'],
                        'symbol': payload['s'],
                        'price': payload['L'],
                        'qty': payload['l'],
                        'commission': payload['n'],
                        'commissionAsset': payload['N'],
                        'time': payload['T'],
                        'isBuyer': not bool(payload['m']),
                        'isMaker': payload['m'],
                        'isBestMatch': None
                    }

                    self.post_trades([trade_params], raw_json=payload)
            elif payload['e'] == 'error':
                error = payload['m']
                logger.error(f"A network connection error occured, {error}")
                self.ws_client.stop_socket(self.user_socket_conn_key)
                #time.sleep(30)
                #self.start_account_socket()
            elif payload['e'] == 'connection_lost':
                #update last_update_time
                message = payload['m']
                logger.error(f"{message}")

            elif payload['e'] == 'connection_started':
                #update connection established
                message = payload['m']
                logger.info(f"{message}")
                self.master.restart_scrapper(
                )  #the only way to cover up for lost time
                self.ws_client._keepalive_user_socket()

            else:
                logger.error(f"unknown event, {msg}")
        except json.JSONDecodeError as e:
            logger.error(f"error occured, {e}")
            self.post_error(e)
        except Exception as e:
            logger.error(f"unknown error occurred, {e}")
            self.post_error(e)

    def start_account_socket(self):
        logger.info(f"{self.name} starting account socket")
        self.user_socket_conn_key = self.ws_client.start_user_socket(
            self.process_user_socket_message)
        self.ws_client.start()

    def update_account_portfolio(self):
        with create_session() as session:
            timestamp = datetime.utcnow()
            account = session.query(AccountModel).filter_by(
                api_key=self.api_key).first()
            if not account:
                raise ValueError("account not found")
            assets = account.my_assets
            portfolio_in_eth = get_portfolio_in_eth(assets)
            default_quotes = portfolio_in_eth['default_quotes']
            quote_dict = {}
            for default_quote in default_quotes:
                quote_dict[default_quote['symbol']] = default_quote['price']
            for asset_portfolio in portfolio_in_eth['portfolio']:
                asset = [
                    asset for asset in assets
                    if asset.name == asset_portfolio['symbol']
                ]
                if asset:
                    asset = asset[0]
                    asset.eth_value = asset_portfolio['eth_cost']
                    asset.quote_price = asset_portfolio['price']
                    asset.pair_name = asset_portfolio['pair_name']
                    asset.timestamp = timestamp  #last time the asset eth value was update
                    session.add(asset)

                    port = Portfolio(account=account,
                                     asset=asset,
                                     asset_name=asset.name,
                                     total_value=asset.free + asset.fixed,
                                     eth_value=asset_portfolio['eth_cost'],
                                     quote_price=asset_portfolio['price'],
                                     pair_name=asset_portfolio['pair_name'],
                                     timestamp=timestamp,
                                     eth_btc_quote=quote_dict['ETHBTC'],
                                     bnb_eth_quote=quote_dict['BNBETH'],
                                     eth_usdt_quote=quote_dict['ETHUSDT'])
                    session.add(port)
            session.commit()

    def update_account_trade_history(self, resume_update=False):
        '''
        brute force, there being no other way.
        :return:
        '''
        # 1. get all symbols.
        info = self.rest_client.get_exchange_info()
        symbols = [sym['symbol'] for sym in info['symbols']]

        if not resume_update:  #a neat way to stop update from restart when it hits max requests per second
            self.symbol_counter = 0
            logger.info("updating all history")
        else:
            logger.info(f"starting the update at {self.symbol_counter}")
        while self.keep_running:
            try:
                symbol = symbols[self.symbol_counter]
                print(f"[+] Fetching the trades for {symbol}")
                start = time.time()
                trade_history = self.get_account_trade_history(symbol)
                if trade_history:
                    print(f"I got new trades, {trade_history}")
                    self.post_trades(trade_history)

                left = time.time() - start
                sleep_time = 0.2 - left
                if sleep_time > 0:
                    print(f"sleeping for {sleep_time}")
                    time.sleep(sleep_time)
                self.symbol_counter += 1
            except IndexError:
                self.symbol_counter = 0
                break
            except Exception as e:
                raise e

    def run(self):
        '''
        1.get account balances
            - check keys are okay and inform on bad keys
            - get balances
        2. start trade socket.

        :NOTE: updating history is done by scrapper class.
        :return:
        '''
        while self.keep_running:
            try:
                balances = self.get_asset_balances()
                self.post_assets(balances)

                self.start_account_socket()  #
                break

            except BinanceAPIException as e:
                self.post_error(e)
                if int(e.code) in [-2010, -1010, -2011]:
                    logger.info(f"{self.name}, {e.message}")
                elif int(e.code) == -1013:
                    # balance below minimum allowable, should get here if we checking balances, end trade
                    logger.info(f"{self.name} cannot , {e.message}")
                elif int(e.code) == -2015:  # api key error.
                    logger.error(f"{self.name} {e.message}, exiting")
                    break
                elif int(e.status_code) == 429:
                    logger.warning(
                        f"{self.name} hit a rate limit, backing dowm for 1 minute"
                    )
                    time.sleep(60)
                elif int(e.status_code) == 418:
                    logger.error(f"{self.name} Ooops, IP has been auto banned")
                    time.sleep(300)
                else:
                    logger.error(
                        f"{self.name} uncaught API exception, {e.message}, {e.code}, {e.status_code}"
                    )

            except Exception as e:  # default, for all uncaught exceptions.
                logger.error(f"{self.name} Exceptiion, {e}")
                self.post_error(e)
                raise e