示例#1
0
        public ZeroCouponInflationSwapHelper(
            Handle <Quote> quote,
            Period swapObsLag,   // lag on swap observation of index
            Date maturity,
            Calendar calendar,   // index may have null calendar as valid on every day
            BusinessDayConvention paymentConvention,
            DayCounter dayCounter,
            ZeroInflationIndex zii)
            : base(quote)
        {
            swapObsLag_        = swapObsLag;
            maturity_          = maturity;
            calendar_          = calendar;
            paymentConvention_ = paymentConvention;
            dayCounter_        = dayCounter;
            zii_ = zii;

            if (zii_.interpolated())
            {
                // if interpolated then simple
                earliestDate_ = maturity_ - swapObsLag_;
                latestDate_   = maturity_ - swapObsLag_;
            }
            else
            {
                // but if NOT interpolated then the value is valid
                // for every day in an inflation period so you actually
                // get an extended validity, however for curve building
                // just put the first date because using that convention
                // for the base date throughout
                KeyValuePair <Date, Date> limStart = Utils.inflationPeriod(maturity_ - swapObsLag_,
                                                                           zii_.frequency());
                earliestDate_ = limStart.Key;
                latestDate_   = limStart.Key;
            }

            // check that the observation lag of the swap
            // is compatible with the availability lag of the index AND
            // it's interpolation (assuming the start day is spot)
            if (zii_.interpolated())
            {
                Period pShift = new Period(zii_.frequency());
                if ((swapObsLag_ - pShift) <= zii_.availabilityLag())
                {
                    throw new ApplicationException(
                              "inconsistency between swap observation of index "
                              + swapObsLag_ +
                              " index availability " + zii_.availabilityLag() +
                              " index period " + pShift +
                              " and index availability " + zii_.availabilityLag() +
                              " need (obsLag-index period) > availLag");
                }
            }
            Settings.registerWith(update);
        }
        public InterpolatedYoYInflationCurve(Date referenceDate,
                                             Calendar calendar,
                                             DayCounter dayCounter,
                                             Period lag,
                                             Frequency frequency,
                                             bool indexIsInterpolated,
                                             Handle <YieldTermStructure> yTS,
                                             List <Date> dates,
                                             List <double> rates,
                                             Interpolator interpolator)
            : base(referenceDate, calendar, dayCounter, rates[0], lag, frequency, indexIsInterpolated, yTS)
        {
            times_        = new List <double>();
            dates_        = dates;
            data_         = rates;
            interpolator_ = interpolator ?? FastActivator <Interpolator> .Create();

            Utils.QL_REQUIRE(dates_.Count > 1, () => "too few dates: " + dates_.Count);

            // check that the data starts from the beginning,
            // i.e. referenceDate - lag, at least must be in the relevant
            // period
            KeyValuePair <Date, Date> lim = Utils.inflationPeriod(yTS.link.referenceDate() - this.observationLag(), frequency);

            Utils.QL_REQUIRE(lim.Key <= dates_[0] && dates_[0] <= lim.Value, () =>
                             "first data date is not in base period, date: " + dates_[0]
                             + " not within [" + lim.Key + "," + lim.Value + "]");

            Utils.QL_REQUIRE(this.data_.Count == dates_.Count, () =>
                             "indices/dates count mismatch: "
                             + this.data_.Count + " vs " + dates_.Count);

            this.times_    = new InitializedList <double>(dates_.Count);
            this.times_[0] = timeFromReference(dates_[0]);

            for (int i = 1; i < dates_.Count; i++)
            {
                Utils.QL_REQUIRE(dates_[i] > dates_[i - 1], () => "dates not sorted");

                // YoY inflation data may be positive or negative
                // but must be greater than -1
                Utils.QL_REQUIRE(this.data_[i] > -1.0, () => "year-on-year inflation data < -100 %");

                // this can be negative
                this.times_[i] = timeFromReference(dates_[i]);

                Utils.QL_REQUIRE(!Utils.close(this.times_[i], this.times_[i - 1]), () =>
                                 "two dates correspond to the same time " +
                                 "under this curve's day count convention");
            }

            this.interpolation_ = this.interpolator_.interpolate(times_, times_.Count, data_);
            this.interpolation_.update();
        }
