import 'package:decimal/decimal.dart'; /// K 线数据模型 class Candle { final int timestamp; // 毫秒时间戳 final Decimal open; final Decimal high; final Decimal low; final Decimal close; final Decimal volume; const Candle({ required this.timestamp, required this.open, required this.high, required this.low, required this.close, required this.volume, }); /// 从 JSON 解析,使用 Decimal 避免精度丢失 factory Candle.fromJson(Map json) { return Candle( timestamp: json['timestamp'] as int? ?? json['time'] as int? ?? 0, open: _parseDecimal(json['open']), high: _parseDecimal(json['high']), low: _parseDecimal(json['low']), close: _parseDecimal(json['close']), volume: _parseDecimal(json['volume']), ); } /// 安全解析 Decimal static Decimal _parseDecimal(dynamic value) { if (value == null) return Decimal.zero; if (value is Decimal) return value; if (value is num) return Decimal.fromJson(value.toString()); if (value is String) { try { return Decimal.parse(value); } catch (_) { return Decimal.zero; } } return Decimal.zero; } Map toJson() => { 'timestamp': timestamp, 'open': open.toDouble(), 'high': high.toDouble(), 'low': low.toDouble(), 'close': close.toDouble(), 'volume': volume.toDouble(), }; /// 转换为 double(供图表库使用) double get openDouble => open.toDouble(); double get highDouble => high.toDouble(); double get lowDouble => low.toDouble(); double get closeDouble => close.toDouble(); double get volumeDouble => volume.toDouble(); /// 安全加法(防止溢出) Decimal safeAdd(Decimal other) { try { return open + other; } catch (_) { return Decimal.zero; } } /// 安全乘法(防止溢出) Decimal safeMultiply(Decimal other) { try { return open * other; } catch (_) { return Decimal.zero; } } /// 格式化价格显示 String formatPrice({int decimalPlaces = 2}) { return close.toStringAsFixed(decimalPlaces); } /// 格式化成交量显示 String formatVolume() { final vol = volume.toDouble(); if (vol >= 1000000000) { return '${(vol / 1000000000).toStringAsFixed(2)}B'; } else if (vol >= 1000000) { return '${(vol / 1000000).toStringAsFixed(2)}M'; } else if (vol >= 1000) { return '${(vol / 1000).toStringAsFixed(2)}K'; } return vol.toStringAsFixed(2); } }