stock_market_data 0.1.4
stock_market_data: ^0.1.4 copied to clipboard
Package to get base indicators from the stock and base stats like buy and hold CAGR and total drawdown
import 'package:flutter/material.dart';
import 'package:stock_market_data/stock_market_data.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ThemeMode _themeMode = ThemeMode.light;
void _toggleTheme() {
setState(() {
_themeMode = _themeMode == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
});
}
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Flutter Demo',
themeMode: _themeMode,
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: HomeScreen(onToggleTheme: _toggleTheme),
);
}
class HomeScreen extends StatelessWidget {
final VoidCallback onToggleTheme;
const HomeScreen({required this.onToggleTheme, super.key});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Stock Market Example'),
actions: [
IconButton(
icon: const Icon(Icons.brightness_6),
onPressed: onToggleTheme,
),
],
),
body: Container(
padding: const EdgeInsets.all(20),
child: const SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 355,
width: double.infinity,
child: BuyAndHoldResult(exampleTicker: 'AAPL'),
),
SizedBox(
height: 355,
width: double.infinity,
child: BuyAndHoldResult(exampleTicker: 'VUSA.AS'),
),
SizedBox(
height: 355,
width: double.infinity,
child: BuyAndHoldResult(exampleTicker: 'ES=F, GC=F'),
),
],
),
),
),
);
}
class BuyAndHoldResult extends StatefulWidget {
final String exampleTicker;
const BuyAndHoldResult({required this.exampleTicker, super.key});
@override
State<BuyAndHoldResult> createState() => _BuyAndHoldResultState();
}
class _BuyAndHoldResultState extends State<BuyAndHoldResult> {
final TextEditingController controller = TextEditingController();
BuyAndHoldStrategyResult backTest = BuyAndHoldStrategyResult();
bool loading = true;
String error = '';
@override
void initState() {
super.initState();
controller.text = widget.exampleTicker;
load();
}
@override
Widget build(BuildContext context) => Column(
children: [
const Text('Ticker from yahoo finance'),
TextField(controller: controller),
MaterialButton(
onPressed: load,
color: Theme.of(context).primaryColor,
child: const Text('Load'),
),
Expanded(
child: error.isNotEmpty
? Text('Error: $error')
: loading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.all(8),
children: [
const SizedBox(height: 20),
_BackTestResult(backTest),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
_IndicatorsData(controller.text),
),
),
color: Colors.teal,
child: const Text('Indicators'),
),
const SizedBox(width: 20),
MaterialButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
_YearStatsWidget(controller.text),
),
),
color: Colors.orange,
child: const Text('Year stats'),
),
],
),
],
),
),
],
);
Future<void> load() async {
try {
setState(() {
error = '';
loading = true;
});
backTest = await StockMarketDataService().getBackTestResultForSymbol(
controller.text,
);
setState(() => loading = false);
} catch (e) {
setState(() {
error = 'Error getting the symbol ${controller.text}:\n $e';
loading = false;
});
}
}
}
class _BackTestResult extends StatelessWidget {
final BuyAndHoldStrategyResult backTest;
const _BackTestResult(this.backTest);
@override
Widget build(BuildContext context) => Column(
children: [
_row('CAGR', backTest.cagr.toStringAsFixed(2)),
_row('Max Drawdown', backTest.maxDrawdown.toStringAsFixed(2)),
_row('MAR', backTest.mar.toStringAsFixed(2)),
_row('Trading years', backTest.tradingYears.toStringAsFixed(2)),
_row('Start date', backTest.startDate.toString()),
_row('End date', backTest.endDate.toString()),
_row('Current drawdown', backTest.currentDrawdown.toStringAsFixed(2)),
_row('End price', backTest.endPrice.toStringAsFixed(2)),
],
);
Widget _row(String label, String value) => Row(
children: [
Expanded(child: Text(label)),
Expanded(child: Text(value)),
],
);
}
class _YearStatsWidget extends StatefulWidget {
final String symbol;
const _YearStatsWidget(this.symbol);
@override
State<_YearStatsWidget> createState() => _YearStatsWidgetState();
}
class _YearStatsWidgetState extends State<_YearStatsWidget> {
List<YahooFinanceCandleData> prices = [];
List<YearlyStats> yearlyStats = [];
@override
void initState() {
super.initState();
load();
}
Future<void> load() async {
final YahooFinanceResponse response = await const YahooFinanceDailyReader()
.getDailyDTOs(widget.symbol);
prices = response.candlesData;
yearlyStats = YearlyCalculations.calculate(prices);
setState(() {});
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Year stats')),
body: Column(
children: [
const Row(
children: [
Expanded(child: Text('Year')),
Expanded(child: Text('Variation')),
Expanded(child: Text('Drawdown')),
],
),
const SizedBox(height: 10),
Expanded(
child: ListView.builder(
itemCount: yearlyStats.length,
itemBuilder: (context, index) {
final y = yearlyStats[index];
return Row(
children: [
Expanded(child: Text(y.year.toString())),
Expanded(child: Text(y.variation.toStringAsFixed(2))),
Expanded(child: Text(y.drawdown.toStringAsFixed(2))),
],
);
},
),
),
],
),
);
}
class _IndicatorsData extends StatefulWidget {
final String symbol;
const _IndicatorsData(this.symbol);
@override
State<_IndicatorsData> createState() => _IndicatorsDataState();
}
class _IndicatorsDataState extends State<_IndicatorsData> {
final TextEditingController indicatorsController = TextEditingController(
text:
'SMA_20,EMA_20,RSI_20,STDDEV_20,VWMA_20,BB_20,%R_20,MFI_14,BOP_14,P_1',
);
List<YahooFinanceCandleData> prices = [];
Future<void> load() async {
prices = await StockMarketDataService().getCandlesWithIndicators(
widget.symbol,
indicatorsController.text.split(','),
);
prices = prices.reversed.toList();
setState(() {});
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Indicators')),
body: ListView.builder(
itemCount: prices.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return Container(
margin: const EdgeInsets.all(10),
child: Column(
children: [
const Text('Indicators'),
TextField(controller: indicatorsController, maxLines: 3),
MaterialButton(
onPressed: load,
color: Theme.of(context).primaryColor,
child: const Text('Load'),
),
],
),
);
}
final candle = prices[index - 1];
return _PriceWithIndicators(candle);
},
),
);
}
class _PriceWithIndicators extends StatelessWidget {
final YahooFinanceCandleData candle;
const _PriceWithIndicators(this.candle);
@override
Widget build(BuildContext context) => Card(
child: Container(
margin: const EdgeInsets.all(10),
child: Column(
children: [
Text('Date: ${candle.date}'),
Text('Close: ${candle.close}'),
...candle.indicators.entries.map(
(entry) => Text('${entry.key}: ${entry.value?.toStringAsFixed(2)}'),
),
],
),
),
);
}