コード例 #1
0
 def test_get_lowest(self):
     self.assertEqual(Frequency.get_lowest_freq({"A": Frequency.DAILY, "B": Frequency.WEEKLY, "C": Frequency.YEARLY}),
                      "C")
     self.assertEqual(Frequency.get_lowest_freq({"A": Frequency.DAILY, "B": Frequency.WEEKLY, "C": Frequency.MONTHLY}),
                      "C")
     self.assertEqual(Frequency.get_lowest_freq({"A": Frequency.DAILY}),
                      "A")
コード例 #2
0
    def test_infer(self):
        index = DatetimeIndex(['2015-01-02T00:00:00.000000000', '2015-01-05T00:00:00.000000000',
                               '2015-01-06T00:00:00.000000000', '2015-01-07T00:00:00.000000000',
                               '2015-01-08T00:00:00.000000000', '2015-01-09T00:00:00.000000000',
                               '2015-01-12T00:00:00.000000000', '2015-01-13T00:00:00.000000000',
                               '2015-01-14T00:00:00.000000000', '2015-01-15T00:00:00.000000000',
                               '2015-01-16T00:00:00.000000000', '2015-01-20T00:00:00.000000000',
                               '2015-01-21T00:00:00.000000000', '2015-01-22T00:00:00.000000000',
                               '2015-01-23T00:00:00.000000000', '2015-01-26T00:00:00.000000000',
                               '2015-01-27T00:00:00.000000000', '2015-01-28T00:00:00.000000000'])
        self.assertEqual(Frequency.infer_freq(index), Frequency.DAILY)

        index = DatetimeIndex(['2015-01-31T00:00:00.000000000', '2015-02-28T00:00:00.000000000',
                               '2015-03-31T00:00:00.000000000', '2015-04-30T00:00:00.000000000',
                               '2015-05-31T00:00:00.000000000', '2015-06-30T00:00:00.000000000',
                               '2015-07-31T00:00:00.000000000', '2015-08-31T00:00:00.000000000',
                               '2015-09-30T00:00:00.000000000', '2015-10-31T00:00:00.000000000',
                               '2015-11-30T00:00:00.000000000', '2015-12-31T00:00:00.000000000',
                               '2016-01-31T00:00:00.000000000', '2016-02-29T00:00:00.000000000',
                               '2016-03-31T00:00:00.000000000', '2016-04-30T00:00:00.000000000',
                               '2016-05-31T00:00:00.000000000', '2016-06-30T00:00:00.000000000',
                               '2016-07-31T00:00:00.000000000', '2016-08-31T00:00:00.000000000',
                               '2016-09-30T00:00:00.000000000', '2016-10-31T00:00:00.000000000',
                               '2016-11-30T00:00:00.000000000', '2016-12-31T00:00:00.000000000'])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MONTHLY)

        index = DatetimeIndex(['2015-01-31T00:00:00.000000000', '2015-02-28T00:00:00.000000000',
                               '2015-04-30T00:00:00.000000000',
                               '2015-05-31T00:00:00.000000000', '2015-06-30T00:00:00.000000000',
                               '2015-07-31T00:00:00.000000000', '2015-08-31T00:00:00.000000000'])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MONTHLY)
