Using ATM Options for Signals

Hello - I am trying to trade shares of SPY by picking up on signals from ATM calls. I am having issues backtesting. It appears that using a ticker in the SetForeign function to get the pricing data of ATM Options is not dynamic. By this I mean, one ticker is set for the entire backtest and does not update with every bar or trade within the session. Although updated in Trace, in the backtest report the Open Price for ATM Option will always correspond to the price at that bar but of the same strike price as the first trade and not shift with the price of the underlier.

This is similar to this topic and this one, however, I am not trying to trade the option based on the underlier movement. Instead, I am trying to trade SPY based on the pricing info of the ATM option at that time so using a watchlist to filter has not been particularly helpful.

Any ideas on how to ensure the ticker within SetForeign updates? Thanks!

//Get Spy price
SpyPrice = Open;
SpyPricePrev = Ref(Open, -1);

RoundedSpyOpen = round(SpyPrice);
RoundedSpyOpenPrev = round(SpyPricePrev);

CallExpiryMonth = WriteIf( DateNum() < 1240722, "SPY2419G", WriteIf( DateNum() < 1240819, "SPY2416H", "SPY2420I")); 
PutExpiryMonth = WriteIf( DateNum() < 1240722, "SPY2419S", WriteIf( DateNum() < 1240819, "SPY2416T", "SPY2420U")); 

AtmCallStrike = 0;
AtmPutStrike = 0;
AtmCallTicker = "";
AtmPutTicker =  "";

AtmCallStrike = round(SpyPrice);
AtmPutStrike = round(SpyPrice);

//Get pricing info for real price of ATM Call Ticker
for( i = 0; i < BarCount; i++ )
{

	AtmCallTicker = ""+CallExpiryMonth+""+AtmCallStrike[i]+"";
	AtmPutTicker =  ""+PutExpiryMonth+""+AtmPutStrike+"";
	
	SetForeign(AtmCallTicker);
		
	ATMCallOpenPrice = Open;
	ATMCallOpenPricePrev = Ref(Open,-1);

}

Buy = 1;

@edmondmishaancohen, try to modify your loop, moving the logic to build the ATM ticker names inside the loop referring to the current Datenum item with the bracket operator :


// .... code before

ATMCallOpenPrice = Null;
ATMCallOpenPricePrev = Null;
//Get pricing info for real price of ATM Call Ticker
dn = DateNum();
for( i = 1; i < BarCount; i++ )
{
    CallExpiryMonth = WriteIf( dn[i] < 1240722, "SPY2419G", WriteIf( dn[i] < 1240819, "SPY2416H", "SPY2420I" ) );
    PutExpiryMonth  = WriteIf( dn[i] < 1240722, "SPY2419S", WriteIf( dn[i] < 1240819, "SPY2416T", "SPY2420U" ) );

    AtmCallTicker = CallExpiryMonth + "" + AtmCallStrike[i];
    AtmPutTicker =  PutExpiryMonth + "" + AtmPutStrike[i];

    _TRACEF( "Bar %g - DateNum: %s - Call Ticker: %s - Put Ticker: %s",
             i, DateTimeToStr( DateTimeConvert( 2, dn[i] ) ), AtmCallTicker, AtmPutTicker );

	// Assign the values to the 
    fO = Foreign( AtmCallTicker, "O");
    ATMCallOpenPrice = fO[i];
    ATMCallOpenPricePrev = fO[i-1];
}

// .... code after


Note that in the code I also modified the AtmPutTicker assignement: you forgot to use the [i] index to assign the AtmPutStrike price.
Moreover I also assign the ATMCallOpenPrice using the index.

1 Like

Thanks @Beppe! I am still getting issues. The pricing seems to correspond only to the options with strike 559 and does not change ticker as the price does. Here is a print out based on your code where you can see the ticker's strike price is coming out as "0". For context, I am backtesting hourly sessions (1030,1130,etc) on 8/21/2024

