**Bob:** I got it! This will solve all my trading problems!

**Alice:** Problems? I thought you were already getting rich by automated trading?

**Bob:** It started well, but then it went all wrong. You know, from all the systems you programmed for me, the counter trend system worked best. And from all assets, I found it had the most profit with the EUR/USD. So I invested all my money in that system trading with EUR/USD.

**Alice:** Oh no!

**Bob:** Oh yes! And it worked like a charm! My account went up from $100,000 to $350,000 in six months. I naturally began to think about what do do with all that money. I had already ordered a Porsche.** **But then all of the sudden, your system started losing money. My account went down and down and didn't stop going down. It lost $200,000 in a single month. I'm now back at $150,000, and it's staying there since weeks.

**Alice:** Wins and losses follow a random sequence. Any system will eventually meet a loss streak of any length and depth - that's mathematically certain.

**Bob:** I know, it's a drawdown and I must just sit it out. But I don't have the nerve for that. Every morning I'm looking at the PC screen and my account is either going down, or not going up!

**Alice:** Put a blanket over your PC.

**Bob:** I got a better idea. That's why I was on the way to you. What if the system traded with many assets at the same time? When EUR/USD is in a drawdown, chances are that another pair, like USD/JPY, is still profitable. So I lose with one but win with the other. And I can do this with a portfolio of many assets.

**Alice:** That could work.

**Bob:** And can't you also combine your systems into one? I mean a super system that goes with the trend and against the trend at the same time? So I win when assets are trending and I also win when assets are cycling? Meaning I win all the time?

**Alice:** You probably won't win all the time because I have heard that different price curves are still somewhat correlated. But it can reduce your drawdowns.

**Bob:** Well, then program it. I'll sell my Porsche and pay you well.

Bob has given Alice the job to write a script that trades with several assets simultaneously, and uses both trend and counter-trend strategy algorithms. This is it (**Workshop****6.c**):

// Counter trend trading function from Workshop 5function tradeCounterTrend() { TimeFrame = 4;// 4 hour time framevars Price = series(price()); vars Filtered = series(BandPass(Price,optimize(30,25,35),0.5));

vars Signal = series(Fisher(Filtered,500));

var Threshold = optimize(1,0.5,2,0.1); Stop = optimize(4,2,10) * ATR(100);

Trail = 4*ATR(100);

if(crossUnder(Signal,-Threshold)) enterLong(); else if(crossOver(Signal,Threshold)) enterShort(); }// Trend trading function from Workshop 4function tradeTrend() { TimeFrame = 1;// 1 hour time framevars Price = series(price()); vars Trend = series(LowPass(Price,optimize(500,300,700)));

Stop = optimize(4,2,10) * ATR(100);

Trail = 0;

