- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Text.RegularExpressions;
- using Zanetti.Data;
- namespace Zanetti
- {
- class UpdateOneBrand
- {
- public void Update()
- {
- AbstractBrand br = Env.Frame.ChartCanvas.GetBrand();
- if (br.IsBuiltIn)//個別銘柄だけ
- return;
- var title = Env.Frame.SetMainFrameTxt;
- Env.Frame.SetMainFrameTxt = string.Format("{0} データ更新中", title);
- SortedDictionary<int, NewDailyData> Prices = new SortedDictionary<int, NewDailyData>();
- var page = string.Empty;
- var latestdate = LatestDate();
- if (latestdate < int.Parse(DateTime.Today.ToString("yyyyMMdd")))
- {
- GetPage(br.Code, out page);
- ParsePage(br.Code, page, Prices);
- if (Prices.Count > 0)
- UpdateDataFarm(br.Code, Prices);
- }
- latestdate = LatestDate();
- if (DateTime.UtcNow.Hour >= 0 && DateTime.UtcNow.Hour <= 6)
- {
- Prices = new SortedDictionary<int, NewDailyData>();
- GetPageDetail(br.Code, out page);
- ParsePageDetail(br.Code, page, Prices, latestdate);
- if (Prices.Count > 0)
- UpdateDataFarm(br.Code, Prices);
- }
- Env.Frame.SetMainFrameTxt = title;
- }
- private bool IsIndex(int code)
- {
- return code == (int)BuiltInIndex.Nikkei225 ||
- code == (int)BuiltInIndex.TOPIX;
- }
- private int LatestDate()
- {
- var br = Env.Frame.ChartCanvas.GetBrand();
- var path = Util.GetDailyDataFileName(br.Code);
- var last = -1;
- if (File.Exists(path))
- {
- using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
- {
- var buffer = new byte[fs.Length];
- fs.Read(buffer, 0, buffer.Length);
- last = BitConverter.ToInt32(buffer, buffer.Length > 32 ? buffer.Length - 32 : 0);
- }
- }
- return last;
- }
- private void UpdateDataFarm(int code, SortedDictionary<int, NewDailyData> prices)
- {
- var farm = (DailyDataFarm)Env.BrandCollection.FindBrand(code).CreateDailyFarm(prices.Count);
- foreach (var pair in prices)
- {
- if (IsIndex(code) ? false : pair.Value.volume == 0)
- continue;
- farm.UpdateDataFarm(pair.Key, pair.Value);
- }
- farm.Save(Util.GetDailyDataFileName(code));
- }
- private void GetPageDetail(int code, out string page)
- {
- var url = string.Format("https://stocks.finance.yahoo.co.jp/stocks/detail/?code={0}", code);
- page = string.Empty;
- try
- {
- using (var reader = new StreamReader(Util.HttpDownload(url)))
- page = reader.ReadToEnd();
- }
- catch (System.Net.WebException e)
- {
- Console.WriteLine(e.Message);
- }
- }
- private void GetPage(int code, out string page)
- {
- var url = string.Format("https://stocks.finance.yahoo.co.jp/stocks/history/?code={0}", code);
- page = string.Empty;
- try
- {
- using (var reader = new StreamReader(Util.HttpDownload(url)))
- page = reader.ReadToEnd();
- }
- catch (System.Net.WebException e)
- {
- Console.WriteLine(e.Message);
- }
- }
- private bool HtmlAgilitySelectSingleNodeValue(HtmlAgilityPack.HtmlDocument htmldoc, string xpath, ref double val)
- {
- var doc = htmldoc.DocumentNode.SelectSingleNode(xpath);
- if (doc == null)
- return false;
- if (!double.TryParse(doc.InnerText.Replace(",", "").Trim(), out val))
- return false;
- return true;
- }
- private bool HtmlAgilitySelectSingleNodeDatetime(HtmlAgilityPack.HtmlDocument htmldoc, string xpath, ref DateTime dt)
- {
- var doc = htmldoc.DocumentNode.SelectSingleNode(xpath);
- if (doc == null)
- return false;
- var s = doc.InnerText.Replace("リアルタイム株価", "");
- if (!DateTime.TryParse(s, out dt))
- return false;
- return true;
- }
- private bool HtmlAgilitySelectNodes(HtmlAgilityPack.HtmlDocument htmldoc, string xpath, ref double val, ref DateTime dt, int cnt, int pos)
- {
- var doc = htmldoc.DocumentNode.SelectNodes(xpath);
- if (doc == null)
- return false;
- if (doc.Count != cnt)
- return false;
- var valid = new Regex(xpath);
- var s = doc[pos].InnerText.Replace(",", "").Replace("株", "").Trim();
- valid = new Regex("(?<value>[0-9,.]+)((?<datetime>[0-9:]+))");
- var matches = valid.Matches(s);
- if (matches.Count != 1)
- return false;
- if (!double.TryParse(matches[0].Groups["value"].Value, out val))
- return false;
- if (!DateTime.TryParse(matches[0].Groups["datetime"].Value, out dt))
- dt = DateTime.MinValue;
- return true;
- }
- private void ParsePageDetail(int code, string buf, SortedDictionary<int, NewDailyData> dict, int max_d)
- {
- var date = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
- var shift = 10;
- var kabuka = new double[4];
- var volume = .0;
- var open_datetime = DateTime.MinValue;
- var high_datetime = DateTime.MinValue;
- var low_datetime = DateTime.MinValue;
- var close_datetime = DateTime.MinValue;
- var volume_datetime = DateTime.MinValue;
- var xpath = string.Empty;
- var htmldoc = new HtmlAgilityPack.HtmlDocument();
- htmldoc.LoadHtml(buf);
- if (!HtmlAgilitySelectSingleNodeValue(htmldoc, "//td[@class=\"stoksPrice\"]", ref kabuka[3]))
- return;
- if (!HtmlAgilitySelectSingleNodeDatetime(htmldoc, "//dd[@class=\"yjSb real\"]", ref close_datetime))
- return;
- if (!HtmlAgilitySelectNodes(htmldoc, "//div[@class=\"innerDate\"]/div[@class=\"lineFi clearfix\"]/dl[@class=\"tseDtl\"]/dd[@class=\"ymuiEditLink mar0\"]", ref kabuka[0], ref open_datetime, 3, 0))
- return;
- if (!HtmlAgilitySelectNodes(htmldoc, "//div[@class=\"innerDate\"]/div[@class=\"lineFi clearfix\"]/dl[@class=\"tseDtl\"]/dd[@class=\"ymuiEditLink mar0\"]", ref kabuka[1], ref high_datetime, 3, 1))
- return;
- if (!HtmlAgilitySelectNodes(htmldoc, "//div[@class=\"innerDate\"]/div[@class=\"lineFi clearfix\"]/dl[@class=\"tseDtl\"]/dd[@class=\"ymuiEditLink mar0\"]", ref kabuka[2], ref low_datetime, 3, 2))
- return;
- if (!HtmlAgilitySelectNodes(htmldoc, "//div[@class=\"innerDate\"]/div[@class=\"lineFi clearfix\"]/dl[@class=\"tseDtlDelay\"]/dd[@class=\"ymuiEditLink mar0\"]", ref volume, ref volume_datetime, 4, 1))
- return;
- dict[date] = new NewDailyData
- {
- open = (int)(kabuka[0] * shift),
- high = (int)(kabuka[1] * shift),
- low = (int)(kabuka[2] * shift),
- close = (int)(kabuka[3] * shift),
- volume = (int)volume
- };
- }
- private void ParsePage(int code, string buf, SortedDictionary<int, NewDailyData> dict)
- {
- var valid = new Regex(
- @"<td>(?<year>\d{4})年(?<month>1?\d)月(?<day>\d?\d)日</td>[\n]" +
- "<td>(?<open>[0-9,.]+)</td>[\n]<td>(?<high>[0-9,.]+)</td>[\n]<td>(?<low>[0-9,.]+)</td>[\n]" +
- "<td>(?<close>[0-9,.]+)</td>[\n](?:<td>(?<volume>[0-9,]+)</td>[\n])?");
- var invalid = new Regex("該当する期間のデータはありません。<br>期間をご確認ください。");
- var obs = new Regex("該当する銘柄はありません。<br>再度銘柄(コード)を入力し、「表示」ボタンを押してください。");
- var empty = new Regex("<dl class=\"stocksInfo\">\n<dt></dt><dd class=\"category yjSb\"></dd>");
- if (buf.Trim() == string.Empty)
- return;
- var matches = valid.Matches(buf);
- if (matches.Count == 0)
- return;
- try
- {
- var shift = IsIndex(code) ? 100 : 10; // 指数は100倍、株式は10倍で記録する
- const NumberStyles s = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
- foreach (Match m in matches)
- {
- var date = new DateTime(int.Parse(m.Groups["year"].Value),
- int.Parse(m.Groups["month"].Value),
- int.Parse(m.Groups["day"].Value));
- var int_date = Util.DateToInt(date);
- dict[int_date] = new NewDailyData
- {
- open = (int)(double.Parse(m.Groups["open"].Value, s) * shift),
- high = (int)(double.Parse(m.Groups["high"].Value, s) * shift),
- low = (int)(double.Parse(m.Groups["low"].Value, s) * shift),
- close = (int)(double.Parse(m.Groups["close"].Value, s) * shift),
- volume = m.Groups["volume"].Value == "" ? 0 : (int)double.Parse(m.Groups["volume"].Value, s)
- };
- }
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
- }
- }
- }