コード例 #1
0
ファイル: backtesting.py プロジェクト: starfoxbeta/freqtrade
    def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:

        sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX],  # type: ignore
                                         sell_row[DATE_IDX], sell_row[BUY_IDX], sell_row[SELL_IDX],
                                         low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX])

        if sell.sell_flag:
            trade.close_date = sell_row[DATE_IDX]
            trade.sell_reason = sell.sell_type.value
            trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
            closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)

            # Confirm trade exit:
            time_in_force = self.strategy.order_time_in_force['sell']
            if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)(
                    pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
                    rate=closerate,
                    time_in_force=time_in_force,
                    sell_reason=sell.sell_type.value):
                return None

            trade.close(closerate, show_msg=False)
            return trade

        return None
コード例 #2
0
ファイル: backtesting.py プロジェクト: jlaw/freqtrade
    def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:

        sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX],  # type: ignore
                                         sell_row[DATE_IDX], sell_row[BUY_IDX], sell_row[SELL_IDX],
                                         low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX])
        if sell.sell_flag:

            trade.close_date = sell_row[DATE_IDX]
            trade.sell_reason = sell.sell_type.value
            trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
            closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
            trade.close(closerate, show_msg=False)
            return trade

        return None
コード例 #3
0
ファイル: backtesting.py プロジェクト: raviv1995/freqtrade
 def _enter_trade(self, pair: str, row: List, max_open_trades: int,
                  open_trade_count: int) -> Optional[LocalTrade]:
     try:
         stake_amount = self.wallets.get_trade_stake_amount(
             pair, max_open_trades - open_trade_count, None)
     except DependencyException:
         return None
     min_stake_amount = self.exchange.get_min_pair_stake_amount(
         pair, row[OPEN_IDX], -0.05)
     if stake_amount and (not min_stake_amount
                          or stake_amount > min_stake_amount):
         # Enter trade
         trade = LocalTrade(
             pair=pair,
             open_rate=row[OPEN_IDX],
             open_date=row[DATE_IDX],
             stake_amount=stake_amount,
             amount=round(stake_amount / row[OPEN_IDX], 8),
             fee_open=self.fee,
             fee_close=self.fee,
             is_open=True,
             exchange='backtesting',
         )
         return trade
     return None
コード例 #4
0
ファイル: backtesting.py プロジェクト: starfoxbeta/freqtrade
    def _enter_trade(self, pair: str, row: List) -> Optional[LocalTrade]:
        try:
            stake_amount = self.wallets.get_trade_stake_amount(pair, None)
        except DependencyException:
            return None
        min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05)

        order_type = self.strategy.order_types['buy']
        time_in_force = self.strategy.order_time_in_force['sell']
        # Confirm trade entry:
        if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
                pair=pair, order_type=order_type, amount=stake_amount, rate=row[OPEN_IDX],
                time_in_force=time_in_force):
            return None

        if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
            # Enter trade
            trade = LocalTrade(
                pair=pair,
                open_rate=row[OPEN_IDX],
                open_date=row[DATE_IDX],
                stake_amount=stake_amount,
                amount=round(stake_amount / row[OPEN_IDX], 8),
                fee_open=self.fee,
                fee_close=self.fee,
                is_open=True,
                exchange='backtesting',
            )
            return trade
        return None
コード例 #5
0
ファイル: backtesting.py プロジェクト: arris69/freqtrade
    def _get_sell_trade_entry_for_candle(
            self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:
        sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
        sell = self.strategy.should_sell(
            trade,
            sell_row[OPEN_IDX],  # type: ignore
            sell_candle_time,
            sell_row[BUY_IDX],
            sell_row[SELL_IDX],
            low=sell_row[LOW_IDX],
            high=sell_row[HIGH_IDX])

        if sell.sell_flag:
            trade.close_date = sell_candle_time

            trade_dur = int(
                (trade.close_date_utc - trade.open_date_utc).total_seconds() //
                60)
            closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)

            # Confirm trade exit:
            time_in_force = self.strategy.order_time_in_force['sell']
            if not strategy_safe_wrapper(self.strategy.confirm_trade_exit,
                                         default_retval=True)(
                                             pair=trade.pair,
                                             trade=trade,
                                             order_type='limit',
                                             amount=trade.amount,
                                             rate=closerate,
                                             time_in_force=time_in_force,
                                             sell_reason=sell.sell_reason,
                                             current_time=sell_candle_time):
                return None

            trade.sell_reason = sell.sell_reason

            # Checks and adds an exit tag, after checking that the length of the
            # sell_row has the length for an exit tag column
            if (len(sell_row) > EXIT_TAG_IDX
                    and sell_row[EXIT_TAG_IDX] is not None
                    and len(sell_row[EXIT_TAG_IDX]) > 0):
                trade.sell_reason = sell_row[EXIT_TAG_IDX]

            trade.close(closerate, show_msg=False)
            return trade

        return None