コード例 #3
0
ファイル: test_frequency.py プロジェクト: quarkfin/qf-lib
    def test_infer(self):
        index = DatetimeIndex([
            '2015-01-02T00:00:00.000000000', '2015-01-05T00:00:00.000000000',
            '2015-01-06T00:00:00.000000000', '2015-01-07T00:00:00.000000000',
            '2015-01-08T00:00:00.000000000', '2015-01-09T00:00:00.000000000',
            '2015-01-12T00:00:00.000000000', '2015-01-13T00:00:00.000000000',
            '2015-01-14T00:00:00.000000000', '2015-01-15T00:00:00.000000000',
            '2015-01-16T00:00:00.000000000', '2015-01-20T00:00:00.000000000',
            '2015-01-21T00:00:00.000000000', '2015-01-22T00:00:00.000000000',
            '2015-01-23T00:00:00.000000000', '2015-01-26T00:00:00.000000000',
            '2015-01-27T00:00:00.000000000', '2015-01-28T00:00:00.000000000'
        ])
        self.assertEqual(Frequency.infer_freq(index), Frequency.DAILY)

        index = DatetimeIndex([
            '2015-01-31T00:00:00.000000000', '2015-02-28T00:00:00.000000000',
            '2015-03-31T00:00:00.000000000', '2015-04-30T00:00:00.000000000',
            '2015-05-31T00:00:00.000000000', '2015-06-30T00:00:00.000000000',
            '2015-07-31T00:00:00.000000000', '2015-08-31T00:00:00.000000000',
            '2015-09-30T00:00:00.000000000', '2015-10-31T00:00:00.000000000',
            '2015-11-30T00:00:00.000000000', '2015-12-31T00:00:00.000000000',
            '2016-01-31T00:00:00.000000000', '2016-02-29T00:00:00.000000000',
            '2016-03-31T00:00:00.000000000', '2016-04-30T00:00:00.000000000',
            '2016-05-31T00:00:00.000000000', '2016-06-30T00:00:00.000000000',
            '2016-07-31T00:00:00.000000000', '2016-08-31T00:00:00.000000000',
            '2016-09-30T00:00:00.000000000', '2016-10-31T00:00:00.000000000',
            '2016-11-30T00:00:00.000000000', '2016-12-31T00:00:00.000000000'
        ])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MONTHLY)

        index = DatetimeIndex([
            '2015-01-31T00:00:00.000000000', '2015-02-28T00:00:00.000000000',
            '2015-04-30T00:00:00.000000000', '2015-05-31T00:00:00.000000000',
            '2015-06-30T00:00:00.000000000', '2015-07-31T00:00:00.000000000',
            '2015-08-31T00:00:00.000000000'
        ])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MONTHLY)

        index = DatetimeIndex([
            '2015-01-01T00:00:00.000000000', '2015-01-01T00:01:00.000000000',
            '2015-01-01T00:02:00.000000000', '2015-01-01T00:03:00.000000000',
            '2015-01-01T00:04:00.000000000', '2015-01-01T00:05:00.000000000',
            '2015-01-01T00:09:00.000000000', '2015-01-01T00:10:00.000000000',
            '2015-01-01T00:14:00.000000000', '2015-01-01T00:15:00.000000000'
        ])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MIN_1)

        index = DatetimeIndex([
            '2015-01-01T00:00:00.000000000', '2015-01-01T00:05:00.000000000',
            '2015-01-01T00:10:00.000000000', '2015-01-01T00:15:00.000000000',
            '2015-01-01T00:18:00.000000000', '2015-01-01T00:19:00.000000000',
            '2015-01-01T00:22:00.000000000', '2015-01-01T00:27:00.000000000',
            '2015-01-01T00:32:00.000000000', '2015-01-01T00:37:00.000000000'
        ])
        self.assertEqual(Frequency.infer_freq(index), Frequency.MIN_5)
コード例 #4
0
    def test_get_price_many_tickers_many_fields_many_dates(self):
        data_provider = self.get_data_provider(self.tickers, self.fields)
        prices = data_provider.get_price(self.tickers, self.fields, self.start_date, self.end_date, Frequency.MIN_1)

        self.assertEqual(type(prices), QFDataArray)
        self.assertEqual(prices.shape, (self.number_of_data_bars, len(self.tickers), len(self.fields)))
        self.assertEqual(Frequency.infer_freq(pd.to_datetime(prices.dates.values)), Frequency.MIN_1)
