From e376eea76b2335abf0f852d6f1f119e8f83dc322 Mon Sep 17 00:00:00 2001 From: falsycat Date: Sun, 23 Jul 2023 08:55:06 +0900 Subject: [PATCH] implement losscut in MM algorithm --- algo/mm.py | 66 +++++++++++++++++++++++++++++++++++++++++--------- main.py | 2 ++ util/player.py | 18 ++++++++++++++ 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/algo/mm.py b/algo/mm.py index 6343af8..48dfdef 100644 --- a/algo/mm.py +++ b/algo/mm.py @@ -7,37 +7,76 @@ class MM: self._player = player self._pair = pair - self._lastBuy = "" - self._lastSell = "" + self._lastBuy = "" + self._lastSell = "" + self._lastPrice = None - self._delta = float(config["delta"]) + self._delta = float(config["delta"]) + self._positiveCut = float(config["+cut"]) + self._negativeCut = float(config["-cut"]) async def update(self): + sales = self._player.assets[self._pair.names[0]] + crncy = self._player.assets[self._pair.names[1]] + price = self._pair.ticker.price + + lot = (sales.amount + crncy.amount / price) / 2 + pos = sales.amount / lot - 1 + + # evaluate if cutting should be executed + negCut = False + posCut = False + if self._lastPrice is not None: + change = (price - self._lastPrice) / self._lastPrice + negCut = pos > 0.5 and change < -abs(self._negativeCut) + posCut = pos < -0.5 and change > abs(self._positiveCut) + cutting = negCut or posCut + + # check if the past orders are active lastBuyAgreed = (self._lastBuy is not None and self._lastBuy not in self._player.orders) lastSellAgreed = (self._lastSell is not None and self._lastSell not in self._player.orders) - if not lastBuyAgreed and not lastSellAgreed: + if not lastBuyAgreed and not lastSellAgreed and not cutting: return + # cancel active orders cancel_orders = [] if not lastBuyAgreed and self._lastBuy is not None: + logging.info(f"cancel buy") cancel_orders.append(self._lastBuy) self._lastBuy = None + elif lastBuyAgreed: + logging.info(f"agree buy") + if not lastSellAgreed and self._lastSell is not None: + logging.info(f"cancel sell") cancel_orders.append(self._lastSell) self._lastSell = None + elif lastSellAgreed: + logging.info(f"agree sell") + if len(cancel_orders) != 0: await self._player.cancel(self._pair.names, cancel_orders) - sales = self._player.assets[self._pair.names[0]] - crncy = self._player.assets[self._pair.names[1]] - price = self._pair.ticker.price - - lot = (sales.amount + crncy.amount / price) / 4 - pos = sales.amount / lot - 2 + # execute cutting + if cutting: + if negCut: + logging.info(f"-cut (change: {change}, pos: {pos} -> 0)") + if posCut: + logging.info(f"+cut (change: {change}, pos: {pos} -> 0)") + self._lastPrice = None + self._lastBuy = None + self._lastSell = None + sell_amount = pos * lot + if sell_amount > 0: + await self._player.orderMarketSell(self._pair.names, amount=sell_amount) + else: + await self._player.orderMarketBuy(self._pair.names, amount=-buy_amount) + return + # execute new cycle buy_amount = _quad( pos) * lot sell_amount = _quad(-pos) * lot @@ -46,6 +85,8 @@ class MM: order_unit = pow(10, -sales.precision) + logging.info(f"new cycle (pos: {pos}, price: {price}, buy: {buy_amount}, sell: {sell_amount})") + async def buy(): return (None if order_unit > buy_amount else await self._player.orderLimitBuy( @@ -57,8 +98,9 @@ class MM: self._pair.names, amount = sell_amount, price = sell_price)) orders = await asyncio.gather(buy(), sell()) - self._lastBuy = orders[0] - self._lastSell = orders[1] + self._lastBuy = orders[0] + self._lastSell = orders[1] + self._lastPrice = price # https://kijitora-2018.hatenablog.com/entry/2018/12/23/102913 def _quad(x): diff --git a/main.py b/main.py index f3a81a0..4c23c37 100644 --- a/main.py +++ b/main.py @@ -14,6 +14,8 @@ with open("config.json", "r") as file: config = json.load(file) async def main(): + logging.info("#### TMM ####") + alive = True logging.basicConfig(level=logging.DEBUG) diff --git a/util/player.py b/util/player.py index e5cebf2..f1e4eed 100644 --- a/util/player.py +++ b/util/player.py @@ -14,6 +14,24 @@ class Player: }) self._check(res) + async def orderMarketSell(self, pair, amount): + res = await self._post("user/spot/order", data={ + "pair": f"{pair[0]}_{pair[1]}", + "amount": str(amount), + "side": "sell", + "type": "market", + }) + return self._check(res)["data"]["order_id"] + + async def orderMarketBuy(self, pair, amount): + res = await self._post("user/spot/order", data={ + "pair": f"{pair[0]}_{pair[1]}", + "amount": str(amount), + "side": "buy", + "type": "market", + }) + return self._check(res)["data"]["order_id"] + async def orderLimitSell(self, pair, amount, price): res = await self._post("user/spot/order", data={ "pair": f"{pair[0]}_{pair[1]}",