/** * <summary>logOfColumn calculates the logarithm of each value in a specific column in the transition probability matrix.</summary> * * <param name="column">Column index of the transition probability matrix.</param> * <returns>A vector consisting of the logarithm of each value in the column in the transition probability matrix.</returns> */ private Vector LogOfColumn(int column) { var result = new Vector(0, 0); int i; for (i = 0; i < StateCount; i++) { result.Add(SafeLog(TransitionProbabilities.GetValue(i, column))); } return(result); }
public int CreateObservation(ICRFNodeData node, int label) { var index = TransitionProbabilities.ChooseTransition(label, Random); return(index); }
public static TimeSeries <T, IReadOnlyList <TreeNode> > CreateTree <T>([NotNull] TimeSeries <T, double> forwardCurve, double meanReversion, [NotNull] TimeSeries <T, double> spotVolatilityCurve, double onePeriodTimeDelta) where T : ITimePeriod <T> { if (forwardCurve == null) { throw new ArgumentNullException(nameof(forwardCurve)); } if (spotVolatilityCurve == null) { throw new ArgumentNullException(nameof(spotVolatilityCurve)); } if (meanReversion <= 0) { throw new ArgumentException("Mean reversion must be positive.", nameof(meanReversion)); } if (onePeriodTimeDelta <= 0) { throw new ArgumentException("Time delta must be positive.", nameof(onePeriodTimeDelta)); } if (forwardCurve.Count < 2) { throw new ArgumentException("Forward curve must contain at least 2 points.", nameof(forwardCurve)); } // TODO replace the two conditions below with call to new method on TimeSeries, e.g. indices are subset if (spotVolatilityCurve.IsEmpty) { throw new ArgumentException("Volatility curve is empty.", nameof(spotVolatilityCurve)); } if (spotVolatilityCurve.Start.CompareTo(forwardCurve.Start) > 0 || spotVolatilityCurve.End.CompareTo(forwardCurve.End) < 0) { throw new ArgumentException("Volatility curve does not contain a point for every point on the forward curve.", nameof(spotVolatilityCurve)); } int numPeriods = forwardCurve.Count; double expectedOuReturn = Math.Exp(-meanReversion * onePeriodTimeDelta) - 1; // Equal to M in Hull & White 1994, as calculated in end note 5, p16 double onePeriodOuVariance = (1 - Math.Exp(-2 * meanReversion * onePeriodTimeDelta)) / (2.0 * meanReversion); double treeSpacing = Math.Sqrt(3 * onePeriodOuVariance); // For jMax calc see Hull & White p12 int jMax = Convert.ToInt32(Math.Ceiling(-0.184 / expectedOuReturn)); int maxNumTreeLevels = jMax * 2 + 1; var middleNodeTypeTransitionProbabilities = new Dictionary <int, TransitionProbabilities>(); TransitionProbabilities bottomEdgeTransitionProbabilities = null; TransitionProbabilities topEdgeTransitionProbabilities = null; // Calculate the probability of reaching each node using forward induction var nodeProbabilities = new double[numPeriods][]; nodeProbabilities[0] = new[] { 1.0 }; // Current point as probability 1 for (int i = 0; i < numPeriods - 1; i++) // Loop forward through time { int currentStepNumLevels = Math.Min(i * 2 + 1, maxNumTreeLevels); int indexAdjustToJ = (currentStepNumLevels - 1) / 2; int nextStepNumLevels = Math.Min((i + 1) * 2 + 1, maxNumTreeLevels); nodeProbabilities[i + 1] = new double[nextStepNumLevels]; bool treeHasReachedWidestPoint = currentStepNumLevels == maxNumTreeLevels; for (int priceLevelIndex = 0; priceLevelIndex < currentStepNumLevels; priceLevelIndex++) // Loop through the tree price levels { double currentNodeProbability = nodeProbabilities[i][priceLevelIndex]; (int nextStepTopIndex, int nextStepMiddleIndex, int nextStepBottomIndex) = GetNextStepIndexPositions(priceLevelIndex, treeHasReachedWidestPoint, maxNumTreeLevels); int j = priceLevelIndex - indexAdjustToJ; TransitionProbabilities transitionProbabilities = GetTransitionProbabilities(j, priceLevelIndex, maxNumTreeLevels, treeHasReachedWidestPoint, expectedOuReturn, ref bottomEdgeTransitionProbabilities, middleNodeTypeTransitionProbabilities, ref topEdgeTransitionProbabilities); nodeProbabilities[i + 1][nextStepTopIndex] += currentNodeProbability * transitionProbabilities.TopProbability; nodeProbabilities[i + 1][nextStepMiddleIndex] += currentNodeProbability * transitionProbabilities.MiddleProbability; nodeProbabilities[i + 1][nextStepBottomIndex] += currentNodeProbability * transitionProbabilities.BottomProbability; } } // Populate results looping backward from end in order to populate transitions from the next time step var resultNodes = new TreeNode[numPeriods][]; for (int i = numPeriods - 1; i >= 0; i--) { int numPriceLevels = nodeProbabilities[i].Length; int indexAdjustToJ = (numPriceLevels - 1) / 2; // Calculate adjustment factor to ensure expected spot price equals current forward price // See Clewlow and Strickland equation 4.10 with discount factor cancelled out double expectedExponentialOfOu = 0; double spotVolatility = spotVolatilityCurve[i]; for (int priceLevelIndex = 0; priceLevelIndex < numPriceLevels; priceLevelIndex++) { int j = priceLevelIndex - indexAdjustToJ; double ouProcessValue = treeSpacing * j; expectedExponentialOfOu += nodeProbabilities[i][priceLevelIndex] * Math.Exp(spotVolatility * ouProcessValue); } double forwardPrice = forwardCurve[i]; double adjustmentTerm = Math.Log(forwardPrice / expectedExponentialOfOu); // Populate tree nodes resultNodes[i] = new TreeNode[numPriceLevels]; bool treeHasReachedWidestPoint = numPriceLevels == maxNumTreeLevels; for (int priceLevelIndex = 0; priceLevelIndex < numPriceLevels; priceLevelIndex++) // Loop through price levels { NodeTransition[] nodeTransitions; int j = priceLevelIndex - indexAdjustToJ; if (i == numPeriods - 1) { nodeTransitions = new NodeTransition[0]; } else { TransitionProbabilities transitionProbabilities = GetTransitionProbabilities(j, priceLevelIndex, maxNumTreeLevels, treeHasReachedWidestPoint, expectedOuReturn, ref bottomEdgeTransitionProbabilities, middleNodeTypeTransitionProbabilities, ref topEdgeTransitionProbabilities); (int nextPeriodTopNodeIndex, int nextPeriodMiddleNodeIndex, int nextPeriodBottomNodeIndex) = GetNextStepIndexPositions(priceLevelIndex, treeHasReachedWidestPoint, maxNumTreeLevels); var topTransition = new NodeTransition(transitionProbabilities.TopProbability, resultNodes[i + 1][nextPeriodTopNodeIndex]); var middleTransition = new NodeTransition(transitionProbabilities.MiddleProbability, resultNodes[i + 1][nextPeriodMiddleNodeIndex]); var bottomTransition = new NodeTransition(transitionProbabilities.BottomProbability, resultNodes[i + 1][nextPeriodBottomNodeIndex]); nodeTransitions = new[] { bottomTransition, middleTransition, topTransition }; } double ouProcessValue = treeSpacing * j; double nodeSpotPrice = Math.Exp(ouProcessValue * spotVolatility + adjustmentTerm); resultNodes[i][priceLevelIndex] = new TreeNode(nodeSpotPrice, nodeProbabilities[i][priceLevelIndex], priceLevelIndex, nodeTransitions); } } return(new TimeSeries <T, IReadOnlyList <TreeNode> >(forwardCurve.Indices, resultNodes)); }
private static TransitionProbabilities GetTransitionProbabilities( int j, int arrayIndex, int maxNumTreePriceLevels, bool treeHasReachedWidestPoint, double expectedOuReturn, ref TransitionProbabilities bottomEdgeTransitionProbabilities, Dictionary <int, TransitionProbabilities> middleNodeTypeTransitionProbabilities, ref TransitionProbabilities topEdgeTransitionProbabilities) { TransitionProbabilities transitionProbabilities; if (treeHasReachedWidestPoint && arrayIndex == 0) { // Bottom node if (bottomEdgeTransitionProbabilities != null) { transitionProbabilities = bottomEdgeTransitionProbabilities; } else { double jSquaredTimesMSquared = j * j * expectedOuReturn * expectedOuReturn; double jTimesM = j * expectedOuReturn; double probabilityUp = 1.0 / 6.0 + (jSquaredTimesMSquared - jTimesM) / 2.0; double probabilityMiddle = -1.0 / 3.0 - jSquaredTimesMSquared + 2 * jTimesM; double probabilityDown = 7.0 / 6.0 + (jSquaredTimesMSquared - 3.0 * jTimesM) / 2.0; transitionProbabilities = new TransitionProbabilities(probabilityUp, probabilityMiddle, probabilityDown); bottomEdgeTransitionProbabilities = transitionProbabilities; } } else if (treeHasReachedWidestPoint && arrayIndex == maxNumTreePriceLevels - 1) { // Top node if (topEdgeTransitionProbabilities != null) { transitionProbabilities = topEdgeTransitionProbabilities; } else { double jSquaredTimesMSquared = j * j * expectedOuReturn * expectedOuReturn; double jTimesM = j * expectedOuReturn; double probabilityUp = 7.0 / 6.0 + (jSquaredTimesMSquared + 3.0 * jTimesM) / 2.0; double probabilityMiddle = -1.0 / 3.0 - jSquaredTimesMSquared - 2.0 * jTimesM; double probabilityDown = 1.0 / 6.0 + (jSquaredTimesMSquared + jTimesM) / 2.0; transitionProbabilities = new TransitionProbabilities(probabilityUp, probabilityMiddle, probabilityDown); topEdgeTransitionProbabilities = transitionProbabilities; } } else { // Central node if (middleNodeTypeTransitionProbabilities.TryGetValue(j, out transitionProbabilities)) { return(transitionProbabilities); } double jSquaredTimesMSquared = j * j * expectedOuReturn * expectedOuReturn; double jTimesM = j * expectedOuReturn; double probabilityUp = 1.0 / 6.0 + (jSquaredTimesMSquared + jTimesM) / 2.0; double probabilityMiddle = 2.0 / 3.0 - jSquaredTimesMSquared; double probabilityDown = 1.0 / 6.0 + (jSquaredTimesMSquared - jTimesM) / 2.0; transitionProbabilities = new TransitionProbabilities(probabilityUp, probabilityMiddle, probabilityDown); middleNodeTypeTransitionProbabilities.Add(j, transitionProbabilities); } return(transitionProbabilities); }