settings.py 是一个 Python 文件,里面定义一个 settings 字典。大部分时候你当 JSON 写就行。
大体结构一览
settings = {
"is_enabled": True,
"meta": { ... },
"core": { ... },
"data": { ... },
"calculation": { ... },
"tag_definitions": [ ... ],
}下面逐块说明。
meta
标签元信息。
"meta": {
"key": "volume_spike", # 唯一标识,CLI 命令用这个名字
"display_name": "放量标签", # 显示名
"description": "成交量超过均量2倍", # 描述
"keywords": ["volume", "spike"], # 关键词
"category": ["volume"], # 分类
"details": {}, # 额外信息
}key 是最重要的字段——CLI 命令用 python cli.py t volume_spike 找到这个标签场景。改了 key 等于换了一个标签场景。
core
标签私有参数。你想配什么就配什么,在 tag.py 里通过 ctx.settings.raw.get("core", {}) 读取。
"core": {
"spike_multiplier": 2.0,
"lookback_days": 20,
"min_volume": 100000,
}data
声明标签计算需要什么数据。
"data": {
"base": {
"data_key": "stock.kline.daily", # 基础数据源
"params": {"adjust": "qfq"}, # 前复权
"indicators": { # 可选:附加指标
"macd": [{"fast": 12, "slow": 26, "signal": 9}],
},
},
"required": [ # 可选:附加数据源
{"data_key": "stock.finance.quarterly"},
],
"min_required_records": 20, # 最少需要多少条记录
}base 是主数据源,required 是附加数据源。两者都会出现在 ctx.data.items 里。
base.data_key 决定了路由
系统根据 data.base.data_key 对应的数据契约(data contract)自动推断路由模式,不需要你手动指定:
数据契约的 scope/type | 路由 | 说明 |
per_entity + 时序 | per_entity | 逐股票按日推进 |
global + 时序 | global | 全市场一份,按日推进 |
non_time_series | non_time_series | 静态表,只算一次 |
per_entity 路由下还有子路由,由 calculation.execution.mode 决定。
calculation
计算执行配置。
"calculation": {
"update_mode": "incremental", # incremental / refresh
"recompute": False, # 是否强制重算
"is_dry_run": False, # 试运行,不写入数据库
"execution": {
"mode": "entity_based", # entity_based / slice_based(仅 per_entity 需要)
"start_date": "20200101",
"end_date": "20241231",
},
}update_mode
模式 | 说明 | 适用场景 |
incremental | 增量模式,从上次计算日期开始,只算新的 | 日常更新,速度快 |
refresh | 全量重算,从头开始 | 数据出错需要重建 |
增量模式下,ctx.data.prior_value 会带上上次的标签值,你可以在 calculate_tag 里判断值是否变化,不变就返回 None 跳过写入。
execution.mode
模式 | 说明 | 适用 |
entity_based | 按股票分组,每组独立跑完整时间线 | 少股票、长时间窗口 |
slice_based | 按时间切片,每片处理全部股票 | 多股票、需要横截面计算 |
slice_based 模式下,如果你实现了 on_calendar_asof,系统会走横截面计算分支;否则走 calculate_tag 逐股计算。
global 和 non_time_series 路由不需要设 execution.mode。
start_date / end_date
回测时间窗口。格式 YYYYMMDD,如 "20200101"。留空则使用系统默认。
tag_definitions
定义这个标签场景产出哪些标签。一个标签场景可以产出多个标签。
"tag_definitions": [
{
"name": "is_spike", # 唯一标识
"display_name": "放量", # 显示名
"description": "成交量超过均量2倍", # 描述
},
{
"name": "spike_ratio",
"display_name": "放量倍数",
"description": "今日成交量除以20日均量",
},
]name 必须在同一个标签场景内唯一。calculate_tag 返回的 value 会按 tag_definition 的 name 写入数据库。
如果只产出一个标签,列表里放一项就行。
顶层字段
字段 | 类型 | 默认值 | 说明 |
is_enabled | bool | False | 标签场景是否启用。禁用的不会被批量计算选中 |
name | str | 从 meta.key 派生 | 标签场景名称(自动生成,一般不需要手写) |
core | dict | {} | 策略私有参数 |
完整示例
逐股标签:RSI 超卖标记
settings = {
"is_enabled": True,
"meta": {
"key": "rsi_oversold",
"display_name": "RSI超卖",
"description": "RSI低于30时标记为超卖",
},
"core": {
"rsi_period": 14,
"rsi_threshold": 30,
},
"data": {
"base": {
"data_key": "stock.kline.daily",
"params": {"adjust": "qfq"},
},
"min_required_records": 14,
},
"calculation": {
"update_mode": "incremental",
"execution": {
"mode": "entity_based",
"start_date": "20200101",
"end_date": "20241231",
},
},
"tag_definitions": [
{"name": "is_oversold", "display_name": "RSI超卖"},
{"name": "rsi_value", "display_name": "RSI值"},
],
}横截面标签:每日成交量排名前 10%
settings = {
"is_enabled": True,
"meta": {
"key": "volume_top10",
"display_name": "放量前10%",
"description": "每日成交量排名前10%的股票",
},
"core": {
"percentile": 0.1,
},
"data": {
"base": {
"data_key": "stock.kline.daily",
"params": {"adjust": "qfq"},
},
"min_required_records": 1,
},
"calculation": {
"update_mode": "incremental",
"execution": {
"mode": "slice_based",
"start_date": "20200101",
"end_date": "20241231",
},
},
"tag_definitions": [
{"name": "is_top10", "display_name": "放量前10%"},
],
}全局标签:市场情绪指数
settings = {
"is_enabled": True,
"meta": {
"key": "market_sentiment",
"display_name": "市场情绪",
"description": "全市场涨跌比,每日一个值",
},
"core": {},
"data": {
"base": {
"data_key": "market.sentiment.daily",
},
"min_required_records": 1,
},
"calculation": {
"update_mode": "incremental",
# global 路由不需要 execution.mode
},
"tag_definitions": [
{"name": "sentiment_score", "display_name": "情绪分数"},
],
}静态标签:行业分类
settings = {
"is_enabled": True,
"meta": {
"key": "industry_map",
"display_name": "行业分类",
"description": "股票的行业分类,静态不随时间变化",
},
"core": {},
"data": {
"base": {
"data_key": "stock.industry_map",
},
},
"calculation": {
"update_mode": "refresh",
# non_time_series 路由不需要 execution.mode
},
"tag_definitions": [
{"name": "industry", "display_name": "所属行业"},
{"name": "sector", "display_name": "所属板块"},
],
}动态 settings - 但不建议您这么做
和策略的 settings.py 一样,你可以在 settings.py 里写 Python 逻辑:
import os
settings = {
"meta": {
"key": "rsi_oversold",
},
"core": {
"rsi_threshold": int(os.environ.get("RSI_THRESHOLD", "30")),
},
# ... 其他配置
}一般当 JSON 写就行。需要动态参数时才用 Python 特性。