OptimalDecisionAndValue(ICmdtyStorage <T> storage, T period, double inventory,
                                double nextStepInventorySpaceMin, double nextStepInventorySpaceMax, double cmdtyPrice,
                                Func <double, double> continuationValueByInventory, double discountFactorFromCmdtySettlement,
                                Func <Day, double> discountFactors, double numericalTolerance)
        {
            InjectWithdrawRange injectWithdrawRange = storage.GetInjectWithdrawRange(period, inventory);
            double inventoryLoss = storage.CmdtyInventoryPercentLoss(period) * inventory;

            double[] decisionSet = StorageHelper.CalculateBangBangDecisionSet(injectWithdrawRange, inventory, inventoryLoss,
                                                                              nextStepInventorySpaceMin, nextStepInventorySpaceMax, numericalTolerance);
            var valuesForDecision        = new double[decisionSet.Length];
            var cmdtyConsumedForDecision = new double[decisionSet.Length];
            var periodPvForDecision      = new double[decisionSet.Length];

            for (var j = 0; j < decisionSet.Length; j++)
            {
                double decisionInjectWithdraw = decisionSet[j];
                (valuesForDecision[j], cmdtyConsumedForDecision[j], periodPvForDecision[j]) = StorageValueForDecision(storage, period, inventory, inventoryLoss,
                                                                                                                      decisionInjectWithdraw, cmdtyPrice, continuationValueByInventory, discountFactorFromCmdtySettlement, discountFactors);
            }

            (double storageNpv, int indexOfOptimalDecision) = StorageHelper.MaxValueAndIndex(valuesForDecision);

            return(StorageNpv : storageNpv, OptimalInjectWithdraw : decisionSet[indexOfOptimalDecision],
                   CmdtyConsumedOnAction : cmdtyConsumedForDecision[indexOfOptimalDecision],
                   InventoryLoss : inventoryLoss, PeriodPv : periodPvForDecision[indexOfOptimalDecision]);
        }
示例#2
0
        public static TimeSeries <T, InventoryRange> CalculateInventorySpace <T>(ICmdtyStorage <T> storage, double startingInventory, T currentPeriod)
            where T : ITimePeriod <T>
        {
            if (currentPeriod.CompareTo(storage.EndPeriod) > 0)     // TODO should condition be >= 0?
            {
                throw new ArgumentException("Storage has expired"); // TODO change to return empty TimeSeries?
            }
            T startActiveStorage = storage.StartPeriod.CompareTo(currentPeriod) > 0 ? storage.StartPeriod : currentPeriod;

            int numPeriods = storage.EndPeriod.OffsetFrom(startActiveStorage);

            // Calculate the inventory space range going forward

            var forwardCalcMaxInventory = new double[numPeriods];
            var forwardCalcMinInventory = new double[numPeriods];

            double minInventoryForwardCalc = startingInventory;
            double maxInventoryForwardCalc = startingInventory;

            for (int i = 0; i < numPeriods; i++)
            {
                T      periodLoop           = startActiveStorage.Offset(i);
                T      nextPeriod           = periodLoop.Offset(1);
                double inventoryPercentLoss = storage.CmdtyInventoryPercentLoss(periodLoop);

                double injectWithdrawMin  = storage.GetInjectWithdrawRange(periodLoop, minInventoryForwardCalc).MinInjectWithdrawRate;
                double inventoryLossAtMin = inventoryPercentLoss * minInventoryForwardCalc;
                double storageMin         = storage.MinInventory(nextPeriod);
                minInventoryForwardCalc    = Math.Max(minInventoryForwardCalc - inventoryLossAtMin + injectWithdrawMin, storageMin);
                forwardCalcMinInventory[i] = minInventoryForwardCalc;

                double injectWithdrawMax  = storage.GetInjectWithdrawRange(periodLoop, maxInventoryForwardCalc).MaxInjectWithdrawRate;
                double inventoryLossAtMax = inventoryPercentLoss * maxInventoryForwardCalc;
                double storageMax         = storage.MaxInventory(nextPeriod);
                maxInventoryForwardCalc    = Math.Min(maxInventoryForwardCalc - inventoryLossAtMax + injectWithdrawMax, storageMax);
                forwardCalcMaxInventory[i] = maxInventoryForwardCalc;
            }

            // Calculate the inventory space range going backwards
            var backwardCalcMaxInventory = new double[numPeriods];

            var backwardCalcMinInventory = new double[numPeriods];

            T periodBackLoop = storage.EndPeriod;

            backwardCalcMaxInventory[numPeriods - 1] = storage.MustBeEmptyAtEnd ? 0 : storage.MaxInventory(storage.EndPeriod);
            backwardCalcMinInventory[numPeriods - 1] = storage.MustBeEmptyAtEnd ? 0 : storage.MinInventory(storage.EndPeriod);

            for (int i = numPeriods - 2; i >= 0; i--)
            {
                periodBackLoop = periodBackLoop.Offset(-1);
                backwardCalcMaxInventory[i] = storage.InventorySpaceUpperBound(periodBackLoop, backwardCalcMinInventory[i + 1], backwardCalcMaxInventory[i + 1]);
                backwardCalcMinInventory[i] = storage.InventorySpaceLowerBound(periodBackLoop, backwardCalcMinInventory[i + 1],
                                                                               backwardCalcMaxInventory[i + 1]);
            }

            // Calculate overall inventory space and check for consistency

            var inventoryRanges = new InventoryRange[numPeriods];

            for (int i = 0; i < numPeriods; i++)
            {
                double inventorySpaceMax = Math.Min(forwardCalcMaxInventory[i], backwardCalcMaxInventory[i]);
                double inventorySpaceMin = Math.Max(forwardCalcMinInventory[i], backwardCalcMinInventory[i]);
                if (inventorySpaceMin > inventorySpaceMax)
                {
                    throw new InventoryConstraintsCannotBeFulfilledException();
                }
                inventoryRanges[i] = new InventoryRange(inventorySpaceMin, inventorySpaceMax);
            }

            return(new TimeSeries <T, InventoryRange>(startActiveStorage.Offset(1), inventoryRanges));
        }