import 'package:flutter/foundation.dart'; import 'package:flutter_chen_kchart/k_chart.dart'; import 'package:decimal/decimal.dart'; import '../models/candle.dart'; /// 时间周期 enum ChartInterval { min1('1分', '1m'), min5('5分', '5m'), min15('15分', '15m'), hour1('1小时', '1h'), hour4('4小时', '4h'), day1('1天', '1d'); final String label; final String value; const ChartInterval(this.label, this.value); } /// 技术指标设置 class IndicatorSettings { final bool showMA; final bool showEMA; final bool showBOLL; final bool showVOL; final bool showMACD; final bool showKDJ; final bool showRSI; final bool showWR; final bool showCCI; const IndicatorSettings({ this.showMA = true, this.showEMA = false, this.showBOLL = false, this.showVOL = true, this.showMACD = true, this.showKDJ = false, this.showRSI = false, this.showWR = false, this.showCCI = false, }); IndicatorSettings copyWith({ bool? showMA, bool? showEMA, bool? showBOLL, bool? showVOL, bool? showMACD, bool? showKDJ, bool? showRSI, bool? showWR, bool? showCCI, }) { return IndicatorSettings( showMA: showMA ?? this.showMA, showEMA: showEMA ?? this.showEMA, showBOLL: showBOLL ?? this.showBOLL, showVOL: showVOL ?? this.showVOL, showMACD: showMACD ?? this.showMACD, showKDJ: showKDJ ?? this.showKDJ, showRSI: showRSI ?? this.showRSI, showWR: showWR ?? this.showWR, showCCI: showCCI ?? this.showCCI, ); } } /// K 线数据 Provider class ChartProvider extends ChangeNotifier { // 当前币种 String _symbol = 'BTC'; String get symbol => _symbol; // 时间周期 ChartInterval _interval = ChartInterval.hour1; ChartInterval get interval => _interval; // 技术指标 IndicatorSettings _indicators = const IndicatorSettings(); IndicatorSettings get indicators => _indicators; // K 线数据(原始) List _candles = []; List get candles => _candles; // k_chart 需要的数据格式(转换为 double) List get klineData { return _candles.map((c) { return KLineEntity.fromCustom( open: c.openDouble, high: c.highDouble, low: c.lowDouble, close: c.closeDouble, vol: c.volumeDouble, time: c.timestamp, ); }).toList(); } // 状态 bool _loading = false; bool get loading => _loading; String? _error; String? get error => _error; /// 切换币种 void setSymbol(String symbol) { if (_symbol != symbol) { _symbol = symbol; loadCandles(); } } /// 切换时间周期 void setInterval(ChartInterval interval) { if (_interval != interval) { _interval = interval; loadCandles(showLoading: false); } } /// 更新指标设置 void updateIndicators(IndicatorSettings settings) { _indicators = settings; notifyListeners(); } /// 加载 K 线数据 Future loadCandles({bool showLoading = true}) async { if (showLoading) { _loading = true; _error = null; notifyListeners(); } try { // TODO: 对接真实 API // 临时使用模拟数据 await Future.delayed(const Duration(milliseconds: 300)); _candles = _generateMockCandles(); if (showLoading) { _loading = false; notifyListeners(); } else { notifyListeners(); } } catch (e) { if (showLoading) { _loading = false; } _error = e.toString(); notifyListeners(); } } /// 生成模拟 K 线数据(使用 Decimal 防止精度丢失) List _generateMockCandles() { final now = DateTime.now(); final basePrice = _getBasePrice(_symbol); final candles = []; for (int i = 100; i >= 0; i--) { final time = now.subtract(Duration( minutes: _interval == ChartInterval.min1 ? i : _interval == ChartInterval.min5 ? i * 5 : _interval == ChartInterval.min15 ? i * 15 : _interval == ChartInterval.hour1 ? i * 60 : _interval == ChartInterval.hour4 ? i * 240 : i * 1440, )); // 使用 Decimal 进行安全计算 final random = Decimal.parse(((i * 17) % 100 / 100).toString()); final change = basePrice * Decimal.parse('0.02') * (random - Decimal.parse('0.5')); final open = basePrice + change; final close = open + basePrice * Decimal.parse('0.01') * (Decimal.parse(((i * 13) % 100 / 100).toString()) - Decimal.parse('0.5')); final high = open > close ? open * Decimal.parse('1.005') : close * Decimal.parse('1.005'); final low = open < close ? open * Decimal.parse('0.995') : close * Decimal.parse('0.995'); final volume = Decimal.fromInt(1000 + (i * 37) % 5000); candles.add(Candle( timestamp: time.millisecondsSinceEpoch, open: open, high: high, low: low, close: close, volume: volume, )); } return candles; } Decimal _getBasePrice(String symbol) { switch (symbol.toUpperCase()) { case 'BTC': return Decimal.parse('42000'); case 'ETH': return Decimal.parse('2200'); case 'BNB': return Decimal.parse('300'); case 'SOL': return Decimal.parse('100'); case 'XRP': return Decimal.parse('0.5'); case 'DOGE': return Decimal.parse('0.08'); default: return Decimal.fromInt(100); } } }