import asyncio class MM: def __init__(self, logger, pair, lot, spread_amount, spread_band): self._logger = logger self._pair = pair self._lot = lot self._remain = 0 self._buy_price = None self._sell_price = None self._buy = None self._sell = None self._spread_amount = spread_amount self._spread_band = spread_band self._total = 0 asyncio.create_task(self._main()) async def _main(self): self._logger.info("started") while True: pair = self._pair depth = pair.depth await depth.wait() if len(depth.bids) == 0 or len(depth.asks) == 0: continue # calculate best amount to buy/sell pos = self._remain / self._lot - 2 buy_amount = _quad( pos) * self._lot sell_amount = _quad(-pos) * self._lot # calculate ideal spread ask_delta_amount = self._spread_amount * (_quad( pos) + self._spread_band) bid_delta_amount = self._spread_amount * (_quad(-pos) + self._spread_band) # calculate best sell price if self._sell is not None and self._sell.done: ask_delta_amount += self._sell.amount ask = 0 prev = -1e100 for i in range(len(depth.asks)): if (depth.asks[i][0]-prev) > pair.price_unit*1.5: ask = depth.asks[i][0] - pair.price_unit my_order = \ self._sell is not None and \ not self._sell.done and \ abs(self._sell_price-depth.asks[i][0]) < pair.price_unit if my_order: ask_delta_amount -= depth.asks[i][1] - self._sell.amount prev = -1e100 else: ask_delta_amount -= depth.asks[i][1] prev = depth.asks[i][0] if ask_delta_amount < 0: break # calculate best buy price if self._buy is not None and self._buy.done: bid_delta_amount += self._buy.amount bid = 0 prev = 1e100 for i in range(len(depth.bids)): if (prev-depth.bids[i][0]) > pair.price_unit*1.5: bid = depth.bids[i][0] + pair.price_unit my_order = \ self._buy is not None and \ not self._buy.done and \ abs(self._buy_price-depth.bids[i][0]) < pair.price_unit if my_order: bid_delta_amount -= depth.bids[i][1] - self._buy.amount prev = 1e100 else: bid_delta_amount -= depth.bids[i][1] prev = depth.bids[i][0] if bid_delta_amount < 0: break if self._sell is not None: # check current SELL order await asyncio.sleep(0.5) await self._sell.update() if self._sell.done: amount = self._sell.amount - self._sell.remain self._remain -= amount if amount > 0: income = amount*self._sell.price self._total += income self._logger.info(f" {amount} / {income} ({self._remain} / {self._total})") self._sell = None elif abs(self._sell_price-ask) >= pair.price_unit: await self._sell.cancel() elif sell_amount >= pair.order_unit: # order SELL self._sell_price = ask self._sell = await self._order_sell(sell_amount) if self._buy is not None: # check current BUY order await self._buy.update() if self._buy.done: amount = self._buy.amount - self._buy.remain self._remain += amount if amount > 0: outgo = amount*self._buy.price self._total -= outgo self._logger.info(f" {amount} / {outgo} ({self._remain} / {self._total})") self._buy = None elif abs(self._buy_price-bid) >= pair.price_unit: await self._buy.cancel() elif buy_amount >= pair.order_unit: # order BUY self._buy_price = bid self._buy = await self._order_buy(buy_amount) async def _order_sell(self, amount): return await self._pair.sell_limit(amount, self._sell_price, True) async def _order_buy(self, amount): return await self._pair.buy_limit(amount, self._buy_price, True) # 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