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))
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])
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)
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)