示例#1
0
        public void HWTTest2WeekSystemPriceOptim()
        {
            double[] y = { 31.0500, 30.4700, 28.9200, 27.8800, 26.9600, 27.8400, 28.7900, 28.6300, 28.4400, 28.3000, 30.6500, 31.5500, 32.1600, 32.4500, 32.6300, 33.6500, 34.9000, 36.2200, 36.6500, 36.3700, 35.4900, 34.4100, 34.6600, 32.5500, 33.1500, 32.6600, 31.8300, 31.4700, 32.5600, 34.3600, 36.2800, 38.3900, 39.0900, 38.3300, 38.4200, 38.2500, 37.9600, 37.8900, 37.8800, 38.7800, 39.8300, 39.9100, 39.3200, 38.4900, 37.4600, 36.9400, 36.3700, 34.5900, 33.1100, 32.2200, 31.4600, 31.6700, 32.0500, 33.6700, 34.9300, 35.8200, 36.3800, 36.5200, 36.7100, 36.6000, 36.5100, 36.4000, 36.4200, 36.5800, 36.9400, 36.9400, 36.8100, 36.4300, 35.9100, 35.4500, 34.7700, 32.0900, 31.7100, 30.8600, 30.2100, 30.3600, 30.8900, 32.2100, 34.3300, 35.9800, 36.7200, 36.6800, 36.8000, 36.6400, 36.4400, 36.3100, 35.8300, 36.1200, 36.8000, 37.2900, 37.1500, 36.4700, 36.0300, 35.4300, 35.1700, 32.9500, 33.6900, 32.7100, 32.0900, 32.1700, 32.7300, 33.7700, 34.0300, 33.9800, 34.4500, 36.1200, 36.6900, 36.7400, 36.5200, 36.1200, 36.1400, 36.7800, 37.7100, 38.3700, 37.9900, 36.9400, 36.2400, 35.8900, 35.7000, 35.4400, 34.8700, 33.0500, 32.2500, 32.4100, 32.4500, 32.8000, 33.2700, 32.9700, 33.6500, 34.2500, 35.0100, 35.9400, 35.7900, 35.0200, 34.8500, 35.1800, 35.9000, 37.0900, 37.6500, 37.3900, 36.9900, 36.1000, 36.0000, 34.9300, 34.9700, 33.7100, 33.1300, 33.2300, 34.1000, 35.6800, 38.5100, 40.6600, 42.4800, 42.0800, 41.9700, 41.3700, 40.3000, 39.9600, 40.1700, 41.6000, 42.3700, 41.9300, 39.9700, 38.9000, 37.9200, 37.4700, 36.4900, 35.5500 };
            int period = 24;
            int m = 3 * 24;

            double alpha = 0.5;
            double beta = 0.4;
            double gamma = 0.6;

            var abg = HoltWinters.OptimizeTripleHWT(y, period, m);

            double[] prediction = HoltWinters.TripleHWT(y, period, m, abg.alpha, abg.beta, abg.gamma);

            var tst = string.Join(" ", y.Select(x => x.ToString()));
            var tstpred = string.Join(" ", prediction.Select(x => x.ToString()));
            
            //Assert.AreEqual("Forecast does not match", expected,
            //        prediction, 0.0000000000001);
        }
