OmegaChartのソースコードの保守
Revisão | 5e51b27cc9eb546e343469111d4f4306fc487666 (tree) |
---|---|
Hora | 2021-04-29 16:08:32 |
Autor | panacoran <panacoran@user...> |
Commiter | panacoran |
YahooファイナンスからETF/ETNの株価をダウンロードできないのを直す
@@ -125,25 +125,29 @@ namespace Zanetti.DataSource.Specialized | ||
125 | 125 | |
126 | 126 | private FetchResult.Status GetPage(int code, DateTime begin, DateTime end, out string page) |
127 | 127 | { |
128 | - string url; | |
128 | + string codeString; | |
129 | 129 | switch (code) |
130 | 130 | { |
131 | 131 | case (int)BuiltInIndex.Nikkei225: |
132 | + codeString = "998407.O"; | |
133 | + break; | |
132 | 134 | case (int)BuiltInIndex.TOPIX: |
133 | - var realCode = code == (int)BuiltInIndex.Nikkei225 ? "998407.O" : "998405.T"; | |
134 | - url = | |
135 | - $"https://info.finance.yahoo.co.jp/history/?code={realCode}&sy={begin.Year}&sm={begin.Month}&sd={begin.Day}&ey={end.Year}&em={end.Month}&ed={end.Day}&tm=d"; | |
135 | + codeString = "998405.T"; | |
136 | 136 | break; |
137 | 137 | default: |
138 | - url = | |
139 | - $"https://finance.yahoo.co.jp/quote/{code}.T/history?from={begin:yyyyMMdd}&to={end:yyyyMMdd}&timeFrame=d&page=1"; | |
138 | + codeString = code.ToString(); | |
140 | 139 | break; |
141 | 140 | } |
141 | + var oldUrl = $"https://info.finance.yahoo.co.jp/history/?code={codeString}&sy={begin.Year}&sm={begin.Month}&sd={begin.Day}&ey={end.Year}&em={end.Month}&ed={end.Day}&tm=d"; | |
142 | + var url = $"https://finance.yahoo.co.jp/quote/{codeString}.T/history?from={begin:yyyyMMdd}&to={end:yyyyMMdd}&timeFrame=d&page=1"; | |
142 | 143 | page = null; |
144 | + retry: | |
143 | 145 | try |
144 | 146 | { |
145 | 147 | using (var reader = new StreamReader(Util.HttpDownload(url))) |
148 | + { | |
146 | 149 | page = reader.ReadToEnd(); |
150 | + } | |
147 | 151 | } |
148 | 152 | catch (WebException e) |
149 | 153 | { |
@@ -157,7 +161,10 @@ namespace Zanetti.DataSource.Specialized | ||
157 | 161 | case HttpStatusCode.BadGateway: |
158 | 162 | return FetchResult.Status.Retry; |
159 | 163 | case HttpStatusCode.NotFound: |
160 | - return FetchResult.Status.Failure; | |
164 | + if (url == oldUrl) | |
165 | + return FetchResult.Status.Failure; | |
166 | + url = oldUrl; | |
167 | + goto retry; | |
161 | 168 | } |
162 | 169 | throw; |
163 | 170 | case WebExceptionStatus.Timeout: |
@@ -172,18 +179,18 @@ namespace Zanetti.DataSource.Specialized | ||
172 | 179 | return FetchResult.Status.Success; |
173 | 180 | } |
174 | 181 | |
175 | - private static readonly Regex ValidNew = new Regex( | |
182 | + private static readonly Regex Valid = new Regex( | |
176 | 183 | @"<tr[^>]*><th[^>]*>(?<year>\d{4})年(?<month>1?\d)月(?<day>\d?\d)日<\/th><td[^>]+>(?:<span[^>]+>)+(?<open>[0-9,.]+)<\/span>.*?<\/td><td[^>]+>(?:<span[^>]+>)+(?<high>[0-9,.]+)<\/span>.*?<\/td><td[^>]+>(?:<span[^>]+>)+(?<low>[0-9,.]+)<\/span>.+?<\/td><td[^>]+>(?:<span[^>]+>)+(?<close>[0-9,.]+)<\/span>.+?<\/td>(?:<td.*?>(?<volume>[0-9,.]+)<\/span>.+?<\/td>)?<\/tr>", |
177 | 184 | RegexOptions.Compiled); |
178 | 185 | |
179 | - private static readonly Regex NoDataNew = new Regex("時系列情報がありません"); | |
186 | + private static readonly Regex NoData = new Regex("時系列情報がありません"); | |
180 | 187 | |
181 | - private static readonly Regex Valid = new Regex( | |
188 | + private static readonly Regex ValidOld = new Regex( | |
182 | 189 | @"<td>(?<year>\d{4})年(?<month>1?\d)月(?<day>\d?\d)日</td>" + |
183 | 190 | "<td>(?<open>[0-9,.]+)</td><td>(?<high>[0-9,.]+)</td><td>(?<low>[0-9,.]+)</td>" + |
184 | 191 | "<td>(?<close>[0-9,.]+)</td>(?:<td>(?<volume>[0-9,]+)</td>)?", RegexOptions.Compiled); |
185 | 192 | |
186 | - private static readonly Regex NoData = new Regex("該当する期間のデータはありません。<br>期間をご確認ください。"); | |
193 | + private static readonly Regex NoDataOld = new Regex("該当する期間のデータはありません。<br>期間をご確認ください。"); | |
187 | 194 | |
188 | 195 | private static readonly Regex Obs = |
189 | 196 | new Regex("該当する銘柄はありません。<br>再度銘柄(コード)を入力し、「表示」ボタンを押してください。", RegexOptions.Compiled); |
@@ -197,25 +204,22 @@ namespace Zanetti.DataSource.Specialized | ||
197 | 204 | var dict = new SortedDictionary<int, NewDailyData>(); |
198 | 205 | MatchCollection matches; |
199 | 206 | |
200 | - if (code == (int)BuiltInIndex.Nikkei225 || code == (int)BuiltInIndex.TOPIX) | |
207 | + matches = Valid.Matches(buf); | |
208 | + if (matches.Count == 0) | |
201 | 209 | { |
202 | - matches = Valid.Matches(buf); | |
203 | - if (matches.Count == 0) | |
210 | + if (!NoData.IsMatch(buf)) | |
204 | 211 | { |
205 | - if (Obs.Match(buf).Success || Empty.Match(buf).Success) // 上場廃止(銘柄データが空のこともある) | |
206 | - return new FetchResult {ReturnStatus = FetchResult.Status.Obsolete}; | |
207 | - if (!NoData.Match(buf).Success) | |
208 | - throw new Exception("ページから株価を取得できません。"); | |
209 | - // ここに到達するのは出来高がないか株価が用意されていない場合 | |
212 | + matches = ValidOld.Matches(buf); | |
213 | + if (matches.Count == 0) | |
214 | + { | |
215 | + if (Obs.Match(buf).Success || Empty.Match(buf).Success) // 上場廃止(銘柄データが空のこともある) | |
216 | + return new FetchResult {ReturnStatus = FetchResult.Status.Obsolete}; | |
217 | + if (!NoDataOld.IsMatch(buf)) | |
218 | + throw new Exception("ページから株価を取得できません。"); | |
219 | + // ここに到達するのは出来高がないか株価が用意されていない場合 | |
220 | + } | |
210 | 221 | } |
211 | 222 | } |
212 | - else | |
213 | - { | |
214 | - matches = ValidNew.Matches(buf); | |
215 | - if (matches.Count == 0 && !NoDataNew.Match(buf).Success) | |
216 | - throw new Exception("ページから株価を取得できません。"); | |
217 | - } | |
218 | - | |
219 | 223 | try |
220 | 224 | { |
221 | 225 | var shift = IsIndex(code) ? 100 : 10; // 指数は100倍、株式は10倍で記録する |