From e98ae395e8827d69c70c5fa080eee3d40b4a9c10 Mon Sep 17 00:00:00 2001 From: microwavedcola1 Date: Fri, 24 Sep 2021 19:25:57 +0200 Subject: [PATCH] cleanup python examples Signed-off-by: microwavedcola1 --- README.md | 1 - mango-service-v3/src/mango.simple.client.ts | 3 +- mango-service-v3/src/orders.controller.ts | 2 + py/example1_basic.py | 92 +++++++++++++-------- py/example2_set_stink_bids.py | 32 ++++--- py/example3_market_maker.py | 26 +++--- py/mango_service_v3_py/api.py | 81 +++++++++++------- py/mango_service_v3_py/dtos.py | 3 - 8 files changed, 142 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 3837393..3d2d99e 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ losely sorted in order of importance/priority - stop loss, - market orders - modify order - - withdraw - funding payments - advanced order types e.g. split - populate still undefined fields in various endpoints diff --git a/mango-service-v3/src/mango.simple.client.ts b/mango-service-v3/src/mango.simple.client.ts index e72f28d..d9cb2d3 100644 --- a/mango-service-v3/src/mango.simple.client.ts +++ b/mango-service-v3/src/mango.simple.client.ts @@ -42,7 +42,7 @@ class MangoSimpleClient { public owner: Account, public mangoAccount: MangoAccount ) { - setInterval(this.roundRobinClusterUrl, 20_000); + setInterval(this.roundRobinClusterUrl, 1_000); } static async create() { @@ -595,7 +595,6 @@ class MangoSimpleClient { Math.floor(Math.random() * possibleClustersUrls.length) ]; - logger.info(`switching to rpc node - ${clusterUrl}...`); this.connection = new Connection(clusterUrl, "processed" as Commitment); } } diff --git a/mango-service-v3/src/orders.controller.ts b/mango-service-v3/src/orders.controller.ts index 548976f..d504308 100644 --- a/mango-service-v3/src/orders.controller.ts +++ b/mango-service-v3/src/orders.controller.ts @@ -138,6 +138,8 @@ class OrdersController implements Controller { const placeOrderDto = request.body as PlaceOrderDto; logger.info(`placing order`); + logger.info(`${placeOrderDto.price}`); + logger.info(`${placeOrderDto.size}`); try { await this.mangoSimpleClient.placeOrder( diff --git a/py/example1_basic.py b/py/example1_basic.py index 8ba1b30..c49b19c 100644 --- a/py/example1_basic.py +++ b/py/example1_basic.py @@ -1,30 +1,64 @@ -from mango_service_v3_py.api import Exchange -from mango_service_v3_py.dtos import PlacePerpOrder, PlaceOrder +from mango_service_v3_py.api import MangoServiceV3Client +from mango_service_v3_py.dtos import PlaceOrder if __name__ == "__main__": - exchange = Exchange() + mango_service_v3_client = MangoServiceV3Client() - print(exchange.get_open_positions()) + for position in mango_service_v3_client.get_open_positions(): + print(position.json(indent=4, sort_keys=True)) - print(exchange.get_balances()) + for balance in mango_service_v3_client.get_balances(): + print(balance.json(indent=4, sort_keys=True)) - print(exchange.get_markets()) - print(exchange.get_market_by_market_name("BTC-PERP")) + for market in mango_service_v3_client.get_markets(): + print(market.json(indent=4, sort_keys=True)) + print( + mango_service_v3_client.get_market_by_market_name("BTC-PERP").json( + indent=4, sort_keys=True + ) + ) - print(exchange.get_orderboook("BTC-PERP")) + for order in mango_service_v3_client.get_orderboook("BTC-PERP"): + print(order.json(indent=4, sort_keys=True)) - print(exchange.get_trades("BTC-PERP")) + for trade in mango_service_v3_client.get_trades("BTC-PERP"): + print(trade.json(indent=4, sort_keys=True)) - print(exchange.get_candles("BTC-PERP", 60, 1625922900, 1631214960)) + for candle in mango_service_v3_client.get_candles( + "BTC-PERP", 60, 1625922900, 1631214960 + ): + print(candle.json(indent=4, sort_keys=True)) - print(exchange.get_orders()) - print(exchange.get_orders_by_market_name("BTC-PERP")) + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) + for order in mango_service_v3_client.get_orders_by_market_name("BTC-PERP"): + print(order.json(indent=4, sort_keys=True)) - exchange.place_order( - PlacePerpOrder( + mango_service_v3_client.place_order( + PlaceOrder( market="BTC-PERP", side="buy", + price=20000, + type="limit", + size=0.0001, + reduce_only=False, + ioc=False, + post_only=False, + client_id=123, + ) + ) + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) + + mango_service_v3_client.cancel_order_by_client_id("123") + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) + + mango_service_v3_client.place_order( + PlaceOrder( + market="BTC/USDC", + side="buy", price=2000, type="limit", size=0.0001, @@ -34,27 +68,13 @@ if __name__ == "__main__": client_id=123, ) ) - print(exchange.get_orders()) + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) - exchange.place_order( - PlaceOrder( - market="BTC/USDC", - side="buy", - price=2000, - type="limit", - size=0.0001, - reduce_only=False, - ioc=False, - post_only=False, - ) - ) - print(exchange.get_orders()) + mango_service_v3_client.cancel_order_by_client_id("123") + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) - exchange.cancel_order_by_order_id("3689367261485984031001846") - print(exchange.get_orders()) - - exchange.cancel_order_by_client_id("3689367261485984031001846") - print(exchange.get_orders()) - - exchange.cancel_all_orders() - print(exchange.get_orders()) + mango_service_v3_client.cancel_all_orders() + for order in mango_service_v3_client.get_orders(): + print(order.json(indent=4, sort_keys=True)) diff --git a/py/example2_set_stink_bids.py b/py/example2_set_stink_bids.py index b5327a1..832c02a 100644 --- a/py/example2_set_stink_bids.py +++ b/py/example2_set_stink_bids.py @@ -1,5 +1,8 @@ -from mango_service_v3_py.api import Exchange -from mango_service_v3_py.dtos import PlacePerpOrder +import os +import time + +from mango_service_v3_py.api import MangoServiceV3Client +from mango_service_v3_py.dtos import PlaceOrder MARKET = "BTC-PERP" @@ -11,28 +14,34 @@ def fibonacci_of(n): if __name__ == "__main__": - exchange = Exchange() + os.environ["DELAY"] = 1 - exchange.cancel_all_orders() + mango_service_v3_client = MangoServiceV3Client() - balances = exchange.get_balances() + market = mango_service_v3_client.get_market_by_market_name("BTC-PERP")[0] + + mango_service_v3_client.cancel_all_orders() + + balances = mango_service_v3_client.get_balances() total_usd_balance = sum([balance.usd_value for balance in balances]) - market = exchange.get_market_by_market_name(MARKET)[0] + market = mango_service_v3_client.get_market_by_market_name(MARKET)[0] lowest = 25 fibs = [fib for fib in [fibonacci_of(n) for n in range(10)] if fib < lowest][1:] fibs_sum = sum(fibs) for i, fib in enumerate(fibs): - print((100 - fibs[-1] + fib) / 100) price = market.last * (100 - fibs[-1] + fib) / 100 + price = mango_service_v3_client.to_nearest(price, market.price_increment) + size = (total_usd_balance / market.price) * (fibs[len(fibs) - 1 - i] / fibs_sum) + size = mango_service_v3_client.to_nearest(size, market.size_increment) if size < market.size_increment: continue print(f"setting order, price: {price}, size: {size}, value: {price * size}") - exchange.place_order( - PlacePerpOrder( + mango_service_v3_client.place_order( + PlaceOrder( market=MARKET, side="buy", price=price, @@ -41,7 +50,8 @@ if __name__ == "__main__": reduce_only=False, ioc=False, post_only=False, - client_id=123, + client_id=int(time.time()), ) ) - print(exchange.get_orders()) + for order in mango_service_v3_client.get_orders(): + print(f"set order at, price: {order.price}, size: {order.size}") diff --git a/py/example3_market_maker.py b/py/example3_market_maker.py index cc00745..d2274ea 100644 --- a/py/example3_market_maker.py +++ b/py/example3_market_maker.py @@ -9,8 +9,8 @@ from os.path import getmtime from tenacity import retry, wait_fixed, stop_after_delay, stop_after_attempt -from mango_service_v3_py.api import Exchange -from mango_service_v3_py.dtos import Side, PlacePerpOrder +from mango_service_v3_py.api import MangoServiceV3Client +from mango_service_v3_py.dtos import Side, PlaceOrder # based on https://github.com/BitMEX/sample-market-maker/blob/master/market_maker/market_maker.py @@ -44,7 +44,7 @@ def toNearest(num, tickDec): class MM: def __init__(self): - self.exchange = Exchange() + self.mango_service_v3_client = MangoServiceV3Client() self.market = None self.start_position_buy = None self.start_position_sell = None @@ -52,11 +52,11 @@ class MM: # todo unused @retry(stop=(stop_after_delay(10) | stop_after_attempt(5)), wait=wait_fixed(5)) - def retry_wrapper(self, exchange_method, *arg): - getattr(self.exchange, exchange_method)(arg) + def retry_wrapper(self, mango_service_v3_client_method, *arg): + getattr(self.mango_service_v3_client, mango_service_v3_client_method)(arg) def log_recent_trades(self) -> None: - trades = self.exchange.get_trades(MARKET) + trades = self.mango_service_v3_client.get_trades(MARKET) recent_trades = [ trade for trade in trades @@ -73,13 +73,13 @@ class MM: logger.info("") def get_ticker(self): - self.market = self.exchange.get_market_by_market_name(MARKET)[0] + self.market = self.mango_service_v3_client.get_market_by_market_name(MARKET)[0] self.start_position_buy = self.market.bid - self.market.price_increment self.start_position_sell = self.market.ask + self.market.price_increment self.positions = [ position - for position in self.exchange.get_open_positions() + for position in self.mango_service_v3_client.get_open_positions() if position.future == MARKET ] @@ -103,7 +103,7 @@ class MM: to_cancel = [] buys_matched = 0 sells_matched = 0 - existing_orders = self.exchange.get_orders() + existing_orders = self.mango_service_v3_client.get_orders() existing_orders = sorted(existing_orders, key=lambda order_: order_.price) buy_orders = sorted(buy_orders, key=lambda order_: order_.price) @@ -145,7 +145,7 @@ class MM: ) for order in to_cancel: try: - self.exchange.cancel_order_by_order_id(order.id) + self.mango_service_v3_client.cancel_order_by_order_id(order.id) except: pass logger.info("") @@ -178,8 +178,8 @@ class MM: f" |_ price {order.price}, side {order.side:4}, size {order.size}, value {order.price * order.size}" ) for order in to_create: - self.exchange.place_order( - PlacePerpOrder( + self.mango_service_v3_client.place_order( + PlaceOrder( market=MARKET, side=order.side, price=order.price, @@ -248,7 +248,7 @@ if __name__ == "__main__": logger.info("deleting all orders...") try: - mm.exchange.cancel_all_orders() + mm.mango_service_v3_client.cancel_all_orders() except Exception as e: logger.error(f"Exception: {e}") diff --git a/py/mango_service_v3_py/api.py b/py/mango_service_v3_py/api.py index 97b178f..3382086 100644 --- a/py/mango_service_v3_py/api.py +++ b/py/mango_service_v3_py/api.py @@ -1,10 +1,9 @@ -import inspect import json import os +import time from typing import List import httpx -from httpx import TimeoutException from pydantic import parse_obj_as from mango_service_v3_py.dtos import ( @@ -20,87 +19,105 @@ from mango_service_v3_py.dtos import ( # todo add mypy -def timeout_error_msg_customizer(response): - try: - response.raise_for_status() - except TimeoutException as e: - raise Exception(f"timed out within {inspect.stack()[1][3]}") from e -class Exchange: +def delayed(seconds): + def decorator(f): + def wrapper(*args, **kargs): + time.sleep(seconds) + return f(*args, **kargs) + + return wrapper + + return decorator + + +class MangoServiceV3Client: def __init__(self): if "BASE_URL" in os.environ: self.BASE_URL = f"{os.environ['BASE_URL']}/api" else: self.BASE_URL = "http://localhost:3000/api" + @delayed(os.environ["DELAY"]) def get_open_positions(self) -> List[Position]: - response = httpx.get(f"{self.BASE_URL}/positions") - timeout_error_msg_customizer(response) + response = httpx.get(f"{self.BASE_URL}/positions", timeout=10.0) return parse_obj_as(List[Position], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_balances(self) -> List[Balance]: - response = httpx.get(f"{self.BASE_URL}/wallet/balances") - timeout_error_msg_customizer(response) + response = httpx.get(f"{self.BASE_URL}/wallet/balances", timeout=10.0) return parse_obj_as(List[Balance], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_markets(self) -> List[Market]: - response = httpx.get(f"{self.BASE_URL}/markets") - timeout_error_msg_customizer(response) + response = httpx.get(f"{self.BASE_URL}/markets", timeout=10.0) return parse_obj_as(List[Market], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_market_by_market_name(self, market_name: str) -> List[Market]: - response = httpx.get(f"{self.BASE_URL}/markets/{market_name}") - timeout_error_msg_customizer(response) + response = httpx.get(f"{self.BASE_URL}/markets/{market_name}", timeout=10.0) return parse_obj_as(List[Market], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_orderboook(self, market_name: str, depth: int = 30) -> Orderbook: response = httpx.get( f"{self.BASE_URL}/markets/{market_name}/orderbook?depth={depth}" ) - timeout_error_msg_customizer(response) return parse_obj_as(Orderbook, json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_trades(self, market_name: str) -> List[Trade]: - response = httpx.get(f"{self.BASE_URL}/markets/{market_name}/trades") - timeout_error_msg_customizer(response) + response = httpx.get( + f"{self.BASE_URL}/markets/{market_name}/trades", timeout=10.0 + ) return parse_obj_as(List[Trade], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_candles( self, market_name: str, resolution: int, start_time: int, end_time: int ) -> List[Candle]: response = httpx.get( f"{self.BASE_URL}/markets/{market_name}/candles?resolution={resolution}&start_time={start_time}&end_time={end_time}" ) - timeout_error_msg_customizer(response) return parse_obj_as(List[Candle], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_orders(self,) -> List[Order]: - response = httpx.get(f"{self.BASE_URL}/orders") - timeout_error_msg_customizer(response) + response = httpx.get(f"{self.BASE_URL}/orders", timeout=10.0) return parse_obj_as(List[Order], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def get_orders_by_market_name(self, market_name: str) -> List[Order]: - response = httpx.get(f"{self.BASE_URL}/orders?market={market_name}") - timeout_error_msg_customizer(response) + response = httpx.get( + f"{self.BASE_URL}/orders?market={market_name}", timeout=10.0 + ) return parse_obj_as(List[Order], json.loads(response.text)["result"]) + @delayed(os.environ["DELAY"]) def place_order(self, order: PlaceOrder) -> None: - response = httpx.post(f"{self.BASE_URL}/orders", json=order.dict()) - timeout_error_msg_customizer(response) + response = httpx.post( + f"{self.BASE_URL}/orders", json=order.dict(by_alias=True), timeout=10.0 + ) # if response.status_code == httpx.codes.BAD_REQUEST: # return parse_obj_as( # List[BadRequestError], json.loads(response.text)["errors"] # ) + @delayed(os.environ["DELAY"]) def cancel_order_by_client_id(self, client_id): - response = httpx.delete(f"{self.BASE_URL}/orders/by_client_id/{client_id}") - timeout_error_msg_customizer(response) + response = httpx.delete( + f"{self.BASE_URL}/orders/by_client_id/{client_id}", timeout=10.0 + ) + @delayed(os.environ["DELAY"]) def cancel_order_by_order_id(self, order_id): - response = httpx.delete(f"{self.BASE_URL}/orders/{order_id}") - timeout_error_msg_customizer(response) + response = httpx.delete(f"{self.BASE_URL}/orders/{order_id}", timeout=10.0) + @delayed(os.environ["DELAY"]) def cancel_all_orders(self): - response = httpx.delete(f"{self.BASE_URL}/orders") - timeout_error_msg_customizer(response) + response = httpx.delete(f"{self.BASE_URL}/orders", timeout=10.0) + + @staticmethod + def to_nearest(num, tickDec): + return float(round(num / tickDec, 0)) * tickDec diff --git a/py/mango_service_v3_py/dtos.py b/py/mango_service_v3_py/dtos.py index 4bf6203..ad18dda 100644 --- a/py/mango_service_v3_py/dtos.py +++ b/py/mango_service_v3_py/dtos.py @@ -127,9 +127,6 @@ class PlaceOrder(CamelCaseModel): reduce_only: bool ioc: bool post_only: bool - - -class PlacePerpOrder(PlaceOrder): client_id: int