예제 #1
0
    def setup_method(self):
        self.clearing_server = ErisXMockClearingAPIServer()
        do_nothing = lambda *args: None

        orig_get_account = ErisxApi.get_account
        FixEngine.logon = do_nothing
        ErisxApi.get_account = do_nothing
        self.account_id = 0
        self.client = ErisxApi(fix_trading_endpoint="127.0.0.1:1752", fix_trading_user="******",
                               fix_marketdata_endpoint="127.0.0.1:1753", fix_marketdata_user="******",
                               password="******",
                               clearing_url="https://127.0.0.1/api/v1/",
                               api_key="key", api_secret="secret", certs=None, account_id=self.account_id)
        ErisxApi.get_account = orig_get_account
    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='erisx-market-maker-keeper')

        parser.add_argument("--erisx-clearing-url",
                            type=str,
                            required=True,
                            help="Address of the ErisX clearing server")

        parser.add_argument("--fix-trading-endpoint",
                            type=str,
                            required=True,
                            help="FIX endpoint for ErisX trading")

        parser.add_argument("--fix-trading-user",
                            type=str,
                            required=True,
                            help="Account ID for ErisX trading")

        parser.add_argument("--fix-marketdata-endpoint",
                            type=str,
                            required=True,
                            help="FIX endpoint for ErisX market data")

        parser.add_argument("--fix-marketdata-user",
                            type=str,
                            required=True,
                            help="Account ID for ErisX market data")

        parser.add_argument("--erisx-password",
                            type=str,
                            required=True,
                            help="password for FIX account")

        parser.add_argument("--erisx-api-key",
                            type=str,
                            required=True,
                            help="API key for ErisX REST API")

        parser.add_argument("--erisx-api-secret",
                            type=str,
                            required=True,
                            help="API secret for ErisX REST API")

        parser.add_argument(
            "--erisx-certs",
            type=str,
            default=None,
            help=
            "Client key pair used to authenticate to Production FIX endpoints")

        parser.add_argument("--account-id",
                            type=int,
                            default=0,
                            help="ErisX account ID index")

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument(
            "--refresh-frequency",
            type=int,
            default=3,
            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        self.erisx_api = ErisxApi(
            fix_trading_endpoint=self.arguments.fix_trading_endpoint,
            fix_trading_user=self.arguments.fix_trading_user,
            fix_marketdata_endpoint=self.arguments.fix_marketdata_endpoint,
            fix_marketdata_user=self.arguments.fix_marketdata_user,
            password=self.arguments.erisx_password,
            clearing_url=self.arguments.erisx_clearing_url,
            api_key=self.arguments.erisx_api_key,
            api_secret=self.arguments.erisx_api_secret,
            certs=self.arguments.erisx_certs,
            account_id=self.arguments.account_id)

        self.market_info = self.erisx_api.get_markets()

        super().__init__(self.arguments, self.erisx_api)
class ErisXMarketMakerKeeper(CEXKeeperAPI):
    """
    Keeper acting as a market maker on ErisX.
    """

    logger = logging.getLogger()

    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='erisx-market-maker-keeper')

        parser.add_argument("--erisx-clearing-url",
                            type=str,
                            required=True,
                            help="Address of the ErisX clearing server")

        parser.add_argument("--fix-trading-endpoint",
                            type=str,
                            required=True,
                            help="FIX endpoint for ErisX trading")

        parser.add_argument("--fix-trading-user",
                            type=str,
                            required=True,
                            help="Account ID for ErisX trading")

        parser.add_argument("--fix-marketdata-endpoint",
                            type=str,
                            required=True,
                            help="FIX endpoint for ErisX market data")

        parser.add_argument("--fix-marketdata-user",
                            type=str,
                            required=True,
                            help="Account ID for ErisX market data")

        parser.add_argument("--erisx-password",
                            type=str,
                            required=True,
                            help="password for FIX account")

        parser.add_argument("--erisx-api-key",
                            type=str,
                            required=True,
                            help="API key for ErisX REST API")

        parser.add_argument("--erisx-api-secret",
                            type=str,
                            required=True,
                            help="API secret for ErisX REST API")

        parser.add_argument(
            "--erisx-certs",
            type=str,
            default=None,
            help=
            "Client key pair used to authenticate to Production FIX endpoints")

        parser.add_argument("--account-id",
                            type=int,
                            default=0,
                            help="ErisX account ID index")

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument(
            "--refresh-frequency",
            type=int,
            default=3,
            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        self.erisx_api = ErisxApi(
            fix_trading_endpoint=self.arguments.fix_trading_endpoint,
            fix_trading_user=self.arguments.fix_trading_user,
            fix_marketdata_endpoint=self.arguments.fix_marketdata_endpoint,
            fix_marketdata_user=self.arguments.fix_marketdata_user,
            password=self.arguments.erisx_password,
            clearing_url=self.arguments.erisx_clearing_url,
            api_key=self.arguments.erisx_api_key,
            api_secret=self.arguments.erisx_api_secret,
            certs=self.arguments.erisx_certs,
            account_id=self.arguments.account_id)

        self.market_info = self.erisx_api.get_markets()

        super().__init__(self.arguments, self.erisx_api)

    def init_order_book_manager(self, arguments, erisx_api):
        self.order_book_manager = ErisXOrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(
            lambda: self.erisx_api.get_orders(self.pair()))
        self.order_book_manager.get_balances_with(
            lambda: self.erisx_api.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.erisx_api.cancel_order(
                order.order_id, self.pair(), order.is_sell))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)

        self.order_book_manager.pair = self.pair()
        self.order_book_manager.start()

    def main(self):
        with ErisXLifecycle() as lifecycle:
            lifecycle.initial_delay(10)
            lifecycle.every(1, self.synchronize_orders)
            lifecycle.on_shutdown(self.shutdown)

    def pair(self):
        return self.arguments.pair

    def token_sell(self) -> str:
        return self.arguments.pair.split('/')[0].upper()

    def token_buy(self) -> str:
        return self.arguments.pair.split('/')[1].upper()

    def our_available_balance(self, our_balances: dict, token: str) -> Wad:
        if 'newrelease' in self.arguments.erisx_clearing_url:
            if token == 'ETH':
                token = 'TETH'
            if token == 'BTC':
                token = 'TBTC'

        token_balances = list(
            filter(lambda asset: asset['asset_type'].upper() == token,
                   our_balances))
        if token_balances:
            return Wad.from_number(
                float(token_balances[0]['available_to_trade']))
        else:
            return Wad(0)

    def place_orders(self, new_orders):
        def place_order_function(new_order_to_be_placed):
            amount = new_order_to_be_placed.pay_amount if new_order_to_be_placed.is_sell else new_order_to_be_placed.buy_amount
            # automatically retrive qty precision
            round_lot = str(self.market_info[self.pair()]["RoundLot"])
            price_increment = str(
                self.market_info[self.pair()]["MinPriceIncrement"])
            order_qty_precision = abs(Decimal(round_lot).as_tuple().exponent)
            price_precision = abs(Decimal(price_increment).as_tuple().exponent)

            order_id = self.erisx_api.place_order(
                pair=self.pair().upper(),
                is_sell=new_order_to_be_placed.is_sell,
                price=round(Wad.__float__(new_order_to_be_placed.price),
                            price_precision),
                amount=round(Wad.__float__(amount), order_qty_precision))

            return Order(str(order_id), int(time.time()), self.pair(),
                         new_order_to_be_placed.is_sell,
                         new_order_to_be_placed.price, amount)

        for new_order in new_orders:
            self.order_book_manager.place_order(
                lambda new_order=new_order: place_order_function(new_order))
