Esempio n. 1
0
def waves_btc(profit_calculator):
    time.sleep(10)
    client = Client(api_key, api_secret, {"verify": True, "timeout": 20})
    print("Getting Old Trades WAVESBTC " + str(datetime.datetime.now()))
    orders_prev = client.get_all_margin_orders(symbol='WAVESBTC', limit=100, requests_params={'timeout': 5})
    for prev_orders in orders_prev:
        for curr_order in actual_list_wavesbtc:
            if prev_orders["status"] == "FILLED" and prev_orders["orderId"] == curr_order[0]:
                if curr_order[1] == "BUY":
                    order_price = float(curr_order[3]) + float(curr_order[3]) * percentage
                    order_price = format(order_price, '.4f')
                    order = client.create_margin_order(
                        symbol='WAVESBTC',
                        side=SIDE_SELL,
                        type=ORDER_TYPE_LIMIT,
                        timeInForce=TIME_IN_FORCE_GTC,
                        quantity=float(curr_order[2]),
                        sideEffectType="MARGIN_BUY",
                        price=str(order_price),
                        requests_params={'timeout': 5})
                    print_statement = str(order["side"]) + " at " + str(order["price"] + " WAVESBTC")  # FIX THIS
                    with open('logger.txt', 'a') as log:
                        log.write(print_statement + " " + str(datetime.datetime.now()) + " \n")
                    print(str(order["side"]) + " at " + str(order["price"]))
                    actual_list_wavesbtc.remove(curr_order)
                    temp_orderId = order["orderId"]
                    temp_side = order["side"]
                    temp_origQty = order["origQty"]
                    temp_price = order["price"]
                    actual_list_wavesbtc.append([temp_orderId, temp_side, temp_origQty, temp_price])
                elif curr_order[1] == "SELL":
                    profit_calculator.append(profit_calculator[-1] + (float(curr_order[3])*float(curr_order[2])) * percentage)



                    order_price = float(curr_order[3]) - float(curr_order[3]) * percentage
                    order_price = format(order_price, '.4f')
                    order = client.create_margin_order(
                        symbol='WAVESBTC',
                        side=SIDE_BUY,
                        type=ORDER_TYPE_LIMIT,
                        timeInForce=TIME_IN_FORCE_GTC,
                        quantity=float(curr_order[2]),
                        sideEffectType="MARGIN_BUY",
                        price=str(order_price),
                        requests_params={'timeout': 5})
                    print_statement = str(order["side"]) + " at " + str(order["price"] + " WAVESBTC")
                    with open('logger.txt', 'a') as log:
                        log.write(print_statement + " " + str(datetime.datetime.now()) + " \n")
                        log.write(str(profit_calculator[-1])+"$ Profit Made Yet \n")
                    print(str(order["side"]) + " at " + str(order["price"]))
                    actual_list_wavesbtc.remove(curr_order)
                    temp_orderId = order["orderId"]
                    temp_side = order["side"]
                    temp_origQty = order["origQty"]
                    temp_price = order["price"]
                    actual_list_wavesbtc.append([temp_orderId, temp_side, temp_origQty, temp_price])
