feat: K线数据使用Decimal类型,防止数值精度丢失和溢出
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import 'package:decimal/decimal.dart';
|
||||
|
||||
/// K 线数据模型
|
||||
class Candle {
|
||||
final int timestamp; // 毫秒时间戳
|
||||
final double open;
|
||||
final double high;
|
||||
final double low;
|
||||
final double close;
|
||||
final double volume;
|
||||
final Decimal open;
|
||||
final Decimal high;
|
||||
final Decimal low;
|
||||
final Decimal close;
|
||||
final Decimal volume;
|
||||
|
||||
const Candle({
|
||||
required this.timestamp,
|
||||
@@ -16,23 +18,82 @@ class Candle {
|
||||
required this.volume,
|
||||
});
|
||||
|
||||
/// 从 JSON 解析,使用 Decimal 避免精度丢失
|
||||
factory Candle.fromJson(Map<String, dynamic> json) {
|
||||
return Candle(
|
||||
timestamp: json['timestamp'] as int? ?? json['time'] as int? ?? 0,
|
||||
open: (json['open'] as num?)?.toDouble() ?? 0,
|
||||
high: (json['high'] as num?)?.toDouble() ?? 0,
|
||||
low: (json['low'] as num?)?.toDouble() ?? 0,
|
||||
close: (json['close'] as num?)?.toDouble() ?? 0,
|
||||
volume: (json['volume'] as num?)?.toDouble() ?? 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<String, dynamic> toJson() => {
|
||||
'timestamp': timestamp,
|
||||
'open': open,
|
||||
'high': high,
|
||||
'low': low,
|
||||
'close': close,
|
||||
'volume': volume,
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user