예제 #4
0
import logging
import sys
import time
import uuid

from pprint import pprint
from pyexchange.erisx import ErisxApi

logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(threadName)-18s %(message)s', level=logging.DEBUG)
client = ErisxApi(fix_trading_endpoint=sys.argv[1], fix_trading_user=sys.argv[2],
                  fix_marketdata_endpoint=sys.argv[3], fix_marketdata_user=sys.argv[4],
                  password=sys.argv[5],
                  clearing_url="https://clearing.erisx.com/api/v1/",
                  api_key=sys.argv[6], api_secret=sys.argv[7], certs=None, account_id=0)
# print(sys.argv)
print("ErisxApi created\n")
# print(client.get_balances())
time.sleep(10)


"""
Reset Password

Password is shared across both fix_marketdata and fix_trading sockets.
Can simply set new_password to the desired value, wait for the change to register, and then start a new session.

TODO: Set certs=<ABSOLUTE_PATH_TO_CERTS> string when resetting passwords in production.
"""
print("Resetting Password")
reset_password_id = uuid.uuid4()
print("Password reset request_id: ", reset_password_id)
    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='erisx-market-maker-keeper')

        parser.add_argument("--erisx-clearing-url", type=str, required=True,
                            help="Address of the ErisX clearing server")

        parser.add_argument("--fix-trading-endpoint", type=str, required=True,
                            help="FIX endpoint for ErisX trading")

        parser.add_argument("--fix-trading-user", type=str, required=True,
                            help="Account ID for ErisX trading")

        parser.add_argument("--fix-marketdata-endpoint", type=str, required=True,
                            help="FIX endpoint for ErisX market data")

        parser.add_argument("--fix-marketdata-user", type=str, required=True,
                            help="Account ID for ErisX market data")

        parser.add_argument("--erisx-password", type=str, required=True,
                            help="password for FIX account")

        parser.add_argument("--erisx-api-key", type=str, required=True,
                            help="API key for ErisX REST API")

        parser.add_argument("--erisx-api-secret", type=str, required=True,
                            help="API secret for ErisX REST API")

        parser.add_argument("--erisx-certs", type=str, default=None,
                            help="Client key pair used to authenticate to Production FIX endpoints")

        parser.add_argument("--account-id", type=int, default=0,
                            help="ErisX account ID index")

        parser.add_argument("--pair", type=str, required=True,
                            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--config", type=str, required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed", type=str, required=True,
                            help="Source of price feed")

        parser.add_argument("--price-feed-expiry", type=int, default=120,
                            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed", type=str,
                            help="Source of spread feed")

        parser.add_argument("--spread-feed-expiry", type=int, default=3600,
                            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed", type=str,
                            help="Source of control feed")

        parser.add_argument("--control-feed-expiry", type=int, default=86400,
                            help="Maximum age of the control feed (in seconds, default: 86400)")

        parser.add_argument("--order-history", type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument("--order-history-every", type=int, default=30,
                            help="Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument("--refresh-frequency", type=int, default=3,
                            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument("--debug", dest='debug', action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        setup_logging(self.arguments)

        self.erisx_api = ErisxApi(fix_trading_endpoint=self.arguments.fix_trading_endpoint,
                                  fix_trading_user=self.arguments.fix_trading_user,
                                  fix_marketdata_endpoint=self.arguments.fix_marketdata_endpoint,
                                  fix_marketdata_user=self.arguments.fix_marketdata_user,
                                  password=self.arguments.erisx_password,
                                  clearing_url=self.arguments.erisx_clearing_url,
                                  api_key=self.arguments.erisx_api_key,
                                  api_secret=self.arguments.erisx_api_secret,
                                  certs=self.arguments.erisx_certs,
                                  account_id=self.arguments.account_id)

        termination_time = time.time() + 15
        while self.erisx_api.fix_trading.connection_state != FixConnectionState.LOGGED_IN and self.erisx_api.fix_marketdata.connection_state != FixConnectionState.LOGGED_IN:
            time.sleep(0.3)
            if time.time() > termination_time:
                raise RuntimeError("Timed out while waiting to log in")

        self.market_info = self.erisx_api.get_markets()

        self.orders = self.erisx_api.get_orders(self.pair())
        
        super().__init__(self.arguments, self.erisx_api)
class ErisXMarketMakerKeeper(CEXKeeperAPI):
    """
    Keeper acting as a market maker on ErisX.
    """

    logger = logging.getLogger()

    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='erisx-market-maker-keeper')

        parser.add_argument("--erisx-clearing-url", type=str, required=True,
                            help="Address of the ErisX clearing server")

        parser.add_argument("--fix-trading-endpoint", type=str, required=True,
                            help="FIX endpoint for ErisX trading")

        parser.add_argument("--fix-trading-user", type=str, required=True,
                            help="Account ID for ErisX trading")

        parser.add_argument("--fix-marketdata-endpoint", type=str, required=True,
                            help="FIX endpoint for ErisX market data")

        parser.add_argument("--fix-marketdata-user", type=str, required=True,
                            help="Account ID for ErisX market data")

        parser.add_argument("--erisx-password", type=str, required=True,
                            help="password for FIX account")

        parser.add_argument("--erisx-api-key", type=str, required=True,
                            help="API key for ErisX REST API")

        parser.add_argument("--erisx-api-secret", type=str, required=True,
                            help="API secret for ErisX REST API")

        parser.add_argument("--erisx-certs", type=str, default=None,
                            help="Client key pair used to authenticate to Production FIX endpoints")

        parser.add_argument("--account-id", type=int, default=0,
                            help="ErisX account ID index")

        parser.add_argument("--pair", type=str, required=True,
                            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--config", type=str, required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed", type=str, required=True,
                            help="Source of price feed")

        parser.add_argument("--price-feed-expiry", type=int, default=120,
                            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed", type=str,
                            help="Source of spread feed")

        parser.add_argument("--spread-feed-expiry", type=int, default=3600,
                            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed", type=str,
                            help="Source of control feed")

        parser.add_argument("--control-feed-expiry", type=int, default=86400,
                            help="Maximum age of the control feed (in seconds, default: 86400)")

        parser.add_argument("--order-history", type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument("--order-history-every", type=int, default=30,
                            help="Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument("--refresh-frequency", type=int, default=3,
                            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument("--debug", dest='debug', action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        setup_logging(self.arguments)

        self.erisx_api = ErisxApi(fix_trading_endpoint=self.arguments.fix_trading_endpoint,
                                  fix_trading_user=self.arguments.fix_trading_user,
                                  fix_marketdata_endpoint=self.arguments.fix_marketdata_endpoint,
                                  fix_marketdata_user=self.arguments.fix_marketdata_user,
                                  password=self.arguments.erisx_password,
                                  clearing_url=self.arguments.erisx_clearing_url,
                                  api_key=self.arguments.erisx_api_key,
                                  api_secret=self.arguments.erisx_api_secret,
                                  certs=self.arguments.erisx_certs,
                                  account_id=self.arguments.account_id)

        termination_time = time.time() + 15
        while self.erisx_api.fix_trading.connection_state != FixConnectionState.LOGGED_IN and self.erisx_api.fix_marketdata.connection_state != FixConnectionState.LOGGED_IN:
            time.sleep(0.3)
            if time.time() > termination_time:
                raise RuntimeError("Timed out while waiting to log in")

        self.market_info = self.erisx_api.get_markets()

        self.orders = self.erisx_api.get_orders(self.pair())
        
        super().__init__(self.arguments, self.erisx_api)

    def init_order_book_manager(self, arguments, erisx_api):
        self.order_book_manager = ErisXOrderBookManager(refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(lambda: self.get_orders())
        self.order_book_manager.get_balances_with(lambda: self.erisx_api.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.erisx_api.cancel_order(order.order_id, self.pair(), order.is_sell))
        self.order_book_manager.enable_history_reporting(self.order_history_reporter, self.our_buy_orders,
                                                         self.our_sell_orders)

        self.order_book_manager.pair = self.pair()
        self.order_book_manager.start()

    def main(self):
        with ErisXLifecycle() as lifecycle:
            lifecycle.initial_delay(10)
            lifecycle.every(1, self.synchronize_orders)
            lifecycle.on_shutdown(self.shutdown)

    def pair(self):
        return self.arguments.pair

    def token_sell(self) -> str:
        return self.arguments.pair.split('/')[0].upper()

    def token_buy(self) -> str:
        return self.arguments.pair.split('/')[1].upper()

    def our_available_balance(self, our_balances: dict, token: str) -> Wad:
        if 'newrelease' in self.arguments.erisx_clearing_url:
            if token == 'ETH':
                token = 'TETH'
            if token == 'BTC':
                token = 'TBTC'

        token_balances = list(filter(lambda asset: asset['asset_type'].upper() == token, our_balances))
        if token_balances:
            return Wad.from_number(float(token_balances[0]['available_to_trade']))
        else:
            return Wad(0)

    def get_orders(self) -> List[Order]:
        """
           Check the list of orders for cancellations or fills.
           If an order has been partially filled, the order amount will be updated.

           If an application message has been received from ErisX,
           update the Keeper orderbook accordingly.
        """
        existing_orders = self.orders
        self.orders = self.erisx_api.sync_orders(existing_orders)
        return self.orders

    def place_orders(self, new_orders):
        def place_order_function(new_order_to_be_placed):
            amount = new_order_to_be_placed.pay_amount if new_order_to_be_placed.is_sell else new_order_to_be_placed.buy_amount
            # automatically retrive qty precision
            round_lot = str(self.market_info[self.pair()]["RoundLot"])
            price_increment = str(self.market_info[self.pair()]["MinPriceIncrement"])
            price_precision = abs(Decimal(price_increment).as_tuple().exponent)
            order_qty_precision = abs(Decimal(round_lot).as_tuple().exponent)
            rounded_amount = round(Wad.__float__(amount), order_qty_precision)
            rounded_price = round(Wad.__float__(new_order_to_be_placed.price), price_precision)

            order_id = self.erisx_api.place_order(pair=self.pair().upper(),
                                                  is_sell=new_order_to_be_placed.is_sell,
                                                  price=rounded_price,
                                                  amount=rounded_amount)

            # check that order was placed properly
            if len(order_id) > 0:
                placed_order = Order(str(order_id), int(time.time()), self.pair(), new_order_to_be_placed.is_sell,
                            new_order_to_be_placed.price, amount)

                self.orders.append(placed_order)
                return placed_order
            else:
                return None

        for new_order in new_orders:
            # check if new order is greater than exchange minimums
            amount = new_order.pay_amount if new_order.is_sell else new_order.buy_amount
            side = 'Sell' if new_order.is_sell else 'Buy'
            round_lot = str(self.market_info[self.pair()]["RoundLot"])
            order_qty_precision = abs(Decimal(round_lot).as_tuple().exponent)
            min_amount = float(self.market_info[self.pair()]["MinTradeVol"])
            rounded_amount = round(Wad.__float__(amount), order_qty_precision)

            if min_amount < rounded_amount:
                self.order_book_manager.place_order(lambda new_order=new_order: place_order_function(new_order))
            else:
                logging.info(f"New {side} Order below size minimum of {min_amount}. Order of amount {amount} ignored.")

    def synchronize_orders(self):
        bands = Bands.read(self.bands_config, self.spread_feed, self.control_feed, self.history)
        order_book = self.order_book_manager.get_order_book()
        target_price = self.price_feed.get_price()

        # Cancel orders
        cancellable_orders = bands.cancellable_orders(our_buy_orders=self.our_buy_orders(order_book.orders),
                                                      our_sell_orders=self.our_sell_orders(order_book.orders),
                                                      target_price=target_price)
        if len(cancellable_orders) > 0:
            self.order_book_manager.cancel_orders(cancellable_orders)
            return

        # Do not place new orders if order book state is not confirmed
        if order_book.orders_being_placed or order_book.orders_being_cancelled:
            self.logger.debug("Order book is in progress, not placing new orders")
            return

        # Place new orders
        self.place_orders(bands.new_orders(our_buy_orders=self.our_buy_orders(order_book.orders),
                                            our_sell_orders=self.our_sell_orders(order_book.orders),
                                            our_buy_balance=self.our_available_balance(order_book.balances,
                                                                                        self.token_buy()),
                                            our_sell_balance=self.our_available_balance(order_book.balances,
                                                                                        self.token_sell()),
                                            target_price=target_price)[0])
예제 #7
0
class TestErisx:
    def setup_method(self):
        self.clearing_server = ErisXMockClearingAPIServer()
        do_nothing = lambda *args: None

        orig_get_account = ErisxApi.get_account
        FixEngine.logon = do_nothing
        ErisxApi.get_account = do_nothing
        self.account_id = 0
        self.client = ErisxApi(fix_trading_endpoint="127.0.0.1:1752", fix_trading_user="******",
                               fix_marketdata_endpoint="127.0.0.1:1753", fix_marketdata_user="******",
                               password="******",
                               clearing_url="https://127.0.0.1/api/v1/",
                               api_key="key", api_secret="secret", certs=None, account_id=self.account_id)
        ErisxApi.get_account = orig_get_account

    def test_init(self):
        assert self.client.fix_trading.senderCompId == "test"
        assert self.client.fix_trading.targetCompId == "ERISX"
        assert self.client.fix_trading.heartbeat_interval > 0
        assert self.client.fix_marketdata.senderCompId == "test"
        assert self.client.fix_marketdata.targetCompId == "ERISX"
        assert self.client.fix_marketdata.heartbeat_interval > 0

    @pytest.mark.skip("FIX simulation engine needed")
    def test_heartbeats(self):
        # Wait past the heartbeatInterval and confirm heartbeats were received
        time.sleep(self.client.fix_trading.heartbeat_interval*10)
        assert self.client.fix_trading.sequenceNum > 2

    def test_get_account(self, mocker):
        mocker.patch("requests.post", side_effect=self.clearing_server.handle_request)
        response = self.client.get_account(self.account_id)
        assert (len(response) > 0)
        assert(response == "27ff6d34-523d-476d-9ad5-edeb373b83dc")

    def test_get_balances(self, mocker):
        mocker.patch("requests.post", side_effect=self.clearing_server.handle_request)
        response = self.client.get_balances()
        assert (len(response) > 0)
        assert isinstance(response, dict)
        assert response["account_id"] == "27ff6d34-523d-476d-9ad5-edeb373b83dc"
        balances_for_account = response["balances"]
        print(f"balances {type(response)} {response}")
        for balance in balances_for_account:
            assert isinstance(balance, dict)
            if balance["asset_type"] == "TETH":
                assert(float(balance["available_to_trade"]) == 12.6)

    @staticmethod
    def check_trades(trades):
        by_tradeid = {}
        duplicate_count = 0
        duplicate_first_found = -1
        missorted_found = False
        last_timestamp = 0
        for index, trade in enumerate(trades):
            assert(isinstance(trade, Trade))
            if trade.trade_id in by_tradeid:
                print(f"found duplicate trade {trade.trade_id}")
                duplicate_count += 1
                if duplicate_first_found < 0:
                    duplicate_first_found = index
            else:
                by_tradeid[trade.trade_id] = trade
                if not missorted_found and last_timestamp > 0:
                    if trade.timestamp > last_timestamp:
                        print(f"missorted trade found at index {index}")
                        missorted_found = True
                    last_timestamp = trade.timestamp
        if duplicate_count > 0:
            print(f"{duplicate_count} duplicate trades were found, "
                  f"starting at index {duplicate_first_found}")
        else:
            print("no duplicates were found")
        assert(duplicate_count == 0)
        assert(missorted_found is False)

    def test_get_trades(self, mocker):
        pair = "ETH/USD"
        mocker.patch("requests.post", side_effect=self.clearing_server.handle_request)
        response = self.client.get_trades(pair)
        assert (len(response) > 0)
        print(f"trades: {response}")
        TestErisx.check_trades(response)
예제 #8
0
import logging
import sys
import time
from pprint import pprint

from pyexchange.erisx import ErisxApi

logging.basicConfig(
    format='%(asctime)-15s %(levelname)-8s %(threadName)-18s %(message)s',
    level=logging.DEBUG)
client = ErisxApi(fix_trading_endpoint=sys.argv[1],
                  fix_trading_user=sys.argv[2],
                  fix_marketdata_endpoint=sys.argv[3],
                  fix_marketdata_user=sys.argv[4],
                  password=sys.argv[5],
                  clearing_url="https://clearing.newrelease.erisx.com/api/v1/",
                  api_key=sys.argv[6],
                  api_secret=sys.argv[7],
                  account_id=0)
# print(sys.argv)
print("ErisxApi created\n")
# print(client.get_balances())
# time.sleep(30)

securities = client.get_markets()
print(f"Received {len(securities)} securities:")
pprint(securities)
time.sleep(2)

pair = client.get_pair("ETH/USD")
pprint(pair)