mlquantx 0.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mlquant/__init__.py +18 -0
- mlquant/backtest/__init__.py +14 -0
- mlquant/backtest/engine.py +118 -0
- mlquant/backtest/metrics.py +95 -0
- mlquant/cli/__init__.py +1 -0
- mlquant/cli/main.py +292 -0
- mlquant/configs/__init__.py +1 -0
- mlquant/configs/small.yaml +41 -0
- mlquant/data/__init__.py +88 -0
- mlquant/data/akshare_loader.py +168 -0
- mlquant/data/baostock_loader.py +137 -0
- mlquant/data/loaders.py +125 -0
- mlquant/data/panel.py +206 -0
- mlquant/data/synthetic.py +138 -0
- mlquant/data/yfinance_loader.py +101 -0
- mlquant/features/__init__.py +42 -0
- mlquant/features/_factors_add.py +288 -0
- mlquant/features/_factors_alpha101.py +98 -0
- mlquant/features/_factors_best.py +177 -0
- mlquant/features/_factors_better.py +385 -0
- mlquant/features/_factors_change.py +53 -0
- mlquant/features/_factors_extra.py +147 -0
- mlquant/features/_factors_market.py +61 -0
- mlquant/features/_factors_old.py +563 -0
- mlquant/features/_factors_original.py +215 -0
- mlquant/features/_factors_stock.py +270 -0
- mlquant/features/alpha101.py +84 -0
- mlquant/features/bias.py +81 -0
- mlquant/features/legacy_factors.py +126 -0
- mlquant/features/market_breadth.py +190 -0
- mlquant/features/neutralize.py +75 -0
- mlquant/features/target.py +62 -0
- mlquant/features/tensor_factors.py +301 -0
- mlquant/features.py +613 -0
- mlquant/models/__init__.py +8 -0
- mlquant/models/losses.py +88 -0
- mlquant/models/nets.py +62 -0
- mlquant/portfolio/__init__.py +5 -0
- mlquant/portfolio/frontier.py +49 -0
- mlquant/portfolio/markowitz.py +144 -0
- mlquant/training/__init__.py +6 -0
- mlquant/training/augment.py +89 -0
- mlquant/training/dataset.py +50 -0
- mlquant/training/trainer.py +108 -0
- mlquant/utils/__init__.py +5 -0
- mlquant/utils/config.py +60 -0
- mlquant/utils/seed.py +35 -0
- mlquantx-0.2.5.dist-info/METADATA +108 -0
- mlquantx-0.2.5.dist-info/RECORD +54 -0
- mlquantx-0.2.5.dist-info/WHEEL +5 -0
- mlquantx-0.2.5.dist-info/entry_points.txt +2 -0
- mlquantx-0.2.5.dist-info/licenses/LICENSE +21 -0
- mlquantx-0.2.5.dist-info/licenses/NOTICE.md +5 -0
- mlquantx-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""extra_* family: turnover + amount features (14 factors).
|
|
2
|
+
|
|
3
|
+
Ported from legacy Feature.py extra_001-extra_014.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
from ..data.panel import Panel
|
|
10
|
+
from .tensor_factors import (
|
|
11
|
+
cs_rank, delay, delta, ewma, ts_max, ts_mean, ts_min, ts_rank, ts_std, ts_sum,
|
|
12
|
+
)
|
|
13
|
+
from .legacy_factors import register_legacy_factor, _close_loc, _amount
|
|
14
|
+
|
|
15
|
+
Tensor = torch.Tensor
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@register_legacy_factor("extra_001")
|
|
19
|
+
def extra_001(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
20
|
+
"""Delta(close_loc, 1)."""
|
|
21
|
+
cl = -_close_loc(panel)
|
|
22
|
+
return delta(cl, panel.mask, 1)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@register_legacy_factor("extra_002")
|
|
26
|
+
def extra_002(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
27
|
+
"""rank(max(vwap-close, 3)) + rank(min(vwap-close, 3)) * rank(Δvolume, 3)."""
|
|
28
|
+
spread = panel.vwap - panel.close
|
|
29
|
+
mx, mm1 = ts_max(spread, panel.mask, 3)
|
|
30
|
+
mn, mm2 = ts_min(spread, panel.mask, 3)
|
|
31
|
+
dv, dm = delta(panel.volume, panel.mask, 3)
|
|
32
|
+
m = mm1 & mm2 & dm
|
|
33
|
+
mx_r, _ = cs_rank(mx, m)
|
|
34
|
+
mn_r, _ = cs_rank(mn, m)
|
|
35
|
+
dv_r, _ = cs_rank(dv, m)
|
|
36
|
+
out = mx_r + mn_r * dv_r
|
|
37
|
+
return out * m.float(), m
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@register_legacy_factor("extra_003")
|
|
41
|
+
def extra_003(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
42
|
+
"""open / delay(close, 1) - 1 (overnight gap)."""
|
|
43
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
44
|
+
out = panel.open / prev.clamp_min(1e-9) - 1.0
|
|
45
|
+
return out * pm.float(), pm
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register_legacy_factor("extra_004")
|
|
49
|
+
def extra_004(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
50
|
+
"""Volume-weighted close-loc EWMA difference (AD oscillator)."""
|
|
51
|
+
cl = _close_loc(panel)
|
|
52
|
+
data = panel.volume * cl
|
|
53
|
+
x, m1 = ewma(data, panel.mask, alpha=2.0 / 9.0)
|
|
54
|
+
y, m2 = ewma(data, panel.mask, alpha=2.0 / 4.0)
|
|
55
|
+
m = m1 & m2
|
|
56
|
+
return (x - y) * m.float(), m
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@register_legacy_factor("extra_005")
|
|
60
|
+
def extra_005(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
61
|
+
"""Amount surge: amount / ts_mean(amount, 20) - 1."""
|
|
62
|
+
amt = _amount(panel)
|
|
63
|
+
avg, am = ts_mean(amt, panel.mask, 20)
|
|
64
|
+
out = amt / avg.clamp_min(1.0) - 1.0
|
|
65
|
+
return out * am.float(), am
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@register_legacy_factor("extra_006")
|
|
69
|
+
def extra_006(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
70
|
+
"""cs_rank of volume / mean(volume, 20)."""
|
|
71
|
+
avg, am = ts_mean(panel.volume, panel.mask, 20)
|
|
72
|
+
ratio = panel.volume / avg.clamp_min(1.0)
|
|
73
|
+
return cs_rank(ratio * am.float(), am)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@register_legacy_factor("extra_007")
|
|
77
|
+
def extra_007(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
78
|
+
"""ewma(high-low, 2/5) / ewma(ewma(high-low, 2/5), 2/20) - range expansion."""
|
|
79
|
+
rng = panel.high - panel.low
|
|
80
|
+
sma1, m1 = ewma(rng, panel.mask, alpha=2.0 / 5.0)
|
|
81
|
+
sma2, m2 = ewma(sma1, m1, alpha=2.0 / 20.0)
|
|
82
|
+
m = m1 & m2
|
|
83
|
+
out = sma1 / sma2.clamp_min(1e-9)
|
|
84
|
+
return out * m.float(), m
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@register_legacy_factor("extra_008")
|
|
88
|
+
def extra_008(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
89
|
+
"""sum(max(high-delay(close,1), 0), 5) / sum(max(delay(close,1)-low, 0), 10) * 100."""
|
|
90
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
91
|
+
up = torch.clamp(panel.high - prev, min=0.0)
|
|
92
|
+
down = torch.clamp(prev - panel.low, min=0.0)
|
|
93
|
+
s_up, um = ts_sum(up, pm, 5)
|
|
94
|
+
s_down, dm = ts_sum(down, pm, 10)
|
|
95
|
+
m = um & dm
|
|
96
|
+
out = s_up * 100.0 / s_down.clamp_min(1e-9)
|
|
97
|
+
return out * m.float(), m
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@register_legacy_factor("extra_009")
|
|
101
|
+
def extra_009(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
102
|
+
"""Conditional volatility: ewma(std(close,20) * (close<=delay(close,1)), 1/5) - ewma(..., 1/20)."""
|
|
103
|
+
sd, sm = ts_std(panel.close, panel.mask, 20)
|
|
104
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
105
|
+
cond = (panel.close <= prev).float() * pm.float()
|
|
106
|
+
signal = sd * cond
|
|
107
|
+
m = sm & pm
|
|
108
|
+
ew1, m1 = ewma(signal, m, alpha=1.0 / 5.0)
|
|
109
|
+
ew2, m2 = ewma(signal, m, alpha=1.0 / 20.0)
|
|
110
|
+
m_out = m1 & m2
|
|
111
|
+
return (ew1 - ew2) * m_out.float(), m_out
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@register_legacy_factor("extra_010")
|
|
115
|
+
def extra_010(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
116
|
+
"""close / delay(close, 5) - 5-day momentum."""
|
|
117
|
+
prev, pm = delay(panel.close, panel.mask, 5)
|
|
118
|
+
out = panel.close / prev.clamp_min(1e-9)
|
|
119
|
+
return out * pm.float(), pm
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@register_legacy_factor("extra_011")
|
|
123
|
+
def extra_011(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
124
|
+
"""Raw amount (turnover)."""
|
|
125
|
+
amt = _amount(panel)
|
|
126
|
+
return amt * panel.mask.float(), panel.mask
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@register_legacy_factor("extra_012")
|
|
130
|
+
def extra_012(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
131
|
+
"""high / open."""
|
|
132
|
+
out = panel.high / panel.open.clamp_min(1e-9)
|
|
133
|
+
return out * panel.mask.float(), panel.mask
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@register_legacy_factor("extra_013")
|
|
137
|
+
def extra_013(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
138
|
+
"""vwap / close."""
|
|
139
|
+
out = panel.vwap / panel.close.clamp_min(1e-9)
|
|
140
|
+
return out * panel.mask.float(), panel.mask
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@register_legacy_factor("extra_014")
|
|
144
|
+
def extra_014(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
145
|
+
"""(high - low) / close - intraday range."""
|
|
146
|
+
out = (panel.high - panel.low) / panel.close.clamp_min(1e-9)
|
|
147
|
+
return out * panel.mask.float(), panel.mask
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Market breadth signals: cs_rank_* (6 factors).
|
|
2
|
+
|
|
3
|
+
Cross-sectional rank of daily price changes relative to previous close.
|
|
4
|
+
Ported from legacy/cuda_features.py.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from typing import Tuple
|
|
8
|
+
import torch
|
|
9
|
+
|
|
10
|
+
from ..data.panel import Panel
|
|
11
|
+
from .tensor_factors import cs_rank, delay
|
|
12
|
+
from .legacy_factors import register_legacy_factor, _amount
|
|
13
|
+
|
|
14
|
+
Tensor = torch.Tensor
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@register_legacy_factor("cs_rank_close")
|
|
18
|
+
def cs_rank_close(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
19
|
+
"""Cross-sectional rank of close return."""
|
|
20
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
21
|
+
ret = (panel.close / prev.clamp_min(1e-9) - 1.0).clamp(-0.1, 0.1)
|
|
22
|
+
return cs_rank(ret, pm)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@register_legacy_factor("cs_rank_open")
|
|
26
|
+
def cs_rank_open(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
27
|
+
"""Cross-sectional rank of open gap."""
|
|
28
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
29
|
+
ret = (panel.open / prev.clamp_min(1e-9) - 1.0).clamp(-0.1, 0.1)
|
|
30
|
+
return cs_rank(ret, pm)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@register_legacy_factor("cs_rank_high")
|
|
34
|
+
def cs_rank_high(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
35
|
+
"""Cross-sectional rank of high return."""
|
|
36
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
37
|
+
ret = panel.high / prev.clamp_min(1e-9) - 1.0
|
|
38
|
+
return cs_rank(ret, pm)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@register_legacy_factor("cs_rank_low")
|
|
42
|
+
def cs_rank_low(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
43
|
+
"""Cross-sectional rank of low return."""
|
|
44
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
45
|
+
ret = panel.low / prev.clamp_min(1e-9) - 1.0
|
|
46
|
+
return cs_rank(ret, pm)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@register_legacy_factor("cs_rank_avg")
|
|
50
|
+
def cs_rank_avg(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
51
|
+
"""Cross-sectional rank of vwap return."""
|
|
52
|
+
prev, pm = delay(panel.close, panel.mask, 1)
|
|
53
|
+
ret = panel.vwap / prev.clamp_min(1e-9) - 1.0
|
|
54
|
+
return cs_rank(ret, pm)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@register_legacy_factor("cs_rank_amount")
|
|
58
|
+
def cs_rank_amount(panel: Panel) -> Tuple[Tensor, Tensor]:
|
|
59
|
+
"""Cross-sectional rank of amount."""
|
|
60
|
+
amt = _amount(panel)
|
|
61
|
+
return cs_rank(amt, panel.mask)
|