Files
monisuo/flutter_monisuo/lib/providers/chart_provider.dart

221 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Candle> _candles = [];
List<Candle> get candles => _candles;
// k_chart 需要的数据格式(转换为 double
List<KLineEntity> 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<void> 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<Candle> _generateMockCandles() {
final now = DateTime.now();
final basePrice = _getBasePrice(_symbol);
final candles = <Candle>[];
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);
}
}
}