Puzzle: Your performance-based portfolio strategy dynamically allocates capital across 28 currency pairs based on their distance from a smoothed equity curve.
Question: If the EUR/USD component consistently outperforms all other pairs, its distance metric (dist) remains positive and high, while USD/JPY struggles with a negative distance.
The strategy caps any single component’s weight at 0.3 (30% of total capital). After several runs, the Total_Dist across all pairs is 200 and EUR/USD’s distance is 90. Can you calculate the actual capital allocation for EUR/USD, given that the total capital is $100,000?
A working code :-)
Code
// AlgoVars for component-specific parameters
#define dist AlgoVar[0]
#define component_weight AlgoVar[1]
// Global variables accessible by all components
var Total_Dist = 0;
var Max_Weight = 0.3;
void updateDist()
{
/* Calculate distance metric from equity curve */
var old_dist = dist; // Store component's previous dist value
vars EquityCurve = series(EquityLong + EquityShort); // Create component equity curve
vars EquityFilt = series(LowPass(EquityCurve, 100)); // Create component filtered equity curve
dist = (EquityCurve[0] - EquityFilt[0]) * PIP; // Calculate new dist value
if (dist <= 0)
{
if (old_dist > 0) Total_Dist = Total_Dist - old_dist;
}
else if (dist > 0)
{
if (old_dist <= 0) Total_Dist = Total_Dist + dist;
if (old_dist > 0) Total_Dist = Total_Dist - old_dist + dist;
}
// Plots
plot("Component_Eq", EquityCurve, NEW, BLUE);
plot("Filtered_Eq", EquityFilt, 0, RED);
}
void componentWeight()
{
if (dist <= 0)
{
Lots = 0.01; // Set the lot size to 0.01
Margin = 0.025 * Max_Weight * Capital;
component_weight = 0;
}
else if (dist > 0)
{
component_weight = ifelse(Total_Dist > 0, dist / Total_Dist, 0); // Prevent division by zero
if (component_weight > Max_Weight) component_weight = Max_Weight; // Limit max weight
Lots = 0.01; // Turn off phantom trading
Margin = 0.025 * component_weight * Capital; // Set margin according to weight
}
// Plots
plot("dist", dist, NEW | BARS, BLUE);
plot("Total_Dist", Total_Dist, NEW, RED);
plot("wgt", component_weight, NEW, BLACK);
}
void tradeRSI()
{
TimeFrame = 4;
vars PriceH4 = series(price());
vars Filter = series(LowPass(PriceH4, 200));
TimeFrame = 1;
vars PriceH1 = series(price());
vars rsi = series(RSI(PriceH1, 14));
int overbought = optimize(70, 60, 90, 5);
int oversold = optimize(30, 10, 40, 5);
Stop = 4 * ATR(100);
Trail = Stop;
TakeProfit = optimize(4, 1, 12, 1) * ATR(100);
if (crossOver(rsi, overbought) && PriceH1[0] < Filter[0] && NumOpenShort == 0)
{
enterShort();
}
if (crossUnder(rsi, oversold) && PriceH1[0] > Filter[0] && NumOpenLong == 0)
{
enterLong();
}
}
void tradeDigi()
{
vars Price = series(price());
vars filter = series(Roof(Price, 50, 100));
Stop = optimize(3, 1, 6, 0.5) * ATR(100);
Trail = 0.5 * Stop;
TrailLock = 10;
TrailSpeed = 200;
if (valley(filter))
{
MaxLong = 1;
enterLong();
}
if (peak(filter))
{
MaxShort = 1;
enterShort();
}
}
function run()
{
set(TESTNOW | PLOTNOW | PARAMETERS);
StartDate = 20231231;
EndDate = 2025;
NumWFOCycles = 10;
BarPeriod = 60;
LookBack = 150;
Capital = 1000;
// Full Asset List
string My_Assets[28];
My_Assets[0] = "EUR/USD"; My_Assets[1] = "GBP/USD"; My_Assets[2] = "USD/JPY";
My_Assets[3] = "USD/CHF"; My_Assets[4] = "USD/CAD"; My_Assets[5] = "AUD/USD";
My_Assets[6] = "NZD/USD"; My_Assets[7] = "EUR/GBP"; My_Assets[8] = "EUR/JPY";
My_Assets[9] = "EUR/CHF"; My_Assets[10] = "GBP/JPY"; My_Assets[11] = "GBP/CHF";
My_Assets[12] = "AUD/JPY"; My_Assets[13] = "AUD/CHF"; My_Assets[14] = "NZD/JPY";
My_Assets[15] = "NZD/CHF"; My_Assets[16] = "CAD/JPY"; My_Assets[17] = "CAD/CHF";
My_Assets[18] = "CHF/JPY";
My_Assets[19] = "EUR/AUD"; My_Assets[20] = "EUR/NZD"; My_Assets[21] = "EUR/CAD";
My_Assets[22] = "GBP/AUD"; My_Assets[23] = "GBP/NZD"; My_Assets[24] = "GBP/CAD";
My_Assets[25] = "AUD/NZD"; My_Assets[26] = "GBP/CHF"; My_Assets[27] ="NZD/CAD";
string My_Algos[2];
My_Algos[0] = "rsi";
My_Algos[1] = "digi";
// Update dist metric and Total_Dist for all components
int i, j;
for (i = 0; i < 28; i++)
{
for (j = 0; j < 2; j++)
{
asset(My_Assets[i]);
algo(My_Algos[j]);
updateDist();
}
}
// Update component weights and trade
while (asset(loop(
"EUR/USD", "GBP/USD", "USD/JPY", "USD/CHF", "USD/CAD", "AUD/USD", "NZD/USD",
"EUR/GBP", "EUR/JPY", "EUR/CHF", "GBP/JPY", "GBP/CHF", "AUD/JPY", "AUD/CHF", "GBP/CHF", "NZD/CAD",
"NZD/JPY", "NZD/CHF", "CAD/JPY", "CAD/CHF", "CHF/JPY",
"EUR/AUD", "EUR/NZD", "EUR/CAD", "GBP/AUD", "GBP/NZD", "GBP/CAD", "AUD/NZD")))
{
while (algo(loop("rsi", "digi")))
{
componentWeight();
if (Algo == "rsi") tradeRSI();
else if (Algo == "digi") tradeDigi();
}
}
PlotWidth = 600;
PlotHeight1 = 400;
}
(It is quite hard to develop strategies with higher complexities, when documentation of parameters and flags are limited in how they are to be used. It would be quite an advantage to extend the manual of Zorro Trader to include detailed information for the use of parameters.) While it is possible to use a dynamic threshold for a more sophisticated strategy :
Code
#define dist AlgoVar[0]
#define component_weight AlgoVar[1] // Each pair-algo has its own weight stored in AlgoVar
var Total_Dist = 0;
var Max_Weight = 0.3;
var MLsignals[8];
#define condition1 MLsignals[0]
#define condition2 MLsignals[1]
#define condition3 MLsignals[2]
#define condition4 MLsignals[3]
#define condition5 MLsignals[4]
#define component_weight_signal MLsignals[5]
#define dynamicThreshold_RSI MLsignals[6]
#define dynamicThreshold_Digi MLsignals[7]
var totalWeight = 0; // Global to store total weights
void updateDist() {
vars EquityCurve = series(EquityLong + EquityShort);
vars EquityFilt = series(LowPass(EquityCurve, 100));
dist = (EquityCurve[0] - EquityFilt[0]) * PIP;
vars rsiSeries = series(RSI(series(price()), 14));
vars atrSeries = series(ATR(100));
condition1 = rsiSeries[0];
condition2 = atrSeries[0];
condition3 = EquityCurve[0];
condition4 = EquityFilt[0];
condition5 = dist;
component_weight_signal = component_weight;
if (dist > 0) Total_Dist += dist;
}
void componentWeight() {
if (dist <= 0) {
component_weight = 0;
} else {
component_weight = ifelse(Total_Dist > 0, dist / Total_Dist, 0);
if (component_weight > Max_Weight) component_weight = Max_Weight;
var perceptronOutput = adviseLong(PERCEPTRON+RETURNS, 2, MLsignals, 8);
if (perceptronOutput > 0) {
Margin = 0.025 * component_weight * Capital * (1 + perceptronOutput / 100);
} else {
Margin = 0.025 * component_weight * Capital * (1 + perceptronOutput / 200);
}
}
totalWeight += component_weight; // Accumulate total weight during the loop
plot("dist", dist, NEW | BARS, BLUE);
plot("wgt", component_weight, NEW, BLACK);
}
void tradeRSI() {
vars PriceH4 = series(price());
vars Filter = series(LowPass(PriceH4, 200));
vars PriceH1 = series(price());
vars rsi = series(RSI(PriceH1, 14));
var Objective = priceClose(0) - priceClose(5);
if (adviseLong(DTREE+RETURNS, Objective, MLsignals, 5) > dynamicThreshold_RSI && PriceH1[0] > Filter[0])
enterLong();
if (adviseShort(DTREE+RETURNS, Objective, MLsignals, 5) > dynamicThreshold_RSI && PriceH1[0] < Filter[0])
enterShort();
}
void tradeDigi() {
vars Price = series(price());
vars filter = series(Roof(Price, 50, 100));
var Objective = priceClose(0) - priceClose(5);
if (valley(filter) && adviseLong(PATTERN+RETURNS, Objective, MLsignals, 5) > dynamicThreshold_Digi)
enterLong();
if (peak(filter) && adviseShort(PATTERN+RETURNS, Objective, MLsignals, 5) > dynamicThreshold_Digi)
enterShort();
}
function run() {
set(PARAMETERS | RULES | PLOTNOW | TESTNOW);
StartDate = 20231231;
EndDate = 2025;
NumWFOCycles = 10;
BarPeriod = 60;
LookBack = 150;
Capital = 1000;
while (asset(loop(
"EUR/USD", "GBP/USD", "USD/JPY", "USD/CHF", "USD/CAD", "AUD/USD", "NZD/USD",
"EUR/GBP", "EUR/JPY", "EUR/CHF", "GBP/JPY", "GBP/CHF", "AUD/JPY", "AUD/CHF", "GBP/CHF", "NZD/CAD",
"NZD/JPY", "NZD/CHF", "CAD/JPY", "CAD/CHF", "CHF/JPY",
"EUR/AUD", "EUR/NZD", "EUR/CAD", "GBP/AUD", "GBP/NZD", "GBP/CAD", "AUD/NZD")))
{
while (algo(loop("rsi","digi"))) {
updateDist();
componentWeight();
if (Algo == "rsi") tradeRSI();
else if (Algo == "digi") tradeDigi();
}
}
// Normalize weights after all pairs and algos are processed
while (asset(loop(
"EUR/USD", "GBP/USD", "USD/JPY", "USD/CHF", "USD/CAD", "AUD/USD", "NZD/USD",
"EUR/GBP", "EUR/JPY", "EUR/CHF", "GBP/JPY", "GBP/CHF", "AUD/JPY", "AUD/CHF", "GBP/CHF", "NZD/CAD",
"NZD/JPY", "NZD/CHF", "CAD/JPY", "CAD/CHF", "CHF/JPY",
"EUR/AUD", "EUR/NZD", "EUR/CAD", "GBP/AUD", "GBP/NZD", "GBP/CAD", "AUD/NZD")))
{
while (algo(loop("rsi","digi"))) {
component_weight = component_weight / totalWeight; // Normalize
plot(strf("Weight_%s_%s", Asset, Algo), component_weight, NEW, RED);
}
}
PlotWidth = 600;
PlotHeight1 = 400;
}