コード例 #5
0
    def _data_array_to_dataframe(self, prices_data_array: QFDataArray,
                                 frequency: Frequency):
        """
        Converts a QFDataArray into a DataFrame by removing the "Price Field" axis.

        Every index (e.g. 15:00) denotes the close price of the time range beginning at this time (15:00 - 15:01)
        The only exception is the time range 1 minute before market open (e.g. 9:29 - 9:30 if market opens 9:30). The
        price for this time range, denotes the OPEN price of 9:30 - 9:31.
        """
        original_dates = list(prices_data_array.dates.to_index())
        dates = prices_data_array.resample(dates='1D').first().dates.to_index()
        market_open_datetimes = [
            price_datetime + MarketOpenEvent.trigger_time()
            for price_datetime in dates if price_datetime +
            MarketOpenEvent.trigger_time() in original_dates
        ]
        shifted_open_datetimes = [
            price_datetime - frequency.time_delta()
            for price_datetime in market_open_datetimes
        ]

        new_dates = list(set(original_dates + shifted_open_datetimes))
        new_dates = sorted(new_dates)
        prices_df = PricesDataFrame(index=new_dates,
                                    columns=prices_data_array.tickers)

        prices_df.loc[shifted_open_datetimes, :] = \
            prices_data_array.loc[market_open_datetimes, :, PriceField.Open].values
        prices_df.loc[original_dates, :] = prices_data_array.loc[
            original_dates, :, PriceField.Close].values

        return prices_df
コード例 #6
0
    def test_get_price_single_ticker_many_fields_many_dates(self):
        data_provider = self.get_data_provider(self.ticker, self.fields)
        prices = data_provider.get_price(self.ticker, self.fields, self.start_date, self.end_date, Frequency.MIN_1)

        self.assertEqual(type(prices), PricesDataFrame)
        self.assertEqual(prices.shape, (self.number_of_data_bars, len(self.fields)))
        self.assertEqual(Frequency.infer_freq(prices.index), Frequency.MIN_1)
コード例 #7
0
    def get_frequency(self) -> Frequency:
        """
        Attempts to infer the frequency of this series. The analysis uses pandas' infer_freq, as well as a heuristic
        to reduce the amount of ``Irregular`` results.

        See the implementation of the Frequency.infer_freq function for more information.
        """
        return Frequency.infer_freq(self.index)
コード例 #8
0
    def test_get_price_many_tickers_single_field_many_dates(self):
        data_provider = self.get_data_provider(self.tickers, PriceField.Close)
        prices = data_provider.get_price(self.tickers, PriceField.Close, self.start_date, self.end_date,
                                         Frequency.MIN_1)

        self.assertEqual(type(prices), PricesDataFrame)
        self.assertEqual(prices.shape, (self.number_of_data_bars, len(self.tickers)))
        self.assertEqual(Frequency.infer_freq(pd.to_datetime(prices.index)), Frequency.MIN_1)
コード例 #9
0
    def _aggregate_intraday_data(self, data_array, start_date: datetime,
                                 end_date: datetime, tickers: Sequence[Ticker],
                                 fields, frequency: Frequency):
        """
        Function, which aggregates the intraday data array for various dates and returns a new data array with data
        sampled with the given frequency.
        """

        # If the data is of intraday data type, which spans over multiple days, the base parameter of resample()
        # function should be adjusted differently for the first day.
        #
        # Therefore, the data array is divided into two separate arrays data_array_1, data_array_2 - first containing
        # only the first day, and the second one - containing all other dates.

        end_of_day = start_date + RelativeDelta(hour=23, minute=59, second=59)
        _end_date = end_of_day if (end_of_day < end_date) else end_date

        # Get both parts of the data array
        data_array_1 = data_array.loc[start_date:_end_date, :, :]
        data_array_2 = data_array.loc[end_of_day:end_date, :, :]

        if len(data_array_1) > 0:
            base_data_array_1 = pd.to_datetime(
                data_array_1[DATES].values[0]).minute
            data_array_1 = data_array_1.resample(
                dates=frequency.to_pandas_freq(),
                base=base_data_array_1,
                label='left',
                skipna=True).apply(
                    lambda x: self._aggregate_data_array(x, tickers, fields))

        if len(data_array_2) > 0:
            base_data_array_2 = MarketOpenEvent.trigger_time().minute
            data_array_2 = data_array_2.resample(
                dates=frequency.to_pandas_freq(),
                base=base_data_array_2,
                label='left',
                skipna=True).apply(
                    lambda x: self._aggregate_data_array(x, tickers, fields))

        data_array = QFDataArray.concat([data_array_1, data_array_2],
                                        dim=DATES)

        return data_array
