Overall Statistics
Total Trades
504
Average Win
0.19%
Average Loss
-0.03%
Compounding Annual Return
21.212%
Drawdown
1.600%
Expectancy
2.519
Net Profit
22.476%
Sharpe Ratio
5.165
Probabilistic Sharpe Ratio
100.000%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
6.09
Alpha
0.143
Beta
0
Annual Standard Deviation
0.028
Annual Variance
0.001
Information Ratio
1.138
Tracking Error
0.201
Treynor Ratio
470.747
Total Fees
$0.00
Estimated Strategy Capacity
$1000.00
Lowest Capacity Asset
SPXW 3244WIPK7V90U|SPX 31
from AlgorithmImports import *
from collections import deque
import statistics as stats
import config



class PremiumThresholdAdjuster():



    def __init__(self, algorithm):
        self.algorithm = algorithm


        self.Avg_Premium_Length = config.PREMIUM_ADJUST_LENGTH


        self.Avg_Premium_1_Queue = deque(maxlen=self.Avg_Premium_Length)
        self.Avg_Premium_15_Queue = deque(maxlen=self.Avg_Premium_Length)
        self.Avg_Premium_20_Queue = deque(maxlen=self.Avg_Premium_Length)
        self.Avg_Premium_25_Queue = deque(maxlen=self.Avg_Premium_Length)
        self.Avg_Premium_30_Queue = deque(maxlen=self.Avg_Premium_Length)
        
        self.Avg_Premium_1 = None
        self.Avg_Premium_15 = None
        self.Avg_Premium_20 = None
        self.Avg_Premium_25 = None
        self.Avg_Premium_30 = None

        self.One_Premium = None
        self.One_Five_Premium = None
        self.Two_Premium = None
        self.Two_Five_Premium = None
        self.Three_Premium = None



    def receive_condors(self, c_1, c_1_5, c_2, c_2_5, c_3):
        self.One_Premium = c_1
        self.One_Five_Premium = c_1_5
        self.Two_Premium = c_2
        self.Two_Five_Premium = c_2_5
        self.Three_Premium = c_3




    def return_premium_today(self, range_today):
        if range_today == 0.01:
            if self.One_Premium is not None:
                return self.One_Premium
            else:
                return config.FIXED_PREMIUM_1
        elif range_today == 0.015:
            if self.One_Five_Premium is not None:
                return self.One_Five_Premium
            else:
                return config.FIXED_PREMIUM_1_5
        elif range_today == 0.02:
            if self.Two_Premium is not None:
                return self.Two_Premium
            else:
                return config.FIXED_PREMIUM_2
        elif range_today == 0.025:
            if self.Two_Five_Premium is not None:
                return self.Two_Five_Premium
            else:
                return config.FIXED_PREMIUM_2_5
        elif range_today == 0.03:
            if self.Three_Premium is not None:
                return self.Three_Premium
            else:
                return config.FIXED_PREMIUM_3





    def Update_Highest_Premiums(self):
        if self.Two_Premium is not None:
            premium = self.Two_Premium.Calculate_Premium()

            if premium > self.Two_Premium.Highest_Premium:
                self.Two_Premium.Highest_Premium = premium
        
        if self.Two_Five_Premium is not None:
            premium = self.Two_Five_Premium.Calculate_Premium()

            if premium > self.Two_Five_Premium.Highest_Premium:
                self.Two_Five_Premium.Highest_Premium = premium
        
        if self.One_Five_Premium is not None:
            premium = self.One_Five_Premium.Calculate_Premium()

            if premium > self.One_Five_Premium.Highest_Premium:
                self.One_Five_Premium.Highest_Premium = premium
        
        if self.Three_Premium is not None:
            premium = self.Three_Premium.Calculate_Premium()

            if premium > self.Three_Premium.Highest_Premium:
                self.Three_Premium.Highest_Premium = premium



    def Append_1(self, premium):
        self.Avg_Premium_1_Queue.appendleft(premium)
        if len(self.Avg_Premium_1_Queue) == self.Avg_Premium_Length:
            self.Avg_Premium_1 = sum(self.Avg_Premium_1_Queue) / len(self.Avg_Premium_1_Queue)
            self.algorithm.Plot("Average Premium 1%", "Premium", self.Avg_Premium_1)

    def Append_15(self, premium):
        self.Avg_Premium_15_Queue.appendleft(premium)
        if len(self.Avg_Premium_15_Queue) == self.Avg_Premium_Length:
            self.Avg_Premium_15 = sum(self.Avg_Premium_15_Queue) / len(self.Avg_Premium_15_Queue)
            self.algorithm.Plot("Average Premium 1.5%", "Premium", self.Avg_Premium_15)

    def Append_20(self, premium):
        self.Avg_Premium_20_Queue.appendleft(premium)
        if len(self.Avg_Premium_20_Queue) == self.Avg_Premium_Length:
            self.Avg_Premium_20 = sum(self.Avg_Premium_20_Queue) / len(self.Avg_Premium_20_Queue)
            self.algorithm.Plot("Average Premium 2%", "Premium", self.Avg_Premium_20)

    def Append_25(self, premium):
        self.Avg_Premium_25_Queue.appendleft(premium)
        if len(self.Avg_Premium_25_Queue) == self.Avg_Premium_Length:
            self.Avg_Premium_25 = sum(self.Avg_Premium_25_Queue) / len(self.Avg_Premium_25_Queue)
            self.algorithm.Plot("Average Premium 2.5%", "Premium", self.Avg_Premium_25)

    def Append_30(self, premium):
        self.Avg_Premium_30_Queue.appendleft(premium)
        if len(self.Avg_Premium_30_Queue) == self.Avg_Premium_Length:
            self.Avg_Premium_30 = sum(self.Avg_Premium_30_Queue) / len(self.Avg_Premium_30_Queue)
            self.algorithm.Plot("Average Premium 3%", "Premium", self.Avg_Premium_30)

from AlgorithmImports import *
from collections import deque
import config




class AvgDown():



    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol


        self.use_avg_down = config.USE_AVG_DOWN
        self.avg_down_mode = config.AVG_DOWN_MODE
        self.fixed_avg_down_premium = config.FIXED_AVG_DOWN_PREMIUM
        self.avg_down_percentage = config.AVG_DOWN_PERCENTAGE
        self.avg_down_x_times = config.AVG_DOWN_X_TIMES
        self.use_most_recent_entry = config.USE_MOST_RECENT_ENTRY

        self.pivot_queue = deque(maxlen=3)

        self.entered_condor = None
        self.entered_key = None
        self.avg_down_counter = 0
        self.first_entry = None


    def append_pivot(self):
        if self.entered_condor is not None:
            self.entered_condor.Calculate_Premium()
            self.pivot_queue.appendleft(self.entered_condor.Premium)



    def check_avg_down(self):
        if self.use_avg_down and self.avg_down_counter < self.avg_down_x_times:
            if self.avg_down_mode == "PERCENTAGE":
                if self.check_percentage_avg_down():
                    return True
                else:
                    return False
            elif self.avg_down_mode == "PIVOT POINT":
                if self.check_pivot_avg_down():
                    return True
                else:
                    return False
            elif self.avg_down_mode == "PERCENTAGE PIVOT POINT":
                if self.check_percentage_avg_down() and self.check_pivot_avg_down():
                    return True
                else:
                    return False
            elif self.avg_down_mode == "FIXED":
                if self.entered_condor.Premium >= self.fixed_avg_down_premium:
                    return True
                else:
                    return False
            elif self.avg_down_mode == "FIXED PIVOT POINT":
                if self.entered_condor.Premium >= self.fixed_avg_down_premium and self.check_pivot_avg_down():
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False
    


    def check_percentage_avg_down(self):
        if not self.use_most_recent_entry:
            self.entered_condor.Calculate_Premium()
            if self.entered_condor.Premium >= self.first_entry * self.avg_down_percentage:
                return True
            else:
                return False
        else:
            self.entered_condor.Calculate_Premium()
            if self.entered_condor.Premium >= self.entered_condor.Entry_Premium * self.avg_down_percentage:
                return True
            else:
                return False

    


    def check_pivot_avg_down(self):
        if len(self.pivot_queue) == 3 and self.pivot_queue[0] < self.pivot_queue[1] > self.pivot_queue[2]:
            return True
        else:
            return False
    


    def reset(self):
        self.use_avg_down = config.USE_AVG_DOWN
        self.avg_down_mode = config.AVG_DOWN_MODE
        self.fixed_avg_down_premium = config.FIXED_AVG_DOWN_PREMIUM
        self.avg_down_percentage = config.AVG_DOWN_PERCENTAGE
        self.avg_down_x_times = config.AVG_DOWN_X_TIMES
        self.use_most_recent_entry = config.USE_MOST_RECENT_ENTRY

        self.pivot_queue = deque(maxlen=3)

        self.entered_condor = None
        self.entered_key = None
        self.avg_down_counter = 0
        self.first_entry = None
#region imports
from AlgorithmImports import *





USE_POWELL = True