vars MMI_Raw = series(MMI(Price,300)); vars MMI_Smooth = series(LowPass(MMI_Raw,500)); if(falling(MMI_Smooth)) { if(valley(Trend)) enterLong(); else if(peak(Trend)) enterShort(); } } function run() { set(PARAMETERS+FACTORS+LOGFILE);BarPeriod = 60;LookBack = 2000;StartDate = 2005;NumWFOCycles = 10;Capital = 10000;

if(ReTrain) { UpdateDays = -1; SelectWFO = -1; reset(FACTORS); }// double portfolio loopwhile(asset(loop("EUR/USD","USD/JPY"))) while(algo(loop("TRND","CNTR"))) { Margin = 0.5 * OptimalF * Capital; if(Algo == "TRND") tradeTrend(); else if(Algo == "CNTR") tradeCounterTrend(); } }

The strategy is now divided into 3 different functions: **tradeTrend** for trend trading, **tradeCounterTrend** for counter trend trading, and the **run** function that sets up the parameters, selects the assets, sets up the trade volume, and calls the two trade functions.

The **tradeCounterTrend** function is almost the same as in the last workshop. At the begin of the function Alice has added the line

**TimeFrame = 4; **

The trend trading strategy from workshop 4 used 60-minutes bars, while counter trend trading was based on 240-minutes bars. When we trade both together in the same script, we'll need both types of bars. The **BarPeriod** variable must not be changed at runtime, as it determines the sampling of the price curves. But the **TimeFrame** variable does the job. Alice has set **BarPeriod** to 60 minutes, so it needs a **TimeFrame** of **4** bars for getting the 240 minutes period for the counter trend strategy. **TimeFrame** affects all subsequent **price** and **series** calls, all indicators using those series, and the **ATR** function.

The **tradeTrend** function uses the same algorithm as in workshop 4. The **TimeFrame** variable is set to **1**, so this strategy is still based on a 60-minutes bar period. The **LowPass** time period and the stop loss value are now optimized with the method explained in the last workshop.
Alice has also explicitly set the **Trail** variable to **0**:

**Trail = 0;**

Trend trading works best when profitable trades last a long time; trailing would stop them too early. Since the **Trail** variable was already set in the **tradeCounterTrend** function, it must now be reset to **0**, meaning no trailing. When predefined variables are used anywhere in the script, make sure that they have the right value at any place where they are needed. This is a typical source of mistakes, so always look into the trade log as described in workshop 4 to be sure that trades behave as they should.

The core of the strategy looks very unfamiliar:

**while(asset(loop("EUR/USD","USD/JPY")))
while(algo(loop("TRND","CNTR")))
{
Margin = 0.5 * OptimalF * Capital;
if(Algo == "TRND")
tradeTrend();
else if(Algo == "CNTR")
tradeCounterTrend();
}**

Let's first examine the two 'nested' **while** loops, and untangle them from inside out:

**loop("EUR/USD","USD/JPY")**

The loop function takes a number of arguments, and returns one of them every time it is called. On the first call it returns the first parameter, which is the string **"EUR/USD"**. We have learned in Workshop 1 that a **string** is a variable that contains text instead of numbers. Strings can be passed to functions and returned from functions just as numerical variables. If the **loop** function in the above line is called the next time, it returns **"USD/JPY"**. And on all further calls it returns **0**, as there are no further arguments.

**asset(loop("EUR/USD","USD/JPY"))**

The string returned by the **loop** function is now used as argument to the asset function. This function selects the traded asset, just as if it had been choosen with the [Asset] scrollbox. The string passed to **asset** must correspond to an asset subscribed with the broker. If asset is called with **0** instead of a string, it does nothing and returns **0**. Otherwise it returns a nonzero value. This is used in the outer **while** loop:

**while(asset(loop("EUR/USD","USD/JPY")))**

This loop is repeated as long as the **while** argument, which is the return value of the **asset** function, is nonzero (a nonzero comparison result is equivalent to 'true'). And **asset** returns nonzero (= true) when the **loop** function returns nonzero. Thus, the **while** loop is repeated exactly two times, once with **"EUR/USD"** and once with **"USD/JPY"** as the selected asset. If Alice had wanted to trade the same strategy with more assets - for instance, also with commodities or stock indices - she only needed to add more arguments to the **loop** function, like this:

**while(asset(loop("EUR/USD","USD/CHF","USD/JPY","AUD/USD","XAU/USD","USOil","SPX500","NAS100",...)))**

and the rest of the code would remain the same. However, Alice does not only want to trade with several assets, she also wants to trade different algorithms. For this, nested inside the first while loop is another while loop:

while(algo(loop("TRND","CNTR")))

The algo function basically copies the argument string into the predefined **Algo** string variable that is used for identifying a trade algorithm in the performance statistics. It also returns **0** (= false) when it gets **0**, so it can be used to control the second **while** loop, just as the **asset** function in the first while loop. Thus, for every repetition of the first loop, the second loop repeats 2 times, first with "TRND" and then with "CNTR". As these strings are now stored in the Algo variable, Alice uses them for calling the trade function in the inner code of the double loop:

**if(Algo == "TRND")
tradeTrend();
else if(Algo == "CNTR"))
tradeCounterTrend();**

The **if** condition checks if the **Algo** string is identical to a given string constant (**== "TRND"**) and returns nonzero, i.e. true, in that case. So when **Algo** was set to **"TRND"** by the algo function, **tradeTrend** is called; otherwise **tradeCounterTrend** is called. Due to the two nested loops, this inner code is now run four times per **run()** call:

- First with
**"EUR/USD"**and**"TRND"** - Then with
**"EUR/USD"**and**"CNTR"** - Then with
**"USD/JPY"**and**"TRND"** - And finally with
**"USD/JPY"**and**"CNTR"**

Those are the 4 asset/algorithm combinations - the **components** - of the strategy. If Alice had instead entered 10 assets and 5 algorithms in the loops, we had 50 (10*5) components and the inner code would be run fifty times every bar. Keep in mind that all parts of the script that are affected by the asset - for instance, retrieving prices or calling indicators - must be inside the asset loop. This is fulfilled here because anything asset related happens inside the two called trading functions.

The trade volume is controlled by this line:

**Margin = 0.5 * OptimalF * Capital; **

Alice uses here a money management method developed by **Ralph Vince**. He published a computer algorithm that evaluates every component's balance curve for calculating the optimal percentage of the gained capital to be reinvested. This percentage is called the **OptimalF** factor (you can see it also in the **OptF** column in the performance report). Multiply **OptimalF** with the capital, and you'll get the maximum margin to be allocated to a certain trade. Normally, people try to stay well below this maximum margin. **OptimalF** is specific to the strategy component and calculated from historical performance, meaning there's no guarantee that this performance will continue in the future. Exceeding the maximum margin is worse than staying below it, therefore it's recommended to only invest about 50% of **OptimalF**.