コード例 #10
0
    def get_current_bar(self, tickers: Union[Ticker, Sequence[Ticker]], frequency: Frequency = None) \
            -> Union[QFSeries, QFDataFrame]:
        """
        Gets the current bar(s) for given Ticker(s). If the bar is not available yet, None is returned.

        If the request for single Ticker was made, then the result is a QFSeries indexed with PriceFields (OHLCV).
        If the request for multiple Tickers was made, then the result has Tickers as an index and PriceFields
        as columns.

        In case of N minutes frequency, the current bar can be returned in the time between (inclusive) N minutes
        after MarketOpenEvent and the MarketCloseEvent).

        E.g. for 1 minute frequency, at 13:00 (if the market opens before 13:00), the 12:59 - 13:00 bar will be
        returned. In case of 15 minutes frequency, when the market opened less then 15 minutes ago, Nones will be
        returned. If current time ("now") contains non-zero seconds or microseconds, Nones will be returned.

        Parameters
        -----------
        tickers: Ticker, Sequence[Ticker]
            tickers of the securities which prices should be downloaded
        frequency: Frequency
            frequency of the data

        Returns
        -------
        QFSeries, QFDataFrame
            current bar

        """
        if not tickers:
            return QFSeries()

        frequency = frequency or self.fixed_data_provider_frequency or Frequency.MIN_1

        tickers, was_single_ticker_provided = convert_to_list(tickers, Ticker)

        current_datetime = self.timer.now()

        start_date = current_datetime - frequency.time_delta()
        prices_data_array = self.get_price(tickers=tickers,
                                           fields=PriceField.ohlcv(),
                                           start_date=start_date,
                                           end_date=current_datetime,
                                           frequency=frequency)
        try:
            last_available_bars = cast_data_array_to_proper_type(
                prices_data_array.loc[start_date])
        except KeyError:
            return QFDataFrame(index=tickers, columns=PriceField.ohlcv())

        if was_single_ticker_provided:
            last_available_bars = last_available_bars.iloc[0, :]

        return last_available_bars
コード例 #11
0
    def test_get_price_single_future_ticker_many_fields(self):
        data_provider = self.get_data_provider(self.future_ticker, self.fields)
        tickers_to_check = [PortaraTicker('AB2021M', SecurityType.FUTURE, 1),
                            PortaraTicker('AB2021U', SecurityType.FUTURE, 1)]

        prices = data_provider.get_price(tickers_to_check, self.fields, self.start_date, self.end_date, Frequency.MIN_1)

        self.assertEqual(type(prices), QFDataArray)
        self.assertEqual(prices.shape, (self.number_of_data_bars, len(tickers_to_check), len(self.fields)))
        self.assertEqual(Frequency.infer_freq(pd.to_datetime(prices.dates.values)), Frequency.MIN_1)
        self.assertCountEqual(prices.tickers.values, tickers_to_check)
