示例#1
0
    def _receive_reference_response(self, tickers, fields):
        response_events = get_response_events(self._session)

        tickers_fields_container = QFDataFrame(index=tickers, columns=fields)

        for ev in response_events:
            check_event_for_errors(ev)
            security_data_array = extract_security_data(ev)
            check_security_data_for_errors(security_data_array)

            for i in range(security_data_array.numValues()):
                security_data = security_data_array.getValueAsElement(i)
                check_security_data_for_errors(security_data)

                security_name = security_data.getElementAsString(SECURITY)
                ticker = BloombergTicker.from_string(security_name)
                field_data_array = security_data.getElement(FIELD_DATA)

                for field_name in fields:
                    try:
                        value = field_data_array.getElementAsFloat(field_name)
                    except blpapi.exception.InvalidConversionException:
                        value = field_data_array.getElementAsString(field_name)
                    except blpapi.exception.NotFoundException:
                        value = np.nan

                    tickers_fields_container.loc[ticker, field_name] = value

        return tickers_fields_container
示例#2
0
    def _get_exposure(self, regressors_tickers: List, regression_len: int):
        df = QFDataFrame()
        for portfolio_date, positions in self.positions_history.iterrows():
            positions = positions.dropna()
            positions_tickers = positions.index.tolist()
            exposure = QFSeries([x.total_exposure for x in positions])
            portfolio_net_liquidation = self.portfolio_nav_history.asof(
                portfolio_date)
            positions_allocation = exposure / portfolio_net_liquidation
            from_date = portfolio_date - RelativeDelta(months=regression_len)
            coefficients, current_regressors_tickers = self._get_coefficients_and_current_regressors_tickers(
                regressors_tickers, positions_tickers, positions_allocation,
                from_date, portfolio_date)

            df = df.append(
                QFDataFrame(
                    {
                        col: val
                        for val, col in zip(coefficients,
                                            current_regressors_tickers)
                    },
                    index=[portfolio_date]))

        # remove missing regressors
        df = df.dropna(axis=1, how='all')
        return df
示例#3
0
    def test_import_custom_dataframe_shifted(self):
        # This tests issue #79.
        template_file_path = self.template_file_path(SINGLE_SHEET_CUSTOM_INDEX_DATA_FRAME_SHIFTED)

        # With index and column names.
        df = QFDataFrame({"Test": [1, 2, 3, 4, 5], "Test2": [10, 20, 30, 40, 50]}, ["A", "B", "C", "D", "E"])
        imported_dataframe = self.xl_importer.import_container(file_path=template_file_path, container_type=QFDataFrame,
                                                               starting_cell='C10', ending_cell='E15',
                                                               include_index=True, include_column_names=True)

        assert_dataframes_equal(df, imported_dataframe)

        # With index and no column names.
        df = QFDataFrame({0: [1, 2, 3, 4, 5], 1: [10, 20, 30, 40, 50]}, ["A", "B", "C", "D", "E"])
        imported_dataframe = self.xl_importer.import_container(file_path=template_file_path, container_type=QFDataFrame,
                                                               starting_cell='C11', ending_cell='E15',
                                                               include_index=True, include_column_names=False)

        assert_dataframes_equal(df, imported_dataframe)

        # With column names and no index.
        df = QFDataFrame({"Test": [1, 2, 3, 4, 5], "Test2": [10, 20, 30, 40, 50]})
        imported_dataframe = self.xl_importer.import_container(file_path=template_file_path, container_type=QFDataFrame,
                                                               starting_cell='D10', ending_cell='E15',
                                                               include_index=False, include_column_names=True)

        assert_dataframes_equal(df, imported_dataframe)

        # With no column names and no index.
        df = QFDataFrame({0: [1, 2, 3, 4, 5], 1: [10, 20, 30, 40, 50]})
        imported_dataframe = self.xl_importer.import_container(file_path=template_file_path, container_type=QFDataFrame,
                                                               starting_cell='D11', ending_cell='E15',
                                                               include_index=False, include_column_names=False)

        assert_dataframes_equal(df, imported_dataframe)
