Oblicza najgorsze łączne maksymalne obsunięcie w trakcie pozycji (MAE), gdy otwartych jest jednocześnie wiele pozycji, pomagając oszacować obsunięcia kapitału w strategiach wejść koszykowych.
Biggest MAE — przydatne do analizowania strategii z pojedynczymi lub wieloma zleceniami.
Wiele zleceń a pojedyncze zlecenie
Bardziej efektywne jest rozłożenie ryzyka na koszyk zleceń niż opieranie się na pojedynczym zleceniu. Wiele pozycji wejściowych zapewnia większą elastyczność bez wymogu precyzyjnego pozycjonowania pojedynczego wejścia.
Otwierając wiele zleceń jednocześnie w tym samym kierunku, oblicz łączne MAE wszystkich otwartych pozycji. Jeśli pozycje są otwarte w przeciwnych kierunkach (hedging), odejmij MFE pozycji przeciwnych od większego MAE, aby uzyskać globalne MAE.
Cztery fragmenty kodu (Snippets)
BiggestMAEallTrade — największe MAE dla jednego kontraktu, znormalizowane na kontrakt w celu uzyskania dokładnych wyników, nawet jeśli strategia zwiększa wielkość pozycji podczas backtestingu.
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 — największe MAE jako procent salda konta. Pokazuje, czy strategia stosuje odpowiednie ryzyko w stosunku do rachunku (np. w porównaniu z maksymalną dzienną stratą na poziomie 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 & BiggestMAEallHedgingTradePct — dla pozycji zabezpieczających (hedging) w przeciwnych kierunkach. Pobiera większe MAE i odejmuje MFE z przeciwnego kierunku. Działa, gdy przeciwne transakcje są otwarte podczas występowania większego MAE.
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;
}
}