コード例 #6
0
ファイル: backtesting.py プロジェクト: donjonson/freqtrade
    def _enter_trade(self, pair: str, row: List) -> Optional[LocalTrade]:
        try:
            stake_amount = self.wallets.get_trade_stake_amount(pair, None)
        except DependencyException:
            return None

        min_stake_amount = self.exchange.get_min_pair_stake_amount(
            pair, row[OPEN_IDX], -0.05) or 0
        max_stake_amount = self.wallets.get_available_stake_amount()

        stake_amount = strategy_safe_wrapper(
            self.strategy.custom_stake_amount, default_retval=stake_amount)(
                pair=pair,
                current_time=row[DATE_IDX].to_pydatetime(),
                current_rate=row[OPEN_IDX],
                proposed_stake=stake_amount,
                min_stake=min_stake_amount,
                max_stake=max_stake_amount)
        stake_amount = self.wallets._validate_stake_amount(
            pair, stake_amount, min_stake_amount)

        if not stake_amount:
            return None

        order_type = self.strategy.order_types['buy']
        time_in_force = self.strategy.order_time_in_force['sell']
        # Confirm trade entry:
        if not strategy_safe_wrapper(
                self.strategy.confirm_trade_entry, default_retval=True)(
                    pair=pair,
                    order_type=order_type,
                    amount=stake_amount,
                    rate=row[OPEN_IDX],
                    time_in_force=time_in_force,
                    current_time=row[DATE_IDX].to_pydatetime()):
            return None

        if stake_amount and (not min_stake_amount
                             or stake_amount > min_stake_amount):
            # Enter trade
            has_buy_tag = len(row) >= BUY_TAG_IDX + 1
            trade = LocalTrade(
                pair=pair,
                open_rate=row[OPEN_IDX],
                open_date=row[DATE_IDX].to_pydatetime(),
                stake_amount=stake_amount,
                amount=round(stake_amount / row[OPEN_IDX], 8),
                fee_open=self.fee,
                fee_close=self.fee,
                is_open=True,
                buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
                exchange='backtesting',
            )
            return trade
        return None
コード例 #7
0
    def handle_left_open(self, open_trades: Dict[str, List[LocalTrade]],
                         data: Dict[str, List[Tuple]]) -> List[LocalTrade]:
        """
        Handling of left open trades at the end of backtesting
        """
        trades = []
        for pair in open_trades.keys():
            if len(open_trades[pair]) > 0:
                for trade in open_trades[pair]:
                    sell_row = data[pair][-1]

                    trade.close_date = sell_row[DATE_IDX].to_pydatetime()
                    trade.sell_reason = SellType.FORCE_SELL.value
                    trade.close(sell_row[OPEN_IDX], show_msg=False)
                    LocalTrade.close_bt_trade(trade)
                    # Deepcopy object to have wallets update correctly
                    trade1 = deepcopy(trade)
                    trade1.is_open = True
                    trades.append(trade1)
        return trades
コード例 #8
0
ファイル: backtesting.py プロジェクト: tomgross/freqtrade
    def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
                                           ) -> LocalTrade:

        current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
        min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1)
        max_stake = self.wallets.get_available_stake_amount()
        stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
                                             default_retval=None)(
            trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
            current_profit=current_profit, min_stake=min_stake, max_stake=max_stake)

        # Check if we should increase our position
        if stake_amount is not None and stake_amount > 0.0:
            pos_trade = self._enter_trade(trade.pair, row, stake_amount, trade)
            if pos_trade is not None:
                self.wallets.update()
                return pos_trade

        return trade