def convert_dataframe_frequency(
        dataframe: QFDataFrame,
        frequency: Frequency) -> SimpleReturnsDataFrame:
    """
    Converts each column in the dataframe to the specified frequency.
    ValueError is raised when a column has a lower frequency than the one we are converting to.
    """
    # Verify that all columns in the dataframe have a lower frequency.
    data_frequencies = dataframe.get_frequency()
    for column, col_frequency in data_frequencies.items():
        if col_frequency < frequency:
            raise ValueError(
                "Column '{}' cannot be converted to '{}' frequency because its frequency is '{}'."
                .format(column, frequency, col_frequency))

    if frequency == Frequency.DAILY:
        return dataframe.to_simple_returns()

    filled_df = dataframe.to_prices().fillna(method="ffill")
    new_columns = {}
    for column in filled_df:
        new_columns[column] = get_aggregate_returns(filled_df[column],
                                                    frequency)

    return SimpleReturnsDataFrame(new_columns)
    def _receive_intraday_response(self, requested_ticker: BloombergTicker,
                                   requested_fields):
        """ The response for intraday bar is related to a single ticker. """
        response_events = get_response_events(self._session)
        tickers_data_dict = defaultdict(
            lambda: QFDataFrame(columns=requested_fields))

        for event in response_events:
            try:
                check_event_for_errors(event)
                bar_data = extract_bar_data(event)

                bar_tick_data_array = bar_data.getElement(BAR_TICK_DATA)
                dates = [
                    to_datetime(e.getElementAsDatetime("time"))
                    for e in bar_tick_data_array.values()
                ]
                dates_fields_values = QFDataFrame(np.nan,
                                                  index=dates,
                                                  columns=requested_fields)

                for field_name in requested_fields:
                    dates_fields_values.loc[:, field_name] = [
                        self._get_float_or_nan_intraday(
                            data_of_date_elem, field_name)
                        for data_of_date_elem in bar_tick_data_array.values()
                    ]

                df = tickers_data_dict[requested_ticker]
                tickers_data_dict[requested_ticker] = df.append(
                    dates_fields_values)
            except BloombergError as e:
                self.logger.error(e)

        return tickers_data_dict[requested_ticker]
示例#6
0
    def test_tickers_dict_to_data_array(self):
        ticker_1 = BloombergTicker("Example 1")
        ticker_2 = BloombergTicker("Example 2")
        fields = [PriceField.Open, PriceField.Close]
        index = self.index[:3]
        data = [[[4., 1.], [nan, 5.]], [[5., 2.], [nan, 7.]],
                [[6., 3.], [nan, 8.]]]

        prices_df_1 = QFDataFrame(data={
            PriceField.Close: [1., 2., 3.],
            PriceField.Open: [4., 5., 6.]
        },
                                  index=index)
        prices_df_2 = QFDataFrame(data={PriceField.Close: [5., 7., 8.]},
                                  index=index)

        data_array = tickers_dict_to_data_array(
            {
                ticker_1: prices_df_1,
                ticker_2: prices_df_2
            }, [ticker_1, ticker_2], fields)

        self.assertEqual(dtype("float64"), data_array.dtype)

        expected_data_array = QFDataArray.create(index, [ticker_1, ticker_2],
                                                 fields, data)
        assert_equal(data_array, expected_data_array)
示例#7
0
    def _add_up_and_down_trend_strength(self, prices_df: QFDataFrame):
        def _down_trend_fun(df):
            return down_trend_strength(df, self.use_next_open_instead_of_close)

        def _up_trend_fun(df):
            return up_trend_strength(df, self.use_next_open_instead_of_close)

        up_trend_strength_tms = prices_df.rolling_time_window(window_length=self.window_len, step=1,
                                                              func=_down_trend_fun)
        down_trend_strength_tms = prices_df.rolling_time_window(window_length=self.window_len, step=1,
                                                                func=_up_trend_fun)
        chart = LineChart()
        up_trend_elem = DataElementDecorator(up_trend_strength_tms)
        down_trend_elem = DataElementDecorator(down_trend_strength_tms)
        chart.add_decorator(up_trend_elem)
        chart.add_decorator(down_trend_elem)
        legend = LegendDecorator(legend_placement=Location.BEST, key='legend')
        legend.add_entry(up_trend_elem, 'Up trend strength')
        legend.add_entry(down_trend_elem, 'Down trend strength')
        chart.add_decorator(legend)
        title = "Strength of the up and down trend - rolling {} days".format(self.window_len)
        title_decorator = TitleDecorator(title, key="title")
        chart.add_decorator(title_decorator)
        self._add_axes_position_decorator(chart)
        self.document.add_element(ChartElement(chart, figsize=self.image_size, dpi=self.dpi))