示例#3
0
 public Date initialDate(ZeroInflationTermStructure t)
 {
     if (t.indexIsInterpolated())
     {
         return(t.referenceDate() - t.observationLag());
     }
     else
     {
         return(Utils.inflationPeriod(t.referenceDate() - t.observationLag(),
                                      t.frequency()).Key);
     }
 }
示例#4
0
        public double zeroRate(Date d, Period instObsLag,
                               bool forceLinearInterpolation,
                               bool extrapolate)
        {
            Period useLag = instObsLag;

            if (instObsLag == new Period(-1, TimeUnit.Days))
            {
                useLag = observationLag();
            }

            double zeroRate;

            if (forceLinearInterpolation)
            {
                KeyValuePair <Date, Date> dd = Utils.inflationPeriod(d - useLag, frequency());
                Date   ddValue = dd.Value + new Period(1, TimeUnit.Days);
                double dp      = ddValue - dd.Key;
                double dt      = d - dd.Key;
                // if we are interpolating we only check the exact point
                // this prevents falling off the end at curve maturity
                base.checkRange(d, extrapolate);
                double t1 = timeFromReference(dd.Key);
                double t2 = timeFromReference(ddValue);
                zeroRate = zeroRateImpl(t1) + zeroRateImpl(t2) * (dt / dp);
            }
            else
            {
                if (indexIsInterpolated())
                {
                    base.checkRange(d - useLag, extrapolate);
                    double t = timeFromReference(d - useLag);
                    zeroRate = zeroRateImpl(t);
                }
                else
                {
                    KeyValuePair <Date, Date> dd = Utils.inflationPeriod(d - useLag, frequency());
                    base.checkRange(dd.Key, extrapolate);
                    double t = timeFromReference(dd.Key);
                    zeroRate = zeroRateImpl(t);
                }
            }

            if (hasSeasonality())
            {
                zeroRate = seasonality().correctZeroRate(d - useLag, zeroRate, this);
            }


            return(zeroRate);
        }
        public override Date maxDate()
        {
            Date d;

            if (indexIsInterpolated())
            {
                d = dates_.Last();
            }
            else
            {
                d = Utils.inflationPeriod(dates_.Last(), frequency()).Value;
            }
            return(d);
        }
示例#6
0
        public override void addFixing(Date fixingDate, double fixing, bool forceOverwrite)
        {
            KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
            int           n     = lim.Value - lim.Key + 1;
            List <Date>   dates = new List <Date>(n);
            List <double> rates = new List <double>(n);

            for (int i = 0; i < n; ++i)
            {
                dates.Add(lim.Key + i);
                rates.Add(fixing);
            }

            base.addFixings(dates, rates, forceOverwrite);
        }
示例#7
0
 public virtual Date baseDate()
 {
     // Depends on interpolation, or not, of observed index
     // and observation lag with which it was built.
     // We want this to work even if the index does not
     // have a term structure.
     if (indexIsInterpolated())
     {
         return(referenceDate() - observationLag());
     }
     else
     {
         return(Utils.inflationPeriod(referenceDate() - observationLag(),
                                      frequency()).Key);
     }
 }
示例#8
0
        private double forecastFixing(Date fixingDate)
        {
            Date d;

            if (interpolated())
            {
                d = fixingDate;
            }
            else
            {
                // if the value is not interpolated use the starting value
                // by internal convention this will be consistent
                KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
                d = lim.Key;
            }
            return(yoyInflation_.link.yoyRate(d, new Period(0, TimeUnit.Days)));
        }
示例#9
0
        private bool needsForecast(Date fixingDate)
        {
            // Stored fixings are always non-interpolated.
            // If an interpolated fixing is required then
            // the availability lag + one inflation period
            // must have passed to use historical fixings
            // (because you need the next one to interpolate).
            // The interpolation is calculated (linearly) on demand.

            Date today         = Settings.evaluationDate();
            Date todayMinusLag = today - availabilityLag_;

            Date historicalFixingKnown = Utils.inflationPeriod(todayMinusLag, frequency_).Key - 1;
            Date latestNeededDate      = fixingDate;

            if (interpolated_)
            {
                // might need the next one too
                KeyValuePair <Date, Date> p = Utils.inflationPeriod(fixingDate, frequency_);
                if (fixingDate > p.Key)
                {
                    latestNeededDate = latestNeededDate + new Period(frequency_);
                }
            }

            if (latestNeededDate <= historicalFixingKnown)
            {
                // the fixing date is well before the availability lag, so
                // we know that fixings were provided.
                return(false);
            }
            else if (latestNeededDate > today)
            {
                // the fixing can't be available, no matter what's in the
                // time series
                return(true);
            }
            else
            {
                // we're not sure, but the fixing might be there so we
                // check.  Todo: check which fixings are not possible, to
                // avoid using fixings in the future
                return(!timeSeries().ContainsKey(latestNeededDate));
            }
        }
