Exemplo n.º 1
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 FreqtradeException as e:
                logger.warning('Unable to create trade: %s', e)

        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

            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 RuntimeError:
        rpc.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
Exemplo n.º 2
0
def cleanup(*args, **kwargs) -> None:
    """
    Cleanup the application state und finish all pending tasks
    :return: None
    """
    rpc.send_msg('*Status:* `Stopping trader...`')
    logger.info('Stopping trader and cleaning up modules...')
    update_state(State.STOPPED)
    persistence.cleanup()
    rpc.cleanup()
    exit(0)
Exemplo n.º 3
0
def cleanup() -> None:
    """
    Cleanup the application state und finish all pending tasks
    :return: None
    """
    rpc.send_msg('*Status:* `Stopping trader...`')
    logger.info('Stopping trader and cleaning up modules...')
    update_state(State.STOPPED)
    persistence.cleanup()
    rpc.cleanup()
    exit(0)
Exemplo n.º 4
0
def create_trade(stake_amount: float) -> Optional[Trade]:
    """
    Checks the implemented trading indicator(s) for a randomly picked pair,
    if one pair triggers the buy_signal a new trade record gets created
    :param stake_amount: amount of btc to spend
    """
    logger.info(
        'Checking buy signals to create a new trade with stake_amount: %f ...',
        stake_amount)
    whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])
    # Check if stake_amount is fulfilled
    if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
        raise FreqtradeException(
            'stake amount is not fulfilled (currency={})'.format(
                _CONF['stake_currency']))

    # Remove currently opened and latest pairs from whitelist
    for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
        if trade.pair in whitelist:
            whitelist.remove(trade.pair)
            logger.debug('Ignoring %s in pair whitelist', trade.pair)
    if not whitelist:
        raise FreqtradeException('No pair in whitelist')

    # Pick pair based on StochRSI buy signals
    for _pair in whitelist:
        if get_signal(_pair, SignalType.BUY):
            pair = _pair
            break
    else:
        return None

    # Calculate amount and subtract fee
    fee = exchange.get_fee()
    buy_limit = get_target_bid(exchange.get_ticker(pair))
    amount = (1 - fee) * stake_amount / buy_limit

    order_id = exchange.buy(pair, buy_limit, amount)
    # Create trade entity and return
    rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
        exchange.get_name().upper(), pair.replace('_', '/'),
        exchange.get_pair_detail_url(pair), buy_limit))
    # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
    return Trade(pair=pair,
                 stake_amount=stake_amount,
                 amount=amount,
                 fee=fee * 2,
                 open_rate=buy_limit,
                 open_date=datetime.utcnow(),
                 exchange=exchange.get_name().upper(),
                 open_order_id=order_id)
Exemplo n.º 5
0
def execute_sell(trade: Trade, limit: float) -> None:
    """
    Executes a limit sell for the given trade and limit
    :param trade: Trade instance
    :param limit: limit rate for the sell order
    :return: None
    """
    # Execute sell and update trade record
    order_id = exchange.sell(str(trade.pair), limit, trade.amount)
    trade.open_order_id = order_id

    fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
    profit_trade = trade.calc_profit(rate=limit)

    message = '*{exchange}:* Selling [{pair}]({pair_url}) with limit `{limit:.8f}`'.format(
        exchange=trade.exchange,
        pair=trade.pair.replace('_', '/'),
        pair_url=exchange.get_pair_detail_url(trade.pair),
        limit=limit
    )

    # For regular case, when the configuration exists
    if 'stake_currency' in _CONF and 'fiat_display_currency' in _CONF:
        fiat_converter = CryptoToFiatConverter()
        profit_fiat = fiat_converter.convert_amount(
            profit_trade,
            _CONF['stake_currency'],
            _CONF['fiat_display_currency']
        )
        message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \
                   '` / {profit_fiat:.3f} {fiat})`'.format(
                       gain="profit" if fmt_exp_profit > 0 else "loss",
                       profit_percent=fmt_exp_profit,
                       profit_coin=profit_trade,
                       coin=_CONF['stake_currency'],
                       profit_fiat=profit_fiat,
                       fiat=_CONF['fiat_display_currency'],
                   )
    # Because telegram._forcesell does not have the configuration
    # Ignore the FIAT value and does not show the stake_currency as well
    else:
        message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f})`'.format(
            gain="profit" if fmt_exp_profit > 0 else "loss",
            profit_percent=fmt_exp_profit,
            profit_coin=profit_trade
        )

    # Send the message
    rpc.send_msg(message)
    Trade.session.flush()
Exemplo n.º 6
0
def execute_sell(trade: Trade, limit: float) -> None:
    """
    Executes a limit sell for the given trade and limit
    :param trade: Trade instance
    :param limit: limit rate for the sell order
    :return: None
    """
    # Execute sell and update trade record
    order_id = exchange.sell(str(trade.pair), limit, trade.amount)
    trade.open_order_id = order_id

    fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)
    rpc.send_msg(
        '*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(
            trade.exchange, trade.pair.replace('_', '/'),
            exchange.get_pair_detail_url(trade.pair), limit, fmt_exp_profit))
Exemplo n.º 7
0
def execute_sell(trade: Trade, limit: float) -> None:
    """
    Executes a limit sell for the given trade and limit
    :param trade: Trade instance
    :param limit: limit rate for the sell order
    :return: None
    """
    # Execute sell and update trade record
    order_id = exchange.sell(str(trade.pair), limit, trade.amount)
    trade.open_order_id = order_id

    fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)
    profit_trade = trade.calc_profit(rate=limit)

    message = '*{exchange}:* Selling [{pair}]({pair_url}) with limit `{limit:.8f}`'.format(
        exchange=trade.exchange,
        pair=trade.pair.replace('_', '/'),
        pair_url=exchange.get_pair_detail_url(trade.pair),
        limit=limit)

    # For regular case, when the configuration exists
    if 'stake_currency' in _CONF and 'fiat_display_currency' in _CONF:
        fiat_converter = CryptoToFiatConverter()
        profit_fiat = fiat_converter.convert_amount(
            profit_trade, _CONF['stake_currency'],
            _CONF['fiat_display_currency'])
        message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \
                   '` / {profit_fiat:.3f} {fiat})`'.format(
                       gain="profit" if fmt_exp_profit > 0 else "loss",
                       profit_percent=fmt_exp_profit,
                       profit_coin=profit_trade,
                       coin=_CONF['stake_currency'],
                       profit_fiat=profit_fiat,
                       fiat=_CONF['fiat_display_currency'],
                   )
    # Because telegram._forcesell does not have the configuration
    # Ignore the FIAT value and does not show the stake_currency as well
    else:
        message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f})`'.format(
            gain="profit" if fmt_exp_profit > 0 else "loss",
            profit_percent=fmt_exp_profit,
            profit_coin=profit_trade)

    # Send the message
    rpc.send_msg(message)
    Trade.session.flush()
Exemplo n.º 8
0
def main():
    """
    Loads and validates the config and handles the main loop
    :return: None
    """
    global _CONF
    args = parse_args(sys.argv[1:])
    if not args:
        exit(0)

    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    )

    logger.info('Starting freqtrade %s (loglevel=%s)', __version__,
                logging.getLevelName(args.loglevel))

    # Load and validate configuration
    _CONF = load_config(args.config)

    # Initialize all modules and start main loop
    if args.dynamic_whitelist:
        logger.info(
            'Using dynamically generated whitelist. (--dynamic-whitelist detected)'
        )
    init(_CONF)
    old_state = None
    while True:
        new_state = get_state()
        # Log state transition
        if new_state != old_state:
            rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
            logger.info('Changing state to: %s', new_state.name)

        if new_state == State.STOPPED:
            time.sleep(1)
        elif new_state == State.RUNNING:
            throttle(
                _process,
                min_secs=_CONF['internals'].get('process_throttle_secs', 10),
                dynamic_whitelist=args.dynamic_whitelist,
            )
        old_state = new_state
Exemplo n.º 9
0
def test_send_msg_telegram_disabled(mocker):
    mocker.patch('freqtrade.rpc.REGISTERED_MODULES', [])
    telegram_mock = mocker.patch('freqtrade.rpc.telegram.send_msg', MagicMock())
    send_msg('test')
    assert telegram_mock.call_count == 0
