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

237 lines
5.8 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:interactive_chart/interactive_chart.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 int maPeriod;
final int emaPeriod;
final int bollPeriod;
const IndicatorSettings({
this.showMA = true,
this.showEMA = false,
this.showBOLL = false,
this.showVOL = true,
this.maPeriod = 7,
this.emaPeriod = 14,
this.bollPeriod = 20,
});
IndicatorSettings copyWith({
bool? showMA,
bool? showEMA,
bool? showBOLL,
bool? showVOL,
int? maPeriod,
int? emaPeriod,
int? bollPeriod,
}) {
return IndicatorSettings(
showMA: showMA ?? this.showMA,
showEMA: showEMA ?? this.showEMA,
showBOLL: showBOLL ?? this.showBOLL,
showVOL: showVOL ?? this.showVOL,
maPeriod: maPeriod ?? this.maPeriod,
emaPeriod: emaPeriod ?? this.emaPeriod,
bollPeriod: bollPeriod ?? this.bollPeriod,
);
}
}
/// 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;
List<CandleData> get candleData {
final baseData = _candles
.map((c) => CandleData(
timestamp: c.timestamp,
open: c.open,
high: c.high,
low: c.low,
close: c.close,
volume: c.volume,
))
.toList();
// 如果开启MA计算均线并添加到趋势线
if (_indicators.showMA && baseData.isNotEmpty) {
final ma7 = CandleData.computeMA(baseData, 7);
final ma14 = CandleData.computeMA(baseData, 14);
final ma30 = CandleData.computeMA(baseData, 30);
return List.generate(baseData.length, (i) {
return CandleData(
timestamp: baseData[i].timestamp,
open: baseData[i].open,
high: baseData[i].high,
low: baseData[i].low,
close: baseData[i].close,
volume: baseData[i].volume,
trends: [ma7[i], ma14[i], ma30[i]],
);
});
}
return baseData;
}
// 状态
bool _loading = false;
bool get loading => _loading;
String? _error;
String? get error => _error;
/// 切换币种
void setSymbol(String symbol) {
if (_symbol != symbol) {
_symbol = symbol;
loadCandles(); // 切换币种显示loading
}
}
/// 切换时间周期
void setInterval(ChartInterval interval) {
if (_interval != interval) {
_interval = interval;
loadCandles(showLoading: false); // 切换时间周期不显示loading
}
}
/// 更新指标设置
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
// final response = await _api.getKlineData(
// symbol: _symbol,
// interval: _interval.value,
// );
// 临时使用模拟数据
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 线数据
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,
));
final random = (i * 17) % 100 / 100;
final change = basePrice * 0.02 * (random - 0.5);
final open = basePrice + change;
final close = open + basePrice * 0.01 * ((i * 13) % 100 / 100 - 0.5);
final high = open > close ? open * 1.005 : close * 1.005;
final low = open < close ? open * 0.995 : close * 0.995;
final volume = 1000 + (i * 37) % 5000;
candles.add(Candle(
timestamp: time.millisecondsSinceEpoch,
open: open,
high: high,
low: low,
close: close,
volume: volume.toDouble(),
));
}
return candles;
}
double _getBasePrice(String symbol) {
switch (symbol.toUpperCase()) {
case 'BTC':
return 42000.0;
case 'ETH':
return 2200.0;
case 'BNB':
return 300.0;
case 'SOL':
return 100.0;
case 'XRP':
return 0.5;
case 'DOGE':
return 0.08;
default:
return 100.0;
}
}
}