Пример #1
0
def test_get_order(default_conf, mocker):
    default_conf['dry_run'] = True
    mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
    order = MagicMock()
    order.myid = 123
    exchange._DRY_RUN_OPEN_ORDERS['X'] = order
    print(exchange.get_order('X'))
    assert exchange.get_order('X').myid == 123

    default_conf['dry_run'] = False
    mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
    api_mock = MagicMock()
    api_mock.get_order = MagicMock(return_value=456)
    mocker.patch('freqtrade.exchange._API', api_mock)
    assert exchange.get_order('X') == 456
Пример #2
0
    def check_handle_timedout(self, timeoutvalue: int) -> None:
        """
        Check if any orders are timed out and cancel if neccessary
        :param timeoutvalue: Number of minutes until order is considered timed out
        :return: None
        """
        timeoutthreashold = arrow.utcnow().shift(
            minutes=-timeoutvalue).datetime

        for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
            try:
                order = exchange.get_order(trade.open_order_id)
            except requests.exceptions.RequestException:
                logger.info('Cannot query order for %s due to %s', trade,
                            traceback.format_exc())
                continue
            ordertime = arrow.get(order['opened'])

            # Check if trade is still actually open
            if int(order['remaining']) == 0:
                continue

            if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold:
                self.handle_timedout_limit_buy(trade, order)
            elif order[
                    'type'] == "LIMIT_SELL" and ordertime < timeoutthreashold:
                self.handle_timedout_limit_sell(trade, order)
Пример #3
0
def _status(bot: Bot, update: Update) -> None:
    """
    Handler for /status.
    Returns the current TradeThread status
    :param bot: telegram bot
    :param update: message update
    :return: None
    """

    # Check if additional parameters are passed
    params = update.message.text.replace('/status', '').split(' ') \
        if update.message.text else []
    if 'table' in params:
        _status_table(bot, update)
        return

    # Fetch open trade
    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
    if get_state() != State.RUNNING:
        send_msg('*Status:* `trader is not running`', bot=bot)
    elif not trades:
        send_msg('*Status:* `no active trade`', bot=bot)
    else:
        for trade in trades:
            order = None
            if trade.open_order_id:
                order = exchange.get_order(trade.open_order_id)
            # calculate profit and send message to user
            current_rate = exchange.get_ticker(trade.pair, False)['bid']
            current_profit = trade.calc_profit_percent(current_rate)
            fmt_close_profit = '{:.2f}%'.format(
                round(trade.close_profit * 100, 2)
            ) if trade.close_profit else None
            message = """
*Trade ID:* `{trade_id}`
*Current Pair:* [{pair}]({market_url})
*Open Since:* `{date}`
*Amount:* `{amount}`
*Open Rate:* `{open_rate:.8f}`
*Close Rate:* `{close_rate}`
*Current Rate:* `{current_rate:.8f}`
*Close Profit:* `{close_profit}`
*Current Profit:* `{current_profit:.2f}%`
*Open Order:* `{open_order}`
            """.format(
                trade_id=trade.id,
                pair=trade.pair,
                market_url=exchange.get_pair_detail_url(trade.pair),
                date=arrow.get(trade.open_date).humanize(),
                open_rate=trade.open_rate,
                close_rate=trade.close_rate,
                current_rate=current_rate,
                amount=round(trade.amount, 8),
                close_profit=fmt_close_profit,
                current_profit=round(current_profit * 100, 2),
                open_order='({} rem={:.8f})'.format(
                    order['type'], order['remaining']
                ) if order else None,
            )
            send_msg(message, bot=bot)