コード例 #12
0
    def test_get_price_aggregation_single_ticker(self):
        MarketOpenEvent.set_trigger_time({"hour": 17, "minute": 13, "second": 0, "microsecond": 0})

        dp = PortaraDataProvider(self.futures_path, self.ticker, PriceField.Close, self.start_date, self.end_date,
                                 Frequency.MIN_1)
        prices = dp.get_price(self.ticker, PriceField.Close, self.start_date, self.end_date, Frequency.MIN_1)
        prices5 = dp.get_price(self.ticker, PriceField.Close, self.start_date, self.end_date, Frequency.MIN_5)
        prices15 = dp.get_price(self.ticker, PriceField.Close, self.start_date, self.end_date, Frequency.MIN_15)

        self.assertTrue(len(prices5))
        self.assertEqual(type(prices5), PricesSeries)
        self.assertEqual(Frequency.infer_freq(prices5.index), Frequency.MIN_5)

        assert_series_equal(prices5, prices.resample('5T', origin=datetime(2021, 6, 17, 17, 13)).last().dropna(),
                            check_names=False)

        self.assertTrue(len(prices15))
        self.assertEqual(type(prices15), PricesSeries)
        self.assertEqual(Frequency.infer_freq(prices15.index), Frequency.MIN_15)
        assert_series_equal(prices15, prices.resample('15T', origin=datetime(2021, 6, 17, 17, 13)).last().dropna(),
                            check_names=False)
コード例 #13
0
 def test_from_string(self):
     self.assertEqual(Frequency.DAILY, Frequency.from_string('daily'))
     self.assertEqual(Frequency.WEEKLY, Frequency.from_string('weekly'))
     self.assertEqual(Frequency.MONTHLY, Frequency.from_string('monthly'))
     self.assertEqual(Frequency.QUARTERLY, Frequency.from_string('quarterly'))
     self.assertEqual(Frequency.SEMI_ANNUALLY, Frequency.from_string('semi_annually'))
     self.assertEqual(Frequency.YEARLY, Frequency.from_string('yearly'))
コード例 #14
0
    def get_history(self, tickers: Union[Ticker, Sequence[Ticker]],
                    fields: Union[str, Sequence[str]],
                    start_date: datetime, end_date: datetime = None, frequency: Frequency = Frequency.DAILY, **kwargs
                    ) -> Union[QFSeries, QFDataFrame, QFDataArray]:

        # Verify whether the passed frequency parameter is correct and can be used with the preset data
        assert frequency == self._frequency, "Currently, for the get history does not support data sampling"

        if end_date is None:
            end_date = datetime.now()

        if frequency > Frequency.DAILY:
            # In case of high frequency - the data array should not include the end_date. The data range is
            # labeled with the beginning index time and excludes the end of the time range, therefore a new
            # end date is computed.
            end_date = end_date - Frequency.MIN_1.time_delta()

        # In order to be able to return data for FutureTickers create a mapping between tickers and corresponding
        # specific tickers (in case of non FutureTickers it will be an identity mapping)
        tickers, got_single_ticker = convert_to_list(tickers, Ticker)
        tickers_mapping = {
            (t.get_current_specific_ticker() if isinstance(t, FutureTicker) else t): t for t in tickers
        }
        specific_tickers = list(tickers_mapping.keys())

        fields, got_single_field = convert_to_list(fields, str)
        got_single_date = start_date is not None and (
            (start_date == end_date) if frequency <= Frequency.DAILY else
            (start_date + frequency.time_delta() >= end_date)
        )

        self._check_if_cached_data_available(specific_tickers, fields, start_date, end_date)

        data_array = self._data_bundle.loc[start_date:end_date, specific_tickers, fields]

        normalized_result = normalize_data_array(data_array, specific_tickers, fields, got_single_date,
                                                 got_single_ticker,
                                                 got_single_field, use_prices_types=False)

        # Map the specific tickers onto the tickers given by the tickers_mapping array
        if isinstance(normalized_result, QFDataArray):
            normalized_result = normalized_result.assign_coords(
                tickers=[tickers_mapping[t] for t in normalized_result.tickers.values])
        elif isinstance(normalized_result, PricesDataFrame):
            normalized_result = normalized_result.rename(columns=tickers_mapping)
        elif isinstance(normalized_result, PricesSeries):
            # Name of the PricesSeries can only contain strings
            ticker = tickers[0]
            normalized_result = normalized_result.rename(ticker.name)

        return normalized_result
