Skip to main content

Amibroker stands out as a top choice for building and backtesting trading systems with minimal coding required. It supports a range of instruments like equities, futures & options, commodities, and forex. This article will guide you through creating an options trading system in Amibroker.

As a bonus, we’re providing a free AFL code at the end of this post for backtesting options strategies.

Discover More: Mastering Amibroker – An In-depth Guide

Developing Options Trading Systems: The Difference

Creating an options trading system in Amibroker is more complex than standard stock-based systems. There are several reasons for this.

Firstly, options as instruments are inherently complex. Their pricing relies on multiple factors, not just market demand. These elements become variables in your trading system.

Moreover, an options symbol isn’t singular; it represents a variety of symbols with different strike prices and expiries, known as the options chain. Your system can utilize any symbol from this chain, depending on your chosen logic.

Step One: Importing Historical Data

The first critical step is importing historical options data into Amibroker for backtesting.

Refer to this comprehensive guide for step-by-step instructions: Historical Options Data: Free Download.

Follow the guide to organize symbols into watchlists, simplifying the backtesting process.

This guide focuses on Nifty and Banknifty options. If you’re looking to backtest other options and have the data, the same steps apply.

Understanding the Trading System Logic

Let’s explore a popular yet straightforward strategy known as the “Short Straddle.”

Here are the strategy details:

  • Entry Time: 09:20 AM
  • Exit Time: 15:05 PM
  • Entry Rules: Sell 1 lot ATM Call Option and 1 lot ATM Put Option at entry time
  • Exit Rules: Close all positions at the exit time
  • Stop Loss: 40% per leg
  • Expiry: Trade with near week expiry options

This strategy is commonly referred to as the 0920 straddle.

AFL Coding Insights

The AFL code is designed for backtesting rather than just visual representation in charts.

It backtests each option strike in the options chain. Here’s a glimpse of the AFL coding logic:

  • Identify “At the Money (ATM)” strikes at 9:20 AM by calculating the ATM strike price and comparing it with the option symbol’s numeric part.
  • Determine if it’s a weekly option contract, especially important in the last week of the month when monthly strikes act as weekly.

Positions are taken at 09:20 AM if conditions meet, and squared off at either a 40% stop loss or at 15:05 PM.

The AFL also embeds parameters like initial equity, commission, margin, position size, etc.

A Glimpse at the AFL

Here’s how the AFL looks when applied to the NIFTYWK17400CE symbol:

Short-Straddle-AFL-Screenshot

The downward arrow at 09:20 AM indicates a SHORT trade, and the star symbol at 15:05 PM shows a COVER trade.

Further Reading: Top Tool for Backtesting Trading Systems

How to Backtest the System

To backtest this system, follow these steps:

  • Step 1: Open File → New → Analysis
  • Step 2: Choose your AFL formula
  • Step 3: Select “All Symbols” or “Filter” for specific watchlists, then click “Backtest”

Results will appear in seconds. You can view detailed reports and Equity Curves.

Short-Straddle-Backtesting

Performance and Download

From January 1st, 2021, to February 17th, 2022, a capital of 200,000 grew to 320,425 – a 60% net profit and 51% CAGR. Impressive results!

Download the detailed report here.

Getting the AFL Code

 

				
					//------------------------------------------------------
//
//  Formula Name:    Short Straddle with each Leg SL
//  Website:         https://zerobrokerageclub.com/
//------------------------------------------------------

_SECTION_BEGIN( "Short Straddle with each Leg SL" );

//Custom Functions

function getStrikePrice( CurrentSymbol )  //Returns numeric portion from the String
{
    Input = CurrentSymbol;
    Output = "";
    Digits = "0,1,2,3,4,5,6,7,8,9"; // Or Digits = "0123456789";

    for( i = 0; ( X = StrMid( Input, i, 1 ) ) != ""; i++ )  if( StrFind( Digits, X ) ) Output += X;

    return Output;
}

function getWeeklyOptionPrice( BaseSymbol , CurrentSymbol , type )  //Returns corresponding weekly option price for the monthly strike
{

    printf( "\n" + BaseSymbol +" Monthly " + type + " Option" ); //Ex: Banknifty Monthly Put Option
    Strike = getStrikePrice( CurrentSymbol );
    WeeklyOption = BaseSymbol + "WK" + Strike + type;
    printf( "\nCorresponding Weekly Option:" + WeeklyOption );
    WeeklyOptionPrice = Foreign( WeeklyOption, "Close", False );
    printf( "\nCorresponding Weekly Option Price:" + WeeklyOptionPrice );
    return WeeklyOptionPrice;
}

//Constants

InitialEquity = 200000;
FixedCommissionAmount = 0;
Time1 = 092000;  //Time when 1st trade would be taken
Time2 = 150500;  //Time when all positions would be squared off;
BankniftyLotSize = 25;
NiftyLotSize = 50;
StrikeDifference = 50; //Difference in each strike price in the option chain
SLPcnt = 40; //Stop Loss % for each Leg


//Initial Parameters
SetTradeDelays( 0, 0, 0, 0 );
SetOption( "InitialEquity", InitialEquity );
SetOption( "FuturesMode" , True );
SetOption( "MinShares", 1 );
SetOption( "CommissionMode", 2 );
SetOption( "CommissionAmount", FixedCommissionAmount );
SetOption( "AccountMargin", 100 );
SetOption( "RefreshWhenCompleted", True );
SetOption( "AllowPositionShrinking", True );
EnableTextOutput( 0 );