Esempio n. 2
0
class RealTrader(SimulationTrader):
    def __init__(
            self,
            apiKey: str,
            apiSecret: str,
            interval: str = '1h',
            symbol: str = 'BTCUSDT',
            loadData: bool = True,
            updateData: bool = True,
            isIsolated: bool = False,
            tld: str = 'com',
            precision: int = 2,
    ):
        """
        :param apiKey: API key to start trading bot with.
        :param apiSecret: API secret to start trading bot with.
        :param interval: Data interval to trade at.
        :param symbol: Symbol to trade in.
        :param loadData: Boolean that'll determine whether data object is loaded or not.
        :param updateData: Boolean that'll determine where data object is updated or not.
        :param isIsolated: Boolean that'll determine whether margin asset is isolated or not.
        :param tld: Top level domain. If based in the us, it'll be us; else it'll be com.
        """
        if apiKey is None or apiSecret is None:
            raise ValueError('API credentials not provided.')

        super().__init__(interval=interval, symbol=symbol, logFile='live', loadData=loadData, updateData=updateData,
                         precision=precision)

        self.binanceClient = Client(apiKey, apiSecret, tld=tld)
        self.transactionFeePercentage = 0.002  # Added 0.001 for volatility safety.
        self.isolated = isIsolated

        symbolInfo = self.binanceClient.get_symbol_info(self.symbol)
        self.purchasePrecision = self.get_purchase_precision(symbolInfo)
        self.minNotional = self.get_min_notional(symbolInfo)

        self.spot_usdt = self.get_spot_usdt()
        self.spot_coin = self.get_spot_coin()
        # self.check_spot_and_transfer()

        self.retrieve_margin_values()
        self.previousNet = self.get_net()
        self.startingBalance = self.get_starting_balance()
        self.check_current_position()
        self.netWorth = round(self.get_net(), self.precision)
        self.validate_minimum_funds()

    @staticmethod
    def get_min_notional(symbolInfo) -> float:
        filters = symbolInfo['filters']
        for filterDict in filters:
            if 'minNotional' in filterDict:
                min_notional = float(filterDict['minNotional'])
                return min_notional  # Get the default min_notional value from Binance if found.
        return 10  # Default value of $10.

    @staticmethod
    def get_purchase_precision(symbolInfo) -> int:
        filters = symbolInfo['filters']
        for filterDict in filters:
            if 'stepSize' in filterDict:
                stepSize = float(filterDict['stepSize'])
                return int(round(-math.log(stepSize, 10), 0))
        return 6  # Default value if no step size found.

    def check_spot_and_transfer(self):
        """
        Checks if spot account has more than $10. If it does, it transfers money to margin account.
        """
        if self.spot_usdt > 10:
            self.initial_transfer()

    def validate_minimum_funds(self):
        """
        Checks if account has enough funds to initiate trading.
        """
        if not self.has_enough_money():
            raise ValueError(f"You only have ${self.netWorth}. Please make sure you have at least $10 in your account.")

    def has_enough_money(self) -> bool:
        """
        Live trading requires at least $10. This function checks if our net balance is over or equal to $10.
        :return: Boolean whether we have at least $10.
        """
        return self.get_net() >= 10

    def is_isolated(self) -> bool:
        """
        Checks whether the coin we are trying to trade is isolated or not.
        :return: A boolean whether it is isolated or not.
        """
        try:  # Attempt to get coin from regular cross margin account.
            assets = self.binanceClient.get_margin_account()['userAssets']
            _ = [asset for asset in assets if asset['asset'] == self.coinName][0]
            return False
        except IndexError:  # If not found, it most likely means it is not in the cross margin account.
            return True

    def round_down(self, num: float) -> float:
        """
        Rounds down number for trading purposes.
        :param num: Number to be rounded down.
        :return: Rounded down number.
        """
        factor = 10.0 ** self.purchasePrecision
        return math.floor(float(num) * factor) / factor

    def check_current_position(self):
        """
        Checks current position to check if bot is in a long, short, or neither position.
        """
        self.currentPrice = self.dataView.get_current_price()
        if self.get_margin_coin() * self.currentPrice >= 10:
            self.currentPosition = LONG
            self.buyLongPrice = self.currentPrice
            self.longTrailingPrice = self.buyLongPrice

        elif self.get_borrowed_margin_coin() * self.currentPrice >= 10:
            self.currentPosition = SHORT
            self.sellShortPrice = self.currentPrice
            self.shortTrailingPrice = self.sellShortPrice

    def retrieve_margin_values(self):
        """
        Retrieves margin values and sets them to instance variables.
        """
        if self.isolated:
            assets = self.get_isolated_margin_account()['assets']
            coin = [asset for asset in assets if asset['baseAsset']['asset'] == self.coinName][0]['baseAsset']
            usdt = [asset for asset in assets if asset['baseAsset']['asset'] == self.coinName and
                    asset['quoteAsset']['asset'] == 'USDT'][0]['quoteAsset']
        else:
            assets = self.binanceClient.get_margin_account()['userAssets']
            coin = [asset for asset in assets if asset['asset'] == self.coinName][0]
            usdt = [asset for asset in assets if asset['asset'] == 'USDT'][0]

        self.balance = self.round_down(float(usdt['free']))
        self.coin = self.round_down(float(coin['free']))
        self.coinOwed = self.round_down(float(coin['borrowed']))

    def transfer_spot_to_margin(self):
        """
        Transfer assets from spot account to margin account.
        """
        self.binanceClient.transfer_spot_to_margin(asset=self.coinName, amount=self.get_spot_coin())
        self.add_trade(message='Transferred from spot to margin',
                       force=False,
                       orderID="TRANSFER SPOT TO MARGIN")

    def transfer_margin_to_spot(self):
        """
        Transfers assets from margin account to spot account.
        """
        order = self.binanceClient.transfer_margin_to_spot(asset=self.coinName, amount=self.get_margin_coin())
        self.add_trade(message='Transferred from margin to spot',
                       force=False,
                       orderID=order['clientOrderId'])

    def initial_transfer(self):
        """
        Buys long in spot then transfers it to margin account.
        """
        self.spot_buy_long()
        self.transfer_spot_to_margin()

    def spot_buy_long(self):
        """
        Enters long position in spot account.
        """
        self.spot_usdt = self.get_spot_usdt()
        self.currentPrice = self.dataView.get_current_price()
        max_buy = self.round_down(self.spot_usdt * (1 - self.transactionFeePercentage) / self.currentPrice)

        order = self.binanceClient.order_market_buy(
            symbol=self.symbol,
            quantity=max_buy
        )

        self.add_trade(message='Bought spot long.',
                       force=False,
                       orderID=order['clientOrderId'])

    def spot_sell_long(self):
        """
        Exits long position in spot account.
        """
        self.spot_coin = self.get_spot_coin()
        order = self.binanceClient.order_market_sell(
            symbol=self.symbol,
            quantity=self.spot_coin
        )

        self.add_trade(message='Sold spot long.',
                       force=False,
                       orderID=order['clientOrderId'])

    def get_spot_usdt(self) -> float:
        """
        Returns spot USDT amount.
        """
        return self.round_down(self.binanceClient.get_asset_balance(asset='USDT')['free'])

    def get_spot_coin(self) -> float:
        """
        Returns spot coin amount.
        """
        return self.round_down(self.binanceClient.get_asset_balance(asset=self.coinName)['free'])

    # noinspection PyProtectedMember
    def get_isolated_margin_account(self, **params) -> dict:
        """
        Retrieves margin isolated account information.
        :param params: **kwargs that normally go to binanceClient's request_margin_api function.
        :return: Margin isolated account information
        """
        return self.binanceClient._request_margin_api('get', 'margin/isolated/account', True, data=params)

    def get_starting_balance(self) -> float:
        """
        Returns the initial starting balance for bot.
        :return: initial starting balance for bot
        """
        self.currentPrice = self.dataView.get_current_price()
        usdt = self.coin * self.currentPrice + self.balance
        usdt -= self.coinOwed * self.currentPrice
        return usdt

    def get_asset(self, targetAsset: str) -> dict:
        """
        Retrieves asset specified (if exists).
        :param targetAsset: Asset to be retrieved.
        :return: The target asset (if found).
        """
        if self.isolated:
            assets = self.get_isolated_margin_account()['assets']
            return [asset for asset in assets if asset['baseAsset']['asset'] == targetAsset][0]['baseAsset']
        else:
            assets = self.binanceClient.get_margin_account()['userAssets']
            return [asset for asset in assets if asset['asset'] == targetAsset][0]

    def get_margin_coin_info(self) -> dict:
        """
        Retrieves margin info about coin.
        :return: Margin info about coin.
        """
        return self.get_asset(self.coinName)

    def get_margin_usdt(self) -> float:
        """
        Retrieves USDT available in margin account.
        :return: USDT available.
        """
        if self.isolated:
            assets = self.get_isolated_margin_account()['assets']
            for asset in assets:
                if asset['baseAsset']['asset'] == self.coinName and asset['quoteAsset']['asset'] == 'USDT':
                    return self.round_down(float(asset['quoteAsset']['free']))
        else:
            return self.round_down(float(self.get_asset('USDT')['free']))

    def get_margin_coin(self) -> float:
        """
        Retrieves margin coin amount.
        :return: Margin coin amount.
        """
        coin = self.get_margin_coin_info()
        return self.round_down(float(coin['free']))

    def get_borrowed_margin_coin(self) -> float:
        """
        Retrieves borrowed margin coin amount.
        :return: Borrowed margin coin amount.
        """
        coin = self.get_margin_coin_info()
        return self.round_down(float(coin['borrowed']))

    def get_borrowed_margin_interest(self) -> float:
        """
        Retrieves borrowed margin coin interest amount.
        :return: Borrowed margin coin interest amount.
        """
        coin = self.get_margin_coin_info()
        return self.round_down(float(coin['interest']))

    def create_margin_loan(self, amount: float, force: bool):
        """
        Creates margin loan.
        :param force: Boolean that determines whether bot executed action or human.
        :param amount: Amount to borrow in margin loan.
        :return: Order dictionary.
        """
        if self.isolated:
            self.binanceClient.create_margin_loan(asset=self.coinName,
                                                  amount=amount,
                                                  isIsolated=True,
                                                  symbol=self.symbol)
        else:
            self.binanceClient.create_margin_loan(asset=self.coinName,
                                                  amount=amount)

        self.retrieve_margin_values()
        self.add_trade(message='Created margin loan.',
                       force=force,
                       orderID=None)

    def repay_margin_loan(self, force: bool):
        """
        Repays margin loan.
        :param force: Boolean that determines whether bot executed action or human.
        """
        if self.isolated:
            self.binanceClient.repay_margin_loan(
                asset=self.coinName,
                amount=self.coin,
                isIsolated=self.isolated,
                symbol=self.symbol
            )
        else:
            self.binanceClient.repay_margin_loan(
                asset=self.coinName,
                amount=self.coin
            )

        self.retrieve_margin_values()
        self.add_trade(message='Repaid margin loan.',
                       force=force,
                       orderID=None)

    def buy_long(self, msg: str, coin: float or None = None, force: bool = False, smartEnter=False):
        """
        Buys coin at current market price with amount of coin specified. If not specified, assumes bot goes all in.
        :param smartEnter: Boolean that'll determine whether current position is entered from a smart enter or not.
        :param msg: Message to be used for displaying trade information.
        :param coin: Amount used to enter long position.
        :param force: Boolean that determines whether bot executed action or human.
        """
        with self.lock:
            if self.currentPosition == LONG:
                return

            self.balance = self.get_margin_usdt()
            self.currentPrice = self.dataView.get_current_price()
            if coin is None:
                coin = self.balance / self.currentPrice * (1 - self.transactionFeePercentage)

            self.output_message(f'Attempting to enter long by buying {coin} coins...')

            order = self.binanceClient.create_margin_order(
                symbol=self.symbol,
                side=SIDE_BUY,
                type=ORDER_TYPE_MARKET,
                quantity=self.round_down(coin),
                isIsolated=self.isolated
            )

            time.sleep(2)  # Sleep for a second so that the bot registers new margin values.
            self.retrieve_margin_values()
            self.currentPosition = LONG
            self.buyLongPrice = self.currentPrice
            self.longTrailingPrice = self.currentPrice
            self.add_trade(message=msg,
                           force=force,
                           orderID=order['clientOrderId'],
                           smartEnter=smartEnter)

    def sell_long(self, msg: str, coin: float or None = None, force: bool = False, stopLossExit=False):
        """
        Sells specified amount of coin at current market price. If not specified, assumes bot sells all coin.
        :param stopLossExit: Boolean for whether last position was exited because of a stop loss.
        :param msg: Message to be used for displaying trade information.
        :param coin: Coin amount to sell to exit long position.
        :param force: Boolean that determines whether bot executed action or human.
        """
        with self.lock:
            if self.currentPosition != LONG:
                return

            if coin is None:
                coin = self.get_margin_coin()

            self.output_message(f"Attempting to exit long by selling {coin} coins...")

            order = self.binanceClient.create_margin_order(
                symbol=self.symbol,
                side=SIDE_SELL,
                type=ORDER_TYPE_MARKET,
                quantity=coin,
                isIsolated=self.isolated
            )

            time.sleep(2)  # Sleep for a second so that the bot registers new margin values.
            self.retrieve_margin_values()
            self.previousPosition = LONG
            self.currentPosition = None
            self.customStopLoss = None
            self.buyLongPrice = None
            self.longTrailingPrice = None
            self.add_trade(message=msg,
                           force=force,
                           orderID=order['clientOrderId'], stopLossExit=stopLossExit)

    def buy_short(self, msg: str, coin: float or None = None, force: bool = False, stopLossExit=False):
        """
        Returns coin by buying them at current market price.
        If no coin is provided in function, bot will assume we try to pay back everything in return.
        :param stopLossExit: Boolean for whether last position was exited because of a stop loss.
        :param msg: Message to be used for displaying trade information.
        :param coin: Coin amount to buy back to exit short position.
        :param force: Boolean that determines whether bot executed action or human.
        """
        with self.lock:
            if self.currentPosition != SHORT:
                return

            # self.coinOwed = self.get_borrowed_margin_coin()
            # difference = (self.coinOwed + self.get_borrowed_margin_interest()) * (1 + self.transactionFeePercentage)
            asset = self.get_asset(self.coinName)
            coin = (float(asset['borrowed']) + float(asset['interest'])) * (1 + self.transactionFeePercentage)

            self.output_message(f'Attempting to exit short by returning {coin} coins...')

            order = self.binanceClient.create_margin_order(
                side=SIDE_BUY,
                symbol=self.symbol,
                quantity=self.round_down(coin),
                type=ORDER_TYPE_MARKET,
                isIsolated=self.isolated,
                sideEffectType="AUTO_REPAY"
            )

            # order = self.binanceClient.create_margin_order(
            #     symbol=self.symbol,
            #     side=SIDE_BUY,
            #     type=ORDER_TYPE_MARKET,
            #     quantity=self.round_down(difference),
            #     isIsolated=self.isolated
            # )

            time.sleep(2)  # Sleep for a second so that the bot registers new margin values.
            self.retrieve_margin_values()
            self.add_trade(message=msg,
                           force=force,
                           orderID=order['clientOrderId'],
                           stopLossExit=stopLossExit)

            # self.repay_margin_loan(force=force)
            self.previousPosition = SHORT
            self.currentPosition = None
            self.sellShortPrice = None
            self.customStopLoss = None
            self.shortTrailingPrice = None

    def sell_short(self, msg: str, coin: float or None = None, force: bool = False, smartEnter=False):
        """
        Borrows coin and sells them at current market price.
        If no coin is provided in function, bot will assume we borrow as much as
        bot can buy with current balance and market value.
        :param smartEnter: Boolean that'll determine whether current position is entered from a smart enter or not.
        :param msg: Message to be used for displaying trade information.
        :param coin: Coin amount to sell to enter short position.
        :param force: Boolean that determines whether bot executed action or human.
        """
        with self.lock:
            if self.currentPosition == SHORT:
                return

            self.currentPrice = self.dataView.get_current_price()
            self.balance = self.get_margin_usdt()
            transactionFee = self.balance * self.transactionFeePercentage * 2

            if coin is None:
                coin = (self.balance - transactionFee) / self.currentPrice
            # max_borrow = self.round_down(self.balance / self.currentPrice - self.get_borrowed_margin_coin())
            # self.create_margin_loan(amount=max_borrow, force=force)
            self.output_message(f'Attempting to enter short by selling {coin} coins...')

            order = self.binanceClient.create_margin_order(
                side=SIDE_SELL,
                symbol=self.symbol,
                type=ORDER_TYPE_MARKET,
                quantity=self.round_down(coin),
                isIsolated=self.isolated,
                sideEffectType="MARGIN_BUY"
            )

            time.sleep(2)  # Sleep for a second so that the bot registers new margin values.
            self.currentPosition = SHORT
            self.sellShortPrice = self.currentPrice
            self.shortTrailingPrice = self.currentPrice
            self.retrieve_margin_values()
            self.add_trade(message=msg,
                           force=force,
                           orderID=order['clientOrderId'],
                           smartEnter=smartEnter)
