Example #1
0
    def __init__(self, exchange, symbol, leverage=None, api_key=None, api_secret=None, testnet=True, dry_run=False,
                 candle_limit=None, candle_period=None):
        """
        :param exchange: 거래소이름. bitmex, bithumb..
        :param symbol: 심볼페어. XBTUSD, BTC/USD
        :param dry_run: 참일 경우 실제 주문 api를 호출하지 않는다.
        :param candle_limit: 저장할 최대 캔들갯수.
        :param candle_period: 봉주기. Bitmex는 4가지만 지원한다. 1m, 5m, 1h, 1d
        """
        self.candle_handler = None
        raw_symbol = symbol.replace('/', '')
        self.api = utils.ccxt_exchange(exchange, api_key=api_key, api_secret=api_secret, is_async=False, opt={'test':testnet})
        if api_key and api_secret and self.api and leverage is not None:
            self.api.private_post_position_leverage({'symbol': raw_symbol, 'leverage': leverage})

        logger.info('>>candle_period>> %s',candle_period)
            # 웹소켓 처리기.
        self.ws = BitmexWS(raw_symbol, candle_period,
                           api_key=api_key,
                           api_secret=api_secret,
                           testnet=testnet)

        if candle_limit and candle_period:
            period_in_second = 0
            if candle_period == '1m':
                period_in_second = 60
            elif candle_period == '5m':
                period_in_second = 300
            elif candle_period == '1h':
                period_in_second = 3600
            elif candle_period == '1d':
                period_in_second = 3600 * 24
            since = datetime.datetime.now().timestamp() - period_in_second * candle_limit
            since = since * 1000
            self.candle_handler = CandleHandler(self.api, symbol, period=candle_period, history=candle_limit, since=since)
Example #2
0
    def enter(self, df, hei):
        # 포지션이 없을때만
        if self.s['current_qty'] == 0:
            if hei.HA_Diff.iloc[-1] > 0 and hei.HA_Diff.iloc[-2] > 0:
                # 이전 5개 봉중에 diff>0 인 봉이 다른 색일 경우, 진입가능.
                allowed = False
                for i in range(5):
                    if abs(hei.HA_Diff.iloc[-2 - i]) > 0:
                        allowed = hei.HA_Diff.iloc[-2 - i] < 0
                        break
                # 가격이 높아지는 추세.
                if allowed and abs(hei.HA_Diff.iloc[-1] + hei.HA_Diff.iloc[-2]) >= 1:
                    # 롱 진입.
                    self.s['order'] += 1
                    logger.info('롱 진입요청 %s XBT@%s', self.amount, self.s['bid1'])
                    self.send_telegram('Enter Long Req {} XBT@{}'.format(self.amount, self.s['bid1']))
                    self.buy_orderbook1(self.amount)

            elif hei.HA_Diff.iloc[-1] < 0 and hei.HA_Diff.iloc[-2] < 0:
                allowed = False
                for i in range(5):
                    if abs(hei.HA_Diff.iloc[-2 - i]) > 0:
                        allowed = hei.HA_Diff.iloc[-2 - i] < 0
                        break
                # 가격이 낮아지는 추세.
                if allowed and abs(hei.HA_Diff.iloc[-1] + hei.HA_Diff.iloc[-2]) >= 1:
                    # 숏 진입.
                    self.s['order'] += 1
                    logger.info('숏 진입요청 %s XBT@%s', self.amount, self.s['ask1'])
                    self.send_telegram('Enter Short Req {} XBT@{}'.format(self.amount, self.s['ask1']))
                    self.sell_orderbook1(self.amount)
Example #3
0
    def leave(self, df, hei):
        N = 3  # 3개의 연속 가격을 확인하여 익절.
        close = hei.HA_Close.iloc[-1]
        if self.s['current_qty'] < 0:
            # 숏 청산.
            tobe_pnl = self.s['entry_price'] - close
            if hei.HA_Diff.iloc[-1] > 0 \
                    or self.great_or_eq(hei.HA_Close, N) \
                    or tobe_pnl >= self.takeprofit \
                    or -tobe_pnl >= self.losscut:
                # takeprofit=익절. losscut=손절.
                self.s['order'] += 1
                logger.info('숏 청산요청 @%s tobe_pnl[%s]', self.s['bid1'], tobe_pnl)
                self.send_telegram(
                    'End Short Req{} XBT@{} tobe_pnl[{}]'.format(abs(self.s['current_qty']), self.s['bid1'], tobe_pnl))
                self.buy_orderbook1(self.s['current_qty'], reduce_only=True)

        elif self.s['current_qty'] > 0:
            # 롱 청산.
            tobe_pnl = close - self.s['entry_price']
            if hei.HA_Diff.iloc[-1] < 0 \
                    or self.less_or_eq(hei.HA_Close, N) \
                    or tobe_pnl >= self.takeprofit \
                    or -tobe_pnl >= self.losscut:
                self.s['order'] += 1
                logger.info('롱 청산요청 @%s tobe_pnl[%s]', self.s['ask1'], tobe_pnl)
                self.send_telegram(
                    'End Long Req {} XBT@{} tobe_pnl[{}]'.format(abs(self.s['current_qty']), self.s['ask1'], tobe_pnl))
                self.sell_orderbook1(self.s['current_qty'], reduce_only=True)
