1
$\begingroup$

The following function

Angel[startingBankroll_, investmentSize_, successProbability_, investmentsToMake_, targetReturnMultiple_, numberOfSimulations_] := 
   With[{
    simulationData = NestList[If[(RandomReal[] < successProbability) && #  >= 0, (targetReturnMultiple) (investmentSize), Max[#-investmentSize, 0]] &, 
                                                                startingBankroll, 
                                                                      investmentsToMake],
    runs = Table[simulationData, {r, numberOfSimulations}],
         finalBankrolls = Map[Last, runs],
      frequencyOfBust = Count[finalBankrolls, u_ /; u <= 0] / Length[finalBankrolls],
      frequencyOfOutsizedReturn = Count[finalBankrolls, u_ /; u  >Max[(startingBankroll - (investmentsToMake)(investmentSize)),0]] / Length[finalBankrolls]
    },
    runs]
Angel[100000, 5000, 0.05, 10, 1000, 10]

yields an "infinite expression 1/0 encountered" error:

enter image description here

How does one get past this?

$\endgroup$
  • 2
    $\begingroup$ Presumably this is due to Length[finalBankrolls] being 0. $\endgroup$ – Carl Lange May 23 at 23:28
4
$\begingroup$

Since this question is a follow-up, this answer will, for the most part, depend on my answer there.

Before getting down to the details of the answer, I would like to comment on a few issues with the code in this question.

With does not 'do' recursive definitions

Namely, one issue is that With does not support the kind of sequential assignments used as a first argument. What that means is that evaluating something like

With[{x = 3, y = x^2},
  3 y + 1
 ]

does not return 28 as it would have been probably expected, but 3 x^2 + 1 (assuming of course that x is a symbol that does not evaluate to anything else eg some numeric value, during the course of evaluation).

As it is the case that another answer to this question links to a really useful post about the different scope constructs in Mathematica, I'd like to provide a link to an answer that actually implements a recursive version of With in the sense expounded above.

Since this is a slightly more involved avenue to travel down to, I would like to propose a way to solve the problem in the question without having to actually implement such exotic solutions (exotic in the sense that the answer can be derived with simpler means not in the sense that the linked answer is useless or irrelevant, far from it).

Set and SetDelayed have different effects

Another issue with the code in the question is related to the final two attempts at recursive definitions in With. The assignment made to simulationData uses Set not SetDelayed. This means that once the assignment is made, the value of simulationData does not change in subsequent evaluations. This is not a problem per se but it creates a problem for the flow of the simulation because when the runs are calculated (outside the first argument of With this time) they will not contain different random sequences but the same random sequence that was evaluated the first time With was evaluated. I don't think this is the intended behavior of the code segment.

Minor conceptual and practical issues

Related to simulationData is a conceptual issue; the way NestList iterates the nested results, upon success it returns just the prize of the investment while upon failure it returns a figure that is conceptually (probably) expected to be the result from continuously investing (either a positive number or zero) but in actual fact that is not the case. Upon success, the result that gets passed to the next iteration of NestList is simply the increment in available funds instead of the sum of the available funds augmented by any possible gains from investing; therefore simulationData does not record 'wealth' (stock) but 'income' (flow), so to speak.

Finally, the assignment to finalBankrolls is redundant; it is possible to obtain the same effect when needed with less effort. This wouldn't have been a serious problem with the code, had the problem with the recursive definitions not been an issue to begin with.


Proposed solution

The proposed solution extends my answer in the previous question in such a way as to obtain the required statistics ie. frequencyOfBust and frequencyOfOutsizedReturn.

There are minor changes needed inside the code of simulation and the addition of a function that performs the desired calculations.

  1. The first line in the Module of simulation should read

    {distribution, repetitions, observations} = {args};
    

    This line is prepended in order to make the arguments of randomSequence available in the rest of the code.

  2. label (just before legend) should now read

    label = Row[Riffle[Thread[header -> {args}], ", "]]
    
  3. The last line of simulation should be modified to

    Join[{Legended[ListLinePlot[series, Apply[Sequence][opts]], Placed[legend, Below]]}, 
      collectStats[series, capital, repetitions, observations]]
    

    where collectStats is the new function that will perform the required calculations.

Following is the implementation of collectStats:

collectStats[randomMatrix_, args___] := Module[{capital, repetitions, observations, folder, combined, 
  bustFreq, xtrmRets, onBust, opts, label},

  (* retrieve relevant args *)
  {capital, repetitions, observations} = {args};

  (* frequency of bust and 'extreme' returns *)
  folder = With[{sum = #1 + #2}, If[sum >= 0, sum, -Infinity]] &;
  combined = With[{diff = #, maxDiff = Max[#]},
    {Fold[folder, capital, diff], {maxDiff, Count[#, maxDiff]}}
   ] & /@ Differences[randomMatrix, {0, 1}];

  (* bustFreq is a list with elements -Infinity or some positive figure *)
  (* xtrmRets is a list with elements {some figure, its frequency} *)
  {bustFreq, xtrmRets} = combined // Transpose;

  (* NumericQ[Infinity] -> False allows us to distinguish between normal occasions and busts *)

  onBust = If[NumericQ[#1], #2, {Null, 0.}] &;

  {
    label = StringTemplate["Absolute frequency of busts in `1` samples"];
    opts = {ChartLabels -> (Style[#, FontSize -> Large] & /@ {"normal", "bust"}), 
      PlotLabel -> label[repetitions], ImageSize -> Medium, ChartStyle -> 96};
    Framed@PieChart[{#[True], #[False]}, 
      Apply[Sequence][opts]] &@(Length /@     GroupBy[bustFreq, NumericQ]),

    label = StringTemplate["Absolute frequency of max returns in `1` observations\n(in normal situations)"];
    opts = {ChartLabels -> Range[repetitions], Frame -> True, ImageSize -> Medium, 
      PlotLabel -> label[observations], ChartStyle -> 24};
    BarChart[#2, Apply[Sequence][opts]] & @@ Transpose[Map[Apply[onBust], combined]],

    Iconize[combined, "data"]
   }

]

Evaluating

simulation[randomSequence[UniformDistribution[], 15, 10^3],
  10^4, 5 10^3, 10^3, 0.915] // Most /* (Riffle[#, Null] &) /* Row

returns

Blockquote

$\endgroup$
3
$\begingroup$

So you tried to refer to the local variables: With[{x = 1, y = x}, ...] which will not achieve what you want. I would recommend using the Module (See this post by Leonid Shifrin for a good overview of localizing mechanism in Mathematica). Here is the code:

Angel[startingBankroll_, investmentSize_, successProbability_, 
    investmentsToMake_, targetReturnMultiple_, numberOfSimulations_] := 
  Module[{simulationData, runs, finalBankrolls, frequencyOfBust, frequencyOfOutsizedReturn},
    simulationData := NestList[
        If[(RandomReal[] < successProbability) && # >= 0, 
           (targetReturnMultiple) (investmentSize), 
           Max[# - investmentSize, 0]
        ] &, startingBankroll, investmentsToMake]; 
    runs = Table[simulationData, {r, numberOfSimulations}];
    finalBankrolls = Map[Last, runs];
    frequencyOfBust = Count[finalBankrolls, u_ /; u <= 0]/Length[finalBankrolls];
    frequencyOfOutsizedReturn = Count[finalBankrolls, 
         u_ /; u > Max[(startingBankroll - (investmentsToMake) (investmentSize)), 0]
      ] / Length[finalBankrolls];
    runs
];

Also, another point is that if you want different simulation results at each run, simulationData should be defined by SetDelayed, i.e., :=.

$\endgroup$
  • $\begingroup$ This worked the first time I ran it, but subsequent runs cause the 1/0 errors to come back. I tried changing all remaining = to := to see if that would fix it but it didn't. I also tried changing the final runs at the end to i.e. frequencyOfOutsizedReturn and the same errors occurred. Very strange. $\endgroup$ – George May 24 at 4:30
  • $\begingroup$ Do you mind posting your full code? I tried on my machine with 12.0 and it works fine. $\endgroup$ – L.Yu May 24 at 14:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.