Multi-Criteria Optimization

 

Hello,

I've been messing around a bit with some ratios, trying to come up with a way to optimize multiple criteria.

And here's the result:

double OnTester()
  {
   // Calculate the lowest monthly return
   double min_monthly_return=0;
   if(ArraySize(returns)>0)
      min_monthly_return = returns[ArrayMinimum(returns)]/((TesterStatistics(STAT_EQUITY_DD)+maxmargin)+0.1);
   // Calculate the winning ratio, shifting down by 0.5 
   // then limiting it to (-0.25,0.25) 
   // to give it less weight in the overall ratio
   double winpercent = (TesterStatistics(STAT_PROFIT_TRADES)/(TesterStatistics(STAT_TRADES)+0.1));
   double winratio = (winpercent-0.5)/2;  
   // Calculate average trade duration 
   // scaling it down by raising it to the power of 0.25
   // substracting it from the maximum accepted duration of 30 hours
   double trade_duration = AvgTradeDuration();
   double duration = (MathPow(30,0.25)-MathPow(trade_duration,0.25))/2;   
   // Calculate return/dd then substracting the minimum accepted ratio of 2
   // Then scaling it down to give it less weight
   double return_to_dd=((TesterStatistics(STAT_PROFIT)/(TesterStatistics(STAT_EQUITY_DD)+0.01))-2)/3;
   // Calculate the Sharpe Ratio
   double sharpe=CalculateSharpe();
   // Stagnation is the duration of the longest drawdown period (in days)
   // Same as with the trade duration, it's scaled down and substracted from a maximum accepted 100 days
   double stag = (MathPow(100,0.25)-MathPow(stagnation,0.25))/2;

   //---- Apply the sigmoid function to all the values above
   //---- to Normalize them in a (0,1) range
   double min_month_score = MathExp(min_monthly_return)/(MathExp(min_monthly_return)+1);
   double winratio_score = MathExp(winratio)/(MathExp(winratio)+1);
   double duration_score = MathExp(duration)/(MathExp(duration)+1);
   double stag_score = MathExp(stag)/(MathExp(stag)+1);
   double sharpe_score = MathExp(sharpe)/(MathExp(sharpe)+1);
   double return_to_dd_score = MathExp(return_to_dd)/(MathExp(return_to_dd)+1);

   //---- Calculate the mean of the scores
   //-------------------------------------
   double mean = (min_month_score+winratio_score+duration_score+stag_score+sharpe_score+return_to_dd_score)/6;
   
   // Calculate the square differences for all the scores
   // In order to later calculate the standard deviation
   double min_month_sq_diff = MathPow((min_month_score-mean),2);
   double duration_sq_diff = MathPow((duration_score-mean),2);
   double stag_sq_diff = MathPow((stag_score-mean),2);
   double sharpe_sq_diff = MathPow((sharpe_score-mean),2);
   double winratio_sq_diff = MathPow((winratio_score-mean),2);
   double returndd_sq_diff = MathPow((return_to_dd_score-mean),2);
   
   // Only penalize downward deviation
   // below a required minimum of 0.6 for each score
   double sq_diff_mean=0;
   double minimum = 0.6;
   
   if(min_month_score<minimum)
      sq_diff_mean+=min_month_sq_diff;
   if(duration_score<minimum)
      sq_diff_mean+=duration_sq_diff;
   if(stag_score<minimum)
      sq_diff_mean+=stag_sq_diff;
   if(sharpe_score<minimum)
      sq_diff_mean+=sharpe_sq_diff;
   if(winratio_score<minimum)
      sq_diff_mean+=winratio_sq_diff;
   if(return_to_dd_score<minimum)
      sq_diff_mean+=returndd_sq_diff;

   sq_diff_mean/=6;
   
   // Calcualte the Standard Deviation
   double stdv = MathSqrt(sq_diff_mean);

   // Discard tests with few trades
   if(TesterStatistics(STAT_TRADES)<TestingMinTrades)
      return 0.3;
   // Otherwise, calculate the mean over the standard deviation
   // Tried to give less weight to standard deviation variations (4*stdv+1)
   // eg. a standard deviation of 0.04 would have been half a stdv of 0.08
   // making the results differ greatly, while the stdv difference is not that big
   // now the mean would be divided by 1.16 and 1.32 respectively making the difference a mere 13%
   else
      return (mean)/(4*stdv+1);
  }

I'm including 6 different criteria:

  1. Sharpe Ratio maximize
  2. Return/DD maximize
  3. Lowest Monthly Return maximize
  4. Winning Ratio maximize
  5. Stagnation (longest duration of a Drawdown period) minimize
  6. Avg Trade Duration (this is a personal preference) minimize

I apply different weights to different criteria (like for example I don't give much weight to the Winning Ratio, but still consider it), and I normalize all ratios to a (0,1) range using the Sigmoid function.

Now what I'm trying to achieve is to optimize the mean of the ratios, but also to have a low standard deviation:

for example a mean of 0.6 is better if the values were {0.58, 0.64, 0.57... the if they were {0.91, 0.18, 0.7....

How I tried to achieve that is by a dividing the mean by the standard deviation for the final OnTester result, but that was giving too much weight to the standard deviation, so I scaled it down.

But then also the ratio was penalizing positive peak scores, so I applied a "Sortino-like" idea, where then standard deviation is only calculated by taking into consideration downwards deviations below a defined minimum score of 0.6


Anyway, I would love to hear your thoughts for improving this thing, and maybe some different formulae to replace mean/standard deviation.

I have attached the full code so you could see all the referenced functions.

 
Naim El Hajj:

Hello,

I've been messing around a bit with some ratios, trying to come up with a way to optimize multiple criteria.

And here's the result:

I'm including 6 different criteria:

  1. Sharpe Ratio maximize
  2. Return/DD maximize
  3. Lowest Monthly Return maximize
  4. Winning Ratio maximize
  5. Stagnation (longest duration of a Drawdown period) minimize
  6. Avg Trade Duration (this is a personal preference) minimize

I apply different weights to different criteria (like for example I don't give much weight to the Winning Ratio, but still consider it), and I normalize all ratios to a (0,1) range using the Sigmoid function.

Now what I'm trying to achieve is to optimize the mean of the ratios, but also to have a low standard deviation:

for example a mean of 0.6 is better if the values were {0.58, 0.64, 0.57... the if they were {0.91, 0.18, 0.7....

How I tried to achieve that is by a dividing the mean by the standard deviation for the final OnTester result, but that was giving too much weight to the standard deviation, so I scaled it down.

But then also the ratio was penalizing positive peak scores, so I applied a "Sortino-like" idea, where then standard deviation is only calculated by taking into consideration downwards deviations below a defined minimum score of 0.6


Anyway, I would love to hear your thoughts for improving this thing, and maybe some different formulae to replace mean/standard deviation.

I have attached the full code so you could see all the referenced functions.

Very interesting post, it guided me in thinking in new ways to overcome the problem of weighting diferent criteria, thank you for sharing this.
Reason: