# No copyright import asyncio import logging class MM: def __init__(self, player, pair, config): self._player = player self._pair = pair self._lastBuy = "" self._lastSell = "" self._lastPrice = None 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 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) # 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 buy_price = price * (1 - self._delta); sell_price = price * (1 + self._delta); 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( self._pair.names, amount = buy_amount, price = buy_price)) async def sell(): return (None if order_unit > sell_amount else await self._player.orderLimitSell( self._pair.names, amount = sell_amount, price = sell_price)) orders = await asyncio.gather(buy(), sell()) self._lastBuy = orders[0] self._lastSell = orders[1] self._lastPrice = price # https://kijitora-2018.hatenablog.com/entry/2018/12/23/102913 def _quad(x): if x < -1: return 1 if x <= 1: return -1/4 * (x+1)**2 + 1 return 0