internal SeriesSymbolData(InstrumentDataRequest dataRequest, InstrumentSettings settings = null) { this.Id = 0x40000000 | System.Threading.Interlocked.Increment(ref __iLastSeriesId); this.Indexer = new SeriesIndexer(); __cDataRequest = dataRequest; __cSettings = ((settings == null) ? new InstrumentSettings(ref __cDataRequest) : settings.Create(ref __cDataRequest)); __cUpdateTime = DateTime.UtcNow.AddHours(__cSettings.TimeZone); SessionObject cSession = __cSettings.GetSessionFromToday(); __iRealtimeCount = (cSession == null) ? 1 : (int) ((cSession.GetCloseTimeForDaylight() - cSession.GetStartTimeForDaylight()).TotalSeconds / dataRequest.Resolution.TotalSeconds + 1); Indexer.HistoryIndex = 0; Indexer.RealtimeIndex = -1; __cOpens = new Series<double>(__iRealtimeCount); __cOpens.onRequest += Series_onRequest; __cHighs = new Series<double>(__iRealtimeCount); __cHighs.onRequest += Series_onRequest; __cLows = new Series<double>(__iRealtimeCount); __cLows.onRequest += Series_onRequest; __cCloses = new Series<double>(__iRealtimeCount); __cCloses.onRequest += Series_onRequest; __cTimes = new Series<DateTime>(__iRealtimeCount); __cTimes.onRequest += Series_onRequest; __cVolumes = new Series<double>(__iRealtimeCount); __cVolumes.onRequest += Series_onRequest; __cDataRequest.Range.Count = 0; //將資料筆數設定為0(因為一開始沒有請求任何資訊) }
private DataAdapter LoadAdapter(ref InstrumentDataRequest dataRequest, bool useCache = true) { InstrumentDataRequest cRequest = dataRequest; cRequest.Resolution = Resolution.GetBaseValue(dataRequest.Resolution); DataAdapter cAdapter = new DataAdapter(cRequest); InstrumentDataRequest cDataRequest = cAdapter.Series.DataRequest; dataRequest.Range.Count = cDataRequest.Range.Count; //將正確的請求歷史資料數量傳入至結構 if (useCache) { //使用快取(true=建立完 DataAdapter 之後,將其保存至快取內以方便後續請求時使用) if (!cDataRequest.Range.IsAlreadyRequestAllData) { cAdapter.onCompleted += DataAdapter_onCompleted; string sLSymbolId = dataRequest.Symbol.ToLower(); int iTotalSeconds = cDataRequest.Resolution.TotalSeconds; if (iTotalSeconds < Resolution.MAX_BASE_TOTALSECONDS) { lock (__cMinBases) { if (!__cMinBases.ContainsKey(sLSymbolId)) { __cMinBases.Add(sLSymbolId, cAdapter); } } } else { lock (__cDayBases) { if (!__cDayBases.ContainsKey(sLSymbolId)) { __cDayBases.Add(sLSymbolId, cAdapter); } } } } } return cAdapter; }
private void CreateInstrument(SeriesSymbolDataRand seriesSymbolDataRand, int dataIndex) { if (seriesSymbolDataRand != null) { if (logger.IsInfoEnabled) { InstrumentDataRequest cDataRequest = seriesSymbolDataRand.Source.DataRequest; logger.InfoFormat("[DataLoader.CreateInstrument] {0}:{1}{2} Instrument create completed...", cDataRequest.Symbol, cDataRequest.Resolution.Size, cDataRequest.Resolution.Type); } bool bCompleted = false; seriesSymbolDataRand.SetMaxbarsReferance(__iMaxBarsReference); Instrument cInstrument = new Instrument(seriesSymbolDataRand); lock (__cInstruments) { __cInstruments[dataIndex] = cInstrument; ++__iMaxInstrumentCount; bCompleted = __iMaxInstrumentCount == __cInstruments.Count; } if (onAddInstrument != null) { onAddInstrument(this, new InstrumentChangeEvent(cInstrument, dataIndex)); } if (bCompleted) { //如果讀取資料都已經完成, 發出事件通知已經完成 if (onLoadCompleted != null) { onLoadCompleted(this, EventArgs.Empty); } } } }
/// <summary> /// 非同步模式取得序列商品資訊 /// </summary> /// <param name="dataRequest">資料請求結構</param> /// <param name="result">序列商品資訊回報事件</param> /// <param name="useCache">是否使用快取 [預設:true](true=序列資料結構建立後保存在快取內,下次需要使用直接從快取拿取, false=重新建立序列資料結構,建立的序列資料需要自行移除否則會占用記憶體空間)</param> /// <param name="args">使用者自訂參數</param> /// <param name="millisecondsTimeout">回補資料 Timeout 毫秒數 [預設:System.Threading.Timeout.Infinite (永遠等待直到回補完成)]</param> public void AsyncGetSeries(InstrumentDataRequest dataRequest, EventHandler<SeriesResultEvent> result, bool useCache = true, object args = null, int millisecondsTimeout = Timeout.Infinite) { Task.Factory.StartNew(() => { CheckLogin(dataRequest.DataFeed); Complement(dataRequest, millisecondsTimeout); SeriesSymbolDataRand cSeries = InternalGetSeries(dataRequest, useCache); result(this, new SeriesResultEvent(cSeries, args)); }); }
/// <summary> /// 根據設定資訊調整 InstrumentDataRequest 設定並回傳目前的設定類別 /// </summary> /// <param name="request">InstrumentDataRequest類別</param> /// <returns>回傳值: 目前的 InstrumentSettings類別</returns> public InstrumentSettings Create(ref InstrumentDataRequest request) { SessionObject cSession = GetSessionFromToday(); DataRequestType cRequestType = request.Range.RequestType; request.Resolution.CalculateRate(cSession.GetStartTimeForDaylight(), cSession.GetCloseTimeForDaylight(), __cProperty.Sessions.Count); if (cRequestType == DataRequestType.DaysBack || cRequestType == DataRequestType.FromTo) { request.Range.Count = request.Resolution.ConvertDaysToBars(request.Range.Count); } return this; }
internal DataRequestEvent(InstrumentDataRequest dataRequest) { this.Result = -1; __iRate = dataRequest.Resolution.Rate; DataRequest cRange = dataRequest.Range; this.Count = cRange.Count; this.Ranges = new DateTime[] { cRange.From, cRange.To }; }
internal DataAdapter(InstrumentDataRequest request) { __cSeries = new SeriesSymbolData(request); __cSeries.onRequest += SeriesSymbolData_onRequest; __cDevice = __cDeviceCreator.Create(); __cDevice.SetSeries(__cSeries); __cSeries.OnRequest(new DataRequestEvent(request)); //請求歷史資料 __cSeries.MergeTicks(); //合併即時Tick資訊 }
private void DrawTitle(Rectangle rectangle, ChartProperty property) { IQuote cQuote = __cBars.Quotes; IInstrumentSettings cSettings = __cBars.Info; InstrumentDataRequest cRequest = __cBars.Request; Resolution cResolution = cRequest.Resolution; string sTitle = string.Format("{0} {1} ({2}{3}) #{4} {5}", (cQuote == null) ? cRequest.Symbol : cQuote.SymbolId, (cQuote == null) ? cSettings.ASymbolInfo2.SymbolName : cQuote.SymbolName, cResolution.Size, cResolution.Type.ToString(), cSettings.Category.ToString(), cRequest.Exchange); IntPtr cOldFont = __cGDI.SelectFont(property.TitleFont); __cGDI.DrawString(sTitle, property.ForeColor, 5, rectangle.Top + 2); __cGDI.RemoveObject(__cGDI.SelectFont(cOldFont)); }
private static SeriesSymbolData CreateSeries(string symbolId, EResolution type) { InstrumentDataRequest cRequest = new InstrumentDataRequest() { Exchange = "TWSE", DataFeed = "Mitake", Range = DataRequest.CreateBarsBack(DateTime.Now, 1), Resolution = new Resolution(type, 1), Symbol = symbolId }; return(new SeriesSymbolData(cRequest)); }
/// <summary> /// 請求商品歷史資料 /// </summary> /// <param name="e">資料請求事件</param> protected internal override void Request(DataRequestEvent e) { InstrumentDataRequest cDataRequest = this.DataRequest; DateTime[] cRanges = e.Ranges; ZRequest cRequest = new ZRequest(); cRequest.Method = "POST"; cRequest.Url = __sHttpUrl; cRequest.Parameters = string.Format("exchange={0}&symbolId={1}&timeFrame={2}&position={3}&startDate={4}&endDate={5}&count={6}", cDataRequest.Exchange, cDataRequest.Symbol, cDataRequest.Resolution.TotalSeconds, this.Position, cRanges[0].ToString("yyyy-MM-dd"), cRanges[1].ToString("yyyy-MM-dd"), e.Count); cRequest.CookieContainer = __cCookies; int iRet = cRequest.Request(); if (iRet == 0) { ZReader cReader = new ZReader(cRequest.Response); e.Result = cReader.Result; if (cReader.Result == 0) { int iCount = e.Count = cReader.Count; this.AdjustSize(iCount); ZBuffer cBuffer = cReader.Read(); while (--iCount >= 0) { cBuffer.Position = iCount * DATA_BLOCK_SIZE; DateTime cDate = cBuffer.GetDateTime(); double dOpen = cBuffer.GetDouble(); double dHigh = cBuffer.GetDouble(); double dLow = cBuffer.GetDouble(); double dClose = cBuffer.GetDouble(); double dVolume = cBuffer.GetDouble(); this.AddSeries(cDate, dOpen, dHigh, dLow, dClose, dVolume); } this.Position = cReader.Position; e.Ranges[0] = cReader.BeginDate; e.Ranges[1] = cReader.EndDate; if (this.Position == 0) { e.IsAlreadyRequestAllData = true; } } } }
/// <summary> /// 非同步讀取資料請求結構內的 Instrument 資料 /// </summary> /// <param name="request">資料請求結構</param> /// <param name="result">當成功取得 Instrument 商品資料會使用此委派方法回傳資料</param> /// <param name="useCache">是否使用快取 [預設:false](true=序列資料結構建立後保存在快取內,下次需要使用直接從快取拿取, false=重新建立序列資料結構,建立的序列資料需要自行移除否則會占用記憶體空間)</param> /// <param name="args">使用者自訂參數</param> /// <param name="millisecondsTimeout">回補資料 Timeout 毫秒數 [預設:System.Threading.Timeout.Infinite (永遠等待直到回補完成)]</param> public void BeginLoadData(InstrumentDataRequest request, LoadDataCallback result, bool useCache = false, object args = null, int millisecondsTimeout = System.Threading.Timeout.Infinite) { SeriesManager.Manager.SetQuoteService(request.DataFeed); SeriesManager.Manager.AsyncGetSeries(request, (object sender, SeriesResultEvent e) => { SeriesSymbolDataRand cSeriesSymbolDataRand = e.Data; cSeriesSymbolDataRand.SetMaxbarsReferance(__iMaxBarsReference); Instrument cInstrument = new Instrument(cSeriesSymbolDataRand); //建立 Instrument 商品資料 Instrument cBars_0 = GetInstrument(0); //取得目前第 0 個 Instrument 商品資料 if (cBars_0 != null) { cInstrument.MoveBars(cBars_0.Time.Value); } result(new DataLoaderResult(cInstrument, cInstrument.Quotes, args)); }, useCache, null, millisecondsTimeout); }
/// <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> /// 將設定檔轉換為 InstrumentDataRequest 列表的格式 /// </summary> /// <param name="requests">RequestSetting 陣列</param> /// <returns>返回值: InstrumentDataRequest 列表</returns> public static List <InstrumentDataRequest> Convert(List <RequestSetting> requests) { List <InstrumentDataRequest> cResult = new List <InstrumentDataRequest>(); foreach (RequestSetting cRequest in requests) { string[] sPeriods = cRequest.DataPeriod.Split(','); int iPeriodSize = int.Parse(sPeriods[0]); EResolution cResolution = (EResolution)Enum.Parse(typeof(EResolution), sPeriods[1]); InstrumentDataRequest cDataRequest = new InstrumentDataRequest() { Exchange = cRequest.Exchange, DataFeed = cRequest.DataFeed, Resolution = new Resolution(cResolution, iPeriodSize), Symbol = cRequest.SymbolId }; string[] sParams = cRequest.Range.Split(','); string sMode = sParams[0]; string[] sArgs = sParams[1].Split(';'); DateTime cEndDate = DateTime.Parse(sArgs[0]); if (cEndDate == MAX_REQUEST_LASTDATE) { cEndDate = DateTime.Today; } switch (sMode) { case "barsBack": cDataRequest.Range = DataRequest.CreateBarsBack(cEndDate, int.Parse(sArgs[1])); break; case "daysBack": cDataRequest.Range = DataRequest.CreateDaysBack(cEndDate, int.Parse(sArgs[1])); break; case "fromTo": cDataRequest.Range = DataRequest.CreateFromTo(DateTime.Parse(sArgs[1]), cEndDate); break; } cResult.Add(cDataRequest); } return(cResult); }
private void Complement(InstrumentDataRequest request, int millisecondsTimeout) { string sDataSource = request.DataFeed; if (__cDataSources.Contains(sDataSource)) { AbstractQuoteService cService = QuoteManager.Manager.GetQuoteService(sDataSource); if (cService != null) { string sSymbolId = request.Symbol; IQuote cQuote = cService.Storage.GetQuote(sSymbolId); if (cQuote != null && cQuote.ComplementStatus != ComplementStatus.Complemented) { EventWaitHandle cWaitHandle = null; lock (__cAsyncArgs) { _AsyncEventArgs cArgs = null; string sHashKey = string.Format("{0}_{1}", sDataSource, sSymbolId); if (__cAsyncArgs.TryGetValue(sHashKey, out cArgs)) { cWaitHandle = cArgs.handle; } else { if (cQuote.ComplementStatus == ComplementStatus.NotComplement) { cArgs = new _AsyncEventArgs(); cArgs.request = request; cWaitHandle = new ManualResetEvent(false); cArgs.handle = cWaitHandle; __cAsyncArgs.Add(sHashKey, cArgs); cService.AddSubscribe(sSymbolId); cService.Complement(sSymbolId); } } } if (cWaitHandle != null) { if (!cWaitHandle.WaitOne(millisecondsTimeout)) { //等待回補資訊(如果 millisecondsTimeout 為 -1 則會無限等待, 如果有設定 Timeout 在時間到了之後還沒回補完畢就直接釋放 WaitHandle 元件並移除) cWaitHandle.Dispose(); string sHashKey = string.Format("{0}_{1}", sDataSource, sSymbolId); lock (__cAsyncArgs) { __cAsyncArgs.Remove(sHashKey); } } } } } } }
private void SeriesSymbolData_onRequest(object sender, DataRequestEvent e) { int iCount = e.Count; DataRequest cDataRequest = __cSeries.DataRequest.Range; int iBaseCount = cDataRequest.Count; bool bRequest = e.Totals > iBaseCount; if (!bRequest) { bRequest = (__cDevice.Position == -1) ? true : e.CheckRequest(cDataRequest); } if (bRequest) { __cDevice.Request(e); if (e.Result == 0) { e.Count += iBaseCount; if (e.IsAlreadyRequestAllData) { __cSeries.RemoveRequest(); //如果已經全部讀取完畢就取消事件 if (onCompleted != null) { InstrumentDataRequest cInstDataRequest = __cSeries.DataRequest; onCompleted(this, new DataAdapterCompleteEvent(cInstDataRequest.Symbol, cInstDataRequest.Resolution.TotalSeconds)); } } } } else { e.Result = 0; e.Count = iBaseCount; e.Ranges = new DateTime[] { cDataRequest.From, cDataRequest.To }; e.IsAlreadyRequestAllData = cDataRequest.IsAlreadyRequestAllData; } }
internal SeriesSymbolData(InstrumentDataRequest dataRequest, InstrumentSettings settings = null) { this.Indexer = new SeriesIndexer(); __cDataRequest = dataRequest; __cSettings = ((settings == null) ? new InstrumentSettings(ref __cDataRequest) : settings.Create(ref __cDataRequest)); __cUpdateTime = DateTime.UtcNow.AddHours(__cSettings.TimeZone); SessionObject cSession = __cSettings.GetSessionFromToday(); __iRealtimeCount = (int)((cSession.EndTime - cSession.StartTime).TotalSeconds / dataRequest.Resolution.TotalSeconds + 1); Indexer.HistoryIndex = 0; Indexer.RealtimeIndex = -1; __cOpens = new Series <double>(__iRealtimeCount); __cHighs = new Series <double>(__iRealtimeCount); __cLows = new Series <double>(__iRealtimeCount); __cCloses = new Series <double>(__iRealtimeCount); __cTimes = new Series <DateTime>(__iRealtimeCount); __cVolumes = new Series <double>(__iRealtimeCount); __cDataRequest.Range.Count = 0; //將資料筆數設定為0(因為一開始沒有請求任何資訊) }
/// <summary> /// 建構子 /// </summary> /// <param name="request">InstrumentDataRequest類別</param> public InstrumentSettings(ref InstrumentDataRequest request) { AbstractExchange cExchange = ProductManager.Manager.GetExchange(request.Exchange); __sSymbolId = request.Symbol; __sExchange = request.Exchange; __sDataSource = request.DataFeed; __dTimeZone = cExchange.TimeZone; __cProduct = cExchange.GetProduct(__sSymbolId); __cProperty = cExchange.GetProperty(__sSymbolId, __sDataSource); //如果沒有商品資訊就使用 request.Symbol 第一個字元當作搜尋條件來取代 if (__cProduct == null) { List<Product> cProducts = cExchange.SearchProducts(new string(__sSymbolId[0], 1), false); foreach (Product cProduct in cProducts) { AbstractProductProperty cProperty = cExchange.GetProperty(cProduct.SymbolId); if (cProperty != null) { __cProduct = cProduct; __cProperty = cProperty; break; } } } __cOptionType = __cProperty.GetCallOrPut(__cProduct); //取得選擇權 Call or Put 類型 __dStrikePrice = __cProperty.GetStrikePrice(__cProduct); //取得選擇權履約價格 //取得合約到期日索引值 IContractTime cContractTime = __cProperty.ContractRule as IContractTime; if (cContractTime != null) { __iContractIndex = cContractTime.GetContractIndex(__sSymbolId); } this.Create(ref request); __cResolution = request.Resolution; }
private Instrument GetBars(string commodityId, char callOrPut, int targetPrice) { InstrumentDataRequest cRequest = new InstrumentDataRequest() { Exchange = "TWSE", DataFeed = "Mitake", Range = DataRequest.CreateBarsBack(DateTime.Now, MAX_LOAD_OPTIONS_COUNT), Resolution = new Resolution(EResolution.Day, 1), Symbol = string.Format("{0}{1}{2}.tw", commodityId, callOrPut, targetPrice) }; __cExchange.AddProduct(new Product() { Category = ESymbolCategory.IndexOption, CommodityId = "TXW", SymbolId = cRequest.Symbol, SymbolName = cRequest.Symbol }); return new Instrument(SeriesManager.Manager.GetSeries(cRequest), 0); }
internal SeriesSymbolData CreateSeries(InstrumentDataRequest dataRequest) { return new SeriesSymbolData(dataRequest, __cSettings); }
private SeriesSymbolDataRand InternalGetSeries(InstrumentDataRequest dataRequest, bool useCache) { SeriesStorage cStorage = null; string sLSymbolId = dataRequest.Symbol.ToLower(); lock (__cStorages) { if (!__cStorages.TryGetValue(sLSymbolId, out cStorage)) { cStorage = new SeriesStorage(16); __cStorages.Add(sLSymbolId, cStorage); } } SeriesSymbolDataRand cSeriesRand = null; int iTotalSeconds = dataRequest.Resolution.TotalSeconds; if (useCache) { //是否使用快取 lock (cStorage) { //需要 lock 這個區塊(避免非同步讀取資料時發生問題) int iBaseSeconds = (iTotalSeconds < Resolution.MAX_BASE_TOTALSECONDS) ? Resolution.MIN_BASE_TOTALSECONDS : Resolution.MAX_BASE_TOTALSECONDS; SeriesSymbolData cSeries = cStorage.GetSeries(iTotalSeconds); if (cSeries == null) { cSeries = cStorage.GetSeries(iBaseSeconds); if (cSeries == null) { DataAdapter cAdapter = LoadAdapter(ref dataRequest); cSeries = cAdapter.Series; cStorage.Add(cSeries); } } if (iBaseSeconds == iTotalSeconds) { dataRequest = cSeries.DataRequest; goto exit; } else { cSeries = cSeries.CreateSeries(dataRequest); //利用基礎周期建立其他的資料周期 cStorage.Add(cSeries); //加入至 SeriesStorage } dataRequest.Resolution = cSeries.DataRequest.Resolution; //將目標的週期結構更新至傳入的 InstrumentDataRequest 週期結構 DataRequestEvent cRequestEvent = new DataRequestEvent(dataRequest); cSeries.OnRequest(cRequestEvent); //如果已經存在則請求使用者需要的歷史資料區間(請求方法會檢查目前已下載的歷史資料區間是否足夠, 如果使用者需要的歷史資料區間比較大會向伺服器請求) dataRequest.Range.Count = cRequestEvent.Count; //將請求後的正確數量傳入至結構內 exit: cSeriesRand = new SeriesSymbolDataRand(cSeries, dataRequest); } } else { DataAdapter cAdapter = LoadAdapter(ref dataRequest, false); //重新建立新的基礎週期序列資料(不使用快取, 不保存至快取內, 使用完畢之後立即 Dispose) SeriesSymbolData cSeries = cAdapter.Series; //取得新的基礎周期序列資料 int iBaseSeconds = (iTotalSeconds < Resolution.MAX_BASE_TOTALSECONDS) ? Resolution.MIN_BASE_TOTALSECONDS : Resolution.MAX_BASE_TOTALSECONDS; if (iBaseSeconds == iTotalSeconds) { dataRequest = cSeries.DataRequest; } else { SeriesSymbolData cTargetSeries = cSeries.CreateSeries(dataRequest); //使用 InstrumentDataRequest 建立新的其他週期序列資料 cSeries.Merge(cTargetSeries); //將基礎周期序列資料合併至新的其他週期序列資料 cSeries.Dispose(); //釋放基礎周期序列資料 cSeries = cTargetSeries; } cSeriesRand = new SeriesSymbolDataRand(cSeries, dataRequest); cStorage.Add(cSeries, true); //保存序列資料(存放在 SeriesStorage 內的序列資料才會自動合併最新的即時資訊報價) cAdapter.Dispose(); //釋放資料配置者類別 } return cSeriesRand; }
/// <summary> /// 取得序列商品資訊 /// </summary> /// <param name="dataRequest">資料請求結構</param> /// <param name="useCache">是否使用快取 [預設:true](true=序列資料結構建立後保存在快取內,下次需要使用直接從快取拿取, false=重新建立序列資料結構,建立的序列資料需要自行移除否則會占用記憶體空間)</param> /// <param name="millisecondsTimeout">回補資料 Timeout 毫秒數 [預設:System.Threading.Timeout.Infinite (永遠等待直到回補完成)]</param> /// <returns>返回值: SeriesSymbolDataRand 類別</returns> public SeriesSymbolDataRand GetSeries(InstrumentDataRequest dataRequest, bool useCache = true, int millisecondsTimeout = Timeout.Infinite) { CheckLogin(dataRequest.DataFeed); Complement(dataRequest, millisecondsTimeout); return InternalGetSeries(dataRequest, useCache); }