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); >
