ๅทฅไธ็บงๅ ๅญ็ ๅไธๅๆตๆกๆถ ยท ไปฟ็ ง WorldQuant ๅทฅไฝๆต
v2.0.0 ๆฐ็นๆง ๐ ๆฐๅข 13 ไธช้ขๆๅปบ็ฎๅญ๏ผๆถๅบ/้ไปท/ๅจ้/ๆๆฏๆๆ ๏ผ+ fusion ๅคๅ ๅญ่ๅๆจกๅ๏ผ็ป่ฎก่ๅ / ๆบๅจๅญฆไน ่ๅ๏ผ๏ผๆฏๆไธ้ฎ่พๅบ BacktestResultใ
v2.1.0 ๆฐ็นๆง ๐ VectorEngine ๆฏๆ start_date / end_date ๅๆฐ๏ผ็ตๆดปๆงๅถๅๆต่ฏไผฐๆถ้ดๅบ้ด๏ผไธๅฝฑๅๅ ๅญๅๅฒ็ชๅฃ่ฎก็ฎ๏ผใ
V3 ๆฐ็นๆง ๐ ๆฐๅข 4 ไธช็ผฉ้็จณไปทๅ ๅญ็ฎๅญ๏ผVolSpikeใPriceVarShrinkใPriceMeanShrinkใVolSpikeStablePrice๏ผใ
- ้กน็ฎ็ฎไป
- ็ฎๅฝ็ปๆ
- ๅฟซ้ๅผๅง
- ไพ่ตๅฎ่ฃ
- ๆจกๅ่ฏฆ่งฃ
- ๆๆ ่ฏดๆ
- ไฝฟ็จ็ๅฎๆฐๆฎ
- ๅ ๅญๆ้ ็คบไพ้
- ๅธธ่ง้ฎ้ข
QuantAlpha Engine ๆฏไธไธช้ขๅ้ๅ็ ็ฉถๅ็ๅ ๅญๅๆตไธ็ซๅผๆกๆถ๏ผๆ ธๅฟ่ฎพ่ฎก็ๅฟตไปฟ็ ง WorldQuant Alpha ๅนณๅฐๅทฅไฝๆต๏ผ
- ็จๅตๅฅ็ฎๅญๆ่ฟฐๅ ๅญ้ป่พ๏ผ้ป่พๆธ ๆฐ๏ผๅฏ่ฏปๆงๅผบ
- ๅ จๅ้ๅ่ฎก็ฎ๏ผๆๆๆไฝๅบไบ pandas/numpy ็ฉ้ต่ฟ็ฎ๏ผๆ ๆพๅผ่ก็ฅจๅพช็ฏ
- ไธฅๆ ผ้ฒๆญขๆชๆฅๅฝๆฐ๏ผLook-ahead bias๏ผ๏ผT ๆฅๅ ๅญๅผ โ T+1 ๆฅๅฎ้ ๆถ็
- ๆฏๆๅ็ใๆถจ่ทๅ่ชๅจ่ฟๆปค๏ผๆฃ้ค็ๅฎไบคๆๆๆฌ
- ไธ่กไปฃ็
.run()ๅพๅฐๅฎๆด็ปฉๆๆฅๅ
ๆ็ฎไฝฟ็จๆต็จ๏ผ
from quant_alpha_engine import MockDataGenerator
from quant_alpha_engine.ops import AlphaOps as op
from quant_alpha_engine.backtest import VectorEngine
# 1. ๅๅคๆฐๆฎ
data = MockDataGenerator(n_stocks=100, n_days=504).generate()
# 2. ็จ็ฎๅญๆ้ ๅ ๅญ๏ผๆฏๆไปปๆๅตๅฅ๏ผ
factor = op.Rank(op.Ts_Delta(data.close, 20))
# 3. ไธ่ก่ฐ็จๅๆต
result = VectorEngine(
factor=factor, close=data.close,
is_suspended=data.is_suspended, is_limit=data.is_limit
).run()
# 4. ๆฅ็็ปๆ
result.print_summary() # ๆงๅถๅฐๆๅฐๆๆ ่กจๆ ผ
result.plot() # ็ๆ 6 ๅญๅพๅๆๆฅๅfree_market_data/ # ็ฌ็ซๅ
่ดนๆฐๆฎๆๅก๏ผไธ่ฆๅ quant_alpha_engine
โโโ store.py # FreeMarketDataStore๏ผๅฏไธๆจ่็จๆทๅ
ฅๅฃ
โโโ schema.py # TSV ๅญๆฎตๆ่ฟฐๅ้ป่ฎคๅญๆฎต
โโโ symbols.py # ่ก็ฅจไปฃ็ ๆ ๅๅ
โโโ providers/ # akshare / baostock / Yahoo / ่
พ่ฎฏ / ้ช็ provider
quant_alpha_engine/
โโโ __init__.py # ็ปไธๅฏผๅบ๏ผไธ่กๅฏผๅ
ฅๆๆๆจกๅ๏ผv2.0.0๏ผ
โโโ data/
โ โโโ mock_generator.py # ๆจกๆๆฐๆฎ็ๆๅจ
โโโ ops/
โ โโโ alpha_ops.py # ็ฎๅญๅบ๏ผv1: 13ไธช + v2: 13ไธชๆฐ็ฎๅญ + V3: 4ไธช็ผฉ้็จณไปท็ฎๅญ๏ผ
โโโ backtest/
โ โโโ performance.py # ็ปฉๆๆๆ ่ฎก็ฎ
โ โโโ vector_engine.py # ็ฉ้ตๅผๅๅผๅๆตๅผๆ
โโโ fusion/ # ๐ v2.0 ๅคๅ ๅญ่ๅๆกๆถ
โ โโโ __init__.py # ๅฏผๅบ Labeler / StatisticalCombiner / MLCombiner
โ โโโ labeler.py # ๅๅๆถ็็ๆ ็ญพ็ๆๅจ
โ โโโ combiner.py # ็ป่ฎก่ๅ + ML ่ๅๅฎ็ฐ
โโโ visualization/
โโโ report.py # Matplotlib 6ๅญๅพๆฅๅ๏ผๆฏๆไธญๆๅญไฝ่ชๅจๆฃๆต๏ผ
QuantAlpha_Demo.ipynb # v1 Jupyter ๆผ็คบ
QuantAlpha_Demo_V2.ipynb # ๐ v2 Jupyter ๆผ็คบ๏ผๆจ่ๅ
ฅๅฃ๏ผๅซ่ๅๆกๆถ๏ผ
QuantAlpha_Demo_V3.ipynb # ๐ V3 Jupyter ๆผ็คบ๏ผ็ผฉ้็จณไปทๅ ๅญ๏ผ
demo.py # Python ่ๆฌ็ๆผ็คบ
requirements.txt # ไพ่ตๅ่กจ
FACTORS.md # ๐ ๅ ๅญ็ฎๅญๅฎๆดๅ่ๆๆกฃ๏ผ็ฌ็ซๆไปถ๏ผ
ๆนๅผไธ๏ผๆจ่๏ผ๏ผ็ดๆฅๆๅผ V2 Jupyter Notebook
jupyter notebook QuantAlpha_Demo_V2.ipynbๅ ๅซๆๆ v2.0 ๆฐ็นๆงๆผ็คบ๏ผ13 ไธชๆฐ็ฎๅญ + ๅคๅ ๅญ่ๅๅฎๆดๆต็จ๏ผใ
ๆนๅผไบ๏ผๆๅผ V3 Jupyter Notebook๏ผ็ผฉ้็จณไปทๅ ๅญ๏ผ
jupyter notebook QuantAlpha_Demo_V3.ipynbๆนๅผไธ๏ผๆๅผ V1 Jupyter Notebook๏ผๅบ็กๅ่ฝ๏ผ
jupyter notebook QuantAlpha_Demo.ipynbๆนๅผๅ๏ผ่ฟ่ก Python ่ๆฌ
python demo.pypip install -r requirements.txt| ไพ่ตๅ | ๆไฝ็ๆฌ | ็จ้ |
|---|---|---|
| numpy | 1.24 | ็ฉ้ต่ฟ็ฎ |
| pandas | 2.0 | DataFrame ๆ ธๅฟๆไฝ |
| matplotlib | 3.7 | ๅพ่กจ็ปๅถ |
| seaborn | 0.12 | ็ญๅๅพ๏ผๅฏ้๏ผๆ ๅ้็บง๏ผ |
| scipy | 1.10 | KDEใๆญฃๆๆๅใOLSใmin-variance ไผๅ |
| scikit-learn | 1.3 | ๐ MLCombiner ๆบๅจๅญฆไน ่ๅ๏ผๅฟ ้๏ผ |
| akshare | 1.12 | A ่กๆฅ็บฟใๅ้ใๅฎๆถ่กๆ ๅ ่ดนๆฐๆฎๆบ |
| baostock | 0.8.9 | A ่กๆฅ็บฟๅไผฐๅผๅญๆฎตๅค็จๆฐๆฎๆบ |
| yfinance | 0.2.36 | Yahoo Finance ๆฅ็บฟใๅ้ใๅฎๆถๅค็จๆฐๆฎๆบ |
ๅฏ้ไพ่ต๏ผ ่ฅ้ไฝฟ็จ
MLCombiner(model_type='xgboost')๏ผ้ขๅคๅฎ่ฃ ๏ผpip install xgboost>=1.7.0ๆชๅฎ่ฃ ๆถ่ชๅจ้็บงไธบ
random_forest๏ผไธๅฝฑๅๅ ถไปๅ่ฝใ
ไธญๆๅญไฝ๏ผ
result.plot()ไผ่ชๅจๆฃๆต็ณป็ปไธญ็ CJK ๅญไฝ๏ผๅพฎ่ฝฏ้ ้ปใSimHeiใPingFang SC ็ญ๏ผใ่ฅๆฃๆตไธๅฐ๏ผไธญๆๆ ็ญพๅฐๆพ็คบไธบๆนๅ๏ผไธๅฝฑๅๆฐๅผๅฑ็คบใ
ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine import MockDataGenerator
็จไบๅจๆฒกๆ็ๅฎๆฐๆฎๆถๅฟซ้็ๆๆต่ฏ็จๅธๅบๆฐๆฎใๅบไบ**ๅ ไฝๅธๆ่ฟๅจ๏ผGBM๏ผ**ๅ ๅ ่กไธๅ ฑๅๅ ๅญ๏ผๆจกๆ็ๅฎๅธๅบ็่กไธ็ธๅ ณ็ปๆใๅ็ใๆถจ่ทๅ็ญ็นๅพใ
MockDataGenerator(
n_stocks = 100, # ่ก็ฅจๆฐ้
n_days = 504, # ไบคๆๆฅๆปๆฐ๏ผ504 โ 2ๅนด๏ผ
n_industries = 10, # ่กไธๆฐ้
start_date = '2022-01-01', # ่ตทๅงๆฅๆ๏ผ่ทณ่ฟๅจๆซ๏ผ
seed = 42, # ้ๆบ็งๅญ๏ผNone ๅๆฏๆฌก็ปๆไธๅ
mu = 0.08, # ๆๆ่ก็ฅจๅนดๅๆผ็งป็็ๅบๅๅผ
sigma = 0.30, # ๆๆ่ก็ฅจๅนดๅๆณขๅจ็็ๅบๅๅผ
suspended_ratio = 0.04, # ๆๅ็ๅๅฒ็่ก็ฅจๅ ๆฏ๏ผ็บฆ4%๏ผ
limit_pct = 0.099, # ๆถจ่ทๅ่งฆๅ้ๅผ๏ผ|ๆถจ่ทๅน
| >= 9.9%๏ผ
)| ๅๆฐ | ็ฑปๅ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|---|
n_stocks |
int | 100 | ็ๆ็่ก็ฅจๆฐ้ใ่ถๅคง๏ผๆช้ข่ฆ็่ถๅ จ๏ผไฝ่ฟ็ฎ็ฅๆ ข |
n_days |
int | 504 | ไบคๆๆฅๆฐใ252 โ 1ๅนด๏ผ504 โ 2ๅนด๏ผ756 โ 3ๅนด |
n_industries |
int | 10 | ่กไธๆฐ้ใๆฏไธช่กไธ่ณๅฐๅ้ 2 ๅช่ก็ฅจ |
start_date |
str | '2022-01-01' |
่ตทๅงๆฅๆใ่ชๅจ่ทณ่ฟๅจๆซ๏ผไธๅค็่ๅๆฅ |
seed |
int | None | 42 | ๅบๅฎ้ๆบ็งๅญไฟ่ฏๅค็ฐๆง๏ผ่ฎพไธบ None ๆฏๆฌก้ๆบ |
mu |
float | 0.08 | ๅนดๅๆผ็งป็ๅบๅ๏ผๆฏๅช่ก็ฅจๅจๆญคๅบ็กไธ ยฑ4% ้ๆบๅ็งป๏ผ |
sigma |
float | 0.30 | ๅนดๅๆณขๅจ็ๅบๅ๏ผๆฏๅช่ก็ฅจๅจๆญคๅบ็กไธ ยฑ10% ้ๆบๅ็งป๏ผ |
suspended_ratio |
float | 0.04 | ๅๅฒๅ็่ก็ฅจๅ ๆฏ๏ผๆฏๅชๅ็่กๆ 1~3 ๆฎต่ฟ็ปญๅ็ๆ |
limit_pct |
float | 0.099 | ๆถจ่ทๅๅคๆญ้ๅผ๏ผA่ก้ๅธธไธบ 0.099๏ผ9.9%๏ผ |
data = gen.generate() # ็ๆๅนถ่ฟๅ MockData ๅฏน่ฑกgenerate() ่ฟๅไธไธช MockData ๆฐๆฎ็ฑป๏ผๅ
ๅซไปฅไธๅญๆฎต๏ผ
| ๅญๆฎต | ็ฑปๅ | ๅฝข็ถ | ่ฏดๆ |
|---|---|---|---|
data.close |
DataFrame | T ร N | ๆถ็ไปท๏ผๅ็ๆ้ดไธบ NaN |
data.open |
DataFrame | T ร N | ๅผ็ไปท |
data.high |
DataFrame | T ร N | ๆ้ซไปท |
data.low |
DataFrame | T ร N | ๆไฝไปท |
data.volume |
DataFrame | T ร N | ๆไบค้๏ผๆ๏ผ100่ก/ๆ๏ผ๏ผไธๆณขๅจๆญฃ็ธๅ ณ |
data.industry |
Series | N | ่กไธๆ ๅฐ๏ผindex=่ก็ฅจไปฃ็ , values=่กไธๅ็งฐ |
data.is_suspended |
DataFrame | T ร N | ๅ็็ฉ้ต๏ผTrue ่กจ็คบๅฝๆฅๅ็ |
data.is_limit |
DataFrame | T ร N | ๆถจ่ทๅ็ฉ้ต๏ผTrue ่กจ็คบ่งฆๅๆถจๅๆ่ทๅ |
ๆๆ DataFrame ็ index ไธบ DatetimeIndex๏ผไบคๆๆฅ๏ผ๏ผcolumns ไธบ่ก็ฅจไปฃ็ ๏ผๅฆ SH600000๏ผใ
from quant_alpha_engine import MockDataGenerator
# ๅบ็ก็จๆณ
gen = MockDataGenerator(n_stocks=100, n_days=504, seed=42)
data = gen.generate()
close = data.close # ๆถ็ไปท็ฉ้ต
volume = data.volume # ๆไบค้็ฉ้ต
industry = data.industry # ่กไธๆ ๅฐ Series
is_susp = data.is_suspended # ๅ็็ฉ้ต
is_limit = data.is_limit # ๆถจ่ทๅ็ฉ้ต
# ๆฅ็ๆฐๆฎ
print(data.close.shape) # (504, 100)
print(data.close.index[:3]) # DatetimeIndex ๅ3ไธชไบคๆๆฅ
print(data.industry.value_counts()) # ๅ่กไธ่ก็ฅจๆฐ้ๅๅธ
# ็ๅธๆจกๆ๏ผ้ซๆณขๅจใ่ดๆผ็งป๏ผ
bear_gen = MockDataGenerator(mu=-0.05, sigma=0.45, seed=100)
bear_data = bear_gen.generate()ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.ops import AlphaOps as op
ๆๆ็ฎๅญๅไธบ้ๆๆนๆณ๏ผ่พๅ
ฅ่พๅบๅไธบ pd.DataFrame๏ผIndex=ๆถ้ด๏ผColumns=่ก็ฅจ๏ผ๏ผๆฏๆไปปๆๅตๅฅ็ปๅใ
๐ ๅ ๅญ็ฎๅญๅฎๆดๅ่ๆๆกฃ๏ผๅๆฐ่ฏฆ่งฃ / ๅ ฌๅผ / ็คบไพไปฃ็ ๏ผ่ฏท่ง๏ผFACTORS.md
| ็ฑปๅซ | ็ฎๅญ | ็ๆฌ | ็ฎไป |
|---|---|---|---|
| ๆถๅบ็ฑป | Ts_Sum |
v1 | ๆปๅจๆฑๅ |
Ts_Mean |
v1 | ๆปๅจๅๅผ | |
Ts_Max |
v1 | ๆปๅจๆๅคงๅผ | |
Ts_Min |
v1 | ๆปๅจๆๅฐๅผ | |
Ts_Std |
v1 | ๆปๅจๆ ๅๅทฎ | |
Ts_Delta |
v1 | N ๆฅไปทๆ ผๅๅ้ | |
Ts_Delay |
v1 | N ๆฅๅปถ่ฟ๏ผๅนณ็งป๏ผ | |
Ts_Rank |
v1 | ๆถๅบ็พๅไฝๆๅ | |
Ts_Corr |
v1 | ๆปๅจ Pearson ็ธๅ ณ็ณปๆฐ | |
| ๆช้ข็ฑป | Rank |
v1 | ๆช้ข็พๅๆฏๆๅ [0,1] |
ZScore |
v1 | ๆช้ข Z-Score ๆ ๅๅ | |
Scale |
v1 | ๆช้ข็ปๅฏนๅผๅฝไธๅ | |
| ็นๆฎ็ฑป | Decay_Linear |
v1 | ็บฟๆง่กฐๅ็งปๅจๅนณๅ๏ผๅนณๆปไฟกๅท๏ผ |
Neutralize |
v1 | OLS ่กไธไธญๆงๅ | |
| ๆถๅบ็ป่ฎก ๐ | Ts_Skew |
v2 | ๆปๅจๅๅบฆ๏ผๅๅธๅฐพ้จ้ฃ้ฉ๏ผ |
Ts_Kurt |
v2 | ๆปๅจๅณฐๅบฆ๏ผๆ็ซฏ่ทณ่ท้ฃ้ฉ๏ผ | |
Ts_Autocorr |
v2 | ๆปๅจ่ช็ธๅ ณ็ณปๆฐ๏ผ่ถๅฟ/ๅ่ฝฌ๏ผ | |
Ts_Hurst |
v2 | Hurst ๆๆฐ๏ผ้ฟ่ฎฐๅฟๆง๏ผ | |
| ้ไปทๅ ๅญ ๐ | VWAP |
v2 | ๆไบค้ๅ ๆๅไปท |
VWAP_Bias |
v2 | VWAP ไน็ฆป็ | |
PVDeviation |
v2 | ้ไปทๅ็ฆปๅบฆ๏ผๆ ๅๅ๏ผ | |
Amihud |
v2 | Amihud ้ๆตๅจๆงๆๆ | |
| ๅจ้ๅ ๅญ ๐ | RiskAdjMomentum |
v2 | ้ฃ้ฉ่ฐๆดๅจ้๏ผๅจ้ๅคๆฎ๏ผ |
PricePathQuality |
v2 | ไปทๆ ผ่ทฏๅพ่ดจ้๏ผ่ถๅฟ็บฟๆงๅบฆ๏ผ | |
RangeBreakout |
v2 | ๅบ้ด็ช็ ดไฝ็ฝฎ [0,1] | |
| ๆๆฏๆๆ ๐ | RSI |
v2 | ็ธๅฏนๅผบๅผฑๆๆฐ [0,100] |
KDJ |
v2 | KDJ ๆๆ K ๅผ | |
MACD |
v2 | MACD ๆฑ็ถๅพ๏ผDIF-DEA๏ผ | |
| ็ผฉ้็จณไปท ๐ | VolSpike |
V3 | ๆไบค้ๅผๅจๅผบๅบฆ |
PriceVarShrink |
V3 | ็ผฉ้ไปทๆ ผๆณขๅจๆถ็ผฉ | |
PriceMeanShrink |
V3 | ็ผฉ้ไปทๆ ผ่ถๅฟๅนณๅฆ | |
VolSpikeStablePrice |
V3 | ็ผฉ้็จณไปท็ปผๅ่ฏๅ๏ผๆจ่๏ผ |
from quant_alpha_engine.ops import AlphaOps as op
# ๆถๅบ + ๆช้ข็ปๅ๏ผๆๅธธ่งๆจกๅผ๏ผ
factor = op.Rank(op.Ts_Delta(data.close, 5))
# ้ไปท่็ฆป + ่กไธไธญๆงๅ
factor_vp = op.Neutralize(
op.Rank(-op.Ts_Corr(data.volume, data.close, 10)),
data.industry
)
# V3 ็ผฉ้็จณไปทๆ ธๅฟๅ ๅญ
factor_vss = op.Rank(
op.VolSpikeStablePrice(data.close, data.volume,
price_window=10, vol_window=20)
)ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.backtest import VectorEngine
็ฉ้ตๅผๅๅผๅๆตๅผๆ๏ผๆฅๆถๅ ๅญ็ฉ้ตๅ่กๆ ๆฐๆฎ๏ผ่พๅบๅฎๆดๅๆต็ปๆใ
VectorEngine(
factor = factor_df, # ๅฟ
ๅกซ๏ผๅ ๅญ็ฉ้ต
close = close_df, # ๅฟ
ๅกซ๏ผๆถ็ไปท็ฉ้ต
is_suspended = susp_df, # ๅฟ
ๅกซ๏ผๅ็็ฉ้ต
is_limit = limit_df, # ๅฟ
ๅกซ๏ผๆถจ่ทๅ็ฉ้ต
rebalance_freq = 1, # ่ฐไป้ข็๏ผๅคฉๆฐ๏ผ
top_n = 50, # ๆไป่ก็ฅจๆฐ
weight_method = 'equal', # ๆ้ๆนๅผ
cost_rate = 0.0015, # ๅ่พนไบคๆๆๆฌ
initial_capital = 1_000_000.0, # ๅๅง่ต้๏ผไป
ๅฑ็คบ็จ๏ผ
# โโ ๅ
็ฝฎ้ขๅค็ๅๆฐ๏ผv2.0 ๆฐๅข๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
delay = 1, # ๅ ๅญๅปถ่ฟๅคฉๆฐ๏ผ0=ไธๅปถ่ฟ๏ผ
decay = 0, # ็บฟๆง่กฐๅ็ชๅฃ๏ผ0=ไธ่กฐๅ๏ผ
industry = None, # ่กไธๆ ๅฐ๏ผNone=่ทณ่ฟไธญๆงๅ
# โโ ๅๆตๆถ้ด่ๅดๆงๅถ๏ผv2.1 ๆฐๅข๏ผโโโโโโโโโโโโโโโโโโโโโโโโโ
start_date = None, # ๅๆตๅผๅงๆฅๆ๏ผๅฆ '2022-06-01'๏ผNone=ไธ้ๅถ
end_date = None, # ๅๆต็ปๆๆฅๆ๏ผๅฆ '2023-12-31'๏ผNone=ไธ้ๅถ
)| ๅๆฐ | ็ฑปๅ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|---|
factor |
DataFrame | โ | ๅฟ ๅกซใๅ ๅญ็ฉ้ต๏ผT ร N๏ผ๏ผๅผ่ถๅคงไปฃ่กจ่ฏฅ่ก็ฅจ่ถๅบ่ฏฅ่ขซไนฐๅ ฅ |
close |
DataFrame | โ | ๅฟ ๅกซใๆถ็ไปท็ฉ้ต๏ผT ร N๏ผ๏ผ็จไบ่ฎก็ฎๆฏๆฅๆถ็็ |
is_suspended |
DataFrame | โ | ๅฟ ๅกซใๅ็็ฉ้ต๏ผT ร N๏ผbool๏ผใๅ็่ก็ฅจๅฝๆฅๆ้่ชๅจ็ฝฎ้ถ |
is_limit |
DataFrame | โ | ๅฟ ๅกซใๆถจ่ทๅ็ฉ้ต๏ผT ร N๏ผbool๏ผใๆถจ่ทๅ่ก็ฅจๅฝๆฅๆ้่ชๅจ็ฝฎ้ถ๏ผๆ ๆณๆไบค๏ผ |
rebalance_freq |
int | 1 | ่ฐไป้ข็ใ1=ๆฏๆฅ่ฐไป๏ผ5=ๆฏๅจ่ฐไป๏ผ21โๆฏๆ่ฐไปใ้่ฐไปๆฅๆไปไธๅ |
top_n |
int | 50 | ๆฏๆฌก่ฐไปๆถ๏ผๆๅ ๅญๅผๆๅๅๅ N ๅช่ก็ฅจๆไปใ่ฅๅฏไบคๆ่ก็ฅจไธ่ถณ N ๅช๏ผๅๅๅ จ้จๅฏไบคๆ่ก็ฅจ |
weight_method |
str | 'equal' |
ๆไปๆ้่ฎก็ฎๆนๅผ๏ผ่งไธๆน่ฏดๆ |
cost_rate |
float | 0.0015 | ๅ่พนไบคๆๆๆฌ็๏ผๆ็ปญ่ดน + ๆป็น๏ผใๆฏๆฌกๆขๆ็ไนฐๅ ฅๆๅๅบๅๆๆญคๆฏ็ๆฃ่ดน |
initial_capital |
float | 1,000,000 | ๅๅง่ต้๏ผไป ็จไบ็ปๆๅฑ็คบ๏ผไธๅฝฑๅๆถ็็่ฎก็ฎ |
delay |
int | 1 | ๅ ๅญๅปถ่ฟๅคฉๆฐ๏ผTs_Delay๏ผใ1 ่กจ็คบ T ๆฅๅ ๅญๅจ T+1 ๆฅๅ็ๆ๏ผ้ฒๆญขๆฅๅ
ไฝฟ็จๆชๆฅๆฐๆฎ๏ผ0 ๅ
ณ้ญๅปถ่ฟ |
decay |
int | 0 | ็บฟๆง่กฐๅ็ชๅฃ๏ผDecay_Linear๏ผใๅฏนๅ ๅญๅ็บฟๆงๅ ๆ็งปๅจๅนณๅ๏ผๅนณๆปไฟกๅทใ้ไฝๆขๆ็๏ผ0 ่ทณ่ฟ |
industry |
Series/DataFrame/None | None | ่กไธๆ ๅฐ๏ผ่ก็ฅจ โ ่กไธๆ ็ญพ๏ผ๏ผไผ ๅ
ฅๅ่ชๅจๅฏนๅ ๅญๅ OLS ่กไธไธญๆงๅ๏ผNeutralize๏ผ๏ผNone ่ทณ่ฟ |
start_date |
str/None | None | ๅๆต่ฏไผฐ่ตทๅงๆฅๆ๏ผๅฆ '2022-06-01'๏ผใไธๅฝฑๅๅ ๅญๅๅฒ็ชๅฃ่ฎก็ฎ๏ผไป
ๆชๅๅๅผๆฒ็บฟๅบ้ด๏ผNone ไธ้ๅถ |
end_date |
str/None | None | ๅๆต่ฏไผฐ็ปๆๆฅๆ๏ผๅฆ '2023-12-31'๏ผใไธๅฝฑๅๅ ๅญๅๅฒ็ชๅฃ่ฎก็ฎ๏ผไป
ๆชๅๅๅผๆฒ็บฟๅบ้ด๏ผNone ไธ้ๅถ |
PLACEHOLDER_SPLIT_MARKER
็ญไปทไบ df.shift(period)ใๅธธ็จไบๆ้ "Nๅคฉๅ็ๅผ"ๅไธ่ฎก็ฎใ
result = op.Ts_Delay(df, period)# ไธๅจๅไธๅคฉ็ไปทๆ ผ
close_5d_ago = op.Ts_Delay(data.close, 5)
# ๆ้ ็ธๅฏนๅผบๅบฆ๏ผๅฝๅไปทๆ ผ / 20ๅคฉๅไปทๆ ผ
rs = data.close / op.Ts_Delay(data.close, 20)ๅฝๅๅผๅจ่ฟๅป window ไธช่งๆตๅผไธญ็ๅไฝๆฐๆๅ๏ผ่ถ้ซ่กจ็คบๅฝๅๅคไบๅๅฒ้ซไฝ๏ผใ
result = op.Ts_Rank(df, window) # ่ฟๅๅผๅ [0, 1]| ๅๆฐ | ่ฏดๆ |
|---|---|
window |
ๅๅฒๅ็็ชๅฃ๏ผๅคฉๆฐ๏ผ |
ไธๆช้ข Rank ็ๅบๅซ๏ผ
Ts_Rank๏ผๅๅช่ก็ฅจๅจ่ช่บซๅๅฒไธญ็ๆๅ๏ผๆถ้ด็ปดๅบฆ๏ผRank๏ผๆๆ่ก็ฅจๅจๅไธๆช้ขๆฅๆไธญ็ๆๅ๏ผๆช้ข็ปดๅบฆ๏ผ
# ๅฝๅๆไบค้ๅจ่ฟ20ๅคฉไธญ็ๅๅฒๅไฝ๏ผ>0.8 ่กจ็คบ้่ฝๅคไบๅๅฒ้ซไฝ๏ผ
vol_rank = op.Ts_Rank(data.volume, 20)
# ไปทๆ ผๅคไบๅๅฒ้ซไฝ + ๆไบค้ไนๅคไบ้ซไฝ โ ๆพ้ๅๆฐ้ซ
breakout = op.Rank(op.Ts_Rank(data.close, 60) + op.Ts_Rank(data.volume, 20))
โ ๏ธ Ts_Rankๅ ้จไฝฟ็จrolling.apply๏ผๅฏน่ถ ๅคง็ฉ้ต๏ผ>500ๅช่ก็ฅจ๏ผ้ๅบฆ่พๆ ข๏ผๅปบ่ฎฎwindowไธ่ถ ่ฟ 60ใ
่ฎก็ฎไธคไธชๅๅฝข็ถ DataFrame ๅจๆปๅจ็ชๅฃๅ ็้ๅ Pearson ็ธๅ ณ็ณปๆฐใ
result = op.Ts_Corr(df1, df2, window) # ่ฟๅๅผๅ [-1, 1]| ๅๆฐ | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
df1 |
DataFrame | ็ฌฌไธไธช่พๅ ฅ็ฉ้ต |
df2 |
DataFrame | ็ฌฌไบไธช่พๅ ฅ็ฉ้ต๏ผ้กปไธ df1 ๅฝข็ถ็ธๅ |
window |
int | ๆปๅจ็ชๅฃๅคงๅฐ |
ไธคไธช DataFrame ้กปๆ็ธๅ็ index ๅ columns๏ผ่ฅไธไธ่ด๏ผๆกๆถ่ชๅจๅไบค้ๅฏน้ฝใ
# ่ฟๅป10ๅคฉ๏ผๆไบค้ไธไปทๆ ผ็็ธๅ
ณ็ณปๆฐ
# ๆญฃๅผ๏ผ้ไปทๅๅ๏ผๆพ้ไธๆถจ๏ผ๏ผ่ดๅผ๏ผ้ไปท่็ฆป๏ผ็ผฉ้ไธๆถจ๏ผ
vol_price_corr = op.Ts_Corr(data.volume, data.close, 10)
# ้ไปท่็ฆปๅ ๅญ๏ผ่ด็ธๅ
ณ๏ผ็ผฉ้ไธๆถจ๏ผๆๅ้ ๅ
factor = op.Rank(-vol_price_corr)
# ่กไธไธญๆงๅๅไฝฟ็จ
factor_neut = op.Neutralize(factor, data.industry)ๆช้ข็ฎๅญๅจๅไธๆฅๆ็ๆๆ่ก็ฅจไธๆไฝ๏ผๆจชๅๆไฝ๏ผ๏ผ้่ก็ฌ็ซ่ฎก็ฎใ
ๅฏนๆฏไธชๆฅๆๆช้ข๏ผๅฐๆๆ่ก็ฅจ็ๅ ๅญๅผๅ็พๅๆฏๆๅใ
result = op.Rank(df) # ่ฟๅๅผๅ [0, 1]- ๅผ่ถๅคง โ ่ฏฅ่ก็ฅจๅจๅฝๆฅๅ ๅญๅผไธญๆๅ่ถ้ ๅ
- NaN ๅผไธๅไธๆๅ๏ผๅฏนๅบไฝ็ฝฎไป่ฟๅ NaN
- ้ๅๆถ้คๅ ๅญ้็บฒ๏ผ็ปไธๅไธๅๅ ๅญ็ๅฐบๅบฆ
# ๆๅธธ็จ็็จๆณ๏ผๅฏนๅๅงๅ ๅญๅๆช้ขๆๅ๏ผๅป้คๆๅผๅฝฑๅ
raw_factor = op.Ts_Delta(data.close, 20)
factor = op.Rank(raw_factor) # ๅผๅ็ผฉๅฐ [0,1]๏ผๅๅๅๅธ
# ๅตๅฅ๏ผๅ
ๆถๅบๅค็๏ผๅๆช้ขๆๅ
factor = op.Rank(op.Ts_Mean(data.volume, 5) / op.Ts_Mean(data.volume, 20))ๅฏนๆฏไธชๆฅๆๆช้ข๏ผๅๆ ๅๅ๏ผ(value - ๆช้ขๅๅผ) / ๆช้ขๆ ๅๅทฎใ
result = op.ZScore(df) # ๆช้ขๅๅผโ0๏ผๆ ๅๅทฎโ1- ้ๅ้่ฆไฟ็ๅ ๅญ็บฟๆง็ปๆ๏ผ่ไธๆฏๆๅไฟกๆฏ๏ผ็ๅบๆฏ
- ๅฏนๆ็ซฏๅผๆๆ๏ผไธ
Rankไธๅ๏ผๆๅผไผ่ขซไฟ็๏ผ - ่ณๅฐ้่ฆ 2 ไธช้ NaN ๅผ๏ผๅฆๅ่ฟๅ NaN
# ๆ ๅๅๅ็ๆไบค้ๅ็ฆปๅบฆ
vol_zscore = op.ZScore(data.volume)
# ไธ Rank ็ๅบๅซ
factor_rank = op.Rank(raw_factor) # ๅๅๅๅธ๏ผๅฏนๆๅผไธๆๆ
factor_zscore = op.ZScore(raw_factor) # ๆญฃๆๅๅธ๏ผไฟ็ๆๅผไฟกๆฏๅฐๆฏไธชๆช้ข็ๅ ๅญ็ปๅฏนๅผไนๅ็ผฉๆพ่ณ a๏ผsum(|factor_i|) = aใ
result = op.Scale(df, a=1.0)| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
a |
1.0 | ็ฎๆ ็ปๅฏนๅผไนๅ |
ๅ
ธๅ็จ้๏ผ ๅฐๅ ๅญๅผ็ดๆฅ็จไฝๆไปๆ้๏ผๅค็ฉบ็ปๅ๏ผๆถ๏ผScale(factor, 1) ็กฎไฟๆปๆ้็ปๅฏนๅผไธบ 1๏ผๅณๅคๅคด + ็ฉบๅคดๆปๆ ๆไธบ 1ใ
# ๅฐๅ ๅญ็ดๆฅ่ฝฌไธบๅค็ฉบๆ้๏ผๆญฃๅผๅๅค๏ผ่ดๅผๅ็ฉบ๏ผๆปๆ ๆ=1๏ผ
weights_factor = op.Scale(op.ZScore(raw_factor), a=1.0)
# ๆป็ปๅฏนๆ้้ช่ฏ
print(weights_factor.abs().sum(axis=1).mean()) # โ 1.0WorldQuant ๅนณๅฐ็ๆ ธๅฟ็ฎๅญไนไธใๅฏน่ฟๅป d ๅคฉๆฐๆฎๅ็บฟๆงๅ ๆๅนณๅ๏ผ่ถ่ฟ็ๆฐๆฎๆ้่ถๅคงใ
result = op.Decay_Linear(df, d)| ๅๆฐ | ่ฏดๆ |
|---|---|
d |
่กฐๅ็ชๅฃๅคงๅฐ๏ผๅคฉๆฐ๏ผ |
ๆ้ๅ้ ๏ผ
- d ๅคฉๅ็ๆฐๆฎๆ้ไธบ 1๏ผๆๅฐ๏ผ
- d-1 ๅคฉๅๆ้ไธบ 2
- ...
- 1 ๅคฉๅๆ้ไธบ d-1
- ๅฝๅคฉๆ้ไธบ d๏ผๆๅคง๏ผ
- ๆๆๆ้ๅฝไธๅๅๆฑๅ ๆๅๅผ
ไธๆฎ้็งปๅจๅนณๅ็ๅบๅซ๏ผ
Ts_Mean(df, d)๏ผ็ญๆๅๅผ๏ผๆฏๅคฉๆ้ = 1/dDecay_Linear(df, d)๏ผ็บฟๆงๅ ๆ๏ผๆๆฐๆฐๆฎๆ้ๆ้ซ๏ผไฟกๅท่กฐๅๆดๅนณๆป
# ๅฏนๅจ้ไฟกๅทๅ็บฟๆง่กฐๅๅนณๆป๏ผๅๅฐไฟกๅทๆๅจ๏ผ
raw_signal = op.Rank(op.Ts_Delta(data.close, 5))
smooth_signal = op.Decay_Linear(raw_signal, d=5)
# ๅตๅฅ๏ผๅ
ๆช้ขๆๅ๏ผๅ็บฟๆง่กฐๅ๏ผๅ้ๆฐๆๅ
factor = op.Rank(
op.Decay_Linear(
op.Rank(op.Ts_Delta(data.close, 10)),
d=5
)
)้่ฟ OLS ๆฎๅทฎๆณ๏ผๅ้คๅ ๅญไธญ็่กไธๅ ฑๆงๆๅ๏ผไฟ็่ก็ฅจ็็บฏไธช่ก็น่ดจไฟกๅทใ
result = op.Neutralize(df, group_data)| ๅๆฐ | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
df |
DataFrame | ้่ฆไธญๆงๅ็ๅ ๅญ็ฉ้ต (T ร N) |
group_data |
Series ๆ DataFrame | ่กไธๆ ๅฐใSeries(index=่ก็ฅจไปฃ็ , values=่กไธๅ)๏ผๆจ่๏ผ |
ๅ็๏ผ ๅฏนๆฏไธชๆถ้ดๆช้ข๏ผไปฅ่กไธๅๅ้็ฉ้ตไธบ่ชๅ้ๅ OLS ๅๅฝ๏ผๅๆฎๅทฎไฝไธบไธญๆงๅๅ็ๅ ๅญๅผใๆฎๅทฎไธญไธๅซ่กไธๆดไฝ็ๆถจ่ทไฟกๆฏ๏ผๅชไฟ็ไธช่ก็ธๅฏน่กไธ็่ถ ้ข่กจ็ฐใ
้็จๅบๆฏ๏ผ
- ้ฟๅ ๅ ๅญๅคง้ๆด้ฒไบๆไธ่กไธ๏ผๅฆ็งๆ่กใ้ถ่ก่ก๏ผๅฏผ่ด็่กไธ Beta
- ไฝฟๅ ๅญๅจ่กไธๅ ้จๅๆจชๅๆฏ่พ๏ผ่้่ทจ่กไธๆฏ่พ
# ้ไปท็ธๅ
ณๅ ๅญ๏ผ่กไธไธญๆงๅ
raw_corr = op.Ts_Corr(data.volume, data.close, window=10)
factor_raw = op.Rank(-raw_corr)
# ไธญๆงๅ๏ผๆถ้ค่กไธๅ
ฑๆง
factor_neut = op.Neutralize(factor_raw, data.industry)
# ไธญๆงๅๅ็ๅๅผๆฅ่ฟ 0๏ผ่กไธๅ
ๅค็ฉบๅนณ่กก๏ผ
print(factor_neut.mean(axis=1).mean()) # โ 0ๆณจๆ๏ผ
Neutralizeๅ ้จๅซ้่กๅพช็ฏ๏ผๆฏๆฅๅไธๆฌก OLS๏ผ๏ผๅฏน่ถ ้ฟๆถ้ดๅบๅ๏ผ>2000ๅคฉ๏ผ้ๅบฆ่พๆ ขใ
v2.0 ๆฐๅข 4 ไธชๆถๅบ็ป่ฎก็ฎๅญ๏ผ็จไบๆๆไปทๆ ผ/ๆถ็ๅบๅ็้ซ้ถ็ฉๅ่ช็ธๅ ณ็ปๆใ
result = op.Ts_Skew(df, window)่ฎก็ฎ่ฟๅป window ๅคฉ็ไธ้ถๆ ๅ็ฉ๏ผๅๅบฆ๏ผใๆญฃๅๅบฆ่กจ็คบๅณๅฐพๅ๏ผๅถๆๅคงๆถจ๏ผ๏ผ่ดๅๅบฆ่กจ็คบๅทฆๅฐพๅ๏ผๅถๆๅคง่ท๏ผใ
| ๅๆฐ | ่ฏดๆ |
|---|---|
window |
ๆปๅจ็ชๅฃๅคงๅฐ๏ผๅคฉๆฐ๏ผ๏ผๅปบ่ฎฎ โฅ 10 |
่ฟๅๅผๅ๏ผ (-โ, +โ)๏ผๅ ธๅๅผๅจ [-3, 3] ไน้ดใ
daily_ret = data.close.pct_change()
# ๆปๅจๅๅบฆ๏ผ่ดๅๅบฆ โ ๅฐพ้จ้ฃ้ฉๅคง โ ไฝ้
๏ผ
factor_skew = op.Rank(-op.Ts_Skew(daily_ret, 20))result = op.Ts_Kurt(df, window)่ฎก็ฎ่ฟๅป window ๅคฉ็่ถ
้ขๅณฐๅบฆ๏ผๅ้ถ็ฉ - 3๏ผใๆญฃๅผ่กจ็คบๅฐๅณฐๅๅฐพ๏ผ่ดๅผ่กจ็คบๆๅนณ่ๅฐพใ
# ้ซๅณฐๅบฆๆๅณ็ๆ็ซฏๆถ็ๅบ็ฐ้ข็ๆด้ซ๏ผๆดๅฑ้ฉ๏ผ
factor_kurt = op.Rank(-op.Ts_Kurt(daily_ret, 20))result = op.Ts_Autocorr(df, lag, window) # ่ฟๅๅผๅ [-1, 1]่ฎก็ฎๅฝๅๅผไธ lag ๅคฉๅ็ๅผๅจ window ็ชๅฃๅ
็ๆปๅจ Pearson ็ธๅ
ณ็ณปๆฐใ
| ๅๆฐ | ่ฏดๆ |
|---|---|
lag |
ๆปๅ้ถๆฐ |
window |
ๆปๅจ็ชๅฃๅคงๅฐ |
# 1้ถ่ช็ธๅ
ณ็ณปๆฐ๏ผ>0 ่ถๅฟ๏ผ<0 ๅ่ฝฌ๏ผ
autocorr1 = op.Ts_Autocorr(daily_ret, lag=1, window=20)
# ็จ่ช็ธๅ
ณๅๅๅผๅๅฝไฟกๅท๏ผ่ด่ช็ธๅ
ณ โ ๅ่ฝฌๆบไผ
factor_mean_rev = op.Rank(-autocorr1)result = op.Ts_Hurst(df, window) # ่ฟๅๅผๅ็บฆ [0, 1]้่ฟ R/S ๅๆ๏ผๆๅทฎ/ๆ ๅๅทฎ๏ผไผฐ่ฎก่ตซๆฏ็นๆๆฐ๏ผ
- H > 0.5๏ผ่ถๅฟๆง๏ผๆ็ปญๆง๏ผๅจ้ไฟกๅทๆๆ๏ผ
- H < 0.5๏ผๅๅผๅๅฝ๏ผๅๆ็ปญๆง๏ผๅ่ฝฌไฟกๅทๆๆ๏ผ
- H โ 0.5๏ผ้ๆบๆธธ่ตฐ
hurst = op.Ts_Hurst(data.close.pct_change(), window=30)
# ่ถๅฟ่ก๏ผH ้ซ๏ผๅๅจ้๏ผๅๅผๅๅฝ่ก๏ผH ไฝ๏ผๅๅ่ฝฌ
โ ๏ธ Ts_Hurstไฝฟ็จrolling.apply๏ผๅฏนๅคง็ฉ้ต่พๆ ข๏ผๅปบ่ฎฎwindowๅจ 20~60 ไน้ดใ
v2.0 ๆฐๅข 3 ไธช้ไปท่ๅ็ฎๅญ๏ผๆๆๆไบค้ไธไปทๆ ผ็ๅพฎ่ง็ปๆๅ ณ็ณปใ
result = op.VWAP(close, volume, window)่ฎก็ฎ่ฟๅป window ๅคฉ็ๆไบค้ๅ ๆๅไปท๏ผฮฃ(P ร V) / ฮฃVใ
| ๅๆฐ | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
close |
DataFrame | ๆถ็ไปท็ฉ้ต |
volume |
DataFrame | ๆไบค้็ฉ้ต |
window |
int | ๆปๅจ็ชๅฃๅคงๅฐ |
่ฟๅ๏ผ ไธ close ๅๅฝข็ถ๏ผๅไฝไธไปทๆ ผ็ธๅใ
vwap = op.VWAP(data.close, data.volume, window=10)
# ไปทๆ ผ้ซไบ VWAP โ ๅผบๅฟ๏ผไฝไบ VWAP โ ๅผฑๅฟ
price_vs_vwap = op.Rank(data.close / vwap - 1)result = op.VWAP_Bias(close, volume, window)่กก้ๅฝๅๆถ็ไปท็ธๅฏนๆปๅจ VWAP ็็พๅๆฏๅ็ฆป๏ผClose / VWAP(window) - 1
| ๅๆฐ | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
close |
DataFrame | ๆถ็ไปท็ฉ้ต |
volume |
DataFrame | ๆไบค้็ฉ้ต |
window |
int | ่ฎก็ฎ VWAP ็ๆปๅจ็ชๅฃๅคงๅฐ |
่ฟๅๅผๅ๏ผ ๆ ็๏ผๅ ธๅ่ๅด [-0.1, 0.1]๏ผๅณ ยฑ10%๏ผใ
- > 0๏ผไปทๆ ผ้ซไบ VWAP๏ผๅคๅคดๅ ไผ๏ผๅฏ่ฝ็ธๅฏน่ถ ไนฐ๏ผ
- < 0๏ผไปทๆ ผไฝไบ VWAP๏ผ็ฉบๅคดๅ ไผ๏ผๆฝๅจๅๅผๅๅฝๆบไผ๏ผ
ไธ
PVDeviation็ๅบๅซ๏ผVWAP_Biasไฟ็ไปทๆ ผ้็บฒ๏ผ็พๅๆฏๅ็ฆป๏ผ๏ผPVDeviation็จไปทๆ ผๆ ๅๅทฎๆ ๅๅ๏ผๆ ้็บฒ๏ผ๏ผไธค่ ไฟกๆฏไบ่กฅใ
bias = op.VWAP_Bias(data.close, data.volume, window=10)
# ๅๅผๅๅฝไฟกๅท๏ผๅคงๅน
ไฝไบ VWAP โ ็ญๆๅๅผน
factor_bias_rev = op.Rank(-bias)
# ่ถๅฟไฟกๅท๏ผๆ็ปญๆญฃไน็ฆป โ ๅจ้ๅผบ
factor_bias_mom = op.Rank(bias)result = op.PVDeviation(close, volume, window)่ฎก็ฎๅฝๅไปทๆ ผ็ธๅฏน VWAP ็ๅ็ฆป็จๅบฆ๏ผๆ ๅๅ๏ผ๏ผ(close - VWAP) / rolling_std(close, window)ใ
ๅผไธบๆญฃ่กจ็คบไปทๆ ผๆพ่้ซไบ VWAP๏ผๅๅคๅ่ดต๏ผ๏ผๅผไธบ่ด่กจ็คบไปทๆ ผๆพ่ไฝไบ VWAP๏ผๆฝๅจไฝไผฐ๏ผใ
pv_dev = op.PVDeviation(data.close, data.volume, window=10)
# ่ดๅ็ฆป๏ผไปทๆ ผไฝไบ VWAP๏ผโ ๅๅผๅๅฝไนฐๅ
ฅไฟกๅท
factor_pvdev = op.Rank(-pv_dev)result = op.Amihud(close, volume, window)Amihud๏ผ2002๏ผๆๅบ็้ๆตๅจๆงๅบฆ้๏ผmean(|ๆฅๆถ็็| / ๆฅๆไบค้)๏ผๅผ่ถๅคง่กจ็คบๅไฝๆไบค้ๅฏนไปทๆ ผ็ๅฒๅป่ถๅคง๏ผๆตๅจๆง่ถๅทฎ๏ผใ
amihud = op.Amihud(data.close, data.volume, window=20)
# ้ซๆตๅจๆง่ก็ฅจ๏ผAmihud ไฝ๏ผ้ๅธธๆดๅๆบๆๅๅฅฝ
factor_liq = op.Rank(-amihud)v2.0 ๆฐๅข 3 ไธชๅจ้ๅ ๅญ็ฎๅญ๏ผๆไพๆฏ็ฎๅไปทๆ ผๅจ้ๆด็ฒพ็ป็ไฟกๅทใ
result = op.RiskAdjMomentum(close, window=20, vol_window=20)้ฃ้ฉ่ฐๆดๅ็ๅจ้ๅ ๅญ๏ผNๆฅ็ดฏ่ฎกๆถ็็ / Nๆฅๆปๅจๆณขๅจ็๏ผ็ญไปทไบๅจ้ไฟกๅท็ๅคๆฎๆฏ็ใ
| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
window |
20 | ๅจ้่ฎก็ฎๅจๆ๏ผๅคฉๆฐ๏ผ |
vol_window |
20 | ๆณขๅจ็่ฎก็ฎ็ชๅฃ๏ผๅคฉๆฐ๏ผ |
# ้ซๅคๆฎๅจ้๏ผไธไป
ๆถจไบ๏ผ่ไธๆถจๅพๅนณ็จณ
factor_ram = op.Rank(op.RiskAdjMomentum(data.close, window=20, vol_window=20))result = op.PricePathQuality(close, window) # ่ฟๅๅผๅ [-1, 1]่กก้่ฟๅป window ๅคฉไปทๆ ผ่ตฐๅฟ็ๅ่ฐๆงไธ็บฟๆงๅบฆ๏ผ
|Spearman(t, x)| ร Pearson(t, x)ยฒ
- ๅผๆฅ่ฟ 1๏ผไปทๆ ผๅ่ฐ็บฟๆงไธๆถจ๏ผ้ซ่ดจ้่ถๅฟ๏ผ
- ๅผๆฅ่ฟ 0๏ผไปทๆ ผ้่กๆ ๆนๅ
- ่ดๅผ๏ผๅ่ฐไธ่ท
# ่ทฏๅพ่ดจ้้ซ็ไธๆถจ โ ๅผบ่ถๅฟไฟกๅท
ppq = op.PricePathQuality(data.close, window=20)
factor_trend = op.Rank(ppq)result = op.RangeBreakout(close, high, low, window) # ่ฟๅๅผๅ [0, 1]ๅฝๅๆถ็ไปทๅจ่ฟๅป window ๅคฉ้ซไฝๅบ้ดไธญ็ไฝ็ฝฎ๏ผ
(close - rolling_min(low)) / (rolling_max(high) - rolling_min(low))
- ๅผๆฅ่ฟ 1๏ผๆฅ่ฟๅๅฒ้ซ็น๏ผ็ช็ ดไฟกๅท๏ผ
- ๅผๆฅ่ฟ 0๏ผๆฅ่ฟๅๅฒไฝ็น๏ผ่ถ ๅไฟกๅท๏ผ
| ๅๆฐ | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
close |
DataFrame | ๆถ็ไปท |
high |
DataFrame | ๆ้ซไปท |
low |
DataFrame | ๆไฝไปท |
breakout = op.RangeBreakout(data.close, data.high, data.low, window=20)
# ไปทๆ ผๅคไบๅบ้ด้ซไฝ โ ็ช็ ด่ถๅฟ
factor_breakout = op.Rank(breakout)v2.0 ๆฐๅข็ปๅ ธๆๆฏๅๆๆๆ ไฝไธบๅ ๅญไฟกๅท๏ผๅ จ้จๅ้ๅๅฎ็ฐ๏ผๆฏๆไปปๆ่ก็ฅจ็ฉ้ตใ
result = op.RSI(close, window=14) # ่ฟๅๅผๅ [0, 100]Wilder ๅนณๆป RSI๏ผ100 - 100 / (1 + AvgGain / AvgLoss)๏ผไฝฟ็จ ewm(span=2รwindow-1) ๅฎ็ฐใ
| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
window |
14 | RSI ่ฎก็ฎๅจๆ |
- RSI > 70๏ผ่ถ ไนฐ๏ผๅฏ่ฝๅ่ฐ๏ผ
- RSI < 30๏ผ่ถ ๅ๏ผๅฏ่ฝๅๅผน๏ผ
rsi = op.RSI(data.close, window=14)
# ่ถ
ๅๅ่ฝฌๅ ๅญ
factor_rsi = op.Rank(-(rsi - 50).abs()) # ่ท็ฆป 50 ่ถ่ฟ่ถไธญๆง
# ๆ๏ผ่ถ
ๅๅๅค
factor_oversold = op.Rank(100 - rsi)result = op.KDJ(close, high, low, n=9, m1=3, m2=3) # ่ฟๅ K ๅผ๏ผ็บฆ [0, 100]ๅบไบ RSV๏ผๆชๆ็้ๆบๅผ๏ผ็ EWM ๅนณๆป K ๅผ๏ผ
RSV = (close - Ln) / (Hn - Ln) ร 100K = EWM(RSV, m1)
| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
n |
9 | ๅบ้ดๅจๆ |
m1 |
3 | K ๅผๅนณๆป็ณปๆฐ |
m2 |
3 | D ๅผๅนณๆป็ณปๆฐ๏ผๅฝๅไป ่ฟๅ K ๅผ๏ผ |
k_val = op.KDJ(data.close, data.high, data.low, n=9)
# K ๅผ่ถ
ไนฐ่ถ
ๅ
factor_kdj = op.Rank(50 - k_val) # ไฝ K ๅผๆๅ้ ๅ๏ผ่ถ
ๅ๏ผresult = op.MACD(close, fast=12, slow=26, signal=9) # ่ฟๅ MACD ๆฑ็ถๅพ๏ผHistogram๏ผๆ ๅ MACD๏ผ
DIF = EMA(close, fast) - EMA(close, slow)DEA = EMA(DIF, signal)- ่ฟๅๆฑ็ถๅพ =
DIF - DEA๏ผๆญฃๅผไธ็ฉฟไธบไนฐๅ ฅไฟกๅท๏ผ่ดๅผไธ็ฉฟไธบๅๅบไฟกๅท๏ผ
| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
fast |
12 | ๅฟซ็บฟ EMA ๅจๆ |
slow |
26 | ๆ ข็บฟ EMA ๅจๆ |
signal |
9 | ไฟกๅท็บฟ EMA ๅจๆ |
macd_hist = op.MACD(data.close, fast=12, slow=26, signal=9)
# ๆฑ็ถๅพ็ฑ่ด่ฝฌๆญฃ โ ่ถๅฟๅ่ฝฌไฟกๅท
factor_macd = op.Rank(macd_hist)ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.backtest import VectorEngine
็ฉ้ตๅผๅๅผๅๆตๅผๆ๏ผๆฅๆถๅ ๅญ็ฉ้ตๅ่กๆ ๆฐๆฎ๏ผ่พๅบๅฎๆดๅๆต็ปๆใ
VectorEngine(
factor = factor_df, # ๅฟ
ๅกซ๏ผๅ ๅญ็ฉ้ต
close = close_df, # ๅฟ
ๅกซ๏ผๆถ็ไปท็ฉ้ต
is_suspended = susp_df, # ๅฟ
ๅกซ๏ผๅ็็ฉ้ต
is_limit = limit_df, # ๅฟ
ๅกซ๏ผๆถจ่ทๅ็ฉ้ต
rebalance_freq = 1, # ่ฐไป้ข็๏ผๅคฉๆฐ๏ผ
top_n = 50, # ๆไป่ก็ฅจๆฐ
weight_method = 'equal', # ๆ้ๆนๅผ
cost_rate = 0.0015, # ๅ่พนไบคๆๆๆฌ
initial_capital = 1_000_000.0, # ๅๅง่ต้๏ผไป
ๅฑ็คบ็จ๏ผ
# โโ ๅ
็ฝฎ้ขๅค็ๅๆฐ๏ผv2.0 ๆฐๅข๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
delay = 1, # ๅ ๅญๅปถ่ฟๅคฉๆฐ๏ผ0=ไธๅปถ่ฟ๏ผ
decay = 0, # ็บฟๆง่กฐๅ็ชๅฃ๏ผ0=ไธ่กฐๅ๏ผ
industry = None, # ่กไธๆ ๅฐ๏ผNone=่ทณ่ฟไธญๆงๅ
)| ๅๆฐ | ็ฑปๅ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|---|
factor |
DataFrame | โ | ๅฟ ๅกซใๅ ๅญ็ฉ้ต๏ผT ร N๏ผ๏ผๅผ่ถๅคงไปฃ่กจ่ฏฅ่ก็ฅจ่ถๅบ่ฏฅ่ขซไนฐๅ ฅ |
close |
DataFrame | โ | ๅฟ ๅกซใๆถ็ไปท็ฉ้ต๏ผT ร N๏ผ๏ผ็จไบ่ฎก็ฎๆฏๆฅๆถ็็ |
is_suspended |
DataFrame | โ | ๅฟ ๅกซใๅ็็ฉ้ต๏ผT ร N๏ผbool๏ผใๅ็่ก็ฅจๅฝๆฅๆ้่ชๅจ็ฝฎ้ถ |
is_limit |
DataFrame | โ | ๅฟ ๅกซใๆถจ่ทๅ็ฉ้ต๏ผT ร N๏ผbool๏ผใๆถจ่ทๅ่ก็ฅจๅฝๆฅๆ้่ชๅจ็ฝฎ้ถ๏ผๆ ๆณๆไบค๏ผ |
rebalance_freq |
int | 1 | ่ฐไป้ข็ใ1=ๆฏๆฅ่ฐไป๏ผ5=ๆฏๅจ่ฐไป๏ผ21โๆฏๆ่ฐไปใ้่ฐไปๆฅๆไปไธๅ |
top_n |
int | 50 | ๆฏๆฌก่ฐไปๆถ๏ผๆๅ ๅญๅผๆๅๅๅ N ๅช่ก็ฅจๆไปใ่ฅๅฏไบคๆ่ก็ฅจไธ่ถณ N ๅช๏ผๅๅๅ จ้จๅฏไบคๆ่ก็ฅจ |
weight_method |
str | 'equal' |
ๆไปๆ้่ฎก็ฎๆนๅผ๏ผ่งไธๆน่ฏดๆ |
cost_rate |
float | 0.0015 | ๅ่พนไบคๆๆๆฌ็๏ผๆ็ปญ่ดน + ๆป็น๏ผใๆฏๆฌกๆขๆ็ไนฐๅ ฅๆๅๅบๅๆๆญคๆฏ็ๆฃ่ดน |
initial_capital |
float | 1,000,000 | ๅๅง่ต้๏ผไป ็จไบ็ปๆๅฑ็คบ๏ผไธๅฝฑๅๆถ็็่ฎก็ฎ |
delay |
int | 1 | ๅ ๅญๅปถ่ฟๅคฉๆฐ๏ผTs_Delay๏ผใ1 ่กจ็คบ T ๆฅๅ ๅญๅจ T+1 ๆฅๅ็ๆ๏ผ้ฒๆญขๆฅๅ
ไฝฟ็จๆชๆฅๆฐๆฎ๏ผ0 ๅ
ณ้ญๅปถ่ฟ |
decay |
int | 0 | ็บฟๆง่กฐๅ็ชๅฃ๏ผDecay_Linear๏ผใๅฏนๅ ๅญๅ็บฟๆงๅ ๆ็งปๅจๅนณๅ๏ผๅนณๆปไฟกๅทใ้ไฝๆขๆ็๏ผ0 ่ทณ่ฟ |
industry |
Series/DataFrame/None | None | ่กไธๆ ๅฐ๏ผ่ก็ฅจ โ ่กไธๆ ็ญพ๏ผ๏ผไผ ๅ
ฅๅ่ชๅจๅฏนๅ ๅญๅ OLS ่กไธไธญๆงๅ๏ผNeutralize๏ผ๏ผNone ่ทณ่ฟ |
weight_method ๅฏ้ๅผ๏ผ
| ๅผ | ่ฏดๆ | ้็จๅบๆฏ |
|---|---|---|
'equal' |
Top-N ่ก็ฅจ็ญๆๆไป๏ผๆฏๅชๆ้ = 1/N | ้ป่ฎคๆจ่๏ผๆถ้ค่งๆจกๅๅทฎ |
'factor_weighted' |
Top-N ่ก็ฅจๆๅ ๅญ็ปๅฏนๅผๅ ๆ๏ผๅฝไธๅๅ๏ผ๏ผๅ ๅญๅผ่ถๅคงๆ้่ถ้ซ | ๅธๆๅ ๅญๅผบไฟกๅท่ก็ฅจ่ทๅพๆด้ซ้ ็ฝฎ |
่ฐไป้ข็ๅปบ่ฎฎ๏ผ
rebalance_freq |
็ญๆ้ข็ | ้็จๅ ๅญ็ฑปๅ |
|---|---|---|
| 1 | ๆฏๆฅ่ฐไป | ้ซ้ขๅจ้ใ้ไปทๅ ๅญ |
| 5 | ๆฏๅจ่ฐไป | ไธญ้ขๆๆฏๅ ๅญ๏ผๆจ่่ตท็น๏ผ |
| 21 | ๆฏๆ่ฐไป | ไฝ้ขๅบๆฌ้ขๅ ๅญ |
result = engine.run() # ๆง่กๅๆต๏ผ่ฟๅ BacktestResultrun() ไผ่ชๅจ๏ผ
0. ๅ ๅญ้ขๅค็๏ผv2.0 ๆฐๅข๏ผ๏ผๆ delay โ decay โ neutralize ้กบๅบๆง่กๅ
็ฝฎ้ขๅค็๏ผไปปไธๅๆฐไธบ้ป่ฎคๅผๆถ่ชๅจ่ทณ่ฟๅฏนๅบๆญฅ้ชค๏ผ
- ๅฏน้ฝๆๆ่พๅ ฅๆฐๆฎ๏ผๅๅ ฑๅๆฅๆๅ่ก็ฅจ๏ผ
- ่ฎก็ฎๅๅ 1 ๆฅๆถ็็๏ผไธฅๆ ผ้ฒๆชๆฅๅฝๆฐ๏ผ
- ๅจ่ฐไปๆฅ็ๆๆ้๏ผ่ฟๆปคๅ็/ๆถจ่ทๅ่ก็ฅจ
- ่ฎก็ฎๆฏๆฅๆขๆ็ๅไบคๆๆๆฌ
- ่ฎก็ฎๅๅผๅบๅๅๅ จ้็ปฉๆๆๆ
from quant_alpha_engine.backtest import VectorEngine
# ๅบ็ก็จๆณ๏ผๆฏๅจ่ฐไป๏ผ็ญๆๆไป30ๅช๏ผ้ป่ฎค delay=1 ่ชๅจ้ฒๆชๆฅๅฝๆฐ๏ผ
engine = VectorEngine(
factor = factor,
close = data.close,
is_suspended = data.is_suspended,
is_limit = data.is_limit,
rebalance_freq = 5,
top_n = 30,
weight_method = 'equal',
cost_rate = 0.0015,
# delay=1 ไธบ้ป่ฎคๅผ๏ผTๆฅๅ ๅญ โ T+1ๆฅ็ๆ๏ผไฟๅฎ้ฃๆง๏ผ
)
result = engine.run()
# ๅ ๅญๅ ๆ + ไฝๆขๆ๏ผๆฏๆ่ฐไป๏ผ
engine2 = VectorEngine(
factor = factor,
close = data.close,
is_suspended = data.is_suspended,
is_limit = data.is_limit,
rebalance_freq = 21,
top_n = 20,
weight_method = 'factor_weighted',
cost_rate = 0.0010, # ๆดไฝๆๆฌ๏ผๆบๆ่ดน็๏ผ
)
result2 = engine2.run()
# ๅฎๆด้ขๅค็ๆตๆฐด็บฟ๏ผdelay + decay + ่กไธไธญๆงๅ๏ผ
engine3 = VectorEngine(
factor = factor,
close = data.close,
is_suspended = data.is_suspended,
is_limit = data.is_limit,
rebalance_freq = 5,
top_n = 30,
cost_rate = 0.0015,
delay = 1, # Step1: Tๆฅๅ ๅญ โ T+1ๆฅ็ๆ
decay = 5, # Step2: ็บฟๆง่กฐๅๅนณๆป๏ผ้ๆขๆ็๏ผ
industry = data.industry, # Step3: OLS่กไธไธญๆงๅ
)
result3 = engine3.run()engine.run() ่ฟๅไธไธช BacktestResult ๆฐๆฎ็ฑป๏ผๅ
ๅซๆๆไธญ้ด็ปๆๅ็ปฉๆๆๆ ๏ผๅฏ็ดๆฅ็จไบไบๆฌกๅๆใ
| ๅญๆฎต | ็ฑปๅ | ่ฏดๆ |
|---|---|---|
result.nav |
Series | ็ญ็ฅๅๅผๅบๅ๏ผไป 1.0 ๅผๅง๏ผindex=ๆฅๆ |
result.daily_returns |
Series | ๆฃ้คไบคๆๆๆฌๅ็ๆฅๆถ็็ |
result.gross_returns |
Series | ๆฃ้คๆๆฌๅ็ๆฅๆถ็็๏ผๆฏๆถ็๏ผ |
result.cost_series |
Series | ๆฏๆฅๅฎ้ ไบคๆๆๆฌ๏ผๆขๆ็ ร cost_rate๏ผ |
result.weights |
DataFrame | ๆไปๆ้็ฉ้ต๏ผT ร N๏ผ๏ผๅทฒๅซๅๅๅกซๅ |
result.turnover |
Series | ๆฏๆฅๅ่พนๆขๆ็ = `sum( |
result.ic_series |
Series | ๆฏๆฅๆช้ข Rank IC ๅผ |
result.forward_returns |
DataFrame | ๅๅ1ๆฅๆถ็็็ฉ้ต๏ผT ร N๏ผ |
result.factor |
DataFrame | ๅฏน้ฝๅ็ๅ ๅญ็ฉ้ต๏ผT ร N๏ผ |
result.metrics |
dict | ๆๆ็ปฉๆๆๆ ๅญๅ ธ๏ผ่งไธๆนๆๆ ่ฏดๆ๏ผ |
result.rebalance_dates |
list | ๅฎ้ ่ฐไปๆฅๆๅ่กจ |
result.print_summary() # ๆงๅถๅฐๆๅฐ Unicode ๆ ผๅผๆๆ ่กจๆ ผ
result.plot() # ๅผน็ชๆพ็คบ 6 ๅญๅพๅๆๆฅๅ
result.plot(save_path='out.png') # ไฟๅญๆฅๅไธบ PNG ๆไปถ๏ผdpi=150๏ผ# ๆฅ็ๅๅผๅบๅ
result.nav.plot(title='็ญ็ฅๅๅผ')
# ๆฅ็ๆ้ๅๅธ
result.weights.sum(axis=1) # ๆฏๆฅๆปๆ้๏ผๅบๆฅ่ฟ1๏ผ
# ๆพๅฐๆไปๆๅค็่ก็ฅจ
result.weights.mean().nlargest(10) # ๅๅฒๅนณๅๆ้ๆ้ซ็10ๅช่ก็ฅจ
# ๆๅ IC ๅบๅๅ็ป่ฎก
ic = result.ic_series.dropna()
print(f"IC ๅๅผ: {ic.mean():.4f}, ICIR: {ic.mean()/ic.std()*252**0.5:.3f}")
# ่ฎฟ้ฎๅๅงๆๆ ๅญๅ
ธ
print(result.metrics['Sharpe_Ratio'])
print(result.metrics['IC_Mean'])
# ่ฎก็ฎ็ดฏ่ฎกๆๆฌ
cumcost = result.cost_series.cumsum()ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.backtest.performance import Performance
็ฌ็ซ็็ปฉๆ่ฎก็ฎๅทฅๅ ท็ฑป๏ผๆๆๆนๆณไธบ้ๆๆนๆณ๏ผๅฏๅฏนไปปๆๅบๅ่ฐ็จ๏ผไธไพ่ตๅๆตๅผๆใ
ann_ret = Performance.calc_annualized_return(nav)
# ๅ
ฌๅผ๏ผ(nav_ๆซ / nav_ๅ)^(252/T) - 1| ๅๆฐ | ่ฏดๆ |
|---|---|
nav |
ๅๅผๅบๅ๏ผpd.Series๏ผไป1.0ๅผๅง๏ผ |
่ฟๅ๏ผ float๏ผๅฆ 0.15 ่กจ็คบๅนดๅ 15%
ann_vol = Performance.calc_annualized_volatility(returns)
# ๅ
ฌๅผ๏ผstd(r) * sqrt(252)่ฟๅ๏ผ float๏ผๅฆ 0.20 ่กจ็คบๅนดๅ 20%
sharpe = Performance.calc_sharpe(returns, risk_free=0.03)
# ๅ
ฌๅผ๏ผ(mean(r) - rf/252) / std(r) * sqrt(252)| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
returns |
โ | ๆฅๆถ็็ๅบๅ |
risk_free |
0.0 | ๆ ้ฃ้ฉๅฉ็๏ผๅนดๅ๏ผ๏ผๅฆ 0.03 ่กจ็คบ 3% |
annualize |
True | ๆฏๅฆๅนดๅ |
่ฟๅ๏ผ float๏ผ่ถๅคง่ถๅฅฝ๏ผ้ๅธธ >1 ไธบไผ็ง๏ผ>2 ไธบๅ่ถ๏ผ
mdd = Performance.calc_max_drawdown(nav)
# ๅ
ฌๅผ๏ผmin((nav_t - max(nav_{0..t})) / max(nav_{0..t}))่ฟๅ๏ผ float๏ผ่ดๆฐ๏ผ๏ผๅฆ -0.15 ่กจ็คบๆๅคงๅๆค -15%
calmar = Performance.calc_calmar(nav)
# ๅ
ฌๅผ๏ผๅนดๅๆถ็็ / |ๆๅคงๅๆค|่ฟๅ๏ผ float๏ผ่กก้ๅไฝๅๆค้ฃ้ฉ่ทๅพ็ๅนดๅๆถ็๏ผ่ถๅคง่ถๅฅฝ
ic_series = Performance.calc_ic_series(factor_df, fwd_ret_df)| ๅๆฐ | ่ฏดๆ |
|---|---|
factor |
ๅ ๅญ็ฉ้ต๏ผT ร N๏ผ |
forward_returns |
ๅฏนๅบ็ๅๅๆถ็็ฉ้ต๏ผT ร N๏ผ๏ผ้กปไธ factor ๅฏน้ฝ |
IC ่ฎก็ฎๆนๅผ๏ผ ๆฏๆฅๆช้ข Rank IC = Spearman ็ธๅ ณ็ณปๆฐ๏ผๅ้ๅๅฎ็ฐ๏ผ็ญไปทไบๅฏนๆๅๅ็ๅ ๅญๅๆถ็ๅ Pearson ็ธๅ ณ๏ผ
่ฟๅ๏ผ pd.Series๏ผๆฏๆฅ็ IC ๅผ๏ผindex=ๆฅๆ
stats = Performance.calc_ic_stats(ic_series)่ฟๅๅญๅ ธ๏ผ
| Key | ่ฏดๆ |
|---|---|
IC_Mean |
IC ๅๅผ๏ผ้ๅธธไผ็งๅ ๅญ็ |
IC_Std |
IC ๆ ๅๅทฎ๏ผๅๆ IC ็จณๅฎๆง |
ICIR |
IC ไฟกๆฏๆฏ็ = IC_Mean / IC_Std ร โ252๏ผ้ๅธธ >0.5 ไธบไผ็ง |
IC_Positive_Ratio |
IC > 0 ็ๆฏไพ๏ผ่็๏ผ๏ผ้ๅธธ >55% ไธบไผ็ง |
IC_t_stat |
IC ๅๅผ็ t ็ป่ฎก้๏ผ |
turnover = Performance.calc_turnover(weights_df)
# ๅ
ฌๅผ๏ผsum(|w_t - w_{t-1}|) / 2่ฟๅ๏ผ pd.Series๏ผๆฏๆฅๅ่พนๆขๆ็๏ผ0~1ไน้ด๏ผๅฆ 0.05 ่กจ็คบ 5%๏ผ
fitness = Performance.calc_fitness(sharpe, nav, turnover)
# ๅ
ฌๅผ๏ผSharpe ร sqrt(|ๅนดๅๆถ็็| / ๆฅๅๆขๆ็)WorldQuant ็จไบ็ปผๅ่กก้ๅ ๅญๆถ็่ดจ้ ร ็จณๅฎๆง / ๆขๆๆๆฌ็็ปผๅๆๆ ใ
่ฟๅ๏ผ float๏ผ้ๅธธ >1.0 ไธบๆไปทๅผ็ๅ ๅญ
metrics = Performance.summary(
nav = nav,
daily_returns = returns,
weights = weights,
factor = factor,
forward_returns = fwd_ret,
cost_series = cost_series, # ๅฏ้
)่ฟๅๅญๅ ธ๏ผๆๆ้ฎๅ๏ผ๏ผ
{
'ๅนดๅๆถ็็': 0.1234, # ๅฆ 0.1234 ่กจ็คบ 12.34%
'ๅนดๅๆณขๅจ็': 0.0856,
'Sharpe_Ratio': 1.44,
'Calmar_Ratio': 0.88,
'ๆๅคงๅๆค': -0.1567, # ่ดๆฐ
'IC_Mean': 0.053,
'IC_Std': 0.089,
'ICIR': 1.23,
'IC_่็': 0.583, # ๅฆ 0.583 ่กจ็คบ 58.3%
'IC_t็ป่ฎก้': 3.45,
'ๆฅๅๆขๆ็': 0.056,
'ๅนดๅๆ็ปญ่ดน': 0.0123,
'Fitness': 0.89,
}from quant_alpha_engine.backtest.performance import Performance
import pandas as pd
# ๅฏน่ชๅทฑ็ๆถ็ๅบๅ่ฎก็ฎๆๆ
my_returns = pd.Series([0.001, -0.002, 0.003, ...], index=dates)
my_nav = (1 + my_returns).cumprod()
sharpe = Performance.calc_sharpe(my_returns, risk_free=0.03)
mdd = Performance.calc_max_drawdown(my_nav)
print(f"Sharpe: {sharpe:.3f}, MaxDD: {mdd*100:.1f}%")
# ่ฎก็ฎ IC
ic = Performance.calc_ic_series(my_factor, my_fwd_returns)
stats = Performance.calc_ic_stats(ic)
print(f"IC ๅๅผ: {stats['IC_Mean']:.4f}, ICIR: {stats['ICIR']:.3f}")ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.visualization.report import Report
้ๅธธ้่ฟ result.plot() ้ดๆฅ่ฐ็จ๏ผไนๅฏ็ดๆฅ่ฐ็จ Report.plot(result)ใ
Report.plot(result) # ๅผน็ชๅฑ็คบ
Report.plot(result, save_path='report.png') # ไฟๅญไธบ PNG๏ผdpi=150๏ผ
Report.plot(result, benchmark_seed=42) # ๆๅฎๅบๅ้ๆบ็งๅญ| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
result |
โ | BacktestResult ๅฏน่ฑก |
save_path |
None | ไฟๅญ่ทฏๅพ๏ผๅฆ './output/factor1.png'๏ผใไธบ None ๆถๅผน็ชๅฑ็คบ |
benchmark_seed |
2024 | ๆจกๆๅบๅ๏ผ้ๆบๆธธ่ตฐ๏ผ็้ๆบ็งๅญ๏ผๅบๅฎๅๆฏๆฌกๅบๅ็ธๅ |
โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโ
โ ๐ ๅๅผๆฒ็บฟ โ ๐๏ธ ๆๅบฆๆถ็็ญๅๅพ โ ๐ IC ๆถๅบ โ
โ ็ญ็ฅ vs ๅบๅ โ ่ก=ๅนด๏ผๅ=ๆ โ ๆญฃ็ปฟ่ด็บข + ๅๅผ็บฟ โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโค
โ ๐ ๆฅๆถ็็ๅๅธ โ ๐ ๆขๆ็ๅบๅ โ ๐ IC ๅๅธ โ
โ ็ดๆนๅพ+KDE+ๆญฃๆ โ ๆ็บฟ+ๅๅผ+่ฐไปๆฅ โ ็ดๆนๅพ+ๆญฃๆ+่็ โ
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโ
้กถ้จๆๆ ๆ่ฆๆก๏ผ8้กนๆ ธๅฟๆๆ ๏ผ
| ๅญๅพ | ๅ ๅฎน | ๅ ณ้ฎไฟกๆฏ |
|---|---|---|
| ๅๅผๆฒ็บฟ | ็ญ็ฅๅๅผ๏ผ็บข๏ผ+ ๆจกๆๅบๅ๏ผ็ฐ่็บฟ๏ผ+ ๅๆค้ดๅฝฑ | ๆๅคงๅๆคๆ ๆณจ็ฎญๅคด |
| ๆๅบฆๆถ็็ญๅๅพ | ๆฏๆๆถ็็๏ผ็บข็ปฟ้ ่ฒ๏ผ็ปฟ=ๆญฃๆถ็๏ผ็บข=่ดๆถ็๏ผ | ็ด่ง็ๅญฃ่ๆง่งๅพ |
| IC ๆถๅบ | ๆฏๆฅ IC ๆฑ็ถๅพ๏ผๆญฃ็ปฟ่ด็บข๏ผ+ IC ๅๅผ่็บฟ | ๅทฆไธ่งๆ ๆณจ ICIR |
| ๆฅๆถ็็ๅๅธ | ๅฎ้ ๅๅธ็ดๆนๅพ + KDE + ๆญฃๆๅๅธๅฏนๆฏๆฒ็บฟ | ๅฐๅณฐๅๅฐพ็นๅพ |
| ๆขๆ็ๅบๅ | ้้ถๆขๆ็ๆ็บฟ + ๅๅผ่็บฟ + ่ฐไปๆฅๆ ่ฎฐ๏ผ็ฐ่ฒ็ซ็บฟ๏ผ | ไบ่งฃๆๆฌ่ๅฅ |
| IC ๅๅธ | IC ๅผๅๅธ็ดๆนๅพ + ๆญฃๆๆๅ + ่็ๆ ๆณจ | IC ๅๅธๅๆญฃๆง |
ๅฏผๅ ฅ่ทฏๅพ๏ผ
from quant_alpha_engine.fusion import Labeler, StatisticalCombiner, MLCombiner
# ๆ้่ฟๆ นๅ
็ดๆฅๅฏผๅ
ฅ๏ผ
from quant_alpha_engine import Labeler, StatisticalCombiner, MLCombinerๅคๅ ๅญ่ๅๆกๆถๆไพไธค็ง่ๅผๅฐๅคไธชๅๅ ๅญๅๆไธบไธไธช็ปผๅๅ ๅญ๏ผๅนถ็ดๆฅ่พๅบๆ ๅ BacktestResult๏ผๆ ็ผๅฏนๆฅ็ฐๆ่ฏไผฐไฝ็ณปใ
ๅฎๆด่ๅๆต็จ๏ผ
from quant_alpha_engine import MockDataGenerator, Labeler, StatisticalCombiner, MLCombiner
from quant_alpha_engine.ops import AlphaOps as op
# 1. ๅๅคๆฐๆฎ
data = MockDataGenerator(n_stocks=100, n_days=504).generate()
close = data.close
# 2. ๆ้ ๅคไธชๅๅ ๅญ
f1 = op.Rank(op.Ts_Delta(close, 5))
f2 = op.Rank(-op.Ts_Corr(data.volume, close, 10))
f3 = op.Rank(op.MACD(close))
# 3. ็ๆๆ ็ญพ
label = Labeler().set_label(target='close', horizon=5, data={'close': close})
# 4. ็ป่ฎก่ๅ๏ผIC ๅ ๆ๏ผ
stat = StatisticalCombiner('ic_weighted').fit([f1, f2, f3], label)
result = stat.evaluate(
[f1, f2, f3],
close=close, is_suspended=data.is_suspended, is_limit=data.is_limit,
rebalance_freq=5, top_n=30,
)
result.print_summary()
result.plot()
# 5. ML ่ๅ๏ผRidge๏ผ้ฒๆชๆฅๅฝๆฐ๏ผ
ml = MLCombiner('ridge', min_train_periods=60, refit_freq=20)
ml.fit([f1, f2, f3], label)
result2 = ml.evaluate(
[f1, f2, f3],
close=close, is_suspended=data.is_suspended, is_limit=data.is_limit,
rebalance_freq=5, top_n=30,
)
result2.plot()
print(ml.feature_importances_) # ๆฅ็ๅๅ ๅญ้่ฆๆงๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.fusion import Labeler
็ๆ็จไบๅคๅ ๅญ่ๅ็ๅๅๆถ็็ๆ ็ญพ๏ผ็็ฃๅญฆไน ็็ฎๆ Y๏ผใ
label = Labeler().set_label(
target = 'close', # ็ฎๆ ไปทๆ ผๅญๆฎต๏ผ'close' | 'open' | 'vwap' ็ญ
horizon = 5, # ๅๅๅคฉๆฐ๏ผ้ขๆตๅ ๅคฉๅ็ๆถ็๏ผ
method = 'return', # ๆถ็่ฎก็ฎๆนๅผ๏ผ'return' | 'log_return'
data = {'close': close_df}, # ๅซ็ฎๆ ไปทๆ ผ็ฉ้ต็ๅญๅ
ธ
custom_label = None, # ็จๆท่ชๅฎไน Y (TรN DataFrame)๏ผไผๅ
็บงๆ้ซ
)| ๅๆฐ | ้ป่ฎคๅผ | ่ฏดๆ |
|---|---|---|
target |
'close' |
ไปทๆ ผๅญๆฎตๅ๏ผ้ไธ data ๅญๅ
ธไธญ็ key ๅฏนๅบ |
horizon |
1 |
ๅๅๅคฉๆฐ๏ผๅฆ 5 ่กจ็คบ้ขๆต 5 ๆฅๅ็ๆถ็็ |
method |
'return' |
'return'๏ผ็ฎๅๆถ็็ P_{t+h}/P_t - 1๏ผ'log_return'๏ผๅฏนๆฐๆถ็็ |
data |
None |
ไปทๆ ผๆฐๆฎๅญๅ
ธ๏ผๅฆ {'close': close_df} |
custom_label |
None |
่ฅๆไพ๏ผ็ดๆฅ่ฟๅ่ฏฅ DataFrame๏ผ่ทณ่ฟๅ ็ฝฎ่ฎก็ฎ๏ผ |
่ฟๅ๏ผ pd.DataFrame๏ผT ร N๏ผ๏ผไธ่พๅ
ฅไปทๆ ผ็ฉ้ตๅฝข็ถ็ธๅ๏ผๅ horizon ่กไธบ NaNใ
# ๆนๅผไธ๏ผๅ
็ฝฎๆ ็ญพ๏ผ5ๆฅ็ฎๅๆถ็็๏ผ
y = Labeler().set_label(target='close', horizon=5, data={'close': close})
# ๆนๅผไบ๏ผๅฏนๆฐๆถ็็ๆ ็ญพ
y_log = Labeler().set_label(target='close', horizon=5,
method='log_return', data={'close': close})
# ๆนๅผไธ๏ผ็จๆท่ชๅฎไนๆ ็ญพ
my_y = (close.shift(-3) / close - 1) # 3ๆฅๆถ็็
y_custom = Labeler().set_label(custom_label=my_y)
# ้ๆๅทฅๅๆนๆณ๏ผ็ดๆฅไปไปทๆ ผ่ฎก็ฎ๏ผ
y_static = Labeler.from_price(close, horizon=5, method='return')ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.fusion import StatisticalCombiner
ๅบไบ็ป่ฎกๆ้็ๅคๅ ๅญๅๆ๏ผๆฏๆไธ็งๆ้่ฎก็ฎๆนๅผใ
StatisticalCombiner(method='equal')method |
่ฏดๆ | ้็จๅบๆฏ |
|---|---|---|
'equal' |
็ญๆๅๆ | ๅฟซ้ๅบๅ๏ผๆ ้่ฎญ็ปๆ ็ญพ |
'ic_weighted' |
ๆ IC ๅๅผๅ ๆ | ้ขๆตๅๅผบ็ๅ ๅญ่ทๅพๆด้ซๆ้ |
'min_variance' |
ๆๅฐๆนๅทฎไผๅ๏ผSLSQP๏ผ | ่ฟฝๆฑๅๆๅ ๅญ็็จณๅฎๆง |
ๆๆๆนๆณๅจๅๆๅ่ชๅจๅฏนๆฏไธชๅ ๅญๅ
rank(axis=1, pct=True)ๅค็๏ผๆถ้ค้็บฒ๏ผใ
combiner = StatisticalCombiner('ic_weighted')
# ่ฎญ็ป๏ผ่ฎก็ฎๆ้๏ผ
combiner.fit(factors=[f1, f2, f3], label=y)
# ้ขๆต๏ผ็ๆๅๆๅ ๅญ TรN๏ผ
composite = combiner.predict([f1, f2, f3])
# ไธ้ฎๅๆต่ฏไผฐ
result = combiner.evaluate(
factors = [f1, f2, f3],
close = close,
is_suspended = data.is_suspended,
is_limit = data.is_limit,
rebalance_freq = 5,
top_n = 30,
weight_method = 'equal',
cost_rate = 0.0015,
)
# ๆฅ็ๆ้
print(combiner.weights_) # np.ndarray๏ผๅๅ ๅญๆ้
print(combiner.ic_matrix_) # DataFrame๏ผๆฏๆฅๅๅ ๅญ IC ๅผ
# ๆไน
ๅ
combiner.save('stat_combiner.pkl')
loaded = StatisticalCombiner.load('stat_combiner.pkl')from quant_alpha_engine.fusion import StatisticalCombiner
# ็ญๆๅๆ๏ผๆ ้ๆ ็ญพ๏ผ
equal = StatisticalCombiner('equal').fit([f1, f2, f3], y)
print("็ญๆๆ้:", equal.weights_) # [0.333, 0.333, 0.333]
# IC ๅ ๆ๏ผ้ขๆตๅๅผบ็ๅ ๅญๆ้ๆด้ซ๏ผ
ic_w = StatisticalCombiner('ic_weighted').fit([f1, f2, f3], y)
print("ICๅ ๆๆ้:", ic_w.weights_) # ไพ๏ผ[0.45, 0.35, 0.20]
# ๆๅฐๆนๅทฎๅๆ๏ผๅๅฐๅๆๅ ๅญๆณขๅจ๏ผ
min_var = StatisticalCombiner('min_variance').fit([f1, f2, f3], y)
result = min_var.evaluate([f1, f2, f3], close, data.is_suspended, data.is_limit,
rebalance_freq=5, top_n=30)
result.print_summary()ๅฏผๅ
ฅ่ทฏๅพ๏ผ from quant_alpha_engine.fusion import MLCombiner
ไฝฟ็จๆบๅจๅญฆไน ๆจกๅๅญฆไน ๅ ๅญไธๆชๆฅๆถ็็ๅ ณ็ณป๏ผ้่ฟ Expanding Window ๆนๅผไธฅๆ ผ้ฒๆญขๆชๆฅๅฝๆฐใ
MLCombiner(
model_type = 'ridge', # ๆจกๅ็ฑปๅ
min_train_periods = 60, # ๆๅฐ่ฎญ็ปๆ๏ผๅ N ๅคฉไธบ NaN๏ผไธ้ขๆต๏ผ
refit_freq = 20, # ๆฏ้ N ๅคฉ้ๆฐ่ฎญ็ปไธๆฌก
ridge_alpha = 1.0, # Ridge ๆญฃๅๅๅผบๅบฆ
rf_n_estimators = 100, # RandomForest ๆ ็ๆฐ้
rf_max_depth = 5, # RandomForest ๆๅคงๆทฑๅบฆ
xgb_n_estimators = 100, # XGBoost ๆ ็ๆฐ้๏ผ้ๅฎ่ฃ
xgboost๏ผ
xgb_max_depth = 3, # XGBoost ๆๅคงๆทฑๅบฆ
xgb_learning_rate = 0.1, # XGBoost ๅญฆไน ็
)model_type |
่ฏดๆ | ็นๅพ้่ฆๆงๆฅๆบ |
|---|---|---|
'linear' |
ๆฎ้็บฟๆงๅๅฝ | |coef_| ๅฝไธๅ |
'ridge' |
Ridge ๅๅฝ๏ผL2 ๆญฃๅๅ๏ผ | |coef_| ๅฝไธๅ |
'random_forest' |
้ๆบๆฃฎๆ | feature_importances_ |
'xgboost' |
XGBoost๏ผ้้ขๅคๅฎ่ฃ ๏ผ | feature_importances_๏ผๆชๅฎ่ฃ
ๆถ่ชๅจ้็บงไธบ random_forest |
่ฎญ็ปๆ๏ผๅ min_train_periods ๅคฉ๏ผโ ่พๅบๅ
จไธบ NaN
Day 0 ~ 59 โ ็งฏ็ดฏๆ๏ผๆ ้ขๆต
Day 60 ~ 79 โ ็จ [0, 60) ่ฎญ็ป โ ้ขๆต [60, 80)
Day 80 ~ 99 โ ็จ [0, 80) ่ฎญ็ป โ ้ขๆต [80, 100)
Day 100~119 โ ็จ [0, 100) ่ฎญ็ป โ ้ขๆต [100, 120)
...๏ผ่ฎญ็ป้ๆ็ปญๆฉๅคง๏ผๆฐธไธไฝฟ็จๆชๆฅๆฐๆฎ๏ผ
ml = MLCombiner('ridge', min_train_periods=60, refit_freq=20)
# ่ฎญ็ป
ml.fit(factors=[f1, f2, f3], label=y)
# ้ขๆต๏ผ็ๆๅๆๅ ๅญ TรN๏ผๅ min_train_periods ่กไธบ NaN๏ผ
pred = ml.predict([f1, f2, f3])
assert pred.iloc[:60].isna().all().all() # ไธฅๆ ผ้ช่ฏๆ ๆชๆฅๅฝๆฐ
# ๆฅ็ๅๅ ๅญ้่ฆๆง
print(ml.feature_importances_) # pd.Series๏ผindex = ['f0', 'f1', 'f2']
# ไธ้ฎๅๆต่ฏไผฐ
result = ml.evaluate(
[f1, f2, f3],
close=close, is_suspended=data.is_suspended, is_limit=data.is_limit,
rebalance_freq=5, top_n=30,
)
result.plot()
# ๆไน
ๅ๏ผไธค็งๆจกๅผ๏ผ
ml.save('ml_combiner.pkl') # ๅซ้ขๆต็ผๅญ๏ผๆไปถ่พๅคง๏ผ
ml.save('ml_combiner_lite.pkl', save_predictions=False) # ไป
ๆจกๅๆ้๏ผๆไปถๆดๅฐ๏ผ
loaded = MLCombiner.load('ml_combiner.pkl')from quant_alpha_engine.fusion import MLCombiner
# Ridge ่ๅ
ml_ridge = MLCombiner('ridge', min_train_periods=60, refit_freq=20, ridge_alpha=1.0)
ml_ridge.fit([f1, f2, f3], y)
# ้ช่ฏๆ ๆชๆฅๅฝๆฐ
pred = ml_ridge.predict([f1, f2, f3])
assert pred.iloc[:60].isna().all().all()
print(f"ๆๆ้ขๆตๅคฉๆฐ: {pred.dropna(how='all').shape[0]}")
# ๆฅ็็นๅพ้่ฆๆง
print(ml_ridge.feature_importances_)
# RandomForest ่ๅ๏ผๆๆ้็บฟๆงๅ
ณ็ณป๏ผ
ml_rf = MLCombiner('random_forest', min_train_periods=120, refit_freq=60,
rf_n_estimators=100, rf_max_depth=5)
ml_rf.fit([f1, f2, f3], y)
result_rf = ml_rf.evaluate(
[f1, f2, f3],
close=close, is_suspended=data.is_suspended, is_limit=data.is_limit,
rebalance_freq=5, top_n=30,
)
result_rf.print_summary()| ๆๆ | ่ฎก็ฎๅ ฌๅผ | ๅ่่ๅด | ๅซไน |
|---|---|---|---|
| ๅนดๅๆถ็็ | (nav_ๆซ/nav_ๅ)^(252/T) - 1 |
>5% | ็ญ็ฅๅนดๅ่ถ ้ขๆถ็ |
| ๅนดๅๆณขๅจ็ | std(ๆฅๆถ็) ร โ252 |
<20% | ๆถ็็ไธ็จณๅฎ็จๅบฆ |
| Sharpe Ratio | ๅนดๅ่ถ
้ขๆถ็ / ๅนดๅๆณขๅจ็ |
>1.0 | ้ฃ้ฉ่ฐๆดๅๆถ็๏ผ่ถ้ซ่ถๅฅฝ |
| Calmar Ratio | ๅนดๅๆถ็็ / |ๆๅคงๅๆค| |
>0.5 | ๅไฝๅๆค้ฃ้ฉ็ๅนดๅๆถ็ |
| ๆๅคงๅๆค | min((nav_t - nav_้ซ็น) / nav_้ซ็น) |
>-30% | ๅๅฒๆๅคงไบๆๅน ๅบฆ๏ผ่ถๅฐ่ถๅฅฝ |
| IC ๅๅผ | ๆฏๆฅ Rank IC ็ๅๅผ | >0.03 | ๅ ๅญ้ขๆตๅ๏ผ่ถ้ซ่ถๅฅฝ |
| IC ๆ ๅๅทฎ | ๆฏๆฅ IC ็ๆ ๅๅทฎ | โ | IC ็จณๅฎๆง๏ผ่ถไฝ่ถๅฅฝ |
| ICIR | ICๅๅผ / ICๆ ๅๅทฎ ร โ252 |
>0.5 | IC ไฟกๆฏๆฏ็๏ผ็ปผๅ่กก้็จณๅฎ้ขๆตๅ |
| IC ่็ | IC > 0 ็ๅคฉๆฐๅ ๆฏ | >55% | ๅ ๅญๆนๅๆญฃ็กฎ็ๆฆ็ |
| IC t-stat | IC ๅๅผ็ t ็ป่ฎก้ | |t|>2 |
IC ็ป่ฎกๆพ่ๆง๏ผ>2 ่กจ็คบๆพ่ |
| ๆฅๅๆขๆ็ | mean(ฮฃ|w_t - w_{t-1}| / 2) |
<10% | ๆฏๆฅๅนณๅๆขๆๆฏไพ๏ผ่ถไฝๆๆฌ่ถๅฐ |
| ๅนดๅๆ็ปญ่ดน | ๆฅๅไบคๆๆๆฌ ร 252 |
โ | ๆฏๅนดๅ ไบคๆๆ่็ๆถ็ |
| Fitness | Sharpe ร โ(|ๅนดๅๆถ็| / ๆฅๅๆขๆ) |
>1.0 | WorldQuant ็ปผๅ่ฏไผฐๆๆ |
ๅ
่ดนๆฐๆฎๆๅกๅทฒ็ป็ฌ็ซๆพๅจ free_market_data/๏ผไธๅๆๅจ quant_alpha_engine ไธ้ขใๆ็ป็จๆทๅช้่ฆๅฎไพๅ FreeMarketDataStore๏ผakshareใbaostockใYahooใ่
พ่ฎฏใ้ช็็ญๆฐๆฎๆบ่ขซๆๆ provider ็ฑป๏ผๅนถ็ฑๆ็ป็ Provider ้จ้ข็ปไธ็ปๆฐๆฎๅบไฝฟ็จใๆฅ็บฟๆฐๆฎไผ้ๅๅ
จ้จ้
็ฝฎ็ provider ๅๅๆๆฅๆ่ๅ๏ผไธป OHLC ๅญๆฎตไฟ็ๆชๅคๆไปทๆ ผ๏ผๅๆถไฟๅญ qfq_factor ๅ hfq_factor๏ผ้่ฆๅคๆไปทๆ ผๆถ็จ close * qfq_factor ๆ close * hfq_factor ่ฎก็ฎใ
ๅคๆๅ ๅญ็ฑ Provider.fetch_daily ่ด่ดฃ็ๆ๏ผไธ็ฑ TSV ๆฐๆฎๅบ่กฅ็ฎใๆฏไธชๅ
ทไฝ provider ๅ
ๅๆชๅคๆๆฅ็บฟ๏ผๅ็จๅไธไธช provider ็ๅๅคๆ/ๅๅคๆๆฅ็บฟ่ฎก็ฎ qfq_factor ๅ hfq_factor๏ผๅฆๆๆไธช provider ไธ่ฝๅๆถๆไพๆชๅคๆ OHLC ไธไธคไธชๅ ๅญ๏ผ่ฏฅ provider ็ๆฅ็บฟๅ
ไผ่ขซ่ทณ่ฟใ่ๅๆถ open/high/low/close/qfq_factor/hfq_factor ่ขซๅฝๆๅๆบๅ
ๅค็๏ผไธๅ
่ฎธๅ็ปญ provider ่ฆ็ๅ
ถไธญไธ้จๅ๏ผprice_source ๆ ่ฎฐ่ฟไธชๅๆบๅ
ๆฅ่ชๅชไธช provider๏ผsource ๅ่ฎฐๅฝ่ฏฅ่กๆ็ปไฝฟ็จ่ฟๅชไบ provider ๅญๆฎตใ้คๅคๆๅ ๅญๅค๏ผschema ไธญ็ provider ๅญๆฎตๅช็ป่ฎฐๆฐๆฎๆบๆฅ็บฟๆฅๅฃ็ดๆฅ่ฟๅๆ็ดๆฅๆ ๅฐ็ๅญๆฎต๏ผไธๅจๆฐๆฎๅบๅฑๆ็ฎๆๆฏ็นๅพใ
ๆฏไธช provider ้ฝไฟ็ fetch_dailyใfetch_minuteใfetch_realtime ไธไธชๅฏ่ฐ็จๅ
ฅๅฃ๏ผ่ฝไป่ฏฅๆฐๆฎๆบ่ทๅ็ๆฅๅฃไผ็ๅฎ่ฏทๆฑๅนถ่งฃๆ๏ผๆฐๆฎๆบ่ช่บซไธๆฏๆๆ้่ฆ็ปๅฝๆๆถไผๆๅบๆธ
ๆฐ้่ฏฏ๏ผ็ฑๆ็ป Provider ่ฎฐๅฝๅ็ปง็ปญๅฐ่ฏๅ็ปญๆฐๆฎๆบใ้ช็ๆฅๅฃ้่ฆ่ฎพ็ฝฎ XUEQIU_COOKIE๏ผๆๆๅจๅๅปบ XueqiuProvider(cookie="...") ๅๆณจๅใ
from free_market_data import FreeMarketDataStore
store = FreeMarketDataStore(
db_path="data/free_market_db",
stock_codes=["600000.SH", "000001.SZ"], # ไผ ๅ
ฅ่ก็ฅจๆฑ ๆถ๏ผ้ป่ฎค้ข็ญ่ฟ 1 ๅนดๆฅ็บฟ
daily_fields={
"default": ["open", "high", "low", "close", "volume", "amount", "qfq_factor", "hfq_factor"],
"baostock": ["open", "high", "low", "close", "volume", "amount", "pe_ttm", "pb", "ps_ttm", "pcf_ttm", "trade_status", "is_st"],
},
)
# ๆฅ็บฟ๏ผไผๅ
่ฏปๆฌๅฐ TSV๏ผ็ผบๅฃ่ชๅจ้ๅ providerใ่ๅๅญๆฎตๅนถๅๅๆฐๆฎๅบ
daily = store.get_daily(
stock_codes=["600000.SH", "000001.SZ"],
start_date="2024-01-01",
end_date="2024-12-31",
)
# ๅๆตๅธธ็จ T ร N ็ฉ้ต
matrices = store.get_daily(
["600000.SH", "000001.SZ"],
"2024-01-01",
"2024-12-31",
fields=["close", "volume"],
return_format="dict",
)
close = matrices["close"]
volume = matrices["volume"]
# ๅญๆฎต็ฎก็๏ผๅธๅผใ่ดขๆฅใๅๅ่ต้็ญๅญๆฎตๅฏๅ็ปญ่กฅๅ
ฅ TSV ๅบ
store.add_field("northbound_net_buy", "ๅๅ่ต้ๅไนฐๅ
ฅ้ข")
fields = store.describe_fields()
issues = store.validate_database()
# ๅ้็บง / ๅฎๆถๆฐๆฎ็ด่ฟ provider๏ผไธๅๅ
ฅๆฌๅฐๆฐๆฎๅบ
minute = store.get_minute("600000.SH", period="5m")
realtime = store.get_realtime(["600000.SH", "000001.SZ"])ๅฐๆกๆถๅบ็จๅฐ็ๅฎๆฐๆฎๆถ๏ผๅช้ๅฐๆฐๆฎ็ป็ปไธบๆญฃ็กฎๆ ผๅผ็ DataFrame๏ผ
# ๆๆ DataFrame ้กปๆปก่ถณ๏ผ
# - index: pd.DatetimeIndex๏ผๆถ้ดๅๅบ
# - columns: ่ก็ฅจไปฃ็ ๏ผๅญ็ฌฆไธฒ๏ผ๏ผๅฝๅไธ่ด
import pandas as pd
# ไปทๆ ผๆฐๆฎ๏ผT ร N๏ผ
close = pd.DataFrame(...) # index=ๆฅๆ, columns=่ก็ฅจไปฃ็
# ๅ็็ฉ้ต๏ผT ร N๏ผ๏ผbool ็ฑปๅ
is_suspended = pd.DataFrame(...).astype(bool)
# ๆถจ่ทๅ็ฉ้ต๏ผT ร N๏ผ๏ผbool ็ฑปๅ
# ๅฏไปฅๅๅนถๆถจๅๅ่ทๅ๏ผis_limit = is_limit_up | is_limit_down
is_limit = (is_limit_up | is_limit_down).astype(bool)
# ่กไธๆ ๅฐ๏ผ้ๆ๏ผ
# index=่ก็ฅจไปฃ็ , values=่กไธๅ็งฐๅญ็ฌฆไธฒ
industry = pd.Series({
'000001.SZ': '้ถ่ก',
'000002.SZ': 'ๆฟๅฐไบง',
...
})import pandas as pd
from quant_alpha_engine.ops import AlphaOps as op
from quant_alpha_engine.backtest import VectorEngine
# โโ ๅ ่ฝฝๆจ็ๆฐๆฎ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
close = pd.read_csv('close.csv', index_col=0, parse_dates=True)
volume = pd.read_csv('volume.csv', index_col=0, parse_dates=True)
is_suspended = pd.read_csv('suspended.csv', index_col=0, parse_dates=True).astype(bool)
is_limit_up = pd.read_csv('limit_up.csv', index_col=0, parse_dates=True).astype(bool)
is_limit_dn = pd.read_csv('limit_down.csv', index_col=0, parse_dates=True).astype(bool)
is_limit = is_limit_up | is_limit_dn
industry = pd.read_csv('industry.csv', index_col=0).squeeze() # Series
# โโ ๆ้ ๆจ็ๅ ๅญ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
factor = op.Neutralize(
op.Rank(-op.Ts_Corr(volume, close, window=10)),
industry
)
# โโ ไธ่กๅๆต โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
result = VectorEngine(
factor = factor,
close = close,
is_suspended = is_suspended,
is_limit = is_limit,
rebalance_freq = 5,
top_n = 50,
weight_method = 'equal',
cost_rate = 0.0015,
).run()
result.print_summary()
result.plot(save_path='my_factor_report.png')ไปฅไธ็คบไพๅฑ็คบๅธธ่งๅ ๅญ็ฑปๅ็็ฎๅญ็ปๅๆนๅผ๏ผๅฏ็ดๆฅๅค็จๆๅ่ไฟฎๆน๏ผ
from quant_alpha_engine.ops import AlphaOps as op
# โโ ๅ่ฝฌ็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ็ญๆๅ่ฝฌ๏ผ5ๆฅ๏ผ
factor_rev5 = op.Rank(-op.Ts_Delta(close, 5))
# ไธญๆๅ่ฝฌ๏ผ20ๆฅ๏ผ+ ่กไธไธญๆงๅ
factor_rev20 = op.Neutralize(op.Rank(-op.Ts_Delta(close, 20)), industry)
# โโ ๅจ้็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ๆๅบฆๅจ้๏ผ่ทณ่ฟๆ่ฟ5ๅคฉ๏ผ้ฟๅ
็ญๆๅ่ฝฌ๏ผ
momentum = close / op.Ts_Delay(close, 21) - close / op.Ts_Delay(close, 5)
factor_mom = op.Rank(momentum)
# ็บฟๆง่กฐๅๅนณๆป็ๅจ้
factor_decay_mom = op.Rank(op.Decay_Linear(op.Rank(op.Ts_Delta(close, 10)), d=5))
# ๐ ้ฃ้ฉ่ฐๆดๅจ้๏ผv2๏ผ
factor_ram = op.Rank(op.RiskAdjMomentum(close, window=20, vol_window=20))
# ๐ ไปทๆ ผ่ทฏๅพ่ดจ้๏ผv2๏ผโ ๅ่ฐ็บฟๆง่ถๅฟ
factor_ppq = op.Rank(op.PricePathQuality(close, window=20))
# โโ ้ไปท็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ้ไปท่็ฆป๏ผ็ผฉ้ไธๆถจไธบๆญฃไฟกๅท๏ผ
factor_vp = op.Neutralize(op.Rank(-op.Ts_Corr(volume, close, 10)), industry)
# ๆไบค้ๆถๅบๅไฝ๏ผ่ฟๆๆไบค้ๅคไบๅๅฒไฝไฝ๏ผ
factor_vol_rank = op.Rank(-op.Ts_Rank(volume, 20))
# ๆพ้ๅๆฐ้ซ๏ผไปทๆ ผๅๆฐ้ซ + ๆไบค้ไนๅ้ซ๏ผ
price_rank = op.Ts_Rank(close, 60)
vol_rank = op.Ts_Rank(volume, 20)
factor_breakout = op.Rank(price_rank + vol_rank)
# ๐ VWAP ๅ็ฆปๅบฆ๏ผv2๏ผ
factor_pvdev = op.Rank(-op.PVDeviation(close, volume, window=10))
# ๐ Amihud ๆตๅจๆง๏ผv2๏ผโ ้ซๆตๅจๆง่ก็ฅจ
factor_liq = op.Rank(-op.Amihud(close, volume, window=20))
# โโ ๆณขๅจ็็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ไฝๆณขๅจๅ ๅญ๏ผๆณขๅจ็่ถๅฐ่ถๅฅฝ๏ผ
daily_ret = close.pct_change()
factor_lowvol = op.Rank(-op.Ts_Std(daily_ret, 20))
# ๐ ้ซ้ถ็ฉๅ ๅญ๏ผv2๏ผโ ไฝ่ดๅๅบฆ๏ผๅฐพ้จ้ฃ้ฉๅฐ๏ผ
factor_skew = op.Rank(-op.Ts_Skew(daily_ret, 20))
# โโ ๆๆฏๆๆ ็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ๐ RSI ่ถ
ๅๅ ๅญ๏ผv2๏ผ
factor_rsi = op.Rank(100 - op.RSI(close, window=14)) # RSI ่ถไฝ โ ่ถ่ถ
ๅ
# ๐ MACD ๆฑ็ถๅพ๏ผv2๏ผ
factor_macd = op.Rank(op.MACD(close, fast=12, slow=26, signal=9))
# ๐ KDJ ่ถ
ๅ๏ผv2๏ผ
factor_kdj = op.Rank(50 - op.KDJ(close, high, low, n=9))
# ๐ ๅบ้ด็ช็ ด๏ผv2๏ผ
factor_rng = op.Rank(op.RangeBreakout(close, high, low, window=20))
# โโ ไปทๆ ผไฝ็ฝฎ็ฑป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Williams %R๏ผไปทๆ ผๅจ่ฟ20ๆฅๅบ้ดๅ
็ไฝ็ฝฎ๏ผ่ถไฝ่ถ่ถ
ๅ๏ผ
high20 = op.Ts_Max(high, 20)
low20 = op.Ts_Min(low, 20)
wr = (close - high20) / (high20 - low20 + 1e-8)
factor_wr = op.Rank(-wr) # ่ถ
ๅๆๅ้ ๅ
# โโ ๅคๅ ๅญๅๆ๏ผๆๅจ็ญๆ๏ผ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ็ญๆๅๆ๏ผๅ
ๅๅซ Rank ๆถ้ค้็บฒ๏ผๅๅ ๆ๏ผ
alpha_combo = (
0.4 * op.Rank(factor_rev5) +
0.3 * op.Rank(factor_vp) +
0.3 * op.Rank(factor_lowvol)
)
# โโ ๐ ๅคๅ ๅญ่ๅ๏ผfusion ๆจกๅ๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
from quant_alpha_engine.fusion import Labeler, StatisticalCombiner, MLCombiner
label = Labeler().set_label(target='close', horizon=5, data={'close': close})
# IC ๅ ๆ็ป่ฎก่ๅ
stat = StatisticalCombiner('ic_weighted').fit([factor_rev5, factor_vp, factor_rsi], label)
result = stat.evaluate([factor_rev5, factor_vp, factor_rsi],
close, data.is_suspended, data.is_limit,
rebalance_freq=5, top_n=30)
result.print_summary()
# Ridge ML ่ๅ
ml = MLCombiner('ridge', min_train_periods=60, refit_freq=20)
ml.fit([factor_rev5, factor_vp, factor_rsi], label)
result2 = ml.evaluate([factor_rev5, factor_vp, factor_rsi],
close, data.is_suspended, data.is_limit,
rebalance_freq=5, top_n=30)
result2.plot()Q: ๅๆตๆถๆ็คบ ๆฐๆฎ่งๆจก๏ผN ไธชไบคๆๆฅ ร M ๅช่ก็ฅจ๏ผM ๆฏ้ขๆๅฐ๏ผ
A: VectorEngine ไผ่ชๅจๅ factorใcloseใis_suspendedใis_limit ๅไธช่พๅ
ฅ็ๅ
ฑๅ่ก็ฅจๅไบค้ใ่ฏทๆฃๆฅๅ DataFrame ็ columns ๆฏๅฆๅฎๅ
จไธ่ด๏ผๅคงๅฐๅใๆ ผๅผ้็ธๅ๏ผใ
Q: Ts_Rank ่ฟ่กๅพๆ
ข๏ผ
A: Ts_Rank ๅ
้จไฝฟ็จ rolling.apply๏ผPython ๅฑๅพช็ฏ๏ผ๏ผๅฏนๅคง็ฉ้ตๆง่ฝๆ้ใไผๅๅปบ่ฎฎ๏ผ
- ๅๅฐ่ก็ฅจๆฐ้ๆ็ผฉ็ญๆถ้ดๅบๅ
- ๅฐ
windowๆงๅถๅจ 60 ไปฅๅ - ่่็จ
Rank(df)ๆฟไปฃ๏ผๆช้ขๆๅ้ๅบฆๆดๅฟซ๏ผ
Q: ๆณๅฏนๅ ๅญๅ IC ๅๆ๏ผไฝไธๆณ่ตฐๅฎๆดๅๆตๆต็จ๏ผ
A: ็ดๆฅไฝฟ็จ Performance ๆจกๅ๏ผ
from quant_alpha_engine.backtest.performance import Performance
# ่ฎก็ฎๅๅ1ๆฅๆถ็็
fwd_ret = close.shift(-1) / close - 1
# ่ฎก็ฎ IC ๅบๅ
ic = Performance.calc_ic_series(my_factor, fwd_ret)
stats = Performance.calc_ic_stats(ic)
print(stats)Q: ๅฆไฝไฟๅญๅๆตๆฅๅๅพ็๏ผ
result.plot(save_path='./reports/factor1_report.png')
# ๆ
from quant_alpha_engine.visualization.report import Report
Report.plot(result, save_path='./reports/factor1_report.png')Q: Neutralize ไธญๆงๅๅๅ ๅญๅๅผไธไธบ้ถ๏ผ
A: ๆญฃๅธธ็ฐ่ฑกใNeutralize ไฝฟ็จ OLS ๆฎๅทฎ๏ผๆฎๅทฎๅจๆฏไธช่กไธๅ
ๅๅผไธบ้ถ๏ผไฝ่ทจ่กไธ็ๆดไฝๅๅผไธไธๅฎไธบ้ถใ่ฅ้ๆดไฝๅๅผไธบ้ถ๏ผๅฏๅจไธญๆงๅๅๅๅไธๆฌก ZScore๏ผ
factor_neut = op.Neutralize(raw_factor, industry)
factor_final = op.ZScore(factor_neut) # ็กฎไฟๆช้ขๅๅผ=0Q: ๅฆไฝๆจกๆไธๅๅธๅบ็ฏๅข๏ผ็ๅธ/็ๅธ/้่ก๏ผ๏ผ
# ็ๅธ๏ผ้ซๆผ็งป + ไฝๆณขๅจ๏ผ
bull = MockDataGenerator(mu=0.20, sigma=0.18, seed=1).generate()
# ็ๅธ๏ผ่ดๆผ็งป + ้ซๆณขๅจ๏ผ
bear = MockDataGenerator(mu=-0.10, sigma=0.45, seed=2).generate()
# ้่กๅธ๏ผ้ถๆผ็งป + ไธญ็ญๆณขๅจ๏ผ
flat = MockDataGenerator(mu=0.00, sigma=0.25, seed=3).generate()Q: ๅฆไฝๅจ Jupyter Notebook ไธญไฟๅญๅพ่กจ่ไธๅผน็ช๏ผ
import matplotlib
matplotlib.use('Agg') # ๅจ import pyplot ไนๅ่ฎพ็ฝฎ้ไบคไบๅ็ซฏ
result.plot(save_path='report.png')ๆๅจ Notebook ้กถ้จไฝฟ็จ %matplotlib inline ๆถ๏ผ็ดๆฅ็จ save_path ๅๆฐไฟๅญใ
Q: ๐ MLCombiner ้ขๆตๅๅ ่กๅ
จๆฏ NaN๏ผ่ฟๆฏๆญฃๅธธ็ๅ๏ผ
A: ๅฎๅ
จๆญฃๅธธ๏ผ่ฟๆฏ้ฒๆชๆฅๅฝๆฐ่ฎพ่ฎก็ๆ ธๅฟไฟ่ฏใๅ min_train_periods ่ก๏ผ้ป่ฎค60ๅคฉ๏ผๆฒกๆ่ถณๅค็ๅๅฒๆฐๆฎๆฅ่ฎญ็ปๆจกๅ๏ผๅ ๆญค่พๅบ NaNใVectorEngine ไผ่ชๅจ้่ฟ ffill ๅกซๅ
๏ผๅๆตๆ้ดๆ้ไป็ฌฌไธไธชๆๆ้ขๆตๆฅๅผๅงๅปบ็ซใ
ml = MLCombiner('ridge', min_train_periods=60)
ml.fit([f1, f2, f3], y)
pred = ml.predict([f1, f2, f3])
# ๅ 60 ่กๅบๅ
จไธบ NaN๏ผไธฅๆ ผ้ช่ฏ๏ผ
assert pred.iloc[:60].isna().all().all()
# ็ฌฌ 60 ่กไนๅๆๆๆ้ขๆต
print(f"็งฏ็ดฏๆ๏ผๅ {pred.isna().all(axis=1).sum()} ่ก")Q: ๐ StatisticalCombiner ๅ MLCombiner ็ evaluate() ้่ฆๅชไบๅๆฐ๏ผ
A: ไธ VectorEngine ๅฎๅ
จไธ่ด๏ผ้คไบ factor ๅๆฐ่ขซ factors ๅ่กจๆฟไปฃ๏ผ๏ผ
result = combiner.evaluate(
factors = [f1, f2, f3], # ๅ ๅญๅ่กจ
close = close_df, # ๅฟ
ๅกซ
is_suspended = susp_df, # ๅฟ
ๅกซ
is_limit = limit_df, # ๅฟ
ๅกซ
rebalance_freq = 5, # ๅฏ้๏ผ้ป่ฎค 1
top_n = 30, # ๅฏ้๏ผ้ป่ฎค 50
weight_method = 'equal', # ๅฏ้๏ผ้ป่ฎค 'equal'
cost_rate = 0.0015, # ๅฏ้๏ผ้ป่ฎค 0.0015
)Q: ๐ ๅฆไฝไฟๅญๅๅ ่ฝฝ่ฎญ็ปๅฅฝ็่ๅๆจกๅ๏ผ
# ไฟๅญ
stat.save('my_combiner.pkl')
ml.save('my_ml_combiner.pkl')
ml.save('my_ml_light.pkl', save_predictions=False) # ไธๅซ้ขๆต็ผๅญ๏ผๆไปถๆดๅฐ
# ๅ ่ฝฝ
from quant_alpha_engine.fusion import StatisticalCombiner, MLCombiner
stat2 = StatisticalCombiner.load('my_combiner.pkl')
ml2 = MLCombiner.load('my_ml_combiner.pkl')
# ้ช่ฏๅ ่ฝฝๆๅ
assert stat2._is_fitted
assert ml2._is_fittedQ: ๐ ๆฐ็ฎๅญ๏ผRSIใMACDใKDJ ็ญ๏ผ็่ฟๅๅผๆฏไปไน็ฑปๅ๏ผ
A: ๆๆๆฐ็ฎๅญๅ่ฟๅ pd.DataFrame๏ผๅฝข็ถไธ่พๅ
ฅ็ธๅ๏ผT ร N๏ผIndex=ๆฅๆ๏ผColumns=่ก็ฅจไปฃ็ ๏ผ๏ผๅฏ็ดๆฅไฝไธบๅ ๅญไผ ๅ
ฅ VectorEngine ๆ่ๅๆกๆถใไธ้่ฆ้่ก็ฅจๅพช็ฏ่ฐ็จใ
from quant_alpha_engine.ops import AlphaOps as op
# ๆน้่ฎก็ฎๆๆ่ก็ฅจ็ RSI๏ผT ร N๏ผ
rsi = op.RSI(close, window=14) # DataFrame
macd = op.MACD(close) # DataFrame
hurst = op.Ts_Hurst(close.pct_change(), window=30) # DataFrame
# ็ดๆฅ็จไบๅๆต
factor = op.Rank(rsi)
result = VectorEngine(factor=factor, close=close, ...).run()