Ejemplo n.º 1
0
    def __init__(self, hub: aiopubsub.Hub,
                 currency: mm_bot.model.currency.CurrencyPair, bc_address: str,
                 bc_scookie: str, bc_wallet_address: str,
                 bc_private_key_hex: str, base_address: str,
                 counter_address: str):
        self.side = 'maker'
        self.name = 'borderless'
        self._logger = logging.getLogger(self.__class__.__name__)
        self._loop = aiopubsub.loop.Loop(self._run,
                                         delay=config(
                                             'exchange_borderless_loop_delay',
                                             parser=int))
        self._hub = hub
        self._publisher = aiopubsub.Publisher(self._hub, self.name)
        self._currency = currency

        self._bc_rpc_address = bc_address
        self._bc_rpc_scookie = bc_scookie
        self._bc_wallet_address = bc_wallet_address
        self._bc_private_key_hex = bc_private_key_hex
        self._bc_base_address = base_address
        self._bc_counter_address = counter_address

        self._last_ask_best: mm_bot.model.book.PriceLevel = None
        self._last_bid_best: mm_bot.model.book.PriceLevel = None
Ejemplo n.º 2
0
def validate_cross_exchange_configs():
    invalid_params = []
    for param, parser in REQUIRED_PARAMS:
        val = config(param, parser=parser)
        if val == 'fillme':
            invalid_params.append(param)

    if len(invalid_params) > 0:
        raise InvalidParam('Please fill these values: \n\n' +
                           '\n'.join(invalid_params))
Ejemplo n.º 3
0
    async def get_open_orders(self) -> List[mm_bot.model.order.Order]:
        dry_run = config('dry_run', parser=bool)
        if dry_run:
            self._logger.info('DRY-RUN, get_open_orders')
            return []

        json = await _call_js_cli([
            'get', 'open_orders', '--bcRpcAddress', self._bc_rpc_address,
            '--bcRpcScookie', self._bc_rpc_scookie, '--bcAddress',
            self._bc_wallet_address
        ], self._logger)

        orders = []
        utc_now = datetime.utcnow()
        for order in json:
            if self.is_buy_order(self._currency, order):
                order_type = OrderType.BUY
            elif self.is_sell_order(self._currency, order):
                order_type = OrderType.SELL
            else:
                # other orders that i don't care
                continue

            tx_hash = order['txHash']
            tx_output_index = order['txOutputIndex']
            block_height = str(order['tradeHeight'])

            o = mm_bot.model.order.MakerOrder(
                exchange=self.name,
                currency=self._currency.to_currency(),
                status=mm_bot.model.constants.Status.OPEN,
                order_type=order_type,
                order_body=order,
                tx_hash=tx_hash,
                tx_output_index=tx_output_index,
                block_height=block_height,
                taker_order_body={},
                created_at=utc_now,
                updated_at=utc_now)

            orders.append(o)

        return orders
Ejemplo n.º 4
0
    async def create_orders(self, orders_to_open):
        """
        orders_to_open:
        [{
            'profit': profit
            'qty': qty,
            'price': best_bid_price_from_maker
        }]
        """
        dry_run = config('dry_run', parser=bool)
        orders_to_return = []
        for order in orders_to_open:
            if order['order_type'] == mm_bot.model.constants.OrderType.SELL:
                order_side = binance.AsyncClient.SIDE_SELL
            else:
                order_side = binance.AsyncClient.SIDE_BUY

            if dry_run:
                self._logger.info(f'DRY-RUN: Would create order {order}')
            else:
                price = self.normalize_price(order['price'])
                res = await self._client.create_order(
                        symbol=self._currency.to_currency(self.name),
                        side=order_side,
                        type=binance.AsyncClient.ORDER_TYPE_LIMIT,
                        timeInForce=binance.AsyncClient.TIME_IN_FORCE_GTC,
                        quantity=order['qty'],
                        price=price,
                        )

                order_str = dict(order)
                order_str['order_id'] = res['orderId']
                order_str['price'] = price
                order_str['qty'] = str(order['qty'])

                orders_to_return.append(order_str)

                self._logger.info(f'Created order {order_str} with res: {res}')

        return orders_to_return
