Calcula a pior excursão adversa máxima combinada quando várias posições estão abertas ao mesmo tempo, ajudando você a quantificar drawdowns em estratégias de entrada em lote (basket).
Maior MAE - útil para analisar estratégias com ordens únicas ou múltiplas.
Múltiplas Ordens vs. Ordem Única
É mais eficiente distribuir o risco entre uma cesta de ordens do que em uma única ordem. Múltiplas posições de entrada oferecem mais flexibilidade sem exigir um posicionamento preciso de entrada única.
Ao abrir múltiplas ordens simultaneamente na mesma direção, calcule o MAE total de todas as posições abertas. Se as posições estiverem em direções opostas (hedging), subtraia o MFE das posições opostas do MAE maior para obter o MAE global.
Quatro Snippets
BiggestMAEallTrade - Maior MAE para um contrato, normalizado por contrato para obter resultados precisos mesmo se a estratégia aumentar o tamanho do contrato durante o backtest.
package SQ.Columns.Databanks;
import com.strategyquant.lib.L;
import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.SettingsMap;
import com.strategyquant.tradinglib.*;
import java.util.List;
import java.util.ArrayList;
import java.util.*;
import java.lang.*;
// Mode in France by Emmanuel Evrard for the StrategyQuantX Community :)
public class BiggestMAEallTrade extends DatabankColumn
{
public BiggestMAEallTrade()
{
super(L.tsq("Biggest Multiple MAE"), DatabankColumn.Decimal2PL, ValueTypes.Minimize, 0, 0, 100);
setWidth(140);
setTooltip(L.tsq("Biggest Multiple MAE - is the worst Maximum Adverse Excursion of all trades open at once"));
}
//------------------------------------------------------------------------
@Override
public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception
{
double worstMAE = -Double.MAX_VALUE;
Date date = new Date();
Date dateTemp = new Date();
double Total1 = 0;
double Size1 = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
Size1 = order.Size;
if (Size1 == 0) { Size1 = 0.01; }
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
if((Total1 / Size1) > worstMAE)
{
worstMAE = (Total1/order.Size);
}
}
return -1*worstMAE;
}
}
BiggestMAEallTradePct - Maior MAE como porcentagem do saldo da conta. Mostra se a estratégia utiliza um risco adequado em relação à conta (por exemplo, comparando com a perda máxima diária de 5%).
package SQ.Columns.Databanks;
import com.strategyquant.lib.L;
import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.SettingsMap;
import com.strategyquant.tradinglib.*;
import java.util.List;
import java.util.ArrayList;
import java.util.*;
import java.lang.*;
// Mode in France by Emmanuel Evrard for the StrategyQuantX Community :)
public class BiggestMAEallTradePct extends DatabankColumn
{
public BiggestMAEallTradePct()
{
super(L.tsq("Biggest Multiple MAE %"), DatabankColumn.Decimal2Pct, ValueTypes.Minimize, 0, 0, 100);
setWidth(70);
setTooltip(L.tsq("Biggest Multiple MAE % - is the worst Maximum Adverse Excursion of all trades open at once compared to AccountBalance"));
}
@Override
public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception
{
double worstMAE = -Double.MAX_VALUE;
Date date = new Date();
Date dateTemp = new Date();
double Total1 = 0;
double Total2 = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
Total2 = SQUtils.safeDivide(Total1, order.AccountBalance - order.PL) * 100.0;
if(Total2 > worstMAE)
{
worstMAE = Total2;
}
}
return -1*worstMAE;
}
}
BiggestMAEallHedgingTrade e BiggestMAEallHedgingTradePct - Para posições com hedge em direções opostas. Utiliza o MAE maior e subtrai o MFE da direção oposta. Funciona quando operações opostas estão abertas durante o MAE maior.
package SQ.Columns.Databanks;
import com.strategyquant.lib.L;
import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.SettingsMap;
import com.strategyquant.tradinglib.*;
import java.util.List;
import java.util.ArrayList;
import java.util.*;
import java.lang.*;
// Mode in France by Emmanuel Evrard for the StrategyQuantX Community :)
public class BiggestMAEallHedgingTrade extends DatabankColumn
{
public BiggestMAEallHedgingTrade()
{
super(L.tsq("Biggest Multiple Hedging MAE"), DatabankColumn.Decimal2PL, ValueTypes.Minimize, 0, 0, 100);
setWidth(140);
setTooltip(L.tsq("Biggest Multiple Hedging MAE - is the worst Maximum Adverse Excursion of all trades open at once"));
}
//------------------------------------------------------------------------
@Override
public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception
{
double worstMAE1= -Double.MAX_VALUE;
double worstMAE2 = -Double.MAX_VALUE;
double BesttMFE1= -Double.MAX_VALUE;
Date date = new Date();
Date dateTemp = new Date();
double Total1 = 0;
double Size1 = 0;
int direction = 0;
for(int i = 0; i < ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
Size1 = order.Size;
direction=order.getDirection();
if (direction == 1) // Bullish positions
{
if (Size1 == 0) { Size1 = 0.01; }
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
if((Total1 / Size1) > worstMAE1)
{
worstMAE1 = (Total1/order.Size);
}
}
}
Total1 = 0;
for(int i = 0; i < ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
Size1 = order.Size;
direction=order.getDirection();
if (direction == -1) // Bearish positions
{
if (Size1 == 0) { Size1 = 0.01; }
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
if((Total1 / Size1) > worstMAE2)
{
worstMAE2 = (Total1/order.Size);
}
}
}
if (worstMAE1 >= worstMAE2)
{
Total1 = 0;
for(int i = 0; i < ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
Size1 = order.Size;
direction=order.getDirection();
if (direction == -1) // Bearish positions
{
if (Size1 == 0) { Size1 = 0.01; }
if (dateTemp.equals(date))
{
Total1 += order.MFE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MFE;
}
if((Total1 / Size1) > BesttMFE1)
{
BesttMFE1 = (Total1/order.Size);
}
}
}
return -1*(worstMAE1 - BesttMFE1);
}
else if (worstMAE2 > worstMAE1)
{
Total1 = 0;
for(int i = 0; i < ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
Size1 = order.Size;
direction=order.getDirection();
if (direction == 1) // Bullish positions
{
if (Size1 == 0) { Size1 = 0.01; }
if (dateTemp.equals(date))
{
Total1 += order.MFE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MFE;
}
if((Total1 / Size1) > BesttMFE1)
{
BesttMFE1 = (Total1/order.Size);
}
}
}
return -1*(worstMAE2 - BesttMFE1);
}
return 0;
}
}
package SQ.Columns.Databanks;
import com.strategyquant.lib.L;
import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.SettingsMap;
import com.strategyquant.tradinglib.*;
import java.util.List;
import java.util.ArrayList;
import java.util.*;
import java.lang.*;
// Mode in France by Emmanuel Evrard for the StrategyQuantX Community :)
public class BiggestMAEallHedgingTradePct extends DatabankColumn
{
public BiggestMAEallHedgingTradePct()
{
super(L.tsq("Biggest Multiple Hedging MAE %"), DatabankColumn.Decimal2Pct, ValueTypes.Minimize, 0, 0, 100);
setWidth(100);
setTooltip(L.tsq("Biggest Multiple Hedging MAE % - is the worst Maximum Adverse Excursion of all trades open at once compared to AccountBalance"));
}
@Override
public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception
{
double worstMAE1= -Double.MAX_VALUE;
double worstMAE2 = -Double.MAX_VALUE;
double BesttMFE1= -Double.MAX_VALUE;
Date date = new Date();
Date dateTemp = new Date();
double Total1 = 0;
double Total2 = 0;
int direction = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
direction=order.getDirection();
if (direction == 1) // Bullish position
{
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
Total2 = SQUtils.safeDivide(Total1, order.AccountBalance - order.PL) * 100.0;
if(Total2 > worstMAE1)
{
worstMAE1 = Total2;
}
}
}
Total1 = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
direction=order.getDirection();
if (direction == -1) // Bearish position
{
if (dateTemp.equals(date))
{
Total1 += order.MAE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MAE;
}
Total2 = SQUtils.safeDivide(Total1, order.AccountBalance - order.PL) * 100.0;
if(Total2 > worstMAE2)
{
worstMAE2 = Total2;
}
}
}
if (worstMAE1 >= worstMAE2)
{
Total1 = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
direction=order.getDirection();
if (direction == -1) // Bearish position
{
if (dateTemp.equals(date))
{
Total1 += order.MFE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MFE;
}
Total2 = SQUtils.safeDivide(Total1, order.AccountBalance - order.PL) * 100.0;
if(Total2 > BesttMFE1)
{
BesttMFE1 = Total2;
}
}
}
return -1*(worstMAE1 - BesttMFE1);
}
else if (worstMAE2 > worstMAE1)
{
Total1 = 0;
for(int i = 0; i<ordersList.size(); i++)
{
Order order = ordersList.get(i);
dateTemp = new Date(order.CloseTime);
direction=order.getDirection();
if (direction == 1) // Bullish position
{
if (dateTemp.equals(date))
{
Total1 += order.MFE;
}
else
{
date = new Date(order.CloseTime);
Total1 = order.MFE;
}
Total2 = SQUtils.safeDivide(Total1, order.AccountBalance - order.PL) * 100.0;
if(Total2 > BesttMFE1)
{
BesttMFE1 = Total2;
}
}
}
return -1*(worstMAE2 - BesttMFE1);
}
return 0;
}
}