示例#8
0
    def __init__(self,
                 data=None,
                 index=None,
                 columns=None,
                 dtype=None,
                 copy=False):

        self.logger = qf_logger.getChild(self.__class__.__name__)

        # Data Frame containing the table data
        self.data = QFDataFrame(data, index, columns, dtype, copy)

        # Dictionary containing a mapping from column names onto ColumnStyles
        self._columns_styles = {
            column_name: self.ColumnStyle(column_name)
            for column_name in self.data.columns.tolist()
        }

        # Series containing the styles for each of the rows
        self._rows_styles = QFSeries(
            data=[self.RowStyle(loc) for loc in self.data.index],
            index=self.data.index)

        # Data Frame containing styles for all cells in the table, based upon columns_styles and rows_styles
        self._styles = QFDataFrame(data={
            column_name: [
                self.CellStyle(row_style, column_style)
                for row_style in self.rows_styles
            ]
            for column_name, column_style in self.columns_styles.items()
        },
                                   index=self.data.index,
                                   columns=self.data.columns)

        self.table_styles = self.Style()
示例#9
0
    def _get_expiration_dates(self, dir_path: str, future_tickers: Sequence[FutureTicker]):
        tickers_dates_dict = {}

        for future_ticker in future_tickers:
            for path in list(Path(dir_path).glob('**/{}.txt'.format(future_ticker.family_id.replace("{}", "")))):
                try:
                    df = pd.read_csv(path, names=['Contract', 'Expiration Date'], parse_dates=['Expiration Date'],
                                     date_parser=lambda date: datetime.strptime(date, '%Y%m%d'), index_col="Contract")
                    df = df.rename(columns={'Expiration Date': ExpirationDateField.LastTradeableDate})
                    df.index = PortaraTicker.from_string(df.index, security_type=future_ticker.security_type,
                                                         point_value=future_ticker.point_value)

                    if all(future_ticker.belongs_to_family(x) for x in df.index):
                        tickers_dates_dict[future_ticker] = QFDataFrame(df)
                    else:
                        self.logger.info(f"Not all tickers belong to family {future_ticker}")

                except Exception:
                    self.logger.debug(f"File {path} does not contain valid expiration dates and therefore will be "
                                      f"excluded.")

        # Log all the future tickers, which could not have been mapped correctly
        tickers_without_matching_files = set(future_tickers).difference(tickers_dates_dict.keys())
        for ticker in tickers_without_matching_files:
            tickers_dates_dict[ticker] = QFDataFrame(columns=[ExpirationDateField.LastTradeableDate])
            self.logger.warning(f"No expiration dates were found for ticker {ticker}. Check if file "
                                f"{ticker.family_id.replace('{}', '')}.txt exists in the {dir_path} and if it contains"
                                f"valid expiration dates for the ticker.")

        return tickers_dates_dict
示例#10
0
    def test_compute_container_hash__data_array(self):
        ticker_1 = BloombergTicker("Example 1")
        ticker_2 = BloombergTicker("Example 2")

        prices_df_1 = QFDataFrame(data={
            PriceField.Close: [1, 2, 3],
            PriceField.Open: [4, 5, 6]
        })
        prices_df_2 = QFDataFrame(data={PriceField.Close: [5, 7, 8]})

        data_array_1 = tickers_dict_to_data_array(
            {
                ticker_1: prices_df_1,
                ticker_2: prices_df_2
            }, [ticker_1, ticker_2], [PriceField.Open, PriceField.Close])

        data_array_2 = tickers_dict_to_data_array(
            {
                ticker_1: prices_df_1,
                ticker_2: prices_df_2,
            }, [ticker_1, ticker_2], [PriceField.Open, PriceField.Close])

        data_array_3 = tickers_dict_to_data_array(
            {
                ticker_2: prices_df_2,
                ticker_1: prices_df_1,
            }, [ticker_1, ticker_2], [PriceField.Open, PriceField.Close])

        self.assertEqual(compute_container_hash(data_array_1),
                         compute_container_hash(data_array_2))
        self.assertNotEqual(compute_container_hash(data_array_1),
                            compute_container_hash(data_array_3))
示例#11
0
    def rank_strategies(self,
                        df: SimpleReturnsDataFrame,
                        ascending: bool = True) -> QFDataFrame:
        """
        Rank strategies using the ranking function. The worst strategy should be marked as 1.
        Parameters
        ----------
        df: SimpleReturnsDataFrame
            dataframe containing different strategies returns in the columns
        ascending: bool
            if True - the smaller the measure, the worse is the strategy

        Returns
        -------
        QFDataFrame
            data frame indexed by strategy names with two columns: quality (containing the quality measure
            computed for the given strategy, e.g. sharpe ratio) and rank.

        """
        rank_df = QFDataFrame(data={
            "quality": [self.ranking_function(df[col]) for col in df.columns]
        },
                              index=df.columns)

        rank_df = rank_df.replace([np.inf, -np.inf], np.nan)
        if rank_df.isna().values.any():
            raise ValueError(
                "There exist nan or infinite values in the rank_df")
        rank_df["rank"] = rank_df["quality"].rank(method="min",
                                                  ascending=ascending)
        return rank_df
    def get_signals(self) -> QFDataFrame:
        df = QFDataFrame.from_records(self._signals_data,
                                      columns=["Date", "Ticker", "Signal"])

        # Modify the dataframe to move all signals for certain tickers to separate columns and set the index to date
        df = df.pivot_table(index='Date',
                            columns='Ticker',
                            values='Signal',
                            aggfunc='first')
        return QFDataFrame(df)