POWELL = [
                #datetime(2021, 1, 14).strftime("%Y%-m%-d"), #Powell 12:30 #
                #datetime(2021, 2, 10).strftime("%Y%-m%-d"), #Powell 14:00 #
                datetime(2021, 2, 23).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2021, 2, 24).strftime("%Y%-m%-d"), #Powell 10:00
                #datetime(2021, 3, 4).strftime("%Y%-m%-d"), #Powell 12:05  #
                datetime(2021, 3, 22).strftime("%Y%-m%-d"), #Powell 08:00
                datetime(2021, 3, 23).strftime("%Y%-m%-d"), #Powell 11:00
                datetime(2021, 3, 24).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2021, 4, 8).strftime("%Y%-m%-d"), #Powell 11:00
                datetime(2021, 4, 14).strftime("%Y%-m%-d"), #Powell 11:00
                #datetime(2021, 5, 3).strftime("%Y%-m%-d"), #Powell 13:20 #
                datetime(2021, 6, 4).strftime("%Y%-m%-d"), #Powell 06:00
                #datetime(2021, 6, 22).strftime("%Y%-m%-d"), #Powell 13:00 #
                datetime(2021, 7, 14).strftime("%Y%-m%-d"), #Powell 11:00
                datetime(2021, 7, 15).strftime("%Y%-m%-d"), #Powell 08:30
                #datetime(2021, 8, 17).strftime("%Y%-m%-d"), #Powell 12:30 #
                datetime(2021, 8, 27).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2021, 9, 24).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2021, 9, 28).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2021, 9, 29).strftime("%Y%-m%-d"), #Powell 09:45
                datetime(2021, 10, 22).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2021, 11, 8).strftime("%Y%-m%-d"), #Powell 10:30
                datetime(2021, 11, 9).strftime("%Y%-m%-d"), #Powell 9:00
               # datetime(2021, 11, 29).strftime("%Y%-m%-d"), #Powell 15:05 #
                datetime(2021, 11, 30).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2021, 12, 1).strftime("%Y%-m%-d"), #Powell 10:00
                
                datetime(2022, 1, 11).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2022, 3, 2).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2022, 3, 3).strftime("%Y%-m%-d"), #Powell 10:00
                datetime(2022, 3, 21).strftime("%Y%-m%-d"), #Powell 11:00
                datetime(2022, 3, 23).strftime("%Y%-m%-d"), #Powell 07:00
                datetime(2022, 4, 21).strftime("%Y%-m%-d"), #Powell 10:00
               # datetime(2022, 5, 17).strftime("%Y%-m%-d"), #Powell 13:00 #
                datetime(2022, 5, 24).strftime("%Y%-m%-d"), #Powell 11:20
                datetime(2022, 6, 17).strftime("%Y%-m%-d"), #Powell 07:45
                datetime(2022, 6, 22).strftime("%Y%-m%-d"), #Powell 08:30
                datetime(2022, 6, 23).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2022, 6, 29).strftime("%Y%-m%-d"), #Powell 08:00
                datetime(2022, 8 ,26).strftime("%Y%-m%-d"), #Powell 09:00
                datetime(2022, 9, 8).strftime("%Y%-m%-d"), #Powell 08:10
               # datetime(2022, 9, 23).strftime("%Y%-m%-d"), #Powell 13:00 #
                datetime(2022, 9, 27).strftime("%Y%-m%-d"), #Powell 06:30
                datetime(2022, 9, 28).strftime("%Y%-m%-d"), #Powell 09:15
                #datetime(2022, 11, 30).strftime("%Y%-m%-d"), #Powell 13:30 #
                ]

USE_POWELL_NEXTDAY = True
POWELL_NEXTDAY = [
                datetime(2021, 1, 15).strftime("%Y%-m%-d"), #DayAfterPowell
                datetime(2021, 2, 11).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 3, 5).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 3, 25).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 4, 9).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 4, 15).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 5, 4).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 6, 5).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 6, 23).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 7, 16).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 9, 30).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 11, 10).strftime("%Y%-m%-d"), #DAP
                datetime(2021, 12, 2).strftime("%Y%-m%-d"), #DAP

                datetime(2022, 1, 12).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 3, 4).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 3, 22).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 3, 24).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 4, 22).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 5, 17).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 5, 25).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 6, 24).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 8, 27).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 9, 9).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 9, 29).strftime("%Y%-m%-d"), #DAP
                datetime(2022, 12, 1).strftime("%Y%-m%-d"), #DAP
                ]

USE_FED_INTEREST_RATE_DECISION = False
FED_INTEREST_RATE_DECISION = [
                datetime(2021, 1, 27).strftime("%Y%-m%-d"),
                datetime(2021, 3, 17).strftime("%Y%-m%-d"),
                datetime(2021, 4, 28).strftime("%Y%-m%-d"),
                datetime(2021, 6, 16).strftime("%Y%-m%-d"),
                datetime(2021, 7, 28).strftime("%Y%-m%-d"),
                datetime(2021, 9, 22).strftime("%Y%-m%-d"),
                datetime(2021, 11, 3).strftime("%Y%-m%-d"),
                datetime(2021, 12, 15).strftime("%Y%-m%-d"),

                datetime(2022, 1, 26).strftime("%Y%-m%-d"),
                datetime(2022, 3, 16).strftime("%Y%-m%-d"),
                datetime(2022, 5, 4).strftime("%Y%-m%-d"),
                datetime(2022, 6, 15).strftime("%Y%-m%-d"),
                datetime(2022, 7, 27).strftime("%Y%-m%-d"),
                datetime(2022, 9, 21).strftime("%Y%-m%-d"),
                datetime(2022, 11, 2).strftime("%Y%-m%-d"),
                datetime(2022, 12, 14).strftime("%Y%-m%-d"),
                ]





USE_FED_INTEREST_RATE_DECISION_NEXTDAY = False
FED_INTEREST_RATE_DECISION_NEXTDAY = [
                datetime(2021, 1, 28).strftime("%Y%-m%-d"),
                datetime(2021, 3, 18).strftime("%Y%-m%-d"),
                datetime(2021, 4, 29).strftime("%Y%-m%-d"),
                datetime(2021, 6, 17).strftime("%Y%-m%-d"),
                datetime(2021, 7, 29).strftime("%Y%-m%-d"),
                datetime(2021, 9, 23).strftime("%Y%-m%-d"),
                datetime(2021, 11, 4).strftime("%Y%-m%-d"),
                datetime(2021, 12, 16).strftime("%Y%-m%-d"),

                datetime(2022, 1, 27).strftime("%Y%-m%-d"),
                datetime(2022, 3, 17).strftime("%Y%-m%-d"),
                datetime(2022, 5, 5).strftime("%Y%-m%-d"),
                datetime(2022, 6, 17).strftime("%Y%-m%-d"),
                datetime(2022, 7, 28).strftime("%Y%-m%-d"),
                datetime(2022, 9, 22).strftime("%Y%-m%-d"),
                datetime(2022, 11, 3).strftime("%Y%-m%-d"),
                datetime(2022, 12, 15).strftime("%Y%-m%-d"),
                ]

USE_FOMC_MEETINGS_FIRSTDAY = False
FOMC_MEETINGS_FIRSTDAY = [
                datetime(2021, 1, 26).strftime("%Y%-m%-d"),
                datetime(2021, 3, 16).strftime("%Y%-m%-d"),
                datetime(2021, 4, 27).strftime("%Y%-m%-d"),
                datetime(2021, 6, 16).strftime("%Y%-m%-d"),
                datetime(2021, 7, 27).strftime("%Y%-m%-d"),
                datetime(2021, 9, 21).strftime("%Y%-m%-d"),
                datetime(2021, 11, 2).strftime("%Y%-m%-d"),
                datetime(2021, 12, 14).strftime("%Y%-m%-d"),

                datetime(2022, 1, 25).strftime("%Y%-m%-d"),
                datetime(2022, 3, 15).strftime("%Y%-m%-d"),
                datetime(2022, 5, 3).strftime("%Y%-m%-d"),
                datetime(2022, 6, 14).strftime("%Y%-m%-d"),
                datetime(2022, 7, 26).strftime("%Y%-m%-d"),
                datetime(2022, 9, 20).strftime("%Y%-m%-d"),
                datetime(2022, 11, 1).strftime("%Y%-m%-d"),
                datetime(2022, 12, 13).strftime("%Y%-m%-d"),
]

USE_FOMC_MEETINGS = True
FOMC_MEETINGS = [

                datetime(2021, 1, 27).strftime("%Y%-m%-d"),
                datetime(2021, 3, 17).strftime("%Y%-m%-d"),
                datetime(2021, 4, 28).strftime("%Y%-m%-d"),
                datetime(2021, 6, 15).strftime("%Y%-m%-d"),
                datetime(2021, 7, 28).strftime("%Y%-m%-d"),
                datetime(2021, 9, 22).strftime("%Y%-m%-d"),
                datetime(2021, 11, 3).strftime("%Y%-m%-d"),
                datetime(2021, 12, 15).strftime("%Y%-m%-d"),

                datetime(2022, 1, 26).strftime("%Y%-m%-d"),
                datetime(2022, 3, 16).strftime("%Y%-m%-d"),
                datetime(2022, 5, 4).strftime("%Y%-m%-d"),
                datetime(2022, 6, 15).strftime("%Y%-m%-d"),
                datetime(2022, 7, 27).strftime("%Y%-m%-d"),
                datetime(2022, 9 ,21).strftime("%Y%-m%-d"),
                datetime(2022, 11, 2).strftime("%Y%-m%-d"),
                datetime(2022, 12, 14).strftime("%Y%-m%-d"),
                ]





