import asyncio

class MM:
  def __init__(self, logger, pair, lot, epsilon):
    self._logger   = logger
    self._pair     = pair
    self._lot      = lot
    self._remain   = 0
    self._epsilon  = epsilon

    asyncio.create_task(self._main())

  async def _main(self):
    self._logger.info("started")
    while True:
      depth = self._pair.depth
      await depth.wait()
      if len(depth.bids) == 0 or len(depth.asks) == 0:
        continue

      spread = depth.asks[0][0] - depth.bids[0][0]
      if spread > self._epsilon*2:
        sell_price = depth.asks[0][0]-self._epsilon
        buy_price  = depth.bids[0][0]+self._epsilon
        await self._make(sell_price, buy_price)

  async def _make(self, sell_price, buy_price):
    if self._remain < self._epsilon/buy_price:
      amount = self._lot/buy_price
      try:
        buy = await self._pair.buy_limit(amount, buy_price, True)
      except Exception:
        buy = None
      try:
        sell = await self._pair.sell_limit(amount, sell_price, True)
      except Exception:
        sell = None
    else:
      buy  = None
      try:
        sell = await self._pair.sell_limit(self._remain, sell_price, True)
      except Exception:
        sell = None

    while (sell is not None) or (buy is not None):
      await asyncio.sleep(1)
      try:
        coro = []
        if buy  is not None: coro.append(buy .update())
        if sell is not None: coro.append(sell.update())
        await asyncio.gather(*coro)
      except Exception as e:
        continue

      depth = self._pair.depth

      if sell is not None:
        if sell.done:
          self._remain -= sell.amount-sell.remain
          sell = None
        elif sell_price > depth.asks[0][0]:
          break

      if buy is not None:
        if buy.done:
          self._remain += buy.amount-buy.remain
          buy = None
        elif buy_price < depth.bids[0][0]:
          break

    async def cancel(self, order, flag):
      await asyncio.sleep(1)
      await order.cancel()
      await order.update()
      self._remain += flag*(order.amount-order.remain)

    coro = []
    if sell is not None:
      coro.append(cancel(self, sell, -1))
    if buy is not None:
      coro.append(cancel(self, buy, 1))
    await asyncio.gather(*coro)