Пример #4
0
def _status(bot: Bot, update: Update) -> None:
    """
    Handler for /status.
    Returns the current TradeThread status
    :param bot: telegram bot
    :param update: message update
    :return: None
    """

    # Check if additional parameters are passed
    params = update.message.text.replace('/status', '').split(' ') \
        if update.message.text else []
    if 'table' in params:
        _status_table(bot, update)
        return

    # Fetch open trade
    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
    if get_state() != State.RUNNING:
        send_msg('*Status:* `trader is not running`', bot=bot)
    elif not trades:
        send_msg('*Status:* `no active trade`', bot=bot)
    else:
        for trade in trades:
            order = None
            if trade.open_order_id:
                order = exchange.get_order(trade.open_order_id)
            # calculate profit and send message to user
            current_rate = exchange.get_ticker(trade.pair)['bid']
            current_profit = trade.calc_profit(current_rate)
            fmt_close_profit = '{:.2f}%'.format(
                round(trade.close_profit * 100, 2)
            ) if trade.close_profit else None
            message = """
*Trade ID:* `{trade_id}`
*Current Pair:* [{pair}]({market_url})
*Open Since:* `{date}`
*Amount:* `{amount}`
*Open Rate:* `{open_rate:.8f}`
*Close Rate:* `{close_rate}`
*Current Rate:* `{current_rate:.8f}`
*Close Profit:* `{close_profit}`
*Current Profit:* `{current_profit:.2f}%`
*Open Order:* `{open_order}`
            """.format(
                trade_id=trade.id,
                pair=trade.pair,
                market_url=exchange.get_pair_detail_url(trade.pair),
                date=arrow.get(trade.open_date).humanize(),
                open_rate=trade.open_rate,
                close_rate=trade.close_rate,
                current_rate=current_rate,
                amount=round(trade.amount, 8),
                close_profit=fmt_close_profit,
                current_profit=round(current_profit * 100, 2),
                open_order='{} ({})'.format(
                    order['remaining'], order['type']
                ) if order else None,
            )
            send_msg(message, bot=bot)
Пример #5
0
def _process(dynamic_whitelist: Optional[bool] = False) -> bool:
    """
    Queries the persistence layer for open trades and handles them,
    otherwise a new trade is created.
    :param: dynamic_whitelist: True is a dynamic whitelist should be generated (optional)
    :return: True if a trade has been created or closed, False otherwise
    """
    state_changed = False
    try:
        # Refresh whitelist based on wallet maintenance
        refresh_whitelist(
            gen_pair_whitelist(_CONF['stake_currency']
                               ) if dynamic_whitelist else None)
        # Query trades from persistence layer
        trades = Trade.query.filter(Trade.is_open.is_(True)).all()
        if len(trades) < _CONF['max_open_trades']:
            try:
                # Create entity and execute trade
                trade = create_trade(float(_CONF['stake_amount']))
                if trade:
                    Trade.session.add(trade)
                    state_changed = True
                else:
                    logger.info(
                        'Checked all whitelisted currencies. '
                        'Found no suitable entry positions for buying. Will keep looking ...'
                    )
            except ValueError:
                logger.exception('Unable to create trade')

        for trade in trades:
            # Get order details for actual price per unit
            if trade.open_order_id:
                # Update trade with order values
                logger.info('Got open order for %s', trade)
                trade.update(exchange.get_order(trade.open_order_id))

            if not close_trade_if_fulfilled(trade):
                # Check if we can sell our current pair
                state_changed = handle_trade(trade) or state_changed

            Trade.session.flush()
    except (requests.exceptions.RequestException,
            json.JSONDecodeError) as error:
        msg = 'Got {} in _process(), retrying in 30 seconds...'.format(
            error.__class__.__name__)
        logger.exception(msg)
        time.sleep(30)
    except RuntimeError:
        telegram.send_msg(
            '*Status:* Got RuntimeError:\n```\n{traceback}```{hint}'.format(
                traceback=traceback.format_exc(),
                hint='Issue `/start` if you think it is safe to restart.'))
        logger.exception('Got RuntimeError. Stopping trader ...')
        update_state(State.STOPPED)
    return state_changed
