Exemple #1
0
    def test_wallet_reload_sequence(self, private_key, env):
        wallet = Wallet(private_key=private_key, env=env)

        wallet.initialise_wallet()
        account_sequence = wallet.sequence

        wallet.increment_account_sequence()

        wallet.reload_account_sequence()

        assert wallet.sequence == account_sequence
class Binance():
    def __init__(self):
        self.env = BinanceEnvironment.get_testnet_env()
        self.client = HttpApiClient(env=self.env)
        with open('secret/bnb_key.txt', 'r') as f:
            key = f.readline()
        self.wallet = Wallet(key, env=self.env)
        self.address = self.wallet.address
        driver_logger.info(f'bnb address: {self.address}')

    def thor_swap(self, i_symbol, amount, to_address, memo):
        transfer_msg = TransferMsg(
            wallet=self.wallet,
            symbol=i_symbol,
            amount=amount,
            to_address=to_address,
            memo=memo
        )
        res = self.client.broadcast_msg(transfer_msg, sync=True)
        tx_hash = res[0]['hash']
        driver_logger.debug(f'boardcasted hash {tx_hash}')
        self.wallet.reload_account_sequence()
        return tx_hash
class BinanceApi:
    def __init__(self, key, test=False):
        self.BUSD = 'BUSD-BD1'
        self.BNB = 'BNB'
        self.pairs = []
        self.bnb_pairs = {}
        self.busd_pairs = {}
        self.api_instance = binance_client.DefaultApi()
        if test:
            self.env = BinanceEnvironment.get_testnet_env()
            self.RUNE = 'RUNE-67C'
            self.api_instance.api_client.configuration.host = self.env.api_url + '/api'
        else:
            self.env = BinanceEnvironment(api_url='https://dex-european.binance.org',
                                          wss_url='wss://dex.binance.org/api/',
                                          hrp='bnb')
            self.RUNE = 'RUNE-B1A'
        binance_logger.info(f'Binance connected to node: {self.env.api_url}')
        self.client = HttpApiClient(env=self.env)
        self.wallet = Wallet(private_key=key, env=self.env)
        self.wallet.reload_account_sequence()
        self.account_info()

    def parse_market(self):
        offset = 0
        try:
            logging.info("parsing market pairs")
            while True:
                pairs = self.api_instance.get_pairs(limit=500, offset=offset)
                for pair in pairs:
                    self.pairs.append(pair)
                    if pair["quote_asset_symbol"] == self.BNB:
                        self.bnb_pairs[pair["base_asset_symbol"]] = pair["lot_size"]
                    elif pair["quote_asset_symbol"] == self.BUSD:
                        self.busd_pairs[pair["base_asset_symbol"]] = pair["lot_size"]
                offset += 500
                time.sleep(1)
        except ApiException as e:
            if e.reason == "Bad Request":
                logging.info("parsing finished, %s market pairs" % len(self.pairs))
                logging.debug("bnb pairs: %s" % self.bnb_pairs)
                logging.debug("busd pairs: %s" % self.busd_pairs)
            else:
                logging.info("Exception when calling DefaultApi->getPairs: %s\n" % e)

    def depth(self, symbol, bnb=True):
        if bnb:
            pair = symbol + '_' + self.BNB
        else:
            pair = symbol + '_' + self.BUSD
        try:
            depth = self.api_instance.get_depth(symbol=pair, limit=5)
            binance_logger.debug(f'{pair} market depth bids:{depth.bids[0]} asks:{depth.asks[0]}')
            return depth
        except ApiException as e:
            if e.reason == 'Gateway Time-out':
                logging.info("cloudfront error")
                time.sleep(5)
                logging.info("recalling get_depth()")
                return self.depth(symbol=pair, bnb=bnb)
            binance_logger.debug(f'Exception when calling get_depth() {e}')

    def account_info(self):
        try:
            account_info = self.api_instance.get_account(self.wallet.address)
            binance_logger.info(f'account info: {account_info}')
            return account_info
        except ApiException as e:
            binance_logger.debug(f'Exception when calling get_account() {e}')
        # tracking = list(filter(lambda coin: coin['symbol'] == self.pairs[token], api_response.balances))
        # pnl = (float(tracking[0]['free']) - float(self.balances[token])) / float(self.balances[token])
        # self.balances[token] = float(tracking[0]['free'])

    def get_balance(self):
        try:
            account_info = self.api_instance.get_account(self.wallet.address)
            balance_info = account_info.balances
            binance_logger.info(f'balance info: {balance_info}')
            binance_logger.info(f'sequence number: {account_info.sequence}')
            return balance_info
        except ApiException as e:
            binance_logger.debug(f'Exception when calling get_account() {e}')

    def binance_check_hash(self, hash):
        while True:
            try:
                api_response = self.api_instance.get_closed_orders(self.wallet.address)
                order = list(filter(lambda order: order.transaction_hash == hash, api_response.order))
                if order:
                    binance_logger.debug(f'order detail {order[0]}')
                    binance_logger.info(f'order status {order[0].status}')
                    return order[0]
                time.sleep(0.5)
            except ApiException as e:
                binance_logger.debug(f'Exception when calling get_closed_orders() {e}')

    def dex_buy(self, symbol, quantity, price=None, bnb=True):
        self.wallet.reload_account_sequence()
        if price is None:
            depth = self.depth(symbol, bnb=bnb)
            price = float(depth.asks[0][0])
        if bnb:
            pair = symbol + '_' + self.BNB
        else:
            pair = symbol + '_' + self.BUSD
        buy_msg = NewOrderMsg(
            wallet=self.wallet,
            symbol=pair,
            time_in_force=TimeInForce.IMMEDIATE_OR_CANCEL,
            order_type=OrderType.LIMIT,
            side=OrderSide.BUY,
            price=price,
            quantity=quantity
        )
        res = self.client.broadcast_msg(buy_msg, sync=True)
        binance_logger.info(f'buy response: {res}')
        hash = res[0]['hash']
        time.sleep(0.1)
        return self.binance_check_hash(hash)

    def dex_sell(self, symbol, quantity, price=None, bnb=True):
        self.wallet.reload_account_sequence()
        if price is None:
            depth = self.depth(symbol, bnb=bnb)
            price = float(depth.bids[0][0])
        if bnb:
            pair = symbol + '_' + self.BNB
        else:
            pair = symbol + '_' + self.BUSD
        sell_msg = NewOrderMsg(
            wallet=self.wallet,
            symbol=pair,
            time_in_force=TimeInForce.IMMEDIATE_OR_CANCEL,
            order_type=OrderType.LIMIT,
            side=OrderSide.SELL,
            price=price,
            quantity=quantity
        )
        res = self.client.broadcast_msg(sell_msg, sync=True)
        binance_logger.info(f'sell response: {res}')
        hash = res[0]['hash']
        time.sleep(0.1)
        return self.binance_check_hash(hash)

    def thor_swap(self, chain, i_symbol, o_symbol, amount, to_address, dest_address=None, limit=None):
        if limit:
            memo = 'SWAP:' + chain + '.' + o_symbol + '::' + str(int(limit * 10**8))
        else:
            memo = 'SWAP:' + chain + '.' + o_symbol
        transfer_msg = TransferMsg(
            wallet=self.wallet,
            symbol=i_symbol,
            amount=amount,
            to_address=to_address,
            memo=memo
        )
        res = self.client.broadcast_msg(transfer_msg, sync=True)
        binance_logger.info(f'swap response: {res}')
        hash = res[0]['hash']
        return hash

    def thor_smart_swap(self, chain, i_symbol, o_symbol, amount, to_address, dest_address=None, limit=None, slice=3):
        hashes = []
        piece = float(amount/slice)
        binance_logger.info(f'smart swap with {slice} slices of {piece} {i_symbol}')
        if limit:
            piece_limit = float(limit/slice)
            for part in range(slice):
                hash = self.thor_swap(chain=chain, i_symbol=i_symbol, o_symbol=o_symbol, amount=piece, to_address=to_address, limit=piece_limit)
                hashes.append(hash)
        else:
            for part in range(slice):
                hash = self.thor_swap(chain=chain, i_symbol=i_symbol, o_symbol=o_symbol, amount=piece, to_address=to_address)
                hashes.append(hash)
        return hashes

    def thor_stake(self, chain, symbol, amount, runeamount, to_address):
        memo = 'STAKE:' + chain + '.' + symbol
        multi_transfer_msg = MultiTransferMsg(
            wallet=self.wallet,
            transfers=[
                Transfer(symbol=self.RUNE, amount=runeamount),
                Transfer(symbol=symbol, amount=amount),
            ],
            to_address=to_address,
            memo=memo
        )
        res = self.client.broadcast_msg(multi_transfer_msg, sync=True)
        binance_logger.info(f'stake response: {res}')
        hash = res[0]['hash']
        return hash

    def thor_withdraw(self, chain, symbol, percent, to_address):
        if chain == 'BNB':
            payload = 0.00000001
            payload_token = 'BNB'
        else:
            payload = 0
        percent_str = str(int(percent * 10**2))
        memo = "WITHDRAW:" + chain + '.' + symbol + ':' + percent_str
        transfer_msg = TransferMsg(
            wallet=self.wallet,
            symbol=payload_token,
            amount=payload,
            to_address=to_address,
            memo=memo
        )
        res = self.client.broadcast_msg(transfer_msg, sync=True)
        binance_logger.info(f'withdraw response: {res}')
        hash = res[0]['hash']
        return hash
