Hedge long portfolio with short SPY using CustomBacktestProcedure issue

Here is my attempt to build a hedge strategy using the CustomBacktestProcedure. It is supposed to offset my total long dollars with the appropriate amount of SPY short dollars.


/////////////  BACKTESTER OBJECT ////////////////////////////////////////////////
SetCustomBacktestProc( "" );
if ( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject(); 
    bo.PreProcess(); 

    for ( i = 0; i < BarCount; i++ )
    {
        bo.ProcessTradeSignals( i );
        
        OkToHedge = 1;
        HedgeThreshPct = 1;
		
		TotalLongInv = 0;
		TotalShortInv = 0;
		SPYshortShares = 0;
		Diff = 0;
        for ( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
        {
			if (openpos.IsLong)	
			{ 
				TotalLongInv += openpos.EntryPrice * openpos.Shares; 
			}
			else
			{ 
				TotalShortInv += openpos.EntryPrice * openpos.Shares; 
				SPYshortShares = openpos.Shares; 
			}
			Diff = TotalLongInv - TotalShortInv;
        }

        _TRACE("$TotalLongInv " + NumToStr(TotalLongInv, 1.0) + " $TotalShortInv: " + NumToStr(TotalShortInv, 1.0) + " $Diff: " + NumToStr(Diff, 1.0) );
        
        if (OkToHedge == 1)
        {      
			if(TotalLongInv > 0 AND TotalShortInv == 0)
			{
				spyC = Foreign("SPY","C");
				bo.EnterTrade( i, "SPY", 0, spyC[i], Diff/spyC[i]);
				_TRACE("i: " + NumToStr(i, 1.0) + " L>0,S=0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) );
				_TRACE("initially Shorting " + NumToStr(Diff/spyC[i]) + " SPY shares");
			}
			else if(TotalLongInv > 0 AND TotalShortInv > 0 AND TotalLongInv + (TotalLongInv * HedgeThreshPct * .01) > TotalShortInv)
			{
				spyC = Foreign("SPY","C");
				bo.ScaleTrade( i, "SPY", 0, spyC[i], abs(Diff)/spyC[i]);
				_TRACE("i: " + NumToStr(i, 1.0) + " L>0+%,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) );
				_TRACE("-Scaling " + NumToStr(Diff/spyC[i]) + " SPY shares");
			}
			else if(TotalLongInv > 0 AND TotalShortInv > 0 AND TotalLongInv - (TotalLongInv * HedgeThreshPct * .01) < TotalShortInv)
			{
				spyC = Foreign("SPY","C");
				bo.ScaleTrade( i, "SPY", 1, spyC[i], abs(Diff)/spyC[i]);
				_TRACE("i: " + NumToStr(i, 1.0) + " L>0-%,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) );
				_TRACE("+Scaling " + NumToStr(Diff/spyC[i]) + " SPY shares");
			}
			else if(TotalLongInv = 0 AND TotalShortInv > 0)
			{
				spyC = Foreign("SPY","C");
				bo.ExitTrade(i, "SPY", spyC[i], 1);
				_TRACE("i: " + NumToStr(i, 1.0) + " L=0,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) );
				_TRACE("Exiting all SPY shares");
			}
        }
    }    
    bo.PostProcess();    
}

But as the following trace log shows, it is not working properly. What am I doing wrong here?

image

Per this doc:
https://www.amibroker.com/guide/a_custombacktest.html

float PosSize
requested position size (positive numbers mean dollar value, negative values mean percent of portfolio equity)

so I had to change:
bo.EnterTrade( i, "SPY", 0, spyC[i], Diff/spyC[i]);
to:
bo.EnterTrade( i, "SPY", 0, spyC[i], Abs(Diff));

So here is it working now:

/////////////  BACKTESTER OBJECT ////////////////////////////////////////////////
dn = DateNum();
tn = TimeNum();
doTrace = 0;

SetCustomBacktestProc( "" );

if ( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject(); 
    bo.PreProcess(); 

    for ( i = 0; i < BarCount; i++ )
    {
        bo.ProcessTradeSignals( i );
        
 
        dn_tn = NumToStr(dn[i], 1.0, False, False) + "_" + NumToStr(tn[i], 1.0, False, False);
        dn_tn_i = "DT: " + dn_tn + " i: " + NumToStr(i,1.0, True, False) + " ||||| " ;
        
        OkToHedge = 1;
        HedgeThreshPct = 5;
		
		TotalLongInv = 0;
		TotalShortInv = 0;
		SPYshortShares = 0;
		Diff = 0;
        for ( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
        {
			if (openpos.IsLong)	
			{ 
				TotalLongInv += openpos.EntryPrice * openpos.Shares; 
			}
			else 
			{ 
				TotalShortInv += openpos.EntryPrice * openpos.Shares; 
				SPYshortShares = openpos.Shares; 
			}
			Diff = TotalLongInv - TotalShortInv;
        }

        if (doTrace) {  _TRACE(dn_tn_i + " $TotalLongInv " + NumToStr(TotalLongInv, 1.0) + " $TotalShortInv: " + NumToStr(TotalShortInv, 1.0) + " $Diff: " + NumToStr(Diff, 1.0) ); }
        
        if (i > 0 AND OkToHedge == 1)
        {      
			if(TotalLongInv > 0 AND TotalShortInv == 0)
			{
				spyC = Foreign("SPY","C");
				bo.EnterTrade( i, "SPY", 0, spyC[i], abs(Diff));
				if (doTrace) { _TRACE(dn_tn_i + " L>0,S=0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) ); }
				if (doTrace) { _TRACE(dn_tn_i + " initially Shorting " + NumToStr(abs(Diff)/spyC[i]) + " SPY shares"); }
			}
			else if(TotalLongInv == 0 AND TotalShortInv > 0)
			{
				spyC = Foreign("SPY","C");
				bo.ExitTrade(i, "SPY", spyC[i], 1);
				if (doTrace) { _TRACE(dn_tn_i + " L=0,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) ); }
				if (doTrace) { _TRACE(dn_tn_i + " Exiting all SPY shares"); }
			}
			else if(TotalLongInv > 0 AND TotalShortInv > 0 AND Diff > TotalLongInv * HedgeThreshPct * .01)
			{
				spyC = Foreign("SPY","C");
				bo.ScaleTrade( i, "SPY", 1, spyC[i], abs(Diff));
				if (doTrace) { _TRACE(dn_tn_i + " L>0+%,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) ); }
				if (doTrace) { _TRACE(dn_tn_i + " -Scaling " + NumToStr(Diff/spyC[i]) + " SPY shares"); }
			}
			else if(TotalLongInv > 0 AND TotalShortInv > 0 AND Diff < -TotalLongInv * HedgeThreshPct * .01)
			{
				spyC = Foreign("SPY","C");
				bo.ScaleTrade( i, "SPY", 0, spyC[i], abs(Diff));
				if (doTrace) { _TRACE(dn_tn_i + " L>0-%,S>0.  CurrSPYshares: " + NumToStr(-SPYshortShares) + " $Diff: " + NumToStr(Diff) + " SPY C: " + NumToStr(spyC[i]) ); }
				if (doTrace) { _TRACE(dn_tn_i + " +Scaling " + NumToStr(Diff/spyC[i]) + " SPY shares"); }
			}
        }
    }    
    bo.PostProcess();    
}
5 Likes

Hi vjsworld, thanks for sharing this. Have you been able able to use this in a rotational system? Thanks!