Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* My own implementation of ChangeInstrument Op. There is a newer, simpler implemenation from remote. On branch main Your branch is behind 'origin/main' by 127 commits, and can be fast-forwarded. (use "git pull" to update your local branch) Changes to be committed: modified: qlib/data/ops.py Changes not staged for commit: modified: qlib/contrib/evaluate.py modified: qlib/contrib/strategy/signal_strategy.py modified: qlib/utils/__init__.py modified: qlib/workflow/cli.py modified: qlib/workflow/expm.py Untracked files: .idea/ ------------------------ >8 ------------------------ Do not modify or remove the line above. Everything below it will be ignored. diff --git a/qlib/data/ops.py b/qlib/data/ops.py index bdc032c0..23db25cc 100644 --- a/qlib/data/ops.py +++ b/qlib/data/ops.py @@ -32,6 +32,90 @@ except ValueError as e: np.seterr(invalid="ignore") +#################### Change instrument ######################## +# In some case, one may want to change to another instrument when calculating, for example +# calculate beta of a stock with respect to a market index +# this would require change the calculation of features from the stock (original instrument) to +# the index (reference instrument) +# ############################# + + +class ChangeInstrument(ExpressionOps): + """Change Instrument Operator + In some case, one may want to change to another instrument when calculating, for example, to + calculate beta of a stock with respect to a market index. + This would require changing the calculation of features from the stock (original instrument) to + the index (reference instrument) + Parameters + ---------- + instrument: new instrument for which the downstream operations should be performed upon. + i.e., SH000300 (CSI300 index), or ^GPSC (SP500 index). + + feature: the feature to be calculated for the new instrument. + Returns + ---------- + Expression + feature operation output + """ + + def __init__(self, instrument, feature): + self.instrument = instrument + self.feature = feature + + def __str__(self): + return "{}({},{})".format(type(self).__name__, self.instrument, self.feature) + + def load(self, instrument, start_index, end_index, freq): + """load feature + + Parameters + ---------- + instrument : str + instrument code, however, the actual instrument loaded is self.instrument through initialization + start_index : str + feature start index [in calendar]. + end_index : str + feature end index [in calendar]. + freq : str + feature frequency. + + Returns + ---------- + pd.Series + feature series: The index of the series is the calendar index + """ + from .cache import H # pylint: disable=C0415 + + # cache + args = str(self), self.instrument, start_index, end_index, freq + if args in H["f"]: + return H["f"][args] + if start_index is not None and end_index is not None and start_index > end_index: + raise ValueError("Invalid index range: {} {}".format(start_index, end_index)) + try: + series = self._load_internal(self.instrument, start_index, end_index, freq) + except Exception as e: + get_module_logger("data").debug( + f"Loading data error: instrument={instrument}, expression={str(self)}, " + f"start_index={start_index}, end_index={end_index}, freq={freq}. " + f"error info: {str(e)}" + ) + raise + series.name = str(self) + H["f"][args] = series + return series + + def _load_internal(self, instrument, start_index, end_index, freq): + series = self.feature.load(self.instrument, start_index, end_index, freq) + return series + + def get_longest_back_rolling(self): + return self.feature.get_longest_back_rolling() + + def get_extended_window_size(self): + return self.feature.get_extended_window_size() + + #################### Element-Wise Operator #################### @@ -1541,6 +1625,7 @@ class TResample(ElemOperator): TOpsList = [TResample] OpsList = [ + ChangeInstrument, Rolling, Ref, Max, * update expm.py * removed duplicate implementation for ChangeInstrument
- Loading branch information