from sqlalchemy import text
from math import isnan, sqrt
from .currency import *

# Function which clears the raw data tables once we have aggregated the data in a 6 minute interval
def reset_raw_data_tables(engine, currency_pairs):
    with engine.begin() as conn:
        for curr in currency_pairs:
            conn.execute(text("DROP TABLE " + curr[0] + curr[1] + "_raw;"))
            conn.execute(
                text(
                    "CREATE TABLE "
                    + curr[0]
                    + curr[1]
                    + "_raw(ticktime text, fxrate  numeric, inserttime text);"
                )
            )


# This creates a table for storing the raw, unaggregated price data for each currency pair in the SQLite database
def initialize_raw_data_tables(engine, currency_pairs):
    with engine.begin() as conn:
        for curr in currency_pairs:
            conn.execute(
                text(
                    "CREATE TABLE "
                    + curr[0]
                    + curr[1]
                    + "_raw(ticktime text, fxrate  numeric, inserttime text);"
                )
            )


# This creates a table for storing the (6 min interval) aggregated price data for each currency pair in the SQLite database
def initialize_aggregated_tables(engine, currency_pairs):
    with engine.begin() as conn:
        for curr in currency_pairs:
            conn.execute(
                text(
                    "CREATE TABLE "
                    + curr[0]
                    + curr[1]
                    + "_agg(inserttime text, avgfxrate  numeric, stdfxrate numeric);"
                )
            )


# This function is called every 6 minutes to aggregate the data, store it in the aggregate table,
# and then delete the raw data
def aggregate_raw_data_tables(engine, currency_pairs):
    with engine.begin() as conn:
        for curr in currency_pairs:
            result = conn.execute(
                text(
                    "SELECT AVG(fxrate) as avg_price, COUNT(fxrate) as tot_count FROM "
                    + curr[0]
                    + curr[1]
                    + "_raw;"
                )
            )
            for row in result:
                avg_price = row.avg_price
                tot_count = row.tot_count
            std_res = conn.execute(
                text(
                    "SELECT SUM((fxrate - "
                    + str(avg_price)
                    + ")*(fxrate - "
                    + str(avg_price)
                    + "))/("
                    + str(tot_count)
                    + "-1) as std_price FROM "
                    + curr[0]
                    + curr[1]
                    + "_raw;"
                )
            )
            for row in std_res:
                std_price = sqrt(row.std_price)
            date_res = conn.execute(
                text(
                    "SELECT MAX(ticktime) as last_date FROM "
                    + curr[0]
                    + curr[1]
                    + "_raw;"
                )
            )
            for row in date_res:
                last_date = row.last_date
            conn.execute(
                text(
                    "INSERT INTO "
                    + curr[0]
                    + curr[1]
                    + "_agg (inserttime, avgfxrate, stdfxrate) VALUES (:inserttime, :avgfxrate, :stdfxrate);"
                ),
                [
                    {
                        "inserttime": last_date,
                        "avgfxrate": avg_price,
                        "stdfxrate": std_price,
                    }
                ],
            )

            # This calculates and stores the return values
            exec(
                "curr[2].append(" + curr[0] + curr[1] + "_return(last_date,avg_price))"
            )
            # exec("print(\"The return for "+curr[0]+curr[1]+" is:"+str(curr[2][-1].hist_return)+" \")")

            if len(curr[2]) > 5:
                try:
                    avg_pop_value = curr[2][-6].hist_return
                except:
                    avg_pop_value = 0
                if isnan(avg_pop_value) == True:
                    avg_pop_value = 0
            else:
                avg_pop_value = 0
            # Calculate the average return value and print it/store it
            curr_avg = curr[2][-1].get_avg(avg_pop_value)
            # exec("print(\"The average return for "+curr[0]+curr[1]+" is:"+str(curr_avg)+" \")")

            # Now that we have the average return, loop through the last 5 rows in the list to start compiling the
            # data needed to calculate the standard deviation
            for row in curr[2][-5:]:
                row.add_to_running_squared_sum(curr_avg)

            # Calculate the standard dev using the avg
            curr_std = curr[2][-1].get_std()
            # exec("print(\"The standard deviation of the return for "+curr[0]+curr[1]+" is:"+str(curr_std)+" \")")

            # Calculate the average standard dev
            if len(curr[2]) > 5:
                try:
                    pop_value = curr[2][-6].std_return
                except:
                    pop_value = 0
            else:
                pop_value = 0
            curr_avg_std = curr[2][-1].get_avg_std(pop_value)
            # exec("print(\"The average standard deviation of the return for "+curr[0]+curr[1]+" is:"+str(curr_avg_std)+" \")")

            # -------------------Investment Strategy-----------------------------------------------
            try:
                return_value = curr[2][-1].hist_return
            except:
                return_value = 0
            if isnan(return_value) == True:
                return_value = 0

            try:
                return_value_1 = curr[2][-2].hist_return
            except:
                return_value_1 = 0
            if isnan(return_value_1) == True:
                return_value_1 = 0

            try:
                return_value_2 = curr[2][-3].hist_return
            except:
                return_value_2 = 0
            if isnan(return_value_2) == True:
                return_value_2 = 0

            try:
                upp_band = curr[2][-1].avg_return + (1.5 * curr[2][-1].std_return)
                if (
                    return_value >= upp_band
                    and curr[3].Prev_Action_was_Buy == True
                    and return_value != 0
                ):  #  (return_value > 0) and (return_value_1 > 0) and
                    curr[3].sell_curr(avg_price)
            except:
                pass

            try:
                loww_band = curr[2][-1].avg_return - (1.5 * curr[2][-1].std_return)
                if (
                    return_value <= loww_band
                    and curr[3].Prev_Action_was_Buy == False
                    and return_value != 0
                ):  # and  (return_value < 0) and (return_value_1 < 0)
                    curr[3].buy_curr(avg_price)
            except:
                pass