コード例 #15
0
    def get_frequency(self) -> Mapping[str, Frequency]:
        """
        Attempts to infer the frequency of each column in this dataframe. The analysis uses pandas' infer_freq,
        as well as a heuristic to reduce the amount of ``Irregular`` results.

        See the implementation of the Frequency.infer_freq function for more information.
        """
        result = {}
        for col in self:
            series = self[col]
            if not series.isnull().all():
                # Drop NaN rows only when the series has at least one non-NaN value.
                # This is necessary because the series has been packed with other series which might have a higher
                # frequency.
                series = series.dropna(axis=0)

            result[col] = Frequency.infer_freq(series.index)
        return result
コード例 #16
0
    def get_last_available_price(
            self,
            tickers: Union[Ticker, Sequence[Ticker]],
            frequency: Frequency = None) -> Union[float, QFSeries]:
        """
        Gets the latest available price for given assets, even if the full bar is not yet available.

        The frequency parameter is always casted into 1 minute frequency, to represent the most recent price.

        It returns the CLOSE price of the last available bar. If "now" is after the market OPEN, and before the market
        CLOSE, the last available price is equal to the current price (CLOSE price of the bar, which right bound is
        equal to "now"). If the market did not open yet, the last available CLOSE price will be returned.
        Non-zero seconds or microseconds values are omitted (e.g. 13:40:01 is always treated as 13:40:00).

        Parameters
        -----------
        tickers: Ticker, Sequence[Ticker]
            tickers of the securities which prices should be downloaded
        frequency: Frequency
            frequency of the data

        Returns
        -------
        float, QFSeries
            last_prices series where:
            - last_prices.name contains a date of current prices,
            - last_prices.index contains tickers
            - last_prices.data contains latest available prices for given tickers
        """
        frequency = frequency or self.fixed_data_provider_frequency or Frequency.MIN_1

        if frequency <= Frequency.DAILY:
            raise ValueError(
                "The Intraday Data Handler can be used only with the Intraday Frequency"
            )

        tickers, was_single_ticker_provided = convert_to_list(tickers, Ticker)

        # if an empty tickers list was supplied then return an empty result
        if not tickers:
            return QFSeries()

        current_datetime = self.timer.now()

        # If the current_datetime represents the time after Market Close and before Market Open, shift it to the
        # Market Close of the day before
        if current_datetime + MarketOpenEvent.trigger_time(
        ) > current_datetime:
            current_datetime = current_datetime - RelativeDelta(days=1)
            current_datetime = current_datetime + MarketCloseEvent.trigger_time(
            )
        elif current_datetime + MarketCloseEvent.trigger_time(
        ) < current_datetime:
            current_datetime = current_datetime + MarketCloseEvent.trigger_time(
            )

        # If the current_datetime represents Saturday or Sunday, shift it to last Friday
        if current_datetime.weekday() in (5, 6):
            current_datetime = current_datetime - RelativeDelta(weekday=4,
                                                                weeks=1)

        # The time range denotes the current_datetime +- time delta related to the given frequency. The current price is
        # represented as the close price of (time_range_start, current_datetime) range, labeled using the time_range_
        # start value in most of the cases.
        #
        # The only exception is the price at the market open - in this case we do not have the bar directly
        # leading up to market open time. Thus, the open price from the time range (current_datetime, time_range_end)
        # is used to denote the price.

        time_range_start = current_datetime - frequency.time_delta()
        time_range_end = current_datetime + frequency.time_delta()

        # The start date is used to download older data, in case if there is no price available currently and we are
        # interested in the last available one. Therefore, at first we look one hour in the past. If this amount of data
        # would not be sufficient, we would look up to a few days in the past.

        download_start_date = current_datetime - Frequency.MIN_60.time_delta()

        def download_prices(start_time, end_time, multiple_days=False):
            # Function which downloads prices for the given tickers. In case if the time range spans over multiple days
            # and thus contains at least one Market Open Event, combine the Open price for the first bar after the
            # market open with the Close prices for all other bars from this day.
            if multiple_days:
                price_fields = [PriceField.Open, PriceField.Close]
                prices = self.data_provider.get_price(tickers, price_fields,
                                                      start_time, end_time,
                                                      frequency)
                return self._data_array_to_dataframe(prices, frequency)
            else:
                return self.data_provider.get_price(tickers, PriceField.Close,
                                                    start_time, end_time,
                                                    frequency)

        # If the data contains the Market Open Price, merge the prices
        if download_start_date <= MarketOpenEvent.trigger_time(
        ) + time_range_end <= time_range_end:
            contains_market_open = True
        elif download_start_date <= MarketOpenEvent.trigger_time(
        ) + download_start_date <= time_range_end:
            contains_market_open = True
        elif (time_range_end - download_start_date) > timedelta(days=1):
            contains_market_open = True
        else:
            contains_market_open = False

        prices_data_array = download_prices(download_start_date,
                                            time_range_end,
                                            contains_market_open)

        # Access the price bar starting at time_range_start and ending at current_datetime
        try:
            prices_series = prices_data_array.asof(time_range_start)
            prices_series.name = "Last available asset prices"

            if prices_series.isnull().values.any():
                # If any of the values is null, download more data, using a longer period of time
                raise IndexError

        except IndexError:
            # Download data using a longer period of time. In case of Monday or Tuesday, we download data from last 4
            # days in order to handle situations, were there was no price on Monday or Friday (and during the weekend).
            # In all other cases, we download data from the last 2 days.
            number_of_days_to_go_back = 2 if download_start_date.weekday(
            ) not in (0, 1) else 4
            prices_data_array = download_prices(
                download_start_date -
                RelativeDelta(days=number_of_days_to_go_back),
                time_range_end,
                multiple_days=True)

            prices_series = prices_data_array.asof(time_range_start)
            prices_series.name = "Last available asset prices"

        prices_series = cast_series(prices_series, QFSeries)
        if was_single_ticker_provided:
            return prices_series[0]
        else:
            return prices_series
