예제 #1
0
        /// <summary>
        ///
        /// </summary>
        /// <returns>The last contract used in the construction of this continuous futures instrument.</returns>
        private Instrument GetContFutData(HistoricalDataRequest request, bool raiseDataEvent = true)
        {
            //copy over the list of contracts that we're gonna be using
            List <Instrument> futures = new List <Instrument>(_contracts[request.AssignedID]);

            //start by cleaning up the data, it is possible that some of the futures may not have had ANY data returned!
            lock (_dataLock)
            {
                futures = futures.Where(x => _data[new KeyValuePair <int, BarSize>(x.ID.Value, request.Frequency)].Count > 0).ToList();
            }

            if (futures.Count == 0)
            {
                Log(LogLevel.Warn, "No data found");

                if (raiseDataEvent)
                {
                    RaiseEvent(HistoricalDataArrived, this, new HistoricalDataEventArgs(request, new List <OHLCBar>()));
                }

                return(null);
            }

            var cf = request.Instrument.ContinuousFuture;

            Instrument frontFuture = futures.FirstOrDefault();
            Instrument backFuture  = futures.ElementAt(1);

            //sometimes the contract will be based on the Xth month
            //this is where we keep track of the actual contract currently being used
            Instrument selectedFuture         = futures.ElementAt(cf.Month - 1);
            Instrument lastUsedSelectedFuture = selectedFuture;

            //final date is the earliest of: the last date of data available, or the request's endingdate
            DateTime lastDateAvailable = new DateTime(1, 1, 1);

            TimeSeries frontData, backData, selectedData;

            lock (_dataLock)
            {
                frontData    = new TimeSeries(_data[new KeyValuePair <int, BarSize>(frontFuture.ID.Value, request.Frequency)]);
                backData     = new TimeSeries(_data[new KeyValuePair <int, BarSize>(backFuture.ID.Value, request.Frequency)]);
                selectedData = new TimeSeries(_data[new KeyValuePair <int, BarSize>(selectedFuture.ID.Value, request.Frequency)]);

                lastDateAvailable = _data[new KeyValuePair <int, BarSize>(futures.Last().ID.Value, request.Frequency)].Last().DT;
            }

            DateTime finalDate = request.EndingDate < lastDateAvailable ? request.EndingDate : lastDateAvailable;

            //This is a super dirty hack to make non-time based rollovers actually work.
            //The reason is that the starting point will otherwise be a LONG time before the date we're interested in.
            //And at that time both the front and back futures are really far from expiration.
            //As such volumes can be wonky, and thus result in a rollover far before we ACTUALLY would
            //want to roll over if we had access to even earlier data.
            DateTime currentDate = frontFuture.Expiration.Value.AddDays(-20 - cf.RolloverDays);

            frontData.AdvanceTo(currentDate);
            backData.AdvanceTo(currentDate);
            selectedData.AdvanceTo(currentDate);

            List <OHLCBar> cfData = new List <OHLCBar>();

            Calendar calendar = MyUtils.GetCalendarFromCountryCode("US");

            bool        switchContract = false;
            int         counter = 0;                               //some rollover rules require multiple consecutive days of greater vol/OI...this keeps track of that
            List <long> frontDailyVolume = new List <long>();      //keeps track of how much volume has occured in each day
            List <int>  frontDailyOpenInterest = new List <int>(); //keeps track of open interest on a daily basis
            List <long> backDailyVolume = new List <long>();
            List <int>  backDailyOpenInterest = new List <int>();
            long        frontTodaysVolume = 0, backTodaysVolume = 0;

            //add the first piece of data we have available, and start looping
            cfData.Add(selectedData[0]);

            //the first time we go from one day to the next we don't want to check for switching conditions
            //because we need to ensure that we use an entire day's worth of volume data.
            bool firstDaySwitchover = true;

            while (currentDate < finalDate)
            {
                //keep track of total volume "today"
                if (frontData[0].Volume.HasValue)
                {
                    frontTodaysVolume += frontData[0].Volume.Value;
                }
                if (backData != null && backData[0].Volume.HasValue)
                {
                    backTodaysVolume += backData[0].Volume.Value;
                }

                if (frontData.CurrentBar > 0 && frontData[0].DT.Day != frontData[1].DT.Day)
                {
                    if (firstDaySwitchover)
                    {
                        firstDaySwitchover = false;
                        frontTodaysVolume  = 0;
                        backTodaysVolume   = 0;
                    }

                    frontDailyVolume.Add(frontTodaysVolume);
                    backDailyVolume.Add(backTodaysVolume);

                    if (frontData[0].OpenInterest.HasValue)
                    {
                        frontDailyOpenInterest.Add(frontData[0].OpenInterest.Value);
                    }
                    if (backData != null && backData[0].OpenInterest.HasValue)
                    {
                        backDailyOpenInterest.Add(backData[0].OpenInterest.Value);
                    }

                    frontTodaysVolume = 0;
                    backTodaysVolume  = 0;

                    //do we need to switch contracts?
                    switch (cf.RolloverType)
                    {
                    case ContinuousFuturesRolloverType.Time:
                        if (MyUtils.BusinessDaysBetween(currentDate, frontFuture.Expiration.Value, calendar) <= cf.RolloverDays)
                        {
                            switchContract = true;
                        }
                        break;

                    case ContinuousFuturesRolloverType.Volume:
                        if (backData != null && backDailyVolume.Last() > frontDailyVolume.Last())
                        {
                            counter++;
                        }
                        else
                        {
                            counter = 0;
                        }
                        switchContract = counter >= cf.RolloverDays;
                        break;

                    case ContinuousFuturesRolloverType.OpenInterest:
                        if (backData != null && backDailyOpenInterest.Last() > frontDailyOpenInterest.Last())
                        {
                            counter++;
                        }
                        else
                        {
                            counter = 0;
                        }
                        switchContract = counter >= cf.RolloverDays;
                        break;

                    case ContinuousFuturesRolloverType.VolumeAndOpenInterest:
                        if (backData != null && backDailyOpenInterest.Last() > frontDailyOpenInterest.Last() &&
                            backDailyVolume.Last() > frontDailyVolume.Last())
                        {
                            counter++;
                        }
                        else
                        {
                            counter = 0;
                        }
                        switchContract = counter >= cf.RolloverDays;
                        break;

                    case ContinuousFuturesRolloverType.VolumeOrOpenInterest:
                        if (backData != null && backDailyOpenInterest.Last() > frontDailyOpenInterest.Last() ||
                            backDailyVolume.Last() > frontDailyVolume.Last())
                        {
                            counter++;
                        }
                        else
                        {
                            counter = 0;
                        }
                        switchContract = counter >= cf.RolloverDays;
                        break;
                    }
                }

                if (frontFuture.Expiration.Value <= currentDate)
                {
                    //no matter what, obviously we need to switch if the contract expires
                    switchContract = true;
                }

                //finally if we have simply run out of data, we're forced to switch
                if (frontData.ReachedEndOfSeries)
                {
                    switchContract = true;
                }

                //finally advance the time and indices...keep moving forward until the selected series has moved
                frontData.NextBar();
                currentDate = frontData[0].DT;
                if (backData != null)
                {
                    backData.AdvanceTo(currentDate);
                }
                selectedData.AdvanceTo(currentDate);

                //this next check here is necessary for the time-based switchover to work after weekends or holidays
                if (cf.RolloverType == ContinuousFuturesRolloverType.Time &&
                    (frontFuture.Expiration.Value < currentDate ||
                     MyUtils.BusinessDaysBetween(currentDate, frontFuture.Expiration.Value, calendar) <= cf.RolloverDays))
                {
                    switchContract = true;
                }

                //we switch to the next contract
                if (switchContract)
                {
                    //make any required price adjustments
                    decimal adjustmentFactor;
                    if (cf.AdjustmentMode == ContinuousFuturesAdjustmentMode.Difference)
                    {
                        adjustmentFactor = backData[0].Close - frontData[0].Close;
                        foreach (OHLCBar bar in cfData)
                        {
                            AdjustBar(bar, adjustmentFactor, cf.AdjustmentMode);
                        }
                    }
                    else if (cf.AdjustmentMode == ContinuousFuturesAdjustmentMode.Ratio)
                    {
                        adjustmentFactor = backData[0].Close / frontData[0].Close;
                        foreach (OHLCBar bar in cfData)
                        {
                            AdjustBar(bar, adjustmentFactor, cf.AdjustmentMode);
                        }
                    }

                    //update the contracts
                    var prevFront = frontFuture;
                    frontFuture = backFuture;
                    backFuture  = futures.FirstOrDefault(x => x.Expiration > backFuture.Expiration);
                    var prevSelected = selectedFuture;
                    selectedFuture = futures.Where(x => x.Expiration >= frontFuture.Expiration).ElementAtOrDefault(cf.Month - 1);

                    Log(LogLevel.Info,
                        string.Format("CFB Filling request for {0}: switching front contract from {1} to {2} (selected contract from {3} to {4}) at {5}",
                                      request.Instrument.Symbol,
                                      prevFront.Symbol,
                                      frontFuture.Symbol,
                                      prevSelected.Symbol,
                                      selectedFuture == null
                            ? ""
                            : selectedFuture.Symbol,
                                      currentDate.ToString("yyyy-MM-dd")));

                    if (frontFuture == null)
                    {
                        break;                      //no other futures left, get out
                    }
                    if (selectedFuture == null)
                    {
                        break;
                    }

                    lock (_dataLock)
                    {
                        frontData    = new TimeSeries(_data[new KeyValuePair <int, BarSize>(frontFuture.ID.Value, request.Frequency)]);
                        backData     = backFuture != null ? new TimeSeries(_data[new KeyValuePair <int, BarSize>(backFuture.ID.Value, request.Frequency)]) : null;
                        selectedData = new TimeSeries(_data[new KeyValuePair <int, BarSize>(selectedFuture.ID.Value, request.Frequency)]);
                    }

                    frontData.AdvanceTo(currentDate);
                    if (backData != null)
                    {
                        backData.AdvanceTo(currentDate);
                    }
                    selectedData.AdvanceTo(currentDate);

                    //TODO make sure that the data series actually cover the current date

                    switchContract         = false;
                    lastUsedSelectedFuture = selectedFuture;
                }

                cfData.Add(selectedData[0]);
            }

            //clean up
            _contracts.Remove(request.AssignedID);

            //throw out any data from before the start of the request
            cfData = cfData.Where(x => x.DT >= request.StartingDate && x.DT <= request.EndingDate).ToList();

            //we're done, so just raise the event
            if (raiseDataEvent)
            {
                RaiseEvent(HistoricalDataArrived, this, new HistoricalDataEventArgs(request, cfData));
            }

            //clean up some data!
            lock (_dataUsesLock)
            {
                foreach (Instrument i in futures)
                {
                    var kvp = new KeyValuePair <int, BarSize>(i.ID.Value, request.Frequency);
                    if (_dataUsesPending[kvp] == 1) //this data isn't needed anywhere else, we can delete it
                    {
                        _dataUsesPending.Remove(kvp);
                        lock (_dataLock)
                        {
                            _data.Remove(kvp);
                        }
                    }
                    else
                    {
                        _dataUsesPending[kvp]--;
                    }
                }
            }

            return(lastUsedSelectedFuture);
        }
