import asyncio

SHORT_PERIOD = 13
LONG_PERIOD  = 200

EXPECT_CYCLE_DUR_IN_CANDLE_INTERVALS = 5

class MA_Cross:
  def __init__(self, logger, pair, candlestick, lot, limit_rate, stop_rate):
    self._logger      = logger
    self._pair        = pair
    self._candlestick = candlestick

    self._lot        = lot
    self._limit_rate = limit_rate
    self._stop_rate  = stop_rate

    asyncio.create_task(self._main())

  async def _main(self):
    self._logger.info("started")

    while True:
      await self._candlestick.wait()

      if len(self._candlestick.values) < LONG_PERIOD:
        continue

      short = self._candlestick.SMA(SHORT_PERIOD)
      long  = self._candlestick.SMA(LONG_PERIOD)
      plong = self._candlestick.SMA(LONG_PERIOD, 1)

      expect_sell     = self._pair.ticker.buy * (1+self._limit_rate)
      ask_volume      = self._pair.depth.askVolumeUntil(expect_sell)
      interval_volume = self._pair.ticker.volume / 24 / 3600 * self._candlestick.interval

      if plong < long and short > long:
        if ask_volume < interval_volume * EXPECT_CYCLE_DUR_IN_CANDLE_INTERVALS:
          await self._buy()

  async def _buy(self):
    self._logger.info("buy signal received")

    # buy (market) and sell (limit)
    amount = self._lot/self._pair.ticker.buy
    try:
      buy = await self._pair.buy_market(amount)
    except Exception as e:
      self._logger.warn("failed to buy", e)
      return

    while not buy.done:
      await asyncio.sleep(0.1)
      await buy.update()
    self._logger.info(f"BUY confirmed (+{amount} / -{buy.price*amount})")

    limit_price = buy.price*(1+self._limit_rate)
    stop_price  = buy.price*(1-self._stop_rate)
    try:
      stop  = await self._pair.sell_stop(amount, stop_price)
      limit = await self._pair.sell_limit(amount, limit_price)
    except Exception as e:
      print("failed to order", e.message)
      self._logger.error("FAILED TO ORDER for STOPPING and LIMITATION!!")
      return

    self._logger.debug("waiting for STOP or LIMIT")
    while (not stop.done) and (not limit.done):
      await asyncio.sleep(2)
      try:
        await asyncio.gather(limit.update(), stop.update())
      except Exception:
        continue

    if stop.done:
      self._logger.info(f"STOP confirmed (-{amount} / +{stop.price*amount})")
      # cancel limit and cooldown
      await asyncio.gather(asycio.sleep(5), limit.cancel())
      return

    if limit.done:
      self._logger.info(f"LIMIT confirmed (-{amount} / +{limit.price*amount})")
      await stop.cancel()
      return