Flutter股市数据获取插件stock_market_data的使用

发布于 1周前 作者 vueper 来自 Flutter

Flutter股市数据获取插件stock_market_data的使用

简介

stock_market_data 是一个用于获取股票市场基础指标和统计数据的Flutter插件。该插件可以帮助你计算买入并持有策略的年化收益率(CAGR)、最大回撤(Drawdown)和收益风险比(MAR),并且支持添加多种技术指标到股票数据中。

Stock market data example screenshot Stock market data example screenshot with indicators

特性

  • 计算买入并持有策略的年化收益率(CAGR)、最大回撤(Drawdown)和收益风险比(MAR)
  • 支持添加以下技术指标到 YahooFinanceCandleData
    • SMA(简单移动平均线)
    • EMA(指数移动平均线)
    • RSI(相对强弱指数)
    • BB(布林带)
    • BOP(平衡量)
    • MFI(资金流量指数)
    • P(价格)
    • STDDEV(标准差)
    • VWMA(成交量加权移动平均线)
    • %R(威廉指标)

开始使用

添加依赖

在你的 pubspec.yaml 文件中添加 stock_market_data 依赖:

dependencies:
  stock_market_data: ^0.0.1

获取买入并持有策略的回测结果

import 'package:stock_market_data/stock_market_data.dart';

void main() async {
  BuyAndHoldStrategyResult backTest = await StockMarketDataService().getBackTestResultForSymbol('GOOG');
  print('CAGR: ${backTest.cagr}');
  print('Max Drawdown: ${backTest.maxDrawdown}');
  print('MAR: ${backTest.mar}');
}

获取带有技术指标的K线数据

import 'package:stock_market_data/stock_market_data.dart';

void main() async {
  List<YahooFinanceCandleData> prices = await StockMarketDataService().getCandlesWithIndicators('GOOG', ['EMA_20', 'RSI_20']);
  for (var price in prices) {
    print('Date: ${price.date}, Close: ${price.close}');
    print('EMA_20: ${price.indicators['EMA_20']}, RSI_20: ${price.indicators['RSI_20']}');
  }
}

获取带有所有技术指标的K线数据

import 'package:stock_market_data/stock_market_data.dart';

void main() async {
  List<YahooFinanceCandleData> prices = await StockMarketDataService().getCandlesWithIndicators('GOOG', [
    'SMA_20', 'EMA_20', 'RSI_20', 'STDDEV_20', 'VWMA_20', 'BB_20', '%R_20', 'MFI_14', 'BOP_14', 'P_1'
  ]);
  for (var price in prices) {
    print('Date: ${price.date}, Close: ${price.close}');
    for (var key in price.indicators.keys) {
      print('$key: ${price.indicators[key]}');
    }
  }
}

依赖

示例代码

以下是一个完整的示例代码,展示了如何在Flutter应用中使用 stock_market_data 插件来获取股票数据和显示结果。

import 'package:flutter/material.dart';
import 'package:stock_market_data/stock_market_data.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Stock Market Example'),
      ),
      body: Container(
        padding: const EdgeInsets.all(20.0),
        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(text: '');
  BuyAndHoldStrategyResult backTest = BuyAndHoldStrategyResult();
  bool loading = true;
  String error = '';

  @override
  void initState() {
    super.initState();
    controller.text = widget.exampleTicker;
    load();
  }

  @override
  Widget build(BuildContext context) {
    return 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 != ''
              ? Text('Error: $error')
              : loading
                  ? const Center(
                      child: CircularProgressIndicator(),
                    )
                  : Column(
                      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'),
                            ),
                          ],
                        )
                      ],
                    ),
        ),
      ],
    );
  }

  void load() async {
    try {
      error = '';
      loading = true;
      setState(() {});

      backTest = await StockMarketDataService().getBackTestResultForSymbol(controller.text);
      loading = false;
      setState(() {});
    } catch (e) {
      error = 'Error getting the symbol ${controller.text}:\n $e';
      setState(() {});
    }
  }
}

class _BackTestResult extends StatelessWidget {
  final BuyAndHoldStrategyResult backTest;

  const _BackTestResult(this.backTest);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            const Expanded(child: Text('CAGR')),
            Expanded(child: Text(backTest.cagr.toStringAsFixed(2))),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('Max Drawdown')),
            Expanded(child: Text(backTest.maxDrawdown.toStringAsFixed(2))),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('MAR')),
            Expanded(child: Text(backTest.mar.toStringAsFixed(2))),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('Trading years')),
            Expanded(child: Text(backTest.tradingYears.toStringAsFixed(2))),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('Start date')),
            Expanded(child: Text(backTest.startDate.toString())),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('End date')),
            Expanded(child: Text(backTest.endDate.toString())),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('Current drawdown')),
            Expanded(child: Text(backTest.currentDrawdown.toStringAsFixed(2))),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Expanded(child: Text('End price')),
            Expanded(child: Text(backTest.endPrice.toStringAsFixed(2))),
          ],
        ),
      ],
    );
  }
}