Example #4
0
    def update_position(self, position):
        logger.debug('update_position > %s', position)
        self.s['current_qty'] = position['currentQty']
        current_qty = self.s['current_qty']

        if current_qty == 0:
            # 포지션이 닫힌 경우.
            if 'isOpen' in position and not position['isOpen']:
                prevPnl = position['prevRealisedPnl']
                logger.info('Position Closed. pnl[%s%s]', prevPnl, position['currency'])
                self.send_telegram('Position Closed. pnl[{} {}]'.format(prevPnl, position['currency']))

            # stop모두 취소.
            for order in self.data['order']:
                if order['ordType'] == 'Stop':
                    order_id = order['orderID']
                    self.api.cancel_order(id=order_id, symbol=symbol)
            return

        if 'avgEntryPrice' in position:
            if position['avgEntryPrice']:
                ## entry_price가 대부분 안들어온다. 포지션이 수정될때만 들어옴. 봇을 리스타트시 안들어옴.
                self.s['entry_price'] = position['avgEntryPrice']
                if current_qty > 0:
                    # 롱 포지션은 아래쪽에 Sell을 예약한다.
                    stop_price = round(self.s['entry_price'] - self.stoploss)
                    found = False
                    for order in self.data['order']:
                        # limit 주문만 수정. stop은 손절주문이므로 수정하지 않으며, update_position에서 수정함.
                        if order['ordType'] == 'Stop' and order['side'] == 'Sell':
                            if order['stopPx'] != stop_price:
                                order_id = order['orderID']
                                self.api.edit_order(id=order_id, symbol=symbol, type='Stop', side='Buy',
                                                    amount=abs(current_qty),
                                                    params={'execInst': 'Close,LastPrice', 'stopPx': stop_price})
                            found = True
                            break
                    if not found:
                        self.api.create_order(symbol, type='Stop', side='Sell',
                                              amount=abs(current_qty),
                                              params={'execInst': 'Close,LastPrice', 'stopPx': stop_price})
                elif current_qty < 0:
                    # 숏 포지션은 위쪽에 Buy를 예약한다.
                    stop_price = round(self.s['entry_price'] + self.stoploss)
                    found = False
                    for order in self.data['order']:
                        # limit 주문만 수정. stop은 손절주문이므로 수정하지 않으며, update_position에서 수정함.
                        if order['ordType'] == 'Stop' and order['side'] == 'Buy':
                            if order['stopPx'] != stop_price:
                                order_id = order['orderID']
                                self.api.edit_order(id=order_id, symbol=symbol, type='Stop', side='Sell',
                                                    amount=abs(current_qty),
                                                    params={'execInst': 'Close,LastPrice', 'stopPx': stop_price})
                            found = True
                            break
                    if not found:
                        self.api.create_order(symbol, type='Stop', side='Buy',
                                              amount=abs(current_qty),
                                              params={'execInst': 'Close,LastPrice', 'stopPx': stop_price})
Example #5
0
 async def connect(self):
     self.websocket = await websockets.connect(
         self.url, extra_headers=self.__get_auth())
     self.connected = True
     logger.info('Connected! websocket > %s...', self.websocket)
     if self.websocket:
         text = utils.json_dumps(self.topic_request)
         await self.websocket.send(text)
Example #6
0
 def load(self):
     logger.info(f'Load data: {os.path.abspath(self.filepath)}')
     self._data = pd.read_csv(self.filepath, index_col='time',
                              usecols=['time', 'open', 'high', 'low', 'close', 'volume'],
                              parse_dates=['time'],
                              date_parser=lambda epoch: pd.to_datetime(epoch, unit='s')
                              )
     self._size = len(self._data.index)
Example #7
0
 async def start(self):
     try:
         if self.candle_handler:
             # await
             self.candle_handler.start()
         else:
             logger.info('candle_handler 를 사용하지 않습니다.')
     except:
         utils.print_traceback()
Example #8
0
    async def start(self):
        if not self.candle_handler:
            logger.error('Nexus mock is not loaded yet!')
            return

        logger.info('Backtest start timer!')
        while True:
            df = await self._update_candle()
            if df is None:
                break