示例#13
0
    def test_stats_functions(self):
        qf_df = QFDataFrame(data=self.prices_values,
                            index=self.dates,
                            columns=self.column_names)
        max_qf_df = qf_df.max()
        expected_max = QFSeries([5, 5, 5, 5, 5],
                                index=['a', 'b', 'c', 'd', 'e'])

        self.assertEqual(type(max_qf_df), QFSeries)
        assert_series_equal(max_qf_df, expected_max)
        self.assertEqual(dtype("float64"), max_qf_df.dtypes)
示例#14
0
    def _create_mse_debug_chart(self, alphas, chosen_solution, mean_square_errors, min_se_solution):
        mse_chart = LineChart()
        mean_square_errors_paths = QFDataFrame(data=mean_square_errors, index=pd.Index(alphas))
        mse_chart.add_decorator(TitleDecorator("Cross-validated avg. MSE of Elastic Net fit"))
        for _, path in mean_square_errors_paths.iteritems():
            mse_chart.add_decorator(DataElementDecorator(path))
        mse_chart.add_decorator(VerticalLineDecorator(x=min_se_solution, linestyle='-.'))
        mse_chart.add_decorator(VerticalLineDecorator(x=chosen_solution, linestyle='-'))
        mse_chart.plot()
        mse_chart.axes.invert_xaxis()

        return mse_chart
示例#15
0
    def _rolling_stress_indicator(self, data_frame_window: QFDataFrame):
        zscore_df = QFDataFrame()
        for name, series in data_frame_window.items():
            zscore_df[name] = (series - series.mean()) / series.std()

        last_row = zscore_df.tail(1)
        result = last_row.dot(
            self.weights)  # produces a weighted sum of the z-scored values
        result = result[0] / sum(
            self.weights
        )  # result was a single element series, return the value only
        return result
示例#16
0
    def _create_coeffs_debug_chart(self, alphas, chosen_solution, coeffs_path, min_se_solution):
        coeffs_chart = LineChart()
        coefficients_paths = QFDataFrame(data=coeffs_path, index=pd.Index(alphas))
        for _, path in coefficients_paths.iteritems():
            coeffs_chart.add_decorator(DataElementDecorator(path))
        coeffs_chart.add_decorator(TitleDecorator("Elastic Net (using Cross Validation)"))
        coeffs_chart.add_decorator(VerticalLineDecorator(x=min_se_solution, linestyle='-.'))
        coeffs_chart.add_decorator(VerticalLineDecorator(x=chosen_solution, linestyle='-'))
        coeffs_chart.plot()
        coeffs_chart.axes.invert_xaxis()

        return coeffs_chart