USE_FOMC_MINUTES = False
FOMC_MINUTES = [
                datetime(2021, 1 ,6).strftime("%Y%-m%-d"),
                datetime(2021, 2 ,17).strftime("%Y%-m%-d"),
                datetime(2021, 4 ,7).strftime("%Y%-m%-d"),
                datetime(2021, 5 ,19).strftime("%Y%-m%-d"),
                datetime(2021, 7 ,7).strftime("%Y%-m%-d"),
                datetime(2021, 8 ,18).strftime("%Y%-m%-d"),
                datetime(2021, 10 ,13).strftime("%Y%-m%-d"),
                datetime(2021, 11 ,24).strftime("%Y%-m%-d"),

                datetime(2022, 1 ,5).strftime("%Y%-m%-d"),
                datetime(2022, 2 ,16).strftime("%Y%-m%-d"),
                datetime(2022, 4, 6).strftime("%Y%-m%-d"),
                datetime(2022, 5, 25).strftime("%Y%-m%-d"),
                datetime(2022, 7, 6).strftime("%Y%-m%-d"),
                datetime(2022, 8, 17).strftime("%Y%-m%-d"),
                datetime(2022, 10, 12).strftime("%Y%-m%-d"),
                datetime(2022, 11, 23).strftime("%Y%-m%-d"),
                ]
                



USE_CPI = True
CPI =          [
                datetime(2021, 1, 13).strftime("%Y%-m%-d"),
                datetime(2021, 2, 10).strftime("%Y%-m%-d"),
                datetime(2021, 3, 10).strftime("%Y%-m%-d"),
                datetime(2021, 4, 13).strftime("%Y%-m%-d"),
                datetime(2021, 5, 12).strftime("%Y%-m%-d"),
                datetime(2021, 6, 10).strftime("%Y%-m%-d"),
                datetime(2021, 7, 13).strftime("%Y%-m%-d"),
                datetime(2021, 8, 11).strftime("%Y%-m%-d"),
                datetime(2021, 9, 14).strftime("%Y%-m%-d"),
                datetime(2021, 10, 13).strftime("%Y%-m%-d"),
                datetime(2021, 11, 10).strftime("%Y%-m%-d"),
                datetime(2021, 12, 10).strftime("%Y%-m%-d"),
                
                datetime(2022, 1, 12).strftime("%Y%-m%-d"),
                datetime(2022, 2, 10).strftime("%Y%-m%-d"),
                datetime(2022, 3, 10).strftime("%Y%-m%-d"),
                datetime(2022, 4, 12).strftime("%Y%-m%-d"),
                datetime(2022, 5, 11).strftime("%Y%-m%-d"),
                datetime(2022, 6, 10).strftime("%Y%-m%-d"),
                datetime(2022, 7, 13).strftime("%Y%-m%-d"),
                datetime(2022, 8, 10).strftime("%Y%-m%-d"),
                datetime(2022, 9, 13).strftime("%Y%-m%-d"),
                datetime(2022, 10, 13).strftime("%Y%-m%-d"),
                datetime(2022, 11, 10).strftime("%Y%-m%-d"),
                datetime(2022, 12, 13).strftime("%Y%-m%-d"),

                ]

USE_CPI_NEXTDAY = True
CPI_NEXTDAY =   [
                datetime(2021, 1, 14).strftime("%Y%-m%-d"),
                datetime(2021, 2, 11).strftime("%Y%-m%-d"),
                datetime(2021, 3, 11).strftime("%Y%-m%-d"),
                datetime(2021, 4, 14).strftime("%Y%-m%-d"),
                datetime(2021, 5, 13).strftime("%Y%-m%-d"),
                datetime(2021, 6, 11).strftime("%Y%-m%-d"),
                datetime(2021, 7, 14).strftime("%Y%-m%-d"),
                datetime(2021, 8, 12).strftime("%Y%-m%-d"),
                datetime(2021, 9, 15).strftime("%Y%-m%-d"),
                datetime(2021, 10, 14).strftime("%Y%-m%-d"),
                datetime(2021, 11, 11).strftime("%Y%-m%-d"),
                datetime(2021, 12, 11).strftime("%Y%-m%-d"),

                datetime(2022, 1, 13).strftime("%Y%-m%-d"),
                datetime(2022, 2, 11).strftime("%Y%-m%-d"),
                datetime(2022, 3, 11).strftime("%Y%-m%-d"),
                datetime(2022, 4, 13).strftime("%Y%-m%-d"),
                datetime(2022, 5, 12).strftime("%Y%-m%-d"),
                datetime(2022, 6, 11).strftime("%Y%-m%-d"),
                datetime(2022, 7, 14).strftime("%Y%-m%-d"),
                datetime(2022, 8, 11).strftime("%Y%-m%-d"),
                datetime(2022, 9, 14).strftime("%Y%-m%-d"),
                datetime(2022, 10, 14).strftime("%Y%-m%-d"),
                datetime(2022, 11, 11).strftime("%Y%-m%-d"),
                datetime(2022, 12, 14).strftime("%Y%-m%-d"),
                ]



DISABLE_SPX_WRONG_DATES = True
SPX_WRONG_DATES = [
                datetime(2021, 3, 23).strftime("%Y%-m%-d"),
                datetime(2021, 3, 24).strftime("%Y%-m%-d"),
                datetime(2021, 5, 10).strftime("%Y%-m%-d"),
                datetime(2021, 5, 11).strftime("%Y%-m%-d"),
                datetime(2021, 8, 19).strftime("%Y%-m%-d"),
                datetime(2021, 8, 20).strftime("%Y%-m%-d"),
                datetime(2021, 12, 3).strftime("%Y%-m%-d"),
                datetime(2022, 2, 25).strftime("%Y%-m%-d"),
                datetime(2022, 3, 4).strftime("%Y%-m%-d"),
                datetime(2022, 5, 6).strftime("%Y%-m%-d"),
                datetime(2022, 9, 30).strftime("%Y%-m%-d"),
                datetime(2023, 1, 26).strftime("%Y%-m%-d"),
                ]



DISABLE_SPX_POTENTIALLY_WRONG_DATES = True
SPX_POTENTIALLY_WRONG_DATES = [
                datetime(2019, 8, 12).strftime("%Y%-m%-d"),
                datetime(2019, 11, 29).strftime("%Y%-m%-d"),
                datetime(2021, 11, 26).strftime("%Y%-m%-d"),
                datetime(2022, 7, 19).strftime("%Y%-m%-d"),
                datetime(2022, 7, 27).strftime("%Y%-m%-d"),
                datetime(2022, 7, 28).strftime("%Y%-m%-d"),
                datetime(2022, 8, 22).strftime("%Y%-m%-d"),
                datetime(2022, 9, 2).strftime("%Y%-m%-d"),
                datetime(2022, 9, 6).strftime("%Y%-m%-d"),
                datetime(2022, 9, 22).strftime("%Y%-m%-d"),
                datetime(2022, 9, 23).strftime("%Y%-m%-d"),
                datetime(2022, 9, 26).strftime("%Y%-m%-d"),
                datetime(2022, 9, 27).strftime("%Y%-m%-d"),
                datetime(2022, 9, 28).strftime("%Y%-m%-d"),
                datetime(2022, 9, 29).strftime("%Y%-m%-d"),
                datetime(2022, 10, 14).strftime("%Y%-m%-d"),
                datetime(2022, 10, 18).strftime("%Y%-m%-d"),
                datetime(2022, 10, 25).strftime("%Y%-m%-d"),
                datetime(2022, 10, 27).strftime("%Y%-m%-d"),
                datetime(2022, 11, 10).strftime("%Y%-m%-d"),
                datetime(2022, 11, 17).strftime("%Y%-m%-d"),
                datetime(2022, 11, 18).strftime("%Y%-m%-d"),
                datetime(2022, 11, 30).strftime("%Y%-m%-d"),
                datetime(2022, 12, 15).strftime("%Y%-m%-d"),
                datetime(2022, 12, 16).strftime("%Y%-m%-d"),
                ]
from AlgorithmImports import *
import calendar_info






