Example #1
0
    def __init__(self, settings: Settings, pdf_exporter: PDFExporter, trades_df: QFDataFrame, start_date: datetime,
                 end_date: datetime, nr_of_assets_traded: int = 1, title: str = "Trades"):
        """
        trades_df
            indexed by consecutive numbers starting at 0.
            columns are indexed using TradeField values
        nr_of_assets_traded
            the model can be used to trade on many instruments at the same time.
            All aggregated trades will be in trades_df
            nr_of_instruments_traded informs on how many instruments at the same time the model was traded.
        title
            title of the document, will be a part of the filename. Do not use special characters
        """
        self.trades_df = trades_df.sort_values([TradeField.EndDate, TradeField.StartDate]).reset_index(drop=True)
        self.start_date = start_date
        self.end_date = end_date
        self.nr_of_assets_traded = nr_of_assets_traded
        self.returns_of_trades = SimpleReturnsSeries(self.trades_df[TradeField.Return])
        self.returns_of_trades.name = "Returns of Trades"
        self.title = title

        self.document = Document(title)

        # position is linked to the position of axis in tearsheet.mplstyle
        self.half_image_size = (4, 2.2)
        self.dpi = 400

        self.settings = settings
        self.pdf_exporter = pdf_exporter
    def _performance_series_for_ticker(self, df: QFDataFrame):
        df = df.sort_values(by="Time")
        time_index = pd.DatetimeIndex(df[df["Total PnL of open position"].notnull()]["Time"].drop_duplicates())

        plot_lines = [
            ("Long positions", [1]),
            ("Short positions", [-1]),
            ("Overall performance", [0, 1, -1])
        ]

        return_data = {}
        for title, directions in plot_lines:
            open_positions_pnl = df[["Total PnL of open position", "Time", "Direction"]].dropna()
            open_positions_pnl.loc[~open_positions_pnl["Direction"].isin(directions), 'Total PnL of open position'] = 0
            open_positions_pnl = open_positions_pnl.groupby("Time").agg({'Total PnL of open position': 'sum'})

            closed_positions_pnl = df[["Realised PnL", "Time", "Direction"]]
            closed_positions_pnl.loc[~closed_positions_pnl["Direction"].isin(directions), 'Realised PnL'] = 0
            closed_positions_pnl = closed_positions_pnl.groupby("Time").agg({'Realised PnL': 'sum'})
            closed_positions_pnl["Realised PnL"] = closed_positions_pnl["Realised PnL"].cumsum()

            pnl_series = QFSeries(
                data=closed_positions_pnl["Realised PnL"] + open_positions_pnl['Total PnL of open position'],
                index=time_index).fillna(method='ffill').fillna(0.0)
            return_data[title] = pnl_series

        return return_data
    def _create_performance_contribution_tables(
            self, performance_df: QFDataFrame) -> List[DFTable]:
        """
        Create a list of DFTables with assets names in the index and different years / months in columns, which contains
        details on the performance contribution for each asset.
        """
        # Create a QFSeries which contains the initial amount of cash in the portfolio for each year / month
        numeric_columns = [
            col for col in performance_df.columns
            if is_numeric_dtype(performance_df[col])
        ]
        portfolio_values = performance_df[numeric_columns].sum().shift(
            fill_value=self._initial_cash).cumsum()
        performance_df[numeric_columns] = performance_df[
            numeric_columns] / portfolio_values[numeric_columns]

        # Add category column and aggregate data accordingly
        ticker_name_to_category = {
            t.name: category
            for t, category in self._ticker_to_category.items()
        }
        performance_df["Category"] = performance_df["Asset"].apply(
            lambda t: ticker_name_to_category[t])
        all_categories = list(set(ticker_name_to_category.values()))
        performance_df = performance_df.sort_values(by=["Category", "Asset"])
        performance_df = performance_df.groupby("Category").apply(
            lambda d: pd.concat([
                PricesDataFrame({
                    **{
                        "Asset": [d.name],
                        "Category": [d.name]
                    },
                    **{c: [d[c].sum()]
                       for c in numeric_columns}
                }), d
            ],
                                ignore_index=True)).drop(columns=["Category"])

        # Add the Total Performance row (divide by 2 as the df contains already aggregated data for each group)
        total_sum_row = performance_df[numeric_columns].sum() / 2
        total_sum_row["Asset"] = "Total Performance"
        performance_df = performance_df.append(total_sum_row,
                                               ignore_index=True)

        # Format the rows using the percentage formatter
        performance_df[numeric_columns] = performance_df[
            numeric_columns].applymap(lambda x: '{:.2%}'.format(x))

        # Divide the performance dataframe into a number of dataframes, so that each of them contains up to
        # self._max_columns_per_page columns
        split_dfs = np.array_split(performance_df.set_index("Asset"),
                                   np.ceil(
                                       (performance_df.num_of_columns - 1) /
                                       self._max_columns_per_page),
                                   axis=1)
        df_tables = [
            DFTable(df.reset_index(),
                    css_classes=[
                        'table', 'shrink-font', 'right-align',
                        'wide-first-column'
                    ]) for df in split_dfs
        ]

        # Get the indices of rows, which contain category info
        category_indices = performance_df[performance_df["Asset"].isin(
            all_categories)].index

        for df_table in df_tables:
            # Add table formatting, highlight rows showing the total contribution of the given category
            df_table.add_rows_styles(
                category_indices, {
                    "font-weight": "bold",
                    "font-size": "0.95em",
                    "background-color": "#cbd0d2"
                })
            df_table.add_rows_styles(
                [performance_df.index[-1]], {
                    "font-weight": "bold",
                    "font-size": "0.95em",
                    "background-color": "#b9bcbd"
                })
        return df_tables