見出し画像

株式投資でプログラミング!パート❷ ーYahoo FinanceのWeb APIを使って過去の株価を自由自在に取得する

今回から実際のコーディングに入っていきます。「株式投資にプログラミングを活かす」の企画内容については前回Part1の記事をご覧ください。

YahooFinanceAPIクラスを作る

Part 1で説明した通り、過去の株価取得にはYahoo FinanceのWeb APIを利用しますがここを一からやると膨大な解説になるので、ここでは既存のサンプルコードを利用します。Dennis LeeさんがGitHubで公開しているC#のコードを参考にしていきます。

このコードはAPIの多くの機能を呼び出すようになっていますが、ここではあくまで「株価取得」にフォーカスするのですべては必要ありません。このコードをそのまま利用しても構わないのですが、株価取得には次のメソッドだけを利用すれば十分です。

  public static async Task<List<HistoryPrice>> GetPriceAsync(string symbol, DateTime start, DateTime end)
  public static async Task<string> GetRawAsync(string symbol, DateTime start, DateTime end, string eventType = "history")
  private static double ToUnixTimestamp(DateTime datetime)
  private static async Task<List<HistoryPrice>> ParsePriceAsync(string csvData)

ちょっと解説すると、コアの関数はGetRawAsyncで、ここでWeb APIを直接たたき、"history"というデータタイプを指定します。すると指定した開始日時と終了日時の株価情報がテキストデータで返ってきます。その中でちょっとしたDateTimeの変換でToUnixTimestampという関数を利用しています。また、GetRawAsyncから返ってきた生のテキストデータをParsePriceAsyncというメソッドでHistoryPriceというオブジェクトに変換します。その呼び出しをしているのがGetPriceAsyncです。

APIから返ってきた株価データは次のHistoryPriceクラスにまとめられます。

 class HistoryPrice
   {
       public DateTime Date { get; set; } //日付
       public double Open { get; set; } //始値
       public double High { get; set; } //最高値
       public double Low { get; set; } //最安値
       public double Close { get; set; } //終値
       public long Volume { get; set; } //取引高
       public double AdjClose { get; set; } //調整済み終値
   }

今回はこの4つの関数と1つのクラスだけを使います。

.Net Coreのコンソールアプリを作る

では次の手順でVisual Stuidoのプロジェクトを作ってみてください。Visual Studioを初めて使う方は、インストールと使い方、そしてコンソールアプリの作り方などは『今すぐ書ける 1分間プログラミング』で詳しく解説していますので是非参考にしてください。

TOEIC英単語アプリでもそうでしたが、これまではコンソールアプリはMainのProgram.csファイルにしかコードを書いてきません出いs多が、今回はこの複雑なAPI呼び出しコードが加わります。同じファイルにコードを全部入れると管理しにくくなるので、YahooFiananceのAPI呼び出し関係はすべて別のCSファイルにまとめます。そのため、次の手順でコンソールアプリのプロジェクトを作ってください。

【プロジェクトの準備】

❶ 新しいプロジェクトを作成。その際に.Net Coreのコンソールアプリを選択する。
❷ 名前をCupAndHandleにする(基本的には何でも構いません)
❸ 右サイドのソリューションエクスプローラーにあるCupAndHandleというプロジェクト名のところで右クリック。【追加】⇒【新しい項目】を選択する。
❹ リストの中から[クラス]を選択し、Class.csという名前をYahooFinanceAPI.csに変更。【追加】ボタンをクリックする。

そうするとMainメソッドが入っているProgramc.csのほかに追加したYahooFinanceAPI.csが追加されます。

画像1

このYahooFinanceAPI.csのファイルの中に、APIを呼び出すYahooFinanceAPIというクラスを作って、例の4つの関数を実装します。同じファイルにHistoryPriceクラスも入れてます。

このために、YahooFinanceAPI.csのファイルの中身をごっそり次のコードと入れ替えてください。簡単に言えば全部テキストを選択肢て消去し、そこに下のコードを全部コピペしていれてみてください。

