You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
2.8 KiB
120 lines
2.8 KiB
package indicator |
|
|
|
import ( |
|
"math" |
|
"sig-pub/pkg/types" |
|
"sig-pub/pkg/types/ta" |
|
) |
|
|
|
// ADX 趋势强度指标, 用于衡量价格趋势的强弱, 而不考虑趋势的方向 |
|
// 它通常与 +DI(正向方向性指标)和 -DI(负向方向性指标)一起使用,形成 DMI(Directional Movement Index,方向性运动指数)系统 |
|
type ADX struct { |
|
} |
|
|
|
// indicator interface |
|
func (c *ADX) Meta() IndicatorMeta { |
|
return IndicatorMeta{ |
|
Name: "ADX", |
|
Input: []types.InputArg{ |
|
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"}, // 14 |
|
}, |
|
} |
|
} |
|
|
|
func (c *ADX) CandlePeriods(ctx IIndicatorContext) int16 { |
|
return ctx.Input().Int16("window") + 1 |
|
} |
|
|
|
// Calculate 计算单根k线ADX指标 |
|
func (c *ADX) Calculate(ctx IIndicatorContext) (vector float64) { |
|
window := ctx.Input().Int16("window") |
|
if window <= 0 { |
|
return |
|
} |
|
|
|
kls := ctx.Series(0, window+1) |
|
highs := kls.High() |
|
lows := kls.Low() |
|
closes := kls.Close() |
|
|
|
trs := make([]float64, 0, window) |
|
plusDMs := make([]float64, 0, window) |
|
minusDMs := make([]float64, 0, window) |
|
|
|
for i := range int(window) { |
|
high := highs[i] |
|
low := lows[i] |
|
prevHigh := highs[i+1] |
|
prevLow := lows[i+1] |
|
prevClose := closes[i+1] |
|
|
|
// 计算真实范围(True Range, TR) |
|
tr := math.Max(high-low, math.Max(math.Abs(high-prevClose), math.Abs(low-prevClose))) |
|
trs = append(trs, tr) |
|
|
|
// 计算方向运动 (Directional Movement, DM) |
|
upMove := high - prevHigh |
|
downMove := prevLow - low |
|
plusDM := 0.0 |
|
minusDM := 0.0 |
|
if upMove > downMove && upMove > 0 { |
|
plusDM = upMove |
|
} |
|
if downMove > upMove && downMove > 0 { |
|
minusDM = downMove |
|
} |
|
plusDMs = append(plusDMs, plusDM) |
|
minusDMs = append(minusDMs, minusDM) |
|
} |
|
|
|
sumTR := ta.Sum(trs) |
|
sumPlus := ta.Sum(plusDMs) |
|
sumMinus := ta.Sum(minusDMs) |
|
|
|
// Wilder smoothing |
|
prevSmTR, ok := ctx.State().Get("_smTR", 1) |
|
if !ok { |
|
prevSmTR = sumTR |
|
} |
|
smTR := prevSmTR - prevSmTR/float64(window) + trs[0] |
|
ctx.State().Set("_smTR", smTR) |
|
|
|
prevSmPlus, ok := ctx.State().Get("_smPlus", 1) |
|
if !ok { |
|
prevSmPlus = sumPlus |
|
} |
|
smPlus := prevSmPlus - prevSmPlus/float64(window) + plusDMs[0] |
|
ctx.State().Set("_smPlus", smPlus) |
|
|
|
prevSmMinus, ok := ctx.State().Get("_smMinus", 1) |
|
if !ok { |
|
prevSmMinus = sumMinus |
|
} |
|
smMinus := prevSmMinus - prevSmMinus/float64(window) + minusDMs[0] |
|
ctx.State().Set("_smMinus", smMinus) |
|
|
|
pdi := 0.0 |
|
mdi := 0.0 |
|
if smTR > 0 { |
|
pdi = 100 * smPlus / smTR |
|
mdi = 100 * smMinus / smTR |
|
} |
|
// ctx.State().Set("pdi", pdi) |
|
// ctx.State().Set("mdi", mdi) |
|
|
|
dx := 0.0 |
|
if (pdi + mdi) > 0 { |
|
dx = 100 * math.Abs(pdi-mdi) / (pdi + mdi) |
|
} |
|
|
|
prevAdx, ok := ctx.State().Get("_adx", 1) |
|
if !ok { |
|
// first time: fall back to current DX as initial ADX |
|
prevAdx = dx |
|
} |
|
adx := ((prevAdx*float64(window-1) + dx) / float64(window)) |
|
ctx.State().Set("_adx", adx) |
|
|
|
vector = adx |
|
return |
|
}
|
|
|