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

221 lines
5.6 KiB
Dart
Raw Normal View History

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);
}
}
}