SetChartOptions( 0, chartShowArrows | chartShowDates );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
Plot( C, "Close", ParamColor( "Color", colorDefault ), styleNoTitle | ParamStyle( "Style" ) | GetPriceStyle() );

NewDay = ( Day() != Ref( Day(), -1 ) ) OR BarIndex() == 0;
Plot( NewDay, "", colorlightGrey, styleHistogram | styleDots | styleNoLabel | styleOwnScale );

StartTime = TimeNum() == Time1;
printf( "\nStartTime : " + StartTime );

EndTime = TimeNum() == Time2;
printf( "\nEndTime : " + EndTime );

CurrentSymbol = Name();
printf( "\nCurrent Symbol : " + CurrentSymbol );

if( StrFind( CurrentSymbol, "BANK" ) )
{
    printf( "\nUnderlying is Banknifty" );
    Underlying = "BANKNIFTY";
    SetPositionSize( BankniftyLotSize, spsShares );
}
else
    if( StrFind( CurrentSymbol, "NIFTY" ) )
    {
        printf( "\nUnderlying is Nifty" );
        Underlying = "NIFTY";
        SetPositionSize( NiftyLotSize, spsShares );
    }
    else
    {
        Underlying = "UNDEFINED";
    }


WeeklyOptionPrice = NULL;

//For a monthly strike if the corresponding weekly strike option price is NULL then that means its an expiry week. 
//So monthly strike should be traded. These checks would be performed in the following blocks

if( StrFind( CurrentSymbol, "BANKNIFTY" ) AND StrFind( CurrentSymbol, "CE" ) AND not StrFind( CurrentSymbol, "WK" ) )
{
    WeeklyOptionPrice = getWeeklyOptionPrice(BaseSymbol = "BANKNIFTY", CurrentSymbol, type = "CE"); 
    
}

else
    if( StrFind( CurrentSymbol, "BANKNIFTY" ) AND StrFind( CurrentSymbol, "PE" ) AND not StrFind( CurrentSymbol, "WK" ) )
    {
        WeeklyOptionPrice = getWeeklyOptionPrice(BaseSymbol = "BANKNIFTY", CurrentSymbol, type = "PE");
    }

    else
        if( StrFind( CurrentSymbol, "NIFTY" ) AND StrFind( CurrentSymbol, "CE" ) AND not StrFind( CurrentSymbol, "WK" ) )
        {
            WeeklyOptionPrice = getWeeklyOptionPrice(BaseSymbol = "NIFTY", CurrentSymbol, type = "CE");
        }

        else
            if( StrFind( CurrentSymbol, "NIFTY" ) AND StrFind( CurrentSymbol, "PE" ) AND not StrFind( CurrentSymbol, "WK" ) )
            {
                WeeklyOptionPrice = getWeeklyOptionPrice(BaseSymbol = "NIFTY", CurrentSymbol, type = "PE");
            }


printf( "\nWeeklyOptionPrice : " + WeeklyOptionPrice );

IsWeeklyContract = IIf( IsNull( WeeklyOptionPrice ), true, false ); //If WeeklyOptionPrice is NULL then the symbol should be traded
printf( "\nIsWeeklyContract : " + IsWeeklyContract );


UnderlyingClosePrice = Foreign( Underlying, "Close" );
printf( "\nUnderlying Close Price : " + UnderlyingClosePrice );

UnderlyingOpenPrice = Foreign( Underlying, "Open" );
printf( "\nUnderlying Open Price : " + UnderlyingOpenPrice );

ATMStrikePrice = round( UnderlyingOpenPrice / StrikeDifference ) * StrikeDifference;
printf( "\nATM Strike Price : " + ATMStrikePrice );


for( i = 0; i < BarCount; i++ )
{
    ATMStrikePriceStr = NumToStr( ATMStrikePrice[i], 1.0, False );

    if( StrCount( CurrentSymbol, ATMStrikePriceStr ) == 1 )
    {
        //printf( "\nThis is a ATM Strike");
        ATMStrike[i] = 1;
    }
    else
    {
        //printf( "\nThis is not a ATM Strike");
        ATMStrike[i] = 0;
    }
}

printf( "\nATM Strike: " + ATMStrike );

Short = StartTime AND ATMStrike AND IsWeeklyContract;
Cover = EndTime AND IsWeeklyContract;

Short = ExRem( Short, Cover );
Cover = ExRem( Cover, Short );

ApplyStop( type = 0, mode = 1, amount = SLPcnt, exitatstop = 1 );

BuyPrice = Open;
SellPrice = Open;
ShortPrice = Open;
CoverPrice = Open;

printf( "\nShort : " + Short );
printf( "\nCover : " + Cover );

PlotShapes( IIf( Short, shapeSquare, shapeNone ), colorRed, 0, H, Offset = 25 );
PlotShapes( IIf( Short, shapeSquare, shapeNone ), colorOrange, 0, H, Offset = 35 );
PlotShapes( IIf( Short, shapeDownArrow, shapeNone ), colorWhite, 0, H, Offset = -30 );
PlotShapes( IIf( Cover, shapeStar, shapeNone ), colorGold, 0, L, Offset = -15 );


_SECTION_END();
				
			

Conclusion: Navigating Options Trading in Amibroker

We hope this guide helps you understand the process of building and backtesting an options trading system in Amibroker. It might seem complex at first, but it gets simpler as you delve deeper.

The system is adaptable, allowing you to modify the logic for different strategies. Amibroker’s quick backtesting is a significant advantage.

Questions? Suggestions? Share them in the comments. For in-depth training on Amibroker and system development, check out this resource.