Example #9
0
 async def _run_server(self):
     try:
         app = web.Application()
         app.add_routes([
             web.get('/', self._handle_info),
         ])
         runner = web.AppRunner(app)
         await runner.setup()
         site = web.TCPSite(runner, '0.0.0.0', self.http_port)
         await site.start()
         logger.info('HTTP server bind port %s', self.http_port)
     except Exception as e:
         logger.error('_run_server error %s', e)
Example #10
0
async def send_telegram(telegram_bot_token, telegram_chat_id, text):
    if telegram_bot_token and telegram_chat_id:
        logger.info('Telegram %s [%s] [%s]', text, telegram_chat_id,
                    telegram_bot_token)
        url = f'https://api.telegram.org/bot{telegram_bot_token}/sendMessage'
        data = {
            'chat_id': telegram_chat_id,
            'text': text,
        }
        async with aiohttp.ClientSession() as session:
            async with session.post(url, data=data) as response:
                resp = await response.text()
                return resp
Example #11
0
    async def load(self):
        try:

            # await
            self.api.load_markets()

            if self.candle_handler:
                # await
                self.candle_handler.load()
            else:
                logger.info('candle_handler 를 사용하지 않습니다.')

            await self.ws.connect()
        except:
            utils.print_traceback()
Example #12
0
    def __init__(self, params):
        self.symbol = params['symbol']
        self.amount = int(params['amount'])  # 계약 갯수.
        self.stoploss = int(params['stoploss'])  # 자동손절범위. 강제청산피하기. 달러.
        self.losscut = int(params['losscut'])  # 손해발생시 반대주문을 낼 범위. 달러
        self.takeprofit = int(params['takeprofit'])  # 익절범위. 달러.
        logger.info('%s created!', self.__class__.__name__)

        # 상태.
        self.s = {
            'current_qty': 0,
            'entry_price': 0,
            'order': 0,
            'bid1': 0,
            'ask1': 0,
        }
Example #13
0
        def handle_topic(topic, action, data):
            print(topic, action, data)
            if topic == 'margin':
                if action == 'partial':
                    self.margin = data
                elif action == 'update':
                    self.margin.update(data)
                    used_margin = self.margin['maintMargin']
                    avail_margin = self.margin['availableMargin']
                    logger.debug(
                        f'[Margin] used_margin[{used_margin}], avail_margin[{avail_margin}]'
                    )

            elif topic == 'order':
                # for order in data:

                if action == 'partial' or action == 'insert':
                    if data:
                        order_id = data['orderID']
                        self.orders[order_id] = data
                else:
                    if data and 'orderID' in data:
                        order_id = data['orderID']
                        working_indicator = data['workingIndicator']
                        if working_indicator:
                            self.orders[order_id].update(data)
                        else:
                            del self.orders[order_id]

                print(self.orders)
                print('--------------------')

            elif topic == 'position':
                if action == 'partial':
                    self.position = data
                else:
                    self.position.update(data)

                # 필수.
                currentQty = self.position['currentQty']
                currency = self.position['currency']
                markPrice = self.position['markPrice']
                timestamp = self.position['timestamp']
                liquidationPrice = self.position['liquidationPrice']

                logger.info('%s %s current_qty[%s]', topic, action, currentQty)
Example #14
0
 def __get_auth(self):
     '''Return auth headers. Will use API Keys if present in settings.'''
     if self.api_key:
         logger.info("BitMEX websocket is authenticated with API Key.")
         # To auth to the WS using an API key, we generate a signature of a nonce and
         # the WS API endpoint.
         expires = generate_nonce()
         return {
             'api-expires':
             str(expires),
             'api-signature':
             generate_signature(self.api_secret, 'GET', '/realtime',
                                expires, ''),
             'api-key':
             self.api_key,
         }
     else:
         logger.warn("BitMEX websocket is not authenticated.")
         return {}
Example #15
0
    def __init__(self, api, symbol, candle_limit, candle_period=None):
        """
        :param symbol: 심볼페어. XBTUSD, BTC/USD
        :param candle_limit: 저장할 최대 캔들갯수.
        :param candle_period: 봉주기. Bitmex는 4가지만 지원한다. 1m, 5m, 1h, 1d
        """
        self.candle_handler = None
        self.api = api
        self.cb_update_candle = None

        logger.info(f'Nexus symbol[{symbol}] period[{candle_period}]')

        if candle_limit and candle_period:
            period_in_second = 0
            if candle_period == '1m':
                period_in_second = 60
            elif candle_period == '5m':
                period_in_second = 300
            elif candle_period == '10m':
                period_in_second = 600
            elif candle_period == '15m':
                period_in_second = 600
            elif candle_period == '1h':
                period_in_second = 3600
            elif candle_period == '4h':
                period_in_second = 3600 * 4
            elif candle_period == '1d':
                period_in_second = 3600 * 24
            ts = datetime.datetime.utcnow().timestamp(
            ) - period_in_second * candle_limit
            since = datetime.datetime.utcfromtimestamp(ts).strftime(
                '%Y-%m-%d %H:00:00')
            logger.info(
                f'Get Candle since {since}, ts[{ts}] candle_limit[{candle_limit}]'
            )
            # 현재시각에서 since를 뺀 날짜를 string으로 만든다.
            self.candle_handler = CandleHandler(
                self.api,
                symbol,
                period=candle_period,
                since=since,
                _update_notify=self._update_candle)
