拓展 FinOL¶
FinOL 在设计时考虑了可扩展性,允许用户集成自己的模型和数据集以进行基准测试。本节提供了就如何拓展 FinOL 框架的逐步指南。
添加新的数据集¶
要将自己的数据集集成到 FinOL 中,请遵循以下步骤:
导航到
{ROOT_PATH}\data\datasets\CustomDataset目录。为不同的资产创建 .xlsx 文件,格式如下:
+------------+----------+----------+----------+----------+---------+
| DATE | OPEN | HIGH | LOW | CLOSE | VOLUME |
|------------+----------+----------+----------+----------+---------|
| 2017-11-09 | 0.025160 | 0.035060 | 0.025006 | 0.032053 | 1871620 |
| 2017-11-10 | 0.032219 | 0.033348 | 0.026450 | 0.027119 | 6766780 |
| 2017-11-11 | 0.026891 | 0.029658 | 0.025684 | 0.027437 | 5532220 |
| 2017-11-12 | 0.027480 | 0.027952 | 0.022591 | 0.023977 | 7280250 |
| 2017-11-13 | 0.024364 | 0.026300 | 0.023495 | 0.025807 | 4419440 |
| 2017-11-14 | 0.025797 | 0.026788 | 0.025342 | 0.026230 | 3033290 |
| 2017-11-15 | 0.026116 | 0.027773 | 0.025261 | 0.026445 | 6858800 |
| ...... | ...... | ...... | ...... | ...... | ...... |
| 2024-02-29 | 0.630859 | 0.705280 | 0.625720 | 0.655646 | 1639531 |
| 2024-03-01 | 0.655440 | 0.719080 | 0.654592 | 0.719080 | 9353798 |
+-----------+-----------+----------+----------+----------+---------+
对于每个资产,确保数据格式正确,没有缺失值。
Define the configuration for your custom dataset in the
{ROOT_PATH}\config.jsonfile, under theconfig["DATASET_SPLIT_CONFIG"]["CustomModel"]andconfig["NUM_DAYS_PER_YEAR"]["CustomModel"]sections. For splitting your dataset, it is recommended to use a ratio of 0.6:0.2:0.2 for training, validation, and testing datasets, respectively.
"DATASET_SPLIT_CONFIG": {
// other datasets...
"CustomDataset": {
"TRAIN_START_TIMESTAMP": "",
"TRAIN_END_TIMESTAMP": "",
"VAL_START_TIMESTAMP": "",
"VAL_END_TIMESTAMP": "",
"TEST_START_TIMESTAMP": "",
"TEST_END_TIMESTAMP": ""
}
},
"NUM_DAYS_PER_YEAR": {
// other datasets...
"CustomDataset": 252 // set an appropriate number of days per year
}
备注
与其自己定制数据集,我们建议您提出问题或通过电子邮件联系我们,以便我们进行评估,并可能将您的数据集纳入 FinOL 项目。 这将确保基准结果得到支持。
添加新的方法¶
导航到
FinOL代码库中的{ROOT_PATH}\model_layer\CustomModel.py文件。通过扩展
CustomModel类来定制你的模型。你将在这里实现自定义数据驱动 OLPS 模型的逻辑。请确保它与FinOL所定义的接口保持一致和兼容。
>>> import torch
>>> import torch.nn as nn
>>> from einops import rearrange
>>> from finol.data_layer.scaler_selector import ScalerSelector
>>> from finol.utils import load_config
>>> # User-defined model class
>>> class CustomModel(nn.Module):
>>> """
>>> Class to serve as a base neural network model for portfolio selection. This class provides users with a framework
>>> to extend and implement their own model architectures and functionality,
>>> allowing for customization to meet specific requirements and objectives in financial modeling.
>>> :param model_args: Dictionary containing model arguments, such as the number of features.
>>> :param model_params: Dictionary containing model hyper-parameters, such as the parameter1, parameter2, etc.
>>> Example:
>>> .. code:: python
>>> >>> from finol.data_layer.dataset_loader import DatasetLoader
>>> >>> from finol.model_layer.model_instantiator import ModelInstantiator
>>> >>> from finol.utils import load_config, update_config, portfolio_selection
>>> >>>
>>> >>> # Configuration
>>> >>> config = load_config()
>>> >>> config["MODEL_NAME"] = "CustomModel"
>>> >>> config["MODEL_PARAMS"]["CustomModel"]["PARAMETER1"] = 2
>>> >>> config["MODEL_PARAMS"]["CustomModel"]["PARAMETER1"] = 128
>>> >>> update_config(config)
>>> >>>
>>> >>> # Data Layer
>>> >>> load_dataset_output = DatasetLoader().load_dataset()
>>> >>>
>>> >>> # Model Layer & Optimization Layer
>>> >>> ...
>>> >>> model = ModelInstantiator(load_dataset_output).instantiate_model()
>>> >>> print(f"model: {model}")
>>> >>> ...
>>> >>> train_loader = load_dataset_output["train_loader"]
>>> >>> for i, data in enumerate(train_loader, 1):
>>> ... x_data, label = data
>>> ... final_scores = model(x_data.float())
>>> ... portfolio = portfolio_selection(final_scores)
>>> ... print(f"batch {i} input shape: {x_data.shape}")
>>> ... print(f"batch {i} label shape: {label.shape}")
>>> ... print(f"batch {i} output shape: {portfolio.shape}")
>>> ... print("-"*50)
>>> .. warning::
>>> When users define their own model, besides modifying this class, they must add different parameter keys and values
>>> in the ``config.json`` at the location ``config["MODEL_PARAMS"]["CustomModel"]``. Similarly, if users want to implement
>>> automatic hyper-parameters tuning for their custom model, they also need to specify the range and type of different
>>> parameters at ``config["MODEL_PARAMS_SPACE"]["CustomModel"]``
>>> """
>>> def __init__(self, model_args, model_params):
>>> super().__init__()
>>> self.config = load_config()
>>> self.model_args = model_args
>>> self.model_parms = model_params
>>> # Define your model architecture here
>>> def forward(self, x: torch.Tensor) -> torch.Tensor:
>>> """
>>> Forward pass of the model.
>>> :param x: Input tensor of shape ``(batch_size, num_assets, num_features_augmented)``.
>>> :return: Output tensor of shape ``(batch_size, num_assets)`` containing the predicted scores for each asset.
>>> """
>>> batch_size, num_assets, num_features_augmented = x.shape
>>> """Input Transformation"""
>>> x = x.view(batch_size, num_assets, self.model_args["window_size"], self.model_args["num_features_original"])
>>> x = rearrange(x, "b m n d -> (b m) n d")
>>> """Input Transformation"""
>>> if self.config["SCALER"].startswith("Window"):
>>> x = ScalerSelector().window_normalize(x)
>>> ...
>>> final_scores = x
>>> return final_scores
在配置
{ROOT_PATH}\config.json处的config["MODEL_PARAMS"]["CustomModel"]中定义必要的超参数。
"MODEL_PARAMS": {
// other models...
"CustomModel": {
"PARAMETER1": 4,
"PARAMETER2": 128,
// other hyper-parameters...
}
},
(可选)如果希望
FinOL自动调优自定义模型的超参数,请在config.json文件的MODEL_PARAMS_SPACE["CustomModel"]部分指定不同参数的范围。
"MODEL_PARAMS_SPACE": {
// other models...
"CustomModel": {
"PARAMETER1": {
"type": "int",
"range": [
1,
4
],
"step": 1
},
"PARAMETER2": {
"type": "int",
"range": [
32,
256
],
"step": 32
},
// other hyper-parameters...
}
}
请参考 CustomModel 中的示例实现,以获得有关自定义模型类的预期结构和接口的指导。此外,FinOL 文档提供了详细的教程和API参考,以帮助您开始。
添加新的投资准则¶
导航到
FinOL代码库中的{ROOT_PATH}\mptimization_layer\Criterion_selector.py文件。找到
CriterionSelector类,并通过改写compute_custom_criterion_loss()方法来定义自己的自定义投资准则。确保它符合FinOL定义的接口,以保持一致性和兼容性。
>>> import time
>>> import torch
>>> from finol.utils import load_config
>>> class CriterionSelector:
>>> """
>>> Class to select and compute different loss criteria for portfolio selection.
>>> """
>>> def __init__(self) -> None:
>>> self.config = load_config()
>>> self.criterion_dict = {
>>> "LogWealth": self.compute_log_wealth_loss,
>>> "LogWealthL2Diversification": self.compute_log_wealth_l2_diversification_loss,
>>> "LogWealthL2Concentration": self.compute_log_wealth_l2_concentration_loss,
>>> "L2Diversification": self.compute_l2_diversification_loss,
>>> "L2Concentration": self.compute_l2_concentration_loss,
>>> "SharpeRatio": self.compute_sharpe_ratio_loss,
>>> "Volatility": self.compute_volatility_loss,
>>> "CustomCriterion": self.compute_custom_criterion_loss,
>>> }
>>> ...
>>> def compute_custom_criterion_loss(self, portfolios: torch.Tensor, labels: torch.Tensor) -> torch.Tensor:
>>> """
>>> Compute the ``CustomCriterion`` loss, which is left for the user to define.
>>> This loss function is a placeholder for the user to implement their own custom loss criterion.
>>> :param portfolios: Portfolio weights tensor of shape (batch_size, num_assets).
>>> :param labels: Label tensor representing asset returns of shape (batch_size, num_assets).
>>> :return: ``CustomCriteria`` loss tensor, representing the user-defined loss criterion.
>>> """
>>> # This is a placeholder for the user to implement their own custom loss function.
>>> # The implementation of the custom loss function is left to the user.
>>> loss = torch.tensor(0.0, requires_grad=True)
>>> return loss
>>> def __call__(self, portfolios: torch.Tensor, labels: torch.Tensor) -> torch.Tensor:
>>> criterion_cls = self.criterion_dict.get(self.config["CRITERION_NAME"], None)
>>> if criterion_cls is None:
>>> raise ValueError(f"Invalid criterion name: {self.config['CRITERION_NAME']}. Supported criteria are: {self.criterion_dict.keys()}")
>>> return criterion_cls(portfolios, labels)