Wow, that is great.  It has been years since I have used Amibroker code!  What 
parameters can you optimize?  Can you access and use the greeks?   How far back 
do the option chains go?  Thanks!
Rick

--- In [email protected], David <amibroker.ygro...@...> wrote:
>
> I've figured out how to back test a four legged options trading strategy,
> specifically selling iron butterflies or iron condors.  The AmiBroker AFL
> code is below.
> 
> I welcome any questions, comments, suggestions, fixes, or improvements.
> 
> Warm regards,
> David
> 
> 
> // This option trading strategy is not intended to make money.
> // It is intended to demonstrate the use of AmiBroker (AB) for back testing
> option trading strategies.
> // This code builds option contract symbols from the underlyings,
> expirations, option types (puts or calls), and strike prices.
> 
> // The example option trading strategy contained herein is:
> // Every day based on end of day data,
> // sell to open 1 contract of an NDX front month iron butterfly or iron
> condor at the Mid.
> // On expiration, cash settle all remaining option positions.
> 
> // This code assumes Interactive Brokers (IB) symbology and data, and uses
> Close instead of Mid because IB doesn't supply end of day Mid or Bid/Ask
> data.
> // To produce results, you need to load IB historical end of day data for
> NDX-NASDAQ-IND and its put and call options,
> // such as NDX   100619P01975000-SMART-OPT where the underlying root is
> padded with spaces to 6 characters.
> 
> // The commented out Plot and _TRACE statements can be uncommented for
> debugging purposes.
> 
> // To do longer term back testing, you would need to obtain lengthier
> historical options data.  Some sources are listed below.
> // Please verify this information yourself.  It was gathered from their
> sales reps in May 2010.
> /*
> LiveVol:  trade and 1 min bid/ask, starting 1/04
> TickData:  tick bid/ask and 1 sec close, starting 7/04
> iQFeed:  trade bid/ask/last, starting 1 month ago; 1 min bid/ask/last,
> starting 5/07
> eSignal:  tick bid/ask starting up to 60 days ago, bar bid/ask starting up
> to 120 days ago, day bid/ask starting up to 1 year ago
> OptionVue:  30 min bid/ask, starting 1/2/01
> Ivy DB:  day bid/ask, starting 1/96
> iVolatility:  day bid/ask, starting 11/1/00
> HistoricalOptionData:  day bid/ask/last, starting 2/02
> Stricknet:  day bid/ask/last, starting 1/2/03
> */
> 
> SetFormulaName("Example of four legged option trading strategy - selling
> iron butterflies or iron condors");
> 
> /* Setting StrikesFromAtmToShort = 0 sells iron butterflies.  Setting
> StrikesFromAtmToShort > 0 sells iron condors.  */
> StrikesFromAtmToShort = Param("StrikesFromATMToShort", 0, 0, 10, 1, sincr =
> 0);
> StrikesFromShortToLong = Param("StrikesFromShortToLong", 1, 1, 10, 1, sincr
> = 0);
> DebugView = ParamToggle("DebugView", "OFF|ON", defaultval = 0); /* Controls
> all debug output */
> 
> RoundLotSize = 100;
> CommissionPerSharePerLeg = 0.01;
> Underlying = "NDX-NASDAQ-IND";
> 
> SetBarsRequired( sbrAll, sbrAll ); /* Uses all bars, NOT any specified
> subset.  Turns off QuickAFL to ensure proper behavior of BarIndex(). */
> SetBacktestMode( backtestRegularRawMulti );
> SetOption( "UseCustomBacktestProc", False );
> //SetOption( "PortfolioReportMode", 0 );
> //SetOption( "Generate", 1 );
> SetOption( "InitialEquity", 1000000 );
> SetOption( "AccountMargin", 100 );
> SetOption( "UsePrevBarEquityForPosSizing", True );
> SetOption( "MinPosValue", 0 );
> SetOption( "MaxOpenPositions", 1000 );
> SetOption( "MinShares", 100 );
> SetOption( "PriceBoundChecking", False );
> SetOption( "CommissionMode", 3 );
> SetOption( "CommissionAmount", 0 );
> SetOption( "InterestRate", 0);
> SetOption( "AllowPositionShrinking", False );
> SetOption( "DisableRuinStop", True );
> SetOption( "ActivateStopsImmediately", False );
> SetOption( "AllowSameBarExit", False );
> 
> SetOption( "NoDefaultColumns", False );
> SetOption( "FuturesMode", False );
> SetOption( "WorstRankHeld", 10 );
> SetOption( "ReverseSignalForcesExit", False );
> SetOption( "EveryBarNullCheck", False);
> SetOption( "HoldMinBars", 0 );
> SetOption( "EarlyExitBars", 0 );
> SetOption( "EarlyExitFee", 0 );
> SetOption( "HoldMinDays", 0 );
> SetOption( "EarlyExitDays", 0 );
> SetOption( "SeparateLongShortRank", True );
> SetOption( "MaxOpenLong", 0 );
> SetOption( "MaxOpenShort", 0 );
> SetOption( "RefreshWhenCompleted", True );
> SetOption( "RequireDeclarations", False );
> SetOption( "ExtraColumnsLocation", 0 );
> 
> function ConvertDateFromAmiBrokerToOPRA(DateNumber)
> {
> _ConvertDateFromAmiBrokerToOPRA = IIf(DateNumber > 1000000, DateNumber -
> 1000000, DateNumber);
> return _ConvertDateFromAmiBrokerToOPRA;
> }
> 
> function DaysInMonth(MonthNum, YearNum)
> {
> _DaysInMonth   = IIf(MonthNum == 1 OR MonthNum == 3 OR MonthNum == 5 OR
> MonthNum == 7 OR MonthNum == 8 OR MonthNum == 10 OR MonthNum == 12, 31, 30);
> DaysInMonthFeb = IIf((YearNum % 4 == 0 AND YearNum % 100 != 0) OR (YearNum %
> 4 == 0 AND YearNum % 400 == 0), 29, 28);
> _DaysInMonth   = IIf(MonthNum == 2, DaysInMonthFeb, _DaysInMonth);
> return _DaysInMonth;
> }
> 
> function DaysToThirdFriday()
> {
> Dy = Day();
> WeekDay = DayOfWeek();
> DaysToFriday       = IIf(5 - WeekDay < 0, (12 - WeekDay) % 7, (5 - WeekDay)
> % 7);
> ThirdFriday        = ((Dy + DaysToFriday) % 7) + 14;
> ThirdFriday        = IIf(ThirdFriday == 14, 21, ThirdFriday); /* Adjusts for
> the first of the month being a Saturday. */
> _DaysToThirdFriday = ThirdFriday - Dy;
> _DaysToThirdFriday = IIf(_DaysToThirdFriday >= 0,
>                          _DaysToThirdFriday,
>                          ThirdFriday + IIf(ThirdFriday + 14 >
> DaysInMonth(Month(), Year()), 28, 35) - Dy);
> return _DaysToThirdFriday;
> }
> //Plot(DaysToThirdFriday() * 100, "DaysToThirdFriday()", colorBlack,
> styleLine);
> //Plot((ConvertDateFromAmiBrokerToOPRA(DateNum()) + DaysToThirdFriday()) /
> 100, "Next 3rd Friday", colorRed, styleLine);
> 
> function FirstMonthExpiration()
> {
> _FirstMonthExpiration = ConvertDateFromAmiBrokerToOPRA(DateNum()) +
> DaysToThirdFriday() + 1; /* OPRA symbols use Saturday after third Friday as
> monthly expiration. */
> Yr       = floor( _FirstMonthExpiration / 10000 );
> MonthDay = _FirstMonthExpiration - Yr * 10000;
> Mon      = floor( MonthDay / 100 );
> Dy       = MonthDay - Mon * 100;
> MonthDay = IIf( MonthDay > Mon * 100 + DaysInMonth(Mon, Yr) AND MonthDay <
> (Mon + 1) * 100, (Mon + 1) * 100 + Dy - DaysInMonth(Mon, Yr), MonthDay);
> Yr       = IIf( MonthDay >= 1300, Yr + 1, Yr);
> MonthDay = IIf( MonthDay >= 1300, MonthDay - 1200, MonthDay);
> _FirstMonthExpiration = Yr * 10000 + MonthDay;
> return _FirstMonthExpiration;
> }
> //Plot(FirstMonthExpiration() / 100, "FirstMonthExpiration()", colorBlack,
> styleLine);
> 
> UnderlyingRoot = StrLeft(Underlying, StrFind(Underlying, "-") - 1);
> while(StrLen(UnderlyingRoot) < 6) UnderlyingRoot = UnderlyingRoot + " ";
> 
> switch(UnderlyingRoot)
> {
> case "NDX   ": StrikeIncrement = 25; SlippagePerSharePerLeg = 0.025;
> SettlementSymbol = "NDS-CBOE-IND"; break;
> case "SPX   ": StrikeIncrement =  5; SlippagePerSharePerLeg = 0.050;
> SettlementSymbol = "SET-CBOE-IND"; break;
> default:       StrikeIncrement =  0; SlippagePerSharePerLeg = 0.000;
> SettlementSymbol = "error";             break;
> };
> 
> Expiration = FirstMonthExpiration();
> //Plot(Expiration / 100, "Expiration ", colorOrange, styleLine);
> 
> SetForeign(Underlying, fixup = True, tradeprices = False);
> UnderlyingPrice = Close;
> // Could add code here to determine trend or other underlying-based
> indicators.
> RestorePriceArrays();
> //Plot (UnderlyingPrice, Underlying + " UnderlyingPrice", colorRed,
> styleLine);
> //if( DebugView ) _TRACE("# UnderlyingPrice = " + NumToStr(UnderlyingPrice,
> 1.2, separator = True) + " (" + typeof(UnderlyingPrice) + ")" );
> 
> AtmStrike = round( UnderlyingPrice / StrikeIncrement ) * StrikeIncrement;
> //Plot (AtmStrike ,"AtmStrike", colorBlack, styleLine);
> 
> UnderlyingToEvaluate  = StrMid(Name(),0,6);
> ExpirationToEvaluate  = IIf(UnderlyingToEvaluate == UnderlyingRoot,
> StrToNum(StrMid(Name(),6,6)), 0);          /* Calculate only if an option is
> being evaluated. */
> OptionTypeToEvaluate  = IIf(UnderlyingToEvaluate == UnderlyingRoot,
> IIf(StrMid(Name(),12,1) == "P", 1, 2), 0); /* Calculate only if an option is
> being evaluated. */  /* 1 = put.  2 = call.  */
> StrikeToEvaluate      = IIf(UnderlyingToEvaluate == UnderlyingRoot,
> StrToNum(StrMid(Name(),13,8)) / 1000, 0);  /* Calculate only if an option is
> being evaluated. */
> 
> OptionType = 1; /* 1 = put.  2 = call.  */
> ShortPutStrike = AtmStrike - StrikeIncrement * StrikesFromAtmToShort;
> 
> ShortPutFound =
>     IIf(UnderlyingToEvaluate  == UnderlyingRoot,     True, False)
> AND IIf(ExpirationToEvaluate  == Expiration,         True, False)
> AND IIf(OptionTypeToEvaluate  == OptionType,         True, False)
> AND IIf(StrikeToEvaluate      == ShortPutStrike,     True, False);
> //if( DebugView ) _TRACE("# UnderlyingToEvaluate  = " +
> UnderlyingToEvaluate  + " from " + Name() );
> //Plot(UnderlyingPrice, UnderlyingRoot, colorBlack, styleLine);
> //Plot(StrikeToEvaluate, "StrikeToEvaluate", colorGreen, styleLine);
> //Plot(ExpirationToEvaluate, "ExpirationToEvaluate", colorGreen, styleLine);
> //Plot(Expiration, "Expiration", colorRed, styleLine);
> //Plot(ShortPutFound, "ShortPutFound", colorBlack, styleDots);
> //Plot(OptionTypeToEvaluate, "OptionTypeToEvaluate", colorBlack, styleLine);
> //Plot(OptionType, "OptionType", colorBlack, styleLine);
> //Plot(ShortPutStrike, "ShortPutStrike", colorBlack, styleLine);
> 
> OptionType = 2; /* 1 = put.  2 = call.  */
> ShortCallStrike = AtmStrike + StrikeIncrement * StrikesFromAtmToShort;
> 
> ShortCallFound =
>     IIf(UnderlyingToEvaluate  == UnderlyingRoot,      True, False)
> AND IIf(ExpirationToEvaluate  == Expiration,          True, False)
> AND IIf(OptionTypeToEvaluate  == OptionType,          True, False)
> AND IIf(StrikeToEvaluate      == ShortCallStrike,     True, False);
> //Plot(ShortCallFound, "ShortCallFound", colorBlack, styleDots);
> //Plot(OptionTypeToEvaluate, "OptionTypeToEvaluate", colorBlack, styleLine);
> //Plot(OptionType, "OptionType", colorRed, styleLine);
> //Plot(ShortCallStrike, "ShortCallStrike", colorBlack, styleLine);
> //if( DebugView ) _TRACE("# UnderlyingRoot        = " + UnderlyingRoot);
> 
> ForceExpiration = False; /* True means force settlment at expiration
> behavior for testing purposes */
> 
> Short        = ShortPutFound OR ShortCallFound;
> //PlotColor = IIf( Short == 0, colorRed, colorGreen ); Plot(Short + 100,
> "Short", PlotColor, style = styleDots + styleNoLine);
> ShortPrice   = Close - SlippagePerSharePerLeg - CommissionPerSharePerLeg; /*
> Close should be Mid (average of Bid and Ask), but IB doesn't supply that
> data end of day. */
> //Plot(ShortPrice, "ShortPrice", colorBlack, styleLine);
> PositionSize = ShortPrice * 100 + 0.01; /* Perhaps due to AB's rounding, the
> additional penny is required to generate a trade every day. */
> 
> Cover =
>       ConvertDateFromAmiBrokerToOPRA(DateNum()) > ExpirationToEvaluate /*
> Cash settlement is calculated on the first trading day after Saturday
> expiration. */
> OR    ForceExpiration;
> //PlotColor = IIf( Cover == 0, colorRed, colorGreen ); Plot(Cover, "Cover",
> PlotColor, style = styleDots + styleNoLine);
> CoverSettlementPrice = Foreign(SettlementSymbol, "Close", fixup = 1);
> //Plot(CoverSettlementPrice, "CoverSettlementPrice", colorBlack, styleLine);
> CoverSettlement =
>    IIf(OptionTypeToEvaluate == 1,                    /* 1 = put.  2 = call.
> */
>        Max(StrikeToEvaluate - CoverSettlementPrice, 0),   /* put settlement
> */
>        Max(CoverSettlementPrice - StrikeToEvaluate, 0) ); /* call settlement
> */
> //Plot(CoverSettlement, "CoverSettlement", colorBlack, styleLine);
> CoverPrice =
>        CoverSettlement + CommissionPerSharePerLeg * sign(CoverSettlement);
> //Plot(CoverPrice, "CoverPrice", colorBlack, styleLine);
> 
> OptionType = 1; /* 1 = put.  2 = call.  */
> LongPutStrike = AtmStrike - StrikeIncrement * StrikesFromAtmToShort -
> StrikeIncrement * StrikesFromShortToLong;
> 
> LongPutFound =
>     IIf(UnderlyingToEvaluate  == UnderlyingRoot,     True, False)
> AND IIf(ExpirationToEvaluate  == Expiration,         True, False)
> AND IIf(OptionTypeToEvaluate  == OptionType,         True, False)
> AND IIf(StrikeToEvaluate      == LongPutStrike,      True, False);
> //if( DebugView ) _TRACE("# UnderlyingToEvaluate  = " +
> UnderlyingToEvaluate  + " from " + Name() );
> //Plot(UnderlyingPrice, UnderlyingRoot, colorBlack, styleLine);
> //Plot(StrikeToEvaluate, "StrikeToEvaluate", colorGreen, styleLine);
> //Plot(ExpirationToEvaluate, "ExpirationToEvaluate", colorGreen, styleLine);
> //Plot(Expiration, "Expiration", colorRed, styleLine);
> //Plot(LongPutFound, "LongPutFound", colorBlack, styleDots);
> //Plot(OptionTypeToEvaluate, "OptionTypeToEvaluate", colorBlack, styleLine);
> //Plot(OptionType, "OptionType", colorBlack, styleLine);
> //Plot(LongPutStrike, "LongPutStrike", colorBlack, styleLine);
> 
> OptionType = 2; /* 1 = put.  2 = call.  */
> LongCallStrike = AtmStrike + StrikeIncrement * StrikesFromAtmToShort +
> StrikeIncrement * StrikesFromShortToLong;
> 
> LongCallFound =
>     IIf(UnderlyingToEvaluate  == UnderlyingRoot,      True, False)
> AND IIf(ExpirationToEvaluate  == Expiration,          True, False)
> AND IIf(OptionTypeToEvaluate  == OptionType,          True, False)
> AND IIf(StrikeToEvaluate      == LongCallStrike,      True, False);
> //Plot(LongCallFound, "LongCallFound", colorBlack, styleDots);
> //Plot(OptionTypeToEvaluate, "OptionTypeToEvaluate", colorBlack, styleLine);
> //Plot(OptionType, "OptionType", colorRed, styleLine);
> //Plot(LongCallStrike, "LongCallStrike", colorBlack, styleLine);
> //if( DebugView ) _TRACE("# UnderlyingRoot        = " + UnderlyingRoot);
> 
> Buy        = LongPutFound OR LongCallFound;
> //PlotColor = IIf( Buy == 0, colorRed, colorGreen ); Plot(Buy + 100, "Buy",
> PlotColor, style = styleDots + styleNoLine);
> BuyPrice   = Close + SlippagePerSharePerLeg + CommissionPerSharePerLeg; /*
> Close should be Mid (average of Bid and Ask), but IB doesn't supply that
> data end of day. */
> //Plot(BuyPrice, "BuyPrice", colorBlack, styleLine);
> PositionSize = BuyPrice * 100 + 0.01; /* Perhaps due to AB's rounding, the
> additional penny is required to generate a trade every day. */
> 
> Sell =
>       ConvertDateFromAmiBrokerToOPRA(DateNum()) > ExpirationToEvaluate /*
> Cash settlement calculated first trading day after Saturday expiration. */
> OR    ForceExpiration;
> //PlotColor = IIf( Sell == 0, colorRed, colorGreen ); Plot(Sell, "Sell",
> PlotColor, style = styleDots + styleNoLine);
> SellSettlementPrice = Foreign(SettlementSymbol, "Close", fixup = 1);
> //Plot(SellSettlementPrice, "SellSettlementPrice", colorBlack, styleLine);
> SellSettlement =
>    IIf(OptionTypeToEvaluate == 1,                    /* 1 = put.  2 = call.
> */
>        Max(StrikeToEvaluate - SellSettlementPrice, 0),   /* put settlement
> */
>        Max(SellSettlementPrice - StrikeToEvaluate, 0) ); /* call settlement
> */
> //Plot(SellSettlement, "SellSettlement", colorBlack, styleLine);
> SellPrice =
>        SellSettlement - CommissionPerSharePerLeg * sign(SellSettlement);
> //Plot(SellPrice, "SellPrice", colorBlack, styleLine);
>


Reply via email to