class CheckCalendar():



    def __init__(self, algorithm, close_trading_window, open_window, close_window):
        self.algorithm = algorithm


        
        # POWELL

        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 1, 14), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 2, 10), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 3, 4), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 5, 3), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 6, 22), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 8, 17), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 11, 29), self.algorithm.TimeRules.At(12,0), close_trading_window)

        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 5, 17), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 9, 23), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 11, 30), self.algorithm.TimeRules.At(12,0), close_trading_window)


        # FED INTEREST RATE


        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 1, 27), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 3, 17), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 4, 28), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 6, 16), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 7, 28), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 9, 22), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 11, 3), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 12, 15), self.algorithm.TimeRules.At(12,0), close_trading_window)

        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 1, 26), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 3, 16), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 5, 4), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 6, 15), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 7, 27), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 9, 21), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 11, 2), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 12, 4), self.algorithm.TimeRules.At(12,0), close_trading_window)
        


        # FOMC MINUTES

        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 1, 6), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 2, 17), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 4, 7), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 5, 19), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 7, 7), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 8, 18), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 10, 13), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2021, 11, 24), self.algorithm.TimeRules.At(12,0), close_trading_window)

        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 1, 5), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 2, 16), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 4, 6), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 5, 25), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 7, 6), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 8, 17), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 10, 12), self.algorithm.TimeRules.At(12,0), close_trading_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.On(2022, 11, 23), self.algorithm.TimeRules.At(12,0), close_trading_window)



        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Friday), self.algorithm.TimeRules.At(9, 31), open_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Friday), self.algorithm.TimeRules.At(15, 59), close_window)


        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Monday), self.algorithm.TimeRules.At(9, 31), open_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Monday), self.algorithm.TimeRules.At(15, 59), close_window)

        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Wednesday), self.algorithm.TimeRules.At(9, 31), open_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Wednesday), self.algorithm.TimeRules.At(15, 59), close_window)
    
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Tuesday), self.algorithm.TimeRules.At(9, 31), open_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Tuesday), self.algorithm.TimeRules.At(15, 59), close_window)

        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Thursday), self.algorithm.TimeRules.At(9, 31), open_window)
        self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Thursday), self.algorithm.TimeRules.At(15, 59), close_window)

    def check_calendar(self):
        if calendar_info.USE_FOMC_MINUTES:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.FOMC_MINUTES:
                return True
        if calendar_info.USE_FOMC_MEETINGS:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.FOMC_MEETINGS:
                return True
        if calendar_info.USE_CPI:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.CPI:
                return True
        if calendar_info.USE_CPI_NEXTDAY:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.CPI_NEXTDAY:
                return True
        if calendar_info.USE_POWELL:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.POWELL:
                return True
        if calendar_info.USE_POWELL_NEXTDAY:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.POWELL_NEXTDAY:
                return True
        if calendar_info.USE_FED_INTEREST_RATE_DECISION:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.FED_INTEREST_RATE_DECISION:
                return True
        if calendar_info.USE_FED_INTEREST_RATE_DECISION_NEXTDAY:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.FED_INTEREST_RATE_DECISION_NEXTDAY:
                return True    
        if calendar_info.USE_FOMC_MEETINGS_FIRSTDAY:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.FOMC_MEETINGS_FIRSTDAY:
                return True
        if calendar_info.DISABLE_SPX_WRONG_DATES:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.SPX_WRONG_DATES:
                return True
        if calendar_info.DISABLE_SPX_POTENTIALLY_WRONG_DATES:
            if self.algorithm.Time.strftime("%Y%-m%-d") in calendar_info.SPX_POTENTIALLY_WRONG_DATES:
                return True
        return False
#region imports
from AlgorithmImports import *
from datetime import *


data_dictionary = {}
QUANTITY = 20







##################################################################  RE ENTRY  ########################################################################

USE_RE_ENTRY = False





# Pick between "SAME CONDOR" and "ANY CONDOR"
# SAME CONDOR will make it so that only the previously invested Iron Condor is Re entered,
# ANY CONDOR will re enter any Iron Condor
RE_ENTRY_MODE = "SAME CONDOR"


# Pick between "STATIC" and "ENTRY MULTIPLIER"
# STATIC is a static threshold,
# ENTRY MULTIPLIER multiplies the previous entry premium and sets that as the new entry premium for the re-entry
RE_ENTRY_THRESHOLD_MODE = "ENTRY MULTIPLIER"

# ENTRY MULTIPLIER multiplies the previous entry premium and sets that as the new entry premium for the re-entry
RE_ENTRY_MULTIPLIER = 1.33

# STATIC is a static threshold
RE_ENTRY_STATIC_PREMIUM = 4.00

RE_ENTRY_X_TIMES = 2



##################################################################  AVG DOWN  ########################################################################

USE_AVG_DOWN = False



AVG_DOWN_QUANTITY = 5



# Pick between "PERCENTAGE" or "PIVOT POINT" or "PERCENTAGE PIVOT POINT" or "FIXED" or "FIXED PIVOT POINT"
AVG_DOWN_MODE = "PERCENTAGE PIVOT POINT"

# True or False,
# If True the most recent entry price of the condor will be used to calculate the AVG_DOWN_PERCENTAGE, otherwise the first entry of the day
USE_MOST_RECENT_ENTRY = False


# Fixed premium threshold at which we will average down
FIXED_AVG_DOWN_PREMIUM = 6.00

# Percentage that premium needs to be higher than entry in order to average down,
# 1.4 here would multiply the last entry premium by 1.4
AVG_DOWN_PERCENTAGE = 1.50

# The maximum amount of times averaging down is allowed per day
AVG_DOWN_X_TIMES = 1


##################################################################  SL AT ENTRY  ########################################################################

USE_SL_AT_ENTRY = False

# Profit Percentage that needs to be reached before allowing a stop loss at entry premium
SL_AT_ENTRY_THRESHOLD = 75



# Pick between "FIRST ENTRY" or "MOST RECENT ENTRY" or "AVG ENTRY"
# "FIRST ENTRY" will use the first entry to calculate the Profit Percentage,
# "MOST RECENT ENTRY" will use the most recent entry to calculate the Profit Percentage,
# "AVG ENTRY" will calculate the total average entry price and use that to calculate the Profit Percentage
SL_AT_ENTRY_MODE = "FIRST ENTRY"







##################################################################  STOP LOSS  ########################################################################

# Pick between True or False
USE_STOP_LOSS = False

# Pick between "FIRST ENTRY" or "MOST RECENT ENTRY" or "AVG ENTRY"
# "FIRST ENTRY" will use the first entry to calculate the Stop Percentage,
# "MOST RECENT ENTRY" will use the most recent entry to calculate the Stop Percentage,
# "AVG ENTRY" will calculate the total average entry price and use that to calculate the Stop Percentage
STOP_LOSS_MODE = "FIRST ENTRY"


# Multiplier applied to entry premium
STOP_LOSS_MULTIPLIER = 10


# Pick between True or False
USE_ABSOLUTE_STOP_LOSS = True

# Premium above entry at which position will be closed
ABSOLUTE_STOP_LOSS = 30




##################################################################  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"

# Fixed Minimum Distance the Iron Condor needs to have from opening price of the day
MINIMUM_DISTANCE = 1.5

# Fixed Maximum Distance the Iron Condor is allowed to have from opening price of the day
# Need a maximum here since there will not always be an Iron Condor at an exact percentage, recommended for this to be 0.3% higher than,
# the minimum above
MAXIMUM_DISTANCE = 1.8

# Minimum Strike Difference 
MINIMUM_STRIKE_DIFFERENCE = 5

# Maximum Strike Difference 
MAXIMUM_STRIKE_DIFFERENCE = 60





# 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.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


##################################################################  IRON CONDOR SETTINGS PREMIUM  ########################################################################


# Pick between "FIXED" or "ADJUSTED PREMIUM" or "FIXED PER RANGE"
# "FIXED" selects the same fixed minimum premium for every range,
# "ADJUSTED PREMIUM" uses the premium adjuster to automatically select the minimum premium for any range
# "FIXED PER RANGE" selects a different fixed minimum premium per range
PREMIUM_THRESHOLD_MODE = "FIXED"


# Lookback Length for the Average Premium that the Premium Adjuster Uses
PREMIUM_ADJUST_LENGTH = 10

# Fixed Premium for all Ranges
FIXED_PREMIUM = 0.70

# Fixed Premium if the range is 1%
FIXED_PREMIUM_1 = 2.5

# Fixed Premium if the range is 1.5%
FIXED_PREMIUM_1_5 = 2.50

# Fixed Premium if the range is 2%
FIXED_PREMIUM_2 = 2.50

# Fixed Premium if the range is 2.5%
FIXED_PREMIUM_2_5 = 2.50

# Fixed Premium if the range is 3%
FIXED_PREMIUM_3 = 2.50



##################################################################  OTHER EXIT SETTINGS  ########################################################################


# Pick between "FIRST ENTRY" or "MOST RECENT ENTRY" or "AVG ENTRY"
# "FIRST ENTRY" will use the first entry to calculate the Profit Percentage,
# "MOST RECENT ENTRY" will use the most recent entry to calculate the Profit Percentage,
# "AVG ENTRY" will calculate the total average entry price and use that to calculate the Profit Percentage
PROFIT_TAKING_MODE = "FIRST ENTRY"

# Pick between True or False
# This enables/disables the regular profit taking
USE_REGULAR_PROFIT_TAKE = True

# Profit percentage for regular profit taking, 50 = 50% profit
REGULAR_PROFIT_PERCENTAGE = 90

# Pick between True or False
# If within X Minutes after entry there is atleast X% of Profit then exit
USE_TIMED_PROFIT_EXIT = True

# If within X Minutes after entry there is atleast X% of Profit then exit
MINUTES_AFTER_ENTRY = timedelta(minutes=60)

