如何在Flutter中正确计算Supertrend指标?
Supertrend指标计算错误问题排查与修复
我基于现有正常运行的EMA代码实现Supertrend指标,但无法得到准确数值,以下是相关代码:
正常运行的EMA代码
import 'dart:ui'; import '../../models/candle.dart'; import '../../models/indicator.dart'; import 'dart:math'; class EMA extends Indicator { EMA({ required int period, required Color color, }) : super( name: "EMA $period", dependsOnNPrevCandles: period, calculator: (int index, List<Candle> candles) { double multiplier = (2 / (period + 1)); double sum = 0; for (int i = 0; i < period; i++) { sum += candles[i + index].close; } double ema = sum / period; ema = multiplier * (candles[index].close - ema) + ema; return [ema]; }, indicatorComponentsStyles: [ IndicatorStyle(name: "EMA $period", color: color), ], ); }
存在问题的Supertrend代码
import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../models/candle.dart'; import '../../models/indicator.dart'; class SupertrendIndicator extends Indicator { SupertrendIndicator( {required int length, required double multiplier, required Color color}) : super( name: "Supertrend $length/$multiplier", dependsOnNPrevCandles: length + 1, calculator: (index, candles) { final candle = candles[index]; final basicUpper = (candle.high + candle.low) / 2 + multiplier * _atr(candles, index, length); final basicLower = (candle.high + candle.low) / 2 - multiplier * _atr(candles, index, length); final finalUpper = basicUpper; final finalLower = basicLower; final supertrend = candle.close > basicUpper ? finalUpper : candle.close < basicLower ? finalLower : previousSupertrend > previousBasicUpper ? basicLower : basicUpper; return [supertrend]; }, indicatorComponentsStyles: [ IndicatorStyle(name: "Supertrend", color: color), ]); static double _atr(List<Candle> candles, int index, int length) { double sum = 0; for (int i = 0; i < length; i++) { final trueRange1 = math.max( candles[i].high - candles[i].low, (i > 0 ? candles[i].high - candles[i - 1].close : 0), ); final trueRange2 = math.max( trueRange1, (i > 0 ? candles[i - 1].close - candles[i].low : 0), ); sum += trueRange2; } return sum / length; } }
问题分析
你的Supertrend代码存在三个核心错误:
- ATR计算逻辑错误:原
_atr函数取的是历史最早的length根K线,而非当前索引index往前的length根K线,完全偏离了ATR的计算规则;同时True Range的初始值处理错误。 - 核心逻辑缺失:直接使用未定义的
previousSupertrend等变量,且未实现Supertrend上下轨的动态调整规则(需基于前一次的计算结果调整当前上下轨)。 - 无状态设计缺陷:Supertrend依赖前一次的计算结果,但原代码没有保存状态的机制,无法实现连续计算。
修复后的Supertrend代码
import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../models/candle.dart'; import '../../models/indicator.dart'; class SupertrendIndicator extends Indicator { final int length; final double multiplier; // 保存前一次计算的状态值 double? _prevSupertrend; double? _prevFinalUpper; double? _prevFinalLower; SupertrendIndicator({ required this.length, required this.multiplier, required Color color, }) : super( name: "Supertrend $length/$multiplier", dependsOnNPrevCandles: length + 1, calculator: (int index, List<Candle> candles) { // 初始周期(不足length根K线)返回空值,避免无效计算 if (index < length - 1) { return [null]; } final currentCandle = candles[index]; // 计算当前周期的ATR final atr = _calculateATR(candles, index, length); // 计算基础上下轨 final basicUpper = (currentCandle.high + currentCandle.low) / 2 + multiplier * atr; final basicLower = (currentCandle.high + currentCandle.low) / 2 - multiplier * atr; double finalUpper, finalLower; if (_prevFinalUpper == null || _prevFinalLower == null) { // 首次计算直接使用基础上下轨 finalUpper = basicUpper; finalLower = basicLower; } else { // 根据前一次结果调整当前上下轨 if (currentCandle.close > _prevFinalUpper!) { finalLower = math.max(basicLower, _prevFinalLower!); finalUpper = basicUpper; } else if (currentCandle.close < _prevFinalLower!) { finalUpper = math.min(basicUpper, _prevFinalUpper!); finalLower = basicLower; } else { // 价格在区间内,保持前一次的上下轨 finalUpper = _prevFinalUpper!; finalLower = _prevFinalLower!; } } // 确定当前Supertrend值 double supertrend; if (_prevSupertrend == null) { supertrend = finalLower; } else { supertrend = currentCandle.close > _prevSupertrend! ? finalLower : finalUpper; } // 更新状态变量,供下一次计算使用 _prevSupertrend = supertrend; _prevFinalUpper = finalUpper; _prevFinalLower = finalLower; return [supertrend]; }, indicatorComponentsStyles: [ IndicatorStyle(name: "Supertrend", color: color), ], ); // 正确计算ATR:基于当前index往前取length根K线的真实波幅平均值 static double _calculateATR(List<Candle> candles, int index, int length) { double sumTR = 0.0; for (int i = index - length + 1; i <= index; i++) { if (i == 0) { // 第一根K线的真实波幅=最高价-最低价 sumTR += candles[i].high - candles[i].low; } else { final currentHigh = candles[i].high; final currentLow = candles[i].low; final prevClose = candles[i - 1].close; // 取三个值中的最大值作为真实波幅 final tr1 = currentHigh - currentLow; final tr2 = (currentHigh - prevClose).abs(); final tr3 = (prevClose - currentLow).abs(); sumTR += math.max(math.max(tr1, tr2), tr3); } } return sumTR / length; } }
修复说明
- 添加状态变量:新增
_prevSupertrend、_prevFinalUpper、_prevFinalLower保存前一次计算结果,满足Supertrend的依赖需求。 - 修正ATR计算:从当前索引往前取
length根K线,正确计算每根K线的真实波幅(TR)后取平均值。 - 实现完整上下轨规则:根据当前收盘价与前一次上下轨的关系,动态调整当前的最终上下轨。
- 处理初始周期:前
length-1根K线返回null,避免无效计算。
内容的提问来源于stack exchange,提问作者user12575366
相关产品推荐
相关产品推荐

