完善知行短线

This commit is contained in:
2026-08-17 00:47:13 +08:00
parent 5029a22de1
commit 6ef8c585d6
6 changed files with 668 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
# 知行指标移植文档(通达信 → 任意平台)
> 供后续导入到其他项目/平台使用的完整规格:公式原文、参数、数值口径、参考实现、
> 校验向量、显示规范。框架无关部分可直接拷贝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] + (N1)·E[i1]) / (N+1)`,首根以自身为种子
`E[0] = X[0]`)。等价于系数 `k = 2/(N+1)``E[i] = k·X[i] + (1k)·E[i1]`
无限记忆,但衰减因子 `(N1)/(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<number>(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<number | null>(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<number | null>(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 根后残差 < 1e5
- 建议 closes 传入前先按 timestamp 升序、剔除停牌空K。
## 八、klinecharts v10 集成要点(本仓库方案)
- 注册:`registerIndicator(makeZhixingIndicator(get))`,模板工厂注入「(key, timestamp) → 预计算值」
访问器;序列由 DetailKLine 在 allData 上预计算写入 `PV['zx_trend']/PV['zx_dkx']`
`calc` 里按 bar.timestamp 取值(**不要用图表相对索引**,翻页后会错位)。
- 回调参数的 `Indicator<D>` 不带 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