- /*
- * Copyright (c) Daisuke OKAJIMA All rights reserved.
- *
- * $Id$
- */
- // Copyright (c) 2014 panacoran <panacoran@users.sourceforge.jp>
- // This program is part of OmegaChart.
- // OmegaChart is licensed under the Apache License, Version 2.0.
- using System;
- using System.IO;
- using System.Collections;
- using System.Diagnostics;
- using System.Text;
- using Zanetti.Arithmetic;
- using Zanetti.Arithmetic.Series;
- using Zanetti.Indicators;
- namespace Zanetti.Data
- {
- //primitive indicatorをサポートするためのもの
- internal class DataFarmPrimitiveAccess
- {
- //delegateの引数になるためにこの形でないとだめ
- internal static double GetDate(TradeData tr)
- {
- return (double)tr.Farm.GetInt(tr.Offset);
- }
- internal static double GetOpen(TradeData tr)
- {
- return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.OPEN_OFFSET), tr);
- }
- internal static double GetHigh(TradeData tr)
- {
- return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.HIGH_OFFSET), tr);
- }
- internal static double GetLow(TradeData tr)
- {
- return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.LOW_OFFSET), tr);
- }
- internal static double GetClose(TradeData tr)
- {
- return AdjustPrice((double)tr.Farm.GetInt(tr.Offset + DataFarm.CLOSE_OFFSET), tr);
- }
- internal static double GetVolume(TradeData tr)
- {
- //オーバーフロー対策の一時しのぎ
- return AdjustVolume((double)(uint)tr.Farm.GetInt(tr.Offset + DataFarm.VOLUME_OFFSET), tr);
- }
- internal static double GetCreditLong(TradeData tr)
- {
- return AdjustVolume((double)tr.Farm.GetInt(tr.Offset + DataFarm.CREDITLONG_OFFSET), tr);
- }
- internal static double GetCreditShort(TradeData tr)
- {
- return AdjustVolume((double)tr.Farm.GetInt(tr.Offset + DataFarm.CREDITSHORT_OFFSET), tr);
- }
- private static double AdjustPrice(double value, TradeData tr)
- {
- double split = Env.Preference.AdjustSplit ? tr.Farm.CalcSplitRatio(tr.Date) : 1;
- if (value == 0 && GetVolume(tr) == 0)
- { //出来高がない日は価格が0と記入されているので前日の終値で代用
- TradeData pr = tr.Prev;
- return pr == null ? 0 : GetClose(tr.Prev);
- }
- else
- return tr.Farm.Brand.PriceScale * value / split;
- }
- private static double AdjustVolume(double value, TradeData tr)
- {
- double split = Env.Preference.AdjustSplit ? tr.Farm.CalcSplitRatio(tr.Date) : 1;
- return value * split;
- }
- }
- internal class NewDailyData
- {
- public int open;
- public int high;
- public int low;
- public int close;
- public int volume;
- }
- internal abstract class DataFarm : IDisposable
- {
- public const int RECORD_LENGTH = 32;
- public const int OPEN_OFFSET = 4;
- public const int HIGH_OFFSET = 8;
- public const int LOW_OFFSET = 12;
- public const int CLOSE_OFFSET = 16;
- public const int VOLUME_OFFSET = 20;
- public const int CREDITSHORT_OFFSET = 24;
- public const int CREDITLONG_OFFSET = 28;
- protected bool _isEmpty; //エラーなどで利用不能なことを示すフラグ
- protected AbstractBrand _brand;
- protected byte[] _farm; //一次データ。同一のDataFarmオブジェクトを他の銘柄に使いまわすときもあるので、必要以上の長さが確保されることもある
- protected int _byteLength; //_farmの論理的な長さ
- protected TradeData[] _data; //必要に応じて生成されるTradeDataの列。一目など未来の日付のデータがあると配列の長さは_farmに対応する分より大きいこともある
- protected int _filledLength; //最新日付までの長さ
- public DataFarm()
- {
- }
- public abstract void LoadFor(AbstractBrand br);
- public AbstractBrand Brand
- {
- get
- {
- return _brand;
- }
- }
- public int TotalLength
- {
- get
- {
- return _data.Length;
- }
- }
- public int FilledLength
- {
- get
- {
- return _filledLength;
- }
- }
- public bool IsEmpty
- {
- get
- {
- return _isEmpty;
- }
- }
- public byte[] RawDataImage
- {
- get
- {
- return _farm;
- }
- }
- internal int GetInt(int offset)
- {
- if (offset >= _byteLength)
- throw new IndexOutOfRangeException();
- unsafe
- {
- fixed (byte* p = &_farm[0])
- {
- return *(int*)(p + offset);
- }
- }
- }
- public TradeData GetByIndex(int index)
- {
- Debug.Assert(_data != null);
- if (index < 0 || index >= _data.Length)
- throw new TradeDataOverflowException(index.ToString() + " is out of range");
- TradeData td = _data[index];
- if (td != null) return td; //cache hit
- td = new TradeData(this, index, index * RECORD_LENGTH);
- _data[index] = td;
- return td;
- }
- public abstract int LastDate { get; }
- public abstract int FirstDate { get; }
- public int DateToIndex(int date)
- {
- return DateToIndex(0, _filledLength, date);
- }
- private int DateToIndex(int begin, int end, int date)
- {
- //binary search
- if (end - begin <= 1)
- return begin;
- else
- {
- int h = (begin + end) / 2;
- int t = GetByIndex(h).Date;
- if (date < t)
- return DateToIndex(begin, h, date);
- else
- return DateToIndex(h, end, date);
- }
- }
- //分割比率の取得
- public double CalcSplitRatio(int date)
- {
- return _brand.CalcSplitRatio(date, this.LastDate);
- }
- public void Dispose()
- {
- _farm = null;
- _data = null;
- }
- internal static int GetInt(byte[] rawdata, int offset)
- {
- Debug.Assert(rawdata.Length > 0);
- unsafe
- {
- fixed (byte* p = &rawdata[0])
- {
- return *(int*)(p + offset);
- }
- }
- }
- internal static void SetInt(byte[] rawdata, int offset, int value)
- {
- unsafe
- {
- fixed (byte* p = &rawdata[0])
- {
- *(int*)(p + offset) = value;
- }
- }
- }
- internal static void SetUInt(byte[] rawdata, int offset, uint value)
- {
- unsafe
- {
- fixed (byte* p = &rawdata[0])
- {
- *(uint*)(p + offset) = value;
- }
- }
- }
- protected static int AdjustPrice(int raw, double ratio)
- {
- return (int)((double)raw / ratio);
- }
- protected static int AdjustVolume(int raw, double ratio)
- {
- return (int)((double)raw * ratio);
- }
- }
- internal class DailyDataFarm : DataFarm
- {
- protected int _extraDataOffset; //1日単位でデータの追加をしたときのために
- public DailyDataFarm()
- : base()
- {
- }
- public override void LoadFor(AbstractBrand br)
- {
- _brand = br;
- Construct(Util.GetDailyDataFileName(br.Code), 0);
- }
- public void LoadFor(AbstractBrand br, int extra_dates)
- {
- _brand = br;
- Construct(Util.GetDailyDataFileName(br.Code), extra_dates);
- }
- private void Construct(string filename, int extra_dates)
- {
- _isEmpty = true;
- #if DOJIMA
- Dojima.DojimaUtil.HalfDailyDataFarmCache.Clear(_brand);
- #endif
- if (File.Exists(filename))
- {
- int length = (int)new FileInfo(filename).Length;
- if (length == 0) return;
- if (_farm == null || _farm.Length < length + extra_dates * RECORD_LENGTH)
- _farm = new byte[length + extra_dates * RECORD_LENGTH];
- int future_length = Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Daily);
- _filledLength = length / RECORD_LENGTH;
- _data = new TradeData[_filledLength + future_length];
- _byteLength = length;
- _extraDataOffset = 0;
- _isEmpty = false;
- FileStream s = null;
- try
- {
- s = new FileStream(filename, FileMode.Open);
- s.Read(_farm, 0, length);
- }
- finally
- {
- if (s != null) s.Close();
- }
- // 個別銘柄の株価データの先頭にある出来高0のデータを取り除く
- var basic = _brand as BasicBrand;
- if (basic == null || basic.Market == MarketType.B || basic.Market == MarketType.Custom)
- return;
- var idx = 0;
- for (var i = 0; i < _filledLength; i++)
- {
- unsafe
- {
- var head = i * RECORD_LENGTH;
- fixed (byte* p = &_farm[0])
- {
- if (*(int*)(p + head + VOLUME_OFFSET) == 0)
- idx += RECORD_LENGTH;
- else
- break;
- }
- }
- }
- if (idx == 0)
- return;
- _byteLength -= idx;
- _filledLength = _byteLength / RECORD_LENGTH;
- for (var i = 0; i < _byteLength; i++)
- _farm[i] = _farm[i + idx];
- }
- }
- public void Save(string filename)
- {
- if (_farm != null)
- { //エラーハンドリングできていない
- FileStream s = new FileStream(filename, FileMode.Create);
- s.Write(_farm, 0, _byteLength + _extraDataOffset);
- s.Close();
- }
- }
- internal void WriteExtraData(int record_offset, int value)
- {
- unsafe
- {
- fixed (byte* p = &_farm[0])
- {
- *(int*)(p + _byteLength + _extraDataOffset + record_offset) = value;
- }
- }
- }
- internal void ProgressExtraDataAddress()
- {
- _extraDataOffset += RECORD_LENGTH;
- Debug.Assert(_extraDataOffset <= _farm.Length);
- }
- //連続的に複数の日付を更新することもできるが、増加方向であることが必須
- internal void UpdateDataFarm(int date, NewDailyData td)
- {
- int ld;
- if (this.IsEmpty)
- {
- //とりあえず1日書き込める分だけ初期化
- if (_farm == null)
- {
- _farm = new byte[RECORD_LENGTH * 200]; //この上限はどこかで取得すべきだが
- _filledLength = 0;
- _data = null;
- _byteLength = 0;
- _extraDataOffset = 0;
- }
- ld = 0;
- }
- else
- ld = this.LastDate;
- int offset;
- if (ld < date)
- {
- offset = _byteLength + _extraDataOffset; //emptyのときは常にこれ
- _extraDataOffset += RECORD_LENGTH;
- }
- else
- {
- offset = _byteLength - RECORD_LENGTH;
- do
- {
- int t = GetInt(offset);
- if (t == date)
- break;
- else if (t < date)
- {
- offset += RECORD_LENGTH;
- break;
- }
- else
- offset -= RECORD_LENGTH;
- } while (true);
- }
- unsafe
- {
- fixed (byte* p = &_farm[0])
- {
- byte* a = p + offset;
- *(int*)(a + 0) = date;
- *(int*)(a + 4) = td.open;
- *(int*)(a + 8) = td.high;
- *(int*)(a + 12) = td.low;
- *(int*)(a + 16) = td.close;
- *(int*)(a + 20) = td.volume;
- }
- }
- }
- //次の2つはTradeDataを作らないようにしている、注意
- public override int LastDate
- {
- get
- {
- return GetInt(_byteLength - RECORD_LENGTH);
- }
- }
- public override int FirstDate
- {
- get
- {
- return GetInt(0);
- }
- }
- }
- internal class WeeklyDataFarm : DataFarm
- {
- private int _firstDate;
- private int _lastDate;
- public WeeklyDataFarm()
- : base()
- {
- }
- public override void LoadFor(AbstractBrand br)
- {
- _brand = br;
- Construct(Util.GetDailyDataFileName(br.Code));
- }
- private void Construct(string filename)
- {
- _isEmpty = true;
- if (File.Exists(filename))
- {
- int length = (int)new FileInfo(filename).Length;
- if (length > 0)
- {
- //まずは日足を読む
- byte[] daily = new byte[length];
- FileStream s = null;
- try
- {
- s = new FileStream(filename, FileMode.Open);
- s.Read(daily, 0, length);
- }
- finally
- {
- if (s != null) s.Close();
- }
- _isEmpty = false;
- _firstDate = GetInt(daily, 0);
- _lastDate = GetInt(daily, daily.Length - RECORD_LENGTH);
- var daily_begin = Util.IntToDate(GetInt(daily, 0));
- var weekly_begin = daily_begin.AddDays(-(int)daily_begin.DayOfWeek);
- var daily_end = Util.IntToDate(GetInt(daily, daily.Length - RECORD_LENGTH));
- var weekly_end = daily_end.AddDays(-(int)daily_end.DayOfWeek);
- _filledLength = (int)(weekly_end - weekly_begin).TotalDays / 7 + 1;
- _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Weekly)];
- //byte[]部分のデータ読み
- _farm = new byte[_data.Length * RECORD_LENGTH];
- _byteLength = _farm.Length;
- int offset = 0;
- var weekly = weekly_begin;
- for (int i = 0; i < _filledLength; i++, weekly = weekly.AddDays(7))
- {
- offset = FillWeeklyData(i * RECORD_LENGTH, daily, offset, Util.DateToInt(weekly));
- if (offset >= daily.Length) break;
- }
- }
- }
- }
- private int FillWeeklyData(int farmoffset, byte[] daily, int offset, int firstdate)
- {
- int enddate = Util.DateToInt(Util.IntToDate(firstdate).AddDays(7));
- int vol = 0, high = Int32.MinValue, low = Int32.MaxValue;
- int open = 0, close = 0, cre_long = 0, cre_short = 0;
- int today = GetInt(daily, offset);
- bool is_index = _brand.IsBuiltIn;
- // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。
- // 下の、SetInt(_farm, farmoffset, wi.FirstDate);
- // で、後に式を評価する際に用いられる基準日として日曜基準で 'wi.FirstDate' を使っているのだから、
- // ここでもこの値を使うべき。 2005/3/15 T. SARUKI
- //
- double base_split = this.CalcSplitRatio(firstdate); //分割を考慮する場合は期間内の調整が要る
- while (offset <= daily.Length - RECORD_LENGTH && today < enddate)
- {
- //if(!is_index && today>20031201) Debugger.Break();
- double split = Env.Preference.AdjustSplit ? this.CalcSplitRatio(today) / base_split : 1;
- int v = AdjustVolume(GetInt(daily, offset + VOLUME_OFFSET), split);
- if (is_index || v != 0)
- { //非indexで出来高0の日は集計しない
- if (open == 0) open = AdjustPrice(GetInt(daily, offset + OPEN_OFFSET), split);
- close = AdjustPrice(GetInt(daily, offset + CLOSE_OFFSET), split);
- high = Math.Max(high, AdjustPrice(GetInt(daily, offset + HIGH_OFFSET), split));
- low = Math.Min(low, AdjustPrice(GetInt(daily, offset + LOW_OFFSET), split));
- cre_long = AdjustVolume(GetInt(daily, offset + CREDITLONG_OFFSET), split);
- cre_short = AdjustVolume(GetInt(daily, offset + CREDITSHORT_OFFSET), split);
- vol += v;
- }
- offset += RECORD_LENGTH;
- if (offset < daily.Length) today = GetInt(daily, offset);
- }
- SetInt(_farm, farmoffset, firstdate);
- SetInt(_farm, farmoffset + OPEN_OFFSET, open);
- SetInt(_farm, farmoffset + HIGH_OFFSET, high);
- SetInt(_farm, farmoffset + LOW_OFFSET, low);
- SetInt(_farm, farmoffset + CLOSE_OFFSET, close);
- SetInt(_farm, farmoffset + VOLUME_OFFSET, vol);
- SetInt(_farm, farmoffset + CREDITLONG_OFFSET, cre_long);
- SetInt(_farm, farmoffset + CREDITSHORT_OFFSET, cre_short);
- return offset;
- }
- public override int LastDate
- {
- get
- {
- return _lastDate;
- }
- }
- public override int FirstDate
- {
- get
- {
- return _firstDate;
- }
- }
- }
- internal class MonthlyDataFarm : DataFarm
- {
- private int _firstDate;
- private int _lastDate;
- public MonthlyDataFarm()
- : base()
- {
- }
- public override void LoadFor(AbstractBrand br)
- {
- _brand = br;
- Construct(Util.GetDailyDataFileName(br.Code));
- }
- private void Construct(string filename)
- {
- _isEmpty = true;
- if (File.Exists(filename))
- {
- int length = (int)new FileInfo(filename).Length;
- if (length > 0)
- {
- //まずは日足を読む
- byte[] daily = new byte[length];
- _isEmpty = false;
- FileStream s = null;
- try
- {
- s = new FileStream(filename, FileMode.Open);
- s.Read(daily, 0, length);
- }
- finally
- {
- if (s != null) s.Close();
- }
- _firstDate = GetInt(daily, 0);
- _lastDate = GetInt(daily, daily.Length - RECORD_LENGTH);
- DateTime monthly_begin = new DateTime(_firstDate / 10000, (_firstDate % 10000) / 100, (_firstDate % 100));
- DateTime monthly_end = new DateTime(_lastDate / 10000, (_lastDate % 10000) / 100, (_lastDate % 100));
- _filledLength = (monthly_end.Year - monthly_begin.Year) * 12 + monthly_end.Month + 1 - monthly_begin.Month;
- _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Monthly)];
- // 以下WeeklyIndexとかぶって冗長
- //byte[]部分のデータ読み
- _farm = new byte[_data.Length * RECORD_LENGTH];
- _byteLength = _farm.Length;
- DateTime yearmonth = monthly_begin;
- int offset = 0;
- for (int i = 0; i < _filledLength; i++)
- {
- offset = FillMonthlyData(i * RECORD_LENGTH, daily, offset, yearmonth);
- if (offset >= daily.Length) break;
- yearmonth = yearmonth.AddMonths(1);
- }
- }
- }
- }
- // このメソッドもWeeklyIndexのFillWeeklyDataとかぶってかなり冗長
- private int FillMonthlyData(int farmoffset, byte[] daily, int offset, DateTime yearmonth)
- {
- DateTime endmonth = yearmonth.AddMonths(1);
- int enddate = endmonth.Year * 10000 + endmonth.Month * 100 + 1;
- int vol = 0, high = Int32.MinValue, low = Int32.MaxValue;
- int open = 0, close = 0, cre_long = 0, cre_short = 0;
- int today = GetInt(daily, offset);
- bool is_index = _brand.IsBuiltIn;
- // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。
- // 下の、SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1);
- // で、後に式を評価する際に用いられる基準日として月の初日である 'yearmonth.Year * 10000 + yearmonth.Month * 100 + 1' を使っているのだから、
- // ここでもこの値を使うべき。 2005/3/15 T. SARUKI
- //
- double base_split = this.CalcSplitRatio(Util.DateToInt(yearmonth.Year, yearmonth.Month, 1));
- while (offset <= daily.Length - RECORD_LENGTH && today < enddate)
- {
- double split = Env.Preference.AdjustSplit ? this.CalcSplitRatio(today) / base_split : 1;
- int v = AdjustVolume(GetInt(daily, offset + VOLUME_OFFSET), split);
- if (is_index || v != 0)
- { //非indexで出来高0の日は集計しない
- if (open == 0) open = AdjustPrice(GetInt(daily, offset + OPEN_OFFSET), split);
- close = AdjustPrice(GetInt(daily, offset + CLOSE_OFFSET), split);
- high = Math.Max(high, AdjustPrice(GetInt(daily, offset + HIGH_OFFSET), split));
- low = Math.Min(low, AdjustPrice(GetInt(daily, offset + LOW_OFFSET), split));
- cre_long = AdjustVolume(GetInt(daily, offset + CREDITLONG_OFFSET), split);
- cre_short = AdjustVolume(GetInt(daily, offset + CREDITSHORT_OFFSET), split);
- vol += v;
- }
- offset += RECORD_LENGTH;
- if (offset < daily.Length) today = GetInt(daily, offset);
- }
- SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1);
- SetInt(_farm, farmoffset + OPEN_OFFSET, open);
- SetInt(_farm, farmoffset + HIGH_OFFSET, high);
- SetInt(_farm, farmoffset + LOW_OFFSET, low);
- SetInt(_farm, farmoffset + CLOSE_OFFSET, close);
- SetInt(_farm, farmoffset + VOLUME_OFFSET, vol);
- SetInt(_farm, farmoffset + CREDITLONG_OFFSET, cre_long);
- SetInt(_farm, farmoffset + CREDITSHORT_OFFSET, cre_short);
- return offset;
- }
- public override int LastDate
- {
- get
- {
- return _lastDate;
- }
- }
- public override int FirstDate
- {
- get
- {
- return _firstDate;
- }
- }
- }
- internal class YearlyDataFarm : DataFarm
- {
- private int _firstDate;
- private int _lastDate;
- public YearlyDataFarm()
- : base()
- {
- }
- public override void LoadFor(AbstractBrand br)
- {
- _brand = br;
- Construct(Util.GetDailyDataFileName(br.Code));
- }
- private void Construct(string filename)
- {
- _isEmpty = true;
- if (File.Exists(filename))
- {
- int length = (int)new FileInfo(filename).Length;
- if (length > 0)
- {
- //まずは日足を読む
- byte[] daily = new byte[length];
- _isEmpty = false;
- FileStream s = null;
- try
- {
- s = new FileStream(filename, FileMode.Open);
- s.Read(daily, 0, length);
- }
- finally
- {
- if (s != null) s.Close();
- }
- _firstDate = GetInt(daily, 0);
- _lastDate = GetInt(daily, daily.Length - RECORD_LENGTH);
- DateTime yearly_begin = new DateTime(_firstDate / 10000, (_firstDate % 10000) / 100, (_firstDate % 100));
- DateTime yearly_end = new DateTime(_lastDate / 10000, (_lastDate % 10000) / 100, (_lastDate % 100));
- _filledLength = yearly_end.Year - yearly_begin.Year + 1;
- _data = new TradeData[_filledLength + Env.CurrentIndicators.GetAddedFutureLength(ChartFormat.Yearly)];
- // 以下WeeklyIndexとかぶって冗長
- //byte[]部分のデータ読み
- _farm = new byte[_data.Length * RECORD_LENGTH];
- _byteLength = _farm.Length;
- DateTime yearmonth = yearly_begin;
- int offset = 0;
- for (int i = 0; i < _filledLength; i++)
- {
- offset = FillYearlyData(i * RECORD_LENGTH, daily, offset, yearmonth);
- if (offset >= daily.Length) break;
- yearmonth = yearmonth.AddYears(1);
- }
- }
- }
- }
- // このメソッドもWeeklyIndexのFillWeeklyDataとかぶってかなり冗長
- private int FillYearlyData(int farmoffset, byte[] daily, int offset, DateTime yearmonth)
- {
- DateTime endyear = yearmonth.AddYears(1);
- int enddate = endyear.Year * 10000 + 101;
- int high = Int32.MinValue, low = Int32.MaxValue;
- int open = 0, close = 0, cre_long = 0, cre_short = 0;
- uint vol = 0;
- int today = GetInt(daily, offset);
- bool is_index = _brand.IsBuiltIn;
- // base_splitを得るのに最初の取引日である 'today' を使うのは誤り。
- // 下の、SetInt(_farm, farmoffset, yearmonth.Year * 10000 + yearmonth.Month * 100 + 1);
- // で、後に式を評価する際に用いられる基準日として月の初日である 'yearmonth.Year * 10000 + yearmonth.Month * 100 + 1' を使っているのだから、
- // ここでもこの値を使うべき。 2005/3/15 T. SARUKI
- //
- double base_split = this.CalcSplitRatio(Util.DateToInt(yearmonth.Year, 1, 1));
- while (offset <= daily.Length - RECORD_LENGTH && today < enddate)
- {
- double split = Env.Preference.AdjustSplit ? this.CalcSplitRatio(today) / base_split : 1;
- int v = AdjustVolume(GetInt(daily, offset + VOLUME_OFFSET), split);
- if (is_index || v != 0)
- { //非indexで出来高0の日は集計しない
- if (open == 0) open = AdjustPrice(GetInt(daily, offset + OPEN_OFFSET), split);
- close = AdjustPrice(GetInt(daily, offset + CLOSE_OFFSET), split);
- high = Math.Max(high, AdjustPrice(GetInt(daily, offset + HIGH_OFFSET), split));
- low = Math.Min(low, AdjustPrice(GetInt(daily, offset + LOW_OFFSET), split));
- cre_long = AdjustVolume(GetInt(daily, offset + CREDITLONG_OFFSET), split);
- cre_short = AdjustVolume(GetInt(daily, offset + CREDITSHORT_OFFSET), split);
- vol += (uint)(v / 10);
- }
- offset += RECORD_LENGTH;
- if (offset < daily.Length) today = GetInt(daily, offset);
- }
- SetInt(_farm, farmoffset, yearmonth.Year * 10000 + 101);
- SetInt(_farm, farmoffset + OPEN_OFFSET, open);
- SetInt(_farm, farmoffset + HIGH_OFFSET, high);
- SetInt(_farm, farmoffset + LOW_OFFSET, low);
- SetInt(_farm, farmoffset + CLOSE_OFFSET, close);
- SetUInt(_farm, farmoffset + VOLUME_OFFSET, vol);
- SetInt(_farm, farmoffset + CREDITLONG_OFFSET, cre_long);
- SetInt(_farm, farmoffset + CREDITSHORT_OFFSET, cre_short);
- return offset;
- }
- public override int LastDate
- {
- get
- {
- return _lastDate;
- }
- }
- public override int FirstDate
- {
- get
- {
- return _firstDate;
- }
- }
- }
- //他の銘柄から導出される銘柄
- internal class DerivedDataFarm : DataFarm
- {
- private int _firstDate;
- private int _lastDate;
- private DerivedBrand _derivedBrand;
- private ChartFormat _chartFormat;
- public DerivedDataFarm(DerivedBrand br, ChartFormat fmt)
- : base()
- {
- _derivedBrand = br;
- _chartFormat = fmt;
- }
- public override void LoadFor(AbstractBrand br)
- {
- Debug.Assert(br is DerivedBrand);
- _brand = br;
- _derivedBrand = (DerivedBrand)br;
- Construct(_derivedBrand);
- }
- private void Construct(DerivedBrand br)
- {
- DataFarm[] fs = new DataFarm[br.Dependencies.Length];
- int len = Int32.MaxValue;
- int shortest_farm_index = 0;
- for (int i = 0; i < fs.Length; i++)
- {
- DataFarm f = Env.BrandCollection.ReserveFarm(br.Dependencies[i], _chartFormat);
- if (f.IsEmpty)
- {
- _isEmpty = true;
- return; //一つでも利用不可があればダメ
- }
- fs[i] = f;
- if (f.FilledLength < len)
- {
- shortest_farm_index = i;
- len = f.FilledLength;
- }
- }
- DataFarm shortest_farm = fs[shortest_farm_index];
- if (_farm == null || _farm.Length < len * RECORD_LENGTH) _farm = new byte[len * RECORD_LENGTH];
- _byteLength = len * RECORD_LENGTH;
- _data = new TradeData[len + Env.CurrentIndicators.GetAddedFutureLength(_chartFormat)];
- _filledLength = len;
- _isEmpty = false;
- _firstDate = shortest_farm.GetByIndex(0).Date;
- _lastDate = shortest_farm.GetByIndex(shortest_farm.FilledLength - 1).Date;
- //データの構築 本当はここも遅延評価すると効率的だが
- FillData(len, shortest_farm_index, br, fs);
- }
- private void FillData(int len, int shortest_farm_index, DerivedBrand br, DataFarm[] deps)
- {
- int[] indexmap = new int[deps.Length];
- EvalResult[][] args = new EvalResult[4][];
- for (int i = 0; i < 4; i++)
- {
- args[i] = new EvalResult[deps.Length];
- for (int j = 0; j < deps.Length; j++) args[i][j] = new EvalResult(0);
- }
- Indicator[] inds = new Indicator[] {
- Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Open),
- Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High),
- Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low),
- Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Close)};
- TradeData[] tds = new TradeData[deps.Length];
- Evaluator ev = new Evaluator(br.Name);
- for (int i = 0; i < len; i++)
- {
- ev.BaseIndex = i;
- //日付の決定
- int date = deps[shortest_farm_index].GetByIndex(i).Date;
- int farmoffset = i * RECORD_LENGTH;
- for (int j = 0; j < deps.Length; j++)
- {
- int candidate = indexmap[j] + 1; //多くの場合日付とindexは一致しているので、DateToIndexの実行回数を減らすためindexmapを用意
- TradeData td = candidate < deps[j].TotalLength ? deps[j].GetByIndex(candidate) : null;
- if (td == null || td.Date != date)
- {
- candidate = deps[j].DateToIndex(date);
- td = deps[j].GetByIndex(candidate);
- }
- indexmap[j] = candidate;
- for (int k = 0; k < inds.Length; k++)
- args[k][j].DoubleVal = td.GetValue(inds[k]);
- }
- //日付
- SetInt(_farm, farmoffset, date);
- //4本値の計算
- Expression expr = br.Expression;
- ev.Args = args[0];
- int open = (int)((EvalResult)expr.Apply(ev)).DoubleVal;
- SetInt(_farm, farmoffset + OPEN_OFFSET, open);
- ev.Args = args[3];
- int close = (int)((EvalResult)expr.Apply(ev)).DoubleVal;
- SetInt(_farm, farmoffset + CLOSE_OFFSET, close);
- ev.Args = args[1];
- int v1 = (int)((EvalResult)expr.Apply(ev)).DoubleVal;
- ev.Args = args[2];
- int v2 = (int)((EvalResult)expr.Apply(ev)).DoubleVal;
- //計算式により、それぞれの高値・安値で計算したものが結果としてどうなるかは変わってしまう
- SetInt(_farm, farmoffset + HIGH_OFFSET, Math.Max(Math.Max(open, close), Math.Max(v1, v2)));
- SetInt(_farm, farmoffset + LOW_OFFSET, Math.Min(Math.Min(open, close), Math.Min(v1, v2)));
- }
- }
- public override int LastDate
- {
- get
- {
- return _lastDate;
- }
- }
- public override int FirstDate
- {
- get
- {
- return _firstDate;
- }
- }
- }
- //internal delegate double Calculate(Indicator indicator, TradeData data);
- //節の種類
- internal enum Fushi
- {
- Unknown,
- None,
- High,
- Low
- }
- /// 日足・週足・月足などの1件のレコード
- internal class TradeData
- {
- private DataFarm _farm;
- private int _index;
- private int _offset;
- private double[] _data;
- private Fushi _fushi;
- public TradeData(DataFarm farm, int index, int offset)
- {
- _farm = farm;
- _index = index;
- _offset = offset;
- _data = new double[Env.CurrentIndicators.IndicatorCount];
- _fushi = Fushi.Unknown;
- for (int i = 0; i < _data.Length; i++)
- _data[i] = Double.NaN;
- }
- public DataFarm Farm
- {
- get
- {
- return _farm;
- }
- }
- public int Index
- {
- get
- {
- return _index;
- }
- }
- public int Offset
- {
- get
- {
- return _offset;
- }
- }
- public TradeData Prev
- {
- get
- {
- return _index > 0 ? _farm.GetByIndex(_index - 1) : null;
- }
- }
- public TradeData Next
- {
- get
- {
- return _index < _farm.TotalLength - 1 ? _farm.GetByIndex(_index + 1) : null;
- }
- }
- public bool IsFuture
- {
- get
- {
- return _index >= _farm.FilledLength;
- }
- }
- public bool CoversDate(int date)
- {
- if (date == this.Date)
- return true;
- else
- {
- int c = this.Date;
- if (c > date)
- return false;
- else
- {
- TradeData next = this.Next;
- return next != null && date < next.Date;
- }
- }
- }
- public double GetValue(Indicator indicator)
- {
- double t = _data[indicator.LaneID];
- //overflowによる演算不可はPositiveInfinityであらわす
- if (Double.IsPositiveInfinity(t)) return Double.NaN;
- if (!Double.IsNaN(t)) return t; //キャッシュにヒット
- try
- {
- if (indicator.CheckRange(this))
- {
- t = indicator.Calculate(this);
- _data[indicator.LaneID] = t;
- }
- else
- t = Double.NaN;
- return t;
- }
- catch (TradeDataOverflowException)
- {
- //Debug.WriteLine("Out of range!");
- _data[indicator.LaneID] = Double.PositiveInfinity;
- return Double.NaN;
- }
- }
- public int Date
- {
- get
- {
- return (int)GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Date));
- }
- }
- public double Open
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Open));
- }
- }
- public double High
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High));
- }
- }
- public double Low
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low));
- }
- }
- public double Close
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Close));
- }
- }
- public double Volume
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Volume));
- }
- }
- public double CreditLong
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditLong));
- }
- }
- public double CreditShort
- {
- get
- {
- return GetValue(Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.CreditShort));
- }
- }
- //節の計算
- public Fushi Fushi
- {
- get
- {
- if (_fushi != Fushi.Unknown) return _fushi;
- double h1 = Double.MinValue;
- double l1 = Double.MaxValue;
- double h2 = Double.MinValue;
- double l2 = Double.MaxValue;
- int fushi = Env.Preference.FushiRange;
- //あまり端に表示しても仕方ない
- if (_index < fushi || _index > _farm.FilledLength - fushi)
- {
- _fushi = Fushi.None;
- return _fushi;
- }
- for (int i = _index - fushi; i < _index; i++)
- {
- h1 = Math.Max(h1, _farm.GetByIndex(i).High);
- l1 = Math.Min(l1, _farm.GetByIndex(i).Low);
- }
- for (int i = _index + 1; i < _index + fushi; i++)
- {
- h2 = Math.Max(h2, _farm.GetByIndex(i).High);
- l2 = Math.Min(l2, _farm.GetByIndex(i).Low);
- }
- //過去に同値があるときは無視、未来にあるときは節
- if (h1 < this.High && h2 <= this.High)
- _fushi = Fushi.High;
- else if (l1 > this.Low && l2 >= this.Low)
- _fushi = Fushi.Low;
- else
- _fushi = Fushi.None;
- return _fushi;
- }
- }
- }
- internal class TradeDataOverflowException : ApplicationException
- {
- public TradeDataOverflowException(string msg)
- : base(msg)
- {
- }
- }
- internal class IndicatorTimeSeries : TimeSeries
- {
- protected DataFarm _farm;
- protected int _begin;
- protected int _end;
- protected Indicator _indicator;
- public IndicatorTimeSeries(DataFarm farm, Indicator ind, int begin, int end)
- {
- _farm = farm;
- _begin = begin;
- _end = end;
- _indicator = ind;
- }
- public override int Count
- {
- get
- {
- return _end - _begin;
- }
- }
- public int BeginIndex
- {
- get
- {
- return _begin;
- }
- }
- public int EndIndex
- {
- get
- {
- return _end;
- }
- }
- public override double LastValue
- {
- get
- {
- return _farm.GetByIndex(_end - 1).GetValue(_indicator);
- }
- }
- protected class IndicatorCursor : TimeSeries.Cursor
- {
- private int _index;
- private IndicatorTimeSeries _parent;
- public IndicatorCursor(IndicatorTimeSeries parent)
- {
- _parent = parent;
- _index = _parent._begin;
- }
- public override bool HasNext
- {
- get
- {
- return _index < _parent._end;
- }
- }
- public override double Next
- {
- get
- {
- return _parent._farm.GetByIndex(_index++).GetValue(_parent._indicator);
- }
- }
- }
- public override Cursor CreateCursor()
- {
- return new IndicatorCursor(this);
- }
- }
- }