示例#17
0
    def _add_performance_statistics(self):
        """
        For each ticker computes its overall performance (PnL of short positions, PnL of long positions, total PnL).
        It generates a table containing final PnL values for each of the ticker nad optionally plots the performance
        throughout the backtest.
        """
        closed_positions = self.backtest_result.portfolio.closed_positions()
        closed_positions_pnl = QFDataFrame.from_records(
            data=[(self._ticker_name(p.contract()), p.end_time, p.direction(), p.total_pnl) for p in closed_positions],
            columns=["Tickers name", "Time", "Direction", "Realised PnL"]
        )
        closed_positions_pnl = closed_positions_pnl.sort_values(by="Time")

        # Get all open positions history
        open_positions_history = self.backtest_result.portfolio.positions_history()
        open_positions_history = open_positions_history.reset_index().melt(
            id_vars='index', value_vars=open_positions_history.columns, var_name='Contract',
            value_name='Position summary')
        open_positions_pnl = QFDataFrame(data={
            "Tickers name": open_positions_history["Contract"].apply(lambda contract: self._ticker_name(contract)),
            "Time": open_positions_history["index"],
            "Direction": open_positions_history["Position summary"].apply(
                lambda p: p.direction if isinstance(p, BacktestPositionSummary) else 0),
            "Total PnL of open position": open_positions_history["Position summary"].apply(
                lambda p: p.total_pnl if isinstance(p, BacktestPositionSummary) else 0)
        })

        all_positions_pnl = pd.concat([closed_positions_pnl, open_positions_pnl], sort=False)

        performance_dicts_series = all_positions_pnl.groupby(by=["Tickers name"]).apply(
            self._performance_series_for_ticker)
        performance_df = QFDataFrame(performance_dicts_series.tolist(), index=performance_dicts_series.index)

        self.document.add_element(NewPageElement())
        self.document.add_element(HeadingElement(level=2, text="Performance of each asset"))
        final_performance = performance_df. \
            applymap(lambda pnl_series: pnl_series.iloc[-1] if not pnl_series.empty else 0.0). \
            sort_values(by="Overall performance", ascending=False). \
            applymap(lambda p: '{:,.2f}'.format(p)). \
            reset_index()
        table = DFTable(final_performance, css_classes=['table', 'left-align'])
        table.add_columns_classes(["Tickers name"], 'wide-column')
        self.document.add_element(table)

        # Add performance plots
        if self.generate_pnl_chart_per_ticker:
            self.document.add_element(NewPageElement())
            self.document.add_element(
                HeadingElement(level=2, text="Performance of each asset during the whole backtest"))
            for ticker_name, performance in performance_df.iterrows():
                self._plot_ticker_performance(ticker_name, performance)
示例#18
0
    def _receive_historical_response(
            self, requested_tickers: Sequence[BloombergTicker],
            requested_fields: Sequence[str]):
        ticker_str_to_ticker: Dict[str, BloombergTicker] = {
            t.as_string(): t
            for t in requested_tickers
        }

        response_events = get_response_events(self._session)
        tickers_data_dict = defaultdict(
            lambda: QFDataFrame(columns=requested_fields))

        for event in response_events:
            try:
                check_event_for_errors(event)

                security_data = extract_security_data(event)
                check_security_data_for_errors(security_data)

                field_data_array = security_data.getElement(FIELD_DATA)
                dates = [
                    to_datetime(x.getElementAsDatetime(DATE))
                    for x in field_data_array.values()
                ]

                dates_fields_values = QFDataFrame(np.nan,
                                                  index=dates,
                                                  columns=requested_fields)

                for field_name in requested_fields:
                    dates_fields_values.loc[:, field_name] = [
                        self._get_float_or_nan(data_of_date_elem, field_name)
                        for data_of_date_elem in field_data_array.values()
                    ]
                security_name = security_data.getElementAsString(SECURITY)

                try:
                    ticker = ticker_str_to_ticker[security_name]
                    tickers_data_dict[ticker] = tickers_data_dict[
                        ticker].append(dates_fields_values)
                except KeyError:
                    self.logger.warning(
                        f"Received data for a ticker which was not present in the request: "
                        f"{security_name}. The data for that ticker will be excluded from parsing."
                    )
            except BloombergError as e:
                self.logger.error(e)

        return tickers_dict_to_data_array(tickers_data_dict,
                                          list(tickers_data_dict.keys()),
                                          requested_fields)
示例#19
0
    def _generate_exposure_values(self, config: FastAlphaModelTesterConfig,
                                  data_handler: FastDataHandler,
                                  tickers: Sequence[Ticker]):
        """
        For the given Alpha model and its parameters, generates the dataframe containing all exposure values, that
        will be returned by the model through signals.
        """

        model = config.generate_model(data_handler)

        current_exposures_values = QFSeries(
            index=pd.Index(tickers, name=TICKERS))
        current_exposures_values[:] = 0.0

        backtest_dates = pd.date_range(self._start_date,
                                       self._end_date,
                                       freq="B")

        exposure_values_df = QFDataFrame(index=backtest_dates,
                                         columns=pd.Index(tickers,
                                                          name=TICKERS))

        for ticker in tickers:
            if isinstance(ticker, FutureTicker):
                # Even if the tickers were already initialize, during pickling process, the data handler and timer
                # information is lost
                ticker.initialize_data_provider(self._timer, data_handler)

        for i, curr_datetime in enumerate(backtest_dates):
            new_exposures = QFSeries(index=tickers)
            self._timer.set_current_time(curr_datetime)

            for j, ticker, curr_exp_value in zip(count(), tickers,
                                                 current_exposures_values):
                curr_exp = Exposure(curr_exp_value) if is_finite_number(
                    curr_exp_value) else None
                try:
                    new_exp = model.calculate_exposure(ticker, curr_exp)
                except NoValidTickerException:
                    new_exp = None
                new_exposures.iloc[
                    j] = new_exp.value if new_exp is not None else None

            # assuming that we always follow the new_exposures from strategy, disregarding confidence levels
            # and expected moves, looking only at the suggested exposure
            current_exposures_values = new_exposures
            exposure_values_df.iloc[i, :] = current_exposures_values.iloc[:]

        exposure_values_df = exposure_values_df.dropna(axis=1, how="all")
        return exposure_values_df
示例#20
0
    def _calculate_portfolio_returns_tms(self, tickers, open_to_open_returns_df: QFDataFrame,
                                         exposure_values_df: QFDataFrame) \
            -> SimpleReturnsSeries:
        """
        SimpleReturnsSeries of the portfolio - for each date equal to the portfolio performance over the last
        open-to-open period, ex. value indexed as 2010-02-15 would refer to the portfolio value change between
        open at 14th and open at 15th, and would be based on the signal from 2010-02-13;

        the first index of the series is the Day 3 of the backtest, as the first signal calculation occurs
        after Day 1 (see ORDER OF ACTIONS below)
        the last index of the series is test_end_date and the portfolio exposure is being set to zero
        on the opening of the test_end_date

        ORDER OF ACTIONS:

        -- Day 1 --
        signal is generated, based on the historic data INCLUDING prices from Day 1
        suggested exposure for Day 2 is calculated

        -- Day 2 --
        a trade is entered, held or exited (or nothing happens) regarding the suggested exposure
        this action is performed on the opening of the day

        -- Day 3 --
        at the opening the open-to-open return is calculated
        now it is possible to estimate current portfolio value
        the simple return of the portfolio (Day 3 to Day 2) is saved and indexed with Day 3 date
        """

        open_to_open_returns_df = open_to_open_returns_df.dropna(how="all")
        shifted_signals_df = exposure_values_df.shift(2, axis=0)
        shifted_signals_df = shifted_signals_df.iloc[2:]

        daily_returns_of_strategies_df = shifted_signals_df * open_to_open_returns_df
        daily_returns_of_strategies_df = daily_returns_of_strategies_df.dropna(
            axis=0, how='all')

        daily_returns_of_strategies_df = cast_dataframe(
            daily_returns_of_strategies_df,
            SimpleReturnsDataFrame)  # type: SimpleReturnsDataFrame

        weights = Portfolio.one_over_n_weights(tickers)
        # for strategies based on more than one ticker (ex. VolLongShort) use the line below:
        # weights = QFSeries(np.ones(daily_returns_of_strategies_df.num_of_columns))

        portfolio_rets_tms, _ = Portfolio.constant_weights(
            daily_returns_of_strategies_df, weights)

        return portfolio_rets_tms
示例#21
0
    def _get_current_bars(self, tickers: Sequence[Ticker]) -> QFDataFrame:
        """
        Gets the current bars for given Tickers. If the bars are not available yet, NaNs are returned.
        The result is a QFDataFrame with Tickers as an index and PriceFields as columns.

        In case of daily trading, the current bar is returned only at the Market Close Event time, as the get_price
        function will not return data for the current date until the market closes.

        In case of intraday trading (for N minutes frequency) the current bar can be returned in the time between
        (inclusive) N minutes after MarketOpenEvent and the MarketCloseEvent. Important: If current time ("now")
        contains non-zero seconds or microseconds, NaNs will be returned.

        """
        if not tickers:
            return QFDataFrame()

        assert self._frequency >= Frequency.DAILY, "Lower than daily frequency is not supported by the simulated " \
                                                   "executor"
        current_datetime = self._timer.now()

        market_close_time = current_datetime + MarketCloseEvent.trigger_time(
        ) == current_datetime

        if self._frequency == Frequency.DAILY:
            # In case of daily trading, the current bar can be returned only at the Market Close
            if not market_close_time:
                return QFDataFrame(index=tickers, columns=PriceField.ohlcv())
            else:
                current_datetime = date_to_datetime(current_datetime.date())
                start_date = current_datetime - self._frequency.time_delta()
                current_bar_start = current_datetime
        else:
            # In case of intraday trading the current full bar is always indexed by the left side of the time range
            start_date = current_datetime - self._frequency.time_delta()
            current_bar_start = start_date

        prices_data_array = self._data_handler.get_price(
            tickers=tickers,
            fields=PriceField.ohlcv(),
            start_date=start_date,
            end_date=current_datetime,
            frequency=self._frequency)
        try:
            current_bars = cast_data_array_to_proper_type(
                prices_data_array.loc[current_bar_start])
        except KeyError:
            current_bars = QFDataFrame(index=tickers,
                                       columns=PriceField.ohlcv())
        return current_bars