コード例 #9
0
    def backtest(self,
                 processed: Dict,
                 start_date: datetime,
                 end_date: datetime,
                 max_open_trades: int = 0,
                 position_stacking: bool = False,
                 enable_protections: bool = False) -> Dict[str, Any]:
        """
        Implement backtesting functionality

        NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
        Of course try to not have ugly code. By some accessor are sometime slower than functions.
        Avoid extensive logging in this method and functions it calls.

        :param processed: a processed dictionary with format {pair, data}, which gets cleared to
        optimize memory usage!
        :param start_date: backtesting timerange start datetime
        :param end_date: backtesting timerange end datetime
        :param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
        :param position_stacking: do we allow position stacking?
        :param enable_protections: Should protections be enabled?
        :return: DataFrame with trades (results of backtesting)
        """
        trades: List[LocalTrade] = []
        self.prepare_backtest(enable_protections)

        # Use dict of lists with data for performance
        # (looping lists is a lot faster than pandas DataFrames)
        data: Dict = self._get_ohlcv_as_lists(processed)

        # Indexes per pair, so some pairs are allowed to have a missing start.
        indexes: Dict = defaultdict(int)
        tmp = start_date + timedelta(minutes=self.timeframe_min)

        open_trades: Dict[str, List[LocalTrade]] = defaultdict(list)
        open_trade_count = 0

        self.progress.init_step(
            BacktestState.BACKTEST,
            int((end_date - start_date) /
                timedelta(minutes=self.timeframe_min)))

        # Loop timerange and get candle for each pair at that point in time
        while tmp <= end_date:
            open_trade_count_start = open_trade_count
            self.check_abort()
            for i, pair in enumerate(data):
                row_index = indexes[pair]
                try:
                    # Row is treated as "current incomplete candle".
                    # Buy / sell signals are shifted by 1 to compensate for this.
                    row = data[pair][row_index]
                except IndexError:
                    # missing Data for one pair at the end.
                    # Warnings for this are shown during data loading
                    continue

                # Waits until the time-counter reaches the start of the data for this pair.
                if row[DATE_IDX] > tmp:
                    continue

                row_index += 1
                indexes[pair] = row_index
                self.dataprovider._set_dataframe_max_index(row_index)

                # without positionstacking, we can only have one open trade per pair.
                # max_open_trades must be respected
                # don't open on the last row
                if ((position_stacking or len(open_trades[pair]) == 0)
                        and self.trade_slot_available(max_open_trades,
                                                      open_trade_count_start)
                        and tmp != end_date and row[BUY_IDX] == 1
                        and row[SELL_IDX] != 1
                        and not PairLocks.is_pair_locked(pair, row[DATE_IDX])):
                    trade = self._enter_trade(pair, row)
                    if trade:
                        # TODO: hacky workaround to avoid opening > max_open_trades
                        # This emulates previous behaviour - not sure if this is correct
                        # Prevents buying if the trade-slot was freed in this candle
                        open_trade_count_start += 1
                        open_trade_count += 1
                        # logger.debug(f"{pair} - Emulate creation of new trade: {trade}.")
                        open_trades[pair].append(trade)
                        LocalTrade.add_bt_trade(trade)

                for trade in list(open_trades[pair]):
                    # also check the buying candle for sell conditions.
                    trade_entry = self._get_sell_trade_entry(trade, row)
                    # Sell occurred
                    if trade_entry:
                        # logger.debug(f"{pair} - Backtesting sell {trade}")
                        open_trade_count -= 1
                        open_trades[pair].remove(trade)

                        LocalTrade.close_bt_trade(trade)
                        trades.append(trade_entry)
                        if enable_protections:
                            self.protections.stop_per_pair(pair, row[DATE_IDX])
                            self.protections.global_stop(tmp)

            # Move time one configured time_interval ahead.
            self.progress.increment()
            tmp += timedelta(minutes=self.timeframe_min)

        trades += self.handle_left_open(open_trades, data=data)
        self.wallets.update()

        results = trade_list_to_dataframe(trades)
        return {
            'results':
            results,
            'config':
            self.strategy.config,
            'locks':
            PairLocks.get_all_locks(),
            'rejected_signals':
            self.rejected_trades,
            'final_balance':
            self.wallets.get_total(self.strategy.config['stake_currency']),
        }