Example #16
0
def ccxt_exchange(exchange,
                  api_key=None,
                  api_secret=None,
                  is_async=True,
                  opt={}):

    if api_key is not None and api_secret is not None:
        opt.update({
            'apiKey': api_key,
            'secret': api_secret,
        })
    logger.info('exchange_id >>> %s', exchange)
    if is_async:
        api = getattr(ccxt_async, exchange)(opt)
    else:
        api = getattr(ccxt, exchange)(opt)

    api.substituteCommonCurrencyCodes = False

    return api
Example #17
0
    def __init__(self,
                 api,
                 symbol,
                 candle_limit,
                 candle_period=None,
                 dry_run=True):
        """
        :param symbol: 심볼페어. XBTUSD, BTC/USD
        :param dry_run: 참일 경우 실제 주문 api를 호출하지 않는다.
        :param candle_limit: 저장할 최대 캔들갯수.
        :param candle_period: 봉주기. Bitmex는 4가지만 지원한다. 1m, 5m, 1h, 1d
        """
        self.candle_handler = None
        self.api = api
        self.cb_update_candle = None
        self.dry_run = dry_run

        logger.info(
            f'Nexus symbol[{symbol}] period[{candle_period}] dry_run[{dry_run}]'
        )

        if candle_limit and candle_period:
            period_in_second = 0
            if candle_period == '1m':
                period_in_second = 60
            elif candle_period == '5m':
                period_in_second = 300
            elif candle_period == '1h':
                period_in_second = 3600
            elif candle_period == '1d':
                period_in_second = 3600 * 24
            ts = datetime.datetime.now().timestamp(
            ) - period_in_second * candle_limit
            since = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d')
            # 현재시각에서 since를 뺀 날짜를 string으로 만든다.
            self.candle_handler = CandleHandler(self.api,
                                                symbol,
                                                period=candle_period,
                                                since=since)
Example #18
0
 def send_telegram(self, text):
     coro = utils.send_telegram(self.telegram_bot_token,
                                self.telegram_chat_id, text)
     logger.info('Telegram %s [%s] [%s]', text, self.telegram_chat_id,
                 self.telegram_bot_token)
     asyncio.get_event_loop().create_task(coro)
Example #19
0
    def run(self, algo):
        self.nexus.callback(update_orderbook=algo.update_orderbook,
                            update_candle=algo.update_candle,
                            update_order=algo.update_order,
                            update_position=algo.update_position)
        # ccxt api 연결.
        algo.api = self.nexus.api
        algo.data = self.nexus.ws.data
        algo.send_telegram = self.send_telegram

        loop = asyncio.get_event_loop()
        try:
            logger.info('SYMBOL: %s', self.symbol)
            logger.info('CANDLE_PERIOD: %s', self.candle_period)
            logger.info('NOW: %s', datetime.now())
            logger.info('UCT: %s', datetime.utcnow())
            logger.info('ENV[TZ]: %s', os.getenv("TZ"))
            logger.info('LOGLEVEL: %s', os.getenv("LOGLEVEL"))
            logger.info('TZNAME: %s', time.tzname)
            ip_address = requests.get('http://ip.42.pl/raw').text
            logger.info('IP: %s', ip_address)
            logger.info('Loading...')
            self.send_telegram(
                'Futuremaker Bot started.. {}'.format(ip_address))
            loop.run_until_complete(self.nexus.load())
            nexus_start = loop.create_task(self.nexus.start())
            loop.run_until_complete(self.init())
            scheduled_task = loop.create_task(self.schedule())
            if self.http_port and not self.backtest:
                server_task = loop.create_task(self._run_server())

            async def go():
                await nexus_start

            loop.run_until_complete(go())
        except KeyboardInterrupt:
            pass
        finally:
            loop.stop()