示例#10
0
文件: Seasonality.cs 项目: cub-/qlnet
      protected override double seasonalityCorrection(double rate,Date atDate,DayCounter dc,Date curveBaseDate,bool isZeroRate)
      {
         double indexFactor = this.seasonalityFactor(atDate);

         // Getting seasonality correction
         double f = 0;
         if (isZeroRate) 
         {
            KeyValuePair<Date,Date> lim = Utils.inflationPeriod(curveBaseDate, Frequency.Monthly);
            double timeFromCurveBase = dc.yearFraction(lim.Key, atDate);
            f = Math.Pow(indexFactor, 1/timeFromCurveBase);
         }
         else 
         {
            Utils.QL_FAIL("Seasonal Kerkhof model is not defined on YoY rates");
         }

         return (rate + 1)*f - 1;
      }
示例#11
0
        //! redefined to use baseFixing() and interpolation
        public override double amount()
        {
            double I0 = baseFixing();
            double I1;

            // what interpolation do we use? Index / flat / linear
            if (interpolation() == InterpolationType.AsIndex)
            {
                I1 = index().fixing(fixingDate());
            }
            else
            {
                // work out what it should be
                //std::cout << fixingDate() << " and " << frequency() << std::endl;
                //std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency());
                //std::cout << fixingDate() << " and " << dd.first << " " << dd.second << std::endl;
                // work out what it should be
                KeyValuePair <Date, Date> dd = Utils.inflationPeriod(fixingDate(), frequency());
                double indexStart            = index().fixing(dd.Key);
                if (interpolation() == InterpolationType.Linear)
                {
                    double indexEnd = index().fixing(dd.Value + new Period(1, TimeUnit.Days));
                    // linear interpolation
                    //std::cout << indexStart << " and " << indexEnd << std::endl;
                    I1 = indexStart + (indexEnd - indexStart) * (fixingDate() - dd.Key)
                         / ((dd.Value + new Period(1, TimeUnit.Days)) - dd.Key); // can't get to next period's value within current period
                }
                else
                {
                    // no interpolation, i.e. flat = constant, so use start-of-period value
                    I1 = indexStart;
                }
            }

            if (growthOnly())
            {
                return(notional() * (I1 / I0 - 1.0));
            }
            else
            {
                return(notional() * (I1 / I0));
            }
        }
示例#12
0
        /*! \warning the forecastTodaysFixing parameter (required by
         *           the Index interface) is currently ignored.
         */
        public override double fixing(Date aFixingDate, bool forecastTodaysFixing = false)
        {
            if (!needsForecast(aFixingDate))
            {
                KeyValuePair <Date, Date> lim = Utils.inflationPeriod(aFixingDate, frequency_);
                Utils.QL_REQUIRE(IndexManager.instance().getHistory(name()).value().ContainsKey(lim.Key), () =>
                                 "Missing " + name() + " fixing for " + lim.Key);

                double?pastFixing = IndexManager.instance().getHistory(name()).value()[lim.Key];
                double?theFixing  = pastFixing;
                if (interpolated_)
                {
                    // fixings stored on first day of every period
                    if (aFixingDate == lim.Key)
                    {
                        // we don't actually need the next fixing
                        theFixing = pastFixing;
                    }
                    else
                    {
                        Utils.QL_REQUIRE(IndexManager.instance().getHistory(name()).value().ContainsKey(lim.Value + 1), () =>
                                         "Missing " + name() + " fixing for " + (lim.Value + 1));

                        double?pastFixing2 = IndexManager.instance().getHistory(name()).value()[lim.Value + 1];

                        // Use lagged period for interpolation
                        KeyValuePair <Date, Date> reference_period_lim = Utils.inflationPeriod(aFixingDate + zeroInflationTermStructure().link.observationLag(), frequency_);

                        // now linearly interpolate
                        double daysInPeriod = reference_period_lim.Value + 1 - reference_period_lim.Key;
                        theFixing = pastFixing + (pastFixing2 - pastFixing) * (aFixingDate - lim.Key) / daysInPeriod;
                    }
                }
                return(theFixing.GetValueOrDefault());
            }
            else
            {
                return(forecastFixing(aFixingDate));
            }
        }
        public double volatility(Date maturityDate, double strike, Period obsLag, bool extrapolate)
        {
            Period useLag = obsLag;

            if (obsLag == new Period(-1, TimeUnit.Days))
            {
                useLag = observationLag();
            }

            if (indexIsInterpolated())
            {
                checkRange(maturityDate - useLag, strike, extrapolate);
                double t = timeFromReference(maturityDate - useLag);
                return(volatilityImpl(t, strike));
            }
            else
            {
                KeyValuePair <Date, Date> dd = Utils.inflationPeriod(maturityDate - useLag, frequency());
                checkRange(dd.Key, strike, extrapolate);
                double t = timeFromReference(dd.Key);
                return(volatilityImpl(t, strike));
            }
        }