Blockquote
Bar 0 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 1 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 2 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 3 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 4 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 5 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0
Bar 6 - DateNum: 8/21/2024 - Call Ticker: SPY2420I0 - Put Ticker: SPY2420U0

I was running the posted example code using daily bars, but I don't have the actual options data, so I just relied on the _TRACEF() output.
In my case I see:
image

From you report all your bars seems to be of the same date.
Please, note that I also modified the posted code in the assignment of the ATMCallOpenPrice and ATMCallOpenPricePrev inside the loop (creating the 2 array variables as null before the loop; moreover the loop iterator now start at 1 instead of 0 since you need to refer to i-1 when assigning a value the ATMCallOpenPricePrev.

There may still be some errors, but I hope these tips can help you fix your code.

You are right, the tickers do update intraday in the trace output (see below). The issue is the price of the option does not correspond to the ticker (ie ATMCallOpenPrice is the price for Call560 throughout the session and does not update to Call559 or Call561 at the underlier price changes). Do you think this is an issue with SetForeign()? Thanks again for your help here

image

I think you need to change this:

ATMCallOpenPrice = fO[i];
ATMCallOpenPricePrev = fO[i-1];

To this:

ATMCallOpenPrice[i] = fO[i];
ATMCallOpenPricePrev[i] = fO[i-1];
1 Like

@mradtke thanks. I was posting this fix, but you were faster than me!

When I realized that the ATM pricing assignement in the loop was incorrectly using the array functions, I hastily edited my first posted response (the 15 minute deadline was near) directly on the forum and clearly made a mistake.

1 Like

Thank you both! That works and now the Open price updates correctly. One last issue I am having is for ATMCallOpenPricePrev. I get the following error using the code suggested here:

Error 10. Array subscript out of range. You must not access array elements outside 0..(BarCount-1) range. You attempted to access non-existing -1st element of array.

Here is the code:

//Get Spy price
SpyPrice = Open;
SpyPricePrev = Ref(Open, -1);

AtmCallStrike = 0;
AtmPutStrike = 0;
AtmCallTicker = "";
AtmPutTicker =  "";

AtmCallStrike = round(SpyPrice);
AtmPutStrike = round(SpyPrice);

//Get pricing info for real price of ATM Call Ticker
dn = DateNum();
for( i = 0; i < BarCount; i++ )
{
    CallExpiryMonth = WriteIf( dn[i] < 1240722, "SPY2419G", WriteIf( dn[i] < 1240819, "SPY2416H", "SPY2420I" ) );
    PutExpiryMonth  = WriteIf( dn[i] < 1240722, "SPY2419S", WriteIf( dn[i] < 1240819, "SPY2416T", "SPY2420U" ) );

    AtmCallTicker = CallExpiryMonth + "" + AtmCallStrike[i];
    AtmPutTicker =  PutExpiryMonth + "" + AtmPutStrike[i];

    _TRACEF( "Bar %g - DateNum: %s - Call Ticker: %s - Put Ticker: %s",
             i, DateTimeToStr( DateTimeConvert( 2, dn[i] ) ), AtmCallTicker, AtmPutTicker );

	// Assign the values to the 
    fO = Foreign( AtmCallTicker, "O");
    ATMCallOpenPrice[i] = fO[i];
	ATMCallOpenPricePrev[i] = fO[i-1];
	
	RestorePriceArrays();
}

@edmondmishaancohen, as I wrote before the loop needs to start from 1:

for( i = 1; i < BarCount; i++ )

Thanks again! @beppe

1 Like

@edmondmishaancohen you should really mark one of @beppe's responses as the solution, as he's the one that put all the time and effort into modifying your code to a working state and explaining what he'd done. I just identified a minor syntax error. :slight_smile: I do hope that you'll take the time to study what @beppe has taught you... the lessons are much more important than the finished code!

3 Likes