コード例 #10
0
    def _enter_trade(
            self,
            pair: str,
            row: Tuple,
            stake_amount: Optional[float] = None,
            trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]:

        # let's call the custom entry price, using the open price as default price
        propose_rate = strategy_safe_wrapper(
            self.strategy.custom_entry_price, default_retval=row[OPEN_IDX])(
                pair=pair,
                current_time=row[DATE_IDX].to_pydatetime(),
                proposed_rate=row[OPEN_IDX])  # default value is the open rate

        # Move rate to within the candle's low/high rate
        propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])

        min_stake_amount = self.exchange.get_min_pair_stake_amount(
            pair, propose_rate, -0.05) or 0
        max_stake_amount = self.wallets.get_available_stake_amount()

        pos_adjust = trade is not None
        if not pos_adjust:
            try:
                stake_amount = self.wallets.get_trade_stake_amount(pair, None)
            except DependencyException:
                return trade

            stake_amount = strategy_safe_wrapper(
                self.strategy.custom_stake_amount,
                default_retval=stake_amount)(
                    pair=pair,
                    current_time=row[DATE_IDX].to_pydatetime(),
                    current_rate=propose_rate,
                    proposed_stake=stake_amount,
                    min_stake=min_stake_amount,
                    max_stake=max_stake_amount)

        stake_amount = self.wallets.validate_stake_amount(
            pair, stake_amount, min_stake_amount)

        if not stake_amount:
            # In case of pos adjust, still return the original trade
            # If not pos adjust, trade is None
            return trade

        order_type = self.strategy.order_types['buy']
        time_in_force = self.strategy.order_time_in_force['sell']
        # Confirm trade entry:
        if not pos_adjust:
            if not strategy_safe_wrapper(
                    self.strategy.confirm_trade_entry, default_retval=True)(
                        pair=pair,
                        order_type=order_type,
                        amount=stake_amount,
                        rate=propose_rate,
                        time_in_force=time_in_force,
                        current_time=row[DATE_IDX].to_pydatetime()):
                return None

        if stake_amount and (not min_stake_amount
                             or stake_amount > min_stake_amount):
            amount = round(stake_amount / propose_rate, 8)
            if trade is None:
                # Enter trade
                has_buy_tag = len(row) >= BUY_TAG_IDX + 1
                trade = LocalTrade(
                    pair=pair,
                    open_rate=propose_rate,
                    open_date=row[DATE_IDX].to_pydatetime(),
                    stake_amount=stake_amount,
                    amount=amount,
                    fee_open=self.fee,
                    fee_close=self.fee,
                    is_open=True,
                    buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
                    exchange='backtesting',
                    orders=[])

            order = Order(ft_is_open=False,
                          ft_pair=trade.pair,
                          symbol=trade.pair,
                          ft_order_side="buy",
                          side="buy",
                          order_type="market",
                          status="closed",
                          price=propose_rate,
                          average=propose_rate,
                          amount=amount,
                          filled=amount,
                          cost=stake_amount + trade.fee_open)
            trade.orders.append(order)
            if pos_adjust:
                trade.recalc_trade_from_orders()

        return trade