Exemplo n.º 10
0
def test_send_msg_telegram_disabled(mocker):
    mocker.patch('freqtrade.rpc.REGISTERED_MODULES', [])
    telegram_mock = mocker.patch('freqtrade.rpc.telegram.send_msg',
                                 MagicMock())
    send_msg('test')
    assert telegram_mock.call_count == 0
Exemplo n.º 11
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
Exemplo n.º 12
0
def main(sysargv=sys.argv[1:]) -> None:
    """
    Loads and validates the config and handles the main loop
    :return: None
    """
    global _CONF
    args = parse_args(sysargv,
                      'Simple High Frequency Trading Bot for crypto currencies')

    # A subcommand has been issued
    if hasattr(args, 'func'):
        args.func(args)
        exit(0)

    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    )

    logger.info(
        'Starting freqtrade %s (loglevel=%s)',
        __version__,
        logging.getLevelName(args.loglevel)
    )

    # Load and validate configuration
    _CONF = load_config(args.config)

    # Initialize all modules and start main loop
    if args.dynamic_whitelist:
        logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)')

    # If the user ask for Dry run with a local DB instead of memory
    if args.dry_run_db:
        if _CONF.get('dry_run', False):
            _CONF.update({'dry_run_db': True})
            logger.info(
                'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". (--dry_run_db detected)'
            )
        else:
            logger.info('Dry run is disabled. (--dry_run_db ignored)')

    try:
        init(_CONF)
        old_state = None
        while True:
            new_state = get_state()
            # Log state transition
            if new_state != old_state:
                rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
                logger.info('Changing state to: %s', new_state.name)

            if new_state == State.STOPPED:
                time.sleep(1)
            elif new_state == State.RUNNING:
                throttle(
                    _process,
                    min_secs=_CONF['internals'].get('process_throttle_secs', 10),
                    nb_assets=args.dynamic_whitelist,
                )
            old_state = new_state
    except KeyboardInterrupt:
        logger.info('Got SIGINT, aborting ...')
    except BaseException:
        logger.exception('Got fatal exception!')
    finally:
        cleanup()
Exemplo n.º 13
0
def create_trade(stake_amount: float) -> bool:
    """
    Checks the implemented trading indicator(s) for a randomly picked pair,
    if one pair triggers the buy_signal a new trade record gets created
    :param stake_amount: amount of btc to spend
    :return: True if a trade object has been created and persisted, False otherwise
    """
    logger.info(
        'Checking buy signals to create a new trade with stake_amount: %f ...',
        stake_amount
    )
    whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])
    # Check if stake_amount is fulfilled
    if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
        raise DependencyException(
            'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency'])
        )

    # Remove currently opened and latest pairs from whitelist
    for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
        if trade.pair in whitelist:
            whitelist.remove(trade.pair)
            logger.debug('Ignoring %s in pair whitelist', trade.pair)
    if not whitelist:
        raise DependencyException('No pair in whitelist')

    # Pick pair based on StochRSI buy signals
    for _pair in whitelist:
        if get_signal(_pair, SignalType.BUY):
            pair = _pair
            break
    else:
        return False

    # Calculate amount
    buy_limit = get_target_bid(exchange.get_ticker(pair))
    amount = stake_amount / buy_limit

    order_id = exchange.buy(pair, buy_limit, amount)

    fiat_converter = CryptoToFiatConverter()
    stake_amount_fiat = fiat_converter.convert_amount(
        stake_amount,
        _CONF['stake_currency'],
        _CONF['fiat_display_currency']
    )

    # Create trade entity and return
    rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '.format(
        exchange.get_name().upper(),
        pair.replace('_', '/'),
        exchange.get_pair_detail_url(pair),
        buy_limit, stake_amount, _CONF['stake_currency'],
        stake_amount_fiat, _CONF['fiat_display_currency']
    ))
    # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
    trade = Trade(
        pair=pair,
        stake_amount=stake_amount,
        amount=amount,
        fee=exchange.get_fee(),
        open_rate=buy_limit,
        open_date=datetime.utcnow(),
        exchange=exchange.get_name().upper(),
        open_order_id=order_id
    )
    Trade.session.add(trade)
    Trade.session.flush()
    return True