Esempio n. 3
0
class BinanceAPI:
    def __init__(self):
        key = os.getenv('key')
        secret = os.getenv('secret')
        self.client = Client(key, secret)
        self.ws = BinanceSocketManager(self.client)
        self.conn_key = None

    def __get_interval(self, timeframe):
        if timeframe == '1m':
            interval = Client.KLINE_INTERVAL_1MINUTE
        elif timeframe == '5m':
            interval = Client.KLINE_INTERVAL_5MINUTE
        elif timeframe == '10m':
            interval = Client.KLINE_INTERVAL_10MINUTE
        elif timeframe == '15m':
            interval = Client.KLINE_INTERVAL_15MINUTE
        elif timeframe == '30m':
            interval = Client.KLINE_INTERVAL_30MINUTE
        elif timeframe == '1h':
            interval = Client.KLINE_INTERVAL_1HOUR
        elif timeframe == '2h':
            interval = Client.KLINE_INTERVAL_2HOUR
        elif timeframe == '4h':
            interval = Client.KLINE_INTERVAL_4HOUR
        elif timeframe == '1d':
            interval = Client.KLINE_INTERVAL_1DAY
        elif timeframe == '1w':
            interval = Client.KLINE_INTERVAL_1WEEK
        return interval

    def bulk_klines(self, symbol, timeframe, since=None):
        # since: "1 Jan, 2017"
        interval = self.__get_interval(timeframe)
        klines = self.client.get_historical_klines(symbol,
                                                   interval,
                                                   start_str=since)
        return klines

    def get_klines(self, symbol, timeframe, since, limit=10):
        interval = self.__get_interval(timeframe)
        klines = self.client.get_historical_klines(symbol,
                                                   interval,
                                                   start_str=since,
                                                   limit=limit)
        return klines

    def margin_account_info(self):
        info = self.client.get_margin_account()
        return info

    def create_buy_order(self, symbol, quantity, price=None):
        if price is not None:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                price=price,
                side=SIDE_BUY,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC)
        else:
            order = self.client.create_margin_order(symbol=symbol,
                                                    quantity=quantity,
                                                    side=SIDE_BUY,
                                                    type=ORDER_TYPE_MARKET)
        return order

    def create_sell_order(self, symbol, quantity, price=None):
        if price is not None:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                price=price,
                side=SIDE_SELL,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC)
        else:
            order = self.client.create_margin_order(symbol=symbol,
                                                    quantity=quantity,
                                                    side=SIDE_SELL,
                                                    type=ORDER_TYPE_MARKET)
        return order

    def get_my_trades(self, symbol):
        return self.client.get_my_trades(symbol=symbol)

    def get_price(self, symbol, price_type='lastPrice'):
        """
        :param symbol:
        :param price_type: bidPrice, askPrice, lastPrice
        :return:
        """
        ticker = self.client.get_ticker(symbol=symbol)
        return float(ticker[price_type])

    def create_loan(self, asset, amount):
        transaction = self.client.create_margin_loan(asset=asset,
                                                     amount=amount)
        return transaction['tranId']

    def get_loan(self, asset, txId):
        details = self.client.get_margin_loan_details(asset=asset, txId=txId)
        if len(details['rows']) == 0:
            return -1, details
        elif details['rows'][0]['status'] == 'CONFIRMED':
            # 성공
            return 0, details
        else:
            # 실패
            return 1, details

    def repay_loan(self, asset, amount):
        transaction = self.client.repay_margin_loan(asset=asset, amount=amount)
        return transaction

    def repay_all(self, asset):
        info = self.margin_account_info()
        obj = next(item for item in info['userAssets']
                   if item['asset'] == asset)
        total = float(obj['borrowed']) + float(obj['interest'])
        available = min(total, float(obj['free']))
        self.repay_loan(asset, available)
        return available

    def asset_detail(self):
        details = self.client.get_asset_details()
        return details

    def get_orderbook_tickers(self, symbol):
        """
        :return: {'symbol': 'BTCUSDT', 'bidPrice': '9379.95000000', 'bidQty': '0.26652500', 'askPrice': '9380.00000000',
                    'askQty': '0.22441700'}
        """
        tickers = self.client.get_orderbook_tickers()
        return next(item for item in tickers if item['symbol'] == symbol)

    def start_websocket(self, symbol, timeframe, callback):
        interval = self.__get_interval(timeframe)
        self.conn_key = self.ws.start_kline_socket(symbol,
                                                   callback,
                                                   interval=interval)
        self.ws.start()

    def stop_websocket(self):
        self.ws.stop_socket(self.conn_key)

    def get_balance(self, asset):
        info = self.margin_account_info()
        o = next(item['free'] for item in info['userAssets']
                 if item['asset'] == asset)
        return float(o)
