Adaptive Deep Learning in Quant Finance with Qlib’s PyTorch AdaRNN
Introduction
AdaRNN is a specialized PyTorch model designed to adaptively learn from non-stationary financial time series—where market distributions evolve over time. Originally proposed in the paper AdaRNN: Adaptive Learning and Forecasting for Time Series, it leverages both GRU layers and transfer-loss techniques to mitigate the effects of distributional shift. This article demonstrates how AdaRNN can be applied within Microsoft’s Qlib—an open-source, AI-oriented platform for quantitative finance.
Background: AdaRNN Methodology
-
Temporal Covariate Shift (TCS)
Market factors may differ drastically from historical data. AdaRNN addresses TCS by adaptively aligning representations over time. -
Two-Phase Design
- Temporal Distribution Characterization:
Better captures distribution information in time-series data. - Temporal Distribution Matching:
Bridges the gap between older and newer data via advanced distribution alignment (e.g., MMD, CORAL, COSINE).
- Temporal Distribution Characterization:
-
Paper & Code References:
AdaRNN’s adaptability makes it relevant for financial forecasting, air-quality prediction, and activity recognition—any scenario where non-stationary data complicates model training.
Qlib Integration
Below is an excerpt from a Qlib-friendly YAML configuration. By running one command, Qlib will:
- Initialize a US market environment (
^GSPCas benchmark). - Load & transform data (e.g.
Alpha360) with specialized normalization and label dropping. - Train a custom or placeholder PyTorch “AdaRNN” model.
- Evaluate predictions via correlation metrics and backtesting.
- Generate logs for advanced debugging and iteration.
Command
qrun workflow_config_adarnn_Alpha360.yaml
YAML Snippet
qlib_init:
provider_uri: "/Users/vadimnicolai/Public/work/qlib-cookbook/.data/us_data"
region: us
kernels: 1
market: &market sp500
benchmark: &benchmark ^GSPC
data_handler_config: &data_handler_config
start_time: 2008-01-01
end_time: 2020-08-01
fit_start_time: 2008-01-01
fit_end_time: 2014-12-31
instruments: *market
infer_processors:
- class: RobustZScoreNorm
kwargs:
fields_group: feature
clip_outlier: true
- class: Fillna
kwargs:
fields_group: feature
learn_processors:
- class: DropnaLabel
- class: CSRankNorm
kwargs:
fields_group: label
label: ["Ref($close, -2) / Ref($close, -1) - 1"]
port_analysis_config: &port_analysis_config
strategy:
class: TopkDropoutStrategy
module_path: qlib.contrib.strategy
kwargs:
signal: <PRED>
topk: 50
n_drop: 5
backtest:
start_time: 2017-01-01
end_time: 2020-08-01
account: 100000000
benchmark: *benchmark
exchange_kwargs:
limit_threshold: 0.095
deal_price: close
open_cost: 0.0005
close_cost: 0.0015
min_cost: 5
task:
model:
# Demonstration of AdaRNN or a placeholder PyTorch model
class: DNNModelPytorch
module_path: qlib.contrib.model.pytorch_nn
kwargs:
batch_size: 1024
max_steps: 4000
loss: mse
lr: 0.002
optimizer: adam
GPU: 0
pt_model_kwargs:
input_dim: 360
dataset:
class: DatasetH
module_path: qlib.data.dataset
kwargs:
handler:
class: Alpha360
module_path: qlib.contrib.data.handler
kwargs: *data_handler_config
segments:
train: [2008-01-01, 2014-12-31]
valid: [2015-01-01, 2016-12-31]
test: [2017-01-01, 2020-08-01]
record:
- class: SignalRecord
module_path: qlib.workflow.record_temp
kwargs:
model: <MODEL>
dataset: <DATASET>
- class: SigAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
ana_long_short: False
ann_scaler: 252
- class: PortAnaRecord
module_path: qlib.workflow.record_temp
kwargs:
config: *port_analysis_config
