Overall Statistics |
Total Orders 14 Average Win 0.07% Average Loss -0.02% Compounding Annual Return 1.939% Drawdown 0.000% Expectancy 1.409 Start Equity 100000 End Equity 100190 Net Profit 0.190% Sharpe Ratio -12.65 Sortino Ratio 0 Probabilistic Sharpe Ratio 99.681% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 3.82 Alpha -0.044 Beta 0.004 Annual Standard Deviation 0.003 Annual Variance 0 Information Ratio -5.945 Tracking Error 0.091 Treynor Ratio -9.688 Total Fees $0.00 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset SPXW 32MQGIJEGAZXQ|SPX 31 Portfolio Turnover 0.01% |
#region imports from AlgorithmImports import * #endregion # region imports from datetime import * TEN_DATES = \ { '20220110': [('TTWO', -10.546780072904001, "TECH")], '20220126': [('FFIV', -11.820566157185487, "TECH")], '20220202': [('CHRW', -10.175002390743037, "TRANSPORTATION_LOGISTICS")], '20220204': [('CLX',-12.598282327325514, "CONSUMER")], '20220210': [('CDAY', -10.77027527465746, "TECH"), ('IPG', -16.176470588235297, "COMM"), ('CTL', -13.338533541341649, "COMM")], '20220216': [('VIAC', -16.171158655181994, "COMM")], '20220217': [('ALB', -11.930894308943095, "MATERIALS")], '20220224': [('NTAP', -13.592579379236536, "TECH"), ('EPAM', -16.313989909858215, "TECH"), ('PCLN', -12.533251276403629, "CONSUMER")], '20220321': [('NLSN', -16.407528641571194, "COMM")], '20220419': [('XRAY', -13.731527093596057, "HEALTH")], '20220422': [('HCA', -10.436460807600954, "HEALTH")], '20220426': [('UHS', -12.460363217065446, "HEALTH")], '20220427': [('FFIV', -11.469238306430823, "TECH")], '20220428': [('CARR', -10.07230239128338, "MANUFACTURING")], '20220429': [('AMZN', -10.129221661658491, "TECH"), ('VRSN', -11.241723443047682, "TECH")], '20220503': [('EL', -12.661627594674435, "CONSUMER")], '20220504': [('MTCH', -11.984307770184763, "TECH"), ('IDXX', -12.377282319478846, "HEALTH"), ('JCI', -12.695848812792768, "MANUFACTURING"), ('AKAM', -12.926186291739889, " TECHNOLOGY")], '20220505': [('ETSY', -13.802250068599657, "RETAIL")], '20220510': [('XRAY', -10.191082802547772, "HEALTH")], '20220513': [('TWTR', -10.734087380793975, "TECH")], '20220519': [('CSCO', -11.269644334160468, "TECH")], '20220601': [('MHP', -10.435504177635337, "TRANSPORTATION"), ('DNB', -10.631030938090653, "TECH")], '20220720': [('BHGE', -10.138248847926265, "INDUSTRIAL")], '20220721': [('CCL R735QTJ8XC9X', -12.804328223624886, "TECH")], '20220722': [('SIVB', -11.731664259348422, "FINANCE")], '20220727': [('SHW', -11.636320736167367, "INDUSTRIAL")], '20220728': [('SWK', -15.121328224776502, "CONSUMER")], '20220729': [('DXCM', -10.024140705828255, "HEALTH"), ('INTC', -10.475950642155626, "TECH")], '20220804': [('FTNT', -10.607506361323157, "TECH")], '20220812': [('ILMN', -12.064720365810762, "HEALTH")], '20220825': [('DLTR', -10.880829015544045, "RETAIL")], '20220915': [('ADBE', -12.973729543496985, "TECH")], '20220929': [('KMX', -14.019016697588112, "RETAIL")], '20220930': [('NKE', -12.304626035875382, "CONSUMER")], '20221019': [('GNRC', -17.517260051441728, "MANUFACTURING"), ('NTRS', -10.809268348362066, "FINANCE")], '20221020': [('ALL', -13.29633740288568, "FINANCE")], '20221021': [('RHI', -17.297094188376754, "COMMERCIAL"), ('SIVB', -15.691331085102156, "FINANCE"), ('HCA', -11.998466037102732, "HEALTH")], '20221026': [('MAS', -11.884567645841956, "MANUFACTURING")], '20221027': [('ALGN', -15.128135715574794, "HEALTH"), ('WST', -18.474842767295595, "HEALTH"), ('INVH', -10.305910305910306, "FINANCE")], '20221028': [('AMZN', -11.679884643114628, "TECH"), ('EW', -16.083429895712626, "HEALTH")], '20221101': [('XRAY', -10.512654120700846, "HEALTH")], '20221103': [('FIS', -16.408707688435882, "FINANCE"), ('FTNT', -14.427954161187301, "TECH"), ('CTL', -15.744680851063823, "MINING"), ('CTSH', -13.757052771324258, "TECH")], '20221108': [('TTWO', -15.22140221402214, "TECH")], '20221109': [('NWS', -11.30334486735871, "CONSUMER"), ('NWSA', -11.936805149210068, "CONSUMER")], '20221116': [('AAP', -14.809176905512677, "RETAIL"), ('CCL', -12.36559139784947, "CONSUMER"), ('DH', -16.675983899821112, "HEALTH")], '20221206': [('NRG', -12.487757100881494, "ENERGY")], '20221214': [('CHTR', -12.567484975043296, "CONSUMER")], '20221222': [('KMX', -11.687436847423383, "RETAIL")], '20230110': [('ILMN', -10.177994308041095, "HEALTH")], '20230124': [('MA', -12.201178117653281, "FINANCE"), ('MCD', -12.206171785064422, "CONSUMER"), ('MET', -12.717498939029573, "FINANCE"), ('MOS', -16.670113753877963, "INDUSTRIAL"), ('MPC', -11.948297604035316, "ENERGY"), ('MS', -12.560485946669411, "FINANCE"), ('NKE', -12.417179826954564, "CONSUMER"), ('OKE', -11.917395669009034), ('PLD', -12.74688665027365, "FINANCE"), ('PPL', -15.109140518417465, "UTILITIES"), ('PRU', -12.411241124112406, "FINANCE"), ('PSX', -11.866467177856741, "ENERGY"), ('UPS', -12., "TRANSPORTATION"), ('VICI', -15.309155766944116, "FINANCE"), ('WFC', -15.352143968007118, "FINANCE")], '20230126': [('SHW', -10.9636164960136, "INDUSTRIAL")], '20230127': [('INTC', -10.103024260551678, "TECH")], '20230201': [('WRK', -15.163098878695205, "INDUSTRIAL"), ('SYF', -10.15518649605227, "FINANCE")], '20230213': [('FIS', -13.245823389021464, "COMMERCIAL")], '20230222': [('CRL', -11.50656814449918, "COMMERCIAL"), ('KEYS', -12.213114754098358, "TECH")], '20230228': [('UHS', -12.232582582103662, "HEALTH")], '20230308': [('WTW', -14.718614718614715, "FINANCE")], '20230310': [('SBNY', -17.62891141472014, "FINANCE")], '20230313': [('TFC', -14.984552008238927, "FINANCE"), ('SCHW', -11.175468483816017, "FINANCE"), ('FITB', -12.347711557458014, "FINANCE"), ('HBAN', -10.845175766641733, "FINANCE")], '20230317': [('FRC', -18.004085205719292, "FINANCE")], '20230320': [('FRC', -19.81707317073171, "FINANCE")] } TWENTY_DATES = \ { '20220121': [('NFLX', -21.300541072306935, "TECH")], '20220127': [('TER', -22.91971821162027, "TECH")], '20220202': [('PYPL', -20.60295790671217, "TECH")], '20220203': [('FB', -24.148606811145513, "TECH")], '20220420': [('NFLX', -29.720891540690168, "TECH")], '20220428': [('ALGN', -21.987625891296506, "HEALTH")], '20220506': [('UA', -21.15963855421686, "CONSUMER")], '20220518': [('DH', -24.471299093655585, "HEALTH")], '20220520': [('ROST', -24.649406688241633, "RETAIL")], '20220803': [('MTCH', -21.705123191239725, "TECH")], '20220916': [('FDX', -21.901693757016655, "TRANSPORTATION")], '20221027': [('FB', -24.51086119242027, "TECH")], '20221028': [('DVA', -20.206718346253226, "HEALTH")], '20221101': [('CTLT', -23.53567625133121, "HEALTH")], '20221103': [('LNC', -24.184261036468328, "FINANCE")], '20230124': [('T', -20.377160817181768, "COMMUNICATIONS")], '20230208': [('CTL', -20.24048096192385, "ENERGY")], '20230309': [('SIVB', -34.096255087182165, "FINANCE")], '20230310': [('FRC', -27.58620689655171, "FINANCE")], '20230313': [('RF', -29.930966469428004, "FINANCE"), ('ZION', -35.41511771995044, "FINANCE"), ('FRC', -66.72782874617737, "FINANCE"), ('KEY', -21.455938697318004, "FINANCE"), ('CMA', -28.54956640027206, "FINANCE")], '20230315': [('FRC', -25.58667676003029, "FINANCE")], '20230316': [('FRC', -33.72913992297818, "FINANCE")], '20230414': [('CTLT', -26.12541462644132, "HEALTH")] } POWELL = { '20200211': '10:00', '20200212': '10:00', '20200326': '06:00', '20200409': '09:00', '20200513': '08:00', '20200519': '09:00', '20200521': '13:30', '20200529': '10:00', '20200616': '09:00', '20200617': '11:00', '20200619': '12:00', '20200630': '11:30', '20200827': '08:10', '20200922': '09:30', '20200923': '09:00', '20200924': '09:00', '20201006': '09:40', '20201019': '07:00', '20201112': '11:45', '20201201': '10:00', '20201202': '10:00', '20210114': '12:30', '20210210': '14:00', '20210223': '10:00', '20210224': '10:00', '20210304': '12:05', '20210322': '08:00', '20210323': '11:00', '20210324': '09:00', '20210408': '11:00', '20210414': '11:00', '20210503': '13:20', '20210604': '06:00', '20210622': '13:00', '20210714': '11:00', '20210715': '08:30', '20210817': '12:30', '20210827': '09:00', '20210924': '09:00', '20210928': '09:00', '20210929': '09:45', '20211022': '10:00', '20211108': '10:30', '20211109': '09:00', '20211129': '15:05', '20211130': '10:00', '20211201': '10:00', '20220111': '10:00', '20220302': '10:00', '20220303': '10:00', '20220321': '11:00', '20220323': '07:00', '20220421': '10:00', '20220517': '13:00', '20220524': '11:20', '20220617': '07:45', '20220622': '08:30', '20220623': '09:00', '20220629': '08:00', '20220826': '09:00', '20220908': '08:10', '20220923': '13:00', '20220927': '06:30', '20220928': '09:15', '20221130': '13:30', '20230110': "PLACEHOLDER", '20230207': "PLACEHOLDER", '20230307': "PLACEHOLDER", '20230308': "PLACEHOLDER", '20230519': "PLACEHOLDER", '20230621': "PLACEHOLDER", '20230622': "PLACEHOLDER", '20230628': "PLACEHOLDER", '20230629': "PLACEHOLDER"} POWELL_NEXT_DAY = { '20200212': 'PLACEHOLDER', '20200213': 'PLACEHOLDER', '20200327': 'PLACEHOLDER', '20200410': 'PLACEHOLDER', '20200514': 'PLACEHOLDER', '20200520': 'PLACEHOLDER', '20200522': 'PLACEHOLDER', '20200530': 'PLACEHOLDER', '20200617': 'PLACEHOLDER', '20200618': 'PLACEHOLDER', '20200620': 'PLACEHOLDER', '20200701': 'PLACEHOLDER', '20200828': 'PLACEHOLDER', '20200923': 'PLACEHOLDER', '20200924': 'PLACEHOLDER', '20200925': 'PLACEHOLDER', '20201007': 'PLACEHOLDER', '20201020': 'PLACEHOLDER', '20201113': 'PLACEHOLDER', '20201202': 'PLACEHOLDER', '20201203': 'PLACEHOLDER', '20210115': 'PLACEHOLDER', '20210211': 'PLACEHOLDER', '20210224': 'PLACEHOLDER', '20210225': 'PLACEHOLDER', '20210305': 'PLACEHOLDER', '20210323': 'PLACEHOLDER', '20210324': 'PLACEHOLDER', '20210325': 'PLACEHOLDER', '20210409': 'PLACEHOLDER', '20210415': 'PLACEHOLDER', '20210504': 'PLACEHOLDER', '20210605': 'PLACEHOLDER', '20210623': 'PLACEHOLDER', '20210715': 'PLACEHOLDER', '20210716': 'PLACEHOLDER', '20210818': 'PLACEHOLDER', '20210828': 'PLACEHOLDER', '20210925': 'PLACEHOLDER', '20210929': 'PLACEHOLDER', '20210930': 'PLACEHOLDER', '20211023': 'PLACEHOLDER', '20211109': 'PLACEHOLDER', '20211110': 'PLACEHOLDER', '20211130': 'PLACEHOLDER', '20211201': 'PLACEHOLDER', '20211202': 'PLACEHOLDER', '20220112': 'PLACEHOLDER', '20220303': 'PLACEHOLDER', '20220304': 'PLACEHOLDER', '20220322': 'PLACEHOLDER', '20220324': 'PLACEHOLDER', '20220422': 'PLACEHOLDER', '20220518': 'PLACEHOLDER', '20220525': 'PLACEHOLDER', '20220618': 'PLACEHOLDER', '20220623': 'PLACEHOLDER', '20220624': 'PLACEHOLDER', '20220630': 'PLACEHOLDER', '20220827': 'PLACEHOLDER', '20220909': 'PLACEHOLDER', '20220924': 'PLACEHOLDER', '20220928': 'PLACEHOLDER', '20220929': 'PLACEHOLDER', '20221130': 'PLACEHOLDER', '20230111': 'PLACEHOLDER', '20230208': 'PLACEHOLDER', '20230308': 'PLACEHOLDER', '20230309': 'PLACEHOLDER', '20230520': 'PLACEHOLDER', '20230622': 'PLACEHOLDER', '20230623': 'PLACEHOLDER', '20230629': 'PLACEHOLDER', '20230630': 'PLACEHOLDER', '20230824': 'PLACEHOLDER', '20230825': 'PLACEHOLDER' } FOMC_MEETINGS_FIRSTDAY = { '20210126': 'PLACEHOLDER', '20210316': 'PLACEHOLDER', '20210427': 'PLACEHOLDER', '20210616': 'PLACEHOLDER', '20210727': 'PLACEHOLDER', '20210921': 'PLACEHOLDER', '20211102': 'PLACEHOLDER', '20211214': 'PLACEHOLDER', '20220125': 'PLACEHOLDER', '20220315': 'PLACEHOLDER', '20220503': 'PLACEHOLDER', '20220614': 'PLACEHOLDER', '20220726': 'PLACEHOLDER', '20220920': 'PLACEHOLDER', '20221101': 'PLACEHOLDER', '20221213': 'PLACEHOLDER', '20230131': 'PLACEHOLDER', '20230321': 'PLACEHOLDER', '20230402': 'PLACEHOLDER', '20230613': 'PLACEHOLDER', '20230725': 'PLACEHOLDER', '20230919': 'PLACEHOLDER', '20231031': 'PLACEHOLDER', '20231212': 'PLACEHOLDER' } FARM_PAYROLL = { '20230106': 'PLACEHOLDER', '20230203': 'PLACEHOLDER', '20230310': 'PLACEHOLDER', '20230407': 'PLACEHOLDER', '20230505': 'PLACEHOLDER', '20230602': 'PLACEHOLDER', '20230707': 'PLACEHOLDER', '20230804': 'PLACEHOLDER', '20230901': 'PLACEHOLDER', '20231006': 'PLACEHOLDER',} FOMC_MEETINGS = { '20210127': 'PLACEHOLDER', '20210317': 'PLACEHOLDER', '20210428': 'PLACEHOLDER', '20210615': 'PLACEHOLDER', '20210728': 'PLACEHOLDER', '20210922': 'PLACEHOLDER', '20211103': 'PLACEHOLDER', '20211215': 'PLACEHOLDER', '20220126': 'PLACEHOLDER', '20220316': 'PLACEHOLDER', '20220504': 'PLACEHOLDER', '20220615': 'PLACEHOLDER', '20220727': 'PLACEHOLDER', '20220921': 'PLACEHOLDER', '20221102': 'PLACEHOLDER', '20221214': 'PLACEHOLDER', '20230201': 'PLACEHOLDER', '20230322': 'PLACEHOLDER', '20230403': 'PLACEHOLDER', '20230614': 'PLACEHOLDER', '20230726': 'PLACEHOLDER', '20230920': 'PLACEHOLDER', '20231101': 'PLACEHOLDER', '20231213': 'PLACEHOLDER' } FOMC_MINUTES = { '20200103': 'PLACEHOLDER', '20200219': 'PLACEHOLDER', '20200408': 'PLACEHOLDER', '20200520': 'PLACEHOLDER', '20200610': 'PLACEHOLDER', '20200701': 'PLACEHOLDER', '20200819': 'PLACEHOLDER', '20200916': 'PLACEHOLDER', '20201007': 'PLACEHOLDER', '20201125': 'PLACEHOLDER', '20210106': 'PLACEHOLDER', '20210217': 'PLACEHOLDER', '20210407': 'PLACEHOLDER', '20210519': 'PLACEHOLDER', '20210707': 'PLACEHOLDER', '20210818': 'PLACEHOLDER', '20211013': 'PLACEHOLDER', '20211124': 'PLACEHOLDER', '20220105': 'PLACEHOLDER', '20220216': 'PLACEHOLDER', '20220406': 'PLACEHOLDER', '20220525': 'PLACEHOLDER', '20220706': 'PLACEHOLDER', '20220817': 'PLACEHOLDER', '20221012': 'PLACEHOLDER', '20221123': 'PLACEHOLDER', '20230104': 'PLACEHOLDER', '20230222': 'PLACEHOLDER', '20230412': 'PLACEHOLDER', '20230524': 'PLACEHOLDER', '20230705': 'PLACEHOLDER', '20230816': 'PLACEHOLDER', '20231011': 'PLACEHOLDER', '20231122': 'PLACEHOLDER' } CPI = { '20180323': 'PLACEHOLDER', '20181107': 'PLACEHOLDER', '20190823': 'PLACEHOLDER', '20210113': 'PLACEHOLDER', '20210210': 'PLACEHOLDER', '20210310': 'PLACEHOLDER', '20210413': 'PLACEHOLDER', '20210512': 'PLACEHOLDER', '20210610': 'PLACEHOLDER', '20210713': 'PLACEHOLDER', '20210811': 'PLACEHOLDER', '20210914': 'PLACEHOLDER', '20211013': 'PLACEHOLDER', '20211110': 'PLACEHOLDER', '20211210': 'PLACEHOLDER', '20220112': 'PLACEHOLDER', '20220210': 'PLACEHOLDER', '20220310': 'PLACEHOLDER', '20220412': 'PLACEHOLDER', '20220511': 'PLACEHOLDER', '20220610': 'PLACEHOLDER', '20220713': 'PLACEHOLDER', '20220810': 'PLACEHOLDER', '20220913': 'PLACEHOLDER', '20221013': 'PLACEHOLDER', '20221110': 'PLACEHOLDER', '20221213': 'PLACEHOLDER', '20230112': 'PLACEHOLDER', '20230214': 'PLACEHOLDER', '20230314': 'PLACEHOLDER', '20230331': 'PLACEHOLDER', '20230412': 'PLACEHOLDER', '20230510': 'PLACEHOLDER', '20230613': 'PLACEHOLDER', '20230712': 'PLACEHOLDER', '20230810': 'PLACEHOLDER', '20230829': 'PLACEHOLDER', '20230913': 'PLACEHOLDER', '20231012': 'PLACEHOLDER', '20231114': 'PLACEHOLDER', '20231212': 'PLACEHOLDER', '20231213': "PLACEHOLDER", '20231214': "PLACEHOLDER", '20231215': "PLACEHOLDER", '20241113': "PLACEHOLDER", '20241101': "PLACEHOLDER", '20241104': "PLACEHOLDER", '20241105': "PLACEHOLDER", '20241106': "PLACEHOLDER", '20241107': "PLACEHOLDER", '20241108': "PLACEHOLDER", '20241211': "PLACEHOLDER", '20241218': "PLACEHOLDER", '20250129': "PLACEHOLDER", } CPI_NEXT_DAY = { '20210114': 'PLACEHOLDER', '20210211': 'PLACEHOLDER', '20210311': 'PLACEHOLDER', '20210414': 'PLACEHOLDER', '20210513': 'PLACEHOLDER', '20210611': 'PLACEHOLDER', '20210714': 'PLACEHOLDER', '20210812': 'PLACEHOLDER', '20210915': 'PLACEHOLDER', '20211014': 'PLACEHOLDER', '20211111': 'PLACEHOLDER', '20211211': 'PLACEHOLDER', '20220113': 'PLACEHOLDER', '20220211': 'PLACEHOLDER', '20220311': 'PLACEHOLDER', '20220413': 'PLACEHOLDER', '20220512': 'PLACEHOLDER', '20220611': 'PLACEHOLDER', '20220714': 'PLACEHOLDER', '20220811': 'PLACEHOLDER', '20220914': 'PLACEHOLDER', '20221014': 'PLACEHOLDER', '20221111': 'PLACEHOLDER', '20221214': 'PLACEHOLDER', '20230113': 'PLACEHOLDER', '20230215': 'PLACEHOLDER', '20230315': 'PLACEHOLDER', '20230413': 'PLACEHOLDER', '20230511': 'PLACEHOLDER', '20230614': 'PLACEHOLDER', '20230713': 'PLACEHOLDER', '20230811': 'PLACEHOLDER', '20230914': 'PLACEHOLDER', '20231013': 'PLACEHOLDER', '20231115': 'PLACEHOLDER', '20231213': 'PLACEHOLDER' } FED_INTEREST_RATE_DECISION = { '20200129': 'PLACEHOLDER', '20200303': 'PLACEHOLDER', '20200315': 'PLACEHOLDER', '20200419': 'PLACEHOLDER', '20200610': 'PLACEHOLDER', '20200729': 'PLACEHOLDER', '20200916': 'PLACEHOLDER', '20201105': 'PLACEHOLDER', '20201216': 'PLACEHOLDER', '20210127': 'PLACEHOLDER', '20210317': 'PLACEHOLDER', '20210428': 'PLACEHOLDER', '20210616': 'PLACEHOLDER', '20210728': 'PLACEHOLDER', '20210922': 'PLACEHOLDER', '20211103': 'PLACEHOLDER', '20211215': 'PLACEHOLDER', '20220126': 'PLACEHOLDER', '20220316': 'PLACEHOLDER', '20220504': 'PLACEHOLDER', '20220615': 'PLACEHOLDER', '20220727': 'PLACEHOLDER', '20220921': 'PLACEHOLDER', '20221102': 'PLACEHOLDER', '20221214': 'PLACEHOLDER', '20230201': 'PLACEHOLDER', '20230322': 'PLACEHOLDER', '20230503': 'PLACEHOLDER', '20230614': 'PLACEHOLDER', '20230726': 'PLACEHOLDER', '20230920': 'PLACEHOLDER', '20231101': 'PLACEHOLDER', '20231213': 'PLACEHOLDER', } FED_INTEREST_RATE_DECISION_NEXTDAY = { '20200130': 'PLACEHOLDER', '20200304': 'PLACEHOLDER', '20200316': 'PLACEHOLDER', '20200420': 'PLACEHOLDER', '20200611': 'PLACEHOLDER', '20200730': 'PLACEHOLDER', '20200917': 'PLACEHOLDER', '20201106': 'PLACEHOLDER', '20201217': 'PLACEHOLDER', '20210128': 'PLACEHOLDER', '20210318': 'PLACEHOLDER', '20210429': 'PLACEHOLDER', '20210617': 'PLACEHOLDER', '20210729': 'PLACEHOLDER', '20210923': 'PLACEHOLDER', '20211104': 'PLACEHOLDER', '20211216': 'PLACEHOLDER', '20220127': 'PLACEHOLDER', '20220317': 'PLACEHOLDER', '20220505': 'PLACEHOLDER', '20220616': 'PLACEHOLDER', '20220728': 'PLACEHOLDER', '20220922': 'PLACEHOLDER', '20221103': 'PLACEHOLDER', '20221215': 'PLACEHOLDER', '20230202': 'PLACEHOLDER', '20230323': 'PLACEHOLDER', '20230504': 'PLACEHOLDER', '20230615': 'PLACEHOLDER', '20230727': 'PLACEHOLDER', '20230921': 'PLACEHOLDER', '20231102': 'PLACEHOLDER', '20231214': 'PLACEHOLDER', } POWELL_NEXT_DAY_2023 = { '20230111':"PLACEHOLDER", '20230208':"PLACEHOLDER", '20230308':"PLACEHOLDER", '20230309':"PLACEHOLDER", } FOMC_MEETINGS_2023 = { '20230131':"PLACEHOLDER", '20230201':"PLACEHOLDER", '20230321':"PLACEHOLDER", '20230322':"PLACEHOLDER", '20230402':"PLACEHOLDER", '20230403':"PLACEHOLDER", '20230613':"PLACEHOLDER", '20230614':"PLACEHOLDER", '20230725':"PLACEHOLDER", '20230726':"PLACEHOLDER", '20230919':"PLACEHOLDER", '20230920':"PLACEHOLDER", '20231031':"PLACEHOLDER", '20231101':"PLACEHOLDER", '20231212':"PLACEHOLDER", '20231213':"PLACEHOLDER", } FOMC_MINUTES_2023 = { '20230104':"PLACEHOLDER", '20230222':"PLACEHOLDER", '20230412':"PLACEHOLDER", '20230524':"PLACEHOLDER", '20230705':"PLACEHOLDER", '20230816':"PLACEHOLDER", '20231011':"PLACEHOLDER", '20231122':"PLACEHOLDER", } CPI_2023 = { '20230112':"PLACEHOLDER", '20230214':"PLACEHOLDER", '20230314':"PLACEHOLDER", '20230412':"PLACEHOLDER", '20230510':"PLACEHOLDER", '20230613':"PLACEHOLDER", '20230712':"PLACEHOLDER", '20230810':"PLACEHOLDER", '20230913':"PLACEHOLDER", '20231012':"PLACEHOLDER", '20231114':"PLACEHOLDER", '20231212':"PLACEHOLDER", '20231213':"PLACEHOLDER", '20231214':"PLACEHOLDER", } CPI_NEXT_DAY_2023 = { '20230113':"PLACEHOLDER", '20230215':"PLACEHOLDER", '20230315':"PLACEHOLDER", '20230413':"PLACEHOLDER", '20230511':"PLACEHOLDER", '20230614':"PLACEHOLDER", '20230713':"PLACEHOLDER", '20230811':"PLACEHOLDER", '20230914':"PLACEHOLDER", '20231013':"PLACEHOLDER", '20231115':"PLACEHOLDER", '20231213':"PLACEHOLDER", }
#region imports from AlgorithmImports import * #endregion import calendar_info import config import pandas as pd USE_FED_INTEREST_RATE_DECISION_NEXTDAY = True class TenTwentyDates(): def __init__(self): self.use_10 = config.TEN_RULE_ACTIVE self.use_20 = config.TWENTY_RULE_ACTIVE self.use_powell = config.POWELL_ACTIVE self.use_powell_next_day = config.POWELL_NEXT_DAY_ACTIVE self.use_fomc_meetings = config.FOMC_MEETINGS_ACTIVE self.use_fomc_minutes = config.FOMC_MINUTES_ACTIVE self.use_fed_interest_rate_decision = config.FED_INTEREST_RATE_DECISION_ACTIVE self.use_fed_interest_rate_decision_nextday = config.FED_INTEREST_RATE_DECISION_NEXTDAY_ACTIVE self.use_cpi = config.CPI_ACTIVE self.use_cpi_next_day = config.CPI_NEXT_DAY_ACTIVE self.use_farm_roll = config.FARM_PAYROLL_ACTIVE # df = pd.read_csv('event_queries_4.csv') # Assuming it's a CSV file, adjust accordingly # df['date'] = pd.to_datetime(df['date']) # # Create a dictionary with date as key and time as value # self.result_dict = dict(zip(df['date'].dt.strftime('%Y%m%d'), df['date'].dt.strftime('%H:%M'))) # Convert the DataFrame to a dictionary def check_rules(self, date, time, date_tomorrow): if self.use_powell: if str(date) in calendar_info.POWELL.keys() or str(date_tomorrow) in calendar_info.POWELL.keys(): date_to_check = str(date) if str(date) in calendar_info.POWELL.keys() else str(date_tomorrow) if calendar_info.POWELL[date_to_check] == 'PLACEHOLDER' or (date_to_check in calendar_info.POWELL.keys() and int(calendar_info.POWELL[date_to_check].replace(":", "")) < 1200 or int(time.replace(":", "")) >= int(calendar_info.POWELL[date_to_check].replace(":", ""))): return True if self.use_powell_next_day: if str(date) in calendar_info.POWELL_NEXT_DAY.keys() or str(date_tomorrow) in calendar_info.POWELL_NEXT_DAY.keys(): return True if self.use_fomc_meetings: if str(date) in calendar_info.FOMC_MEETINGS.keys() or str(date_tomorrow) in calendar_info.FOMC_MEETINGS.keys(): return True if self.use_farm_roll: if str(date) in calendar_info.FARM_PAYROLL.keys() or str(date_tomorrow) in calendar_info.FARM_PAYROLL.keys(): return True if self.use_fomc_minutes: if str(date) in calendar_info.FOMC_MINUTES.keys() or str(date_tomorrow) in calendar_info.FOMC_MINUTES.keys(): return True if self.use_fed_interest_rate_decision: if str(date) in calendar_info.FED_INTEREST_RATE_DECISION.keys() or str(date_tomorrow) in calendar_info.FED_INTEREST_RATE_DECISION.keys(): return True if self.use_fed_interest_rate_decision_nextday: if str(date) in calendar_info.FED_INTEREST_RATE_DECISION_NEXTDAY.keys() or str(date_tomorrow) in calendar_info.FED_INTEREST_RATE_DECISION_NEXTDAY.keys(): return True if self.use_cpi: if str(date) in calendar_info.CPI.keys() or str(date_tomorrow) in calendar_info.CPI.keys(): return True if self.use_cpi_next_day: if str(date) in calendar_info.CPI_NEXT_DAY.keys() or str(date_tomorrow) in calendar_info.CPI_NEXT_DAY.keys(): return True if self.use_10: if str(date) in calendar_info.TEN_DATES or str(date_tomorrow) in calendar_info.TEN_DATES: dates_to_check = [str(date), str(date_tomorrow)] for date_check in dates_to_check: for tuple in calendar_info.TEN_DATES.get(date_check, []): if tuple[2] == "FINANCE" or tuple[2] == "RETAIL": return True if self.use_20: if str(date) in calendar_info.TWENTY_DATES or str(date_tomorrow) in calendar_info.TWENTY_DATES: dates_to_check = [str(date), str(date_tomorrow)] for date_check in dates_to_check: for tuple in calendar_info.TWENTY_DATES.get(date_check, []): if tuple[2] == "FINANCE" or tuple[2] == "RETAIL": return True if self.use_20 and self.use_10: if str(date) in calendar_info.TEN_DATES or str(date) in calendar_info.TWENTY_DATES or str(date_tomorrow) in calendar_info.TEN_DATES or str(date_tomorrow) in calendar_info.TWENTY_DATES: dates_to_check = [str(date), str(date_tomorrow)] for date_check in dates_to_check: for tuple in calendar_info.TEN_DATES.get(date_check, []) + calendar_info.TWENTY_DATES.get(date_check, []): if tuple[2] == "FINANCE" or tuple[2] == "RETAIL": return True return False
#region imports from AlgorithmImports import * from datetime import * ################################################################## IRON CONDOR SETTINGS RANGE ######################################################################## # Pick between "FIXED" or "AUTOMATIC" # Fixed will use the MINIMUM and MAXIMUM DISTANCE below # Automatic will use the AUTOMATIC RANGE PERCENTAGES below RANGE_MODE = "AUTOMATIC" TEST_MODE = True # Set this to False to disable the testing mode TEST_QUANTITY = 1 # The predefined test quantity # Control which range it is allowed to automatically select # Pick between 1, 1.5, 2, 2.5, 3 # If a number is included the set logic will be applied to automatically select that range if the conditions are met # Below are the conditions for each range: # 3% = VIX above 30 # 2.5% = VIX above 25 and ATR% above 2% # 2% = VIX below 25 or ATR% below 2% # 1.5% = VIX below 25 and ATR% below 2% # 1% = VIX below 20 and ATR% below 1.5% AUTOMATIC_RANGE_PERCENTAGES = [1, 1.5, 2, 2.5, 3] # Percentage added to the minimums in the list above to create the maximum for the ranges AUTOMATIC_RANGE_MAX_ADD = 0.3 # VIX Threshold for deciding if 3% range is needed VIX_THRESHOLD_1 = 30 # VIX Threshold for deciding if 2.5% or 2% range is needed VIX_THRESHOLD_2 = 25 # VIX Threshold for deciding if 1% or 1.5% range is needed VIX_THRESHOLD_3 = 20 # ATR Threshold for deciding if 1.5% or 2% or 2.5% range is needed ATR_THRESHOLD_1 = 0.02 # ATR Threshold for deciding if 1% or 1.5%range is needed ATR_THRESHOLD_2 = 0.02 POWELL_ACTIVE = False # True/False POWELL_NEXT_DAY_ACTIVE = False # True/False CPI_ACTIVE = True # True/False CPI_NEXT_DAY_ACTIVE = False # True/False FOMC_MEETINGS_ACTIVE = False # True/False FED_INTEREST_RATE_DECISION_ACTIVE = False # True/False FED_INTEREST_RATE_DECISION_NEXTDAY_ACTIVE = False # True/False FOMC_MINUTES_ACTIVE = False # True/False FOMC_MEETINGS_FIRSTDAY_ACTIVE = False # If a stock in major sectors of S&P 500 gapped down 10% then we dont enter that day TEN_RULE_ACTIVE = 0.0 # Calendar variables, need to remove these since we also use calendar api now USE_2023_CPI = 1.0 USE_2023_CPI_NEXT_DAY = 0.0 USE_2023_FOMC_MEETINGS = 0.0 USE_2023_FOMC_MINUTES = 0 USE_2023_POWELL = 0 USE_2023_POWELL_NEXT_DAY = 0 # If a stock in major sectors of S&P 500 gapped down 20% then we dont enter that day TWENTY_RULE_ACTIVE = 0 # Have to remove this again as this was added for farm 2023 before it came from the calendar api # True/False FARM_PAYROLL_ACTIVE = False # I have removed this since only scaling range for 1 event seems like overfitting # True/False FARM_PAYROLL_AS_SCALED_ACTIVE = False
# region imports from AlgorithmImports import * from setup_contracts import CreateCondors from select_range import SelectRange from check_calendar import TenTwentyDates import numpy as np import config import pytz from datetime import datetime, timedelta # endregion class PensiveSkyBlueZebra(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 11, 1) self.SetEndDate(2024, 12, 31) self.SetCash(100000) self.symbol = self.AddIndex("SPX", Resolution.Minute).Symbol self.range_selector = SelectRange(self, self.symbol) self.calendar_checker = TenTwentyDates() self.option = self.AddIndexOption(self.symbol, "SPXW") self.option_symbol = self.option.Symbol self.option.SetFilter(self.UniverseFunc) self.SetSecurityInitializer(self.CustomSecurityInitializer) # Updated to use correct property for portfolio self.Portfolio.SetPositions(SecurityPositionGroupModel.Null) self.Portfolio.MarginCallModel = MarginCallModel.Null self.open_price = None self.trading_window = False self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.symbol, 11), self.open_window) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.symbol, 0), self.get_close) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.symbol, 0), self.close_window) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.symbol, 1), self.get_range) self.invested_today = False self.puts_1dte = [] self.calls_1dte = [] self.condor_creator = CreateCondors(self) self.slice = None self.orders_to_send = [] self.current_symbols_list = [] self.prev_gap = 0 self.close = 1 self.intraday_movement = 0 self.daily_open = 0 self.test_mode = config.TEST_MODE self.test_quantity = config.TEST_QUANTITY def get_close(self): # Reset daily_open to 0 # self.daily_open = 0 # Pull from history the most recent 9:31 AM price history = self.History[TradeBar](self.symbol, timedelta(days=1), Resolution.Minute) # Convert history to a list for easier manipulation history_list = list(history) # Ensure the returned history is not empty if history_list: # Determine if the bars start at 8 AM or 9 AM by checking the first bar first_bar_time = history_list[0].Time # self.Debug(f"First bar time: {first_bar_time}") # Determine if the data starts at 8:30 AM (requiring a 1-hour shift) needs_shift = first_bar_time.strftime("%H:%M") == "08:30" # if needs_shift: # self.Debug("Bars start at 8 AM, applying a 1-hour shift.") # else: # self.Debug("Bars start at 9 AM, no shift needed.") # Iterate through the TradeBars to find the 9:31 AM NYSE time for bar in history_list: bar_time = bar.Time if needs_shift: # Apply the shift if bars start at 8 AM bar_time += timedelta(hours=1) # Debugging for raw and corrected bar times # self.Debug(f"Raw bar time: {bar.Time}, Corrected bar time: {bar_time} {bar.Open}") # Check if the corrected time matches 9:31 AM if bar_time.strftime("%H:%M") == "09:30": # Assign the most recent 9:31 AM price to daily_open self.daily_open = bar.Open # self.Debug(f"Daily Open: {self.daily_open} at {bar_time}") # self.Debug("-------------------------------------------------------") break # Update close price and calculate intraday movement self.close = self.Securities[self.symbol].Close if self.daily_open != 0: # Ensure daily_open is not 0 to avoid division by zero self.intraday_movement = abs((self.close - self.daily_open) / self.daily_open) else: self.intraday_movement = 0 # Fallback if daily_open could not be set def CustomSecurityInitializer(self, security: Security) -> None: # Disable trading fees security.SetFeeModel(ConstantFeeModel(0)) if security.Type == SecurityType.IndexOption: # Option type security.SetOptionAssignmentModel(NullOptionAssignmentModel()) # Next, overwrite the security buying power security.SetBuyingPowerModel(SecurityMarginModel(10)) def UniverseFunc(self, universe): price = self.Securities[self.symbol].Price atm = self.Securities[self.symbol].Price * 0.06 return universe.IncludeWeeklys().Strikes(-atm, atm).Expiration(TimeSpan.FromDays(1), TimeSpan.FromDays(2)) def liq(self): self.Liquidate() def close_window(self): self.trading_window = False self.puts_1dte = [] self.calls_1dte = [] def get_range(self): gap = abs((self.Securities[self.symbol].Open - self.close) / self.close) self.Plot("Daily Gap", "Gap Percentage", gap * 100) # Plot as a percentage self.daily_open = self.Securities[self.symbol].Open # self.debug(f"Daily Open correct {self.daily_open} {self.time}") if self.orders_to_send: for order_info in self.orders_to_send: current_premium = 0 # Compute current premium for i, symbol in enumerate(order_info['symbols']): if i % 2 == 0: # Sell leg premium = self.Securities[symbol].BidPrice else: # Buy leg premium = -self.Securities[symbol].AskPrice current_premium += premium put_sell_premium = self.Securities[order_info['symbols'][0]].BidPrice call_sell_premium = self.Securities[order_info['symbols'][2]].BidPrice include_put_leg = put_sell_premium >= 0.1 include_call_leg = call_sell_premium >= 0.1 # Skip this order if neither leg meets the premium requirement if not include_put_leg and not include_call_leg: continue # Build the legs list based on which sides are eligible legs = [] if include_put_leg: legs.append(order_info['legs'][0]) # Put Sell legs.append(order_info['legs'][1]) # Put Buy if include_call_leg: legs.append(order_info['legs'][2]) # Call Sell legs.append(order_info['legs'][3]) # Call Buy # Calculate premium only if legs are eligible and proceed if legs: if (gap < 0.009 and self.prev_gap < 0.0125 and self.intraday_movement < 0.03) and current_premium > 0.2: # Calculate the maximum number of contracts based on available cash strike_difference = 25 * 100 # Strike difference multiplied by 100 max_contracts_based_on_cash = int(self.Portfolio.Cash / strike_difference) # Ensure a minimum of 40 contracts quantity = self.test_quantity if self.test_mode else max(1, int(max_contracts_based_on_cash * 0.85 * order_info['quantity_factor'])) self.ComboMarketOrder(legs, quantity, tag=f"range: {order_info['min_range']} gap: {gap} {self.prev_gap}, {order_info['tag_suffix']}") else: # Order conditions not met, discard pass # Clear orders to send self.orders_to_send = [] self.prev_gap = gap # Then reset ranges for next day min_range, max_range = self.range_selector.set_range_today() self.todays_min_range = min_range self.todays_max_range = max_range # Reset variables self.orders_to_send = [] self.current_symbols_list = [] def open_window(self): self.invested_today = False if self.Time.weekday() != 4: self.open_price = self.Securities[self.symbol].Open no_entry_today = self.calendar_checker.check_rules((self.Time + timedelta(days=1)).strftime("%Y%m%d"), self.Time.strftime("%H%M"), (self.Time + timedelta(days=1)).strftime("%Y%m%d")) if not no_entry_today and not (self.time.year == 2021 and self.time.month == 11 and not self.time.day == 24): self.trading_window = True self.puts_1dte, self.calls_1dte = self.condor_creator.populate_lists(self.symbol, None, self.open_price, None, None) def OnData(self, data: Slice): self.slice = data if self.open_price is None: return # Ensure we have an open price to work with if not self.invested_today and self.trading_window and self.Time.day != 3 and not self.Portfolio.Invested and self.Time.hour <= 15: self.SetUpIronCondor() def SetUpIronCondor(self): self.SetUpIronCondorForRange(self.todays_min_range, 0.75, '') self.SetUpIronCondorForRange(self.todays_min_range + 0.005, 0.25, 'small') def SetUpIronCondorForRange(self, min_range, quantity_factor, tag_suffix): if not self.puts_1dte or not self.calls_1dte: return underlying_price = self.Securities[self.symbol].Close sell_strike_put = round(underlying_price * (1 - min_range) / 5) * 5 sell_strike_call = round(underlying_price * (1 + min_range) / 5) * 5 buy_strike_put = sell_strike_put - 25 buy_strike_call = sell_strike_call + 25 put_1dte_sell = min(self.puts_1dte, key=lambda x: abs(x.StrikePrice - sell_strike_put)) call_1dte_sell = min(self.calls_1dte, key=lambda x: abs(x.StrikePrice - sell_strike_call)) put_1dte_buy = next((x for x in self.puts_1dte if x.StrikePrice == buy_strike_put), None) call_1dte_buy = next((x for x in self.calls_1dte if x.StrikePrice == buy_strike_call), None) if put_1dte_buy is None or call_1dte_buy is None: return if put_1dte_sell.Symbol == put_1dte_buy.Symbol or call_1dte_sell.Symbol == call_1dte_buy.Symbol: return current_put_1dte_sell_premium = self.Securities[put_1dte_sell.Symbol].AskPrice current_put_1dte_buy_premium = -self.Securities[put_1dte_buy.Symbol].BidPrice current_call_1dte_sell_premium = self.Securities[call_1dte_sell.Symbol].AskPrice current_call_1dte_buy_premium = -self.Securities[call_1dte_buy.Symbol].BidPrice if current_put_1dte_sell_premium == 0 or current_put_1dte_buy_premium == 0 or current_call_1dte_sell_premium == 0 or current_call_1dte_buy_premium == 0: return current_premium = current_put_1dte_sell_premium - current_put_1dte_buy_premium + current_call_1dte_sell_premium - current_call_1dte_buy_premium if current_premium > 0.2: if self.Time.hour != 1: self.invested_today = True self.trading_window = False # Calculate the maximum number of contracts based on available cash strike_difference = 25 * 100 # Strike difference multiplied by 100 max_contracts_based_on_cash = int(self.Portfolio.Cash / strike_difference) # Ensure a minimum of 40 contracts quantity = self.test_quantity if self.test_mode else max(1, int(max_contracts_based_on_cash * 0.85 * quantity_factor)) include_put_leg = put_sell_pcurrent_put_1dte_sell_premiumremium >= 0.1 include_call_leg = current_call_1dte_sell_premium >= 0.1 # Create order legs # Build legs list based on which sides are eligible legs = [] if include_put_leg: legs.append(Leg.Create(put_1dte_sell.Symbol, -1)) # Put Sell legs.append(Leg.Create(put_1dte_buy.Symbol, 1)) # Put Buy if include_call_leg: legs.append(Leg.Create(call_1dte_sell.Symbol, -1)) # Call Sell legs.append(Leg.Create(call_1dte_buy.Symbol, 1)) # Call Buy # Place combo market order self.ComboMarketOrder(legs, quantity, tag=f"range: {min_range} {tag_suffix}") else: # For after-hours, store the order to send later legs = [] legs.append(Leg.Create(put_1dte_sell.Symbol, -1)) legs.append(Leg.Create(put_1dte_buy.Symbol, 1)) legs.append(Leg.Create(call_1dte_sell.Symbol, -1)) legs.append(Leg.Create(call_1dte_buy.Symbol, 1)) self.orders_to_send.append({ 'legs': legs, 'min_range': min_range, 'quantity_factor': quantity_factor, 'tag_suffix': tag_suffix, 'symbols': [put_1dte_sell.Symbol, put_1dte_buy.Symbol, call_1dte_sell.Symbol, call_1dte_buy.Symbol] })
from AlgorithmImports import * import config class SelectRange(): def __init__(self, algorithm, symbol): self.algorithm = algorithm self.symbol = symbol self.range_mode = config.RANGE_MODE self.VIX = self.algorithm.AddIndex("VIX", Resolution.Minute).Symbol self.range_max_today = None self.range_min_today = None self.automatic_range_percentages = config.AUTOMATIC_RANGE_PERCENTAGES self.automatic_range_max_add = config.AUTOMATIC_RANGE_MAX_ADD/100 self.use_1 = False self.use_1_5 = False self.use_2 = False self.use_2_5 = False self.use_3 = False self.algorithm.Schedule.On(self.algorithm.DateRules.EveryDay(), self.algorithm.TimeRules.AfterMarketOpen(self.symbol, 1), self.temporary_get_vix) if self.range_mode == "FIXED": self.range_max_today = config.MAXIMUM_DISTANCE/100 self.range_min_today = config.MINIMUM_DISTANCE/100 elif self.range_mode == "AUTOMATIC": if 1 in self.automatic_range_percentages: self.use_1 = True if 1.5 in self.automatic_range_percentages: self.use_1_5 = True if 2 in self.automatic_range_percentages: self.use_2 = True if 2.5 in self.automatic_range_percentages: self.use_2_5 = True if 3 in self.automatic_range_percentages: self.use_3 = True else: raise Exception(f"Selected RANGE MODE {self.range_mode} is incorrect, please check the input and either choose 'AUTOMATIC' or 'FIXED'") def temporary_get_vix(self): self.vix = self.algorithm.Securities[self.VIX].Open def set_range_today(self): if self.range_mode == "AUTOMATIC": if self.vix >= 30: self.range_max_today = 0.06 + self.automatic_range_max_add self.range_min_today = 0.06 elif self.vix >= 20: self.range_max_today = 0.04 + self.automatic_range_max_add self.range_min_today = 0.04 else: self.range_max_today = 0.02 + self.automatic_range_max_add self.range_min_today = 0.02 elif self.range_mode == "FIXED": pass else: raise Exception(f"Selected RANGE MODE {self.range_mode} is incorrect, please check the input and either choose 'AUTOMATIC' or 'FIXED'") self.algorithm.Plot("Range Used", "Range", self.range_min_today) return self.range_min_today , self.range_max_today
from AlgorithmImports import * import numpy as np import pandas as pd from pandas import Timestamp class CreateCondors(): def __init__(self, algorithm): self.algo = algorithm def populate_lists(self, symbol, symbolData, open_price, min_range, max_range): if open_price is None: open_price = self.algo.Securities[symbol].Open put_3_expiry = self.generate_price_list(open_price, direction="PUT") call_3_expiry = self.generate_price_list(open_price, direction="CALL") time_1dte = self.algo.Time + timedelta(days=1) # Ensure expiry is of the correct type expiry = Timestamp(time_1dte.year, time_1dte.month, time_1dte.day) chain = self.algo.option_chain(Symbol.create_canonical_option(underlying_symbol=symbol, target_option="SPXW", market=Market.USA, alias=None), flatten=True).data_frame self.algo.debug(chain) self.algo.debug(f"DataFrame columns: {list(chain.columns)}") self.algo.debug(f"DataFrame index: {chain.index.to_list()}") if chain is None or not isinstance(chain, pd.DataFrame): self.algo.debug("The 'chain' variable is invalid or not a DataFrame.") return None else: self.algo.debug(f"DataFrame head:\n{chain.head().to_string()}") # Filter chain by expiry if 'expiry' not in chain.columns: return [], [] filtered_chain = chain[chain['expiry'] == expiry] # Get available strikes available_call_strikes = set(filtered_chain[filtered_chain.right == OptionRight.CALL]['strike'].unique()) available_put_strikes = set(filtered_chain[filtered_chain.right == OptionRight.PUT]['strike'].unique()) # Filter put and call expiry strikes put_3_expiry = [strike for strike in put_3_expiry if strike in available_put_strikes] call_3_expiry = [strike for strike in call_3_expiry if strike in available_call_strikes] call_ivs = [] put_ivs = [] for original_call in call_3_expiry: call_option_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA, OptionStyle.European, OptionRight.Call, original_call, time_1dte) call_contract_1 = self.algo.AddIndexOptionContract(call_option_1, Resolution.Minute) call_ivs.append(call_contract_1) for original_put in put_3_expiry: put_option_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA, OptionStyle.European, OptionRight.Put, original_put, time_1dte) put_contract_1 = self.algo.AddIndexOptionContract(put_option_1, Resolution.Minute) put_ivs.append(put_contract_1) return put_ivs, call_ivs def generate_price_list(self, open_price, increment=5, percentage_range=0.06, direction="CALL"): variation = open_price * percentage_range lower_bound = round((open_price - variation) / increment) * increment upper_bound = round((open_price + variation) / increment) * increment price_list = list(range(lower_bound, upper_bound + increment, increment)) open_price_rounded = round(open_price / increment) * increment if open_price_rounded not in price_list: price_list.insert(0, open_price_rounded) return price_list