Overall Statistics
Total Orders
264
Average Win
0.05%
Average Loss
-0.02%
Compounding Annual Return
2.363%
Drawdown
0.400%
Expectancy
0.797
Start Equity
100000
End Equity
102115
Net Profit
2.115%
Sharpe Ratio
-9.708
Sortino Ratio
-135.461
Probabilistic Sharpe Ratio
100.000%
Loss Rate
52%
Win Rate
48%
Profit-Loss Ratio
2.71
Alpha
-0.039
Beta
0
Annual Standard Deviation
0.004
Annual Variance
0
Information Ratio
-1.811
Tracking Error
0.104
Treynor Ratio
-89.635
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
SPXW YNOF8MEQCQRY|SPX 31
Portfolio Turnover
0.02%
#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, 1, 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))
                
                # 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)
        ).data_frame

        # Filter chain by expiry
        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