# This function is called every 6 minutes to aggregate the data, make the necessary calculations,
# and make a decision about buying
def offline_aggregate_raw_data_tables(engine, currency_pairs):
    with engine.begin() as conn:
        for curr in currency_pairs:
            result = conn.execute(
                text("SELECT inserttime, avgfxrate FROM " + curr[0] + curr[1] + "_agg;")
            )
            for row in result:
                avg_price = row.avgfxrate
                last_date = row.inserttime

                # This calculates and stores the return values
                exec(
                    "curr[2].append("
                    + curr[0]
                    + curr[1]
                    + "_return(last_date,avg_price))"
                )
                # exec("print(\"The return for "+curr[0]+curr[1]+" is:"+str(curr[2][-1].hist_return)+" \")")

                if len(curr[2]) > 5:
                    try:
                        avg_pop_value = curr[2][-6].hist_return
                    except:
                        avg_pop_value = 0
                    if isnan(avg_pop_value) == True:
                        avg_pop_value = 0
                else:
                    avg_pop_value = 0
                # Calculate the average return value and print it/store it
                curr_avg = curr[2][-1].get_avg(avg_pop_value)
                # exec("print(\"The average return for "+curr[0]+curr[1]+" is:"+str(curr_avg)+" \")")

                # Now that we have the average return, loop through the last 5 rows in the list to start compiling the
                # data needed to calculate the standard deviation
                for row in curr[2][-5:]:
                    row.add_to_running_squared_sum(curr_avg)

                # Calculate the standard dev using the avg
                curr_std = curr[2][-1].get_std()
                # exec("print(\"The standard deviation of the return for "+curr[0]+curr[1]+" is:"+str(curr_std)+" \")")

                # Calculate the average standard dev
                if len(curr[2]) > 5:
                    try:
                        pop_value = curr[2][-6].std_return
                    except:
                        pop_value = 0
                else:
                    pop_value = 0
                curr_avg_std = curr[2][-1].get_avg_std(pop_value)
                # exec("print(\"The average standard deviation of the return for "+curr[0]+curr[1]+" is:"+str(curr_avg_std)+" \")")

                # -------------------Investment Strategy-----------------------------------------------
                try:
                    return_value = curr[2][-1].hist_return
                except:
                    return_value = 0
                if isnan(return_value) == True:
                    return_value = 0

                try:
                    return_value_1 = curr[2][-2].hist_return
                except:
                    return_value_1 = 0
                if isnan(return_value_1) == True:
                    return_value_1 = 0

                try:
                    return_value_2 = curr[2][-3].hist_return
                except:
                    return_value_2 = 0
                if isnan(return_value_2) == True:
                    return_value_2 = 0

                try:
                    upp_band = curr[2][-1].avg_return + (1.5 * curr[2][-1].std_return)
                    if (
                        return_value >= upp_band
                        and curr[3].Prev_Action_was_Buy == True
                        and return_value != 0
                    ):  #  (return_value > 0) and (return_value_1 > 0) and
                        curr[3].sell_curr(avg_price)
                except:
                    pass

                try:
                    loww_band = curr[2][-1].avg_return - (1.5 * curr[2][-1].std_return)
                    if (
                        return_value <= loww_band
                        and curr[3].Prev_Action_was_Buy == False
                        and return_value != 0
                    ):  # and  (return_value < 0) and (return_value_1 < 0)
                        curr[3].buy_curr(avg_price)
                except:
                    pass
