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")
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)
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)
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)
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
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)
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)
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)
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
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
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)
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)
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'))
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
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
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
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)
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