# If within X Minutes after entry there is atleast X% of Profit then exit
TIMED_PROFIT_PERCENTAGE = 50

# Pick between True or False
# A simple timed exit in minutes after entry or fixed time of day,
USE_TIMED_EXIT = False

# Pick between "SIMPLE" or "FIXED TIME" 
# "SIMPLE" exits the position after X minutes regardless of time of day, profit/loss etc.
# "FIXED TIME" exits the position at a fixed time of day regardless of how many minutes after entry, profit/loss etc.
TIMED_EXIT_MODE = "SIMPLE"

# "SIMPLE" exits the position after X minutes regardless of time of day, profit/loss etc.
TIMED_EXIT_MINUTES = timedelta(minutes=50)

# "FIXED TIME" exits the position at a fixed time of day regardless of how many minutes after entry, profit/loss etc.
TIMED_EXIT_FIXED = time(hour=12, minute=0)
from AlgorithmImports import *
from iron_condor import IronCondor
import config
from itertools import product



class CreateCondors():



    def __init__(self, algorithm):
        self.algorithm = algorithm
        

        
    




    def populate_lists(self, optionschain, symbol, symbolData, open_price, min_range, max_range): 
        put_list = []
        call_list = []  
        if optionschain is None:
            return
        #for i in optionschain:
            #if i.Key != symbolData.option_symbol: continue
           # optionchain = i.Value
      
            
            
            #if (optionschain.Count == 0):
                #return 


        price = self.algorithm.Securities[symbol].Price
        price = math.floor(price)


        if open_price is None:
            open_price = self.algorithm.Securities[symbol].Open



        #self.algorithm.Debug(f"{self.algorithm.Time} {open_price}")
    
        List_Of_Distances = []
        put_3_expiry = [x for x in optionschain if x.Right == OptionRight.Put and ((x.Expiry - self.algorithm.Time).days) == -1 and (x.Strike - price) < 1]
        call_3_expiry =  [x for x in optionschain if x.Right == OptionRight.Call and ((x.Expiry - self.algorithm.Time).days) == -1 and (price - x.Strike) < 1]
        


       

    

        put_3_min_distances_sorted_sell = sorted(put_3_expiry, key=lambda x: x.Strike, reverse=True)
        call_3_min_distances_sorted_sell = sorted(call_3_expiry, key=lambda x: x.Strike, reverse=False)

        put_3_min_distances_sorted_buy = sorted(put_3_expiry, key=lambda x: x.Strike, reverse=True)
        call_3_min_distances_sorted_buy = sorted(call_3_expiry, key=lambda x: x.Strike, reverse=False)
        
        
        #put_list = [(x, y) for x, y in product(put_3_min_distances_sorted_sell, put_3_min_distances_sorted_buy) if x.Strike > y.Strike and x.Strike - y.Strike >= config.MINIMUM_STRIKE_DIFFERENCE and x.Strike - y.Strike <= config.MAXIMUM_STRIKE_DIFFERENCE]
    
        for x in put_3_min_distances_sorted_sell:
            for y in put_3_min_distances_sorted_buy:
                strike_diff = x.Strike - y.Strike
                if x.Strike > y.Strike and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE:
                    open_put_range = abs((x.Strike - open_price) / open_price)
                    if open_put_range >= min_range and open_put_range <= max_range:
                        combo = (x, y)
                        put_list.append(combo)

        

        # for x in put_3_min_distances_sorted_sell:
        #     for y in put_3_min_distances_sorted_buy:
        #         strike_diff = x.Strike - y.Strike
        #         if x.Strike > y.Strike and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE:
        #             combo = (x, y)
        #             put_list.append(combo)
                    
        # put_list = [(x, y) for x in put_3_min_distances_sorted_sell for y in put_3_min_distances_sorted_buy if x.Strike > y.Strike and x.Strike - y.Strike >= config.MINIMUM_STRIKE_DIFFERENCE and x.Strike - y.Strike <= config.MAXIMUM_STRIKE_DIFFERENCE]   

        # call_list = [(x, y) for x in call_3_min_distances_sorted_sell for y in call_3_min_distances_sorted_buy if y.Strike > x.Strike and y.Strike - x.Strike >= config.MINIMUM_STRIKE_DIFFERENCE and y.Strike - x.Strike <= config.MAXIMUM_STRIKE_DIFFERENCE]

        #call_list = [(x, y) for x, y in product(call_3_min_distances_sorted_sell, call_3_min_distances_sorted_buy) if y.Strike > x.Strike and y.Strike - x.Strike >= config.MINIMUM_STRIKE_DIFFERENCE and y.Strike - x.Strike <= config.MAXIMUM_STRIKE_DIFFERENCE]

        for x in call_3_min_distances_sorted_sell:
            for y in call_3_min_distances_sorted_buy:
                strike_diff = y.Strike - x.Strike
                if y.Strike > x.Strike and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE:
                    open_call_range = ((x.Strike - open_price) / open_price)
                    if open_call_range >= min_range and open_call_range <= max_range:
                        combo = (x, y)
                        call_list.append(combo)



        # for x in call_3_min_distances_sorted_sell:
        #     for y in call_3_min_distances_sorted_buy:
        #         strike_diff = y.Strike - x.Strike
        #         if y.Strike > x.Strike and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE: 
        #             combo = (x, y)
        #             call_list.append(combo)


       
        return self.create_combinations(min_range, max_range, put_list, call_list, open_price)


    def create_combinations(self, min_range, max_range, put_list, call_list, open_price):
        keys = []
        condors = {}
        appended_1 = None
        appended_1_5 = None
        appended_2 = None
        appended_2_5 = None
        appended_3 = None
        for ps, pb in put_list:
            for cs, cb in call_list:
                p_diff = ps.Strike - pb.Strike
                c_diff = cb.Strike - cs.Strike
            
                if cs.Strike > ps.Strike and p_diff == c_diff and open_price is not None:
                    open_call_range = ((cs.Strike - open_price) / open_price)
                    open_put_range = abs((ps.Strike - open_price) / open_price)
                    Strikes = f"{pb.Strike} {ps.Strike} {cs.Strike} {cb.Strike} {open_call_range} {open_put_range} {p_diff}"
                    if Strikes not in condors:
                        condors[Strikes] = IronCondor(self.algorithm, pb, ps, cb, cs, open_call_range, open_put_range)
                        max_max = (max_range + 0.003)
                        if open_put_range >= min_range and open_call_range >= min_range and open_put_range <= max_max and open_call_range <= max_max:
                            diff = abs(open_call_range - open_put_range)
                            percent = (open_call_range + open_put_range) / 2
                            tup = (Strikes, percent, diff, p_diff)
                            keys.append(tup)
                        


                    if appended_2_5 is None and open_call_range >= 0.024 and open_call_range <= 0.027 and open_put_range >= 0.024 and open_put_range <= 0.027:
                        condor = condors[Strikes]
                        appended_2_5 = condor


                    

                    if appended_2 is None and open_call_range >= 0.02 and open_call_range <= 0.022 and open_put_range >= 0.02 and open_put_range <= 0.022:
                        condor = condors[Strikes]
                        appended_2 = condor


                    
                    if appended_3 is None and open_call_range >= 0.03 and open_call_range <= 0.032 and open_put_range >= 0.03 and open_put_range <= 0.032:
                        condor = condors[Strikes]
                        appended_3 = condor

                    


                    if appended_1_5 is None and open_call_range >= 0.015 and open_call_range <= 0.017 and open_put_range >= 0.015 and open_put_range <= 0.017:
                        condor = condors[Strikes]
                        appended_1_5 = condor
                    


                    if appended_1 is None and open_call_range >= 0.01 and open_call_range <= 0.013 and open_put_range >= 0.01 and open_put_range <= 0.013:
                        condor = condors[Strikes]
                        appended_1 = condor
                  
        return condors, keys, appended_1, appended_1_5, appended_2, appended_2_5, appended_3
from AlgorithmImports import *




class DataManager():



    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol
        self.callables = config.data_dictionary

        self.consolidators = {}
        self.indicators = {}

        for key, value in self.callables.items():
            for tup in value:
                # tup is one of the tuples in the list
                # you can unpack the tuple into variables here
                function, indicator_name, indicator, period, consolidator_name, resolution, delta = tup

                if consolidator_name not in self.consolidators:
                    function_receive = self.create_receive_bar_function()
                    self.consolidators[consolidator_name] = TradeBarConsolidator(delta)
                    self.algorithm.SubscriptionManager.AddConsolidator(self.symbol, self.consolidators[consolidator_name])
                    self.consolidators[consolidator_name].DataConsolidated += function_receive
    

    



    
        
from AlgorithmImports import *
import config