示例#2
0
        public JsonResult Forecast(DateTime?date, string forecastMethod, string spikesPreprocessMethod, double spikesThreshold,
                                   string forecastModel, string timeHorizon, double confidence, string MathModel, string exogenousVariables)
        {
            var dt  = date.HasValue ? date.Value : DateTime.Today;
            var ths = GetUnionCaseNames <Types.TimeHorizon>();

            var forecastHorizon   = Array.IndexOf(ths, timeHorizon);
            var forecastingMethod = GetUnionCaseFromName <ForecastMethod>(forecastMethod);

            if (forecastHorizon < 0)
            {
                throw new ArgumentException("Wrong argument");
            }

            //naive hardcoded for now...
            //data to estimate on is not an input parameter...
            var d = AppData.GetHistoricalSeries(_timeSeries);

            var data = d.Where(x => x.DateTime < dt)
                       .Select(x => x.Value != null ? (double)x.Value : 0) //0 for now, when interpolation is done... etc..
                       .ToArray();

            var spikesPreprocess = GetUnionCaseFromName <SpikePreprocess>(spikesPreprocessMethod);

            if (spikesPreprocess.IsSimilarDay)
            {
                //First deseasonalize data at larger lags, all >= weekly, a stable series is need since spikes are at lengths <= day
                var desezonalizedData = Desezonalize(data, 168);

                //Then perform the spike estimation
                var spikeIndices = EstimateSpikesOnMovSDs(desezonalizedData, 24, 2, spikesThreshold);

                //go ahead and pre process the d series... yeah...
                data = ReplaceSingularSpikes(data, spikeIndices, spikesPreprocess, 0.95);
            }

            var horizon = GetTimeHorizonValue(forecastHorizon);

            var rlzd = d.Where(x => x.DateTime >= dt)
                       .Take(horizon)
                       .Select(x => x.Value != null ? (double)x.Value : 0) //0 for now, when interpolation is done... etc..
                       .ToArray();

            ForecastResult forecast;

            double[] forecasted;
            object   model;

            var oneYear  = (int)Math.Floor((dt - dt.AddMonths(-12)).TotalHours);
            var halfYear = (int)Math.Floor((dt - dt.AddMonths(-6)).TotalHours);

            var seasons = new int[] { oneYear, halfYear, 24 * 7 * 4 * 3, 24 * 7 * 4, 24 * 7, 24 };

            //we need always seasonalities >= forecast horizon
            var relevantSeasons = seasons.Where(s => s >= horizon).ToArray();

            double[] estimationData = null;

            //add naive with exogenous variables next...
            if (forecastingMethod == ForecastMethod.Naive)
            {
                forecast = Naive(data, relevantSeasons, horizon, confidence);

                forecasted = forecast.Forecast.Take(rlzd.Length).ToArray();

                model = new object();
            }
            else if (forecastingMethod == ForecastMethod.HoltWinters)
            {
                estimationData = data.Reverse().Take(24 * 7 * 2).Reverse().ToArray();

                var hwparams = JsonConvert.DeserializeObject <HoltWinters.HoltWintersParams>(MathModel);

                //optimize if all seem to be wrong
                if (hwparams.alpha == 0 || hwparams.beta == 0 || hwparams.gamma == 0)
                {
                    hwparams = HoltWinters.OptimizeTripleHWT(estimationData, 24, horizon);
                }

                forecast = HoltWinters.TripleHWTWithPIs(estimationData, 24, horizon, hwparams, confidence);

                forecasted = forecast.Forecast.Take(rlzd.Length).ToArray();

                model = hwparams;
            }
            else if (forecastingMethod == ForecastMethod.ARMA)
            {
                var exogenousVariablesJs = JObject.Parse(exogenousVariables); //alternative...?

                //always take next 2 highest seasons...
                var maSes = seasons.Where(s => s >= horizon).OrderBy(x => x).Take(2).ToArray();
                var arSes = new int[] { 1, 2, 24, 25 };

                //twice as the highest season estimation data
                var estimationDataLength = maSes.Last() * 2;
                estimationData = data.Reverse().Take(estimationDataLength).Reverse().ToArray();

                var isUnivariate = exogenousVariablesJs.Properties().Where(p => p.Value.ToString() == "True").Count() == 0;

                if (isUnivariate)
                {
                    var arma = JsonConvert.DeserializeObject <ARMAResult>(MathModel);

                    //problem: when changing param for ARMA, provide forecast, when changed date or horizon, re-estimate...yes...
                    //fix it later
                    if (arma.AR == null || arma.AR.Coefficients == null)
                    {
                    }

                    arma = ARMASimple2(estimationData, arSes, maSes);

                    var inSampleRes = Infer(arma, estimationData);

                    forecast = MarketModels.TimeSeries.Forecast(estimationData, inSampleRes, arma, horizon, confidence);

                    //when done out of sample, matches the realized and forecasted lengths to compute fit...
                    //if no available data, no fit can be done...
                    forecasted = forecast.Forecast.Take(rlzd.Length).ToArray();

                    //allow only AR and MA coefficients to be changed
                    //model = new { AR = arma.AR, MA = arma.MA };
                    model = null;
                }
                else
                {
                    var arxma = JsonConvert.DeserializeObject <ARXMAModel>(MathModel);

                    var vars = exogenousVariablesJs.Properties()
                               .Where(p => p.Value.ToString() == "True")
                               .Select(p => p.Name)
                               .ToList();

                    var specialDaysSelected = vars.Remove("SpecialWeekDays");

                    var exData = vars
                                 .Select(p =>
                                         AppData.GetHistoricalSeries(_exVarsPre + p + "_Hourly_All.json")
                                         .Where(x => x.DateTime < dt)
                                         .Select(x => x.Value != null ? (double)x.Value : 0) //0 for now, when interpolation is done... etc..
                                         .Reverse().Take(estimationDataLength + horizon).Reverse().ToArray()
                                         .ToList()
                                         ).ToList().ColumnsToRectangularArray();

                    //append indicators for special data...
                    if (specialDaysSelected)
                    {
                        //Friday, Saturday and Sunday
                        var ivars = MathFunctions.IndicatorVariablesMatrix(estimationDataLength + horizon, 168, 24, new int[] { 0, 5, 6 });

                        //concatenate matrices to columns
                        if (exData.GetLength(1) > 0)
                        {
                            exData = MathFunctions.concat2D(exData, ivars, false);
                        }
                        else
                        {
                            exData = ivars;
                        }
                    }

                    var exDataEst = MathFunctions.firstRows2D(exData, estimationDataLength);

                    arxma = ARXMASimple2(estimationData, exDataEst, arSes, maSes);

                    var inSampleRes = arxma.Infer(estimationData, exDataEst);

                    forecast = arxma.Forecast(estimationData, inSampleRes, exData, horizon, confidence);

                    //when done out of sample, matches the realized and forecasted lengths to compute fit...
                    //if no available data, no fit can be done...
                    forecasted = forecast.Forecast.Take(rlzd.Length).ToArray();

                    //allow only AR and MA coefficients to be changed
                    //model = new { AR = arma.AR, MA = arma.MA };
                    model = null;
                }
            }
            else
            {
                throw new ArgumentException("Unsupported argument passed");
            }

            var log = "";

            if (forecast.Forecast.Any(x => x > 500))
            {
                log += "Some data is wrong...";
            }

            if (forecast.Forecast.HasInvalidData() || forecast.Confidence.HasInvalidData())
            {
                throw new Exception("Abnormal results generated");
            }

            /* Post processing */
            //cap all values above a reasonable limit, e.g. 500
            CapSeries(forecast.Forecast, 500, -100);
            CapMultipleSeries(forecast.Confidence, 500, -100);

            FitStatistics eFit = null, eBFit = null, ePFit = null, fit = null, bfit = null, pfit = null;

            if (rlzd.Length > 0)
            {
                fit  = ForecastFit(forecasted, rlzd);
                pfit = ForecastFit(
                    GetSubPeriodsFrom(forecasted, 24, DAY_PEAK_HOURS),
                    GetSubPeriodsFrom(rlzd, 24, DAY_PEAK_HOURS));
                bfit = ForecastFit(
                    GetSubPeriodsFrom(forecasted, 24, DAY_BASE_HOURS),
                    GetSubPeriodsFrom(rlzd, 24, DAY_BASE_HOURS));
            }

            if (estimationData != null && forecast.Backcast.Length > 0)
            {
                //model with estimation, that is: the model is different than naive
                eFit  = ForecastFit(forecast.Backcast, estimationData);
                ePFit = ForecastFit(
                    GetSubPeriodsFrom(forecast.Backcast, 24, DAY_PEAK_HOURS),
                    GetSubPeriodsFrom(estimationData, 24, DAY_PEAK_HOURS));
                eBFit = ForecastFit(
                    GetSubPeriodsFrom(forecast.Backcast, 24, DAY_BASE_HOURS),
                    GetSubPeriodsFrom(estimationData, 24, DAY_BASE_HOURS));
            }
            var obj = new
            {
                Result    = forecast,
                MathModel = model,
                DaysAhead = horizon / 24,
                //in sample fit for all hours
                EstimationFit     = eFit,
                BaseEstimationFit = eBFit,
                PeakEstimationFit = ePFit,
                Fit     = fit,
                BaseFit = bfit, //refine later...
                PeakFit = pfit,
                //additional remarks about the resulting data...
                Log = log
            };

            return(Json(obj, JsonRequestBehavior.AllowGet));
        }