/// <summary> /// Process a FrontContractRequest /// CFs with a time-based switchover are calculated on the spot /// CFs with other types of switchover require data, so we send off the appropriate data requests here /// </summary> private void ProcessFrontContractRequest(FrontContractRequest request) { _logger.Info($"Processing front contract request for symbol: {request.Instrument.Symbol} at: {(request.Date.HasValue ? request.Date.ToString() : "Now")}"); if (request.Instrument.ContinuousFuture.RolloverType == ContinuousFuturesRolloverType.Time) { ProcessTimeBasedFrontContractRequest(request); } else //otherwise, we have to actually look at the historical data to figure out which contract is selected { ProcessDataBasedFrontContractRequest(request); } }
/// <summary> /// Finds the currently active futures contract for a continuous futures instrument. /// The contract is returned asynchronously through the FoundFrontContract event. /// </summary> /// <returns>Returns an ID uniquely identifying this request.</returns> public int RequestFrontContract(Instrument cfInstrument, DateTime?date = null) { if (!cfInstrument.IsContinuousFuture) { throw new Exception("Not a continuous future instrument."); } _lastFrontDontractRequestID++; var req = new FrontContractRequest { ID = _lastFrontDontractRequestID, Instrument = cfInstrument, Date = date }; ProcessFrontContractRequest(req); return(_lastFrontDontractRequestID); }
/// <summary> /// Finds the currently active futures contract for a continuous futures instrument. /// The contract is returned asynchronously through the FoundFrontContract event. /// </summary> /// <returns>Returns an ID uniquely identifying this request.</returns> public int RequestFrontContract(Instrument cfInstrument, DateTime?date = null) { if (!cfInstrument.IsContinuousFuture) { throw new Exception("Not a continuous future instrument."); } var id = Interlocked.Increment(ref _lastFrontDontractRequestID); var req = new FrontContractRequest { ID = id, Instrument = cfInstrument, Date = date }; ProcessFrontContractRequest(req); return(id); }
/// <summary> /// Finds the front contract for continuous futures with time-based roll. /// </summary> private void ProcessTimeBasedFrontContractRequest(FrontContractRequest request) { DateTime currentDate = request.Date ?? DateTime.Now; var cf = request.Instrument.ContinuousFuture; //if the roll-over is time based, we can find the appropriate contract programmatically DateTime selectedDate = currentDate; while (!cf.MonthIsUsed(selectedDate.Month)) { selectedDate = selectedDate.AddMonths(1); } DateTime currentMonthsExpirationDate = cf.UnderlyingSymbol.ExpirationDate(selectedDate.Year, selectedDate.Month); DateTime switchOverDate = currentMonthsExpirationDate; Calendar calendar = MyUtils.GetCalendarFromCountryCode("US"); //the front contract //find the switchover date int daysBack = cf.RolloverDays; while (daysBack > 0) { switchOverDate = switchOverDate.AddDays(-1); if (calendar.isBusinessDay(switchOverDate)) { daysBack--; } } //this month's contract has already been switched to the next one int monthsLeft = 1; int count = 0; if (currentDate >= switchOverDate) { while (monthsLeft > 0) { count++; if (cf.MonthIsUsed(selectedDate.AddMonths(count).Month)) { monthsLeft--; } } selectedDate = selectedDate.AddMonths(count); } //we found the "front" month, no go back the required number of months //while skipping unused months monthsLeft = cf.Month - 1; count = 0; while (monthsLeft > 0) { if (cf.MonthIsUsed(selectedDate.AddMonths(count).Month)) { monthsLeft--; } count++; } selectedDate = selectedDate.AddMonths(count); string tradingClass = request.Instrument.TradingClass; //we got the month we want! find the contract Instrument contract = _instrumentRepo.FindInstruments(x => x.Expiration.HasValue && x.Expiration.Value.Month == selectedDate.Month && x.Expiration.Value.Year == selectedDate.Year && x.UnderlyingSymbol == cf.UnderlyingSymbol.Symbol && (string.IsNullOrEmpty(tradingClass) || x.TradingClass == tradingClass)).Result.FirstOrDefault(); var timer = new Timer(50) { AutoReset = false }; timer.Elapsed += (sender, e) => { lock (_frontContractReturnLock) { RaiseEvent(FoundFrontContract, this, new FoundFrontContractEventArgs(request.ID, contract, currentDate)); } }; timer.Start(); }
/// <summary> /// Finds the front contract for continuous futures with non-time-based roll. /// </summary> private void ProcessDataBasedFrontContractRequest(FrontContractRequest request) { DateTime currentDate = request.Date ?? DateTime.Now; //this is a tough one, because it needs to be asynchronous (historical //data can take a long time to download). var r = new Random(); //we use GetRequiredRequests to get the historical requests we need to make var tmpReq = new HistoricalDataRequest { Instrument = request.Instrument, StartingDate = currentDate.AddDays(-1), EndingDate = currentDate, Frequency = BarSize.OneDay }; //give the request a unique id lock (_requestsLock) { int id; do { id = r.Next(); } while (_requests.ContainsKey(id)); tmpReq.AssignedID = id; _requests.Add(tmpReq.AssignedID, tmpReq); } var reqs = GetRequiredRequests(tmpReq); //make sure the request is fulfillable with the available contracts, otherwise return empty-handed if (reqs.Count == 0 || reqs.Count(x => x.Instrument.Expiration.HasValue && x.Instrument.Expiration.Value >= request.Date) == 0) { lock (_frontContractReturnLock) { RaiseEvent(FoundFrontContract, this, new FoundFrontContractEventArgs(request.ID, null, currentDate)); } lock (_requestsLock) { _requests.Remove(tmpReq.AssignedID); } return; } // add it to the collection of requests so we can access it later _requestTypes.Add(tmpReq.AssignedID, false); //add it to the front contract requests map _frontContractRequestMap.Add(tmpReq.AssignedID, request); //finally send out a request for all the data...when it arrives, //we process it and return the required front future foreach (HistoricalDataRequest req in reqs) { lock (_dataUsesLock) { var kvp = new KeyValuePair <int, BarSize>(req.Instrument.ID.Value, req.Frequency); if (_dataUsesPending.ContainsKey(kvp)) { _dataUsesPending[kvp]++; } else { _dataUsesPending.Add(kvp, 1); } } int requestID = _client.RequestHistoricalData(req); _histReqIDMap.Add(requestID, tmpReq.AssignedID); } }
/// <summary> /// Historical data has arrived. /// Add it to our data store, then check if all requests have been /// fulfilled for a particular continuous futures request. If they /// have, go do the calculations. /// </summary> private void _client_HistoricalDataReceived(object sender, HistoricalDataEventArgs e) { if (e.Request.Instrument.ID == null) { return; } int id = e.Request.Instrument.ID.Value; var kvpID = new KeyValuePair <int, BarSize>(id, e.Request.Frequency); lock (_data) { if (_data.ContainsKey(kvpID)) { //We already have data on this instrument ID/Frequency combo. //Add the arrived data, then discard any doubles, then order. _data[kvpID].AddRange(e.Data); _data[kvpID] = _data[kvpID].Distinct((x, y) => x.DT == y.DT).ToList(); _data[kvpID] = _data[kvpID].OrderBy(x => x.DT).ToList(); } else { //We have nothing on this instrument ID/Frequency combo. //Just add a new entry in the dictionary. _data.Add(kvpID, e.Data); } } //Here we check if all necessary requests have arrived. If they have, do some work. lock (_reqCountLock) { //get the request id of the continuous futures request that caused this contract request int cfReqID = _histReqIDMap[e.Request.RequestID]; _histReqIDMap.Remove(e.Request.RequestID); _requestCounts[cfReqID]--; if (_requestCounts[cfReqID] == 0) { //we have received all the data we asked for _requestCounts.Remove(e.Request.RequestID); HistoricalDataRequest req; lock (_requestsLock) { req = _requests[cfReqID]; _requests.Remove(cfReqID); } if (_requestTypes[cfReqID]) { //This request originates from a CF data request //so now we want to generate the continuous prices GetContFutData(req); } else { //This request originates from a front contract request Instrument frontContract = GetContFutData(req, false); FrontContractRequest originalReq = _frontContractRequestMap[cfReqID]; lock (_frontContractReturnLock) { RaiseEvent(FoundFrontContract, this, new FoundFrontContractEventArgs(originalReq.ID, frontContract, originalReq.Date == null ? DateTime.Now : originalReq.Date.Value)); } } _requestTypes.Remove(e.Request.AssignedID); } } }