# 知行指标移植文档(通达信 → 任意平台) > 供后续导入到其他项目/平台使用的完整规格:公式原文、参数、数值口径、参考实现、 > 校验向量、显示规范。框架无关部分可直接拷贝;klinecharts v10 集成见第八节。 > 本仓库实现:`zhixing.ts`(算法+模板)/ `index.ts`(注册表)/ `DetailKLine.vue`(数据管道)。 ## 一、原始通达信公式 ``` 知行短期趋势线:EMA(EMA(C,10),10),COLORE61851,LINETHICK1; MA1:=MA(CLOSE,60); MA2:=EMA(CLOSE,13); Z1:=STRCAT(HYBLOCK,' '); Z2:=STRCAT(Z1,DYBLOCK); Z3:=STRCAT(Z2,' '); DRAWTEXT_FIX(ISLASTBAR,0,0,0,STRCAT(Z3,GNBLOCK)),COLOR00C0C0; 知行多空线:(MA(CLOSE,M1)+MA(CLOSE,M2)+MA(CLOSE,M3)+MA(CLOSE,M4))/4; ``` **参数表(通达信公式管理器里配置,公式文本不带——移植时最容易丢的就是这个):** | 参数 | 缺省值 | |------|--------| | M1 | 14 | | M2 | 28 | | M3 | 57 | | M4 | 114 | ## 二、逐行语义 | 行 | 作用 | 移植处理 | |----|------|----------| | 知行短期趋势线 | EMA 嵌套两层,主图线 | 画线,#E61851 | | MA1 / MA2 | `:=` 隐藏赋值,且未被后续引用 → 死代码 | 不画 | | Z1~Z3 + DRAWTEXT_FIX | 左上角固定文本:行业板块+地域板块+概念板块(通达信本地板块数据) | 无本地板块数据源,用「行业+地域」近似做成图例行;概念板块缺失 | | 知行多空线 | 4 条 MA 的等权平均,主图线 | 画线,周期 14/28/57/114 | ## 三、数值口径(与通达信逐位一致) **EMA(通达信式)**:`E[i] = (2·X[i] + (N−1)·E[i−1]) / (N+1)`,首根以自身为种子 (`E[0] = X[0]`)。等价于系数 `k = 2/(N+1)` 的 `E[i] = k·X[i] + (1−k)·E[i−1]`。 无限记忆,但衰减因子 `(N−1)/(N+1)`(N=10 时 ≈0.82/根),截断起点的影响几十根后可忽略。 **MA(通达信式,严格窗口)**:窗口不满输出空值(null/NaN),**不是** min_periods=1 的渐进均值—— 很多图表库内置 MA 是渐进口径,直接用会整条线偏掉。 **多空线**:`dkx[i] = (MA14[i] + MA28[i] + MA57[i] + MA114[i]) / 4`,四个 MA 任一为空则为空。 即前 113 根没有多空线(通达信从上市日起算,同样如此)。 **复权口径**:序列必须与图表展示用同一复权基准(前复权/不复权)的收盘价计算,否则数值对不上。 ## 四、参考实现(框架无关 TypeScript,可直接拷贝) ```ts export const DKX_PERIODS = [14, 28, 57, 114]; function ema(src: number[], n: number): number[] { const out = new Array(src.length); const k = 2 / (n + 1); let prev = NaN; for (let i = 0; i < src.length; i++) { prev = Number.isNaN(prev) ? src[i] : src[i] * k + prev * (1 - k); out[i] = prev; } return out; } function sma(src: number[], n: number): (number | null)[] { const out = new Array(src.length).fill(null); let sum = 0; for (let i = 0; i < src.length; i++) { sum += src[i]; if (i >= n) sum -= src[i - n]; if (i >= n - 1) out[i] = sum / n; } return out; } /** closes 为整段历史收盘价(建议从上市日或至少 ≥ 需求长度+114 根起) */ export function computeZhixingSeries(closes: number[]) { const trend = ema(ema(closes, 10), 10); const mas = DKX_PERIODS.map((p) => sma(closes, p)); const dkx = new Array(closes.length).fill(null); for (let i = 0; i < closes.length; i++) { const m = mas.map((a) => a[i]); if (m.every((v) => v != null)) dkx[i] = m.reduce((s, v) => s + (v as number), 0) / m.length; } return { trend, dkx }; } ``` ## 五、参考实现(Python / numpy) ```python import numpy as np DKX_PERIODS = (14, 28, 57, 114) def ema_tdx(x: np.ndarray, n: int) -> np.ndarray: out = np.empty(len(x)) k = 2.0 / (n + 1) prev = np.nan for i, v in enumerate(x): prev = v if np.isnan(prev) else v * k + prev * (1 - k) out[i] = prev return out # pandas 等价写法(adjust=False 且首根种子=自身,与通达信一致): # s.ewm(span=n, adjust=False).mean() def sma_tdx(x: np.ndarray, n: int) -> np.ndarray: c = np.concatenate(([0.0], np.cumsum(x))) out = np.full(len(x), np.nan) out[n - 1:] = (c[n:] - c[:-n]) / n # 窗口不满保持 NaN return out def zhixing(close: np.ndarray): trend = ema_tdx(ema_tdx(close.astype(float), 10), 10) stack = np.vstack([sma_tdx(close.astype(float), p) for p in DKX_PERIODS]) dkx = np.full(len(close), np.nan) ok = ~np.isnan(stack).any(axis=0) dkx[ok] = stack[:, ok].mean(axis=0) return trend, dkx ``` ## 六、校验向量(移植后跑一遍,全对即口径正确) **趋势线** `closes = [10,11,12,13,14,15]`: ``` EMA(C,10) = 10.000000, 10.181818, 10.512397, 10.964688, 11.516563, 12.149915 EMA(EMA(C,10),10) = 10.000000, 10.033058, 10.120210, 10.273752, 10.499717, 10.799753 ``` **MA 严格窗口** `sma([1,2,3,4,5,6], 3)`: ``` null, null, 2.000000, 3.000000, 4.000000, 5.000000 ``` **多空线预热** `closes[i] = i+1`(线性爬坡,116 根):索引 0~112 全为 null; 索引 113 起有值,闭式解 `dkx[i] = (i+1) − (13+27+56+113)/8 = i+1 − 26.125`: ``` dkx[113] = 87.875000 dkx[114] = 88.875000 dkx[115] = 89.875000 ``` ## 七、数据与预热要求 - MA114 需预热 113 根:**喂给算法的历史长度 = 可视区最左端再往左 ≥113 根**。 分页加载的图表不要只算当前窗口(首屏窗口常见 240 根,左边会缺一大段), 应在「全部已加载历史」上整段计算,左滑前插后整体重算。 - EMA 理论上要上市日起算;实际截断到几百根即可(衰减 0.82/根,60 根后残差 < 1e−5)。 - 建议 closes 传入前先按 timestamp 升序、剔除停牌空K。 ## 八、klinecharts v10 集成要点(本仓库方案) - 注册:`registerIndicator(makeZhixingIndicator(get))`,模板工厂注入「(key, timestamp) → 预计算值」 访问器;序列由 DetailKLine 在 allData 上预计算写入 `PV['zx_trend']/PV['zx_dkx']`, `calc` 里按 bar.timestamp 取值(**不要用图表相对索引**,翻页后会错位)。 - 回调参数的 `Indicator` 不带 E 泛型,`extendData` 是 unknown,取值需 `as` 收窄。 - `createTooltipDataSource` 的 `legends` 会整体替换默认图例行,线值图例须自己拼回; `calcParams: DKX_PERIODS` 让图例显示「知行(14,28,57,114)」(通达信式)。 - 长周期指标 + 分页窗口的完整坑位说明见 `memory/custom-indicator-framework.md`。 ## 九、移植检查清单 1. M1~M4 = **14/28/57/114**(不是 BBI 的 3/6/12/24) 2. EMA 首根种子=自身;MA 窗口不满=null(两条都与常见库默认不同) 3. 计算输入与显示用同一复权基准的收盘价 4. 预热 ≥113 根覆盖到可视区最左端之外 5. 跑第六节校验向量,逐位一致 6. 颜色:趋势线 #E61851、多空线 #F5C518、板块文本 #00C0C0