class EntryManager():




    def __init__(self, algorithm, symbol, re_entry_manager, avg_down_manager):
        self.algorithm = algorithm
        self.symbol = symbol
        self.re_entry_manager = re_entry_manager
        self.avg_down_manager = avg_down_manager

    



    def check_re_entry(self, condors, keys, min_range, max_range, premium_threshold):
        price = self.algorithm.Securities[self.symbol].Price
        if self.algorithm.Portfolio.Invested:
            invested = True
        else:
            invested = False
        if self.re_entry_manager.check_permission(invested):
            for key in keys:
                condor = condors[key[0]]
                if self.re_entry_manager.get_new_threshold(self.re_entry_manager.previous_premium) >= premium_threshold and condor.check_range(min_range, max_range):
                    if self.re_entry_manager.re_entry_mode == "SAME CONDOR" and key != self.re_entry_manager.allowed_condor:
                        continue
                    else:
                        self.algorithm.MarketOrder(condor.Put_Buy.Symbol, config.QUANTITY, tag =f"RE ENTRY {condor.open_put_range} Strike {condor.Put_Buy.Strike} pm {condor.Premium} Price {price}")
                        self.algorithm.MarketOrder(condor.Put_Sell.Symbol, -config.QUANTITY, tag =f"RE ENTRY {condor.open_put_range} Strike {condor.Put_Sell.Strike} pm {condor.Premium} Price {price}")
                        self.algorithm.MarketOrder(condor.Call_Buy.Symbol, config.QUANTITY, tag =f"RE ENTRY {condor.open_call_range} Strike {condor.Call_Buy.Strike} pm {condor.Premium} Price {price}")
                        self.algorithm.MarketOrder(condor.Call_Sell.Symbol, -config.QUANTITY, tag =f"RE ENTRY {condor.open_call_range} Strike {condor.Call_Sell.Strike} pm {condor.Premium} Price {price}")
                        self.re_entry_manager.allowed_condor = key
                        self.re_entry_manager.previous_premium = condor.Premium
                        self.re_entry_manager.entered_today = True
                        self.re_entry_manager.entry_count_today += 1
                        condor.entry_premiums.clear()
                        condor.entry_premium = condor.Premium
                        condor.calculate_avg_entry()
                        condor.entry_time = self.algorithm.Time
                        if condor.first_entry is None:
                            condor.first_entry = condor.Premium
                        return condor
                        
                        
    


    def check_avg_down(self, condors, keys, min_range, max_range, premium_threshold):
        price = self.algorithm.Securities[self.symbol].Price
        if self.algorithm.Portfolio.Invested:
            invested = True
        else:
            invested = False

        if invested:
            for key in keys:
                if key != self.avg_down_manager.entered_key:
                    continue
                condor = condors[key[0]]
                if self.avg_down_manager.check_avg_down():
                    self.algorithm.MarketOrder(condor.Put_Buy.Symbol, config.AVG_DOWN_QUANTITY, tag =f"AVG DOWN {condor.open_put_range} Strike {condor.Put_Buy.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Put_Sell.Symbol, -config.AVG_DOWN_QUANTITY, tag =f"AVG DOWN {condor.open_put_range} Strike {condor.Put_Sell.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Call_Buy.Symbol, config.AVG_DOWN_QUANTITY, tag =f"AVG DOWN {condor.open_call_range} Strike {condor.Call_Buy.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Call_Sell.Symbol, -config.AVG_DOWN_QUANTITY, tag =f"AVG DOWN {condor.open_call_range} Strike {condor.Call_Sell.Strike} pm {condor.Premium} Price {price}")
                    self.re_entry_manager.allowed_condor = key
                    self.re_entry_manager.previous_premium = condor.Premium
                    self.re_entry_manager.entered_today = True
                    self.avg_down_manager.entered_condor = condor
                    self.avg_down_manager.entered_key = key
                    self.avg_down_manager.avg_down_counter += 1
                    condor.entry_premium = condor.Premium
                    condor.calculate_avg_entry()
                    condor.entry_time = self.algorithm.Time
                    if self.avg_down_manager.first_entry is None:
                        self.avg_down_manager.first_entry = condor.Premium
                    if condor.first_entry is None:
                        condor.first_entry = condor.Premium
                    return condor



    def check_regular(self, condors, keys, min_range, max_range, premium_threshold):
        price = self.algorithm.Securities[self.symbol].Price
        if self.algorithm.Portfolio.Invested:
            invested = True
        else:
            invested = False
        if not invested and not self.re_entry_manager.entered_today:
            for key in keys:
                condor = condors[key[0]]
                if condor.Calculate_Premium() >= premium_threshold and condor.check_range(min_range, max_range):
                    self.algorithm.MarketOrder(condor.Put_Buy.Symbol, config.QUANTITY, tag =f"REGULAR ENTRY {condor.open_put_range} Strike {condor.Put_Buy.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Put_Sell.Symbol, -config.QUANTITY, tag =f"REGULAR ENTRY {condor.open_put_range} Strike {condor.Put_Sell.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Call_Buy.Symbol, config.QUANTITY, tag =f"REGULAR ENTRY  {condor.open_call_range} Strike {condor.Call_Buy.Strike} pm {condor.Premium} Price {price}")
                    self.algorithm.MarketOrder(condor.Call_Sell.Symbol, -config.QUANTITY, tag =f"REGULAR ENTRY {condor.open_call_range} Strike {condor.Call_Sell.Strike} pm {condor.Premium} Price {price}")
                    self.re_entry_manager.allowed_condor = key
                    self.re_entry_manager.previous_premium = condor.Premium
                    self.re_entry_manager.entered_today = True
                    self.avg_down_manager.entered_condor = condor
                    self.avg_down_manager.entered_key = key
                    condor.entry_premium = condor.Premium
                    condor.calculate_avg_entry()
                    condor.entry_time = self.algorithm.Time
                    if self.avg_down_manager.first_entry is None:
                        self.avg_down_manager.first_entry = condor.Premium
                    if condor.first_entry is None:
                        condor.first_entry = condor.Premium
                    return condor
from AlgorithmImports import *
import config



class ExitManager():



    def __init__(self, algorithm, symbol, close_trading_window):
        self.algorithm = algorithm
        self.symbol = symbol

        self.use_stop_loss = config.USE_STOP_LOSS
        self.stop_loss_mode = config.STOP_LOSS_MODE
        self.stop_loss_multiplier = config.STOP_LOSS_MULTIPLIER
        self.use_absolute_stop_loss = config.USE_ABSOLUTE_STOP_LOSS
        self.absolute_stop_loss = config.ABSOLUTE_STOP_LOSS


        self.use_regular_profit_take = config.USE_REGULAR_PROFIT_TAKE
        self.profit_taking_mode = config.PROFIT_TAKING_MODE
        self.regular_profit_percentage = 1 - (config.REGULAR_PROFIT_PERCENTAGE/100)


        self.use_timed_profit_exit = config.USE_TIMED_PROFIT_EXIT
        self.minutes_after_entry = config.MINUTES_AFTER_ENTRY
        self.timed_profit_percentage = 1 - (config.TIMED_PROFIT_PERCENTAGE/100)


        self.use_timed_exit = config.USE_TIMED_EXIT
        self.timed_exit_mode = config.TIMED_EXIT_MODE
        self.timed_Exit_minutes = config.TIMED_EXIT_MINUTES
        self.timed_exit_fixed = config.TIMED_EXIT_FIXED
    
        #self.algorithm.Schedule.On(self.algorithm.DateRules.EveryDay(), self.algorithm.TimeRules.BeforeMarketClose(self.symbol, 1), close_trading_window)



    def check_stop_loss(self, condor):
        if self.stop_loss_mode == "FIRST_ENTRY":
            if condor.Calculate_Premium >= condor.first_entry * self.stop_loss_multiplier:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "MOST RECENT ENTRY":
            if condor.Calculate_Premium >= condor.entry_premium * self.stop_loss_multiplier:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "AVG ENTRY":
            if condor.Calculate_Premium >= condor.avg_entry * self.stop_loss_multiplier:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        else:
            raise Exception(f"Please input a correct STOP LOSS MODE in the config file. {config.STOP_LOSS_MODE} is incorrect")
    


    def check_absolute_stop_loss(self, condor):
        if self.stop_loss_mode == "FIRST_ENTRY":
            if condor.Calculate_Premium() >= condor.first_entry + self.absolute_stop_loss:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "MOST RECENT ENTRY":
            if condor.Calculate_Premium() >= condor.entry_premium + self.absolute_stop_loss:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "AVG ENTRY":
            if condor.Calculate_Premium() >= condor.avg_entry + self.absolute_stop_loss:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
    


    def check_regular_profit_take(self, condor):
        if self.profit_taking_mode == "FIRST_ENTRY":
            if condor.Calculate_Premium() <= condor.first_entry * self.regular_profit_percentage:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.profit_taking_mode == "MOST RECENT ENTRY":
            if condor.Calculate_Premium() <= condor.entry_premium * self.regular_profit_percentage:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.profit_taking_mode == "AVG ENTRY":
            if condor.Calculate_Premium() <= condor.avg_entry * self.regular_profit_percentage:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
    



    def check_timed_profit_exit(self, condor):
        if self.algorithm.Time <= condor.entry_time + self.minutes_after_entry:
            if self.profit_taking_mode == "FIRST_ENTRY":
                if condor.Calculate_Premium() <= condor.first_entry * self.timed_profit_percentage:
                    self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                    self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Put_Buy.Symbol)
            elif self.profit_taking_mode == "MOST RECENT ENTRY":
                if condor.Calculate_Premium() <= condor.entry_premium * self.timed_profit_percentage:
                    self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                    self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Put_Buy.Symbol)
            elif self.profit_taking_mode == "AVG ENTRY":
                if condor.Calculate_Premium() <= condor.avg_entry * self.timed_profit_percentage:
                    self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                    self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                    self.algorithm.Liquidate(condor.Put_Buy.Symbol)



    def check_timed_exit(self, condor):
        if self.timed_exit_mode == "SIMPLE":
            if condor.entry_time + self.timed_Exit_minutes >= self.algorithm.Time:
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)
        elif self.timed_exit_mode == "FIXED TIME":
            if self.timed_exit_fixed.strftime("%H%M") >= self.algorithm.Time.strftime("%H%M"):
                self.algorithm.Liquidate(condor.Call_Sell.Symbol)
                self.algorithm.Liquidate(condor.Call_Buy.Symbol)
                self.algorithm.Liquidate(condor.Put_Sell.Symbol)
                self.algorithm.Liquidate(condor.Put_Buy.Symbol)

    