예제 #2
0
 public void BusinessDaysBetweenReturnsCorrectNumberOfDays()
 {
     Assert.AreEqual(250, MyUtils.BusinessDaysBetween(new DateTime(2002, 12, 15), new DateTime(2003, 12, 15), new UnitedStates()));
     Assert.AreEqual(18, MyUtils.BusinessDaysBetween(new DateTime(2003, 12, 15), new DateTime(2004, 1, 12), new UnitedStates()));
     Assert.AreEqual(0, MyUtils.BusinessDaysBetween(new DateTime(2003, 12, 15), new DateTime(2003, 12, 15), new UnitedStates()));
 }
예제 #3
0
 public void BusinessDaysBetweenThrowsExceptionWhenEndDateBeforeStartDate()
 {
     MyUtils.BusinessDaysBetween(new DateTime(2000, 1, 1), new DateTime(1999, 1, 1), new UnitedStates());
 }
예제 #4
0
파일: MyUtilsTest.cs 프로젝트: KBurov/qdms
 public void BusinessDaysBetweenThrowsExceptionWhenEndDateBeforeStartDate()
 {
     Assert.That(
         () => MyUtils.BusinessDaysBetween(new DateTime(2000, 1, 1), new DateTime(1999, 1, 1), new UnitedStates()),
         Throws.TypeOf <Exception>());
 }