Ejemplo n.º 5
0
async def _call_js_cli(args: List[str],
                       logger: Optional[logging.Logger] = None):
    if logger:
        filtered_params = ('--bcPrivateKeyHex', '--privateKey')

        log_args = args.copy()
        for param in filtered_params:
            if param in log_args:
                log_args[log_args.index(param) + 1] = '***'

        logger.info('call_js_cli %s', ' '.join(log_args))

    dry_run = config('dry_run', parser=bool)
    cmd = ' '.join(['/usr/bin/env', 'node', str(CLI_PATH)] + args)
    proc = await asyncio.create_subprocess_shell(
        cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    # print(f'[{cmd!r} exited with {proc.returncode}] out: {stdout}, err: {stderr}')

    if proc.returncode == 0:
        try:
            result = json.loads(stdout.decode('utf8'))
            if 'status' in result and result['status'] == 1:
                raise JSCallFailedError(result['status'],
                                        stdout.decode('utf8'))
            return result
        except:
            if logger:
                logger.exception(
                    'failed to decode results from borderless cli, %s',
                    stdout.decode('utf8'))
            raise JSCallFailedError(1, stdout.decode('utf8'))
    else:
        err_msg = stderr.decode('utf8')
        if 'ECONNREFUSED' in err_msg:
            print('Exiting as it failed to connect to the miner', err_msg)
            sys.exit(1)
        raise JSCallFailedError(proc.returncode, err_msg)
Ejemplo n.º 6
0
    async def get_unmatched_orders(self):
        dry_run = config('dry_run', parser=bool)
        if dry_run:
            self._logger.info('DRY-RUN, get_unmatched_orders')
            return []

        json = await _call_js_cli([
            'get', 'unmatched_orders', '--bcRpcAddress', self._bc_rpc_address,
            '--bcRpcScookie', self._bc_rpc_scookie, '--bcAddress',
            self._bc_wallet_address
        ], self._logger)

        unmatched_orders = []
        for order in json:
            tx_hash = order['txHash']
            tx_output_index = order['txOutputIndex']
            unmatched_orders.append({
                'tx_hash': tx_hash,
                'tx_output_index': tx_output_index
            })

        return unmatched_orders
Ejemplo n.º 7
0
async def load_config(request):
    strategy = 'cross_market'

    config_path = get_config_path(strategy)
    params = collections.defaultdict(lambda: '')
    if os.path.isfile(config_path):
        with open(config_path, 'r') as f:
            params = yaml.load(f, Loader=yaml.FullLoader)['mmbc']
    else:
        default_keys = [
            'cancel_order_threshold',
            'max_open_orders',
            'max_open_taker_orders',
            'max_qty_per_order',
            'min_profitability_rate',
        ]
        for key in default_keys:
            params[key] = config(key, parser=str)

    template = jinja_env.get_template(f'{strategy}.html')
    html_content = template.render(params=params)

    return html(html_content)
Ejemplo n.º 8
0
def validate():
    strategy_name = config(STRATEGY_NAME_KEY, parser=str)
    if strategy_name == 'cross_market':
        validate_cross_exchange_configs()
Ejemplo n.º 9
0
logger.setLevel(logging.WARN)


session = Session(app, interface=InMemorySessionInterface())

# we a very simple way for auth
app.token = uuid.uuid4().hex

jinja_env = Environment(loader=PackageLoader('server', 'server/templates'))

from mm_bot.config import config
from mm_bot.config.validator import REQUIRED_PARAMS, STRATEGY_NAME_KEY
from mm_bot.helpers import get_config_path, signal_config_reloaded, LOGS_FILE_PATTERN
from mm_bot.model.repository import OrderRepository

url = config('database_url', parser=str)
order_repository = OrderRepository(url)

def get_borderless_balance(endpoint, public_key, scookie):
    url = f"{endpoint}/rpc"

    payload = {"id":1,"jsonrpc":"2.0","method":"getBalance","params":[public_key]}
    headers = {'Content-Type': 'application/json'}
    if scookie:
        token = ':' + scookie
        token = base64.b64encode(token.encode('ascii')).decode('ascii')
        headers['authorization'] = f"Basic {token}"
    try:
        response = requests.request("POST", url, headers=headers, json=payload)
        if response.ok:
            return response.json()
Ejemplo n.º 10
0
    async def create_orders(self, orders_to_open):
        """
        orders_to_open:
        [{
            'qty': qty,
            'order_type': 'sell|buy',
            'price': best_bid_price_from_maker,
            'ask_nrg_rate': Decimal, # 1 ETH can buy ? NRG
            'bid_nrg_rate': Decimal # 1 BTC can buy ? NRG
        }]

        1. call js lib to create order in borderless
        2. create maker orders and save them to db

        """
        results = []
        for order in orders_to_open:
            if order['order_type'] == OrderType.BUY:
                # eg: ETH/BTC, base is ETH, quote is BTC

                # if BUY ETH/BTC, you receive ETH (base) and send BTC (counter)
                # currency.base = receives_to_chain = bids and vice-versa
                receives_to_chain = self._currency.base.lower()
                receives_unit = decimal_to_str(order['qty'])

                sends_from_chain = self._currency.counter.lower()
                sends_unit = decimal_to_str(order['qty'] * order['price'])

                # BTC NRG rate, if i default to send sends_unit (BTC)
                sends_from_address = self._bc_counter_address
                receives_to_address = self._bc_base_address
            else:
                # if SELL ETH/BTC, you receive BTC and send ETH
                receives_to_chain = self._currency.counter.lower()
                receives_unit = decimal_to_str(order['qty'] * order['price'])

                sends_from_chain = self._currency.base.lower()
                sends_unit = decimal_to_str(order['qty'])

                # ETH NRG rate, if i default to send sends_unit (ETH)

                sends_from_address = self._bc_base_address
                receives_to_address = self._bc_counter_address

            # I loss my collateralized_nrg, so use sends_from_chain
            collateralized_nrg = await self.calculate_collateralized_nrg(
                sends_from_chain, Decimal(sends_unit))

            order_body = {
                'collateralizedNrg': collateralized_nrg,
                'sendsFromChain': sends_from_chain,
                'sendsUnit': sends_unit,
                'receivesUnit': receives_unit,
                'receivesToChain': receives_to_chain,
                'orderType': order['order_type'],
                'askNrgRate': decimal_to_str(order['ask_nrg_rate']),
                'bidNrgRate': decimal_to_str(order['bid_nrg_rate']),
                'qty': decimal_to_str(order['qty']),
            }
            self._logger.info(f'Creating order {order_body}')

            dry_run = config('dry_run', parser=bool)
            if dry_run:
                self._logger.info('DRY-RUN, create maker order, %s', order)
                continue
            else:
                json = await _call_js_cli(
                    [
                        'create',
                        'maker',
                        '--bcRpcAddress',
                        self._bc_rpc_address,
                        '--bcRpcScookie',
                        self._bc_rpc_scookie,
                        '--bcAddress',
                        self._bc_wallet_address,
                        '--shiftMaker',
                        self.get_confirmation_blocks(sends_from_chain),
                        '--shiftTaker',
                        self.get_confirmation_blocks(receives_to_chain),
                        '--depositLength',
                        config('exchange_borderless_deposit_length',
                               parser=str),
                        '--settleLength',
                        config('exchange_borderless_settlement_window_length',
                               parser=str),
                        '--sendsFromChain',
                        sends_from_chain,
                        '--receivesToChain',
                        receives_to_chain,
                        '--sendsFromAddress',
                        sends_from_address,
                        '--receivesToAddress',
                        receives_to_address,
                        '--sendsUnit',
                        sends_unit,
                        '--receivesUnit',
                        receives_unit,
                        '--bcPrivateKeyHex',
                        self._bc_private_key_hex,
                        '--collateralizedNrg',
                        collateralized_nrg,
                        '--nrgUnit',
                        collateralized_nrg,  # does not allow partial order
                        '--additionalTxFee',
                        '0'
                    ],
                    self._logger)
                self._logger.info(f'Created order {json}')
                json['order_body'] = order_body

                results.append(json)

        return results
Ejemplo n.º 11
0
    async def get_order_book(
        self, currency: mm_bot.model.currency.CurrencyPair
    ) -> mm_bot.model.book.OrderBook:
        dry_run = config('dry_run', parser=bool)
        if dry_run:
            self._logger.info('DRY-RUN, get_order_book')
            return mm_bot.model.book.OrderBook([], [], 0, 0)

        json = await _call_js_cli([
            'get',
            'order_book',
            '--bcRpcAddress',
            self._bc_rpc_address,
            '--bcRpcScookie',
            self._bc_rpc_scookie,
            '--bcAddress',
            self._bc_wallet_address,
        ])

        # key is price, value is quantity
        json_bids: DefaultDict[Decimal, Decimal] = collections.defaultdict(
            lambda: Decimal('0'))
        json_asks: DefaultDict[Decimal, Decimal] = collections.defaultdict(
            lambda: Decimal('0'))

        bid_nrg_rate = Decimal('0')
        ask_nrg_rate = Decimal('0')
        for order in json:
            price = self.calc_price(currency, order)
            quantity = self.calc_quantity(currency, order)

            if self.is_buy_order(currency, order):
                json_bids[price] += quantity
                sends_unit = Decimal(order['sendsUnit'])
                bid_nrg_rate += (Decimal(order['collateralizedNrg']) /
                                 sends_unit)
            elif self.is_sell_order(currency, order):
                json_asks[price] += quantity

                sends_unit = Decimal(order['sendsUnit'])
                ask_nrg_rate += (Decimal(order['collateralizedNrg']) /
                                 sends_unit)
            else:
                # idon't care this
                continue

        bid = [
            mm_bot.model.book.PriceLevel(price, quantity)
            for price, quantity in json_bids.items()
        ]
        ask = [
            mm_bot.model.book.PriceLevel(price, quantity)
            for price, quantity in json_asks.items()
        ]
        bid = sorted(bid, key=lambda p: p.price, reverse=True)
        ask = sorted(ask, key=lambda p: p.price)

        if len(bid) == 0:
            bid_nrg_rate = Decimal('0')
        else:
            bid_nrg_rate = bid_nrg_rate / len(bid)

        if len(ask) == 0:
            ask_nrg_rate = Decimal('0')
        else:
            ask_nrg_rate = ask_nrg_rate / len(ask)

        return mm_bot.model.book.OrderBook(bid, ask, bid_nrg_rate,
                                           ask_nrg_rate)