- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using System.IO;
- using System.Net;
- namespace KabuPlusDownloader
- {
- public partial class Form1 : Form
- {
- private const string SITE_ID = "id"; //要変更 KABU+から支給されるID
- private const string SITE_PASSWORD = "pass"; //要変更 KABU+から支給されるパスワード
- private const string HOZON_DIR = @"c:\tmp"; //要変更 KABU+からダウンロードするCSV群を保存するフォルダ
- private const int MAX_DOWNLOAD_NUMBER = 4; //要変更 KABU+契約コースによって違う
- /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
- private const int mindate = 20180104;//データがこれ以前は存在しない。
- private string HISTORY_DATE_FILE = string.Format(@"{0}\KabuPlusDownloader.ini", System.Windows.Forms.Application.StartupPath);
- public Form1()
- {
- //InitializeComponent();
- /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
- var update_ymdhms = ReadIniFile();
- if (!ChkLastDownload(update_ymdhms))
- System.Environment.Exit(0);//強制終了
- /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
- var min = int.MaxValue;
- var max = int.MinValue;
- /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
- //保存フォルダ内から最新日付と最古日付を探す(ファイル名)
- SearchOldfileNewfile(ref min, ref max);
- Run(min, max, ref update_ymdhms);
- WriteIniFile(string.Format("{0}", DateTime.Now.ToString("yyyyMMddhhmmss")));
- System.Environment.Exit(0);
- }
- private bool ChkLastDownload(string ymdhms)
- {
- if (ymdhms.Length != 14) return false;
- var ymd = 0; var hms = 0;
- if (!int.TryParse(ymdhms.Substring(0, 8), out ymd)) return false;
- if (!int.TryParse(ymdhms.Substring(8, 6), out hms)) return false;
- var dt = DateTime.Parse(string.Format("{0}/{1}/{2} {3}:{4}:{5}", ymd / 10000, ymd % 10000 / 100, ymd % 100, hms / 10000, hms % 10000 / 100, hms % 100));
- var ts = new TimeSpan(0, 1, 0, 0);
- if (dt < DateTime.Now - ts)//引数の時間がシステム時間から1時間以内だったらfalse
- return true;
- else
- return false;
- }
- private void SearchOldfileNewfile(ref int min, ref int max)
- {
- var files = System.IO.Directory.GetFiles(HOZON_DIR, "*.csv", SearchOption.TopDirectoryOnly);
- for(int i=0;i<files.Length;i++)
- {
- files[i] = Path.GetFileName(files[i]).Replace(".csv", "").Replace("tosho-reit-stock-prices_", "").Replace("tosho-fund-and-others-stock-prices_", "").Replace("tosho-etf-stock-prices_", "").Replace("japan-all-stock-prices-2_", "");
- }
- Array.Sort(files);
- for(int i=0;i<files.Length;i++)
- {
- var d = 0;
- if(int.TryParse(files[i], out d))
- {
- if (min > d) min = d;
- if (max < d) max = d;
- }
- }
- }
- private string ReadIniFile()
- {
- var ret = string.Empty;
- if (File.Exists(HISTORY_DATE_FILE))
- {
- using (var sr = new StreamReader(HISTORY_DATE_FILE, System.Text.Encoding.GetEncoding("shift_jis")))
- {
- ret = sr.ReadToEnd();
- }
- }
- else
- {
- //存在しなかったら初回起動とみなす
- ret = "19000101000000";
- }
- return ret;
- }
- private void WriteIniFile(string s)
- {
- using (var sw = new StreamWriter(HISTORY_DATE_FILE, false, System.Text.Encoding.GetEncoding("shift_jis")))
- {
- sw.Write(s);
- }
- }
- public void Run(int olddate, int newdate, ref string updatetime)
- {
- //DLすべき日付が存在したら、全部で4ファイル(MAX分)ダウンロードする
- var cnt = 0;
- //初回起動対応
- var date_pos = DateTime.Now;
- if (newdate < 0)
- {
- while (true)
- {
- date_pos = date_pos.AddDays(-1);
- if (!IsHoliday(date_pos)) break;
- }
- newdate = int.Parse(date_pos.ToString("yyyyMMdd"));
- olddate = newdate;
- }
- //最新日付がついてる4ファイルを存在チェック。なかったらDLして補完
- DownloadKabuPlusFile(newdate, ref cnt, ref updatetime);
- //最新日付とシステム日付の間で、相場日あり→古い日付から順にDL
- date_pos = Int2Datetime(newdate);
- while (true)
- {
- date_pos = date_pos.AddDays(1);
- if (!IsHoliday(date_pos)) break;
- }
- if (CompareDatetime2Sysdate(date_pos))
- DownloadKabuPlusFile(Datetime2Int(date_pos), ref cnt, ref updatetime);
- //履歴日付と2017/1/4(を含む)の間に相場があった日が存在したら返す
- DownloadKabuPlusFile(olddate, ref cnt, ref updatetime);
- date_pos = Int2Datetime(olddate);
- while (true)
- {
- date_pos = date_pos.AddDays(-1);
- if (!IsHoliday(date_pos)) break;
- }
- if (CompareDatetime2OpeningDate(date_pos))
- DownloadKabuPlusFile(Datetime2Int(date_pos), ref cnt, ref updatetime);
- }
- private bool CompareDatetime2Sysdate(DateTime dt)
- {
- //dtがシステム日付より前だったら迷わずtrueを返す
- //dtがシステム日付より後だったら迷わずfalseを返す
- //dtがシステム日付と同じだったら、16:10(KABU+でデータが用意される時間)以前だったらFalse、以降だったらtrue
- if (int.Parse(dt.ToString("yyyyMMdd")) < int.Parse(DateTime.Now.ToString("yyyyMMdd")))
- return true;
- else if (int.Parse(dt.ToString("yyyyMMdd")) > int.Parse(DateTime.Now.ToString("yyyyMMdd")))
- return false;
- else if (int.Parse(dt.ToString("yyyyMMdd")) == int.Parse(DateTime.Now.ToString("yyyyMMdd")) && int.Parse(DateTime.Now.ToString("hhmm")) > 1610)
- return true;
- else
- return false;
- }
- private bool CompareDatetime2OpeningDate(DateTime dt)
- {
- //dtが2017/01/4以内かどうか
- if (int.Parse(dt.ToString("yyyyMMdd")) >= 20170104)
- return true;
- else
- return false;
- }
- private void DownloadKabuPlusFile(int date, ref int cnt, ref string updatetime)
- {
- for (int i = 0; i < 4; i++)
- {
- if (cnt >= MAX_DOWNLOAD_NUMBER) break;
- var filename = MakeFilename(date, i);
- var path = string.Format(@"{0}\{1}", HOZON_DIR, filename);
- if (!File.Exists(path))
- {
- Download(date, MakeUrl(date, i), string.Format(@"{0}\{1}", HOZON_DIR, filename));
- cnt++;
- updatetime = DateTime.Now.ToString("yyyyMMddhhmmss");
- }
- }
- }
- private string MakeUrl(int date, int kind)
- {
- var url_base = "https://csvex.com/kabu.plus/csv";
- switch (kind)
- {
- case 0://ETF・ETN
- return string.Format("{0}/tosho-etf-stock-prices/daily/tosho-etf-stock-prices_{1:D8}.csv", url_base, date);
- case 1://REIT
- return string.Format("{0}/tosho-reit-stock-prices/daily/tosho-reit-stock-prices_{1:D8}.csv", url_base, date);
- case 2://上場ファンド
- return string.Format("{0}/tosho-fund-and-others-stock-prices/daily/tosho-fund-and-others-stock-prices_{1:D8}.csv", url_base, date);
- case 3:
- return string.Format("{0}/japan-all-stock-prices-2/daily/japan-all-stock-prices-2_{1:D8}.csv", url_base, date);
- }
- return null;
- }
- private string MakeFilename(int date, int kind)
- {
- switch (kind)
- {
- case 0://ETF・ETN
- return string.Format("tosho-etf-stock-prices_{0:D8}.csv", date);
- case 1://REIT
- return string.Format("tosho-reit-stock-prices_{0:D8}.csv", date);
- case 2://上場ファンド
- return string.Format("tosho-fund-and-others-stock-prices_{0:D8}.csv", date);
- case 3:
- return string.Format("japan-all-stock-prices-2_{0:D8}.csv", date);
- }
- return null;
- }
- private void Download(int date, string url, string filename)
- {
- var uri = new Uri(url);
- using (var webClient = new System.Net.WebClient())
- {
- webClient.Credentials = new NetworkCredential(SITE_ID, SITE_PASSWORD);
- using (var reader = new StreamReader(webClient.OpenRead(url), System.Text.Encoding.GetEncoding("shift_jis")))
- {
- var line = reader.ReadToEnd();
- if (line.Trim() != string.Empty)
- {
- try
- {
- System.IO.File.WriteAllText(filename, line, System.Text.Encoding.GetEncoding("shift_jis"));
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
- }
- }
- }
- private DateTime Int2Datetime(int d)
- {
- return DateTime.Parse(string.Format("{0:D4}/{1:D2}/{2:D2}", d/10000, d % 10000 / 100, d % 100));
- }
- private int Datetime2Int(DateTime dt)
- {
- return int.Parse(dt.ToString("yyyyMMdd"));
- }
- private bool IsHoliday(DateTime d)
- {
- //土日
- if (d.DayOfWeek == DayOfWeek.Sunday || d.DayOfWeek == DayOfWeek.Saturday)
- return true;
- // 動かない休日
- if ((d.Month == 1 && d.Day == 1) ||
- (d.Month == 1 && d.Day == 2) ||
- (d.Month == 1 && d.Day == 3) ||
- (d.Month == 2 && d.Day == 11) ||
- (d.Month == 4 && d.Day == 29) ||
- (d.Month == 5 && (d.Day >= 3 && d.Day <= 5)) ||
- (d.Month == 11 && (d.Day == 3 || d.Day == 23)) ||
- (d.Month == 12 && d.Day == 23))
- return true;
- if (d.Year >= 2016 && d.Month == 8 && d.Day == 11) // 山の日
- return true;
- // 春分と秋分(1980~2099年に対応)
- if (d.Month == 3 &&
- d.Day == (int)(20.8431 + 0.242194 * (d.Year - 1980) - (d.Year - 1980) / 4))
- return true;
- if (d.Month == 9 &&
- d.Day == (int)(23.2488 + 0.242194 * (d.Year - 1980) - (d.Year - 1980) / 4))
- return true;
- // ハッピーマンデー
- if (d.Year < 2000)
- {
- if ((d.Month == 1 && d.Day == 15) ||
- (d.Month == 10 && d.Day == 10))
- return true;
- }
- else
- {
- if ((d.Month == 1 || d.Month == 10) &&
- (d.Day >= 8 && d.Day <= 14) && d.DayOfWeek == DayOfWeek.Monday)
- return true;
- }
- if (d.Year < 2003)
- {
- if ((d.Month == 7 && d.Day == 20) ||
- (d.Month == 9 && d.Day == 15))
- return true;
- }
- else
- {
- if ((d.Month == 7 || d.Month == 9) &&
- (d.Day >= 15 && d.Day <= 21) && d.DayOfWeek == DayOfWeek.Monday)
- return true;
- // シルバーウィーク
- if (d.Month == 9 && (d.Day == 21 || d.Day == 22) && d.DayOfWeek == DayOfWeek.Tuesday)
- return IsHoliday(d.AddDays(1));
- }
- // 5月4日が国民の祝日になったので、振替休日が二日以上繰り越す。
- if (d.Year > 2007 && d.Month == 5 && d.Day == 6)
- return d.DayOfWeek >= DayOfWeek.Monday &&
- d.DayOfWeek <= DayOfWeek.Wednesday;
- // 振り替え休日
- if (d.DayOfWeek == DayOfWeek.Monday)
- return IsHoliday(d.AddDays(-1));
- //天皇陛下御退位および皇太子殿下御即位対応
- if (d.Year == 2019)
- {
- if ((d.Month == 4 && d.Day == 30) ||(d.Month == 5 && d.Day <= 2))
- return true;
- }
- return false;
- }
- }
- }