Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions config/phase_f_volume_peak_count.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# PR-F — Fourth real factor evaluation: volume-peak-minute count.
#
# Reproduces the Kaiyuan market-microstructure series #27 (开源证券《高频成交量的峰、岭、
# 谷信息——市场微观结构研究系列(27)》, reportId 4957417) flagship "量峰分钟数因子" as a
# first-class VolumePeakCountFactor and runs it through the FROZEN
# StandardFactorEvaluator on REAL cached A-share data. CACHE-ONLY: the minute read is
# provably live-call-free; daily / universe / covariate endpoints go through the
# read-through cache (warm -> 0 gap fetches).
#
# The subject factor works DIRECTLY on the 1min cache (no coarser resample): PIT-truncate
# each day at 14:50, classify every visible minute against its SAME-SLOT strictly-prior
# 20-day baseline (eruptive if vol > μ + σ else mild), mark the eruptive minutes whose
# both 1-minute same-session neighbours are mild as PEAKS (ridge / session-boundary /
# unclassifiable-neighbour minutes are not peaks), and count the peak minutes over the
# trailing 20 VALID trading days (a day is valid iff it has >= 100 classifiable bars;
# NaN below 10 valid days). Pre-registered sign = +1 (report IC positive: full-market
# RankIC +10.62% / RankICIR 4.36, CSI500 sub-domain long-short +14.96%/yr). NOTE the
# report is a MONTHLY, market-cap + industry neutral series on Wind data -- our eval cell
# is CSI500 daily with industry + size neutral, so those numbers are a LOOSE reference
# only. Raw minute volume (cached as-is) has split-day magnitude jumps that pollute the
# 20-day σ; the report (Wind) does not adjust for this either, so it is disclosed and NOT
# corrected.
#
# Eval CELL is IDENTICAL to PR-C / PR-D / PR-E: universe = CSI500 (000905.SH), PIT
# membership; window = 2021-07-01 .. 2026-06-30 (the project's minute-coverage window);
# daily rebalance; OOS split 2024-01-01; book = value_ep/value_bp/volatility_20; fee
# 0.001. The ONLY substantive difference is the subject factor, which is minute-derived
# and computed by the runner from the intraday cache -- it is NOT a config-listed daily
# factor. The evaluator runs TWICE (see qt/eval_volume_peak_count.py): once with NO book
# (Incremental NOT_ASSESSED) and once with the confirmed book to measure whether the
# volume-peak-count factor adds alpha BEYOND value / low-vol.

project:
name: quantitative_trading_pr_f_volume_peak_count
timezone: Asia/Shanghai
data:
source: tushare
freq: D
start: '2021-07-01'
end: '2026-06-30'
external_secret_file: /home/shaofl/Projects/financial_projects/.config.json
tushare_token_key: tushare.token
output_name: volume_peak_count_daily
cache:
enabled: true
root_dir: artifacts/cache/tushare/v1
refresh_recent_days: 14
refresh_dimension_days: 30
force_refresh: []
universe:
type: index
index_code: 000905.SH
symbols: []
min_listing_days: 60
filters:
missing_close: true
suspended: false
st: false
limit_up_down: false
# factors = the confirmed BOOK used for the Incremental axis (value + low-vol). The
# SUBJECT factor (volume_peak_count_20) is minute-derived and computed by the runner
# from the intraday cache -- it is NOT a config-listed daily factor.
factors:
- name: value_ep
enabled: true
- name: value_bp
enabled: true
- name: volatility_20
enabled: true
params:
window: 20
price_col: close
processing:
drop_missing: true
standardize:
enabled: true
method: zscore
# winsorize is a P0 no-op in this codebase; the EvalConfig declares winsorize=None
# accordingly (nothing is clipped), so the report never overstates preprocessing.
winsorize:
enabled: false
method: mad
n: 3.0
# Industry + market-cap neutralization (SW-L1), matching the report's neutral column
# and the EvalConfig neutralization declaration.
neutralize:
enabled: true
industry_col: industry
size_col: market_cap
industry_level: L1
alpha:
model: equal_weight
params: {}
portfolio:
# Required by the schema but unused: this runner evaluates a factor, it does not
# build/execute a portfolio.
constructor: topn_equal_weight
top_n: 50
long_only: true
max_weight: null
turnover_cap: null
backtest:
# Required by the schema but unused (no backtest is run). The EvalConfig uses a DAILY
# rebalance (contract default), independent of this field.
initial_nav: 1.0
rebalance: monthly
event_order: close_to_next_period
cash_return: 0.0
cost:
fee_rate: 0.001
slippage_rate: 0.0
turnover_formula: l1
analytics:
forward_return_periods:
- 1
quantiles: 5
benchmark: null
# OOS split (window midpoint) so the OOS section runs and the Predictive axis can be
# assessed (sign consistency across both holdout subperiods).
oos:
split_date: '2024-01-01'
output:
root_dir: artifacts
data_dir: artifacts/data
factor_dir: artifacts/factors
report_dir: artifacts/reports
log_dir: artifacts/logs
overwrite: true
Loading