示例#14
0
        public override bool isConsistent(InflationTermStructure iTS)
        {
            // If multi-year is the specification consistent with the term structure start date?
            // We do NOT test daily seasonality because this will, in general, never be consistent
            // given weekends, holidays, leap years, etc.
            if (this.frequency() == Frequency.Daily)
            {
                return(true);
            }
            if ((int)this.frequency() == seasonalityFactors().Count)
            {
                return(true);
            }

            // how many years do you need to test?
            int nTest = seasonalityFactors().Count / (int)this.frequency();
            // ... relative to the start of the inflation curve
            KeyValuePair <Date, Date> lim = Utils.inflationPeriod(iTS.baseDate(), iTS.frequency());
            Date   curveBaseDate          = lim.Value;
            double factorBase             = this.seasonalityFactor(curveBaseDate);

            double eps = 0.00001;

            for (int i = 1; i < nTest; i++)
            {
                double factorAt = this.seasonalityFactor(curveBaseDate + new Period(i, TimeUnit.Years));
                if (Math.Abs(factorAt - factorBase) >= eps)
                {
                    throw new ApplicationException("seasonality is inconsistent with inflation " +
                                                   "term structure, factors " + factorBase + " and later factor "
                                                   + factorAt + ", " + i + " years later from inflation curve "
                                                   + " with base date at " + curveBaseDate);
                }
            }

            return(true);
        }