Пример #6
0
 def rpc_trade_status(self) -> Tuple[bool, Any]:
     """
     Below follows the RPC backend it is prefixed with rpc_ to raise awareness that it is
     a remotely exposed function
     :return:
     """
     # Fetch open trade
     trades = Trade.query.filter(Trade.is_open.is_(True)).all()
     if self.freqtrade.state != State.RUNNING:
         return True, '*Status:* `trader is not running`'
     elif not trades:
         return True, '*Status:* `no active trade`'
     else:
         result = []
         for trade in trades:
             order = None
             if trade.open_order_id:
                 order = exchange.get_order(trade.open_order_id)
             # calculate profit and send message to user
             current_rate = exchange.get_ticker(trade.pair, False)['bid']
             current_profit = trade.calc_profit_percent(current_rate)
             fmt_close_profit = '{:.2f}%'.format(
                 round(trade.close_profit *
                       100, 2)) if trade.close_profit else None
             message = "*Trade ID:* `{trade_id}`\n" \
                       "*Current Pair:* [{pair}]({market_url})\n" \
                       "*Open Since:* `{date}`\n" \
                       "*Amount:* `{amount}`\n" \
                       "*Open Rate:* `{open_rate:.8f}`\n" \
                       "*Close Rate:* `{close_rate}`\n" \
                       "*Current Rate:* `{current_rate:.8f}`\n" \
                       "*Close Profit:* `{close_profit}`\n" \
                       "*Current Profit:* `{current_profit:.2f}%`\n" \
                       "*Open Order:* `{open_order}`"\
                       .format(
                           trade_id=trade.id,
                           pair=trade.pair,
                           market_url=exchange.get_pair_detail_url(trade.pair),
                           date=arrow.get(trade.open_date).humanize(),
                           open_rate=trade.open_rate,
                           close_rate=trade.close_rate,
                           current_rate=current_rate,
                           amount=round(trade.amount, 8),
                           close_profit=fmt_close_profit,
                           current_profit=round(current_profit * 100, 2),
                           open_order='({} rem={:.8f})'.format(
                               order['type'], order['remaining']
                           ) if order else None,
                       )
             result.append(message)
         return False, result
Пример #7
0
    def process_maybe_execute_sell(self, trade: Trade) -> bool:
        """
        Tries to execute a sell trade
        :return: True if executed
        """
        # Get order details for actual price per unit
        if trade.open_order_id:
            # Update trade with order values
            logger.info('Found open order for %s', trade)
            trade.update(exchange.get_order(trade.open_order_id))

        if trade.is_open and trade.open_order_id is None:
            # Check if we can sell our current pair
            return self.handle_trade(trade)
        return False
Пример #8
0
def handle_trade(trade: Trade) -> bool:
    """
    Sells the current pair if the threshold is reached and updates the trade record.
    :return: True if trade has been sold, False otherwise
    """
    if not trade.is_open:
        raise ValueError('attempt to handle closed trade: {}'.format(trade))

    logger.debug('Handling %s ...', trade)

    # first determine if buy order that is still open
    if trade.open_order_id:
        order_info = exchange.get_order(trade.open_order_id)
        order_type = order_info['type']
        if order_type == 'LIMIT_BUY':
            max_open_time = 10
            current_time = datetime.utcnow()
            amount_minutes_open = (current_time -
                                   trade.open_date).total_seconds() / 60.
            if amount_minutes_open > max_open_time:
                health = exchange.get_wallet_health()
                if exchange.get_name() == 'HitBTC':
                    token = trade.pair
                else:
                    token = get_quote_token(trade.pair)
                token_healthy = False
                for status in health:
                    if status['Currency'] == token:
                        token_healthy = status['IsActive']
                if token_healthy:
                    logger.debug('Cancelling %s ...', trade)
                    exchange.cancel_order(trade.open_order_id)
                    # trade.is_open = 0
                    # trade.open_order_id = None
                    Trade.session.delete(trade)
                else:
                    logger.debug(
                        'Cancelling could not execute due to wallet heath for %s ...',
                        trade)
                return False

    current_rate = exchange.get_ticker(trade.pair)['bid']  # ask?
    if current_rate is None:
        return False
    if should_sell(trade, current_rate, datetime.utcnow()):
        execute_sell(trade, current_rate)
        return True
    return False
Пример #9
0
def check_handle_timedout(timeoutvalue: int) -> None:
    """
    Check if any orders are timed out and cancel if neccessary
    :param timeoutvalue: Number of minutes until order is considered timed out
    :return: None
    """
    timeoutthreashold = arrow.utcnow().shift(minutes=-timeoutvalue).datetime

    for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
        order = exchange.get_order(trade.open_order_id)
        ordertime = arrow.get(order['opened'])

        if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold:
            # Buy timeout - cancel order
            exchange.cancel_order(trade.open_order_id)
            if order['remaining'] == order['amount']:
                # if trade is not partially completed, just delete the trade
                Trade.session.delete(trade)
                Trade.session.flush()
                logger.info('Buy order timeout for %s.', trade)
            else:
                # if trade is partially complete, edit the stake details for the trade
                # and close the order
                trade.amount = order['amount'] - order['remaining']
                trade.stake_amount = trade.amount * trade.open_rate
                trade.open_order_id = None
                logger.info('Partial buy order timeout for %s.', trade)
        elif order['type'] == "LIMIT_SELL" and ordertime < timeoutthreashold:
            # Sell timeout - cancel order and update trade
            if order['remaining'] == order['amount']:
                # if trade is not partially completed, just cancel the trade
                exchange.cancel_order(trade.open_order_id)
                trade.close_rate = None
                trade.close_profit = None
                trade.close_date = None
                trade.is_open = True
                trade.open_order_id = None
                logger.info('Sell order timeout for %s.', trade)
                return True
            else:
                # TODO: figure out how to handle partially complete sell orders
                pass
