Overall Statistics
Total Orders
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Sortino Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
from AlgorithmImports import *
from collections import deque
import statistics as stats
import config



class PremiumThresholdAdjuster():



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

        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.Avg_Premium_1 is not None:
                return self.Avg_Premium_1
            else:
                return config.FIXED_PREMIUM_1
        elif range_today == 0.015:
            if self.Avg_Premium_15 is not None:
                return self.Avg_Premium_15
            else:
                return config.FIXED_PREMIUM_1_5
        elif range_today == 0.02:
            if self.Avg_Premium_20 is not None:
                return self.Avg_Premium_20
            else:
                return config.FIXED_PREMIUM_2
        elif range_today == 0.025:
            if self.Avg_Premium_25 is not None:
                return self.Avg_Premium_25
            else:
                return config.FIXED_PREMIUM_2_5
        elif range_today == 0.03:
            if self.Avg_Premium_30 is not None:
                return self.Avg_Premium_30
            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.algorithm.Debug(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 run_append(self):
        self.append_1()
        self.append_15()
        self.append_20()
        self.append_25()
        self.append_30()

    def append_1(self):
        if self.One_Premium is not None:
            premium = self.One_Premium.Highest_Premium
            if premium is not None:
                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):
        if self.One_Five_Premium is not None:
            premium = self.One_Five_Premium.Highest_Premium
            if premium is not None:
                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):
        if self.Two_Premium is not None:
            premium = self.Two_Premium.Highest_Premium
            if premium is not None:
                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):
        if self.Two_Five_Premium is not None:
            premium = self.Two_Five_Premium.Highest_Premium
            if premium is not None:
                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):
        if self.Three_Premium is not None:
            premium = self.Three_Premium.Highest_Premium
            if premium is not None:
                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)


    def reset_premium_adjuster(self):
        #self.algorithm.Debug(f"{self.Avg_Premium_1} {self.Avg_Premium_15} {self.Avg_Premium_20} {self.Avg_Premium_25} {self.Avg_Premium_30}")
        self.One_Premium = None
        self.One_Five_Premium = None
        self.Two_Premium = None
        self.Two_Five_Premium = None
        self.Three_Premium = None
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

        #######################################################################################################
        self.use_trading_quantity = config.TRADING_QUANTITY_ACTIVE

        self.avg_down_layers = {
             config.TRADING_ENTRY_THRESHOLD: [config.TRADING_ENTRY_QUANTITY, False],
            config.TRADING_ENTRY_THRESHOLD_2: [config.TRADING_ENTRY_QUANTITY_2, False],
            config.TRADING_ENTRY_THRESHOLD_3: [config.TRADING_ENTRY_QUANTITY_3, False],
        }


    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, condor):
        if self.entered_condor is not None:
            condor = self.entered_condor
        condor.calculate_premium()
        if self.use_avg_down:
            if self.avg_down_mode == "PERCENTAGE":
                if self.check_percentage_avg_down():
                    return True, 0, ""
                else:
                    return False, 0, ""
            elif self.avg_down_mode == "PIVOT POINT":
                if self.check_pivot_avg_down():
                    return True, 0, ""
                else:
                    return False, 0, ""
            elif self.avg_down_mode == "PERCENTAGE PIVOT POINT":
                if self.check_percentage_avg_down() and self.check_pivot_avg_down():
                    return True, 0, ""
                else:
                    return False, 0, ""
            elif self.avg_down_mode == "FIXED":
                if condor.Premium >= self.fixed_avg_down_premium:
                    return True
                else:
                    return False, 0, ""
            elif self.avg_down_mode == "FIXED PIVOT POINT":
                if condor.Premium >= self.fixed_avg_down_premium and self.check_pivot_avg_down():
                    return True, 0, ""
                else:
                    return False, 0, ""
            elif self.avg_down_mode == "LAYERED":
                quantity_to_enter = 0
                premium_return = []
                for premium, entry in self.avg_down_layers.items():
                    quantity, entered = entry
                    if condor.Premium >= float(premium) and not entered:
                        quantity_to_enter += quantity
                        premium_return.append(premium)
                        self.avg_down_layers[premium][1] = True
                if quantity_to_enter != 0:
                    return True, quantity_to_enter, premium_return
                else:
                    return False, 0, ""
            else:
                return False, 0, ""
        else:
            return False, 0, ""
    


    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
        self.avg_down_layers = {
            config.TRADING_ENTRY_THRESHOLD: [config.TRADING_ENTRY_QUANTITY, False],
            config.TRADING_ENTRY_THRESHOLD_2: [config.TRADING_ENTRY_QUANTITY_2, False],
            config.TRADING_ENTRY_THRESHOLD_3: [config.TRADING_ENTRY_QUANTITY_3, False],
            # "1": [5, False],
            # "1": [5, False],
        }