Esempio n. 4
0
class BinanceAPI:

    def __init__(self, api_key=None, api_secret=None):
        self.client = Client(api_key, api_secret)

    def fetch_ohlcv(self, symbol, timeframe, since=None):
        # since: "1 Jan, 2017"
        if timeframe == '1m':
            interval = Client.KLINE_INTERVAL_1MINUTE
        elif timeframe == '1h':
            interval = Client.KLINE_INTERVAL_1HOUR
        elif timeframe == '1d':
            interval = Client.KLINE_INTERVAL_1DAY

        klines = self.client.get_historical_klines(symbol, interval, start_str=since)
        return klines

    def account_info(self):
        info = self.client.get_margin_account()
        return info

    def create_buy_order(self, symbol, quantity, price=None):
        if price is not None:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                price=price,
                side=SIDE_BUY,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC)
        else:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                side=SIDE_BUY,
                type=ORDER_TYPE_MARKET)
        return order

    def create_sell_order(self, symbol, quantity, price=None):
        if price is not None:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                price=price,
                side=SIDE_SELL,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC)
        else:
            order = self.client.create_margin_order(
                symbol=symbol,
                quantity=quantity,
                side=SIDE_SELL,
                type=ORDER_TYPE_MARKET)
        return order

    def create_loan(self, asset, amount):
        transaction = self.client.create_margin_loan(asset=asset, amount=amount)
        return transaction

    def repay_loan(self, asset, amount):
        transaction = self.client.repay_margin_loan(asset=asset, amount=amount)
        return transaction

    def repay_all(self, asset):
        info = self.account_info()
        obj = next(item for item in info['userAssets'] if item['asset'] == asset)
        free = obj['free']
        return self.repay_loan(asset, free)

    def asset_detail(self):
        details = self.client.get_asset_details()
        return details

    def get_tickers(self, symbol):
        tickers = self.client.get_all_tickers()
        return next(item for item in tickers if item['symbol'] == symbol)

    def get_orderbook_tickers(self, symbol):
        """
        :return: {'symbol': 'BTCUSDT', 'bidPrice': '9379.95000000', 'bidQty': '0.26652500', 'askPrice': '9380.00000000',
                    'askQty': '0.22441700'}
        """
        tickers = self.client.get_orderbook_tickers()
        return next(item for item in tickers if item['symbol'] == symbol)