示例#15
0
        /*! \warning the forecastTodaysFixing parameter (required by
         *           the Index interface) is currently ignored.
         */
        public override double fixing(Date aFixingDate, bool forecastTodaysFixing)
        {
            if (!needsForecast(aFixingDate))
            {
                if (!IndexManager.instance().getHistory(name()).value().ContainsKey(aFixingDate))
                {
                    throw new ApplicationException("Missing " + name() + " fixing for " + aFixingDate);
                }

                double pastFixing = IndexManager.instance().getHistory(name()).value()[aFixingDate];
                double theFixing  = pastFixing;

                if (interpolated_)
                {
                    // fixings stored flat & for every day
                    Date fixingDate2 = aFixingDate + new Period(frequency_);
                    if (!IndexManager.instance().getHistory(name()).value().ContainsKey(fixingDate2))
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for " + fixingDate2);
                    }

                    double pastFixing2 = IndexManager.instance().getHistory(name()).value()[fixingDate2];

                    // now linearly interpolate
                    KeyValuePair <Date, Date> lim2 = Utils.inflationPeriod(aFixingDate, frequency_);
                    double daysInPeriod            = lim2.Value + 1 - lim2.Key;
                    theFixing = pastFixing
                                + (pastFixing2 - pastFixing) * (aFixingDate - lim2.Key) / daysInPeriod;
                }
                return(theFixing);
            }
            else
            {
                return(forecastFixing(aFixingDate));
            }
        }
        public override void calculate()
        {
            double npv = 0.0;

            // what is the difference between the observationLag of the surface
            // and the observationLag of the cap/floor?
            // TODO next line will fail if units are different
            Period lagDiff = arguments_.observationLag - priceSurf_.link.observationLag();

            // next line will fail if units are different if Period() is not well written
            Utils.QL_REQUIRE(lagDiff >= new Period(0, TimeUnit.Months), () => "InterpolatingCPICapFloorEngine: " +
                             "lag difference must be non-negative: " + lagDiff);

            // we now need an effective maturity to use in the price surface because this uses
            // maturity of calibration instruments as its time axis, N.B. this must also
            // use the roll because the surface does
            Date effectiveMaturity = arguments_.payDate - lagDiff;


            // what interpolation do we use? Index / flat / linear
            if (arguments_.observationInterpolation == InterpolationType.AsIndex)
            {
                // same as index means we can just use the price surface
                // since this uses the index
                if (arguments_.type == Option.Type.Call)
                {
                    npv = priceSurf_.link.capPrice(effectiveMaturity, arguments_.strike);
                }
                else
                {
                    npv = priceSurf_.link.floorPrice(effectiveMaturity, arguments_.strike);
                }
            }
            else
            {
                KeyValuePair <Date, Date> dd = Utils.inflationPeriod(effectiveMaturity, arguments_.infIndex.link.frequency());
                double priceStart            = 0.0;

                if (arguments_.type == Option.Type.Call)
                {
                    priceStart = priceSurf_.link.capPrice(dd.Key, arguments_.strike);
                }
                else
                {
                    priceStart = priceSurf_.link.floorPrice(dd.Key, arguments_.strike);
                }

                // if we use a flat index vs the interpolated one ...
                if (arguments_.observationInterpolation == InterpolationType.Flat)
                {
                    // then use the price for the first day in the period because the value cannot change after then
                    npv = priceStart;
                }
                else
                {
                    // linear interpolation will be very close
                    double priceEnd = 0.0;
                    if (arguments_.type == Option.Type.Call)
                    {
                        priceEnd = priceSurf_.link.capPrice((dd.Value + new Period(1, TimeUnit.Days)), arguments_.strike);
                    }
                    else
                    {
                        priceEnd = priceSurf_.link.floorPrice((dd.Value + new Period(1, TimeUnit.Days)), arguments_.strike);
                    }

                    npv = priceStart + (priceEnd - priceStart) * (effectiveMaturity - dd.Key)
                          / ((dd.Value + new Period(1, TimeUnit.Days)) - dd.Key); // can't get to next period'
                }
            }

            results_.value = npv;
        }
示例#17
0
        public double fixing(Date fixingDate, bool forecastTodaysFixing)
        {
            Date today                     = Settings.evaluationDate();
            Date todayMinusLag             = today - availabilityLag_;
            KeyValuePair <Date, Date> limm = Utils.inflationPeriod(todayMinusLag, frequency_);
            Date lastFix                   = limm.Key - 1;

            Date flatMustForecastOn   = lastFix + 1;
            Date interpMustForecastOn = lastFix + 1 - new Period(frequency_);


            if (interpolated() && fixingDate >= interpMustForecastOn)
            {
                return(forecastFixing(fixingDate));
            }

            if (!interpolated() && fixingDate >= flatMustForecastOn)
            {
                return(forecastFixing(fixingDate));
            }

            // four cases with ratio() and interpolated()
            if (ratio())
            {
                if (interpolated())
                {
                    // IS ratio, IS interpolated
                    KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
                    Date fixMinus1Y = new NullCalendar().advance(fixingDate, new Period(-1, TimeUnit.Years), BusinessDayConvention.ModifiedFollowing);
                    KeyValuePair <Date, Date> limBef = Utils.inflationPeriod(fixMinus1Y, frequency_);
                    double dp    = lim.Value + 1 - lim.Key;
                    double dpBef = limBef.Value + 1 - limBef.Key;
                    double dl    = fixingDate - lim.Key;
                    // potentially does not work on 29th Feb
                    double dlBef = fixMinus1Y - limBef.Key;
                    // get the four relevant fixings
                    // recall that they are stored flat for every day
                    double limFirstFix =
                        IndexManager.instance().getHistory(name()).value()[lim.Key];
                    if (limFirstFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + lim.Key);
                    }
                    double limSecondFix =
                        IndexManager.instance().getHistory(name()).value()[lim.Value + 1];
                    if (limSecondFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + lim.Value + 1);
                    }
                    double limBefFirstFix =
                        IndexManager.instance().getHistory(name()).value()[limBef.Key];
                    if (limBefFirstFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + limBef.Key);
                    }
                    double limBefSecondFix =
                        IndexManager.instance().getHistory(name()).value()[limBef.Value + 1];
                    if (limBefSecondFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + limBef.Value + 1);
                    }

                    double linearNow = limFirstFix + (limSecondFix - limFirstFix) * dl / dp;
                    double linearBef = limBefFirstFix + (limBefSecondFix - limBefFirstFix) * dlBef / dpBef;
                    double wasYES    = linearNow / linearBef - 1.0;

                    return(wasYES);
                }
                else
                {
                    // IS ratio, NOT interpolated
                    double pastFixing =
                        IndexManager.instance().getHistory(name()).value()[fixingDate];
                    if (pastFixing == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + fixingDate);
                    }
                    Date   previousDate   = fixingDate - new Period(1, TimeUnit.Years);
                    double previousFixing =
                        IndexManager.instance().getHistory(name()).value()[previousDate];
                    if (previousFixing == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + previousDate);
                    }

                    return(pastFixing / previousFixing - 1.0);
                }
            }
            else
            {
                // NOT ratio
                if (interpolated())
                {
                    // NOT ratio, IS interpolated
                    KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_);
                    double dp          = lim.Value + 1 - lim.Key;
                    double dl          = fixingDate - lim.Key;
                    double limFirstFix =
                        IndexManager.instance().getHistory(name()).value()[lim.Key];
                    if (limFirstFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + lim.Key);
                    }
                    double limSecondFix =
                        IndexManager.instance().getHistory(name()).value()[lim.Value + 1];
                    if (limSecondFix == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + lim.Value + 1);
                    }
                    double linearNow = limFirstFix + (limSecondFix - limFirstFix) * dl / dp;

                    return(linearNow);
                }
                else
                {
                    // NOT ratio, NOT interpolated
                    // so just flat
                    double pastFixing =
                        IndexManager.instance().getHistory(name()).value()[fixingDate];
                    if (pastFixing == null)
                    {
                        throw new ApplicationException("Missing " + name() + " fixing for "
                                                       + fixingDate);
                    }
                    return(pastFixing);
                }
            }

            // QL_FAIL("YoYInflationIndex::fixing, should never get here");
        }