Exemplo n.º 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
Exemplo n.º 15
0
def main(sysargv=sys.argv[1:]) -> None:
    """
    Loads and validates the config and handles the main loop
    :return: None
    """
    global _CONF
    args = parse_args(
        sysargv, 'Simple High Frequency Trading Bot for crypto currencies')

    # A subcommand has been issued
    if hasattr(args, 'func'):
        args.func(args)
        exit(0)

    # Initialize logger
    logging.basicConfig(
        level=args.loglevel,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    )

    logger.info('Starting freqtrade %s (loglevel=%s)', __version__,
                logging.getLevelName(args.loglevel))

    # Load and validate configuration
    _CONF = load_config(args.config)

    # Initialize all modules and start main loop
    if args.dynamic_whitelist:
        logger.info(
            'Using dynamically generated whitelist. (--dynamic-whitelist detected)'
        )

    # If the user ask for Dry run with a local DB instead of memory
    if args.dry_run_db:
        if _CONF.get('dry_run', False):
            _CONF.update({'dry_run_db': True})
            logger.info(
                'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". (--dry_run_db detected)'
            )
        else:
            logger.info('Dry run is disabled. (--dry_run_db ignored)')

    try:
        init(_CONF)
        old_state = None
        while True:
            new_state = get_state()
            # Log state transition
            if new_state != old_state:
                rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
                logger.info('Changing state to: %s', new_state.name)

            if new_state == State.STOPPED:
                time.sleep(1)
            elif new_state == State.RUNNING:
                throttle(
                    _process,
                    min_secs=_CONF['internals'].get('process_throttle_secs',
                                                    10),
                    nb_assets=args.dynamic_whitelist,
                )
            old_state = new_state
    except KeyboardInterrupt:
        logger.info('Got SIGINT, aborting ...')
    except BaseException:
        logger.exception('Got fatal exception!')
    finally:
        cleanup()
Exemplo n.º 16
0
def create_trade(stake_amount: float) -> bool:
    """
    Checks the implemented trading indicator(s) for a randomly picked pair,
    if one pair triggers the buy_signal a new trade record gets created
    :param stake_amount: amount of btc to spend
    :return: True if a trade object has been created and persisted, False otherwise
    """
    logger.info(
        'Checking buy signals to create a new trade with stake_amount: %f ...',
        stake_amount)
    whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])
    # Check if stake_amount is fulfilled
    if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
        raise DependencyException(
            'stake amount is not fulfilled (currency={})'.format(
                _CONF['stake_currency']))

    # Remove currently opened and latest pairs from whitelist
    for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
        if trade.pair in whitelist:
            whitelist.remove(trade.pair)
            logger.debug('Ignoring %s in pair whitelist', trade.pair)
    if not whitelist:
        raise DependencyException('No pair in whitelist')

    # Pick pair based on StochRSI buy signals
    for _pair in whitelist:
        if get_signal(_pair, SignalType.BUY):
            pair = _pair
            break
    else:
        return False

    # Calculate amount
    buy_limit = get_target_bid(exchange.get_ticker(pair))
    amount = stake_amount / buy_limit

    order_id = exchange.buy(pair, buy_limit, amount)

    fiat_converter = CryptoToFiatConverter()
    stake_amount_fiat = fiat_converter.convert_amount(
        stake_amount, _CONF['stake_currency'], _CONF['fiat_display_currency'])

    # Create trade entity and return
    rpc.send_msg(
        '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '.
        format(exchange.get_name().upper(), pair.replace('_', '/'),
               exchange.get_pair_detail_url(pair), buy_limit, stake_amount,
               _CONF['stake_currency'], stake_amount_fiat,
               _CONF['fiat_display_currency']))
    # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
    trade = Trade(pair=pair,
                  stake_amount=stake_amount,
                  amount=amount,
                  fee=exchange.get_fee(),
                  open_rate=buy_limit,
                  open_date=datetime.utcnow(),
                  exchange=exchange.get_name().upper(),
                  open_order_id=order_id)
    Trade.session.add(trade)
    Trade.session.flush()
    return True