using System;
using System.Net;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace CupAndHandle
{
   /// <summary>
   /// YahooFinanceのWeb API呼び出しメソッド
   /// </summary>
   static class YahooFinanceAPI
   {
       /// <summary>
       /// 株価取得のメソッド
       /// </summary>
       /// <param name="symbol">NYSEのTicker Symbol</param>
       /// <param name="FromDate">開始日時</param>
       /// <param name="ToDate">終了日時</param>
       /// <returns></returns>
       public static List<HistoryPrice> GetStockPrices(string symbol, DateTime FromDate, DateTime ToDate)
       {
           Task<List<HistoryPrice>> historyData = Task.Run(() => {
               return YahooFinanceAPI.GetPriceAsync(symbol, FromDate, ToDate);
           });
           return historyData.Result;
       }

       /// <summary>
       /// CSVデータをHistoryPriveに変換
       /// 参考:https://github.com/dennislwy/YahooFinanceAPI/tree/develop/YahooFinanceAPI
       /// </summary>
       /// <param name="symbol"></param>
       /// <param name="start"></param>
       /// <param name="end"></param>
       /// <returns></returns>
       public static async Task<List<HistoryPrice>> GetPriceAsync(string symbol, DateTime start, DateTime end)
       {
           try
           {
               var csvData = await GetRawAsync(symbol, start, end).ConfigureAwait(false);
               if (csvData != null)
                   return await ParsePriceAsync(csvData).ConfigureAwait(false);
           }
           catch (Exception ex)
           {
               Console.WriteLine(ex.Message);
           }

           return new List<HistoryPrice>();
       }

       /// <summary>
       /// Web APIの呼び出し
       /// </summary>
       /// <param name="symbol"></param>
       /// <param name="start"></param>
       /// <param name="end"></param>
       /// <param name="eventType"></param>
       /// <returns></returns>
       public static async Task<string> GetRawAsync(string symbol, DateTime start, DateTime end, string eventType = "history")
       {
           string csvData = null;

           try
           {
               var url = "https://query1.finance.yahoo.com/v7/finance/download/{0}?period1={1}&period2={2}&interval=1d&events={3}&crumb={4}";

               url = string.Format(url, symbol, Math.Round(ToUnixTimestamp(start), 0),
                   Math.Round(ToUnixTimestamp(end), 0), eventType, "");

               using (var wc = new WebClient())
               {
                   csvData = await wc.DownloadStringTaskAsync(url).ConfigureAwait(false);
               }
           }
           catch (WebException webEx)
           {
               var response = (HttpWebResponse)webEx.Response;
           }
           catch (Exception ex)
           {
               Console.WriteLine(ex.Message);
           }

           return csvData;
       }
       private static double ToUnixTimestamp(DateTime datetime)
       {
           return (datetime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
       }

       private static async Task<List<HistoryPrice>> ParsePriceAsync(string csvData)
       {
           return await Task.Run(() =>
           {
               var lst = new List<HistoryPrice>();

               try
               {
                   var rows = csvData.Split(Convert.ToChar(10));

                   for (var i = 1; i <= rows.Length - 1; i++)
                   {
                       var row = rows[i];
                       if (string.IsNullOrEmpty(row)) continue;

                       var cols = row.Split(',');
                       if (cols[1] == "null") continue;

                       var itm = new HistoryPrice
                       {
                           Date = DateTime.Parse(cols[0]),
                           Open = Convert.ToDouble(cols[1]),
                           High = Convert.ToDouble(cols[2]),
                           Low = Convert.ToDouble(cols[3]),
                           Close = Convert.ToDouble(cols[4]),
                           AdjClose = Convert.ToDouble(cols[5])
                       };

                       if (cols[6] != "null") itm.Volume = Convert.ToInt64(cols[6]);

                       lst.Add(itm);
                   }
               }
               catch (Exception ex)
               {
                   Console.WriteLine(ex.Message);
               }

               return lst;
           }).ConfigureAwait(false);
       }


   }

   /// <summary>
   /// HistoryPriveクラス
   /// </summary>
   class HistoryPrice
   {
       public DateTime Date { get; set; }
       public double Open { get; set; }
       public double High { get; set; }
       public double Low { get; set; }
       public double Close { get; set; }
       public long Volume { get; set; }
       public double AdjClose { get; set; }
   }
}

基本的にオリジナルコードから関数部分だけをコピペしていますが、APIの呼び出しでは若干変更を加えています。元のDennis Leeさんのコードからコピペする場合はさらに別のコードも必要になるので注意してください。

株価取得関数、GetStockPrices

さて、このYahooFianceAPIクラスには最終的なGetPriceAsync関数をラップしたGetStockPricesというのを追加しています。これが皆さんが株価取得で直接使うメソッドです。

public static List<HistoryPrice> GetStockPrices(string symbol, DateTime FromDate, DateTime ToDate)

これはTicker Symbolと開始と終了の日付を入れるだけで株価が取得できます。そこでサンプルプログラムをMainメソッ内ドに書いてみましょう。

using System;
using System.Collections.Generic;

namespace CupAndHandle
{
   class Program
   {
       static void Main(string[] args)
       {
           //株価取得関数を呼び出す
           var results = YahooFinanceAPI.GetStockPrices ("MSFT", new DateTime(2020, 7, 1), new DateTime(2020, 7, 3));

           //結果をすべて書き出す
           foreach (var history in results)
           {
               Console.Write(history.Open + "\t");
               Console.Write(history.Close + "\t");
               Console.Write(history.High + "\t");
               Console.Write(history.Low + "\n");
           }

           Console.ReadLine();
       }
   }
}

ここではMSFT(マイクロソフト)の株価を2020年7月1日から7月3日までの毎日の株価を取得しています(実際は3日は取引は休みなので2日分しかでてきません)。

203.139999      204.699997      206.350006      201.770004
205.679993      206.259995      208.020004      205

他に試したい場合、例えばGoogleはGOOG、アップルはAAPL、アマゾンはAMZN、NvidiaはNVDAです。米国の銘柄コード、Ticker Symbolについては次の記事を参考にしてみてください。

そしてSymbol一覧は次のサイトが参考になります。

さあ、これでどんな銘柄の過去の株価も簡単に取得できるようになりました!

次回のPart 3では

いよいよ「新値買い」プログラミングに入っていきます。まずは一つの銘柄にしぼって、その過去の株価推移をデータ処理してみます。その中でCup and Handleのパターンをどう定義するか、そのアルゴリズムの第一歩を考えていきます。

引き続き乞うご期待!


この記事が気に入ったらサポートをしてみませんか?