#region imports
from AlgorithmImports import *
from collections import deque
#endregion
import config
import statistics as stats


class IronCondor():


    def __init__(self, algorithm, put_buy, put_sell, call_buy, call_sell, open_call_range, open_put_range):
        self.algorithm = algorithm
        self.Put_Buy = put_buy
        self.Put_Sell = put_sell
        self.Call_Buy = call_buy
        self.Call_Sell = call_sell

        self.Sent_Already = False
        self.Premium = None
        self.entry_premium = None
        self.Entry_Fees = None

        self.first_entry = None
        self.entry_premiums = []
        self.avg_entry = None



        self.Lowest_Premium = None
        self.Scanned_Already = False


        self.entry_time = None

        self.Reached_Threshold = False

        self.Profit = None
        self.Stopped_Out = False
        self.Profit_Taken = False
        
        self.Current_Profit = None
        self.Margin_Required = None

        self.open_call_range = open_call_range
        self.open_put_range = open_put_range
        self.Sent_Open_Range = False
        self.Highest_Premium = -1000000
        self.Weight = 0

        self.Entered_Increments = []
        


        #self.Avg_Down_Type = config.AVG_DOWN_TYPE
        #self.Underlying_Increment = config.UNDERLYING_PERCENT_MOVE_INCREMENT/100
       


        self.Pivot_Queue = deque(maxlen=3)

    def check_range(self, min_range, max_range):
        if self.open_call_range >= min_range and self.open_put_range >= min_range and self.open_call_range <= max_range and self.open_put_range <= max_range:
            return True
        else:
            return False




    def calculate_avg_entry(self):
        self.entry_premiums.append(self.entry_premium)
        self.avg_entry = stats.mean(self.entry_premiums)



    
    def Calculate_Premium(self):
        cs_premium = self.algorithm.Securities[self.Call_Sell.Symbol].BidPrice
        cb_premium = self.algorithm.Securities[self.Call_Buy.Symbol].AskPrice
        ps_premium = self.algorithm.Securities[self.Put_Sell.Symbol].BidPrice
        pb_premium = self.algorithm.Securities[self.Put_Sell.Symbol].AskPrice
        call_premium = cs_premium - cb_premium
        put_premium = ps_premium - pb_premium

        list_of_ba = [-pb_premium, ps_premium, -cb_premium, cs_premium]

        self.Premium = sum(list_of_ba)
        
        return self.Premium



  

        


    #def Calculate_Underlying_Re_Entry(self):
        #if
# region imports
from AlgorithmImports import *
from symboldata import SymbolData
from create_condors import CreateCondors
from select_range import SelectRange
from adjust_premium import PremiumThresholdAdjuster
from select_premium import PremiumSelecter
from re_entry import ReEntry
from entry import EntryManager
from avg_down import AvgDown
from exits import ExitManager
from check_calendar import CheckCalendar
import time




class AlertBlackBull(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2022, 1, 1)  # Set Start Date
        self.SetEndDate(2023, 1, 20)
        self.SetCash(1000000)  # Set Strategy Cash
        
        self.symbol = self.AddIndex("SPX", Resolution.Minute).Symbol
        
        self.condor_creator = CreateCondors(self)
        self.range_selector = SelectRange(self, self.symbol)
        self.premium_adjuster = PremiumThresholdAdjuster(self)
        self.premium_selector = PremiumSelecter(self)
        self.re_entry_manager = ReEntry(self, self.symbol)
        self.avg_down_manager = AvgDown(self, self.symbol)
        self.entry_manager = EntryManager(self, self.symbol, self.re_entry_manager, self.avg_down_manager)
        self.exit_manager = ExitManager(self, self.symbol, self.close_if_at_loss)
        self.calendar_checker = CheckCalendar(self, self.close_trading_window, self.open_window, self.close_window)


        self.symbol_dictionary = {}
        self.symbol_dictionary[self.symbol] = SymbolData(self, self.symbol)
        
        self.created_combinations_today = False

        self.trading_window = False
        self.open_price = None
      

        self.today_condors = None
        self.today_keys = None
        self.todays_premium = None
        self.todays_min_range = None
        self.todays_max_range = None
        self.invested_condor = None
        #self.time_taken_1 = 0
        #self.time_taken_2 = 0
        #self.time_taken_3 = 0
        #self.time_taken_4 = 0
        #self.time_taken_5 = 0
        #self.time_taken_6 = 0


    #def OnEndOfAlgorithm(self):
        #self.Debug(("OnData", self.time_taken_1, "seconds to execute"))
        #self.Debug(("Creating Condors", self.time_taken_2, "seconds to execute"))
        #self.Debug(("Exits", self.time_taken_3, "seconds to execute"))
        #self.Debug(("Entires", self.time_taken_4, "seconds to execute"))




    def open_window(self):
        #self.Debug(self.Time)
        self.open_price = self.Securities[self.symbol].Open
        self.trading_window = True
        if self.calendar_checker.check_calendar():
            self.trading_window = False


    def close_window(self):
        #self.Debug(f"{self.Time} {self.Securities[self.symbol].Close}")
        self.close_if_at_loss()
        self.created_combinations_today = False
        self.trading_window = False
        self.today_condors = None
        self.today_keys = None
        self.todays_premium = None
        self.todays_min_range = None
        self.todays_max_range = None
        self.invested_condor = None
        self.re_entry_manager.reset()
        self.avg_down_manager.reset()
      

    def close_trading_window(self):
        self.Liquidate()
        self.created_combinations_today = False
        self.trading_window = False
        self.today_condors = None
        self.today_keys = None
        self.todays_premium = None
        self.todays_min_range = None
        self.todays_max_range = None
        self.invested_condor = None
        self.re_entry_manager.reset()
        self.avg_down_manager.reset()



    def close_if_at_loss(self):
        if self.invested_condor is not None:
            #self.Debug(f"{self.Securities[self.symbol].High+0.2} {self.Securities[self.symbol].Low-0.2}")
            if self.Securities[self.symbol].High+0.2 >= self.invested_condor.Call_Sell.Strike:
                #self.Debug(f"{self.Time} {self.invested_condor}")
                self.Liquidate(tag="At loss")
            elif self.Securities[self.symbol].Low-0.2 <= self.invested_condor.Put_Sell.Strike:
                #self.Debug(f"{self.Time} {self.invested_condor}")
                self.Liquidate(tag="At loss")



    def OnData(self, slice):
        #start_time_1 = time.time()
        if self.trading_window:
            if not self.created_combinations_today:
                #start_time_2 = time.time()
                for symbol, symbolData in self.symbol_dictionary.items():
                    chain = slice.OptionChains.GetValue(symbolData.option_symbol)
                    if not self.created_combinations_today:
                        min_range, max_range = self.range_selector.set_range_today()
                        self.todays_min_range = min_range
                        self.todays_max_range = max_range
                        if self.open_price is None:
                            self.open_price = self.Securities[self.symbol].Open
                        if chain is None:
                            return
                        condors, keys, appended_1, appended_1_5, appended_2, appended_2_5, appended_3 = self.condor_creator.populate_lists(chain, self.symbol, symbolData, self.open_price, min_range, max_range)
                        self.today_condors = condors
                        keys.sort(key=lambda tup: (tup[1], tup[2], -tup[3]), reverse=False)
                        self.today_keys = keys
                        self.premium_adjuster.receive_condors(appended_1, appended_1_5, appended_2, appended_2_5, appended_3)
                        adjustable_premium_today = self.premium_adjuster.return_premium_today(min_range)
                        self.todays_premium = self.premium_selector.select_premium(min_range, adjustable_premium_today)
                        self.created_combinations_today = True
                        #self.Debug(len(keys))
                #self.time_taken_2 += time.time() - start_time_2
                #self.Debug(("populate_lists function took", time.time() - start_time, "seconds to execute"))

            if self.invested_condor is not None:
                #start_time_3 = time.time()
                #start_time = time.time()
                if self.exit_manager.use_stop_loss:
                    self.exit_manager.check_stop_loss(self.invested_condor)
                if self.exit_manager.use_absolute_stop_loss:
                    self.exit_manager.check_absolute_stop_loss(self.invested_condor)
                if self.exit_manager.use_regular_profit_take:
                    self.exit_manager.check_regular_profit_take(self.invested_condor)
                if self.exit_manager.use_timed_exit:
                    self.exit_manager.check_timed_exit(self.invested_condor)
                if self.exit_manager.use_timed_profit_exit:
                    self.exit_manager.check_timed_profit_exit(self.invested_condor)
               
                #self.Debug(("populate_lists function took", time.time() - start_time, "seconds to execute"))
                #self.Debug(self.Time)



                if self.avg_down_manager.use_avg_down:
                    self.avg_down_manager.append_pivot()
                #self.time_taken_3 += time.time() - start_time_3
            if len(self.today_keys) > 0:
                #start_time_4 = time.time()
                if self.invested_condor is None and self.today_condors[self.today_keys[0][0]].Calculate_Premium() >= self.todays_premium:

                    
                    self.invested_condor = self.entry_manager.check_regular(self.today_condors, self.today_keys, self.todays_min_range, self.todays_max_range, self.todays_premium)
                    
                if self.invested_condor is not None:
                    if self.re_entry_manager.use_re_entry:
                        
                        condor = self.entry_manager.check_re_entry(self.today_condors, self.today_keys, self.todays_min_range, self.todays_max_range, self.todays_premium)
                        if condor is not None:
                            self.invested_condor = condor
                    if self.avg_down_manager.use_avg_down:
                        
                        condor = self.entry_manager.check_avg_down(self.today_condors, self.today_keys, self.todays_min_range, self.todays_max_range, self.todays_premium)
                        if condor is not None:
                            self.invested_condor = condor
                #self.time_taken_4 += time.time() - start_time_4
        #self.time_taken_1 += time.time() - start_time_1
      

            