示例#18
0
文件: Seasonality.cs 项目: cub-/qlnet
 public override double correctYoYRate(Date d, double r, InflationTermStructure iTS)
 {
    KeyValuePair<Date,Date> lim = Utils.inflationPeriod(iTS.baseDate(), iTS.frequency());
    Date curveBaseDate = lim.Value;
    return seasonalityCorrection(r, d, iTS.dayCounter(), curveBaseDate, false);
 }
示例#19
0
        //! The factor returned is NOT normalized relative to ANYTHING.
        public virtual double seasonalityFactor(Date to)
        {
            Date      from            = seasonalityBaseDate();
            Frequency factorFrequency = frequency();
            int       nFactors        = seasonalityFactors().Count;
            Period    factorPeriod    = new Period(factorFrequency);
            int       which           = 0;

            if (from == to)
            {
                which = 0;
            }
            else
            {
                // days, weeks, months, years are the only time unit possibilities
                int diffDays = Math.Abs(to - from); // in days
                int dir      = 1;
                if (from > to)
                {
                    dir = -1;
                }
                int diff = 0;
                if (factorPeriod.units() == TimeUnit.Days)
                {
                    diff = dir * diffDays;
                }
                else if (factorPeriod.units() == TimeUnit.Weeks)
                {
                    diff = dir * (diffDays / 7);
                }
                else if (factorPeriod.units() == TimeUnit.Months)
                {
                    KeyValuePair <Date, Date> lim = Utils.inflationPeriod(to, factorFrequency);
                    diff = diffDays / (31 * factorPeriod.length());
                    Date go = from + dir * diff * factorPeriod;
                    while (!(lim.Key <= go && go <= lim.Value))
                    {
                        go += dir * factorPeriod;
                        diff++;
                    }
                    diff = dir * diff;
                }
                else if (factorPeriod.units() == TimeUnit.Years)
                {
                    Utils.QL_FAIL("seasonality period time unit is not allowed to be : " + factorPeriod.units());
                }
                else
                {
                    Utils.QL_FAIL("Unknown time unit: " + factorPeriod.units());
                }
                // now adjust to the available number of factors, direction dependent

                if (dir == 1)
                {
                    which = diff % nFactors;
                }
                else
                {
                    which = (nFactors - (-diff % nFactors)) % nFactors;
                }
            }
            return(seasonalityFactors()[which]);
        }