Пример #10
0
def check_handle_timedout(timeoutvalue: int) -> None:
    """
    Check if any orders are timed out and cancel if neccessary
    :param timeoutvalue: Number of minutes until order is considered timed out
    :return: None
    """
    timeoutthreashold = arrow.utcnow().shift(minutes=-timeoutvalue).datetime

    for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
        order = exchange.get_order(trade.open_order_id)
        ordertime = arrow.get(order['opened'])

        if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold:
            # Buy timeout - cancel order
            exchange.cancel_order(trade.open_order_id)
            if order['remaining'] == order['amount']:
                # if trade is not partially completed, just delete the trade
                Trade.session.delete(trade)
                Trade.session.flush()
                logger.info('Buy order timeout for %s.', trade)
            else:
                # if trade is partially complete, edit the stake details for the trade
                # and close the order
                trade.amount = order['amount'] - order['remaining']
                trade.stake_amount = trade.amount * trade.open_rate
                trade.open_order_id = None
                logger.info('Partial buy order timeout for %s.', trade)
        elif order['type'] == "LIMIT_SELL" and ordertime < timeoutthreashold:
            # Sell timeout - cancel order and update trade
            if order['remaining'] == order['amount']:
                # if trade is not partially completed, just cancel the trade
                exchange.cancel_order(trade.open_order_id)
                trade.close_rate = None
                trade.close_profit = None
                trade.close_date = None
                trade.is_open = True
                trade.open_order_id = None
                logger.info('Sell order timeout for %s.', trade)
                return True
            else:
                # TODO: figure out how to handle partially complete sell orders
                pass
Пример #11
0
def _exec_forcesell(trade: Trade) -> None:
    # Check if there is there is an open order
    if trade.open_order_id:
        order = exchange.get_order(trade.open_order_id)

        # Cancel open LIMIT_BUY orders and close trade
        if order and not order['closed'] and order['type'] == 'LIMIT_BUY':
            exchange.cancel_order(trade.open_order_id)
            trade.close(order.get('rate') or trade.open_rate)
            # TODO: sell amount which has been bought already
            return

        # Ignore trades with an attached LIMIT_SELL order
        if order and not order['closed'] and order['type'] == 'LIMIT_SELL':
            return

    # Get current rate and execute sell
    current_rate = exchange.get_ticker(trade.pair, False)['bid']
    from freqtrade.main import execute_sell
    execute_sell(trade, current_rate)
Пример #12
0
def _exec_forcesell(trade: Trade) -> None:
    # Check if there is there is an open order
    if trade.open_order_id:
        order = exchange.get_order(trade.open_order_id)

        # Cancel open LIMIT_BUY orders and close trade
        if order and not order['closed'] and order['type'] == 'LIMIT_BUY':
            exchange.cancel_order(trade.open_order_id)
            trade.close(order.get('rate') or trade.open_rate)
            # TODO: sell amount which has been bought already
            return

        # Ignore trades with an attached LIMIT_SELL order
        if order and not order['closed'] and order['type'] == 'LIMIT_SELL':
            return

    # Get current rate and execute sell
    current_rate = exchange.get_ticker(trade.pair, False)['bid']
    from freqtrade.main import execute_sell
    execute_sell(trade, current_rate)