コード例 #11
0
    def _get_sell_trade_entry_for_candle(
            self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:

        # Check if we need to adjust our current positions
        if self.strategy.position_adjustment_enable:
            trade = self._get_adjust_trade_entry_for_candle(trade, sell_row)

        sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
        sell = self.strategy.should_sell(
            trade,
            sell_row[OPEN_IDX],  # type: ignore
            sell_candle_time,
            sell_row[BUY_IDX],
            sell_row[SELL_IDX],
            low=sell_row[LOW_IDX],
            high=sell_row[HIGH_IDX])

        if sell.sell_flag:
            trade.close_date = sell_candle_time

            trade_dur = int(
                (trade.close_date_utc - trade.open_date_utc).total_seconds() //
                60)
            closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
            # call the custom exit price,with default value as previous closerate
            current_profit = trade.calc_profit_ratio(closerate)
            if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
                # Custom exit pricing only for sell-signals
                closerate = strategy_safe_wrapper(
                    self.strategy.custom_exit_price,
                    default_retval=closerate)(pair=trade.pair,
                                              trade=trade,
                                              current_time=sell_row[DATE_IDX],
                                              proposed_rate=closerate,
                                              current_profit=current_profit)
            # Use the maximum between close_rate and low as we cannot sell outside of a candle.
            closerate = min(max(closerate, sell_row[LOW_IDX]),
                            sell_row[HIGH_IDX])

            # Confirm trade exit:
            time_in_force = self.strategy.order_time_in_force['sell']
            if not strategy_safe_wrapper(self.strategy.confirm_trade_exit,
                                         default_retval=True)(
                                             pair=trade.pair,
                                             trade=trade,
                                             order_type='limit',
                                             amount=trade.amount,
                                             rate=closerate,
                                             time_in_force=time_in_force,
                                             sell_reason=sell.sell_reason,
                                             current_time=sell_candle_time):
                return None

            trade.sell_reason = sell.sell_reason

            # Checks and adds an exit tag, after checking that the length of the
            # sell_row has the length for an exit tag column
            if (len(sell_row) > EXIT_TAG_IDX
                    and sell_row[EXIT_TAG_IDX] is not None
                    and len(sell_row[EXIT_TAG_IDX]) > 0):
                trade.sell_reason = sell_row[EXIT_TAG_IDX]

            trade.close(closerate, show_msg=False)
            return trade

        return None
コード例 #12
0
    def backtest(self,
                 processed: Dict,
                 start_date: datetime,
                 end_date: datetime,
                 max_open_trades: int = 0,
                 position_stacking: bool = False,
                 enable_protections: bool = False) -> Dict[str, Any]:
        """
        Implement backtesting functionality

        NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
        Of course try to not have ugly code. By some accessor are sometime slower than functions.
        Avoid extensive logging in this method and functions it calls.

        :param processed: a processed dictionary with format {pair, data}, which gets cleared to
        optimize memory usage!
        :param start_date: backtesting timerange start datetime
        :param end_date: backtesting timerange end datetime
        :param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
        :param position_stacking: do we allow position stacking?
        :param enable_protections: Should protections be enabled?
        :return: DataFrame with trades (results of backtesting)
        """
        trades: List[LocalTrade] = []
        self.prepare_backtest(enable_protections)
        # Ensure wallets are uptodate (important for --strategy-list)
        self.wallets.update()
        # Use dict of lists with data for performance
        # (looping lists is a lot faster than pandas DataFrames)
        data: Dict = self._get_ohlcv_as_lists(processed)

        # Indexes per pair, so some pairs are allowed to have a missing start.
        indexes: Dict = defaultdict(int)
        current_time = start_date + timedelta(minutes=self.timeframe_min)

        open_trades: Dict[str, List[LocalTrade]] = defaultdict(list)
        open_trade_count = 0

        self.progress.init_step(
            BacktestState.BACKTEST,
            int((end_date - start_date) /
                timedelta(minutes=self.timeframe_min)))

        # Loop timerange and get candle for each pair at that point in time
        while current_time <= end_date:
            open_trade_count_start = open_trade_count
            self.check_abort()
            for i, pair in enumerate(data):
                row_index = indexes[pair]
                row = self.validate_row(data, pair, row_index, current_time)
                if not row:
                    continue

                row_index += 1
                indexes[pair] = row_index
                self.dataprovider._set_dataframe_max_index(row_index)

                # 1. Process buys.
                # without positionstacking, we can only have one open trade per pair.
                # max_open_trades must be respected
                # don't open on the last row
                if ((position_stacking or len(open_trades[pair]) == 0)
                        and self.trade_slot_available(max_open_trades,
                                                      open_trade_count_start)
                        and current_time != end_date and row[BUY_IDX] == 1
                        and row[SELL_IDX] != 1
                        and not PairLocks.is_pair_locked(pair, row[DATE_IDX])):
                    trade = self._enter_trade(pair, row)
                    if trade:
                        # TODO: hacky workaround to avoid opening > max_open_trades
                        # This emulates previous behavior - not sure if this is correct
                        # Prevents buying if the trade-slot was freed in this candle
                        open_trade_count_start += 1
                        open_trade_count += 1
                        # logger.debug(f"{pair} - Emulate creation of new trade: {trade}.")
                        open_trades[pair].append(trade)

                for trade in list(open_trades[pair]):
                    # 2. Process buy orders.
                    order = trade.select_order('buy', is_open=True)
                    if order and self._get_order_filled(order.price, row):
                        order.close_bt_order(current_time)
                        trade.open_order_id = None
                        LocalTrade.add_bt_trade(trade)
                        self.wallets.update()

                    # 3. Create sell orders (if any)
                    if not trade.open_order_id:
                        self._get_sell_trade_entry(
                            trade, row)  # Place sell order if necessary

                    # 4. Process sell orders.
                    order = trade.select_order('sell', is_open=True)
                    if order and self._get_order_filled(order.price, row):
                        trade.open_order_id = None
                        trade.close_date = current_time
                        trade.close(order.price, show_msg=False)

                        # logger.debug(f"{pair} - Backtesting sell {trade}")
                        open_trade_count -= 1
                        open_trades[pair].remove(trade)
                        LocalTrade.close_bt_trade(trade)
                        trades.append(trade)
                        self.wallets.update()
                        self.run_protections(enable_protections, pair,
                                             current_time)

                    # 5. Cancel expired buy/sell orders.
                    if self.check_order_cancel(trade, current_time):
                        # Close trade due to buy timeout expiration.
                        open_trade_count -= 1
                        open_trades[pair].remove(trade)
                        self.wallets.update()

            # Move time one configured time_interval ahead.
            self.progress.increment()
            current_time += timedelta(minutes=self.timeframe_min)

        trades += self.handle_left_open(open_trades, data=data)
        self.wallets.update()

        results = trade_list_to_dataframe(trades)
        return {
            'results':
            results,
            'config':
            self.strategy.config,
            'locks':
            PairLocks.get_all_locks(),
            'rejected_signals':
            self.rejected_trades,
            'timedout_entry_orders':
            self.timedout_entry_orders,
            'timedout_exit_orders':
            self.timedout_exit_orders,
            'final_balance':
            self.wallets.get_total(self.strategy.config['stake_currency']),
        }
コード例 #13
0
    def _enter_trade(
            self,
            pair: str,
            row: Tuple,
            stake_amount: Optional[float] = None,
            trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]:

        current_time = row[DATE_IDX].to_pydatetime()
        entry_tag = row[BUY_TAG_IDX] if len(row) >= BUY_TAG_IDX + 1 else None
        # let's call the custom entry price, using the open price as default price
        order_type = self.strategy.order_types['buy']
        propose_rate = row[OPEN_IDX]
        if order_type == 'limit':
            propose_rate = strategy_safe_wrapper(
                self.strategy.custom_entry_price,
                default_retval=row[OPEN_IDX])(
                    pair=pair,
                    current_time=current_time,
                    proposed_rate=propose_rate,
                    entry_tag=entry_tag)  # default value is the open rate
            # We can't place orders higher than current high (otherwise it'd be a stop limit buy)
            # which freqtrade does not support in live.
            propose_rate = min(propose_rate, row[HIGH_IDX])

        min_stake_amount = self.exchange.get_min_pair_stake_amount(
            pair, propose_rate, -0.05) or 0
        max_stake_amount = self.wallets.get_available_stake_amount()

        pos_adjust = trade is not None
        if not pos_adjust:
            try:
                stake_amount = self.wallets.get_trade_stake_amount(
                    pair, None, update=False)
            except DependencyException:
                return None

            stake_amount = strategy_safe_wrapper(
                self.strategy.custom_stake_amount,
                default_retval=stake_amount)(pair=pair,
                                             current_time=current_time,
                                             current_rate=propose_rate,
                                             proposed_stake=stake_amount,
                                             min_stake=min_stake_amount,
                                             max_stake=max_stake_amount,
                                             entry_tag=entry_tag)

        stake_amount = self.wallets.validate_stake_amount(
            pair, stake_amount, min_stake_amount)

        if not stake_amount:
            # In case of pos adjust, still return the original trade
            # If not pos adjust, trade is None
            return trade

        time_in_force = self.strategy.order_time_in_force['buy']
        # Confirm trade entry:
        if not pos_adjust:
            if not strategy_safe_wrapper(self.strategy.confirm_trade_entry,
                                         default_retval=True)(
                                             pair=pair,
                                             order_type=order_type,
                                             amount=stake_amount,
                                             rate=propose_rate,
                                             time_in_force=time_in_force,
                                             current_time=current_time,
                                             entry_tag=entry_tag):
                return None

        if stake_amount and (not min_stake_amount
                             or stake_amount > min_stake_amount):
            self.order_id_counter += 1
            amount = round(stake_amount / propose_rate, 8)
            if trade is None:
                # Enter trade
                self.trade_id_counter += 1
                trade = LocalTrade(id=self.trade_id_counter,
                                   open_order_id=self.order_id_counter,
                                   pair=pair,
                                   open_rate=propose_rate,
                                   open_rate_requested=propose_rate,
                                   open_date=current_time,
                                   stake_amount=stake_amount,
                                   amount=amount,
                                   amount_requested=amount,
                                   fee_open=self.fee,
                                   fee_close=self.fee,
                                   is_open=True,
                                   buy_tag=entry_tag,
                                   exchange='backtesting',
                                   orders=[])

            trade.adjust_stop_loss(trade.open_rate,
                                   self.strategy.stoploss,
                                   initial=True)

            order = Order(
                id=self.order_id_counter,
                ft_trade_id=trade.id,
                ft_is_open=True,
                ft_pair=trade.pair,
                order_id=str(self.order_id_counter),
                symbol=trade.pair,
                ft_order_side="buy",
                side="buy",
                order_type=order_type,
                status="open",
                order_date=current_time,
                order_filled_date=current_time,
                order_update_date=current_time,
                price=propose_rate,
                average=propose_rate,
                amount=amount,
                filled=0,
                remaining=amount,
                cost=stake_amount + trade.fee_open,
            )
            if pos_adjust and self._get_order_filled(order.price, row):
                order.close_bt_order(current_time)
            else:
                trade.open_order_id = str(self.order_id_counter)
            trade.orders.append(order)
            trade.recalc_trade_from_orders()

        return trade
コード例 #14
0
    def _get_sell_trade_entry_for_candle(
            self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:

        # Check if we need to adjust our current positions
        if self.strategy.position_adjustment_enable:
            check_adjust_buy = True
            if self.strategy.max_entry_position_adjustment > -1:
                count_of_buys = trade.nr_of_successful_buys
                check_adjust_buy = (
                    count_of_buys <=
                    self.strategy.max_entry_position_adjustment)
            if check_adjust_buy:
                trade = self._get_adjust_trade_entry_for_candle(
                    trade, sell_row)

        sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
        sell = self.strategy.should_sell(
            trade,
            sell_row[OPEN_IDX],  # type: ignore
            sell_candle_time,
            sell_row[BUY_IDX],
            sell_row[SELL_IDX],
            low=sell_row[LOW_IDX],
            high=sell_row[HIGH_IDX])

        if sell.sell_flag:
            trade.close_date = sell_candle_time

            trade_dur = int(
                (trade.close_date_utc - trade.open_date_utc).total_seconds() //
                60)
            try:
                closerate = self._get_close_rate(sell_row, trade, sell,
                                                 trade_dur)
            except ValueError:
                return None
            # call the custom exit price,with default value as previous closerate
            current_profit = trade.calc_profit_ratio(closerate)
            order_type = self.strategy.order_types['sell']
            if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
                # Custom exit pricing only for sell-signals
                if order_type == 'limit':
                    closerate = strategy_safe_wrapper(
                        self.strategy.custom_exit_price,
                        default_retval=closerate)(
                            pair=trade.pair,
                            trade=trade,
                            current_time=sell_candle_time,
                            proposed_rate=closerate,
                            current_profit=current_profit)
                    # We can't place orders lower than current low.
                    # freqtrade does not support this in live, and the order would fill immediately
                    closerate = max(closerate, sell_row[LOW_IDX])
            # Confirm trade exit:
            time_in_force = self.strategy.order_time_in_force['sell']

            if not strategy_safe_wrapper(self.strategy.confirm_trade_exit,
                                         default_retval=True)(
                                             pair=trade.pair,
                                             trade=trade,
                                             order_type='limit',
                                             amount=trade.amount,
                                             rate=closerate,
                                             time_in_force=time_in_force,
                                             sell_reason=sell.sell_reason,
                                             current_time=sell_candle_time):
                return None

            trade.sell_reason = sell.sell_reason

            # Checks and adds an exit tag, after checking that the length of the
            # sell_row has the length for an exit tag column
            if (len(sell_row) > EXIT_TAG_IDX
                    and sell_row[EXIT_TAG_IDX] is not None
                    and len(sell_row[EXIT_TAG_IDX]) > 0):
                trade.sell_reason = sell_row[EXIT_TAG_IDX]

            self.order_id_counter += 1
            order = Order(
                id=self.order_id_counter,
                ft_trade_id=trade.id,
                order_date=sell_candle_time,
                order_update_date=sell_candle_time,
                ft_is_open=True,
                ft_pair=trade.pair,
                order_id=str(self.order_id_counter),
                symbol=trade.pair,
                ft_order_side="sell",
                side="sell",
                order_type=order_type,
                status="open",
                price=closerate,
                average=closerate,
                amount=trade.amount,
                filled=0,
                remaining=trade.amount,
                cost=trade.amount * closerate,
            )
            trade.orders.append(order)
            return trade

        return None
コード例 #15
0
    def _enter_trade(self, pair: str, row: List) -> Optional[LocalTrade]:
        try:
            stake_amount = self.wallets.get_trade_stake_amount(pair, None)
        except DependencyException:
            return None
        # let's call the custom entry price, using the open price as default price
        propose_rate = strategy_safe_wrapper(
            self.strategy.custom_entry_price, default_retval=row[OPEN_IDX])(
                pair=pair,
                current_time=row[DATE_IDX].to_pydatetime(),
                proposed_rate=row[OPEN_IDX])  # default value is the open rate

        # Move rate to within the candle's low/high rate
        propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])

        min_stake_amount = self.exchange.get_min_pair_stake_amount(
            pair, propose_rate, -0.05) or 0
        max_stake_amount = self.wallets.get_available_stake_amount()

        stake_amount = strategy_safe_wrapper(
            self.strategy.custom_stake_amount, default_retval=stake_amount)(
                pair=pair,
                current_time=row[DATE_IDX].to_pydatetime(),
                current_rate=propose_rate,
                proposed_stake=stake_amount,
                min_stake=min_stake_amount,
                max_stake=max_stake_amount)
        stake_amount = self.wallets.validate_stake_amount(
            pair, stake_amount, min_stake_amount)

        if not stake_amount:
            return None

        order_type = self.strategy.order_types['buy']
        time_in_force = self.strategy.order_time_in_force['sell']
        # Confirm trade entry:
        if not strategy_safe_wrapper(
                self.strategy.confirm_trade_entry, default_retval=True)(
                    pair=pair,
                    order_type=order_type,
                    amount=stake_amount,
                    rate=propose_rate,
                    time_in_force=time_in_force,
                    current_time=row[DATE_IDX].to_pydatetime()):
            return None

        if stake_amount and (not min_stake_amount
                             or stake_amount > min_stake_amount):
            # Enter trade
            has_buy_tag = len(row) >= BUY_TAG_IDX + 1
            trade = LocalTrade(
                pair=pair,
                open_rate=propose_rate,
                open_date=row[DATE_IDX].to_pydatetime(),
                stake_amount=stake_amount,
                amount=round(stake_amount / propose_rate, 8),
                fee_open=self.fee,
                fee_close=self.fee,
                is_open=True,
                buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
                exchange='backtesting',
            )
            return trade
        return None