private static void MergeSeries(SeriesSymbolData target, DateTime period, double open, double high, double low, double close, double volume, bool isNewBars, bool isRealtime) { if (isNewBars) { target.AddSeries(period, open, high, low, close, volume, isRealtime); } else { target.SetSeries(open, high, low, close, volume, isRealtime); } }
private static int __iLastSeriesId = 0; //存放最後一個 Series Id(此 Id 會一直遞增, 每次使用 new 產生此類別時就會自動遞增) private static void MergeSeries(SeriesSymbolData target, DateTime period, double open, double high, double low, double close, double volume, bool isNewBars, bool isRealtime) { if (isNewBars) { target.AddSeries(period, open, high, low, close, volume, isRealtime); } else { target.SetSeries(open, high, low, close, volume, isRealtime); } }
internal DataAdapter(InstrumentDataRequest request) { __cCookies = new CookieContainer(); int iCount = request.Range.Count; __cSeries = new SeriesSymbolData(request); __cSeries.onRequest += SeriesSymbolData_onRequest; __cSeries.OnRequest(new DataRequestEvent(iCount, iCount, 1)); //請求歷史資料 __cSeries.MergeTicks(); //合併即時Tick資訊 }
/// <summary> /// 取得指定的總秒數週期 SeriesSymbolData 列表 /// </summary> /// <param name="hashKey">可以為時間週期總秒數或是 Identify</param> internal SeriesSymbolData GetSeries(int hashKey) { int iIndex = 0; SeriesSymbolData cSeries = null; lock (__cIndexs) { if (__cIndexs.TryGetValue(hashKey, out iIndex)) { cSeries = __cSeries[iIndex]; } } return cSeries; }
internal void MergeTick(ITick tick) { lock (__cIndexs) { int iCount = __cSeries.Count; Parallel.For(0, iCount, (i) => { SeriesSymbolData cSeries = __cSeries[i]; if (cSeries.Initialized) { cSeries.Merge(tick); } }); } }
/// <summary> /// 處理清盤重置 /// </summary> /// <param name="dataSource">即時報價來源名稱</param> internal void Reset(string dataSource) { lock (__cIndexs) { int iCount = __cSeries.Count; Parallel.For(0, iCount, (i) => { SeriesSymbolData cSeries = __cSeries[i]; string sDataFeed = cSeries.DataRequest.DataFeed; if (sDataFeed.Equals(dataSource)) { cSeries.Reset(); } }); } }
/// <summary> /// 加入 SeriesSymbolData 列表 /// </summary> /// <param name="series">SeriesSymbolData 列表</param> internal void Add(SeriesSymbolData series) { int iTotalSeconds = series.DataRequest.Resolution.TotalSeconds; lock (__cIndexs) { if (!__cIndexs.ContainsKey(iTotalSeconds)) { int iIndex = __cSeries.Count; __cSeries.Add(series); __cIndexs.Add(iTotalSeconds, iIndex); bool bBase = (iTotalSeconds == Resolution.MIN_BASE_TOTALSECONDS || iTotalSeconds == Resolution.MAX_BASE_TOTALSECONDS); if (!bBase) { series.onRequest += SeriesSymbolData_onRequest; } } } }
private void SeriesSymbolData_onRequest(object sender, DataRequestEvent e) { SeriesSymbolData cTargetSeries = sender as SeriesSymbolData; if (!cTargetSeries.DataRequest.Range.IsAlreadyRequestAllData) { int iTotalSeconds = cTargetSeries.DataRequest.Resolution.TotalSeconds; SeriesSymbolData cBaseSeries = GetSeries(((iTotalSeconds < Resolution.MAX_BASE_TOTALSECONDS) ? Resolution.MIN_BASE_TOTALSECONDS : Resolution.MAX_BASE_TOTALSECONDS)); if (cBaseSeries.DataRequest.Range.IsAlreadyRequestAllData) { e.IsAlreadyRequestAllData = true; e.Count = cBaseSeries.DataRequest.Range.Count / e.Rate; } else { DataRequestEvent cRequestEvent = null; lock (__oLock) { //需要鎖定資源(因為有可能多個策略同時請求基礎序列類別資料, 如果不鎖定會重複請求資料) if (e.Totals == 0) { //檢查是否資料總個數為0(0=使用 InstrumentDataRequest 請求歷史資料) cRequestEvent = e.Clone(); //直接複製 } else { //如果不為0(表示使用者取得資料時超過目前已下載歷史資料的區間, 經過基礎週期比率計算之後再向伺服器請求歷史資料) int iTotals = e.Totals * e.Rate; //資料總個數 * 縮放比率 = 基礎週期需要請求的資料總個數 int iRequestCount = iTotals - cBaseSeries.DataRequest.Range.Count; //計算後的資料總個數 - 基礎週期目前已下載後的資料個數 = 欲請求的的個數 cRequestEvent = new DataRequestEvent(iRequestCount, iTotals, cBaseSeries.DataRequest.Resolution.Rate); } cBaseSeries.OnRequest(cRequestEvent); //回補歷史資訊 } if (cRequestEvent.Result == 0) { e.Result = cRequestEvent.Result; e.Ranges = cRequestEvent.Ranges; e.Count = cRequestEvent.Count / e.Rate; e.IsAlreadyRequestAllData = cRequestEvent.IsAlreadyRequestAllData; } } if (e.IsAlreadyRequestAllData) { //如果已經全部讀取完畢就取消事件 cTargetSeries.RemoveRequest(); } lock (__oLock) { //須鎖定資源(將基礎序列資料合併至目標序列時需要鎖定, 避免合併的時候多執行緒導致合併資料產生問題) int iTargetCount = cTargetSeries.Indexer.Count; int iAllocSize = e.Count - cTargetSeries.DataRequest.Range.Count; if (iTargetCount == 0 || iAllocSize > 0) { if (iAllocSize > 0) { ++iAllocSize; //多預留一個空間(避免陣列空間不足) cTargetSeries.AdjustSize(iAllocSize, true); } cBaseSeries.Merge(cTargetSeries); //合併資料 } } } }
/// <summary> /// 建構子 /// </summary> /// <param name="source">SeriesSymbolData 商品資料類別</param> /// <param name="request">資料請求結構</param> internal SeriesSymbolDataRand(SeriesSymbolData source, InstrumentDataRequest request) { __cSource = source; __cSource.onRequestCompleted += SeriesSymbolData_onRequestCompleted; //附掛請求歷史資料完成的事件通知 __cIndexer = source.Indexer; __iBaseAdjustTotal = __cIndexer.AdjustTotalCount; source.Clone(out __cTimes, out __cOpens, out __cHighs, out __cLows, out __cCloses, out __cVolumes); __iHistoryIndex = __cCloses.Count - (request.Range.Count + source.RealtimeCount); this.Current = 1; //預設值索引從 1 開始(內部會自動計算對應至 SeriesSymbolData 序列資料的正確索引位置) string sDataSource = source.DataRequest.DataFeed; AbstractQuoteService cService = QuoteManager.Manager.GetQuoteService(sDataSource); if (cService != null) { __cQuoteStorage = cService.Storage; } }
/// <summary> /// 加入 SeriesSymbolData 列表 /// </summary> /// <param name="series">SeriesSymbolData 列表</param> /// <param name="useIdentify">是否使用 Identify 當作 Hash 值(true=使用 Identify 作為 Hash 值, false=使用時間週期總秒數當作 Hash 值)</param> internal void Add(SeriesSymbolData series, bool useIdentify = false) { int iHash = (useIdentify) ? series.Id : series.DataRequest.Resolution.TotalSeconds; lock (__cIndexs) { if (!__cIndexs.ContainsKey(iHash)) { int iIndex = __cSeries.Count; __cSeries.Add(series); __cIndexs.Add(iHash, iIndex); if (!useIdentify) { series.Id = iHash; //將 Id 改為 iHash(iHash=時間週期總秒數, 當存入 SeriesStorage 後都以 Id 當作 Hash 存取) bool bBase = (iHash == Resolution.MIN_BASE_TOTALSECONDS || iHash == Resolution.MAX_BASE_TOTALSECONDS); if (!bBase) { series.onRequest += SeriesSymbolData_onRequest; } } } } }
internal void Merge(SeriesSymbolData target) { int iTargetCount = target.Count; int iFirstIndex = target.Indexer.GetBaseIndex(__cDataRequest.Range.Count); DateTime cFrom = __cTimes[Indexer.HistoryIndex]; DateTime cTo = __cTimes[iFirstIndex]; List <DateTime> cPeriods = target.__cDataRequest.Resolution.CalculatePeriods(cFrom, cTo); for (int i = iFirstIndex; i >= Indexer.HistoryIndex; i--) { DateTime cBaseTime = __cTimes[i]; bool bNewBars = Resolution.GetNearestPeriod(cPeriods, ref cBaseTime); MergeSeries(target, cBaseTime, __cTimes[i], __cOpens[i], __cHighs[i], __cLows[i], __cCloses[i], __cVolumes[i], bNewBars, false); if (bNewBars) { target.Indexer.SetBaseIndex(i); } } }
/// <summary> /// 移除 SeriesSymbolData 列表 /// </summary> /// <param name="seriesId">SeriesSymbolData id</param> internal void Remove(int seriesId) { int iIndex = 0; lock (__cIndexs) { if (__cIndexs.TryGetValue(seriesId, out iIndex)) { SeriesSymbolData cTarget = __cSeries[iIndex]; cTarget.Dispose(); int iLast = __cSeries.Count - 1; if (iLast > 0 && iLast > iIndex) { SeriesSymbolData cLast = __cSeries[iLast]; int iLastSeriesId = cLast.Id; __cIndexs[iLastSeriesId] = iIndex; __cSeries[iIndex] = cLast; } __cIndexs.Remove(seriesId); __cSeries.RemoveAt(iLast); } } }
internal void Merge(SeriesSymbolData target) { int iTargetCount = target.Indexer.Count; int iFirstIndex = target.Indexer.GetBaseIndex(__cDataRequest.Range.Count); DateTime cFrom = __cTimes[Indexer.HistoryIndex]; DateTime cTo = __cTimes[iFirstIndex]; List<DateTime> cPeriods = target.__cDataRequest.Resolution.CalculatePeriods(cFrom, cTo); for (int i = iFirstIndex; i >= Indexer.HistoryIndex; i--) { DateTime cBaseTime = __cTimes[i]; bool bNewBars = Resolution.GetNearestPeriod(cPeriods, ref cBaseTime); MergeSeries(target, cBaseTime, __cOpens[i], __cHighs[i], __cLows[i], __cCloses[i], __cVolumes[i], bNewBars, false); if (bNewBars) { target.Indexer.SetBaseIndex(i); } } if (iTargetCount == 0) { target.__cDataRequest.Range.To = __cDataRequest.Range.To; Queue<DateTime> cQueue = target.CreateRealtimePeriods(); for (int i = __cDataRequest.Range.Count; i <= Indexer.RealtimeIndex; i++) { DateTime cBaseTime = __cTimes[i]; bool bCreate = Resolution.GetNearestPeriod(cQueue, ref cBaseTime); MergeSeries(target, cBaseTime, __cOpens[i], __cHighs[i], __cLows[i], __cCloses[i], __cVolumes[i], bCreate, true); } } target.__cUpdateTime = __cUpdateTime; //更新目標資訊的最後更新時間(最後更新時間會牽涉到 Bars 的狀態 Close or Inside) target.Initialized = true; }
internal SeriesSymbolDataRand(SeriesSymbolData source, int maxBarsReferance) { __cIndexer = source.Indexer; __iBaseAdjustTotal = __cIndexer.AdjustTotalCount; source.Clone(out __cTimes, out __cOpens, out __cHighs, out __cLows, out __cCloses, out __cVolumes); if (maxBarsReferance > 0) { __iIgnoreBarsCount = __cCloses.Count - source.RealtimeCount - maxBarsReferance; if (__iIgnoreBarsCount < 0) { __iIgnoreBarsCount = 0; } } }
/// <summary> /// 建構子 /// </summary> /// <param name="source">SeriesSymbolData 類別</param> /// <param name="maxBarsReferance">最大 Bars count 參考值(如果不需要很多歷史資訊計算則可以設定小一點, 設定數量不可以超過歷史資料載入總數量)</param> public Instrument(SeriesSymbolData source, int maxBarsReferance) { __cSource = source; __cFullSymbolData = new SeriesSymbolDataRand(source, maxBarsReferance); this.CurrentBar = 1; //預設值 string sDataSource = __cSource.DataRequest.DataFeed; AbstractQuoteService cService = QuoteManager.Manager.GetQuoteService(sDataSource); if (cService != null) { __cQuoteStorage = cService.Storage; } }