Exemple #4
0
class Monitor(object):
    def __init__(self):

        self.env = BinanceEnvironment(
            api_url='https://dex-asiapacific.binance.org',
            wss_url='wss://dex.binance.org/api/',
            hrp='bnb')
        with open('bnb_real_key.txt', 'r') as f:
            key = f.readline()
        # for testing
        # self.env = BinanceEnvironment.get_testnet_env()
        # with open('bnb_key.txt', 'r') as f:
        #     key = f.readline()
        f.close()
        self.client = HttpApiClient(env=self.env)
        self.wallet = Wallet(key, env=self.env)
        self.address = self.wallet.address
        self.ftx = ccxt.ftx({
            'apiKey':
            '6p37l5AXOzIgFzfeSzkcuPhuaYcw3GcpJrU83ROy',
            'secret':
            '3SphoJJ6Gl_w5pPPkGmQpKbVGN9oPiUgxqs4ob_H'
        })
        #self.assets = ['USD', 'USDT']
        self.assets = ['USD']
        self.BNB_BUSD = 'BNB.BUSD-BD1'
        self.pool = THORChain()

    def get_bnb_balance(self, asset=None):
        balance = self.client.get_account(self.address)['balances']
        if asset:
            return next(
                filter(lambda symbol: symbol['symbol'] == asset,
                       balance))['free']
        return balance

    def thor_swap(self, i_symbol, amount, to_address, memo):
        self.wallet.reload_account_sequence()
        transfer_msg = TransferMsg(wallet=self.wallet,
                                   symbol=i_symbol,
                                   amount=amount,
                                   to_address=to_address,
                                   memo=memo)
        res = self.client.broadcast_msg(transfer_msg, sync=True)
        tx_hash = res[0]['hash']
        arb_logger.debug(f'broadcasting {tx_hash}')
        return tx_hash

    def round_down(self, number, precision):
        return decimal_to_precision(number_to_string(number), TRUNCATE,
                                    precision)

    def book_oracle_asks(self, book, level, max_output, omega=0.8):
        """ return array of available volume and corresponding unit price """
        book_output_volume = []
        book_output_price = []
        cap = False
        # Book[0-level][0] = Price; Book[0-level][1] = Volume
        for i in range(0, level):
            if cap:
                break
            book_output_price.append(book[i][0])
            out_volume = book[i][1] * omega
            if i > 0:
                out_volume += book_output_volume[i - 1]
            if out_volume >= max_output:
                book_output_volume.append(max_output)
                cap = True
            else:
                book_output_volume.append(out_volume)
        return book_output_price, book_output_volume

    def ftx_price_feed_buy(self, asset, level=7, max_rune=700):
        """Buy Rune on Ftx, sell Rune on Bepswap"""
        ftx_balance = self.get_ftx_balance()
        if ftx_balance['USD'] < 720:
            arb_logger.info(f'need recharge')
            self.deposit_ftx()
            while True:
                time.sleep(1)
                ftx_balance = self.get_ftx_balance()
                if ftx_balance['USD'] > 720:
                    arb_logger.info(f'recharge finished')
                    break
        while True:
            try:
                book = self.ftx.fetch_order_book(f'RUNE/{asset}', level)
                break
            except RequestTimeout as e:
                arb_logger.debug(
                    'Request timeout calling self.ftx.fetch_order_book: {e}')
        arb_logger.debug(f'route 2: fetching asset {asset} \n {book}')
        # Route 2: clearing ask side
        oracle = self.book_oracle_asks(book['asks'], level, max_rune)
        fee = 1.0007
        oracle_out_price = oracle[0]
        oracle_out_volume = oracle[1]
        for i in range(0, len(oracle_out_volume)):
            out_price = oracle_out_price[i]
            out_volume_order = self.round_down(oracle_out_volume[i], 1)
            in_volume = out_price * float(out_volume_order)
            out_volume_real = oracle_out_volume[i] / fee
            # book: asset -> rune
            arb_logger.debug(
                f'book: {in_volume} {asset} := {out_volume_real} RUNE '
                f'RUNE market ask price := {out_price}')
            # pool: rune -> asset
            route = self.pool.get_swap_memo(self.BNB_BUSD,
                                            'sell',
                                            amount=out_volume_real,
                                            limit=in_volume)
            expected = route[0]
            optimal_slice = route[1]
            optimal_expected = route[2]
            pool_address = route[3]
            memo = route[4]
            arb_logger.debug(
                f'pool: {out_volume_real} RUNE := {expected} {asset}')
            arb_logger.debug(f'{memo}')
            diff = expected - in_volume
            if diff > 1.3:
                arb_logger.error(f'profit: {diff}')
                ftx_order = self.ftx.create_order(symbol=f'RUNE/{asset}',
                                                  side='buy',
                                                  amount=out_volume_order,
                                                  price=out_price,
                                                  type='limit')
                tx_hash = self.thor_swap(self.pool.rune,
                                         float(out_volume_real), pool_address,
                                         memo)
                self.pool.get_tx_in(tx_hash=tx_hash)
                database.insert_tx({
                    'thorchain': tx_hash,
                    'ftx': ftx_order['id']
                })
                break
            else:
                arb_logger.warning(f'profit: {diff}')
                time.sleep(0.2)

    def get_ftx_balance(self):
        try:
            return self.ftx.fetch_balance()['total']
        except Exception as e:
            print(e)

    def get_ftx_deposit(self, coin='RUNE'):
        try:
            return self.ftx.fetch_deposit_address(coin)
        except Exception as e:
            print(e)

    def deposit_ftx(self):
        info = self.get_ftx_deposit()
        address = info['address']
        memo = info['tag']
        balance = self.get_bnb_balance(asset='BUSD-BD1')
        print(f'sending {balance} to {address} with memo {memo}')
        self.thor_swap('BUSD-BD1', float(balance) - 1, address, memo)

    def profit_report(self):
        database.add_timestamp()
        txs = database.get_unaccounted_txs()
        rune_gain = 0
        usd_gain = 0
        for tx in txs:
            thor_order = self.pool.get_tx_out(tx_hash=tx['thorchain'])
            thor_rune_in = int(thor_order._in.coins[0]['amount']) / 10**8
            thor_usd_out = int(thor_order.out[0].coins[0]['amount']) / 10**8
            ftx_order = self.ftx.fetch_order(tx['ftx'])
            ftx_usd_in = ftx_order['cost']
            ftx_rune_out = ftx_order['filled']
            ftx_remaining = ftx_order['remaining']
            if ftx_remaining != 0:
                database.add_profit_txs(tx['_id'], 0, 0, 'ftx fail')
            elif thor_order.out[0].coins[0]['asset'] == 'BNB.RUNE-B1A':
                database.add_profit_txs(tx['_id'], -1 * ftx_usd_in,
                                        ftx_rune_out - 1, 'thor fail')
            else:
                temp_rune_gain = ftx_rune_out - thor_rune_in
                temp_usd_gain = thor_usd_out - ftx_usd_in
                database.add_profit_txs(tx['_id'], temp_usd_gain,
                                        temp_rune_gain, 'success')
                rune_gain += temp_rune_gain
                usd_gain += temp_usd_gain
        print(f'rune_gain: {rune_gain}')
        print(f'usd_gain: {usd_gain}')

    def fail_report(self):
        txs = database.get_failed_txs()
        for tx in txs:
            print(tx)