Auger/lib/services/stock_price_service.dart

82 lines
2.6 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
class StockPricePoint {
final DateTime date;
final double close;
const StockPricePoint({required this.date, required this.close});
}
class StockPriceService {
Future<List<StockPricePoint>> fetchPriceHistory(
String ticker, {
String range = '1mo',
String interval = '1d',
}) async {
final normalizedTicker = ticker.trim().toUpperCase();
final uri = Uri.parse(
'https://query1.finance.yahoo.com/v8/finance/chart/$normalizedTicker?interval=$interval&range=$range',
);
final response = await http.get(uri, headers: {
'User-Agent': 'Mozilla/5.0',
'Accept': 'application/json',
});
if (response.statusCode != 200) {
throw Exception('Failed to fetch price history for $normalizedTicker: ${response.statusCode}');
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
final chart = json['chart'] as Map<String, dynamic>?;
final results = chart?['result'] as List?;
if (results == null || results.isEmpty) {
throw Exception('No price history returned for $normalizedTicker');
}
final result = results.first as Map<String, dynamic>;
final timestamps = (result['timestamp'] as List?) ?? const [];
final indicators = result['indicators'] as Map<String, dynamic>?;
final adjCloseSeries = ((indicators?['adjclose'] as List?) ?? const [])
.cast<Map<String, dynamic>?>();
final quoteSeries = ((indicators?['quote'] as List?) ?? const [])
.cast<Map<String, dynamic>?>();
final adjCloseEntry = adjCloseSeries.isNotEmpty ? adjCloseSeries.first : null;
final quoteEntry = quoteSeries.isNotEmpty ? quoteSeries.first : null;
final adjCloseValues = adjCloseEntry == null ? null : adjCloseEntry['adjclose'];
final quoteCloseValues = quoteEntry == null ? null : quoteEntry['close'];
final closes = (adjCloseValues as List?) ?? (quoteCloseValues as List?) ?? const [];
final points = <StockPricePoint>[];
final itemCount = timestamps.length < closes.length ? timestamps.length : closes.length;
for (int i = 0; i < itemCount; i++) {
final timestamp = timestamps[i];
final close = closes[i];
if (timestamp is! num || close is! num) {
continue;
}
points.add(
StockPricePoint(
date: DateTime.fromMillisecondsSinceEpoch(
timestamp.toInt() * 1000,
isUtc: true,
).toLocal(),
close: close.toDouble(),
),
);
}
if (points.isEmpty) {
throw Exception('No valid price points returned for $normalizedTicker');
}
return points;
}
}