示例#22
0
def create_holdings_chart(positions: QFDataFrame) -> LineChart:
    """
    Creates a line chart showing holdings per day based on the specified positions.

    Parameters
    ----------
    positions: QFDataFrame
        Positions as returned by the extract_rets_pos_txn_from_zipline function.

    Returns
    -------
    LineChart
        Created line chart
    """
    # Based on:
    # https://github.com/quantopian/pyfolio/blob/5d63df4ca6e0ead83f4bebf9860732d37f532026/pyfolio/plotting.py#L323
    result = LineChart()

    # Perform some calculations.
    positions = positions.copy().drop("cash", axis="columns")
    holdings = positions.apply(lambda x: np.sum(x != 0), axis="columns")
    holdings_by_month = holdings.resample("1M").mean()

    holdings_decorator = DataElementDecorator(holdings,
                                              color="steelblue",
                                              linewidth=1.5)
    result.add_decorator(holdings_decorator)
    holdings_by_month_decorator = DataElementDecorator(holdings_by_month,
                                                       color="orangered",
                                                       alpha=0.5,
                                                       linewidth=2)
    result.add_decorator(holdings_by_month_decorator)

    hline_decorator = HorizontalLineDecorator(holdings.values.mean(),
                                              linestyle="--")
    result.add_decorator(hline_decorator)

    legend = LegendDecorator()
    legend.add_entry(holdings_decorator, "Daily Holdings")
    legend.add_entry(holdings_by_month_decorator,
                     "Average Daily Holdings, by month")
    legend.add_entry(hline_decorator, "Average Daily Holdings, net")
    result.add_decorator(legend)

    result.add_decorator(TitleDecorator("Holdings per Day"))
    result.add_decorator(
        AxesLabelDecorator(y_label="Amount of holdings per Day"))

    return result
示例#23
0
    def get_factor_return_attribution(cls, fund_tms: QFSeries,
                                      fit_tms: QFSeries,
                                      regressors_df: QFDataFrame,
                                      coefficients: QFSeries,
                                      alpha: float) -> Tuple[QFSeries, float]:
        """
        Returns performance attribution for each factor in given regressors and also calculates the unexplained return.
        """
        fund_returns = fund_tms.to_simple_returns()
        regressors_returns = regressors_df.to_simple_returns()
        annualised_fund_return = cagr(fund_returns)
        annualised_fit_return = cagr(fit_tms)

        total_nav = fit_tms.to_prices(initial_price=1.0)

        def calc_factors_profit(series) -> float:
            factor_ret = regressors_returns.loc[:, series.name].values
            return coefficients.loc[series.name] * (total_nav[:-1].values *
                                                    factor_ret).sum()

        factors_profits = regressors_returns.apply(calc_factors_profit)

        alpha_profit = total_nav[:-1].sum() * alpha
        total_profit = factors_profits.sum() + alpha_profit

        regressors_return_attribution = factors_profits * annualised_fit_return / total_profit
        regressors_return_attribution = cast_series(
            regressors_return_attribution, QFSeries)

        unexplained_return = annualised_fund_return - regressors_return_attribution.sum(
        )

        return regressors_return_attribution, unexplained_return
示例#24
0
    def _create_performance_tables(
            self, performance_df: QFDataFrame) -> List[DFTable]:
        """ Create a formatted DFTable out of the performance_df data frame. """
        numeric_columns = [
            col for col in performance_df.columns
            if is_numeric_dtype(performance_df[col])
        ]
        performance_df[numeric_columns] = performance_df[
            numeric_columns].applymap(lambda x: '{:,.0f}'.format(x))
        performance_df = performance_df.set_index("Asset").sort_index()

        # Divide the performance df into a number of data frames, so that each of them contains up to
        # self.max_col_per_page columns, but keep the first column of the original df in all of them
        split_dfs = np.array_split(performance_df,
                                   np.ceil(performance_df.num_of_columns /
                                           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
        ]
        return df_tables
示例#25
0
    def setUp(self):
        self.initial_risk_stats_factory = InitialRiskStatsFactory(
            max_accepted_dd=0.2, target_return=0.05)

        self.sample_trades_df = QFDataFrame(
            data=[[-0.11, 0.0, -0.105], [-0.11, 0.05, 0.19], [0.1, 0.0, 0.0],
                  [0.0, 0.0, 0.0]])
示例#26
0
    def get_liquid_hours(self, contract: IBContract) -> QFDataFrame:
        """ Returns a QFDataFrame containing information about liquid hours of the given contract. """
        with self.lock:
            self._reset_action_lock()
            request_id = 3
            self.client.reqContractDetails(request_id, contract)

            if self._wait_for_results():
                contract_details = self.wrapper.contract_details
                liquid_hours = contract_details.tradingHours.split(";")
                liquid_hours_df = QFDataFrame.from_records([
                    hours.split("-")
                    for hours in liquid_hours if not hours.endswith("CLOSED")
                ],
                                                           columns=[
                                                               "FROM", "TO"
                                                           ])
                for col in liquid_hours_df.columns:
                    liquid_hours_df[col] = to_datetime(liquid_hours_df[col],
                                                       format="%Y%m%d:%H%M")

                liquid_hours_df.name = contract_details.contract.symbol
                return liquid_hours_df

            else:
                error_msg = 'Time out while getting contract details'
                self.logger.error(error_msg)
                raise BrokerException(error_msg)
    def __init__(self,
                 risk_estimation_factor: float,
                 data_provider: DataHandler,
                 start_date: datetime,
                 end_date: datetime,
                 tickers: Sequence[Ticker],
                 number_of_trades: int,
                 time_in_the_market: float,
                 exposure: Exposure = Exposure.LONG,
                 frequency: Frequency = Frequency.DAILY,
                 seed: Optional[int] = None):
        super().__init__(risk_estimation_factor, data_provider)

        self.timer = data_provider.timer
        self.start_date = start_date
        self.end_date = end_date
        self.frequency = frequency
        scenarios_generator = ScenariosGenerator()

        self.trading_scenarios = QFDataFrame(
            columns=tickers,
            data={
                ticker: scenarios_generator.make_exposure_scenarios(
                    start_date, end_date, number_of_trades, time_in_the_market,
                    exposure, frequency, seed and seed + i)
                for i, ticker in enumerate(tickers)
            })
示例#28
0
def compute_container_hash(
        data_container: Union[QFSeries, QFDataFrame, QFDataArray]) -> str:
    """
    For the given data container returns the hexadecimal digest of the data.

    Parameters
    ----------
    data_container: QFSeries, QFDataFrame, QFDataArray
        container, which digest should be computed

    Returns
    -------
    str
        hexadecimal digest of data in the passed data container
    """
    if isinstance(data_container, QFSeries):
        hashed_container = hash_pandas_object(data_container)

    elif isinstance(data_container, QFDataFrame):
        hashed_container = hash_pandas_object(data_container)

    elif isinstance(data_container, QFDataArray):
        hash_data_frame = QFDataFrame([
            hash_pandas_object(data_container.loc[:, :, field].to_pandas())
            for field in data_container.fields
        ])
        hashed_container = hash_pandas_object(hash_data_frame)
    else:
        raise ValueError("Unsupported type of data container")

    return hashlib.sha1(hashed_container.values).hexdigest()
示例#29
0
    def __init__(self,
                 ts: TradingSession,
                 model_tickers_dict: Dict[AlphaModel, Sequence[Ticker]],
                 use_stop_losses=True):
        """
        Parameters
        ----------
        ts
            Trading session
        model_tickers_dict
            Dict mapping models to list of tickers that the model trades. (The tickers for which the
            model gives recommendations)
        use_stop_losses
            flag indicating if the stop losses should be used or not. If False, all stop orders are ignored
        """

        self._broker = ts.broker
        self._order_factory = ts.order_factory
        self._data_handler = ts.data_handler
        self._contract_ticker_mapper = ts.contract_ticker_mapper
        self._position_sizer = ts.position_sizer
        self._timer = ts.timer

        self._model_tickers_dict = model_tickers_dict
        self._use_stop_losses = use_stop_losses
        self.signals_df = QFDataFrame(
        )  # rows indexed by date and columns by "Ticker@AlphaModel" string

        ts.notifiers.scheduler.subscribe(BeforeMarketOpenEvent, listener=self)
        self.logger = qf_logger.getChild(self.__class__.__name__)
        self._log_configuration()
示例#30
0
 def test_asof_multiple_dates_gaps_at_the_end(self):
     actual_result = self.qf_data_array.asof(str_to_date('2018-02-06'))
     expected_result = QFDataFrame(
         index=self.qf_data_array.tickers.to_index(),
         columns=self.qf_data_array.fields.to_index(),
         data=[[16, 17, 18, 19], [12, 13, 14, 15]])
     assert_dataframes_equal(expected_result, actual_result)