コード例 #17
0
    def make_exposure_scenarios(self,
                                start_date: datetime,
                                end_date: datetime,
                                number_of_trades: int,
                                time_in_the_market: float,
                                exposure: Exposure = Exposure.LONG,
                                frequency: Frequency = Frequency.DAILY,
                                seed: int = None) -> QFSeries:
        """
        Creates a random series, which contains information about the exposure of a certain asset for the given dates
        range. Based on a.o. the total desired number of trades and average holding time of the trades, the function
        generates random trades and fills the rows for corresponding dates with the desired exposure.
        In case if the number of trades provided is too high to create non-adjacent trades, which will together occupy
        <time_in_the_market>% percentage of time, the time span between some of the consecutive trades may be set to 0.
        In that case it may seem as if the returned number of trades was smaller than the expected number of trades.

        Exemplary output for daily trading, 2 trades, time in the market = 60% and desired exposure = LONG:

        2021-10-01     Exposure.OUT
        2021-10-02    Exposure.LONG
        2021-10-03    Exposure.LONG
        2021-10-04    Exposure.LONG
        2021-10-05    Exposure.LONG
        2021-10-06     Exposure.OUT
        2021-10-07     Exposure.OUT
        2021-10-08    Exposure.LONG
        2021-10-09    Exposure.LONG
        2021-10-10     Exposure.OUT

        Parameters
        ----------
        start_date: datetime
            first date considered in the returned series
        end_date: datetime
            last date considered in the returned series
        number_of_trades: int
            total number of trades, which should be generated
        time_in_the_market: float
            total time of the ticker in the market (should be a percentage value, between 0.0 and 1.0)
        exposure: Exposure
            the desired exposure (either short or long)
        frequency: Frequency
            frequency of the trading
        seed: int
            seed used to make the scenarios deterministic

        Returns
        -------
        QFSeries
            Series indexed by dates between start_date and end_date with the given frequency
        """
        assert 0.0 <= time_in_the_market <= 1.0, "time_in_the_market should belong to the [0.0, 1.0] range"
        dates_index = date_range(start_date,
                                 end_date,
                                 freq=frequency.to_pandas_freq())
        bars_amount = dates_index.size

        bars_in_the_market = round(bars_amount * time_in_the_market)
        if number_of_trades > bars_in_the_market:
            number_of_trades = bars_in_the_market
            warnings.warn(
                f"The desired number of trades is bigger than the number of bars in the market, which equals "
                f"time_in_the_market * number of all bars between start_date and end_date. The returned "
                f"number of trades will be reduced to {number_of_trades}.")

        trades_lengths = self._get_random_integers(bars_in_the_market, number_of_trades, False, seed) \
            if bars_in_the_market > 0 else []

        # Compute the period lengths between the trades
        out_days = bars_amount - bars_in_the_market

        include_zero = out_days < number_of_trades + 1
        days_between_trades = self._get_random_integers(out_days, number_of_trades + 1, include_zero, seed) \
            if out_days > 0 else []

        # Create the timeseries with the randomly generated trading days
        exposures_list = [
            out * [Exposure.OUT] + long * [exposure] for out, long in
            zip_longest(days_between_trades, trades_lengths, fillvalue=0)
        ]
        exposures_list = [el for sublist in exposures_list for el in sublist]
        return QFSeries(exposures_list, index=dates_index)