Example #20
0
    def update_candle(self, df, candle):
        logger.info('update_candle %s > %s : %s', df.index[-1], df.iloc[-1],
                    candle)
        price = df.Close[-1]
        m, u, l = indicators.bollinger_bands(df.Close, n=20, stdev=1.5)

        high = round(u[-1], 2)
        low = round(l[-1], 2)
        logger.info('BBands high[%s] price[%s] low[%s]', high, price, low)
        df = indicators.RSI(df, period=14)
        rsi2 = ta.RSI(df.Close.values)
        rsi = df.RSI[-1]
        rsi = round(rsi, 2)
        rsi2 = round(rsi2[-1], 2)
        logger.info('RSI[%s] RSI2[%s]', rsi, rsi2)

        score = 0
        if price >= high:
            logger.info('BBands Upper touched! price[%s] >= bbhigh[%s]', price,
                        high)
            score += 1
        elif price <= low:
            logger.info('BBands Lower touched! price[%s] <= bblow[%s]', price,
                        low)
            score -= 1

        if rsi >= 60:
            logger.info('RSI[%s] >= 60.', rsi)
            score += 1
        elif rsi <= 40:
            logger.info('RSI[%s] <= 40.', rsi)
            score -= 1

        order_qty = 10
        if score >= 2:
            # 질문 메시지
            text = f'주문타입: <b>BUY</b>\n' \
                   f'가격: {df.Close[-1]}\n' \
                   f'수량: {order_qty}\n' \
                   f'이유: {self.symbol} Too much buy! price[{price}] >= bbhigh[{high}] AND RSI[{rsi}] >= 60\n'\
                   f'<a href="https://www.bitmex.com/chartEmbed?symbol=XBTUSD">차트열기</a> \n' \
                   f'<a href="https://www.bitmex.com/app/trade/XBTUSD">거래소열기</a> \n'
            self.telegram_bot.send_question(question_text=text,
                                            yes_name="BUY",
                                            yes_func=self.order,
                                            yes_param={
                                                "df": df,
                                                "qty": order_qty
                                            },
                                            no_name="CANCEL")

        elif score <= -2:
            text = f'주문타입: <b>SELL</b>\n' \
                   f'가격: {df.Close[-1]}\n' \
                   f'수량: {-order_qty}\n' \
                   f'이유: {self.symbol} Too much sell! price[{price}] <= bblow[{low}] AND RSI[{rsi}] <= 40\n' \
                   f'<a href="https://www.bitmex.com/chartEmbed?symbol=XBTUSD">차트열기</a> \n' \
                   f'<a href="https://www.bitmex.com/app/trade/XBTUSD">거래소열기</a> \n'
            self.telegram_bot.send_question(question_text=text,
                                            yes_name="SELL",
                                            yes_func=self.order,
                                            yes_param={
                                                "df": df,
                                                "qty": -order_qty
                                            },
                                            no_name="CANCEL")
Example #21
0
    def update_candle(self, df, candle):
        # logger.info('update_candle %s > %s : %s', df.index[-1], df.iloc[-1], candle)
        logger.info(
            '>> %s >> %s', datetime.fromtimestamp(df.index[-1] / 1000),
            f'O:{candle.Open} H:{candle.High} L:{candle.Low} C:{candle.Close} V:{candle.Volume}'
        )
        price = df.Close.iloc[-1]
        m, u, l = indicators.bollinger_bands(df.Close, n=20, stdev=1.5)

        high = round(u.iloc[-1], 2)
        low = round(l.iloc[-1], 2)
        logger.info('BBands high[%s] price[%s] low[%s]', high, price, low)
        df = indicators.RSI(df, period=14)
        rsi = df.RSI.iloc[-1]
        rsi = round(rsi, 2)
        logger.info('RSI[%s]', rsi)

        score = 0
        if price >= high:
            logger.info('BBands Upper touched! price[%s] >= bbhigh[%s]', price,
                        high)
            score += 1
        elif price <= low:
            logger.info('BBands Lower touched! price[%s] <= bblow[%s]', price,
                        low)
            score -= 1

        if rsi >= 60:
            logger.info('RSI[%s] >= 60.', rsi)
            score += 1
        elif rsi <= 40:
            logger.info('RSI[%s] <= 40.', rsi)
            score -= 1

        if score >= 2:
            self.send_telegram(
                f'{self.symbol} Too much buy! price[{price}] >= bbhigh[{high}] AND RSI[{rsi}] >= 60'
            )
        elif score <= -2:
            self.send_telegram(
                f'{self.symbol} Too much sell! price[{price}] <= bblow[{low}] AND RSI[{rsi}] <= 40'
            )

        sys.exit(0)
Example #22
0
 def update_order(self, order):
     logger.info('update_order > %s', order)
Example #23
0
 def update_position(self, position):
     logger.info('update_position > %s', position)
