跳到正文

贡献新指标

贡献新指标:作者模板

本文档定义在实例结果链路(packages/core/src/engine/indicators/instances/)下新增一个指标需要改动的文件与测试要求。

适用范围:per-bar scalar / multi-line / point-array 类指标。结构类(HH/HL/BOS/FVG/OB 等)走 marker/overlay 路径,不适用本文。

架构前提

  • @Indicator 装饰器保存元数据并自动注册。开发和生产构建扫描生产源码里的导出定义类,生成引用这些类的装配入口,保证 tree-shaking 后注册仍执行;无需手动维护 import 或注册清单。
  • 计算结果按 instanceId 保存在结果池。calculationKey = definitionId + 计算参数 + 计算上下文,只用于跨实例去重,不含 pane 与样式。
  • renderer / scale renderer 在创建时绑定自己的 instanceId,绘制时从 context.indicatorStateReader.get(instanceId) 读取该实例的渲染投影。没有按指标类型索引的结果包,没有 stateKey,也不写 PluginHost StateStore。
  • 展示配置(presentation.defaultOptions)不进入计算,由投影阶段合入 renderer 读取的 params。

设计决策见 docs/design/indicator/indicator-instance-state.md 与 docs/design/indicator/indicator-instance-render-binding.md。

文件改动清单(按依赖顺序)

以新指标 XXX 为例(内部 name: 'xxx',对外 displayName: 'XXX')。

#文件改动
1packages/core/src/engine/indicators/state/xxxState.ts(新)定义 XXXRenderState extends BaseIndicatorState、EMPTY_XXX_STATE,以及需要的 DEFAULT_* 常量
2packages/core/src/engine/indicators/calculators/xxx.ts(新)+ calculators/index.ts(改)纯计算函数 calcXXXData(...),并从 calculators/index.ts 导出
3packages/core/src/engine/indicators/indicatorContracts.ts(改)副图登记进 VisibleIndicatorStateContracts,主图登记进 MainIndicatorStateContracts(xxx: XXXRenderState)
4packages/core/src/engine/renderers/Indicator/xxx.ts(新)renderer plugin(读 instanceId 投影)+ 同文件 @Indicator({...}) + static rendererFactory
5自动生成装配入口(无需手改)导出带 @Indicator 的具名类;开发服务器和 core 构建自动发现。需要提交生成文件时运行 pnpm indicators:generate

第 3 步是编译期约束:@Indicator 的 name 类型为 IndicatorName,必须是契约表登记的键,漏登记无法通过类型检查。

扫描覆盖 packages/core/src 的生产 TypeScript 源码,排除测试和生成文件;kind 和 name 必须能在编译期确定。未导出的定义、重复名称或不确定身份会阻止构建。生成文件位于 engine/indicators/generated/,不要手动编辑。CI 比对扫描结果与真实生产 bundle 的定义目录,检查 tree-shaking 后的完整性。

state/xxxState.ts 只保留渲染状态类型与 EMPTY_* 常量,不定义 state key。

renderer 读取契约

const state = context.indicatorStateReader?.get<XXXRenderState>(instanceId)

instanceId 由挂载路径通过 IndicatorRendererOptions.instanceId 注入(必填),renderer 不推导 state key,也不向 PluginHost 查询业务状态。

@Indicator 配置

以 ATR 为样板(packages/core/src/engine/renderers/Indicator/atr.ts):

@Indicator({
  name: 'xxx',
  displayName: 'XXX',
  category: 'oscillator',
  indicatorType: 'volatility',
  defaultPaneId: 'sub_XXX',
  scaleRendererFactory: createXxxScaleRendererPlugin,
  visibleState: { compose: createNonNegativeSparseVisibleStateComposer('xxx', EMPTY_XXX_STATE) },
  getTitleInfo: getXXXTitleInfo,
  presentation: { defaultOptions: { showXXX: true } },
  runtime: {
    defaultParams: { period: 14 },
    computeKey: 'calcXXXData',
    compute: (data, c) => calcXXXData(data, c.period),
  },
})
export class XXXIndicatorDefinition {
  static rendererFactory = createXXXRendererPlugin
}