from AlgorithmImports import *
from collections import deque
import statistics as stats
import config





class ReEntry():


    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol

        self.use_re_entry = config.USE_RE_ENTRY
        self.re_entry_mode = config.RE_ENTRY_MODE
        self.re_entry_threshold_mode = config.RE_ENTRY_THRESHOLD_MODE
        self.re_entry_multiplier = config.RE_ENTRY_MULTIPLIER
        self.re_entry_static = config.RE_ENTRY_STATIC_PREMIUM
        self.re_entry_times_x = config.RE_ENTRY_X_TIMES


        self.entered_today = False
        self.entry_count_today = 0

        self.previous_premium = None


        self.allowed_condor = "ANY CONDOR"



    def check_permission(self, invested):
        if not self.use_re_entry and self.entered_today:
            return False
        elif self.use_re_entry and not invested and self.entered_today and self.entry_count_today < self.re_entry_times_x:
            return True
        else:
            return False
    


    def get_new_threshold(self):
        if self.re_entry_threshold_mode == "STATIC":
            return self.re_entry_static
        elif self.re_entry_threshold_mode == "ENTRY MULTIPLIER":
            return self.previous_premium * self.re_entry_multiplier
        


    def reset(self):
        self.use_re_entry = config.USE_RE_ENTRY
        self.re_entry_mode = config.RE_ENTRY_MODE
        self.re_entry_threshold_mode = config.RE_ENTRY_THRESHOLD_MODE
        self.re_entry_multiplier = config.RE_ENTRY_MULTIPLIER
        self.re_entry_static = config.RE_ENTRY_STATIC_PREMIUM
        self.re_entry_times_x = config.RE_ENTRY_X_TIMES


        self.entered_today = False
        self.entry_count_today = 0

        self.previous_premium = None


        self.allowed_condor = "ANY CONDOR"

        
    

    

    


        
from AlgorithmImports import *
from collections import deque
import statistics as stats
import config




class PremiumSelecter():



    def __init__(self, algorithm):
        self.algorithm = algorithm


        self.premium_threshold_mode = config.PREMIUM_THRESHOLD_MODE
        self.fixed_premium = config.FIXED_PREMIUM
        self.fixed_premium_1 = config.FIXED_PREMIUM_1
        self.fixed_premium_1_5 = config.FIXED_PREMIUM_1_5
        self.fixed_premium_2 = config.FIXED_PREMIUM_2
        self.fixed_premium_2_5 = config.FIXED_PREMIUM_2_5
        self.fixed_premium_3 = config.FIXED_PREMIUM_3
        



    
    def select_premium(self, range_today, adjustable_premium_today):
        if self.premium_threshold_mode == "FIXED":
            self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium)
            return self.fixed_premium
        elif self.premium_threshold_mode == "FIXED PER RANGE":
            if range_today == 0.01:
                self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium_1)
                premium_today = self.fixed_premium_1
            elif range_today == 0.015:
                self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium_1_5)
                premium_today = self.fixed_premium_1_5
            elif range_today == 0.02:
                self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium_2_5)
                premium_today = self.fixed_premium_2_5
            elif range_today == 0.025:
                self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium_2_5)
                premium_today = self.fixed_premium_2_5
            elif range_today == 0.03:
                self.algorithm.Plot("Premium Used", "Premium", self.fixed_premium_3)
                premium_today = self.fixed_premium_3
            return premium_today
        elif self.premium_threshold_mode == "ADJUSTED PREMIUM":
            self.algorithm.Plot("Premium Used", "Premium", adjustable_premium_today)
            return adjustable_premium_today
        else:
            raise Exception(f"Premium Threshold Mode '{config.PREMIUM_THRESHOLD_MODE}' is incorrect, please pick between 'FIXED' or 'FIXED PER RANGE' or 'ADJUSTED PREMIUM'")
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.vix_threshold_1 = config.VIX_THRESHOLD_1
        self.vix_threshold_2 = config.VIX_THRESHOLD_2
        self.vix_threshold_3 = config.VIX_THRESHOLD_3
        self.atr_threshold_1 = config.ATR_THRESHOLD_1
        self.atr_threshold_2 = config.ATR_THRESHOLD_2
        self.atr_21_indie = NormalizedAverageTrueRange(21)
        self.atr_21 = None
        self.vix = None

        self.use_1 = False
        self.use_1_5 = False
        self.use_2 = False
        self.use_2_5 = False
        self.use_3 = False

        self.bar_consolidator = TradeBarConsolidator(timedelta(days=1))
        self.algorithm.SubscriptionManager.AddConsolidator(self.symbol, self.bar_consolidator)
        self.bar_consolidator.DataConsolidated += self.temporary_receive_bar


        history = self.algorithm.History[TradeBar](self.symbol, 100, Resolution.Daily)

        for bar in history:
            self.bar_consolidator.Update(bar)
        

        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":
            config.data_dictionary["select_range_callable"] = [(self.select_range_callable, "atr_21", NormalizedAverageTrueRange(21), 21, "1 day", Resolution.Daily, timedelta(days=1))]
            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 temporary_receive_bar(self, sender, bar):
        self.atr_21_indie.Update(bar)
        if self.atr_21_indie.IsReady:
            self.atr_21 = self.atr_21_indie.Current.Value/100


    def select_range_callable(self, atr_21, vix):
        self.atr_21 = atr_21/100
        self.vix = vix
    


    def set_range_today(self):
        #self.algorithm.Debug(f"{self.vix} {self.atr_21}")
        if self.range_mode == "AUTOMATIC":
            if self.use_1_5 and self.atr_21 <= self.atr_threshold_1 and self.vix <= self.vix_threshold_2:
                self.range_max_today = 0.015 + self.automatic_range_max_add
                self.range_min_today = 0.015
            elif self.use_2 and self.atr_21 <= self.atr_threshold_1:
                self.range_max_today = 0.02 + self.automatic_range_max_add
                self.range_min_today = 0.02
            elif self.use_2 and self.vix <= self.vix_threshold_2:
                self.range_max_today = 0.02 + self.automatic_range_max_add
                self.range_min_today = 0.02
            elif self.use_2_5:
                self.range_max_today = 0.025 + self.automatic_range_max_add
                self.range_min_today = 0.025
            if self.use_3 and self.vix >= self.vix_threshold_1:
                self.range_max_today = 0.03 + self.automatic_range_max_add
                self.range_min_today = 0.03
            if self.use_1 and self.atr_21 < self.atr_threshold_2 and self.vix < self.vix_threshold_3:
                self.range_max_today = 0.01 + self.automatic_range_max_add
                self.range_min_today = 0.01
        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 *
from QuantConnect.Securities.Option import OptionPriceModels


class SymbolData():



    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol

        
        self.option = self.algorithm.AddIndexOption(self.symbol, "SPXW")
        self.option.SetLeverage(1.0)
        self.option_symbol = self.option.Symbol
        #self.option.PriceModel = OptionPriceModels.CrankNicolsonFD()
        self.option.SetFilter(self.UniverseFunc)


    def UniverseFunc(self, universe):
    
        Price = self.algorithm.Securities[self.symbol].Price
        ATM = self.algorithm.Securities[self.symbol].Price * 0.06
        
        return universe.IncludeWeeklys().Strikes(-ATM, ATM).Expiration(TimeSpan.FromDays(0),TimeSpan.FromDays(0))