Margin is one of the methods for determining the amount invested per trade. Other methods were giving the number of lots or the money at risk. **Margin** is only a fixed percentage of the real trade volume - for instance 1% at 1:100 leverage - that the broker keeps as a deposit. If **Margin** is left at its default value, Zorro always buys 1 lot, the minimum allowed trade size. The higher the margin, the higher the number of lots and the higher the profit or loss. **Capital** is a variable set to our initial investment ($10,000) for determining its annual growth (CAGR). For determining the optimal margin for the strategy component, this **Capital** is multiplied with the 50% of the **OptimalF** factor.

For generating the OptimalF factors in the training run, Alice has set the **FACTORS** flag:

**set(PARAMETERS+FACTORS+LOGFILE);**

Now it's time to [Train] the strategy. Because we have now 4 components, 4x more bars, and twice as many cycles, the training process will take much longer than in the last workshop. As soon as it's finished, let's examine the resulting parameters. Use the SED editor to open the **Workshop6.par** file in the **Data** folder. This file contains the optimized parameters from the most recent WFO cycle. It could look like this:

EUR/USD:TRND +106 6.91 => 1.144 EUR/USD:CNTR 0.976 1.10 6.93 => 3.056 USD/JPY:TRND 386 6.93 => 3.089 USD/JPY:CNTR 1.73 1.09 5.75 => 1.899

As we can see, parameters for every asset/algo combination are optimized separately. Each line begins with the identifier for the component, consisting of asset and algorithm separated by a colon. Then follows the list of parameters. This allows Zorro to load the correct parameters at every **asset()** and **algo()** call. We can see that the **tradeTrend()** function uses two parameters and the **tradeCounterTrend()** function three, and their optimal values are slightly different for the **"EUR/USD"** and the **"USD/JPY"** assets. If a '+' sign appears in front of a parameter, its most robust value has been found at the start or end of its range. The last number behind the **"=>"** is not a parameter, but the result of the **objective** function of the final optimization run; it is for your information only and not used in the strategy.

The generated **OptimalF** factors can be found in the **Workshop6.fac** file:

EUR/USD:CNTR .079 1.61 155/95 43.3

EUR/USD:CNTR:L .022 1.12 62/48 3.9

EUR/USD:CNTR:S .105 2.05 93/47 39.4

EUR/USD:TRND .138 1.83 59/222 22.9

EUR/USD:TRND:L .021 1.10 25/113 1.2

EUR/USD:TRND:S .206 2.48 34/109 21.6

USD/JPY:CNTR .079 1.44 110/72 18.3

USD/JPY:CNTR:L .132 1.73 59/34 12.9

USD/JPY:CNTR:S .039 1.23 51/38 5.4

USD/JPY:TRND .119 1.73 64/250 15.6

USD/JPY:TRND:L .191 2.56 28/125 16.4

USD/JPY:TRND:S .000 0.93 36/125 -0.8

The factors are in the first column, separately for every component and separately for long (**":L"**) and short (**":S"**) trades (your own list might look different when you tested a different time period).

Finally, let's have a look at the performance of this portfolio strategy. Click [Test], then click [Result] (again, your equity curve can look different when testing a different time period).

You can see in the chart below that now two different algorithms trade simulaneously. The long green lines are from trend trading where positions are hold a relatively long time, the shorter lines are from counter-trend trading. The combined strategy does both at the same time. It generated about 40% annual CAGR in the walk forward test.

What happens when Alice reinvests the capital, following the square root rule? Modify this line (changes in red):

** Margin = 0.5 * OptimalF * Capital * sqrt(1 + ProfitClosed/Capital); **

ProfitClosed is the sum of the profits of all closed trades of the portfolio component. **1 + ProfitClosed/Capital** is our capital growth factor. Let's examine the strange formula with an example. Assume we started with $10000 capital and the component made $20000 profit. The inner term inside the parentheses is then **1 + $20000/$10000 = 3**. This is the growth of the capital: we started with $10000 and have now $3000 on our account, so it grew by factor 3. The square root of 3 is ~1.7, and this is the factor used for reinvesting our profits.

Since only the investent has changed, the system needs not be retrained. Clicking [Test] is enough. Reinvesting the capital this way increases the CAGR to about 50%.

- The TimeFrame variable allows different time frames for algorithms or indicators within the same strategy.
- The
**loop**function can be used to go through a list of assets or algorithms. - The
**asset**and**algo**functions select a certain asset and set up an algorithm identifier. - The
**OptimalF**factors give the maximum capital allocation to a certain strategy component. - Reinvest only the square root of your profit.