class _YearStatsWidget extends StatefulWidget {
  final String symbol;

  const _YearStatsWidget(this.symbol);

  @override
  State<_YearStatsWidget> createState() => _YearStatsWidgetState();
}

class _YearStatsWidgetState extends State<_YearStatsWidget> {
  @override
  void initState() {
    super.initState();
    load();
  }

  List<YahooFinanceCandleData> prices = [];
  List<YearlyStats> yearlyStats = [];

  void load() async {
    YahooFinanceResponse response = await const YahooFinanceDailyReader().getDailyDTOs(widget.symbol);

    prices = response.candlesData;
    yearlyStats = YearlyCalculations.calculate(prices);

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return 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 YearlyStats currentYearlyStat = yearlyStats[index];

                return Row(
                  children: [
                    Expanded(child: Text(currentYearlyStat.year.toString())),
                    Expanded(child: Text(currentYearlyStat.variation.toStringAsFixed(2))),
                    Expanded(child: Text(currentYearlyStat.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 = [];

  void load() async {
    prices = await StockMarketDataService().getCandlesWithIndicators(
      widget.symbol,
      indicatorsController.text.split(','),
    );

    // Reverse to show in a list
    prices = prices.reversed.toList();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return 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 i = index - 1;
          return _PriceWithIndicators(prices[i]);
        },
      ),
    );
  }
}

class _PriceWithIndicators extends StatelessWidget {
  final YahooFinanceCandleData candle;

  const _PriceWithIndicators(this.candle);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Container(
        margin: const EdgeInsets.all(10),
        child: Column(
          children: [
            Text('Date: ${candle.date}'),
            Text('Close: ${candle.close}'),
            Column(
                children: candle.indicators.keys
                    .map((key) => Text('$key: ${candle.indicators[key]?.toStringAsFixed(2)}'))
                    .toList())
          ],
        ),
      ),
    );
  }
}

在Pub.dev上点赞

如果你喜欢这个插件,请在 Pub.dev 上给它点赞和支持!

发布插件到Pub.dev

如果你想发布这个插件到Pub.dev,可以使用以下命令:

dart pub publish

希望这个插件能帮助你在Flutter应用中更方便地处理股票市场数据!如果有任何问题或建议,请随时联系我。


更多关于Flutter股市数据获取插件stock_market_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter股市数据获取插件stock_market_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用stock_market_data插件来获取股市数据的示例代码。假设你已经在你的Flutter项目中添加了stock_market_data依赖。

首先,确保在你的pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  stock_market_data: ^最新版本号  # 替换为实际的最新版本号

然后,运行flutter pub get来获取依赖。

接下来,我们编写一个简单的Flutter应用来展示如何使用stock_market_data插件获取股市数据。

main.dart

import 'package:flutter/material.dart';
import 'package:stock_market_data/stock_market_data.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Stock Market Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: StockMarketDataScreen(),
    );
  }
}

class StockMarketDataScreen extends StatefulWidget {
  @override
  _StockMarketDataScreenState createState() => _StockMarketDataScreenState();
}

class _StockMarketDataScreenState extends State<StockMarketDataScreen> {
  String stockSymbol = 'AAPL'; // 示例股票代码:苹果公司
  String stockData = '';

  @override
  void initState() {
    super.initState();
    fetchStockData();
  }

  void fetchStockData() async {
    try {
      final stockMarketData = StockMarketData();
      final data = await stockMarketData.getStockMarketData(stockSymbol);
      
      // 假设返回的数据包含一个名为'price'的字段
      setState(() {
        stockData = 'Price: \$${data['price']}';
      });
    } catch (e) {
      print('Error fetching stock data: $e');
      setState(() {
        stockData = 'Error fetching stock data';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stock Market Data'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Stock Symbol: $stockSymbol',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 20),
            Text(
              stockData,
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. API限制和权限:某些股市数据API可能有访问限制或需要API密钥。请确保你了解并遵守你使用的特定API的条款和条件。

  2. 错误处理:上面的代码只简单地打印了错误消息。在实际应用中,你可能需要更详细的错误处理逻辑,例如向用户显示友好的错误消息。

  3. 数据字段:上面的代码假设返回的数据包含一个名为price的字段。你需要根据你使用的API的文档来调整这部分代码。

  4. 依赖更新:确保你的stock_market_data插件是最新版本,并查阅其文档以获取最新的使用方法和API变化。

这个示例展示了如何使用stock_market_data插件在Flutter应用中获取并展示股市数据。根据你的具体需求,你可能需要进一步定制和扩展这个示例。

回到顶部