#region imports
from AlgorithmImports import *





USE_POWELL = False

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 = False
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 = False
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 = False
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 = False
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 = False
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 = False
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
import config





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)

        if config.USE_9_31:

            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)


        elif config.USE_10_31:

            self.algorithm.Schedule.On(self.algorithm.DateRules.Every(DayOfWeek.Friday), self.algorithm.TimeRules.At(10, 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(10, 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(10, 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(10, 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(10, 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

USE_9_31 = True
USE_10_31 = False





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

# 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 = 0.50

RE_ENTRY_X_TIMES = 1



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

USE_AVG_DOWN = True


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

# 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 = 2.50



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

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

AVG_DOWN_QUANTITY = 10

##################################################################  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 = False

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




##################################################################  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 = 30

# Maximum Strike Difference 
MAXIMUM_STRIKE_DIFFERENCE = 30





# 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


##################################################################  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 = "ADJUSTED PREMIUM"


# 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 = 50

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

# 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 = True

# 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=10)

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



########################################################################################################################################

REGULAR_ENTRY_ACTIVE = False


# Enables or disables use of trading quantity
# True/False
TRADING_QUANTITY_ACTIVE = True

# If trading quantity profit taking mode is set to FIXED then this is applied
TRADING_QUANTITY_FIXED_PROFIT = 1.50
# If trading quantity profit taking mode is set to PERCENTAGE then this is applied
TRADING_PROFIT_PERCENTAGE = 50
# "PERCENTAGE" "FIXED"
TRADING_PROFIT_TAKING_MODE = "PERCENTAGE"

# This enables or disables the ATR entry for the trading quantity
TRADING_ATR_ENTRY_ACTIVE = False

TRADING_ENTRY_QUANTITY = 5
TRADING_ENTRY_QUANTITY_2 = 10
TRADING_ENTRY_QUANTITY_3 = 5
TRADING_ENTRY_THRESHOLD = 1
TRADING_ENTRY_THRESHOLD_2 = 3
TRADING_ENTRY_THRESHOLD_3 = 6

PREMIUM_THRESHOLD_REDUCE = 3
NEW_PREMIUM = 1.5
DAYS_BELOW_THRESHOLD = 5
DROP_PREMIUM_BY = 0.25

NO_ENTRY_AFTER = 1700
from AlgorithmImports import *
from iron_condor import IronCondor
import config



class CreateCondors():



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

        
    




    def populate_lists(self, 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}")
    
        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}")
    
        
        put_3_expiry = self.generate_price_list(open_price, direction="PUT")#[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 =  self.generate_price_list(open_price, direction="CALL")#[x for x in optionschain if x.Right == OptionRight.Call and ((x.Expiry - self.algorithm.Time).days) == -1 and (price - x.Strike) < 1]
        

        self.algorithm.Debug(f"LEN PUTS {len(put_3_expiry)}")
        self.algorithm.Debug(f"LEN CALLS {len(call_3_expiry)}")
       
        self.algorithm.Debug(put_3_expiry)
        self.algorithm.Debug(call_3_expiry)
    

        put_3_min_distances_sorted_sell = sorted(put_3_expiry, reverse=True)
        call_3_min_distances_sorted_sell = sorted(call_3_expiry, reverse=False)

        put_3_min_distances_sorted_buy = sorted(put_3_expiry, reverse=True)
        call_3_min_distances_sorted_buy = sorted(call_3_expiry, reverse=False)
        
        

    

        

        for x in put_3_min_distances_sorted_sell:
            for y in put_3_min_distances_sorted_buy:
                strike_diff = x - y
                if x > y and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE:
                    open_put_range = abs((x - 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 - x
                if y > x and strike_diff >= config.MINIMUM_STRIKE_DIFFERENCE and strike_diff <= config.MAXIMUM_STRIKE_DIFFERENCE:
                    open_call_range = ((x - open_price) / open_price)
                    if open_call_range >= min_range and open_call_range <= max_range:
                        combo = (x, y)
                        call_list.append(combo)


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

    def generate_price_list(self, open_price, increment=5, percentage_range=0.02, direction="CALL"):
        # Calculate the price variation
        variation = open_price * percentage_range
        
        # Calculate the lower and upper bounds, rounded to the nearest increment
        lower_bound = round((open_price - variation) / increment) * increment
        upper_bound = round((open_price + variation) / increment) * increment
        if direction == "CALL":
            lower_bound = round((open_price) / increment) * increment
        if direction == "PUT":
            upper_bound = round((open_price) / increment) * increment

        # Generate the list of prices
        price_list = list(range(lower_bound, upper_bound + increment, increment))
        
        # Ensure the open price is included and is the first element, rounded to nearest 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





    def create_combinations(self, min_range, max_range, put_list, call_list, open_price, symbol):
        keys = []
        condors = {}
        appended_1 = None
        appended_1_5 = None
        appended_2 = None
        appended_2_5 = None
        appended_3 = None
        #symbol = Symbol.Create("SPX", SecurityType.Index, Market.USA)
        self.algorithm.Debug("IN CREATE COMBINATIONS")
        for ps, pb in put_list:
            for cs, cb in call_list:
                p_diff = ps - pb
                c_diff = cb- cs
            
                if cs > ps and p_diff == c_diff and open_price is not None:
                    open_call_range = ((cs - open_price) / open_price)
                    open_put_range = abs((ps - open_price) / open_price)
                    Strikes = f"{pb} {ps} {cs} {cb} {open_call_range} {open_put_range} {p_diff}"
                    if Strikes not in condors:
                        
                        cb_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA,
                            OptionStyle.European, OptionRight.Call, cb, datetime(self.algorithm.Time.year, self.algorithm.Time.month, self.algorithm.Time.day))
                        cs_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA,
                            OptionStyle.European, OptionRight.Call, cs, datetime(self.algorithm.Time.year, self.algorithm.Time.month, self.algorithm.Time.day))
                        ps_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA,
                            OptionStyle.European, OptionRight.Put, ps, datetime(self.algorithm.Time.year, self.algorithm.Time.month, self.algorithm.Time.day))
                        pb_1 = Symbol.CreateOption(symbol, "SPXW", Market.USA,
                            OptionStyle.European, OptionRight.Put, pb, datetime(self.algorithm.Time.year, self.algorithm.Time.month, self.algorithm.Time.day))
                        cb_1 = self.algorithm.AddIndexOptionContract(cb_1, Resolution.Minute)
                        cs_1 = self.algorithm.AddIndexOptionContract(cs_1, Resolution.Minute)
                        ps_1 = self.algorithm.AddIndexOptionContract(ps_1, Resolution.Minute)
                        pb_1 = self.algorithm.AddIndexOptionContract(pb_1, Resolution.Minute)
                        condors[Strikes] = IronCondor(self.algorithm, pb_1, ps_1, cb_1, cs_1, open_call_range, open_put_range, ps_1.Expiry)
                        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.algo = algorithm
        self.symbol = symbol
        self.re_entry_manager = re_entry_manager
        self.avg_down_manager = avg_down_manager

        ############################################################################################

        self.use_trading_quantity = config.TRADING_QUANTITY_ACTIVE


    



    def check_re_entry(self, condors, keys, min_range, max_range, premium_threshold):
        price = self.algo.Securities[self.symbol].Price
        
        if self.algo.Portfolio.Invested > 0:
            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.re_entry_mode == "SAME CONDOR" and key != self.re_entry_manager.allowed_condor:
                    continue
                else:
                    if condor.calculate_premium() >= self.re_entry_manager.get_new_threshold() and condor.check_range(min_range, max_range):
                        self.algo.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.algo.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.algo.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.algo.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.algo.Time
                        
                        condor.first_entry = condor.Premium
                        return condor
                        
                        
    def get_target_quantity(self, contract, target_quantity):
        total_quantity = 0

        # Iterate over each entry
        for entry, info in self.avg_down_manager.avg_down_layers.items():
            # Check if the boolean value is True
            if info[1]:
                # Add the quantity to the total
                total_quantity += info[0]
        if target_quantity < 0:
            total_quantity = -total_quantity
        target_quantity += total_quantity
        # Get the current quantity of the contract in the portfolio. It can be negative or positive.
        current_quantity = self.algo.Portfolio[contract.Symbol].Quantity 
        self.algo.Debug(f"Current quantity {contract.Symbol} {current_quantity}")
        # Calculate the quantity needed to reach the target. If the target or current is negative,
        # this calculation will account for it and return the correct adjustment amount.
        quantity_needed = target_quantity - current_quantity
        
        # Return the calculated quantity needed to reach the target.
        return quantity_needed


    def complete_trade(self, layer, condor, quantity, key):
        for key_ in layer:
            condor.active_layers.append(key_)
        condor.trading_entries += 1
        condor.entry_premiums_trading[condor.trading_entries] = [condor.Premium, quantity, True]
        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()
        if condor.first_entry_time is None:
            condor.first_entry_time = int(self.algo.Time.strftime("%H%M").replace(":", ""))
        condor.entry_time = int(self.algo.Time.strftime("%H%M").replace(":", ""))
        condor.is_re_entry = False
        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
        # self.ago.Debug("ENTRY")
        return condor, None


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

        if invested or self.use_trading_quantity:
            for key in keys:
                if key != self.avg_down_manager.entered_key and not self.use_trading_quantity:
                    continue
                condor = condors[key[0]]
                avg_down_sig, quantity, layer = self.avg_down_manager.check_avg_down(condor)

                if avg_down_sig:
                    self.algo.Debug(f"{self.algo.Time} AVERAGED DOWN")
                    # self.algo.MarketOrder(condor.Put_Buy.Symbol, quantity, tag =f"AVG DOWN {condor.open_put_range} Strike {condor.Put_Buy.Strike} pm {condor.Premium} Price {price}")
                    # self.algo.MarketOrder(condor.Put_Sell.Symbol, -quantity, tag =f"AVG DOWN {condor.open_put_range} Strike {condor.Put_Sell.Strike} pm {condor.Premium} Price {price}")
                    # self.algo.MarketOrder(condor.Call_Buy.Symbol, quantity, tag =f"AVG DOWN {condor.open_call_range} Strike {condor.Call_Buy.Strike} pm {condor.Premium} Price {price}")
                    # self.algo.MarketOrder(condor.Call_Sell.Symbol, -quantity, tag =f"AVG DOWN {condor.open_call_range} Strike {condor.Call_Sell.Strike} pm {condor.Premium} Price {price}")
                    legs = [] 
                    # legs.append(Leg.Create(condor.Put_Buy.Symbol, 1))
                    # legs.append(Leg.Create(condor.Put_Sell.Symbol, -1))
                    # legs.append(Leg.Create(condor.Call_Buy.Symbol, 1))
                    # legs.append(Leg.Create(condor.Call_Sell.Symbol, -1))       
                    # self.algo.ComboMarketOrder(legs, quantity)
                    weekly_canonical_symbol = Symbol.CreateCanonicalOption(self.symbol, "SPXW", Market.USA, "?SPXW")
                    self.algo.Transactions.CancelOpenOrders()
                    call_buy = condor.Call_Buy
                    call_sell = condor.Call_Sell
                    put_sell = condor.Put_Sell
                    put_buy = condor.Put_Buy

                    call_buy_quantity = self.get_target_quantity(call_buy, quantity)
                    call_sell_quantity = self.get_target_quantity(call_sell, -quantity)
                    put_sell_quantity = self.get_target_quantity(put_sell, -quantity)
                    put_buy_quantity = self.get_target_quantity(put_buy, quantity)
                    self.algo.Debug(f"{quantity} {call_buy_quantity} {call_sell_quantity} {put_sell_quantity} {put_buy_quantity}")
                    if call_buy_quantity == quantity and call_sell_quantity == -quantity and put_sell_quantity == -quantity and put_buy_quantity == quantity:
                        condor.partial = False
                        legs.append(Leg.Create(condor.Call_Buy.Symbol, 1))
                        legs.append(Leg.Create(condor.Call_Sell.Symbol, -1))
                        legs.append(Leg.Create(condor.Put_Sell.Symbol, -1))
                        legs.append(Leg.Create(condor.Put_Buy.Symbol, 1))
                        self.algo.ComboMarketOrder(legs, quantity)
                        return self.complete_trade(layer, condor, quantity, key)
                    elif call_sell_quantity == -quantity and put_sell_quantity == -quantity and call_buy_quantity == 0 and put_buy_quantity == 0:
                        condor.partial = False
                        legs.append(Leg.Create(condor.Call_Sell.Symbol, -1))
                        legs.append(Leg.Create(condor.Put_Sell.Symbol, -1))  
                        self.algo.ComboMarketOrder(legs, quantity)
                        return self.complete_trade(layer, condor, quantity, key)
                    else:
                        condor.partial = True
                        if call_buy_quantity != quantity:
                            condor.partial_orders.append(self.algo.MarketOrder(call_buy.Symbol, call_buy_quantity))
                        if call_sell_quantity != quantity:
                            condor.partial_orders.append(self.algo.MarketOrder(call_sell.Symbol, call_sell_quantity))
                        if put_sell_quantity != quantity:
                            condor.partial_orders.append(self.algo.MarketOrder(put_sell.Symbol, put_sell_quantity))
                        if put_buy_quantity != quantity:
                            condor.partial_orders.append(self.algo.MarketOrder(put_buy.Symbol, put_buy_quantity))
                        
                    














                    




















                    # iron_condor = OptionStrategies.IronCondor(
                    # weekly_canonical_symbol, 
                    # condor.Put_Buy.StrikePrice,
                    # condor.Put_Sell.StrikePrice,
                    # condor.Call_Sell.StrikePrice,
                    # condor.Call_Buy.StrikePrice,
                    # condor.Put_Sell.Expiry)

                    # self.algo.Buy(iron_condor, quantity)
                    # for key_ in layer:
                    #     condor.active_layers.append(key_)
                    # condor.trading_entries += 1
                    # condor.entry_premiums_trading[condor.trading_entries] = [condor.Premium, quantity, True]
                    # 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()
                    # if condor.first_entry_time is None:
                    #     condor.first_entry_time = int(self.algo.Time.strftime("%H%M").replace(":", ""))
                    # condor.entry_time = int(self.algo.Time.strftime("%H%M").replace(":", ""))
                    # condor.is_re_entry = False
                    # 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
                    # # self.ago.Debug("ENTRY")
                    # return condor, None
                    
                    #####
                    

                    # 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
        return None, None


    def check_regular(self, condors, keys, min_range, max_range, premium_threshold, open_price):
        price = self.algo.Securities[self.symbol].Price
        
        if self.algo.Portfolio.Invested > 0:
            invested = True
        else:
            invested = False
        if not invested and not self.re_entry_manager.entered_today:
            for key in keys:
                condor = condors[key[0]]
                #self.algorithm.Debug(f"{condor.Calculate_Premium()} {premium_threshold} {condor.check_range(min_range, max_range)}")
                if condor.calculate_premium() >= premium_threshold and condor.check_range(min_range, max_range):
                    #self.algorithm.Debug(f" HERE {self.algorithm.Time} {open_price}")
                    self.algo.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} {open_price} {condor.Expiry}")
                    self.algo.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} {open_price} {condor.Expiry}")
                    self.algo.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} {open_price} {condor.Expiry}")
                    self.algo.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} {open_price} {condor.Expiry}")
                    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.algo.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.algo = algorithm
        self.symbol = symbol
        self.avg_down_manager = None
        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.use_trading_quantity = config.TRADING_QUANTITY_ACTIVE

        self.trading_profit_taking_mode = config.TRADING_PROFIT_TAKING_MODE
        self.trading_quantity_fixed_profit = config.TRADING_QUANTITY_FIXED_PROFIT
        self.trading_profit_percentage = config.TRADING_PROFIT_PERCENTAGE/100

        #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.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.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.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "AVG ENTRY":
            if condor.Calculate_Premium >= condor.avg_entry * self.stop_loss_multiplier:
                self.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.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.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.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.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.Liquidate(condor.Put_Buy.Symbol)
        elif self.stop_loss_mode == "AVG ENTRY":
            if condor.Calculate_Premium() >= condor.avg_entry + self.absolute_stop_loss:
                self.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.Liquidate(condor.Put_Buy.Symbol)
    


    def check_regular_profit_take(self, condor):
        if not self.use_trading_quantity:
            if self.profit_taking_mode == "FIRST ENTRY":
                if condor.Calculate_Premium() <= condor.first_entry * self.regular_profit_percentage:
                    self.algo.Liquidate(condor.Call_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from FIRST ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Call_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from FIRST ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from FIRST ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from FIRST ENTRY Premium {condor.Premium}")
            elif self.profit_taking_mode == "MOST RECENT ENTRY":
                if condor.Calculate_Premium() <= condor.entry_premium * self.regular_profit_percentage:
                    self.algo.Liquidate(condor.Call_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from MOST RECENT ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Call_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from MOST RECENT ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from MOST RECENT ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from MOST RECENT ENTRY Premium {condor.Premium}")
            elif self.profit_taking_mode == "AVG ENTRY":
                if condor.Calculate_Premium() <= condor.avg_entry * self.regular_profit_percentage:
                    self.algo.Liquidate(condor.Call_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Call_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
            

        #############################################################################################################

        if self.use_trading_quantity and condor:
            premium = condor.calculate_premium()
            condor.calculate_trading_entry_avg_premium()
            if condor.avg_trading_premium != 0:
                current_profit = (condor.avg_trading_premium - premium)
                self.algo.Debug(current_profit)
                if self.trading_profit_taking_mode == "FIXED" and current_profit >= self.trading_quantity_fixed_profit:
                    for layer in condor.active_layers:
                        self.avg_down_manager.avg_down_layers[layer][1] = False
                    condor.active_layers.clear()
                    condor.current_trading_quantity = 0
                    self.algo.Liquidate(condor.Call_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Call_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
            

                profit_percentage = self.trading_profit_percentage
                # if self.use_adjusted_profit_take:
                #     start_day_con = True if int(
                #         self.spx_info.time.replace(":", "")) >= self.profit_adjust_time else False
                #     profit_percentage = self.adjusted_profit
                if self.trading_profit_taking_mode == "PERCENTAGE" and current_profit >= (condor.avg_trading_premium * profit_percentage):
                    for layer in condor.active_layers:
                        self.avg_down_manager.avg_down_layers[layer][1] = False
                    condor.active_layers.clear()
                    condor.current_trading_quantity = 0
                    self.algo.Liquidate(condor.Call_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Call_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Sell.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
                    self.algo.Liquidate(condor.Put_Buy.Symbol, tag=f"REGULAR PROFIT TAKE at {config.REGULAR_PROFIT_PERCENTAGE}% from AVG ENTRY Premium {condor.Premium}")
            



    def check_timed_profit_exit(self, condor):
        if self.algo.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.algo.Liquidate(condor.Call_Sell.Symbol)
                    self.algo.Liquidate(condor.Call_Buy.Symbol)
                    self.algo.Liquidate(condor.Put_Sell.Symbol)
                    self.algo.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.algo.Liquidate(condor.Call_Sell.Symbol)
                    self.algo.Liquidate(condor.Call_Buy.Symbol)
                    self.algo.Liquidate(condor.Put_Sell.Symbol)
                    self.algo.Liquidate(condor.Put_Buy.Symbol)
            elif self.profit_taking_mode == "AVG ENTRY":
                if condor.Calculate_Premium() <= condor.avg_entry * self.timed_profit_percentage:
                    self.algo.Liquidate(condor.Call_Sell.Symbol)
                    self.algo.Liquidate(condor.Call_Buy.Symbol)
                    self.algo.Liquidate(condor.Put_Sell.Symbol)
                    self.algo.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.algo.Time:
                self.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.Liquidate(condor.Put_Buy.Symbol)
        elif self.timed_exit_mode == "FIXED TIME":
            if self.timed_exit_fixed.strftime("%H%M") >= self.algo.Time.strftime("%H%M"):
                self.algo.Liquidate(condor.Call_Sell.Symbol)
                self.algo.Liquidate(condor.Call_Buy.Symbol)
                self.algo.Liquidate(condor.Put_Sell.Symbol)
                self.algo.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, expiry):
        self.algo = 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.partial = False
        self.partial_orders = []
        self.first_entry = None
        self.entry_premiums = []
        self.avg_entry = None

        self.Expiry = expiry

        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)

        ##########################################################################################

        self.active_layers = []
        self.first_entry_time = None

        self.entry_premiums_trading = {}
        self.avg_trading_premium = None
        self.trading_entries = 0
        self.max_trading_quantity = config.AVG_DOWN_QUANTITY
        self.current_trading_quantity = 0

    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_trading_entry_avg_premium(self):
        length = 0
        total_quantity = 0
        total_value = 0
        for entry, info in self.entry_premiums_trading.items():
            premium, quantity, is_open = info
            if is_open:
                length += 1
                total_quantity += quantity
                total_value += (premium * quantity)
        if length > 0:
            self.avg_trading_premium = total_value / total_quantity
        else:
            self.avg_trading_premium = 0
        self.current_trading_quantity = total_quantity
        # self.algo.Debug(self.entry_premiums_trading)
        return self.avg_trading_premium


    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.algo.Securities[self.Call_Sell.Symbol].BidPrice
        cb_premium = self.algo.Securities[self.Call_Buy.Symbol].AskPrice
        ps_premium = self.algo.Securities[self.Put_Sell.Symbol].BidPrice
        pb_premium = self.algo.Securities[self.Put_Buy.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)
        #self.algo.Debug(f"pb ask {pb_premium} ps bid {ps_premium} cs bid {cs_premium} cb ask {cb_premium}")
        #self.algo.Debug(f"pb ask {self.Call_Sell.AskPrice} ps bid {self.Put_Sell.BidPrice} cs bid {self.Call_Sell.BidPrice} cb ask {self.Call_Buy.AskPrice}")
        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 config
import yfinance as yf


class AlertBlackBull(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2023, 1, 1)  # Set Start Date
        self.SetEndDate(2024, 3, 20)
        self.SetCash(1000000)  # Set Strategy Cash
        
        #self.symbol = self.AddIndex("SPX", Resolution.Minute).Symbol
        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.symbol)
        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.exit_manager.avg_down_manager = self.avg_down_manager
        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.use_regular_entry = config.REGULAR_ENTRY_ACTIVE
        self.key = config.TRADING_ENTRY_THRESHOLD

        self.threshold = config.PREMIUM_THRESHOLD_REDUCE
        self.org_layer = [config.TRADING_ENTRY_QUANTITY, False]
        self.new_premium = config.NEW_PREMIUM
        self.gradual_premium = float(config.TRADING_ENTRY_THRESHOLD)
        self.key_2 = config.TRADING_ENTRY_THRESHOLD_2
        self.key_3 = config.TRADING_ENTRY_THRESHOLD_3
        self.max_count = config.DAYS_BELOW_THRESHOLD
        self.drop_by = config.DROP_PREMIUM_BY
        self.current_key = self.key
        self.today_below = True
        self.latest_time = config.NO_ENTRY_AFTER
        self.first_data_point = True


    def open_window(self):
        self.Debug(self.Time)
        self.open_price = self.Securities[self.symbol].Open
        self.get_open_yfinance()
        self.trading_window = True
        if self.calendar_checker.check_calendar():
            self.trading_window = False
        self.Debug(f"{self.Time} {self.open_price}")

    def close_window(self):
        #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.open_price = None

        self.premium_adjuster.run_append()
        self.premium_adjuster.reset_premium_adjuster()

        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 OnOrderEvent(self, orderEvent: OrderEvent) -> None:
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        if orderEvent.Status == OrderStatus.Filled:
            if self.invested_condor is not None and orderEvent.OrderId in self.invested_condor.partial_orders:
                self.invested_condor.partial_orders.remove(orderEvent.OrderId)
        if self.invested_condor is not None and len(self.invested_condor.partial_orders) == 0 and self.invested_condor.partial:
            self.invested_condor.partial = False
            self.entry_manager.check_avg_down(self.today_condors, self.today_keys, self.todays_min_range, self.todays_max_range, self.todays_premium)

  


    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 get_open_yfinance(self):
        try:
            spx = yf.Ticker('^GSPC')

            # Get historical data for the SPX
            historical_data = spx.history(period='1d')

            # Get the most recent date and open price
            latest_open_price = historical_data['Open'].iloc[-1]
            # Get the most recent date and open price
            latest_date = historical_data.index[-1].strftime('%Y-%m-%d')
            if latest_date == self.Time.strftime("%Y-%m-%d"):
                if latest_open_price is not None and latest_open_price != 0 and latest_open_price < 100000:
                    self.open_price = latest_open_price 
        except:
            self.Debug(f"No yahoo data at {self.Time}")

    def OnData(self, slice):
        if self.first_data_point:
            self.open_window()
            self.first_data_point = False
        self.Debug(f"IN ON DATA {self.Time}")
        if self.trading_window:
            for symbol, symbolData in self.symbol_dictionary.items():
                #chain = slice.OptionChains.GetValue(symbolData.option_symbol)
                #self.Debug(f"chain {chain}")
                self.Debug(f"SPX PRICE {self.Securities[self.symbol].Open}")
                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
                        self.Debug(f"{self.Time} WTF {self.open_price}")
                    #if chain is None:
                       # self.Debug(f"WHY ARE WE NOT RETURNING {chain}")
                        #return
                    condors, keys, appended_1, appended_1_5, appended_2, appended_2_5, appended_3 = self.condor_creator.populate_lists(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.Debug(f"{appended_1} {appended_1_5} {appended_2} {appended_2_5} {appended_3}")
                    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, 1)
                    self.created_combinations_today = True
                    self.Debug(f"LEN {len(keys)}")
            

            if self.invested_condor is not None:
                
                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)
                



                if self.avg_down_manager.use_avg_down:
                    self.avg_down_manager.append_pivot()
            if len(self.today_keys) > 0:
                x = self.today_condors[self.today_keys[0][0]].calculate_premium()
                if self.use_regular_entry:
                    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, self.open_price)
                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
                time = int(self.Time.strftime("%H%M").replace(":", ""))
                self.Debug(f'{int(self.Time.strftime("%H%M").replace(":", ""))} {x} {self.today_keys[0][0]}')
                if time <= self.latest_time and x >= 1 and (
                                (self.invested_condor is not None and
                                 (not self.avg_down_manager.avg_down_layers[self.current_key][-1] or
                                  not self.avg_down_manager.avg_down_layers[self.key_2][-1] or
                                  not self.avg_down_manager.avg_down_layers[self.key_3][-1])) or
                                (self.avg_down_manager.use_trading_quantity and
                                 (not self.avg_down_manager.avg_down_layers[self.current_key][-1] or
                                  not self.avg_down_manager.avg_down_layers[self.key_2][-1] or
                                  not self.avg_down_manager.avg_down_layers[self.key_3][-1]))
                        ):
                    if self.avg_down_manager.use_avg_down and (self.invested_condor is None or not self.invested_condor.partial):
                        condor, strgl = 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

            
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 or 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
import yfinance as yf


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):
        vix = yf.Ticker("^VIX")

        self.vix = vix.history(period="1d")['Close'].iloc[0]
        self.algorithm.Debug(f"VIX {self.vix}")



    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.atr_21 = 0.008
        
        self.vix = 14
        #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.005 + self.automatic_range_max_add
                self.range_min_today = 0.005
        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)

        #self.range_max_today = 0.01 + self.automatic_range_max_add
        #self.range_min_today = 0.01
        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