3
$\begingroup$

For stock data (closeData) going back from 2010 to 2022 it takes roughly .2 seconds for this code to run. That is way too slow for my purposes. Is there anyway I can speed this up using a C compiler or hardware acceleration?

closeData[1] = 
 TimeSeries[{#[[1]], QuantityMagnitude[#[[2]]]} & /@ (Normal[
     FinancialData[(index = {"NYSE:CPB"})[[1]], "AdjustedClose", 
      dates = {{2010, 1, 1}, {2022, 1, 1}}]])]

FinancialIndicator[{"ExponentialMovingAverage", 
    "DoubleExponentialMovingAverage", 
    "TripleExponentialMovingAverage", "TriangularMovingAverage", 
    "WeightedMovingAverage", "VariableMovingAverage"}[[4]], 
  25][closeData[1]]

$\endgroup$
2
  • 1
    $\begingroup$ Input data is insufficient for reproducing this. $\endgroup$ Commented Feb 10, 2022 at 6:58
  • $\begingroup$ Thanks for getting back to me. I added additional information which should allow you to reproduce this. I this calculation to run as fast as possible. Anyway to make it run much faster? The way my program works is that it feeds this time series of close price into these indicators over and over again, thousands of times. So the execution speed here is absolutely critical. $\endgroup$ Commented Feb 10, 2022 at 15:07

2 Answers 2

4
$\begingroup$

We can get substantial speed-up by avoiding two layers of overhead by

  1. using ts["Values"] instead of time series ts as input, and
  2. using the internal indicator function that is is called by FinancialIndicator[...] directly:

As an example, take the indicator "ExponentialMovingAverage":

ts = closeData[1];

values = ts["Values"];

res1 = FinancialIndicator["ExponentialMovingAverage", 25][ts]; // 
    RepeatedTiming // First
0.054
res2 = FinancialIndicator["ExponentialMovingAverage", 25][values]; // 
  RepeatedTiming // First
0.0054
res1["Values"] == res2
True

Next, we can discover the internal indicator function called by FinancialIndicator[indicator,...] using Charting`Indicators[indicator, "InternalIndicatorFunction"]:

iifEMA = Charting`Indicators["ExponentialMovingAverage", "InternalIndicatorFunction"]
Visualization`Utilities`FinancialUtilitiesDump`FIEMA

We get additional speed-up using the internal function directly:

res3 = iifEMA[values, {25}]; // RepeatedTiming // First
0.0034
res3 == res2
True

Further speed-up is obtained using ExponentialMovingAverage (which is called by iifEMA) with the proper second argument (2/(1 + 25) in this case):

res4 = ExponentialMovingAverage[values, 2/(1 + 25)]; //  RepeatedTiming // First
0.0006
res4 == res3
True

Finally, we can compile ExponentialMovingAverage:

cEMA = Compile[{{x, _Real, 1}, {n, _Integer}}, 
   ExponentialMovingAverage[x, 2/(1 + n)], RuntimeOptions -> "Speed"];

res5 = cEMA[values, 25]; // RepeatedTiming // First
0.00056
res5 == res4
True

Note: Further speed-up should be possible using Compile options such as CompilationTarget -> "C".

$\endgroup$
5
  • $\begingroup$ This was exactly what I was looking for thanks so much. I did your first suggestion and now it is ten times faster. I'm working on implementing the second and I do plan on implementing the 3rd. Once I implement all three I'm going to have to find a way to make the second part of my program run faster, it is a piecewise function whose input is trading signals and whose output is a list of the size of the portfolio over time. $\endgroup$ Commented Feb 10, 2022 at 19:33
  • 1
    $\begingroup$ @DanielBerkowitz, my pleasure; glad you found it useful. The third+ steps require looking into the code for the internal indicator functions. You can do this using, for example, GeneralUtilities`PrintDefinitions[Visualization`Utilities`FinancialUtilitiesDump`FIEMA]. Usually the relevant part of the code is not too long. If you are computing ensembles of indicators, it may be worthwhile to pay attention to blocks of computations shared by different indicators: $\endgroup$
    – kglr
    Commented Feb 10, 2022 at 19:52
  • 1
    $\begingroup$ ... For example, during computations of "TripleExponentialMovingAverage" and "DoubleExponentialMovingAverage" one needs to compute "ExponentialMovingAverage" , so you can avoid duplicate processing using the intermediate results for "TripleExponentialMovingAverage". $\endgroup$
    – kglr
    Commented Feb 10, 2022 at 19:56
  • $\begingroup$ I have some experience using C target compilation before. So I need to look how the internal indicator is implemented and rewrite it as a compile function then use C target compilation correct? $\endgroup$ Commented Feb 10, 2022 at 20:19
  • 1
    $\begingroup$ Yes. I don't have C compiler installed; but the examples in the docs suggest one may get significant speed up with CompilationTarget -> "C" , RuntimeAttributes -> {Listable} andParallelization -> True. $\endgroup$
    – kglr
    Commented Feb 10, 2022 at 20:45
0
$\begingroup$

I've noticed FinancialData in general takes some time to execute. However, you can parallel-process it for an initial improvement. Consider the following code for say 10 NASDAQ stocks, first run non-parallel then on a 4.5 GHZ quad core and the timing. Non-parallel took 13 seconds and on 4 cores, the time was 4.5 seconds:

$HistoryLength = 0;
nasdaqNames = FinancialData["NASDAQ:*", "Lookup"];

AbsoluteTiming[
 cData = Table[
    TimeSeries[{#[[1]], QuantityMagnitude[#[[2]]]} & /@ (Normal[
        FinancialData[(index = {nasdaqNames[[i]]})[[1]], 
         "AdjustedClose", dates = {{2010, 1, 1}, {2022, 1, 1}}]])],
    {i, 1, 10}
    ];
 ]

AbsoluteTiming[
 cData = ParallelTable[
    TimeSeries[{#[[1]], QuantityMagnitude[#[[2]]]} & /@ (Normal[
        FinancialData[(index = {nasdaqNames[[i]]})[[1]], 
         "AdjustedClose", dates = {{2010, 1, 1}, {2022, 1, 1}}]])],
    {i, 1, 10}
    ];
 ]

AbsoluteTiming[
 fData = ParallelTable[
    FinancialIndicator[{"ExponentialMovingAverage", 
        "DoubleExponentialMovingAverage", 
        "TripleExponentialMovingAverage", "TriangularMovingAverage", 
        "WeightedMovingAverage", "VariableMovingAverage"}[[4]], 25][
     cData[[i]]],
    {i, 1, 10}
    ];
 ]

{13.8392, Null}

{4.47188, Null}

{0.237649, Null}
$\endgroup$
1
  • $\begingroup$ When I run my code I use ParallelTable. But it still isn't fast enough. I'm running this over and over again tens of thousands of times. Ideally I would like to run it a million times and have it be done in a day or two. I'm thinking of switching to C, but if I can stick with Mathematica that would be preferred. $\endgroup$ Commented Feb 10, 2022 at 17:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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