def get_data_cal_range(self, rtype: str = "full") -> Tuple[int, int]: """ get the calendar range The following assumptions are made 1) The frequency of the exchange in common_infra is the same as the data calendar 2) Users want the **data index** mod by **day** (i.e. 240 min) Parameters ---------- rtype: str - "full": return the full limitation of the deicsion in the day - "step": return the limitation of current step Returns ------- Tuple[int, int]: """ # potential performance issue day_start = pd.Timestamp(self.start_time.date()) day_end = epsilon_change(day_start + pd.Timedelta(days=1)) freq = self.level_infra.get("common_infra").get("trade_exchange").freq _, _, day_start_idx, _ = Cal.locate_index(day_start, day_end, freq=freq) if rtype == "full": _, _, start_idx, end_index = Cal.locate_index(self.start_time, self.end_time, freq=freq) elif rtype == "step": _, _, start_idx, end_index = Cal.locate_index(*self.get_step_time(), freq=freq) else: raise ValueError(f"This type of input {rtype} is not supported") return start_idx - day_start_idx, end_index - day_start_idx
def get_step_time(self, trade_step=None, shift=0): """ Get the left and right endpoints of the trade_step'th trading interval About the endpoints: - Qlib uses the closed interval in time-series data selection, which has the same performance as pandas.Series.loc # - The returned right endpoints should minus 1 seconds becasue of the closed interval representation in Qlib. # Note: Qlib supports up to minutely decision execution, so 1 seconds is less than any trading time interval. Parameters ---------- trade_step : int, optional the number of trading step finished, by default None to indicate current step shift : int, optional shift bars , by default 0 Returns ------- Tuple[pd.Timestamp, pd.Timestap] - If shift == 0, return the trading time range - If shift > 0, return the trading time range of the earlier shift bars - If shift < 0, return the trading time range of the later shift bar """ if trade_step is None: trade_step = self.get_trade_step() trade_step = trade_step - shift calendar_index = self.start_index + trade_step return self._calendar[calendar_index], epsilon_change( self._calendar[calendar_index + 1])
def get_data_cal_range_limit(self, rtype: str = "full", raise_error: bool = False) -> Tuple[int, int]: """ get the range limit based on data calendar NOTE: it is **total** range limit instead of a single step The following assumptions are made 1) The frequency of the exchange in common_infra is the same as the data calendar 2) Users want the index mod by **day** (i.e. 240 min) Parameters ---------- rtype: str - "full": return the full limitation of the deicsion in the day - "step": return the limitation of current step raise_error: bool True: raise error if no trade_range is set False: return full trade calendar. It is useful in following cases - users want to follow the order specific trading time range when decision level trade range is not available. Raising NotImplementedError to indicates that range limit is not available Returns ------- Tuple[int, int]: the range limit in data calendar Raises ------ NotImplementedError: If the following criteria meet 1) the decision can't provide a unified start and end 2) raise_error is True """ # potential performance issue day_start = pd.Timestamp(self.start_time.date()) day_end = epsilon_change(day_start + pd.Timedelta(days=1)) freq = self.strategy.trade_exchange.freq _, _, day_start_idx, day_end_idx = Cal.locate_index(day_start, day_end, freq=freq) if self.trade_range is None: if raise_error: raise NotImplementedError(f"There is no trade_range in this case") else: return 0, day_end_idx - day_start_idx else: if rtype == "full": val_start, val_end = self.trade_range.clip_time_range(day_start, day_end) elif rtype == "step": val_start, val_end = self.trade_range.clip_time_range(self.start_time, self.end_time) else: raise ValueError(f"This type of input {rtype} is not supported") _, _, start_idx, end_index = Cal.locate_index(val_start, val_end, freq=freq) return start_idx - day_start_idx, end_index - day_start_idx