コード例 #18
0
    def get_current_price(
            self,
            tickers: Union[Ticker, Sequence[Ticker]],
            frequency: Frequency = None) -> Union[float, QFSeries]:
        """
        Works just like get_last_available_price() but it can return NaNs if data is not available at the current
        moment (e.g. it returns NaN on a non-trading day).

        The frequency parameter is always casted into 1 minute frequency, to represent the most recent price.

        If the frequency parameter is an intraday frequency, the CLOSE price of the currently available bar will be
        returned.
        E.g. for 1 minute frequency, at 13:00 (if the market opens before 13:00), the CLOSE price of the
        12:59 - 13:00 bar will be returned.
        If "now" contains non-zero seconds or microseconds, None will be returned.

        Parameters
        -----------
        tickers: Ticker, Sequence[Ticker]
            tickers of the securities which prices should be downloaded
        frequency: Frequency
            frequency of the data

        Returns
        -------
        float, QFSeries
            current_prices series where:
            - current_prices.name contains a date of current prices,
            - current_prices.index contains tickers
            - current_prices.data contains latest available prices for given tickers
        """
        frequency = frequency or self.fixed_data_provider_frequency or Frequency.MIN_1

        if frequency <= Frequency.DAILY:
            raise ValueError(
                "The Intraday Data Handler can be used only with the Intraday Frequency"
            )

        tickers, was_single_ticker_provided = convert_to_list(tickers, Ticker)
        # if an empty tickers list was supplied then return an empty result
        if not tickers:
            return QFSeries()

        current_datetime = self.timer.now()

        # Check if the current time is at the market open, if so - take the Open price of the time range, starting
        # at current datetime
        if current_datetime + MarketOpenEvent.trigger_time(
        ) == current_datetime:
            time_range_start = current_datetime
            field = PriceField.Open
        else:
            time_range_start = current_datetime - frequency.time_delta()
            field = PriceField.Close

        prices_data_array = self.data_provider.get_price(
            tickers, field, time_range_start,
            time_range_start + frequency.time_delta(), frequency)
        try:
            # Below, the loc[time_range_start] is used instead of iloc[0], in order to return the price exactly from the
            # time_range_start, and not from the range between time_range_start and time_range_start +
            # frequency.time_delta()
            prices_series = prices_data_array.loc[time_range_start]
        except KeyError:
            prices_series = QFSeries(index=tickers)

        prices_series.name = "Current asset prices"

        prices_series = cast_series(prices_series, QFSeries)
        if was_single_ticker_provided:
            return prices_series[0]
        else:
            return prices_series