Počítá nejhorší kombinovanou maximální nepříznivou odchylku (MAE), když je současně otevřeno více pozic, a pomáhá tak kvantifikovat drawdowny u strategií se vstupem po koších (basket entry).
Největší MAE – užitečné pro analýzu strategií s jedním nebo více příkazy.
Více příkazů vs. jeden příkaz
Efektivnější je rozložit riziko mezi koš příkazů než použít jediný příkaz. Vícenásobné vstupní pozice poskytují větší flexibilitu, aniž by vyžadovaly naprosto přesné načasování jediného vstupu.
Při otevírání více příkazů současně ve stejném směru spočítejte celkový MAE všech otevřených pozic. Pokud jsou pozice v opačných směrech (hedging), odečtěte MFE opačných pozic od většího MAE a získejte globální MAE.
Čtyři snippety
BiggestMAEallTrade – Největší MAE na jeden kontrakt, normalizovaný na kontrakt pro přesné výsledky, i když strategie během backtestování zvyšuje velikost kontraktu.
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 – Největší MAE jako procento ze zůstatku účtu. Ukazuje, zda strategie využívá přiměřené riziko vzhledem k účtu (např. při porovnání s maximální denní ztrátou 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 a BiggestMAEallHedgingTradePct – Pro hedgované pozice v opačných směrech. Vezme větší MAE a odečte MFE opačného směru. Funguje, když jsou během většího MAE otevřeny obchody v opačném směru.
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;
}
}