Пример #13
0
def _process(nb_assets: Optional[int] = 0) -> bool:
    """
    Queries the persistence layer for open trades and handles them,
    otherwise a new trade is created.
    :param: nb_assets: the maximum number of pairs to be traded at the same time
    :return: True if a trade has been created or closed, False otherwise
    """
    state_changed = False
    try:
        # Refresh whitelist based on wallet maintenance
        sanitized_list = refresh_whitelist(
            gen_pair_whitelist(
                _CONF['stake_currency']
            ) if nb_assets else _CONF['exchange']['pair_whitelist']
        )

        # Keep only the subsets of pairs wanted (up to nb_assets)
        final_list = sanitized_list[:nb_assets] if nb_assets else sanitized_list
        _CONF['exchange']['pair_whitelist'] = final_list

        # Query trades from persistence layer
        trades = Trade.query.filter(Trade.is_open.is_(True)).all()
        if len(trades) < _CONF['max_open_trades']:
            try:
                # Create entity and execute trade
                state_changed = create_trade(float(_CONF['stake_amount']))
                if not state_changed:
                    logger.info(
                        'Checked all whitelisted currencies. '
                        'Found no suitable entry positions for buying. Will keep looking ...'
                    )
            except DependencyException as exception:
                logger.warning('Unable to create trade: %s', exception)

        for trade in trades:
            # Get order details for actual price per unit
            if trade.open_order_id:
                # Update trade with order values
                logger.info('Got open order for %s', trade)
                trade.update(exchange.get_order(trade.open_order_id))

            if trade.is_open and trade.open_order_id is None:
                # Check if we can sell our current pair
                state_changed = handle_trade(trade) or state_changed

        if 'unfilledtimeout' in _CONF:
            # Check and handle any timed out open orders
            check_handle_timedout(_CONF['unfilledtimeout'])

            Trade.session.flush()
    except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
        logger.warning(
            'Got %s in _process(), retrying in 30 seconds...',
            error
        )
        time.sleep(30)
    except OperationalException:
        rpc.send_msg('*Status:* Got OperationalException:\n```\n{traceback}```{hint}'.format(
            traceback=traceback.format_exc(),
            hint='Issue `/start` if you think it is safe to restart.'
        ))
        logger.exception('Got OperationalException. Stopping trader ...')
        update_state(State.STOPPED)
    return state_changed
Пример #14
0
def _process(nb_assets: Optional[int] = 0) -> bool:
    """
    Queries the persistence layer for open trades and handles them,
    otherwise a new trade is created.
    :param: nb_assets: the maximum number of pairs to be traded at the same time
    :return: True if a trade has been created or closed, False otherwise
    """
    state_changed = False
    try:
        # Refresh whitelist based on wallet maintenance
        sanitized_list = refresh_whitelist(
            gen_pair_whitelist(_CONF['stake_currency'])
            if nb_assets else _CONF['exchange']['pair_whitelist'])

        # Keep only the subsets of pairs wanted (up to nb_assets)
        final_list = sanitized_list[:nb_assets] if nb_assets else sanitized_list
        _CONF['exchange']['pair_whitelist'] = final_list

        # Query trades from persistence layer
        trades = Trade.query.filter(Trade.is_open.is_(True)).all()
        if len(trades) < _CONF['max_open_trades']:
            try:
                # Create entity and execute trade
                state_changed = create_trade(float(_CONF['stake_amount']))
                if not state_changed:
                    logger.info(
                        'Checked all whitelisted currencies. '
                        'Found no suitable entry positions for buying. Will keep looking ...'
                    )
            except DependencyException as exception:
                logger.warning('Unable to create trade: %s', exception)

        for trade in trades:
            # Get order details for actual price per unit
            if trade.open_order_id:
                # Update trade with order values
                logger.info('Got open order for %s', trade)
                trade.update(exchange.get_order(trade.open_order_id))

            if trade.is_open and trade.open_order_id is None:
                # Check if we can sell our current pair
                state_changed = handle_trade(trade) or state_changed

        if 'unfilledtimeout' in _CONF:
            # Check and handle any timed out open orders
            check_handle_timedout(_CONF['unfilledtimeout'])

            Trade.session.flush()
    except (requests.exceptions.RequestException,
            json.JSONDecodeError) as error:
        logger.warning('Got %s in _process(), retrying in 30 seconds...',
                       error)
        time.sleep(30)
    except OperationalException:
        rpc.send_msg(
            '*Status:* Got OperationalException:\n```\n{traceback}```{hint}'.
            format(traceback=traceback.format_exc(),
                   hint='Issue `/start` if you think it is safe to restart.'))
        logger.exception('Got OperationalException. Stopping trader ...')
        update_state(State.STOPPED)
    return state_changed