Example #24
0
    def update_position(self, position):
        logger.info('update_position > %s', position)
        try:
            price = position["avgEntryPrice"]
            self.avg_entry_price = position["avgEntryPrice"]
        except KeyError:
            logger.debug("시정 평균가가 변동이 없습니다.")
            price = self.avg_entry_price

        current_qty = 0 if position["currentQty"] is None else position["currentQty"] * -1

        if position['currentQty'] > 0:
            self.position = "BOT"
            price = price + self.limit_tick
        elif position['currentQty'] < 0:
            self.position = "SLD"
            price = price - self.limit_tick
        else:
            self.position = "NTL"

        logger.info(f"규모: {current_qty}, 시장평균가: {price}")
        if position["currentQty"] != 0:
            # 포지션을 가지고 있을 경우
            if self.limit_order is None:
                # 익절 주문.
                self.limit_order = self.nexus.api.put_order(order_qty=current_qty,
                                                            price=price,
                                                            stop_price=price,
                                                            type="LimitIfTouched")

            elif self.limit_order is not None \
                    and self.is_order_request \
                    and self.limit_order["orderQty"] != current_qty:
                # 기존 주문 가격, 규모 수정.
                self.limit_order = self.nexus.api.amend_order(order=self.limit_order,
                                                              order_qty=current_qty,
                                                              price=price,
                                                              stop_price=price,
                                                              type="LimitIfTouched")

            self.is_order_request = False
        else:
            # 포지션이 없을때.
            pass

        if self.count % 2 == 0:
            return

        try:
            last_price = position["lastPrice"]
        except KeyError:
            return

        order_list = self.nexus.api.get_order()
        cancel_orders = []
        remove_orders = []
        for pare_order in self.pare_orders:
            high_price = abs(pare_order["price"]) + (0.5 * 10)
            low_price = abs(pare_order["price"]) - (0.5 * 10)

            # 가격 범위가 넘은 경우
            if last_price > high_price or low_price > last_price:
                cancel_orders.append(pare_order)

            # 대기 중이 아닌경우
            is_deplay = False
            for order in order_list:
                if pare_order["limit_order"]["orderID"] == order["orderID"]:
                    is_deplay = True
                    break
            if not is_deplay:
                remove_orders.append(pare_order)

            # 취소처리
            for cancel_order in cancel_orders:
                remove_orders.append(cancel_order)
                try:
                    self.nexus.api.cancel_order(cancel_order["limit_order"])
                except ExchangeError:
                    pass
                try:
                    self.nexus.api.cancel_order(cancel_order["stop_limit_order"])
                except ExchangeError:
                    pass

            # 저장된 데이터 제거
            for remove_order in remove_orders:
                for pare_order in self.pare_orders:
                    if remove_order["limit_order"]["orderID"] == pare_order["limit_order"]["orderID"]:
                        self.pare_orders.remove(pare_order)
                        break