可用字段(即 IndicatorDefinitionConfig),请勿臆造其它字段:

字段必填说明
name是内部 name,必须是 indicatorContracts.ts 登记的键
displayName是对外规范 ID,resolveIndicatorDefinitionId() 返回它
category是'main' | 'sub' | 'oscillator' | 'volume'
indicatorType是IndicatorTypeRegistry 的键,决定指标选择器分组
defaultPaneId是默认 pane(主图 'main',副图如 'sub_XXX')
runtime有 calculator 的指标必填{ defaultParams, compute, computeKey, outputAlignment?, configKey?, paneIdKey? };defaultParams 的键就是计算参数集合
visibleState副图指标需要{ compose },复用 visibleStateComposers.ts 的 composer 工厂
mainPane主图指标需要{ rendererName, toActiveConfig?, computePriceRange?, composeRenderState? }
presentation建议{ defaultOptions, selectSeriesKeys? };展示配置不进入 calculationKey
getTitleInfo否pane 标题 / 主图图例内容
scale / scaleRendererFactory否副图坐标轴
getRendererName / getScaleRendererName / getPaneTitleRendererName否覆盖默认 plugin 命名规则
dataViews否参与渲染的数据视图,未声明时仅 K 线
aliases / indicatorTypeLabel / paneIdField / allowMainPane否兼容别名与能力声明

外加挂在被装饰类上的 static rendererFactory: RendererFactory(必填,缺失会在模块加载时抛 KLineChartError)。

参数分层:影响 calculator 输出的参数放 runtime.defaultParams;show* 一类显隐开关放 presentation.defaultOptions。两者混放会导致切换显隐时触发重新计算。多周期指标可用 presentation.selectSeriesKeys(params, options) 过滤可见序列(参考 ma.ts / rsi.ts)。

测试

复用 packages/core/src/engine/indicators/__tests__/ 的基础设施:

路径用途
__fixtures__/synthetic.ts合成 OHLC 场景(empty、singleBar、pureUptrend、spikeAtBar19 等)
__fixtures__/golden/*.json + golden/index.ts离线金标值与 assertSeriesClose(actual, expected, tolerance)
_propertyAssertions.ts跨指标可复用的数学不变量断言
helpers/instanceTestKit.ts实例快照、计算计划、可控执行器与输出构造
helpers/metadataTestKit.ts最小 IndicatorMetadata

每个新指标必须覆盖:

  1. Edge cases:empty / single bar / shorter-than-period / period ≤ 0 / period = 1
  2. Golden values:≥ 3 个合成 fixture 与离线金标对照
  3. 数学不变量:取值范围(RSI ∈ [0,100]、ATR ≥ 0 等)、单调性、对称性
  4. Warm-up 边界:indices [0, period-1) 为 undefined,自 period-1 起有定义
  5. Incremental ≡ batch:calc(slice(0, n)) ≡ calc(full).slice(0, n)

renderer 侧用 renderers/__tests__/ 的 renderTestKit 构造 RenderContext 与按 instanceId 命中的 reader;投影侧在 indicators/__tests__/stateComposer.test.ts 验证展示配置合入与 selectSeriesKeys 过滤。

Golden values 生成

金标是 commit 进仓库的 JSON,CI 不跑生成脚本。当前已提交 atr / dema / hma / kama / tema / wma 的 golden,由手算 + 公式推导生成;引入离线生成脚本后应替换。

提交节奏建议

  • 单个指标 = 单个 PR;互不依赖的简单指标(如 WMA + DEMA + TEMA + HMA 同属 MA 家族)可合并
  • 强依赖的指标拆成 PR 链(如 ATR → Keltner → SuperTrend,前者合并后才开后者)

本页内容