Esempio n. 5
0
class BinanceBot(LoggerSuper):
    logger = logging.getLogger('BinanceBot')
    db_path = 'db.txt'

    def __init__(self):
        self.client = Client(API_KEY, API_SECRET)
        self.meta_orders = []
        self.prices = None
        self.margin_info = None
        self.read_db()

        self.botThread = threading.Thread(target=self.botThreadfunc,
                                          args=(),
                                          daemon=False)
        self.botThread.start()
        self.logger.info('bot started')

    def read_db(self):
        import os.path
        if os.path.exists(self.db_path):
            with open(self.db_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                for line in lines:
                    cmd_list = line.replace('\n', '').split(' ')
                    meta_order = MetaOrder(cmd_list=cmd_list)
                    if meta_order.inited:
                        self.meta_orders.append(meta_order)
                        print(f'add order: {meta_order}')

    def write_to_db(self):
        with open(self.db_path, 'w', encoding='utf-8') as f:
            for order in self.meta_orders:
                line = f'{order.market} {order.side} {order.symbol} {order.amount} {order.price} {order.trigger_down_price} {order.TP}'
                f.write(line + '\n')

    def botThreadfunc(self):
        while BaseClass.working():
            self._transaction(self._get_prices)
            self._transaction(self._get_margin_info)
            self.check_and_place_orders()
            sleep(1)

    def _transaction(self, foo):
        try:
            foo()
        except Exception as Ex:
            self.logger.debug(Ex)

    def check_and_place_orders(self):
        for order in self.meta_orders:
            created_order = None
            last_price = self.get_price(order.symbol)
            if order.side == 'BUY':
                if order.trigger_down_price is not None and order.trigger_up and last_price <= order.trigger_down_price and not order.trigger_down:
                    order.trigger_down = True
                    self.logger.info(
                        f'order {order} is triggered down. lets ready to buy at terget price!'
                    )

                if order.price <= last_price < order.price * 1.01:
                    if order.trigger_down_price is not None:
                        if not order.trigger_up:
                            order.trigger_up = True
                            self.logger.info(f'order {order} is triggered up!')
                        if not (order.trigger_up and order.trigger_down):
                            continue

                    # сначала занимаем деньги
                    if order.market == 'margin' and self.get_free_usdt(
                    ) < order.amount * order.price * 1.004:
                        self.create_usdt_loan(order.amount * order.price *
                                              1.004 - self.get_free_usdt())
                    # открываем ордер
                    created_order = self.create_order(order.symbol, order.side,
                                                      order.amount,
                                                      order.price * 1.003,
                                                      order.market)
                    if created_order is not None and order.TP is not None:
                        # Выставим TP
                        tp_order = self.create_order(order.symbol, 'SELL',
                                                     order.amount, order.TP,
                                                     order.market)
                        if tp_order is not None:
                            self.logger.info('выставил TP')

                    self.meta_orders.remove(order)
                    self.logger.info(f'remove planned order: {order}')
            elif order.side == 'SELL':
                if order.price >= last_price > order.price * 1.007:
                    # открываем ордер
                    created_order = self.create_order(order.symbol, order.side,
                                                      order.amount,
                                                      order.price * 0.997,
                                                      order.market)
                    if created_order is not None:
                        # возвращаем заем
                        self.repay_usdt_loan(self.get_borrowed())
        self.write_to_db()

    def _get_margin_info(self):
        self.margin_info = self.client.get_margin_account()

    def _get_prices(self):
        self.prices = self.client.get_all_tickers()

    def create_usdt_loan(self, amount):
        try:
            transaction = self.client.create_margin_loan(asset='USDT',
                                                         amount=str(
                                                             round(amount, 2)))
            self.logger.info(transaction)
        except Exception as ex:
            self.logger.critical(f'borrow error: {ex}')

    def repay_usdt_loan(self, amount):
        try:
            transaction = self.client.repay_margin_loan(asset='USDT',
                                                        amount=str(
                                                            round(amount, 2)))
            self.logger.info(transaction)
        except Exception as ex:
            self.logger.critical(f'repay error: {ex}')

    def create_order(self, symbol, side, amount, price, market='spot'):
        try:
            if market == 'margin':
                order = self.client.create_margin_order(symbol=symbol.upper(),
                                                        side=side.upper(),
                                                        type='LIMIT',
                                                        quantity=amount,
                                                        timeInForce='GTC',
                                                        price=str(price))
            else:
                order = self.client.create_order(symbol=symbol.upper(),
                                                 side=side.upper(),
                                                 type='LIMIT',
                                                 price=str(price),
                                                 quantity=amount)
            self.logger.info(order)
            return order
        except Exception as ex:
            self.logger.critical(f'open order error: {ex}')

    def orders(self):
        if len(self.meta_orders) > 0:
            for meta_order in self.meta_orders:
                print(meta_order)
        else:
            print('No meta orders')

    def get_price(self, pair):
        if isinstance(self.prices, list) and isinstance(pair, str):
            for price in self.prices:
                if price['symbol'] == pair.upper():
                    return float(price['price'])
        else:
            return None

    def get_borrowed(self):
        if isinstance(self.margin_info, dict):
            assets = self.margin_info['userAssets']
            for asset in assets:
                if asset['asset'] == 'USDT':
                    return float(asset['borrowed']) + float(asset['interest'])
        else:
            return None

    def get_free_usdt(self):
        if isinstance(self.margin_info, dict):
            assets = self.margin_info['userAssets']
            for asset in assets:
                if asset['asset'] == 'USDT':
                    return float(asset['free'])
        else:
            return None

    def keyboard_notify(self, owner):
        cmd_list = owner.get()
        if len(cmd_list) == 0:
            return
        elif cmd_list[0] == 'borrow':
            if len(cmd_list) != 2:
                print('wrong format. ex.: borrow 30000')
                return
            amount = cmd_list[1]
            self.create_usdt_loan(amount)
        elif cmd_list[0] == 'delete':
            self.meta_orders = []
            self.write_to_db()
            print('all orders deleted')
        elif cmd_list[0] == 'repay':
            self.repay_usdt_loan(self.get_borrowed())
        elif cmd_list[0] == 'free':
            in_meta = 0
            for order in self.meta_orders:
                if order.side.upper() == 'BUY':
                    in_meta += order.amount * order.price * 1.003
            binance_free = self.get_free_usdt()
            print(
                f'Binance {round(binance_free,2)} USDT free. In meta orders {round(in_meta,2)} USDT. Total free is {round(binance_free - in_meta, 2)} USDT.'
            )
        elif len(cmd_list) == 1:
            if cmd_list[0] == 'orders':
                self.orders()
            else:
                symbol = cmd_list[0]
                price = self.get_price(symbol)
                borrowed = self.get_borrowed()
                if price is not None:
                    print(
                        f'{symbol}: price {price}. Borrowed {borrowed} USDT.')
                else:
                    print('wrong pair')
                    return
        else:
            meta_order = MetaOrder(cmd_list=cmd_list)
            if meta_order.inited:
                self.meta_orders.append(meta_order)
                self.write_to_db()
                print(f'add order: {meta_order}')
class MarketMaker( object ):
    
    def __init__( self, monitor = True, output = True ):
        self.equity_usd         = None
        self.equity_btc         = None
        self.equity_usd_init    = None
        self.equity_btc_init    = None
        self.con_size           = float( CONTRACT_SIZE )
        self.client             = None
        self.client3             = None
        self.usdtbalance = 46.18
        self.client2 = None
        self.deltas             = OrderedDict()
        self.futures            = OrderedDict()
        self.futures_prv        = OrderedDict()
        self.logger             = None
        self.mean_looptime      = 1
        self.monitor            = monitor
        self.output             = output or monitor
        self.positions          = OrderedDict()
        self.spread_data        = None
        self.this_mtime         = None
        self.ts                 = None
        self.vols               = OrderedDict()
    
    def create_client( self ):
        #self.client = RestClient( KEY, SECRET, URL )
        #print(binApi)
        from binance.client import Client
        self.client3 = Client(binApi, binSecret)

        binance_futures = ccxt.binance(
            {"apiKey": binApi,
            "secret": binSecret
 })
        self.client2 = ccxt.binance({    "apiKey": binApi,
    "secret": binSecret})
        self.client = binance_futures
        #print(dir(self.client))           

    
    def get_bbo( self, contract ): # Get best b/o excluding own orders
        
        # Get orderbook
        ob      = self.client.fetchOrderBook( contract )
        bids    = ob[ 'bids' ]
        asks    = ob[ 'asks' ]
        
        ords        = self.client.fetchOpenOrders( contract )
        #print(ords)
        bid_ords    = [ o for o in ords if o ['info'] [ 'side' ] == 'buy'  ]
        ask_ords    = [ o for o in ords if o ['info'] [ 'side' ] == 'sell' ]
        best_bid    = None
        best_ask    = None

        err = 10 ** -( self.get_precision( contract ) + 1 )
        
        for b in bids:
            match_qty   = sum( [ 
                o[1] for o in bid_ords 
                if math.fabs( b[0] - o[0] ) < err
            ] )
            if match_qty < b[1]:
                best_bid = b[0]
                break
        
        for a in asks:
            match_qty   = sum( [ 
                o[1] for o in ask_ords 
                if math.fabs( a[0] - o[0] ) < err
            ] )
            if match_qty < a[1]:
                best_ask = a[0]
                break
        
        return { 'bid': best_bid, 'ask': best_ask }
    
        
    def get_futures( self ): # Get all current futures instruments
        
        self.futures_prv    = cp.deepcopy( self.futures )
        insts               = self.client.fetchMarkets()
        #print(insts[0])
        self.futures        = sort_by_key( { 
            i[ 'symbol' ]: i for i in insts if i['symbol'] == 'BTC/USDT'
        } )
        #print(self.futures)
        #for k, v in self.futures.items():
            #self.futures[ k ][ 'expi_dt' ] = datetime.strptime( 
            #                                   v[ 'expiration' ][ : -4 ], 
            #                                   '%Y-%m-%d %H:%M:%S' )
                        
        
    def get_pct_delta( self ):         
        self.update_status()
        return sum( self.deltas.values()) / float(self.equity_btc)

    
    def get_spot( self ):
        #print(self.client2.fetchTicker( 'BTC/USDT' )['bid'])
        return self.client2.fetchTicker( 'BTC/USDT' )['bid']

    
    def get_precision( self, contract ):
        return 2

    
    def get_ticksize( self, contract ):
        return 0.01000000
    
    
    def output_status( self ):
        
        if not self.output:
            return None
        self.cancelall()
        orders = self.client3.get_open_margin_orders(symbol='BTCUSDT')
        for o in orders:
            print(o)
            result = self.client3.cancel_margin_order(
    symbol='BTCUSDT',
    orderId=o['orderId'])

        self.update_status()
        
        now     = datetime.utcnow()
        days    = ( now - self.start_time ).total_seconds() / SECONDS_IN_DAY
        print( '********************************************************************' )
        print( 'Start Time:        %s' % self.start_time.strftime( '%Y-%m-%d %H:%M:%S' ))
        print( 'Current Time:      %s' % now.strftime( '%Y-%m-%d %H:%M:%S' ))
        print( 'Days:              %s' % round( days, 1 ))
        print( 'Hours:             %s' % round( days * 24, 1 ))
        print( 'Spot Price:        %s' % self.get_spot())
        
        
        pnl_usd = self.equity_usd - self.equity_usd_init
        pnl_btc = self.equity_btc - self.equity_btc_init
        
        print( 'Equity ($):        %7.2f'   % self.equity_usd)
        print( 'P&L ($)            %7.2f'   % pnl_usd)
        print( 'Equity (BTC):      %7.4f'   % self.equity_btc)
        print( 'P&L (BTC)          %7.4f'   % pnl_btc)
        print( '%% Delta:           %s%%'% round( self.get_pct_delta() / PCT, 1 ))
        print( 'Total Delta (BTC): %s'   % round( sum( self.deltas.values()), 2 ))        
        print_dict_of_dicts( {
            k: {
                'BTC': self.deltas[ k ]
            } for k in self.deltas.keys()
            }, 
            roundto = 2, title = 'Deltas' )
        
        #print(self.positions)
        print_dict_of_dicts( {
            k: {
                'Contracts': self.positions[ k ][ 'positionAmt' ]
            } for k in self.positions.keys()
            }, 
            title = 'Positions' )
        
        if not self.monitor:
            print_dict_of_dicts( {
                k: {
                    '%': self.vols[ k ]
                } for k in self.vols.keys()
                }, 
                multiple = 100, title = 'Vols' )
            print( '\nMean Loop Time: %s' % round( self.mean_looptime, 2 ))
            self.cancelall()
        print( '' )

        
    def place_orders( self ):

        if self.monitor:
            return None
        
        con_sz  = self.con_size        
        
        for fut in self.futures.keys():
            
            account         = self.client.fetchBalance()
            spot            = self.get_spot()
            bal_btc         = self.usdtbalance / spot 
            pos             = float(self.positions[ fut ][ 'positionAmt' ])
            pos_lim_long    = bal_btc * PCT_LIM_LONG * 125 #/ len(self.futures)
            pos_lim_short   = bal_btc * PCT_LIM_SHORT * 125 #/ len(self.futures)
            #print(pos_lim_long)
            #expi            = self.futures[ fut ][ 'expi_dt' ]
            #tte             = max( 0, ( expi - datetime.utcnow()).total_seconds() / SECONDS_IN_DAY )
            pos_decay       = 1.0 - math.exp( -DECAY_POS_LIM * 8035200 )
            pos_lim_long   *= pos_decay
            pos_lim_short  *= pos_decay
            pos_lim_long   -= pos
            pos_lim_short  += pos
            pos_lim_long    = max( 0, pos_lim_long  )
            pos_lim_short   = max( 0, pos_lim_short )
            
            min_order_size_btc = (MIN_ORDER_SIZE * CONTRACT_SIZE) / spot
            print(min_order_size_btc) #0.0006833471711135484 0.08546200188472201
            qtybtc  = bal_btc * 125 / 25

            nbids   = min( math.trunc( pos_lim_long  / qtybtc ), MAX_LAYERS )
            nasks   = min( math.trunc( pos_lim_short / qtybtc ), MAX_LAYERS )
            
            place_bids = nbids > 0
            place_asks = nasks > 0
            
            if not place_bids and not place_asks:
                print( 'No bid no offer for %s' % fut, min_order_size_btc )
                continue
                
            tsz = float(self.get_ticksize( fut ))            
            # Perform pricing
            vol = max( self.vols[ BTC_SYMBOL ], self.vols[ fut ] )

            eps         = BP * vol * RISK_CHARGE_VOL
            riskfac     = math.exp( eps )

            bbo     = self.get_bbo( fut )
            bid_mkt = bbo[ 'bid' ]
            ask_mkt = bbo[ 'ask' ]
            
            if bid_mkt is None and ask_mkt is None:
                bid_mkt = ask_mkt = spot
            elif bid_mkt is None:
                bid_mkt = min( spot, ask_mkt )
            elif ask_mkt is None:
                ask_mkt = max( spot, bid_mkt )
            mid_mkt = 0.5 * ( bid_mkt + ask_mkt )
            
            ords        = self.client.fetchOpenOrders( fut )
            cancel_oids = []
            bid_ords    = ask_ords = []
            
            if place_bids:
                
                bid_ords        = [ o for o in ords if o['info']['side'] == 'buy'  ]
                len_bid_ords    = min( len( bid_ords ), nbids )
                bid0            = mid_mkt * math.exp( -MKT_IMPACT )
                
                bids    = [ bid0 * riskfac ** -i for i in range( 1, nbids + 1 ) ]

                bids[ 0 ]   = ticksize_floor( bids[ 0 ], tsz )
                
            if place_asks:
                
                ask_ords        = [ o for o in ords if o['info']['side'] == 'sell' ]    
                len_ask_ords    = min( len( ask_ords ), nasks )
                ask0            = mid_mkt * math.exp(  MKT_IMPACT )
                
                asks    = [ ask0 * riskfac ** i for i in range( 1, nasks + 1 ) ]
                
                asks[ 0 ]   = ticksize_ceil( asks[ 0 ], tsz  )
                
            for i in range( max( nbids, nasks )):
                # BIDS
                if place_bids and i < nbids:

                    if i > 0:
                        prc = ticksize_floor( min( bids[ i ], bids[ i - 1 ] - tsz ), tsz )
                    else:
                        prc = bids[ 0 ]

                    qty = round( prc * qtybtc / con_sz )   / spot                     
                        
                    if i < len_bid_ords:    

                        oid = bid_ords[ i ]['info']['side']['orderId']
                        print(oid)
                        try:
                            self.client.editOrder( oid, qty, prc , {'leverage': 5})
                        except (SystemExit, KeyboardInterrupt):
                            raise
                        except Excetion as e:
                            print(e)
                    else:
                        try:
                            print(float("{0:.2f}".format(qty)))
                            order = self.client3.create_margin_order(
                                symbol='BTCUSDT',
                                side=SIDE_BUY,
                                type=ORDER_TYPE_LIMIT,
                                timeInForce=TIME_IN_FORCE_GTC,
                                quantity=0.0012,
                                price=prc)

#                            self.client.sapiPostMarginOrder( {'symbol':fut,'side': 'BUY', 'type':"LIMIT",'timeInForce':  "GTC", 'quantity':qty,'price': prc,'recvWindow': "5000",'timestamp': str(math.floor(time.time())* 1000)})
                        except (SystemExit, KeyboardInterrupt):
                            raise
                        except Exception as e:
                            print(e)
                            self.logger.warn( 'Bid order failed: %s bid for %s'
                                                % ( prc, qty ))

                # OFFERS

                if place_asks and i < nasks:

                    if i > 0:
                        prc = ticksize_ceil( max( asks[ i ], asks[ i - 1 ] + tsz ), tsz )
                    else:
                        prc = asks[ 0 ]
                        
                    qty = round( prc * qtybtc / con_sz ) / spot
                    
                    if i < len_ask_ords:
                        oid = ask_ords[ i ]['info']['side']['orderId']
                        print(oid)
                        try:
                            self.client.editOrder( oid, qty, prc , {'leverage': 5})
                        except (SystemExit, KeyboardInterrupt):
                            raise
                        except Exeption as e:
                            print(e)

                    else:
                        try:
                            order = self.client3.create_margin_order(
                                symbol='BTCUSDT',
                                side=SIDE_SELL,
                                type=ORDER_TYPE_LIMIT,
                                timeInForce=TIME_IN_FORCE_GTC,
                                quantity=0.0012,
                                price=prc)

                            #self.client.sapiPostMarginOrder( {'symbol':fut,'side': 'SELL', 'type':"LIMIT",'timeInForce':  "GTC", 'quantity':qty,'price': prc,'recvWindow': "5000",'timestamp': str(math.floor(time.time())* 1000) })
                        except (SystemExit, KeyboardInterrupt):
                            raise
                        except Exception as e:
                            self.logger.warn( 'Offer order failed: %s at %s'
                                                % ( qty, prc ))


            if nbids < len( bid_ords ):
                cancel_oids += [ o['info']['side']['orderId'] for o in bid_ords[ nbids : ]]
            if nasks < len( ask_ords ):
                cancel_oids += [ o['info']['side']['orderId'] for o in ask_ords[ nasks : ]]
            for oid in cancel_oids:
                try:
                    self.client.cancelOrder( oid , 'BTC/USDT' )
                except:
                    self.logger.warn( 'Order cancellations failed: %s' % oid )
                                        
    def cancelall(self):
        ords        = self.client.fetchOpenOrders( 'BTC/USDT' )
        for order in ords:
            #print(order)
            oid = order ['info'] ['orderId']
           # print(order)
            try:
                self.client.cancelOrder( oid , 'BTC/USDT' )
            except Exception as e:
                print(e)
    def restart( self ):        
        try:
            strMsg = 'RESTARTING'
            print( strMsg )
            self.cancelall()
            strMsg += ' '
            for i in range( 0, 5 ):
                strMsg += '.'
                print( strMsg )
                sleep( 1 )
        except:
            pass
        finally:
            os.execv( sys.executable, [ sys.executable ] + sys.argv )        
            

    def run( self ):
        
        self.run_first()
        self.output_status()

        t_ts = t_out = t_loop = t_mtime = datetime.utcnow()

        while True:
            bal = self.client.sapiGetMarginAccount()
            for b in bal["userAssets"]:
                if b["asset"] == 'USDT':
                    self.usdtbalance = float(b["netAsset"])
              
            
            self.get_futures()
            
            # Restart if a new contract is listed
            if len( self.futures ) != len( self.futures_prv ):
                self.restart()
            
            self.update_positions()
            
            t_now   = datetime.utcnow()
            
            # Update time series and vols
            if ( t_now - t_ts ).total_seconds() >= WAVELEN_TS:
                t_ts = t_now
                self.update_timeseries()
                self.update_vols()
    
            self.place_orders()
            
            # Display status to terminal
            if self.output:    
                t_now   = datetime.utcnow()
                if ( t_now - t_out ).total_seconds() >= WAVELEN_OUT:
                    self.output_status(); t_out = t_now
            
            # Restart if file change detected
            t_now   = datetime.utcnow()
            if ( t_now - t_mtime ).total_seconds() > WAVELEN_MTIME_CHK:
                t_mtime = t_now
                if getmtime( __file__ ) > self.this_mtime:
                    self.restart()
            
            t_now       = datetime.utcnow()
            looptime    = ( t_now - t_loop ).total_seconds()
            
            # Estimate mean looptime
            w1  = EWMA_WGT_LOOPTIME
            w2  = 1.0 - w1
            t1  = looptime
            t2  = self.mean_looptime
            
            self.mean_looptime = w1 * t1 + w2 * t2
            
            t_loop      = t_now
            sleep_time  = MIN_LOOP_TIME - looptime
            if sleep_time > 0:
                time.sleep( sleep_time )
            if self.monitor:
                time.sleep( WAVELEN_OUT )

            
    def run_first( self ):
        
        self.create_client()
        self.cancelall()
        self.logger = get_logger( 'root', LOG_LEVEL )
        # Get all futures contracts
        self.get_futures()
        self.this_mtime = getmtime( __file__ )
        self.symbols    = [ BTC_SYMBOL ] + list( self.futures.keys()); self.symbols.sort()
        self.deltas     = OrderedDict( { s: None for s in self.symbols } )
        
        # Create historical time series data for estimating vol
        ts_keys = self.symbols + [ 'timestamp' ]; ts_keys.sort()
        
        self.ts = [
            OrderedDict( { f: None for f in ts_keys } ) for i in range( NLAGS + 1 )
        ]
        
        self.vols   = OrderedDict( { s: VOL_PRIOR for s in self.symbols } )
        
        self.start_time         = datetime.utcnow()
        self.update_status()
        self.equity_usd_init    = self.equity_usd
        self.equity_btc_init    = self.equity_btc
    
    
    def update_status( self ):
        
        account = self.client.fetchBalance()
        spot    = self.get_spot()

        #print(account)  
        self.equity_btc = self.usdtbalance / spot
        self.equity_usd = self.equity_btc * spot
                
        self.update_positions()
                
        self.deltas = OrderedDict( 
            { k: float(self.positions[ k ][ 'positionAmt' ]) for k in self.futures.keys()}
        )
        self.deltas[ BTC_SYMBOL ] = self.usdtbalance         
        
        
    def update_positions( self ):

        self.positions  = OrderedDict( { f: {
            'size':         0,
            'positionAmt':      0,
            'indexPrice':   None,
            'markPrice':    None
        } for f in self.futures.keys() } )
        positions       = self.client.fapiPrivateGetPositionRisk()
        #print('lala')
        #print(positions)
        
        for pos in positions:
            if 'BTC/USDT' in self.futures:
                self.positions[ 'BTC/USDT'] = pos
        
    
    def update_timeseries( self ):
        
        if self.monitor:
            return None
        
        for t in range( NLAGS, 0, -1 ):
            self.ts[ t ]    = cp.deepcopy( self.ts[ t - 1 ] )
        
        spot                    = self.get_spot()
        self.ts[ 0 ][ BTC_SYMBOL ]    = spot
        
        for c in self.futures.keys():
            
            bbo = self.get_bbo( c )
            bid = bbo[ 'bid' ]
            ask = bbo[ 'ask' ]

            if not bid is None and not ask is None:
                mid = 0.5 * ( bbo[ 'bid' ] + bbo[ 'ask' ] )
            else:
                continue
            self.ts[ 0 ][ c ]               = mid
                
        self.ts[ 0 ][ 'timestamp' ]  = datetime.utcnow()

        
    def update_vols( self ):
        
        if self.monitor:
            return None
        
        w   = EWMA_WGT_COV
        ts  = self.ts
        
        t   = [ ts[ i ][ 'timestamp' ] for i in range( NLAGS + 1 ) ]
        p   = { c: None for c in self.vols.keys() }
        for c in ts[ 0 ].keys():
            p[ c ] = [ ts[ i ][ c ] for i in range( NLAGS + 1 ) ]
        
        if any( x is None for x in t ):
            return None
        for c in self.vols.keys():
            if any( x is None for x in p[ c ] ):
                return None
        
        NSECS   = SECONDS_IN_YEAR
        cov_cap = COV_RETURN_CAP / NSECS
        
        for s in self.vols.keys():
            
            x   = p[ s ]            
            dx  = x[ 0 ] / x[ 1 ] - 1
            dt  = ( t[ 0 ] - t[ 1 ] ).total_seconds()
            v   = min( dx ** 2 / dt, cov_cap ) * NSECS
            v   = w * v + ( 1 - w ) * self.vols[ s ] ** 2
            
            self.vols[ s ] = math.sqrt( v )