Пример #1
0
    def _add_heat_maps(self, tickers: Sequence[Ticker]):
        parameters_list = sorted(
            self.backtest_evaluator.params_backtest_summary_elem_dict.keys())

        # Group plots by type, so that they appear in the given logical order
        title_to_grid = defaultdict(lambda: GridElement(
            mode=PlottingMode.PDF, figsize=self.image_size))
        for start_time, end_time in [
            (self.backtest_summary.start_date, self.out_of_sample_start_date),
            (self.out_of_sample_start_date, self.backtest_summary.end_date)
        ]:
            results = QFDataFrame()

            for param_tuple in parameters_list:
                trades_eval_result = self.backtest_evaluator.evaluate_params_for_tickers(
                    param_tuple, tickers, start_time, end_time)
                row, column = param_tuple
                results.loc[row, column] = trades_eval_result

            results.sort_index(axis=0, inplace=True, ascending=False)
            results.sort_index(axis=1, inplace=True)
            results.fillna(TradesEvaluationResult(), inplace=True)

            sqn_avg_nr_trades = results.applymap(
                lambda x: x.sqn_per_avg_nr_trades).fillna(0)
            avg_nr_of_trades = results.applymap(
                lambda x: x.avg_nr_of_trades_1Y).fillna(0)
            annualised_return = results.applymap(
                lambda x: x.annualised_return).fillna(0)

            adjusted_start_time = results.applymap(
                lambda x: x.start_date).min().min()
            adjusted_end_time = results.applymap(
                lambda x: x.end_date).max().max()
            if adjusted_start_time >= adjusted_end_time:
                adjusted_end_time = adjusted_start_time if adjusted_start_time <= self.backtest_summary.end_date \
                    else end_time
                adjusted_start_time = start_time
            title = "{} - {} ".format(adjusted_start_time.strftime("%Y-%m-%d"),
                                      adjusted_end_time.strftime("%Y-%m-%d"))

            title_to_grid["SQN (Arithmetic return) per year"].add_chart(
                self._create_single_heat_map(title, sqn_avg_nr_trades, 0, 0.5))

            title_to_grid["Avg # trades 1Y"].add_chart(
                self._create_single_heat_map(title, avg_nr_of_trades, 2, 15))

            if len(tickers) == 1:
                title_to_grid["Annualised return"].add_chart(
                    self._create_single_heat_map(title, annualised_return, 0.0,
                                                 0.3))

        tickers_used = "Many tickers" if len(tickers) > 1 else (
            tickers[0].name if isinstance(
                tickers[0], FutureTicker) else tickers[0].as_string())

        for description, grid in title_to_grid.items():
            self.document.add_element(
                HeadingElement(3, "{} - {}".format(description, tickers_used)))
            self.document.add_element(grid)
Пример #2
0
    def _add_avg_time_in_the_market_per_ticker(self):
        """
        Compute the total time in the market per ticker (separately for long and short positions) in minutes
        and divide it by the total duration of the backtest in minutes.
        """
        self.document.add_element(NewPageElement())
        self.document.add_element(HeadingElement(level=2, text="Average time in the market per asset"))

        start_time = self.backtest_result.start_date
        end_time = self.backtest_result.portfolio.timer.now()
        backtest_duration = pd.Timedelta(end_time - start_time) / pd.Timedelta(minutes=1)  # backtest duration in min

        closed_positions_time = [
            (self._ticker_name(position.contract()), position.start_time, position.end_time, position.direction())
            for position in self.backtest_result.portfolio.closed_positions()
        ]
        open_positions_time = [
            (self._ticker_name(c), position.start_time, end_time, position.direction())
            for c, position in self.backtest_result.portfolio.open_positions_dict.items()
        ]

        positions = QFDataFrame(data=closed_positions_time + open_positions_time,
                                columns=["Tickers name", "Start time", "End time", "Position direction"])

        def compute_duration(grouped_rows):
            return pd.DatetimeIndex([]).union_many(
                [pd.date_range(row["Start time"], row["End time"], freq='T', closed='left')
                 for _, row in grouped_rows.iterrows()]).size

        positions = positions.groupby(by=["Tickers name", "Position direction"]).apply(compute_duration) \
            .rename("Duration (minutes)").reset_index()
        positions["Duration"] = positions["Duration (minutes)"] / backtest_duration
        positions = positions.pivot_table(index="Tickers name", columns="Position direction",
                                          values="Duration").reset_index()
        positions = positions.rename(columns={-1: "Short", 1: "Long"})

        # Add default 0 column in case if only short / long positions occurred in the backtest
        for column in ["Short", "Long"]:
            if column not in positions.columns:
                positions[column] = 0.0

        positions["Out"] = 1.0 - positions["Long"] - positions["Short"]
        positions[["Long", "Short", "Out"]] = positions[["Long", "Short", "Out"]].applymap(lambda x: '{:.2%}'.format(x))
        positions = positions.fillna(0.0)

        table = DFTable(positions, css_classes=['table', 'left-align'])
        table.add_columns_classes(["Tickers name"], 'wide-column')
        self.document.add_element(table)