Example #25
0
def ingest_data(api,
                symbol,
                start_date,
                end_date,
                interval,
                history=0,
                reload=False):
    """
    데이터를 받아서 csv파일로 저장한다.
    만약 파일이 존재한다면 스킵한다.
    :return: 해당 파일이 존재하는 디렉토리 경로.
    """
    dir = tempfile.gettempdir()
    timer_start = timeit.default_timer()
    tz = start_date.tzinfo

    # 값, 단위 분리
    interval_num, interval_unit = split_interval(interval)

    interval = interval.lower()
    interval_unit = interval_unit.lower()

    base_dir, filepath = ingest_filepath(dir, api, symbol, start_date,
                                         end_date, interval, history)

    logger.debug('file_path=%s', filepath)
    # 강제 리로드 요청이 없고, 파일이 존재하고, 사이즈도 0보다 크면, 그냥 사용.
    if not reload and os.path.exists(
            filepath) and os.path.getsize(filepath) > 0:
        logger.debug('# [{}] CandleFile Download Passed. {}'.format(
            symbol, filepath))
        return base_dir, filepath

    prepare_date = get_prepare_date(start_date, interval, history)
    delta = (end_date - prepare_date)
    # 나누어 받을때 다음번 루프의 시작시점이 된다.
    next_delta = 0
    if interval_unit == 'm':
        length = (delta.days * 24 * 60 + delta.seconds // 60) // interval_num
        next_delta = timedelta(minutes=interval_num).total_seconds()
    elif interval_unit == 'h':
        length = (delta.days * 24 + delta.seconds // 3600) // interval_num
        next_delta = timedelta(hours=interval_num).total_seconds()
    elif interval_unit == 'd':
        length = (delta.days) // interval_num
        next_delta = timedelta(days=interval_num).total_seconds()

    since = int(prepare_date.timestamp()) * 1000
    logger.info('Ingest time range: %s ~ %s', prepare_date, end_date)
    logger.info('Ingest candle length[%s] of [%s] since[%s]', length,
                interval_unit, since)

    params = {}
    if api.id == 'bitmex':
        max_limit = 750
        # params = { 'partial': 'true' }
    elif api.id == 'binance':
        max_limit = 1000

    with open(filepath, 'w', encoding='utf-8', newline='') as f:

        wr = csv.writer(f)
        wr.writerow(
            ['Datetime', 'Index', 'Open', 'High', 'Low', 'Close', 'Volume'])

        seq = 0
        while length > 0:
            if seq > 0:
                time.sleep(api.rateLimit / 1000)  # time.sleep wants seconds

            limit = min(length, max_limit)
            logger.debug('#### fetch_ohlcv request [%s / %s]', limit, length)
            # candles = await
            candles = fetch_ohlcv(api, symbol, interval, since, limit, params)
            # 읽은 갯수만큼 빼준다.
            length -= limit
            logger.debug('#### fetch_ohlcv remnant [%s]', length)
            logger.debug('#### fetch_ohlcv response [%s] >> \n%s',
                         len(candles), candles)

            if len(candles) == 0:
                logger.warn('candle data rows are empty!')
                break

            last_candle = None
            for candle in candles:
                wr.writerow([
                    datetime.fromtimestamp(
                        int(candle[0] / 1000),
                        tz=tz).strftime('%Y-%m-%d %H:%M:%S'),
                    int(candle[0]),
                    '{:.8f}'.format(candle[1]),
                    '{:.8f}'.format(candle[2]),
                    '{:.8f}'.format(candle[3]),
                    '{:.8f}'.format(candle[4]),
                    '{:.2f}'.format(candle[5]),
                ])
                last_candle = candle

            # 다음번 루프는 마지막 캔들부터 이어서 내려받는다.
            logger.debug('>>> last_candle >>> %s', last_candle)
            since = last_candle[0] + next_delta
            seq += 1

    timer_end = timeit.default_timer()
    logger.debug('# {} Downloaded CandleFile. elapsed: {}'.format(
        symbol, str(timer_end - timer_start)))
    return base_dir, filepath
Example #26
0
    def update_candle(self, df, candle):
        logger.info('>> %s >> %s', datetime.fromtimestamp(df.index[-1] / 1000),
                    f"O:{candle['open']} H:{candle['high']} L:{candle['low']} C:{candle['close']} V:{candle['volume']}")
        logger.info('orders > %s', self.data['order'])
        logger.info('self.s > %s', self.s)
        # 다음 캔들이 도착할때까지 체결못하면 수정 또는 취소.
        for order in self.data['order']:
            # limit 주문만 수정. stop은 손절주문이므로 수정하지 않으며, update_position에서 수정함.
            if order['ordType'] == 'Limit' and order['leavesQty'] > 0:
                price = order['price']
                order_id = order['orderID']
                if order['side'] == 'Sell':
                    if abs(self.s['ask1'] - price) >= 1:  # 3틱이상 밀리면 취소.
                        # 1달러보다 벌어지면 취소.
                        self.api.cancel_order(id=order_id, symbol=symbol)
                elif order['side'] == 'Buy':
                    if abs(self.s['bid1'] - price) >= 1:  # 3틱이상 밀리면 취소.
                        self.api.cancel_order(id=order_id, symbol=symbol)

        hei = heikinashi(df)
        logger.info('Enter..')
        self.enter(df, hei)
        logger.info('Leave..')
        self.leave(df, hei)
        logger.info('Done..')
Example #27
0
    def update_candle(self, df, candle):
        logger.info(f"시장가격: {df.Close[-1]}")
        if self.count < 5:
            self.count += 1
            return
        self.count = 0

        diff_sum = sum(df.Close - df.Open)
        add_qty = 0
        logger.info(f"포지션: {self.position}, 차이가격: {diff_sum}")
        if diff_sum > 0:

            if self.position == "SLD":
                # SLD 포지션이면 정리.
                for pare_order in self.pare_orders:
                    try:
                        self.nexus.api.cancel_order(pare_order["limit_order"])
                    except ExchangeError:
                        logger.debug("<ignore> 없어진 요청.")
                    try:
                        self.nexus.api.cancel_order(pare_order["stop_limit_order"])
                    except ExchangeError:
                        logger.debug("<ignore> 없어진 요청.")

                add_qty = self.limit_order["orderQty"]
                self.limit_order = None
                self.pare_orders = []

            # 매수
            order = self.nexus.api.put_order(order_qty=(self.order_qty + add_qty),
                                             # side="Buy",
                                             price=df.Close[-1] - 0.5
                                             )
            if order["ordStatus"] == "New":
                stop_limit_order = self.nexus.api.put_order(order_qty=-(self.order_qty + add_qty),
                                                            # side="Sell",
                                                            price=df.Close[-1] - 0.5 - self.stop_limit_tick,
                                                            stop_price=df.Close[-1] - 0.5 - self.stop_limit_tick,
                                                            type="StopLimit")
                self.pare_orders.append({
                    "price": df.Close[-1],
                    "position": "BOT",
                    "limit_order": order,
                    "stop_limit_order": stop_limit_order
                })
                self.is_order_request = True

        elif diff_sum < 0:

            if self.position == "BOT":
                # BOT 포지션이면 정리.
                for pare_order in self.pare_orders:
                    try:
                        self.nexus.api.cancel_order(pare_order["limit_order"])
                    except ExchangeError:
                        logger.debug("<ignore> 없어진 요청.")
                    try:
                        self.nexus.api.cancel_order(pare_order["stop_limit_order"])
                    except ExchangeError:
                        logger.debug("<ignore> 없어진 요청.")

                add_qty = self.limit_order["orderQty"]
                self.limit_order = None
                self.pare_orders = []

            # 매도
            order = self.nexus.api.put_order(order_qty=-(self.order_qty + add_qty),
                                             # side="Sell",
                                             price=df.Close[-1] + 0.5)
            if order["ordStatus"] == "New":
                stop_limit_order = self.nexus.api.put_order(order_qty=(self.order_qty + add_qty),
                                                            # side="Buy",
                                                            price=df.Close[-1] + 0.5 + self.stop_limit_tick,
                                                            stop_price=df.Close[-1] + 0.5 + self.stop_limit_tick,
                                                            type="StopLimit")
                self.pare_orders.append({
                    "price": df.Close[-1],
                    "position": "SLD",
                    "limit_order": order,
                    "stop_limit_order": stop_limit_order
                })
                self.is_order_request = True
Example #28
0
def restart():
    logger.info("Restarting the marketmaker...")
    cmd = [sys.executable] + sys.argv
    logger.info("Restarting cmd >> %s", cmd)
    os.execv(sys.executable, cmd)
Example #29
0
    async def run(self, algo):
        self.nexus.callback(update_candle=algo._update_candle)

        algo.api = self.api
        algo.local_tz = self.local_tz
        algo.send_message = self.send_message
        algo.backtest = self.backtest
        try:
            logger.info('SYMBOL: %s', self.symbol)
            logger.info('CANDLE_PERIOD: %s', self.candle_period)
            logger.info('UCT: %s', datetime.utcnow())
            logger.info('ENV[TZ]: %s', os.getenv("TZ"))
            logger.info('TIMEZONE: %s', self.timezone)
            logger.info('LocalTime: %s', utils.localtime(datetime.utcnow(), self.local_tz))
            logger.info('LOGLEVEL: %s', os.getenv("LOGLEVEL"))
            logger.info('TZNAME: %s', time.tzname)
            ip_address = requests.get('https://api.ipify.org?format=json').json()['ip']
            logger.info('IP: %s', ip_address)
            self.send_message(f'{algo.get_name()} Bot started.. {ip_address}')
            logger.info('Loading...')

            # 봇 상태로드
            algo.load_status()
            # ready 구현체가 있다면 호출.
            algo.ready()

            if not self.backtest:
                t = threading.Thread(target=self.start_sched,  daemon=True)
                t.start()
            await self.nexus.load()
            logger.info('Start!')
            await self.nexus.start()
        except KeyboardInterrupt:
            pass
Example #30
0
    async def run(self, algo):
        self.nexus.callback(update_orderbook=algo.update_orderbook,
                            update_candle=algo.update_candle,
                            update_order=algo.update_order,
                            update_position=algo.update_position)
        # ccxt api 연결.
        # algo.api = self.nexus.api
        # algo.data = self.nexus.api.data
        algo.send_telegram = self.send_telegram

        try:
            logger.info('SYMBOL: %s', self.symbol)
            logger.info('CANDLE_PERIOD: %s', self.candle_period)
            logger.info('NOW: %s', datetime.now())
            logger.info('UCT: %s', datetime.utcnow())
            logger.info('ENV[TZ]: %s', os.getenv("TZ"))
            logger.info('LOGLEVEL: %s', os.getenv("LOGLEVEL"))
            logger.info('TZNAME: %s', time.tzname)
            ip_address = requests.get('https://api.ipify.org?format=json').json()['ip']
            logger.info('IP: %s', ip_address)
            await self.send_telegram(f'{algo.get_name()} Bot started.. {ip_address}')
            logger.info('Loading...')
            await self.nexus.load()
            await self.nexus.wait_ready()
            await self.nexus.start()
        except KeyboardInterrupt:
            pass