OmegaChartのソースコードの保守
Revisão | 5b7c4bb6608fd3025fc30a28da452e2149bc6404 (tree) |
---|---|
Hora | 2015-03-26 19:39:58 |
Autor | panacoran <panacoran@user...> |
Commiter | panacoran |
Data.csのコーディングスタイルを元に戻す
@@ -19,667 +19,564 @@ using Zanetti.Indicators; | ||
19 | 19 | |
20 | 20 | namespace Zanetti.Data |
21 | 21 | { |
22 | - | |
23 | - //primitive indicatorをサポートするためのもの | |
24 | - internal class DataFarmPrimitiveAccess | |
25 | - { | |
26 | - //delegateの引数になるためにこの形でないとだめ | |
27 | - internal static double GetDate(TradeData tr) | |
28 | - { | |
29 | - return (double)tr.Farm.GetInt(tr.Offset); | |
30 | - } | |
31 | - internal static double GetOpen(TradeData tr) | |
32 | - { | |
33 | - return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.OPEN_OFFSET), tr); | |
34 | - } | |
35 | - internal static double GetHigh(TradeData tr) | |
36 | - { | |
37 | - return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.HIGH_OFFSET), tr); | |
38 | - } | |
39 | - internal static double GetLow(TradeData tr) | |
40 | - { | |
41 | - return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.LOW_OFFSET), tr); | |
42 | - } | |
43 | - internal static double GetClose(TradeData tr) | |
44 | - { | |
45 | - return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.CLOSE_OFFSET), tr); | |
46 | - } | |
47 | - internal static double GetVolume(TradeData tr) | |
48 | - { | |
49 | - //オーバーフロー対策の一時しのぎ | |
50 | - return AdjustVolume((double)(uint)tr.Farm.GetInt(tr.Offset + DataFarm.VOLUME_OFFSET), tr); | |
51 | - } | |
52 | - internal static double GetCreditLong(TradeData tr) | |
53 | - { | |
54 | - return AdjustVolume((double)tr.Farm.GetInt(tr.Offset + DataFarm.CREDITLONG_OFFSET), tr); | |
55 | - } | |
56 | - internal static double GetCreditShort(TradeData tr) | |
57 | - { | |
58 | - return AdjustVolume((double)tr.Farm.GetInt(tr.Offset + DataFarm.CREDITSHORT_OFFSET), tr); | |
59 | - } | |
60 | - | |
61 | - private static double AdjustPrice(double value, TradeData tr) | |
62 | - { | |
63 | - double split = Env.Preference.AdjustSplit ? tr.Farm.CalcSplitRatio(tr.Date) : 1; | |
64 | - if (value == 0 && GetVolume(tr) == 0) | |
65 | - { //出来高がない日は価格が0と記入されているので前日の終値で代用 | |
66 | - TradeData pr = tr.Prev; | |
67 | - return pr == null ? 0 : GetClose(tr.Prev); | |
68 | - } | |
69 | - else | |
70 | - return tr.Farm.Brand.PriceScale * value / split; | |
71 | - } | |
72 | - private static double AdjustVolume(double value, TradeData tr) | |
73 | - { | |
74 | - double split = Env.Preference.AdjustSplit ? tr.Farm.CalcSplitRatio(tr.Date) : 1; | |
75 | - return value * split; | |
76 | - } | |
77 | - } | |
78 | - | |
79 | - internal class NewDailyData | |
80 | - { | |
81 | - public int open; | |
82 | - public int high; | |
83 | - public int low; | |
84 | - public int close; | |
85 | - public int volume; | |
86 | - } | |
87 | - | |
88 | - internal abstract class DataFarm : IDisposable | |
89 | - { | |
90 | - public const int RECORD_LENGTH = 32; | |
91 | - | |
92 | - public const int OPEN_OFFSET = 4; | |
93 | - public const int HIGH_OFFSET = 8; | |
94 | - public const int LOW_OFFSET = 12; | |
95 | - public const int CLOSE_OFFSET = 16; | |
96 | - public const int VOLUME_OFFSET = 20; | |
97 | - public const int CREDITSHORT_OFFSET = 24; | |
98 | - public const int CREDITLONG_OFFSET = 28; | |
99 | - | |
100 | - protected bool _isEmpty; //エラーなどで利用不能なことを示すフラグ | |
101 | - protected AbstractBrand _brand; | |
102 | - | |
103 | - protected byte[] _farm; //一次データ。同一のDataFarmオブジェクトを他の銘柄に使いまわすときもあるので、必要以上の長さが確保されることもある | |
104 | - protected int _byteLength; //_farmの論理的な長さ | |
105 | - protected TradeData[] _data; //必要に応じて生成されるTradeDataの列。一目など未来の日付のデータがあると配列の長さは_farmに対応する分より大きいこともある | |
106 | - protected int _filledLength; //最新日付までの長さ | |
107 | - | |
108 | - public DataFarm() | |
109 | - { | |
110 | - } | |
111 | - public abstract void LoadFor(AbstractBrand br); | |
112 | - | |
113 | - public AbstractBrand Brand | |
114 | - { | |
115 | - get | |
116 | - { | |
117 | - return _brand; | |
118 | - } | |
119 | - } | |
120 | - | |
121 | - public int TotalLength | |
122 | - { | |
123 | - get | |
124 | - { | |
125 | - return _data.Length; | |
126 | - } | |
127 | - } | |
128 | - public int FilledLength | |
129 | - { | |
130 | - get | |
131 | - { | |
132 | - return _filledLength; | |
133 | - } | |
134 | - } | |
135 | - public bool IsEmpty | |
136 | - { | |
137 | - get | |
138 | - { | |
139 | - return _isEmpty; | |
140 | - } | |
141 | - } | |
142 | - | |
143 | - public byte[] RawDataImage | |
144 | - { | |
145 | - get | |
146 | - { | |
147 | - return _farm; | |
148 | - } | |
149 | - } | |
150 | - | |
151 | - internal int GetInt(int offset) | |
152 | - { | |
153 | - if (offset >= _byteLength) | |
154 | - throw new IndexOutOfRangeException(); | |
155 | - unsafe | |
156 | - { | |
157 | - fixed (byte* p = &_farm[0]) | |
158 | - { | |
159 | - return *(int*)(p + offset); | |
160 | - } | |
161 | - } | |
162 | - } | |
163 | - | |
164 | - public TradeData GetByIndex(int index) | |
165 | - { | |
166 | - Debug.Assert(_data != null); | |
167 | - if (index < 0 || index >= _data.Length) | |
168 | - throw new TradeDataOverflowException(index.ToString() + " is out of range"); | |
169 | - TradeData td = _data[index]; | |
170 | - if (td != null) return td; //cache hit | |
171 | - | |
172 | - td = new TradeData(this, index, index * RECORD_LENGTH); | |
173 | - _data[index] = td; | |
174 | - return td; | |
175 | - } | |
176 | - | |
177 | - public abstract int LastDate { get; } | |
178 | - public abstract int FirstDate { get; } | |
179 | - | |
180 | - public int DateToIndex(int date) | |
181 | - { | |
182 | - return DateToIndex(0, _filledLength, date); | |
183 | - } | |
184 | - private int DateToIndex(int begin, int end, int date) | |
185 | - { | |
186 | - //binary search | |
187 | - if (end - begin <= 1) | |
188 | - return begin; | |
189 | - else | |
190 | - { | |
191 | - int h = (begin + end) / 2; | |
192 | - int t = GetByIndex(h).Date; | |
193 | - if (date < t) | |
194 | - return DateToIndex(begin, h, date); | |
195 | - else | |
196 | - return DateToIndex(h, end, date); | |
197 | - } | |
198 | - } | |
199 | - | |
200 | - //分割比率の取得 | |
201 | - public double CalcSplitRatio(int date) | |
202 | - { | |
203 | - return _brand.CalcSplitRatio(date, this.LastDate); | |
204 | - | |
205 | - } | |
206 | - | |
207 | - public void Dispose() | |
208 | - { | |
209 | - _farm = null; | |
210 | - _data = null; | |
211 | - } | |
212 | - | |
213 | - internal static int GetInt(byte[] rawdata, int offset) | |
214 | - { | |
215 | - Debug.Assert(rawdata.Length > 0); | |
216 | - unsafe | |
217 | - { | |
218 | - fixed (byte* p = &rawdata[0]) | |
219 | - { | |
220 | - return *(int*)(p + offset); | |
221 | - } | |
222 | - } | |
223 | - } | |
224 | - internal static void SetInt(byte[] rawdata, int offset, int value) | |
225 | - { | |
226 | - unsafe | |
227 | - { | |
228 | - fixed (byte* p = &rawdata[0]) | |
229 | - { | |
230 | - *(int*)(p + offset) = value; | |
231 | - } | |
232 | - } | |
233 | - } | |
234 | - internal static void SetUInt(byte[] rawdata, int offset, uint value) | |
235 | - { | |
236 | - unsafe | |
237 | - { | |
238 | - fixed (byte* p = &rawdata[0]) | |
239 | - { | |
240 | - *(uint*)(p + offset) = value; | |
241 | - } | |
242 | - } | |
243 | - } | |
244 | - | |
245 | - protected static int AdjustPrice(int raw, double ratio) | |
246 | - { | |
247 | - return (int)((double)raw / ratio); | |
248 | - } | |
249 | - protected static int AdjustVolume(int raw, double ratio) | |
250 | - { | |
251 | - return (int)((double)raw * ratio); | |
252 | - } | |
253 | - } | |
254 | - | |
255 | - internal class DailyDataFarm : DataFarm | |
256 | - { | |
257 | - | |
258 | - protected int _extraDataOffset; //1日単位でデータの追加をしたときのために | |
259 | - | |
260 | - public DailyDataFarm() | |
261 | - : base() | |
262 | - { | |
263 | - } | |
264 | - public override void LoadFor(AbstractBrand br) | |
265 | - { | |
266 | - _brand = br; | |
267 | - Construct(Util.GetDailyDataFileName(br.Code), 0); | |
268 | - } | |
269 | - public void LoadFor(AbstractBrand br, int extra_dates) | |
270 | - { | |
271 | - _brand = br; | |
272 | - Construct(Util.GetDailyDataFileName(br.Code), extra_dates); | |
273 | - } | |
274 | - private void Construct(string filename, int extra_dates) | |
275 | - { | |
276 | - _isEmpty = true; | |
277 | -#if DOJIMA | |
278 | - Dojima.DojimaUtil.HalfDailyDataFarmCache.Clear(_brand); | |
279 | -#endif | |
280 | - if (File.Exists(filename)) | |
281 | - { | |
282 | - int length = (int)new FileInfo(filename).Length; | |
283 | - if (length == 0) return; | |
284 | - | |
285 | - if (_farm == null || _farm.Length < length + extra_dates * RECORD_LENGTH) | |
286 | - _farm = new byte[length + extra_dates * RECORD_LENGTH]; | |
287 | - int future_length = Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Daily); | |
288 | - _filledLength = length / RECORD_LENGTH; | |
289 | - _data = new TradeData[_filledLength + future_length]; | |
290 | - _byteLength = length; | |
291 | - _extraDataOffset = 0; | |
292 | - _isEmpty = false; | |
293 | - | |
294 | - FileStream s = null; | |
295 | - try | |
296 | - { | |
297 | - s = new FileStream(filename, FileMode.Open); | |
298 | - s.Read(_farm, 0, length); | |
299 | - } | |
300 | - finally | |
301 | - { | |
302 | - if (s != null) s.Close(); | |
303 | - } | |
304 | - // 個別銘柄の株価データの先頭にある出来高0のデータを取り除く | |
305 | - var basic = _brand as BasicBrand; | |
306 | - if (basic == null || basic.Market == MarketType.B || basic.Market == MarketType.Custom) | |
307 | - return; | |
308 | - var idx = 0; | |
309 | - for (var i = 0; i < _filledLength; i++) | |
310 | - { | |
311 | - unsafe | |
312 | - { | |
313 | - var head = i * RECORD_LENGTH; | |
314 | - fixed (byte* p = &_farm[0]) | |
315 | - { | |
316 | - if (*(int*)(p + head + VOLUME_OFFSET) == 0) | |
317 | - idx += RECORD_LENGTH; | |
318 | - else | |
319 | - break; | |
320 | - } | |
321 | - } | |
322 | - } | |
323 | - if (idx == 0) | |
324 | - return; | |
325 | - _byteLength -= idx; | |
326 | - _filledLength = _byteLength / RECORD_LENGTH; | |
327 | - for (var i = 0; i < _byteLength; i++) | |
328 | - _farm[i] = _farm[i + idx]; | |
329 | - } | |
330 | - } | |
331 | - public void Save(string filename) | |
332 | - { | |
333 | - if (_farm != null) | |
334 | - { //エラーハンドリングできていない | |
335 | - FileStream s = new FileStream(filename, FileMode.Create); | |
336 | - s.Write(_farm, 0, _byteLength + _extraDataOffset); | |
337 | - s.Close(); | |
338 | - } | |
339 | - } | |
340 | - internal void WriteExtraData(int record_offset, int value) | |
341 | - { | |
342 | - unsafe | |
343 | - { | |
344 | - fixed (byte* p = &_farm[0]) | |
345 | - { | |
346 | - *(int*)(p + _byteLength + _extraDataOffset + record_offset) = value; | |
347 | - } | |
348 | - } | |
349 | - } | |
350 | - internal void ProgressExtraDataAddress() | |
351 | - { | |
352 | - _extraDataOffset += RECORD_LENGTH; | |
353 | - Debug.Assert(_extraDataOffset <= _farm.Length); | |
354 | - } | |
355 | - | |
356 | - //連続的に複数の日付を更新することもできるが、増加方向であることが必須 | |
357 | - internal void UpdateDataFarm(int date, NewDailyData td) | |
358 | - { | |
359 | - int ld; | |
360 | - if (this.IsEmpty) | |
361 | - { | |
362 | - //とりあえず1日書き込める分だけ初期化 | |
363 | - if (_farm == null) | |
364 | - { | |
365 | - _farm = new byte[RECORD_LENGTH * 200]; //この上限はどこかで取得すべきだが | |
366 | - _filledLength = 0; | |
367 | - _data = null; | |
368 | - _byteLength = 0; | |
369 | - _extraDataOffset = 0; | |
370 | - } | |
371 | - ld = 0; | |
372 | - } | |
373 | - else | |
374 | - ld = this.LastDate; | |
375 | - | |
376 | - | |
377 | - int offset; | |
378 | - if (ld < date) | |
379 | - { | |
380 | - offset = _byteLength + _extraDataOffset; //emptyのときは常にこれ | |
381 | - _extraDataOffset += RECORD_LENGTH; | |
382 | - } | |
383 | - else | |
384 | - { | |
385 | - offset = _byteLength - RECORD_LENGTH; | |
386 | - do | |
387 | - { | |
388 | - int t = GetInt(offset); | |
389 | - if (t == date) | |
390 | - break; | |
391 | - else if (t < date) | |
392 | - { | |
393 | - offset += RECORD_LENGTH; | |
394 | - break; | |
395 | - } | |
396 | - else | |
397 | - offset -= RECORD_LENGTH; | |
398 | - } while (true); | |
399 | - } | |
400 | - | |
401 | - unsafe | |
402 | - { | |
403 | - fixed (byte* p = &_farm[0]) | |
404 | - { | |
405 | - byte* a = p + offset; | |
406 | - *(int*)(a + 0) = date; | |
407 | - *(int*)(a + 4) = td.open; | |
408 | - *(int*)(a + 8) = td.high; | |
409 | - *(int*)(a + 12) = td.low; | |
410 | - *(int*)(a + 16) = td.close; | |
411 | - *(int*)(a + 20) = td.volume; | |
412 | - } | |
413 | - } | |
414 | - | |
415 | - } | |
416 | - | |
417 | - //次の2つはTradeDataを作らないようにしている、注意 | |
418 | - public override int LastDate | |
419 | - { | |
420 | - get | |
421 | - { | |
422 | - return GetInt(_byteLength - RECORD_LENGTH); | |
423 | - } | |
424 | - } | |
425 | - public override int FirstDate | |
426 | - { | |
427 | - get | |
428 | - { | |
429 | - return GetInt(0); | |
430 | - } | |
431 | - } | |
432 | - | |
433 | - } | |
434 | - internal class WeeklyDataFarm : DataFarm | |
435 | - { | |
436 | - private int _firstDate; | |
437 | - private int _lastDate; | |
438 | - | |
439 | - public WeeklyDataFarm() | |
440 | - : base() | |
441 | - { | |
442 | - } | |
443 | - public override void LoadFor(AbstractBrand br) | |
444 | - { | |
445 | - _brand = br; | |
446 | - Construct(Util.GetDailyDataFileName(br.Code)); | |
447 | - } | |
448 | - | |
449 | - private void Construct(string filename) | |
450 | - { | |
451 | - _isEmpty = true; | |
452 | - if (File.Exists(filename)) | |
453 | - { | |
454 | - int length = (int)new FileInfo(filename).Length; | |
455 | - if (length > 0) | |
456 | - { | |
457 | - //まずは日足を読む | |
458 | - byte[] daily = new byte[length]; | |
459 | - FileStream s = null; | |
460 | - try | |
461 | - { | |
462 | - s = new FileStream(filename, FileMode.Open); | |
463 | - s.Read(daily, 0, length); | |
464 | - } | |
465 | - finally | |
466 | - { | |
467 | - if (s != null) s.Close(); | |
468 | - } | |
469 | - | |
470 | - _isEmpty = false; | |
471 | - _firstDate = GetInt(daily, 0); | |
472 | - _lastDate = GetInt(daily, daily.Length - RECORD_LENGTH); | |
473 | - var daily_begin = Util.IntToDate(GetInt(daily, 0)); | |
474 | - var weekly_begin = daily_begin.AddDays(-(int)daily_begin.DayOfWeek); | |
475 | - var daily_end = Util.IntToDate(GetInt(daily, daily.Length - RECORD_LENGTH)); | |
476 | - var weekly_end = daily_end.AddDays(-(int)daily_end.DayOfWeek); | |
477 | - _filledLength = (int)(weekly_end - weekly_begin).TotalDays / 7 + 1; | |
478 | - _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Weekly)]; | |
479 | - | |
480 | - //byte[]部分のデータ読み | |
481 | - _farm = new byte[_data.Length * RECORD_LENGTH]; | |
482 | - _byteLength = _farm.Length; | |
483 | - int offset = 0; | |
484 | - var weekly = weekly_begin; | |
485 | - for (int i = 0; i < _filledLength; i++, weekly = weekly.AddDays(7)) | |
486 | - { | |
487 | - offset = FillWeeklyData(i * RECORD_LENGTH, daily, offset, Util.DateToInt(weekly)); | |
488 | - if (offset >= daily.Length) break; | |
489 | - } | |
490 | - } | |
491 | - } | |
492 | - } | |
493 | - private int FillWeeklyData(int farmoffset, byte[] daily, int offset, int firstdate) | |
494 | - { | |
495 | - | |
496 | - int enddate = Util.DateToInt(Util.IntToDate(firstdate).AddDays(7)); | |
497 | - | |
498 | - int vol = 0, high = Int32.MinValue, low = Int32.MaxValue; | |
499 | - int open = 0, close = 0, cre_long = 0, cre_short = 0; | |
500 | - | |
501 | - int today = GetInt(daily, offset); | |
502 | - bool is_index = _brand.IsBuiltIn; | |
503 | - // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。 | |
504 | - // 下の、SetInt(_farm, farmoffset, wi.FirstDate); | |
505 | - // で、後に式を評価する際に用いられる基準日として日曜基準で 'wi.FirstDate' を使っているのだから、 | |
506 | - // ここでもこの値を使うべき。 2005/3/15 T. SARUKI | |
507 | - // | |
508 | - double base_split = this.CalcSplitRatio(firstdate); //分割を考慮する場合は期間内の調整が要る | |
509 | - while (offset <= daily.Length - RECORD_LENGTH && today < enddate) | |
510 | - { | |
511 | - //if(!is_index && today>20031201) Debugger.Break(); | |
512 | - double split = Env.Preference.AdjustSplit ? this.CalcSplitRatio(today) / base_split : 1; | |
513 | - int v = AdjustVolume(GetInt(daily, offset + VOLUME_OFFSET), split); | |
514 | - if (is_index || v != 0) | |
515 | - { //非indexで出来高0の日は集計しない | |
516 | - if (open == 0) open = AdjustPrice(GetInt(daily, offset + OPEN_OFFSET), split); | |
517 | - close = AdjustPrice(GetInt(daily, offset + CLOSE_OFFSET), split); | |
518 | - high = Math.Max(high, AdjustPrice(GetInt(daily, offset + HIGH_OFFSET), split)); | |
519 | - low = Math.Min(low, AdjustPrice(GetInt(daily, offset + LOW_OFFSET), split)); | |
520 | - cre_long = AdjustVolume(GetInt(daily, offset + CREDITLONG_OFFSET), split); | |
521 | - cre_short = AdjustVolume(GetInt(daily, offset + CREDITSHORT_OFFSET), split); | |
522 | - vol += v; | |
523 | - } | |
524 | - | |
525 | - offset += RECORD_LENGTH; | |
526 | - if (offset < daily.Length) today = GetInt(daily, offset); | |
527 | - } | |
528 | - | |
529 | - SetInt(_farm, farmoffset, firstdate); | |
530 | - SetInt(_farm, farmoffset + OPEN_OFFSET, open); | |
531 | - SetInt(_farm, farmoffset + HIGH_OFFSET, high); | |
532 | - SetInt(_farm, farmoffset + LOW_OFFSET, low); | |
533 | - SetInt(_farm, farmoffset + CLOSE_OFFSET, close); | |
534 | - SetInt(_farm, farmoffset + VOLUME_OFFSET, vol); | |
535 | - SetInt(_farm, farmoffset + CREDITLONG_OFFSET, cre_long); | |
536 | - SetInt(_farm, farmoffset + CREDITSHORT_OFFSET, cre_short); | |
537 | - | |
538 | - return offset; | |
539 | - } | |
540 | - | |
541 | - public override int LastDate | |
542 | - { | |
543 | - get | |
544 | - { | |
545 | - return _lastDate; | |
546 | - } | |
547 | - } | |
548 | - public override int FirstDate | |
549 | - { | |
550 | - get | |
551 | - { | |
552 | - return _firstDate; | |
553 | - } | |
554 | - } | |
555 | - | |
556 | - } | |
557 | - internal class MonthlyDataFarm : DataFarm | |
558 | - { | |
559 | - private int _firstDate; | |
560 | - private int _lastDate; | |
561 | - | |
562 | - public MonthlyDataFarm() | |
563 | - : base() | |
564 | - { | |
565 | - } | |
566 | - public override void LoadFor(AbstractBrand br) | |
567 | - { | |
568 | - _brand = br; | |
569 | - Construct(Util.GetDailyDataFileName(br.Code)); | |
570 | - } | |
571 | - | |
572 | - private void Construct(string filename) | |
573 | - { | |
574 | - _isEmpty = true; | |
575 | - if (File.Exists(filename)) | |
576 | - { | |
577 | - int length = (int)new FileInfo(filename).Length; | |
578 | - if (length > 0) | |
579 | - { | |
580 | - //まずは日足を読む | |
581 | - byte[] daily = new byte[length]; | |
582 | - _isEmpty = false; | |
583 | - FileStream s = null; | |
584 | - try | |
585 | - { | |
586 | - s = new FileStream(filename, FileMode.Open); | |
587 | - s.Read(daily, 0, length); | |
588 | - } | |
589 | - finally | |
590 | - { | |
591 | - if (s != null) s.Close(); | |
592 | - } | |
593 | - | |
594 | - _firstDate = GetInt(daily, 0); | |
595 | - _lastDate = GetInt(daily, daily.Length - RECORD_LENGTH); | |
596 | - DateTime monthly_begin = new DateTime(_firstDate / 10000, (_firstDate % 10000) / 100, (_firstDate % 100)); | |
597 | - DateTime monthly_end = new DateTime(_lastDate / 10000, (_lastDate % 10000) / 100, (_lastDate % 100)); | |
598 | - _filledLength = (monthly_end.Year - monthly_begin.Year) * 12 + monthly_end.Month + 1 - monthly_begin.Month; | |
599 | - | |
600 | - _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Monthly)]; | |
601 | - | |
602 | - // 以下WeeklyIndexとかぶって冗長 | |
603 | - | |
604 | - //byte[]部分のデータ読み | |
605 | - _farm = new byte[_data.Length * RECORD_LENGTH]; | |
606 | - _byteLength = _farm.Length; | |
607 | - DateTime yearmonth = monthly_begin; | |
608 | - int offset = 0; | |
609 | - for (int i = 0; i < _filledLength; i++) | |
610 | - { | |
611 | - offset = FillMonthlyData(i * RECORD_LENGTH, daily, offset, yearmonth); | |
612 | - if (offset >= daily.Length) break; | |
613 | - yearmonth = yearmonth.AddMonths(1); | |
614 | - } | |
615 | - } | |
616 | - } | |
617 | - } | |
618 | - // このメソッドもWeeklyIndexのFillWeeklyDataとかぶってかなり冗長 | |
619 | - private int FillMonthlyData(int farmoffset, byte[] daily, int offset, DateTime yearmonth) | |
620 | - { | |
621 | - | |
622 | - DateTime endmonth = yearmonth.AddMonths(1); | |
623 | - int enddate = endmonth.Year * 10000 + endmonth.Month * 100 + 1; | |
624 | - | |
625 | - int vol = 0, high = Int32.MinValue, low = Int32.MaxValue; | |
626 | - int open = 0, close = 0, cre_long = 0, cre_short = 0; | |
627 | - | |
628 | - int today = GetInt(daily, offset); | |
629 | - bool is_index = _brand.IsBuiltIn; | |
630 | - // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。 | |
631 | - // 下の、SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1); | |
632 | - // で、後に式を評価する際に用いられる基準日として月の初日である 'yearmonth.Year * 10000 + yearmonth.Month * 100 + 1' を使っているのだから、 | |
633 | - // ここでもこの値を使うべき。 2005/3/15 T. SARUKI | |
634 | - // | |
635 | - double base_split = this.CalcSplitRatio(Util.DateToInt(yearmonth.Year, yearmonth.Month, 1)); | |
636 | - while (offset <= daily.Length - RECORD_LENGTH && today < enddate) | |
637 | - { | |
638 | - double split = Env.Preference.AdjustSplit ? this.CalcSplitRatio(today) / base_split : 1; | |
639 | - int v = AdjustVolume(GetInt(daily, offset + VOLUME_OFFSET), split); | |
640 | - if (is_index || v != 0) | |
641 | - { //非indexで出来高0の日は集計しない | |
642 | - if (open == 0) open = AdjustPrice(GetInt(daily, offset + OPEN_OFFSET), split); | |
643 | - close = AdjustPrice(GetInt(daily, offset + CLOSE_OFFSET), split); | |
644 | - high = Math.Max(high, AdjustPrice(GetInt(daily, offset + HIGH_OFFSET), split)); | |
645 | - low = Math.Min(low, AdjustPrice(GetInt(daily, offset + LOW_OFFSET), split)); | |
646 | - cre_long = AdjustVolume(GetInt(daily, offset + CREDITLONG_OFFSET), split); | |
647 | - cre_short = AdjustVolume(GetInt(daily, offset + CREDITSHORT_OFFSET), split); | |
648 | - vol += v; | |
649 | - } | |
650 | - | |
651 | - offset += RECORD_LENGTH; | |
652 | - if (offset < daily.Length) today = GetInt(daily, offset); | |
653 | - } | |
654 | - | |
655 | - SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1); | |
656 | - SetInt(_farm, farmoffset + OPEN_OFFSET, open); | |
657 | - SetInt(_farm, farmoffset + HIGH_OFFSET, high); | |
658 | - SetInt(_farm, farmoffset + LOW_OFFSET, low); | |
659 | - SetInt(_farm, farmoffset + CLOSE_OFFSET, close); | |
660 | - SetInt(_farm, farmoffset + VOLUME_OFFSET, vol); | |
661 | - SetInt(_farm, farmoffset + CREDITLONG_OFFSET, cre_long); | |
662 | - SetInt(_farm, farmoffset + CREDITSHORT_OFFSET, cre_short); | |
663 | - | |
664 | - return offset; | |
665 | - } | |
666 | - | |
667 | - public override int LastDate | |
668 | - { | |
669 | - get | |
670 | - { | |
671 | - return _lastDate; | |
672 | - } | |
673 | - } | |
674 | - public override int FirstDate | |
22 | + //primitive indicatorをサポートするためのもの | |
23 | + internal class DataFarmPrimitiveAccess { | |
24 | + //delegateの引数になるためにこの形でないとだめ | |
25 | + internal static double GetDate(TradeData tr) { | |
26 | + return (double)tr.Farm.GetInt(tr.Offset); | |
27 | + } | |
28 | + internal static double GetOpen(TradeData tr) { | |
29 | + return AdjustPrice((double)tr.Farm.GetInt(tr.Offset+DataFarm.OPEN_OFFSET), tr); | |
30 | + } | |
31 | + internal static double GetHigh(TradeData tr) { | |
32 | + return AdjustPrice((double)tr.Farm.GetInt(tr.Offset+DataFarm.HIGH_OFFSET), tr); | |
33 | + } | |
34 | + internal static double GetLow(TradeData tr) { | |
35 | + return AdjustPrice((double)tr.Farm.GetInt(tr.Offset+DataFarm.LOW_OFFSET), tr); | |
36 | + } | |
37 | + internal static double GetClose(TradeData tr) { | |
38 | + return AdjustPrice((double)tr.Farm.GetInt(tr.Offset+DataFarm.CLOSE_OFFSET), tr); | |
39 | + } | |
40 | + internal static double GetVolume(TradeData tr) { | |
41 | + //オーバーフロー対策の一時しのぎ | |
42 | + return AdjustVolume((double)(uint)tr.Farm.GetInt(tr.Offset+DataFarm.VOLUME_OFFSET), tr); | |
43 | + } | |
44 | + internal static double GetCreditLong(TradeData tr) { | |
45 | + return AdjustVolume((double)tr.Farm.GetInt(tr.Offset+DataFarm.CREDITLONG_OFFSET), tr); | |
46 | + } | |
47 | + internal static double GetCreditShort(TradeData tr) { | |
48 | + return AdjustVolume((double)tr.Farm.GetInt(tr.Offset+DataFarm.CREDITSHORT_OFFSET), tr); | |
49 | + } | |
50 | + | |
51 | + private static double AdjustPrice(double value, TradeData tr) { | |
52 | + double split = Env.Preference.AdjustSplit? tr.Farm.CalcSplitRatio(tr.Date) : 1; | |
53 | + if(value==0 && GetVolume(tr)==0) { //出来高がない日は価格が0と記入されているので前日の終値で代用 | |
54 | + TradeData pr = tr.Prev; | |
55 | + return pr==null? 0 : GetClose(tr.Prev); | |
56 | + } | |
57 | + else | |
58 | + return tr.Farm.Brand.PriceScale * value / split; | |
59 | + } | |
60 | + private static double AdjustVolume(double value, TradeData tr) { | |
61 | + double split = Env.Preference.AdjustSplit? tr.Farm.CalcSplitRatio(tr.Date) : 1; | |
62 | + return value * split; | |
63 | + } | |
64 | + } | |
65 | + | |
66 | + internal class NewDailyData { | |
67 | + public int open; | |
68 | + public int high; | |
69 | + public int low; | |
70 | + public int close; | |
71 | + public int volume; | |
72 | + } | |
73 | + | |
74 | + internal abstract class DataFarm : IDisposable { | |
75 | + public const int RECORD_LENGTH = 32; | |
76 | + | |
77 | + public const int OPEN_OFFSET = 4; | |
78 | + public const int HIGH_OFFSET = 8; | |
79 | + public const int LOW_OFFSET = 12; | |
80 | + public const int CLOSE_OFFSET = 16; | |
81 | + public const int VOLUME_OFFSET = 20; | |
82 | + public const int CREDITSHORT_OFFSET = 24; | |
83 | + public const int CREDITLONG_OFFSET = 28; | |
84 | + | |
85 | + protected bool _isEmpty; //エラーなどで利用不能なことを示すフラグ | |
86 | + protected AbstractBrand _brand; | |
87 | + | |
88 | + protected byte[] _farm; //一次データ。同一のDataFarmオブジェクトを他の銘柄に使いまわすときもあるので、必要以上の長さが確保されることもある | |
89 | + protected int _byteLength; //_farmの論理的な長さ | |
90 | + protected TradeData[] _data; //必要に応じて生成されるTradeDataの列。一目など未来の日付のデータがあると配列の長さは_farmに対応する分より大きいこともある | |
91 | + protected int _filledLength; //最新日付までの長さ | |
92 | + | |
93 | + public DataFarm() { | |
94 | + } | |
95 | + public abstract void LoadFor(AbstractBrand br); | |
96 | + | |
97 | + public AbstractBrand Brand { | |
98 | + get { | |
99 | + return _brand; | |
100 | + } | |
101 | + } | |
102 | + | |
103 | + public int TotalLength { | |
104 | + get { | |
105 | + return _data.Length; | |
106 | + } | |
107 | + } | |
108 | + public int FilledLength { | |
109 | + get { | |
110 | + return _filledLength; | |
111 | + } | |
112 | + } | |
113 | + public bool IsEmpty { | |
114 | + get { | |
115 | + return _isEmpty; | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + public byte[] RawDataImage { | |
120 | + get { | |
121 | + return _farm; | |
122 | + } | |
123 | + } | |
124 | + | |
125 | + internal int GetInt(int offset) { | |
126 | + if(offset>=_byteLength) | |
127 | + throw new IndexOutOfRangeException(); | |
128 | + unsafe { | |
129 | + fixed(byte* p = &_farm[0]) { | |
130 | + return *(int*)(p+offset); | |
131 | + } | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + public TradeData GetByIndex(int index) { | |
136 | + Debug.Assert(_data!=null); | |
137 | + if(index<0 || index>=_data.Length) | |
138 | + throw new TradeDataOverflowException(index.ToString() + " is out of range"); | |
139 | + TradeData td = _data[index]; | |
140 | + if(td!=null) return td; //cache hit | |
141 | + | |
142 | + td = new TradeData(this, index, index*RECORD_LENGTH); | |
143 | + _data[index] = td; | |
144 | + return td; | |
145 | + } | |
146 | + | |
147 | + public abstract int LastDate { get; } | |
148 | + public abstract int FirstDate { get; } | |
149 | + | |
150 | + public int DateToIndex(int date) { | |
151 | + return DateToIndex(0, _filledLength, date); | |
152 | + } | |
153 | + private int DateToIndex(int begin, int end, int date) { | |
154 | + //binary search | |
155 | + if(end-begin <= 1) | |
156 | + return begin; | |
157 | + else { | |
158 | + int h = (begin+end)/2; | |
159 | + int t = GetByIndex(h).Date; | |
160 | + if(date < t) | |
161 | + return DateToIndex(begin, h, date); | |
162 | + else | |
163 | + return DateToIndex(h, end, date); | |
164 | + } | |
165 | + } | |
166 | + | |
167 | + //分割比率の取得 | |
168 | + public double CalcSplitRatio(int date) { | |
169 | + return _brand.CalcSplitRatio(date, this.LastDate); | |
170 | + | |
171 | + } | |
172 | + | |
173 | + public void Dispose() { | |
174 | + _farm = null; | |
175 | + _data = null; | |
176 | + } | |
177 | + | |
178 | + internal static int GetInt(byte[] rawdata, int offset) { | |
179 | + Debug.Assert(rawdata.Length>0); | |
180 | + unsafe { | |
181 | + fixed(byte* p = &rawdata[0]) { | |
182 | + return *(int*)(p+offset); | |
183 | + } | |
184 | + } | |
185 | + } | |
186 | + internal static void SetInt(byte[] rawdata, int offset, int value) { | |
187 | + unsafe { | |
188 | + fixed(byte* p = &rawdata[0]) { | |
189 | + *(int*)(p+offset) = value; | |
190 | + } | |
191 | + } | |
192 | + } | |
193 | + internal static void SetUInt(byte[] rawdata, int offset, uint value) | |
675 | 194 | { |
676 | - get | |
195 | + unsafe | |
677 | 196 | { |
678 | - return _firstDate; | |
197 | + fixed (byte* p = &rawdata[0]) | |
198 | + { | |
199 | + *(uint*)(p + offset) = value; | |
200 | + } | |
679 | 201 | } |
680 | 202 | } |
681 | 203 | |
682 | - } | |
204 | + protected static int AdjustPrice(int raw, double ratio) { | |
205 | + return (int)((double)raw / ratio); | |
206 | + } | |
207 | + protected static int AdjustVolume(int raw, double ratio) { | |
208 | + return (int)((double)raw * ratio); | |
209 | + } | |
210 | + } | |
211 | + | |
212 | + internal class DailyDataFarm : DataFarm { | |
213 | + | |
214 | + protected int _extraDataOffset; //1日単位でデータの追加をしたときのために | |
215 | + | |
216 | + public DailyDataFarm() : base() { | |
217 | + } | |
218 | + public override void LoadFor(AbstractBrand br) { | |
219 | + _brand = br; | |
220 | + Construct(Util.GetDailyDataFileName(br.Code), 0); | |
221 | + } | |
222 | + public void LoadFor(AbstractBrand br, int extra_dates) { | |
223 | + _brand = br; | |
224 | + Construct(Util.GetDailyDataFileName(br.Code), extra_dates); | |
225 | + } | |
226 | + private void Construct(string filename, int extra_dates) { | |
227 | + _isEmpty = true; | |
228 | +#if DOJIMA | |
229 | + Dojima.DojimaUtil.HalfDailyDataFarmCache.Clear(_brand); | |
230 | +#endif | |
231 | + if(File.Exists(filename)) { | |
232 | + int length = (int)new FileInfo(filename).Length; | |
233 | + if(length==0) return; | |
234 | + | |
235 | + if(_farm==null || _farm.Length<length + extra_dates*RECORD_LENGTH) | |
236 | + _farm = new byte[length + extra_dates*RECORD_LENGTH]; | |
237 | + int future_length = Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Daily); | |
238 | + _filledLength = length/RECORD_LENGTH; | |
239 | + _data = new TradeData[_filledLength + future_length]; | |
240 | + _byteLength = length; | |
241 | + _extraDataOffset = 0; | |
242 | + _isEmpty = false; | |
243 | + | |
244 | + FileStream s = null; | |
245 | + try { | |
246 | + s = new FileStream(filename, FileMode.Open); | |
247 | + s.Read(_farm, 0, length); | |
248 | + } | |
249 | + finally { | |
250 | + if(s!=null) s.Close(); | |
251 | + } | |
252 | + // 個別銘柄の株価データの先頭にある出来高0のデータを取り除く | |
253 | + var basic = _brand as BasicBrand; | |
254 | + if (basic == null || basic.Market == MarketType.B || basic.Market == MarketType.Custom) | |
255 | + return; | |
256 | + var idx = 0; | |
257 | + for (var i = 0; i < _filledLength; i++) | |
258 | + { | |
259 | + unsafe | |
260 | + { | |
261 | + var head = i * RECORD_LENGTH; | |
262 | + fixed (byte* p = &_farm[0]) | |
263 | + { | |
264 | + if (*(int*)(p + head + VOLUME_OFFSET) == 0) | |
265 | + idx += RECORD_LENGTH; | |
266 | + else | |
267 | + break; | |
268 | + } | |
269 | + } | |
270 | + } | |
271 | + if (idx == 0) | |
272 | + return; | |
273 | + _byteLength -= idx; | |
274 | + _filledLength = _byteLength / RECORD_LENGTH; | |
275 | + for (var i = 0; i < _byteLength; i++) | |
276 | + _farm[i] = _farm[i + idx]; | |
277 | + } | |
278 | + } | |
279 | + public void Save(string filename) { | |
280 | + if(_farm!=null) { //エラーハンドリングできていない | |
281 | + FileStream s = new FileStream(filename, FileMode.Create); | |
282 | + s.Write(_farm, 0, _byteLength + _extraDataOffset); | |
283 | + s.Close(); | |
284 | + } | |
285 | + } | |
286 | + internal void WriteExtraData(int record_offset, int value) { | |
287 | + unsafe { | |
288 | + fixed(byte* p = &_farm[0]) { | |
289 | + *(int*)(p + _byteLength + _extraDataOffset + record_offset) = value; | |
290 | + } | |
291 | + } | |
292 | + } | |
293 | + internal void ProgressExtraDataAddress() { | |
294 | + _extraDataOffset += RECORD_LENGTH; | |
295 | + Debug.Assert(_extraDataOffset<=_farm.Length); | |
296 | + } | |
297 | + | |
298 | + //連続的に複数の日付を更新することもできるが、増加方向であることが必須 | |
299 | + internal void UpdateDataFarm(int date, NewDailyData td) { | |
300 | + int ld; | |
301 | + if(this.IsEmpty) { | |
302 | + //とりあえず1日書き込める分だけ初期化 | |
303 | + if(_farm==null) { | |
304 | + _farm = new byte[RECORD_LENGTH * 200]; //この上限はどこかで取得すべきだが | |
305 | + _filledLength = 0; | |
306 | + _data = null; | |
307 | + _byteLength = 0; | |
308 | + _extraDataOffset = 0; | |
309 | + } | |
310 | + ld = 0; | |
311 | + } | |
312 | + else | |
313 | + ld = this.LastDate; | |
314 | + | |
315 | + | |
316 | + int offset; | |
317 | + if(ld < date) { | |
318 | + offset = _byteLength + _extraDataOffset; //emptyのときは常にこれ | |
319 | + _extraDataOffset += RECORD_LENGTH; | |
320 | + } | |
321 | + else { | |
322 | + offset = _byteLength - RECORD_LENGTH; | |
323 | + do { | |
324 | + int t = GetInt(offset); | |
325 | + if(t==date) | |
326 | + break; | |
327 | + else if(t < date) { | |
328 | + offset += RECORD_LENGTH; | |
329 | + break; | |
330 | + } | |
331 | + else | |
332 | + offset -= RECORD_LENGTH; | |
333 | + } while(true); | |
334 | + } | |
335 | + | |
336 | + unsafe { | |
337 | + fixed(byte* p = &_farm[0]) { | |
338 | + byte* a = p + offset; | |
339 | + *(int*)(a + 0) = date; | |
340 | + *(int*)(a + 4) = td.open; | |
341 | + *(int*)(a + 8) = td.high; | |
342 | + *(int*)(a + 12) = td.low; | |
343 | + *(int*)(a + 16) = td.close; | |
344 | + *(int*)(a + 20) = td.volume; | |
345 | + } | |
346 | + } | |
347 | + | |
348 | + } | |
349 | + | |
350 | + //次の2つはTradeDataを作らないようにしている、注意 | |
351 | + public override int LastDate { | |
352 | + get { | |
353 | + return GetInt(_byteLength - RECORD_LENGTH); | |
354 | + } | |
355 | + } | |
356 | + public override int FirstDate { | |
357 | + get { | |
358 | + return GetInt(0); | |
359 | + } | |
360 | + } | |
361 | + | |
362 | + } | |
363 | + | |
364 | + internal class WeeklyDataFarm : DataFarm { | |
365 | + private int _firstDate; | |
366 | + private int _lastDate; | |
367 | + | |
368 | + public WeeklyDataFarm() : base() { | |
369 | + } | |
370 | + public override void LoadFor(AbstractBrand br) { | |
371 | + _brand = br; | |
372 | + Construct(Util.GetDailyDataFileName(br.Code)); | |
373 | + } | |
374 | + | |
375 | + private void Construct(string filename) { | |
376 | + _isEmpty = true; | |
377 | + if(File.Exists(filename)) { | |
378 | + int length = (int)new FileInfo(filename).Length; | |
379 | + if(length > 0) { | |
380 | + //まずは日足を読む | |
381 | + byte[] daily = new byte[length]; | |
382 | + FileStream s = null; | |
383 | + try { | |
384 | + s = new FileStream(filename, FileMode.Open); | |
385 | + s.Read(daily, 0, length); | |
386 | + } | |
387 | + finally { | |
388 | + if(s!=null) s.Close(); | |
389 | + } | |
390 | + | |
391 | + _isEmpty = false; | |
392 | + _firstDate= GetInt(daily, 0); | |
393 | + _lastDate = GetInt(daily, daily.Length-RECORD_LENGTH); | |
394 | + var daily_begin = Util.IntToDate(GetInt(daily, 0)); | |
395 | + var weekly_begin = daily_begin.AddDays(-(int)daily_begin.DayOfWeek); | |
396 | + var daily_end = Util.IntToDate(GetInt(daily, daily.Length - RECORD_LENGTH)); | |
397 | + var weekly_end = daily_end.AddDays(-(int)daily_end.DayOfWeek); | |
398 | + _filledLength = (int)(weekly_end - weekly_begin).TotalDays / 7 + 1; | |
399 | + _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Weekly)]; | |
400 | + | |
401 | + //byte[]部分のデータ読み | |
402 | + _farm = new byte[_data.Length * RECORD_LENGTH]; | |
403 | + _byteLength = _farm.Length; | |
404 | + int offset = 0; | |
405 | + var weekly = weekly_begin; | |
406 | + for(int i=0; i < _filledLength; i++, weekly = weekly.AddDays(7)) { | |
407 | + offset = FillWeeklyData(i*RECORD_LENGTH, daily, offset, Util.DateToInt(weekly)); | |
408 | + if(offset>=daily.Length) break; | |
409 | + } | |
410 | + } | |
411 | + } | |
412 | + } | |
413 | + private int FillWeeklyData(int farmoffset, byte[] daily, int offset, int firstdate) { | |
414 | + | |
415 | + int enddate = Util.DateToInt(Util.IntToDate(firstdate).AddDays(7)); | |
416 | + | |
417 | + int vol = 0, high = Int32.MinValue, low = Int32.MaxValue; | |
418 | + int open = 0, close = 0, cre_long = 0, cre_short = 0; | |
419 | + | |
420 | + int today = GetInt(daily, offset); | |
421 | + bool is_index = _brand.IsBuiltIn; | |
422 | + // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。 | |
423 | + // 下の、SetInt(_farm, farmoffset, wi.FirstDate); | |
424 | + // で、後に式を評価する際に用いられる基準日として日曜基準で 'wi.FirstDate' を使っているのだから、 | |
425 | + // ここでもこの値を使うべき。 2005/3/15 T. SARUKI | |
426 | + // | |
427 | + double base_split = this.CalcSplitRatio(firstdate); //分割を考慮する場合は期間内の調整が要る | |
428 | + while(offset<=daily.Length-RECORD_LENGTH && today<enddate) { | |
429 | + //if(!is_index && today>20031201) Debugger.Break(); | |
430 | + double split = Env.Preference.AdjustSplit? this.CalcSplitRatio(today) / base_split : 1; | |
431 | + int v = AdjustVolume(GetInt(daily, offset+VOLUME_OFFSET), split); | |
432 | + if(is_index || v!=0) { //非indexで出来高0の日は集計しない | |
433 | + if(open==0) open = AdjustPrice(GetInt(daily, offset+OPEN_OFFSET), split); | |
434 | + close = AdjustPrice(GetInt(daily, offset+CLOSE_OFFSET), split); | |
435 | + high = Math.Max(high, AdjustPrice(GetInt(daily, offset+HIGH_OFFSET), split)); | |
436 | + low = Math.Min(low, AdjustPrice(GetInt(daily, offset+LOW_OFFSET), split)); | |
437 | + cre_long = AdjustVolume(GetInt(daily, offset+CREDITLONG_OFFSET), split); | |
438 | + cre_short = AdjustVolume(GetInt(daily, offset+CREDITSHORT_OFFSET), split); | |
439 | + vol += v; | |
440 | + } | |
441 | + | |
442 | + offset += RECORD_LENGTH; | |
443 | + if(offset<daily.Length) today = GetInt(daily, offset); | |
444 | + } | |
445 | + | |
446 | + SetInt(_farm, farmoffset, firstdate); | |
447 | + SetInt(_farm, farmoffset+OPEN_OFFSET, open); | |
448 | + SetInt(_farm, farmoffset+HIGH_OFFSET, high); | |
449 | + SetInt(_farm, farmoffset+LOW_OFFSET, low); | |
450 | + SetInt(_farm, farmoffset+CLOSE_OFFSET, close); | |
451 | + SetInt(_farm, farmoffset+VOLUME_OFFSET, vol); | |
452 | + SetInt(_farm, farmoffset+CREDITLONG_OFFSET, cre_long); | |
453 | + SetInt(_farm, farmoffset+CREDITSHORT_OFFSET, cre_short); | |
454 | + | |
455 | + return offset; | |
456 | + } | |
457 | + | |
458 | + public override int LastDate { | |
459 | + get { | |
460 | + return _lastDate; | |
461 | + } | |
462 | + } | |
463 | + public override int FirstDate { | |
464 | + get { | |
465 | + return _firstDate; | |
466 | + } | |
467 | + } | |
468 | + | |
469 | + } | |
470 | + internal class MonthlyDataFarm : DataFarm { | |
471 | + private int _firstDate; | |
472 | + private int _lastDate; | |
473 | + | |
474 | + public MonthlyDataFarm() : base() { | |
475 | + } | |
476 | + public override void LoadFor(AbstractBrand br) { | |
477 | + _brand = br; | |
478 | + Construct(Util.GetDailyDataFileName(br.Code)); | |
479 | + } | |
480 | + | |
481 | + private void Construct(string filename) { | |
482 | + _isEmpty = true; | |
483 | + if(File.Exists(filename)) { | |
484 | + int length = (int)new FileInfo(filename).Length; | |
485 | + if(length > 0) { | |
486 | + //まずは日足を読む | |
487 | + byte[] daily = new byte[length]; | |
488 | + _isEmpty = false; | |
489 | + FileStream s = null; | |
490 | + try { | |
491 | + s = new FileStream(filename, FileMode.Open); | |
492 | + s.Read(daily, 0, length); | |
493 | + } | |
494 | + finally { | |
495 | + if(s!=null) s.Close(); | |
496 | + } | |
497 | + | |
498 | + _firstDate= GetInt(daily, 0); | |
499 | + _lastDate = GetInt(daily, daily.Length-RECORD_LENGTH); | |
500 | + DateTime monthly_begin = new DateTime(_firstDate / 10000, (_firstDate % 10000) / 100, (_firstDate % 100)); | |
501 | + DateTime monthly_end = new DateTime(_lastDate / 10000, (_lastDate % 10000) / 100, (_lastDate % 100)); | |
502 | + _filledLength = (monthly_end.Year - monthly_begin.Year) * 12 + monthly_end.Month+1 - monthly_begin.Month; | |
503 | + | |
504 | + _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Monthly)]; | |
505 | + | |
506 | + // 以下WeeklyIndexとかぶって冗長 | |
507 | + | |
508 | + //byte[]部分のデータ読み | |
509 | + _farm = new byte[_data.Length * RECORD_LENGTH]; | |
510 | + _byteLength = _farm.Length; | |
511 | + DateTime yearmonth = monthly_begin; | |
512 | + int offset = 0; | |
513 | + for(int i=0; i<_filledLength; i++) { | |
514 | + offset = FillMonthlyData(i*RECORD_LENGTH, daily, offset, yearmonth); | |
515 | + if(offset>=daily.Length) break; | |
516 | + yearmonth = yearmonth.AddMonths(1); | |
517 | + } | |
518 | + } | |
519 | + } | |
520 | + } | |
521 | + // このメソッドもWeeklyIndexのFillWeeklyDataとかぶってかなり冗長 | |
522 | + private int FillMonthlyData(int farmoffset, byte[] daily, int offset, DateTime yearmonth) { | |
523 | + | |
524 | + DateTime endmonth = yearmonth.AddMonths(1); | |
525 | + int enddate = endmonth.Year * 10000 + endmonth.Month * 100 + 1; | |
526 | + | |
527 | + int vol = 0, high = Int32.MinValue, low = Int32.MaxValue; | |
528 | + int open = 0, close = 0, cre_long = 0, cre_short = 0; | |
529 | + | |
530 | + int today = GetInt(daily, offset); | |
531 | + bool is_index = _brand.IsBuiltIn; | |
532 | + // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。 | |
533 | + // 下の、SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1); | |
534 | + // で、後に式を評価する際に用いられる基準日として月の初日である 'yearmonth.Year * 10000 + yearmonth.Month * 100 + 1' を使っているのだから、 | |
535 | + // ここでもこの値を使うべき。 2005/3/15 T. SARUKI | |
536 | + // | |
537 | + double base_split = this.CalcSplitRatio(Util.DateToInt(yearmonth.Year, yearmonth.Month, 1)); | |
538 | + while(offset <= daily.Length - RECORD_LENGTH && today < enddate) { | |
539 | + double split = Env.Preference.AdjustSplit? this.CalcSplitRatio(today) / base_split : 1; | |
540 | + int v = AdjustVolume(GetInt(daily, offset+VOLUME_OFFSET), split); | |
541 | + if(is_index || v!=0) { //非indexで出来高0の日は集計しない | |
542 | + if(open==0) open = AdjustPrice(GetInt(daily, offset+OPEN_OFFSET), split); | |
543 | + close = AdjustPrice(GetInt(daily, offset+CLOSE_OFFSET), split); | |
544 | + high = Math.Max(high, AdjustPrice(GetInt(daily, offset+HIGH_OFFSET), split)); | |
545 | + low = Math.Min(low, AdjustPrice(GetInt(daily, offset+LOW_OFFSET), split)); | |
546 | + cre_long = AdjustVolume(GetInt(daily, offset+CREDITLONG_OFFSET), split); | |
547 | + cre_short = AdjustVolume(GetInt(daily, offset+CREDITSHORT_OFFSET), split); | |
548 | + vol += v; | |
549 | + } | |
550 | + | |
551 | + offset += RECORD_LENGTH; | |
552 | + if(offset<daily.Length) today = GetInt(daily, offset); | |
553 | + } | |
554 | + | |
555 | + SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1); | |
556 | + SetInt(_farm, farmoffset+OPEN_OFFSET, open); | |
557 | + SetInt(_farm, farmoffset+HIGH_OFFSET, high); | |
558 | + SetInt(_farm, farmoffset+LOW_OFFSET, low); | |
559 | + SetInt(_farm, farmoffset+CLOSE_OFFSET, close); | |
560 | + SetInt(_farm, farmoffset+VOLUME_OFFSET, vol); | |
561 | + SetInt(_farm, farmoffset+CREDITLONG_OFFSET, cre_long); | |
562 | + SetInt(_farm, farmoffset+CREDITSHORT_OFFSET, cre_short); | |
563 | + | |
564 | + return offset; | |
565 | + } | |
566 | + | |
567 | + public override int LastDate { | |
568 | + get { | |
569 | + return _lastDate; | |
570 | + } | |
571 | + } | |
572 | + public override int FirstDate { | |
573 | + get { | |
574 | + return _firstDate; | |
575 | + } | |
576 | + } | |
577 | + | |
578 | + } | |
579 | + | |
683 | 580 | internal class YearlyDataFarm : DataFarm |
684 | 581 | { |
685 | 582 | private int _firstDate; |
@@ -809,441 +706,361 @@ namespace Zanetti.Data | ||
809 | 706 | } |
810 | 707 | |
811 | 708 | //他の銘柄から導出される銘柄 |
812 | - internal class DerivedDataFarm : DataFarm | |
813 | - { | |
814 | - private int _firstDate; | |
815 | - private int _lastDate; | |
816 | - private DerivedBrand _derivedBrand; | |
817 | - private ChartFormat _chartFormat; | |
818 | - | |
819 | - public DerivedDataFarm(DerivedBrand br, ChartFormat fmt) | |
820 | - : base() | |
821 | - { | |
822 | - _derivedBrand = br; | |
823 | - _chartFormat = fmt; | |
824 | - } | |
825 | - public override void LoadFor(AbstractBrand br) | |
826 | - { | |
827 | - Debug.Assert(br is DerivedBrand); | |
828 | - _brand = br; | |
829 | - _derivedBrand = (DerivedBrand)br; | |
830 | - Construct(_derivedBrand); | |
831 | - } | |
832 | - | |
833 | - private void Construct(DerivedBrand br) | |
834 | - { | |
835 | - DataFarm[] fs = new DataFarm[br.Dependencies.Length]; | |
836 | - int len = Int32.MaxValue; | |
837 | - int shortest_farm_index = 0; | |
838 | - for (int i = 0; i < fs.Length; i++) | |
839 | - { | |
840 | - DataFarm f = Env.BrandCollection.ReserveFarm(br.Dependencies[i], _chartFormat); | |
841 | - if (f.IsEmpty) | |
842 | - { | |
843 | - _isEmpty = true; | |
844 | - return; //一つでも利用不可があればダメ | |
845 | - } | |
846 | - fs[i] = f; | |
847 | - if (f.FilledLength < len) | |
848 | - { | |
849 | - shortest_farm_index = i; | |
850 | - len = f.FilledLength; | |
851 | - } | |
852 | - } | |
853 | - | |
854 | - DataFarm shortest_farm = fs[shortest_farm_index]; | |
855 | - if (_farm == null || _farm.Length < len * RECORD_LENGTH) _farm = new byte[len * RECORD_LENGTH]; | |
856 | - _byteLength = len * RECORD_LENGTH; | |
857 | - | |
858 | - _data = new TradeData[len + Env.CurrentIndicators.GetAddedFutureLength(_chartFormat)]; | |
859 | - _filledLength = len; | |
860 | - _isEmpty = false; | |
861 | - | |
862 | - _firstDate = shortest_farm.GetByIndex(0).Date; | |
863 | - _lastDate = shortest_farm.GetByIndex(shortest_farm.FilledLength - 1).Date; | |
864 | - //データの構築 本当はここも遅延評価すると効率的だが | |
865 | - FillData(len, shortest_farm_index, br, fs); | |
866 | - } | |
867 | - | |
868 | - private void FillData(int len, int shortest_farm_index, DerivedBrand br, DataFarm[] deps) | |
869 | - { | |
870 | - int[] indexmap = new int[deps.Length]; | |
871 | - EvalResult[][] args = new EvalResult[4][]; | |
872 | - for (int i = 0; i < 4; i++) | |
873 | - { | |
874 | - args[i] = new EvalResult[deps.Length]; | |
875 | - for (int j = 0; j < deps.Length; j++) args[i][j] = new EvalResult(0); | |
876 | - } | |
877 | - Indicator[] inds = new Indicator[] { | |
709 | + internal class DerivedDataFarm : DataFarm { | |
710 | + private int _firstDate; | |
711 | + private int _lastDate; | |
712 | + private DerivedBrand _derivedBrand; | |
713 | + private ChartFormat _chartFormat; | |
714 | + | |
715 | + public DerivedDataFarm(DerivedBrand br, ChartFormat fmt) : base() { | |
716 | + _derivedBrand = br; | |
717 | + _chartFormat = fmt; | |
718 | + } | |
719 | + public override void LoadFor(AbstractBrand br) { | |
720 | + Debug.Assert(br is DerivedBrand); | |
721 | + _brand = br; | |
722 | + _derivedBrand = (DerivedBrand)br; | |
723 | + Construct(_derivedBrand); | |
724 | + } | |
725 | + | |
726 | + private void Construct(DerivedBrand br) { | |
727 | + DataFarm[] fs = new DataFarm[br.Dependencies.Length]; | |
728 | + int len = Int32.MaxValue; | |
729 | + int shortest_farm_index = 0; | |
730 | + for(int i=0; i<fs.Length; i++) { | |
731 | + DataFarm f = Env.BrandCollection.ReserveFarm(br.Dependencies[i], _chartFormat); | |
732 | + if(f.IsEmpty) { | |
733 | + _isEmpty = true; | |
734 | + return; //一つでも利用不可があればダメ | |
735 | + } | |
736 | + fs[i] = f; | |
737 | + if(f.FilledLength < len) { | |
738 | + shortest_farm_index = i; | |
739 | + len = f.FilledLength; | |
740 | + } | |
741 | + } | |
742 | + | |
743 | + DataFarm shortest_farm = fs[shortest_farm_index]; | |
744 | + if(_farm==null || _farm.Length<len*RECORD_LENGTH) _farm = new byte[len*RECORD_LENGTH]; | |
745 | + _byteLength = len*RECORD_LENGTH; | |
746 | + | |
747 | + _data = new TradeData[len + Env.CurrentIndicators.GetAddedFutureLength(_chartFormat)]; | |
748 | + _filledLength = len; | |
749 | + _isEmpty = false; | |
750 | + | |
751 | + _firstDate = shortest_farm.GetByIndex(0).Date; | |
752 | + _lastDate = shortest_farm.GetByIndex(shortest_farm.FilledLength-1).Date; | |
753 | + //データの構築 本当はここも遅延評価すると効率的だが | |
754 | + FillData(len, shortest_farm_index, br, fs); | |
755 | + } | |
756 | + | |
757 | + private void FillData(int len, int shortest_farm_index, DerivedBrand br, DataFarm[] deps) { | |
758 | + int[] indexmap = new int[deps.Length]; | |
759 | + EvalResult[][] args = new EvalResult[4][]; | |
760 | + for(int i=0; i<4; i++) { | |
761 | + args[i] = new EvalResult[deps.Length]; | |
762 | + for(int j=0; j<deps.Length; j++) args[i][j] = new EvalResult(0); | |
763 | + } | |
764 | + Indicator[] inds = new Indicator[] { | |
878 | 765 | Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Open), |
879 | 766 | Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High), |
880 | 767 | Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low), |
881 | 768 | Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Close)}; |
882 | 769 | |
883 | - TradeData[] tds = new TradeData[deps.Length]; | |
884 | - | |
885 | - Evaluator ev = new Evaluator(br.Name); | |
886 | - | |
887 | - for (int i = 0; i < len; i++) | |
888 | - { | |
889 | - ev.BaseIndex = i; | |
890 | - | |
891 | - //日付の決定 | |
892 | - int date = deps[shortest_farm_index].GetByIndex(i).Date; | |
893 | - | |
894 | - int farmoffset = i * RECORD_LENGTH; | |
895 | - for (int j = 0; j < deps.Length; j++) | |
896 | - { | |
897 | - int candidate = indexmap[j] + 1; //多くの場合日付とindexは一致しているので、DateToIndexの実行回数を減らすためindexmapを用意 | |
898 | - TradeData td = candidate < deps[j].TotalLength ? deps[j].GetByIndex(candidate) : null; | |
899 | - if (td == null || td.Date != date) | |
900 | - { | |
901 | - candidate = deps[j].DateToIndex(date); | |
902 | - td = deps[j].GetByIndex(candidate); | |
903 | - } | |
904 | - indexmap[j] = candidate; | |
905 | - | |
906 | - for (int k = 0; k < inds.Length; k++) | |
907 | - args[k][j].DoubleVal = td.GetValue(inds[k]); | |
908 | - } | |
909 | - | |
910 | - //日付 | |
911 | - SetInt(_farm, farmoffset, date); | |
912 | - | |
913 | - //4本値の計算 | |
914 | - Expression expr = br.Expression; | |
915 | - ev.Args = args[0]; | |
916 | - int open = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
917 | - SetInt(_farm, farmoffset + OPEN_OFFSET, open); | |
918 | - ev.Args = args[3]; | |
919 | - int close = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
920 | - SetInt(_farm, farmoffset + CLOSE_OFFSET, close); | |
921 | - ev.Args = args[1]; | |
922 | - int v1 = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
923 | - ev.Args = args[2]; | |
924 | - int v2 = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
925 | - | |
926 | - //計算式により、それぞれの高値・安値で計算したものが結果としてどうなるかは変わってしまう | |
927 | - SetInt(_farm, farmoffset + HIGH_OFFSET, Math.Max(Math.Max(open, close), Math.Max(v1, v2))); | |
928 | - SetInt(_farm, farmoffset + LOW_OFFSET, Math.Min(Math.Min(open, close), Math.Min(v1, v2))); | |
929 | - } | |
930 | - } | |
931 | - | |
932 | - public override int LastDate | |
933 | - { | |
934 | - get | |
935 | - { | |
936 | - return _lastDate; | |
937 | - } | |
938 | - } | |
939 | - public override int FirstDate | |
940 | - { | |
941 | - get | |
942 | - { | |
943 | - return _firstDate; | |
944 | - } | |
945 | - } | |
946 | - } | |
770 | + TradeData[] tds = new TradeData[deps.Length]; | |
771 | + | |
772 | + Evaluator ev = new Evaluator(br.Name); | |
773 | + | |
774 | + for(int i=0; i<len; i++) { | |
775 | + ev.BaseIndex = i; | |
776 | + | |
777 | + //日付の決定 | |
778 | + int date = deps[shortest_farm_index].GetByIndex(i).Date; | |
779 | + | |
780 | + int farmoffset = i * RECORD_LENGTH; | |
781 | + for(int j=0; j<deps.Length; j++) { | |
782 | + int candidate = indexmap[j]+1; //多くの場合日付とindexは一致しているので、DateToIndexの実行回数を減らすためindexmapを用意 | |
783 | + TradeData td = candidate<deps[j].TotalLength? deps[j].GetByIndex(candidate) : null; | |
784 | + if(td==null || td.Date!=date) { | |
785 | + candidate = deps[j].DateToIndex(date); | |
786 | + td = deps[j].GetByIndex(candidate); | |
787 | + } | |
788 | + indexmap[j] = candidate; | |
789 | + | |
790 | + for(int k=0; k<inds.Length; k++) | |
791 | + args[k][j].DoubleVal = td.GetValue(inds[k]); | |
792 | + } | |
793 | + | |
794 | + //日付 | |
795 | + SetInt(_farm, farmoffset, date); | |
796 | + | |
797 | + //4本値の計算 | |
798 | + Expression expr = br.Expression; | |
799 | + ev.Args = args[0]; | |
800 | + int open = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
801 | + SetInt(_farm, farmoffset+OPEN_OFFSET, open); | |
802 | + ev.Args = args[3]; | |
803 | + int close = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
804 | + SetInt(_farm, farmoffset+CLOSE_OFFSET, close); | |
805 | + ev.Args = args[1]; | |
806 | + int v1 = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
807 | + ev.Args = args[2]; | |
808 | + int v2 = (int)((EvalResult)expr.Apply(ev)).DoubleVal; | |
809 | + | |
810 | + //計算式により、それぞれの高値・安値で計算したものが結果としてどうなるかは変わってしまう | |
811 | + SetInt(_farm, farmoffset+HIGH_OFFSET, Math.Max(Math.Max(open, close), Math.Max(v1, v2))); | |
812 | + SetInt(_farm, farmoffset+LOW_OFFSET, Math.Min(Math.Min(open, close), Math.Min(v1, v2))); | |
813 | + } | |
814 | + } | |
815 | + | |
816 | + public override int LastDate { | |
817 | + get { | |
818 | + return _lastDate; | |
819 | + } | |
820 | + } | |
821 | + public override int FirstDate { | |
822 | + get { | |
823 | + return _firstDate; | |
824 | + } | |
825 | + } | |
826 | + } | |
947 | 827 | |
948 | 828 | |
949 | 829 | |
950 | 830 | //internal delegate double Calculate(Indicator indicator, TradeData data); |
951 | 831 | |
952 | - //節の種類 | |
953 | - internal enum Fushi | |
954 | - { | |
955 | - Unknown, | |
956 | - None, | |
957 | - High, | |
958 | - Low | |
959 | - } | |
960 | - | |
961 | - /// 日足・週足・月足などの1件のレコード | |
962 | - internal class TradeData | |
963 | - { | |
964 | - private DataFarm _farm; | |
965 | - private int _index; | |
966 | - private int _offset; | |
967 | - private double[] _data; | |
968 | - private Fushi _fushi; | |
969 | - | |
970 | - public TradeData(DataFarm farm, int index, int offset) | |
971 | - { | |
972 | - _farm = farm; | |
973 | - _index = index; | |
974 | - _offset = offset; | |
975 | - _data = new double[Env.CurrentIndicators.IndicatorCount]; | |
976 | - _fushi = Fushi.Unknown; | |
977 | - for (int i = 0; i < _data.Length; i++) | |
978 | - _data[i] = Double.NaN; | |
979 | - } | |
980 | - public DataFarm Farm | |
981 | - { | |
982 | - get | |
983 | - { | |
984 | - return _farm; | |
985 | - } | |
986 | - } | |
987 | - public int Index | |
988 | - { | |
989 | - get | |
990 | - { | |
991 | - return _index; | |
992 | - } | |
993 | - } | |
994 | - public int Offset | |
995 | - { | |
996 | - get | |
997 | - { | |
998 | - return _offset; | |
999 | - } | |
1000 | - } | |
1001 | - public TradeData Prev | |
1002 | - { | |
1003 | - get | |
1004 | - { | |
1005 | - return _index > 0 ? _farm.GetByIndex(_index - 1) : null; | |
1006 | - } | |
1007 | - } | |
1008 | - public TradeData Next | |
1009 | - { | |
1010 | - get | |
1011 | - { | |
1012 | - return _index < _farm.TotalLength - 1 ? _farm.GetByIndex(_index + 1) : null; | |
1013 | - } | |
1014 | - } | |
1015 | - public bool IsFuture | |
1016 | - { | |
1017 | - get | |
1018 | - { | |
1019 | - return _index >= _farm.FilledLength; | |
1020 | - } | |
1021 | - } | |
1022 | - public bool CoversDate(int date) | |
1023 | - { | |
1024 | - if (date == this.Date) | |
1025 | - return true; | |
1026 | - else | |
1027 | - { | |
1028 | - int c = this.Date; | |
1029 | - if (c > date) | |
1030 | - return false; | |
1031 | - else | |
1032 | - { | |
1033 | - TradeData next = this.Next; | |
1034 | - return next != null && date < next.Date; | |
1035 | - } | |
1036 | - } | |
1037 | - } | |
1038 | - | |
1039 | - public double GetValue(Indicator indicator) | |
1040 | - { | |
1041 | - double t = _data[indicator.LaneID]; | |
1042 | - //overflowによる演算不可はPositiveInfinityであらわす | |
1043 | - if (Double.IsPositiveInfinity(t)) return Double.NaN; | |
1044 | - if (!Double.IsNaN(t)) return t; //キャッシュにヒット | |
1045 | - | |
1046 | - try | |
1047 | - { | |
1048 | - if (indicator.CheckRange(this)) | |
1049 | - { | |
1050 | - t = indicator.Calculate(this); | |
1051 | - _data[indicator.LaneID] = t; | |
1052 | - } | |
1053 | - else | |
1054 | - t = Double.NaN; | |
1055 | - return t; | |
1056 | - } | |
1057 | - catch (TradeDataOverflowException) | |
1058 | - { | |
1059 | - //Debug.WriteLine("Out of range!"); | |
1060 | - _data[indicator.LaneID] = Double.PositiveInfinity; | |
1061 | - return Double.NaN; | |
1062 | - } | |
1063 | - } | |
1064 | - public int Date | |
1065 | - { | |
1066 | - get | |
1067 | - { | |
1068 | - return (int)GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Date)); | |
1069 | - } | |
1070 | - } | |
1071 | - public double Open | |
1072 | - { | |
1073 | - get | |
1074 | - { | |
1075 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Open)); | |
1076 | - } | |
1077 | - } | |
1078 | - public double High | |
1079 | - { | |
1080 | - get | |
1081 | - { | |
1082 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High)); | |
1083 | - } | |
1084 | - } | |
1085 | - public double Low | |
1086 | - { | |
1087 | - get | |
1088 | - { | |
1089 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low)); | |
1090 | - } | |
1091 | - } | |
1092 | - public double Close | |
1093 | - { | |
1094 | - get | |
1095 | - { | |
1096 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Close)); | |
1097 | - } | |
1098 | - } | |
1099 | - public double Volume | |
1100 | - { | |
1101 | - get | |
1102 | - { | |
1103 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Volume)); | |
1104 | - } | |
1105 | - } | |
1106 | - public double CreditLong | |
1107 | - { | |
1108 | - get | |
1109 | - { | |
1110 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditLong)); | |
1111 | - } | |
1112 | - } | |
1113 | - public double CreditShort | |
1114 | - { | |
1115 | - get | |
1116 | - { | |
1117 | - return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditShort)); | |
1118 | - } | |
1119 | - } | |
1120 | - | |
1121 | - //節の計算 | |
1122 | - public Fushi Fushi | |
1123 | - { | |
1124 | - get | |
1125 | - { | |
1126 | - if (_fushi != Fushi.Unknown) return _fushi; | |
1127 | - | |
1128 | - double h1 = Double.MinValue; | |
1129 | - double l1 = Double.MaxValue; | |
1130 | - double h2 = Double.MinValue; | |
1131 | - double l2 = Double.MaxValue; | |
1132 | - int fushi = Env.Preference.FushiRange; | |
1133 | - //あまり端に表示しても仕方ない | |
1134 | - if (_index < fushi || _index > _farm.FilledLength - fushi) | |
1135 | - { | |
1136 | - _fushi = Fushi.None; | |
1137 | - return _fushi; | |
1138 | - } | |
1139 | - for (int i = _index - fushi; i < _index; i++) | |
1140 | - { | |
1141 | - h1 = Math.Max(h1, _farm.GetByIndex(i).High); | |
1142 | - l1 = Math.Min(l1, _farm.GetByIndex(i).Low); | |
1143 | - } | |
1144 | - for (int i = _index + 1; i < _index + fushi; i++) | |
1145 | - { | |
1146 | - h2 = Math.Max(h2, _farm.GetByIndex(i).High); | |
1147 | - l2 = Math.Min(l2, _farm.GetByIndex(i).Low); | |
1148 | - } | |
1149 | - | |
1150 | - //過去に同値があるときは無視、未来にあるときは節 | |
1151 | - if (h1 < this.High && h2 <= this.High) | |
1152 | - _fushi = Fushi.High; | |
1153 | - else if (l1 > this.Low && l2 >= this.Low) | |
1154 | - _fushi = Fushi.Low; | |
1155 | - else | |
1156 | - _fushi = Fushi.None; | |
1157 | - | |
1158 | - return _fushi; | |
1159 | - } | |
1160 | - } | |
1161 | - | |
1162 | - } | |
1163 | - | |
1164 | - internal class TradeDataOverflowException : ApplicationException | |
1165 | - { | |
1166 | - public TradeDataOverflowException(string msg) | |
1167 | - : base(msg) | |
1168 | - { | |
1169 | - } | |
1170 | - } | |
1171 | - | |
1172 | - internal class IndicatorTimeSeries : TimeSeries | |
1173 | - { | |
1174 | - protected DataFarm _farm; | |
1175 | - protected int _begin; | |
1176 | - protected int _end; | |
1177 | - protected Indicator _indicator; | |
1178 | - | |
1179 | - public IndicatorTimeSeries(DataFarm farm, Indicator ind, int begin, int end) | |
1180 | - { | |
1181 | - _farm = farm; | |
1182 | - _begin = begin; | |
1183 | - _end = end; | |
1184 | - _indicator = ind; | |
1185 | - } | |
1186 | - | |
1187 | - public override int Count | |
1188 | - { | |
1189 | - get | |
1190 | - { | |
1191 | - return _end - _begin; | |
1192 | - } | |
1193 | - } | |
1194 | - public int BeginIndex | |
1195 | - { | |
1196 | - get | |
1197 | - { | |
1198 | - return _begin; | |
1199 | - } | |
1200 | - } | |
1201 | - public int EndIndex | |
1202 | - { | |
1203 | - get | |
1204 | - { | |
1205 | - return _end; | |
1206 | - } | |
1207 | - } | |
1208 | - public override double LastValue | |
1209 | - { | |
1210 | - get | |
1211 | - { | |
1212 | - return _farm.GetByIndex(_end - 1).GetValue(_indicator); | |
1213 | - } | |
1214 | - } | |
1215 | - | |
1216 | - | |
1217 | - protected class IndicatorCursor : TimeSeries.Cursor | |
1218 | - { | |
1219 | - private int _index; | |
1220 | - private IndicatorTimeSeries _parent; | |
1221 | - | |
1222 | - public IndicatorCursor(IndicatorTimeSeries parent) | |
1223 | - { | |
1224 | - _parent = parent; | |
1225 | - _index = _parent._begin; | |
1226 | - } | |
1227 | - public override bool HasNext | |
1228 | - { | |
1229 | - get | |
1230 | - { | |
1231 | - return _index < _parent._end; | |
1232 | - } | |
1233 | - } | |
1234 | - public override double Next | |
1235 | - { | |
1236 | - get | |
1237 | - { | |
1238 | - return _parent._farm.GetByIndex(_index++).GetValue(_parent._indicator); | |
1239 | - } | |
1240 | - } | |
1241 | - } | |
1242 | - | |
1243 | - public override Cursor CreateCursor() | |
1244 | - { | |
1245 | - return new IndicatorCursor(this); | |
1246 | - } | |
1247 | - } | |
832 | + //節の種類 | |
833 | + internal enum Fushi { | |
834 | + Unknown, | |
835 | + None, | |
836 | + High, | |
837 | + Low | |
838 | + } | |
839 | + | |
840 | + /// 日足・週足・月足などの1件のレコード | |
841 | + internal class TradeData { | |
842 | + private DataFarm _farm; | |
843 | + private int _index; | |
844 | + private int _offset; | |
845 | + private double[] _data; | |
846 | + private Fushi _fushi; | |
847 | + | |
848 | + public TradeData(DataFarm farm, int index, int offset) { | |
849 | + _farm = farm; | |
850 | + _index = index; | |
851 | + _offset = offset; | |
852 | + _data = new double[Env.CurrentIndicators.IndicatorCount]; | |
853 | + _fushi = Fushi.Unknown; | |
854 | + for(int i=0; i<_data.Length; i++) | |
855 | + _data[i] = Double.NaN; | |
856 | + } | |
857 | + public DataFarm Farm { | |
858 | + get { | |
859 | + return _farm; | |
860 | + } | |
861 | + } | |
862 | + public int Index { | |
863 | + get { | |
864 | + return _index; | |
865 | + } | |
866 | + } | |
867 | + public int Offset { | |
868 | + get { | |
869 | + return _offset; | |
870 | + } | |
871 | + } | |
872 | + public TradeData Prev { | |
873 | + get { | |
874 | + return _index>0? _farm.GetByIndex(_index-1) : null; | |
875 | + } | |
876 | + } | |
877 | + public TradeData Next { | |
878 | + get { | |
879 | + return _index<_farm.TotalLength-1? _farm.GetByIndex(_index+1) : null; | |
880 | + } | |
881 | + } | |
882 | + public bool IsFuture { | |
883 | + get { | |
884 | + return _index>=_farm.FilledLength; | |
885 | + } | |
886 | + } | |
887 | + public bool CoversDate(int date) { | |
888 | + if(date==this.Date) | |
889 | + return true; | |
890 | + else { | |
891 | + int c = this.Date; | |
892 | + if(c > date) | |
893 | + return false; | |
894 | + else { | |
895 | + TradeData next = this.Next; | |
896 | + return next!=null && date<next.Date; | |
897 | + } | |
898 | + } | |
899 | + } | |
900 | + | |
901 | + public double GetValue(Indicator indicator) { | |
902 | + double t = _data[indicator.LaneID]; | |
903 | + //overflowによる演算不可はPositiveInfinityであらわす | |
904 | + if(Double.IsPositiveInfinity(t)) return Double.NaN; | |
905 | + if(!Double.IsNaN(t)) return t; //キャッシュにヒット | |
906 | + | |
907 | + try { | |
908 | + if(indicator.CheckRange(this)) { | |
909 | + t = indicator.Calculate(this); | |
910 | + _data[indicator.LaneID] = t; | |
911 | + } | |
912 | + else | |
913 | + t = Double.NaN; | |
914 | + return t; | |
915 | + } | |
916 | + catch(TradeDataOverflowException ) { | |
917 | + //Debug.WriteLine("Out of range!"); | |
918 | + _data[indicator.LaneID] = Double.PositiveInfinity; | |
919 | + return Double.NaN; | |
920 | + } | |
921 | + } | |
922 | + public int Date { | |
923 | + get { | |
924 | + return (int)GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Date)); | |
925 | + } | |
926 | + } | |
927 | + public double Open { | |
928 | + get { | |
929 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Open)); | |
930 | + } | |
931 | + } | |
932 | + public double High { | |
933 | + get { | |
934 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High)); | |
935 | + } | |
936 | + } | |
937 | + public double Low { | |
938 | + get { | |
939 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low)); | |
940 | + } | |
941 | + } | |
942 | + public double Close { | |
943 | + get { | |
944 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Close)); | |
945 | + } | |
946 | + } | |
947 | + public double Volume { | |
948 | + get { | |
949 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Volume)); | |
950 | + } | |
951 | + } | |
952 | + public double CreditLong { | |
953 | + get { | |
954 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditLong)); | |
955 | + } | |
956 | + } | |
957 | + public double CreditShort { | |
958 | + get { | |
959 | + return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditShort)); | |
960 | + } | |
961 | + } | |
962 | + | |
963 | + //節の計算 | |
964 | + public Fushi Fushi { | |
965 | + get { | |
966 | + if(_fushi!=Fushi.Unknown) return _fushi; | |
967 | + | |
968 | + double h1 = Double.MinValue; | |
969 | + double l1 = Double.MaxValue; | |
970 | + double h2 = Double.MinValue; | |
971 | + double l2 = Double.MaxValue; | |
972 | + int fushi = Env.Preference.FushiRange; | |
973 | + //あまり端に表示しても仕方ない | |
974 | + if(_index<fushi || _index>_farm.FilledLength-fushi) { | |
975 | + _fushi = Fushi.None; | |
976 | + return _fushi; | |
977 | + } | |
978 | + for(int i=_index-fushi; i<_index; i++) { | |
979 | + h1 = Math.Max(h1, _farm.GetByIndex(i).High); | |
980 | + l1 = Math.Min(l1, _farm.GetByIndex(i).Low); | |
981 | + } | |
982 | + for(int i=_index+1; i<_index+fushi; i++) { | |
983 | + h2 = Math.Max(h2, _farm.GetByIndex(i).High); | |
984 | + l2 = Math.Min(l2, _farm.GetByIndex(i).Low); | |
985 | + } | |
986 | + | |
987 | + //過去に同値があるときは無視、未来にあるときは節 | |
988 | + if(h1<this.High && h2<=this.High) | |
989 | + _fushi = Fushi.High; | |
990 | + else if(l1>this.Low && l2>=this.Low) | |
991 | + _fushi = Fushi.Low; | |
992 | + else | |
993 | + _fushi = Fushi.None; | |
994 | + | |
995 | + return _fushi; | |
996 | + } | |
997 | + } | |
998 | + | |
999 | + } | |
1000 | + | |
1001 | + internal class TradeDataOverflowException : ApplicationException { | |
1002 | + public TradeDataOverflowException(string msg) : base(msg) { | |
1003 | + } | |
1004 | + } | |
1005 | + | |
1006 | + internal class IndicatorTimeSeries : TimeSeries { | |
1007 | + protected DataFarm _farm; | |
1008 | + protected int _begin; | |
1009 | + protected int _end; | |
1010 | + protected Indicator _indicator; | |
1011 | + | |
1012 | + public IndicatorTimeSeries(DataFarm farm, Indicator ind, int begin, int end) { | |
1013 | + _farm = farm; | |
1014 | + _begin = begin; | |
1015 | + _end = end; | |
1016 | + _indicator = ind; | |
1017 | + } | |
1018 | + | |
1019 | + public override int Count { | |
1020 | + get { | |
1021 | + return _end - _begin; | |
1022 | + } | |
1023 | + } | |
1024 | + public int BeginIndex { | |
1025 | + get { | |
1026 | + return _begin; | |
1027 | + } | |
1028 | + } | |
1029 | + public int EndIndex { | |
1030 | + get { | |
1031 | + return _end; | |
1032 | + } | |
1033 | + } | |
1034 | + public override double LastValue { | |
1035 | + get { | |
1036 | + return _farm.GetByIndex(_end-1).GetValue(_indicator); | |
1037 | + } | |
1038 | + } | |
1039 | + | |
1040 | + | |
1041 | + protected class IndicatorCursor : TimeSeries.Cursor { | |
1042 | + private int _index; | |
1043 | + private IndicatorTimeSeries _parent; | |
1044 | + | |
1045 | + public IndicatorCursor(IndicatorTimeSeries parent) { | |
1046 | + _parent = parent; | |
1047 | + _index = _parent._begin; | |
1048 | + } | |
1049 | + public override bool HasNext { | |
1050 | + get { | |
1051 | + return _index<_parent._end; | |
1052 | + } | |
1053 | + } | |
1054 | + public override double Next { | |
1055 | + get { | |
1056 | + return _parent._farm.GetByIndex(_index++).GetValue(_parent._indicator); | |
1057 | + } | |
1058 | + } | |
1059 | + } | |
1060 | + | |
1061 | + public override Cursor CreateCursor() { | |
1062 | + return new IndicatorCursor(this); | |
1063 | + } | |
1064 | + } | |
1248 | 1065 | |
1249 | 1066 | } |