def candle_interval_stats(file_path, candle_range, ticker, time_frame):
    # format = [open_time, open, high, low, close, volume, close_time, number_of_trades]
    ticker_data = bcd.get_data_by_date(start_date, end_date, file_path)
    candle_data = ticker_data.values.tolist()

    # print(ticker_data)
    #
    # print(len(candle_data))
    # print("-"* 100)

    avg = []            # list for storing candle close values
    candle_gain = []    # list for storing candle gain values
    pct_change = []     # list for storing candle pct gains
    ranges = []         # list for storing candle ranges
    for i in candle_data:

        if len(avg) < candle_range:
            avg.append(i[4])            # appends candle closes

            if len(avg) >= 2:   # if there are atleast 2 values in list

                #print(f"avg len={len(avg)} ||| avg price of last {len(avg)} candles is {round(sum(avg) / len(avg), 2)} -> current price={avg[-1]} ||| % change [max/min] of last {len(avg)} candles=>{round((max(avg) / min(avg) - 1) * 100, 2)} ||| max={max(avg)} - min={min(avg)} ||| last candle gain/loss {round((avg[-1] / avg[-2] - 1) * 100, 2)}")
                candle_gain.append(round((avg[-1] / avg[-2] - 1) * 100, 3))     # append last candle gain

            else:
                #print(f"avg len={len(avg)} ||| avg price of last {len(avg)} candles is {round(sum(avg) / len(avg), 2)} -> current price={avg[-1]}  ||| % change [max/min] of last {len(avg)} candles=>{round((max(avg) / min(avg) - 1) * 100, 2)} ||| max={max(avg)} - min={min(avg)}")
                pass

        if len(avg) == candle_range:        # if candle range list is full
            pct_change.append(round((max(avg) / min(avg) - 1) * 100, 2))    # append this candle range pct range(max/min)
            ranges.append(max(avg) - min(avg))                              # append this candle range max-min
            candle_gain = []                                                # prepare list for next candle range gains
            avg = []                                                        # prepare list for next candle closes

    print("-" * 100)
    print(f"{ticker} stats")
    print(f"avg % change of {candle_range} {time_frame} candles in timespan {start_date.date()} -> {end_date.date()} =>{round(np.average(pct_change), 3)} %")
    print(f"avg range of {candle_range} {time_frame} candles in timespan {start_date.date()} -> {end_date.date()} =>{round(np.average(ranges), 2)} $")
    print("-" * 100)
Beispiel #2
0
ticker = "ETHUSDT"
# --------------------------------------------------------------------
start_size = 5000  # contracts
size = start_size
# --------------------------------------------------------------------
market_order_fee = 0.075    # unit [%]
slippage = 0.1              # unit [%]
# --------------------------------------------------------------------
tp_pct = 0.01               # unit => 1 - 100%, 0.1 - 10%, 0.01 - 1%
# --------------------------------------------------------------------
compounding = True
# END OF USER INPUTS -------------------------------------------------


filepath = f"data/{ticker}_{time_frame}.p"
data = bcd.get_data_by_date(start_date, end_date, filepath)

# ----------------------------------------------------------------
# Add MA and EMAs here
data["EMA5"] = round(data["close"].ewm(span=5).mean(), 2)
data["EMA10"] = round(data["close"].ewm(span=10).mean(), 2)
# ----------------------------------------------------------------

# format =  [open_time, open, high, low, close, volume, close_time, number_of_trades, EMA5,  EMA10]
candle_data_list = data.values.tolist()     # convert df to list


short_long = ""
trade_opened = False

pnl = []
def triple_sdt1_prob_movingWindow(file_path, candle_range, time_frame):

    # format = [open_time, open, high, low, close, volume, close_time, number_of_trades]
    ticker_data = bcd.get_data_by_date(start_date, end_date, file_path)
    candle_data = ticker_data.values.tolist()

    candle_gain = []
    avg = []

    std1 = []
    double_std1 = []
    same_dir_std1 = []
    triple_std1 = []
    double_same_dir_std1 = []

    for index in range(len(candle_data)):

        candle = candle_data[index]

        if len(avg) < candle_range:
            # index 4 is candle close
            avg.append(candle[4])

            if len(avg) >= 2:
                candle_gain.append(round((avg[-1] / avg[-2] - 1) * 100, 3))

        if len(avg) == candle_range:

            std = round(np.std(candle_gain), 4)     # calculates std of candle gains

            # print("candle gains=>", candle_gain)
            # print("candle gain 1std=", std, "candle gain 2std=", 2*std)
            # print("candle gain -1std=", std * (-1), "candle gain -2std=", (2 * std) * (-1))
            # print("-"*50, "current price", candle[4])

            if index+1 < len(candle_data) and index+2 < len(candle_data) and index+3 < len(candle_data):    # check for last candles in dataset
                # triple std1 move
                if abs(round((candle_data[index + 1][4] / candle[4] - 1) * 100, 2)) >= std:     # if candle gain is bigger than 1std

                    # print("-" * 200)
                    # print(f"current price -> {candle[4]}, next candle -> {candle_data[index + 1][4]}")
                    # print(f"move with 1std or more -> % change {round((candle_data[index + 1][4] / candle[4] - 1) * 100, 3)} %")
                    # print("-" * 200)

                    std1.append(1)  # this list is mostly used just for length that's why I append 1

                    if abs(round((candle_data[index + 2][4] / candle_data[index + 1][4] - 1) * 100, 2)) >= std:     # if 2nd candle also has bigger gain than 1std
                        double_std1.append(1)   # this list is mostly used just for length that's why I append 1

                        # print("2nd 1std MOVE IN A ROW", "*" * 200)
                        # print(f"move with 1std or more -> % change {round((candle_data[index + 1][4] / candle[4] - 1) * 100, 3)} %")
                        # print(f"current price -> {candle[4]}, next candle -> {candle_data[index + 1][4]} -> next candle -> {candle_data[index + 2][4]}")
                        # print("*" * 200)

                        # check if 1std moves are in the same direction
                        if round((candle_data[index + 2][4] / candle[4] - 1) * 100, 2) >= 0 and round((candle_data[index + 1][4] / candle[4] - 1) * 100, 2) >= 0:
                            same_dir_std1.append(1)     # this list is mostly used just for length that's why I append 1
                        elif round((candle_data[index + 2][4] / candle[4] - 1) * 100, 2) < 0 and round((candle_data[index + 1][4] / candle[4] - 1) * 100, 2) < 0:
                            same_dir_std1.append(1)     # this list is mostly used just for length that's why I append 1

                        if abs(round((candle_data[index + 3][4] / candle_data[index + 2][4] - 1) * 100, 2)) >= std:     # check if third candle is 1std move
                            triple_std1.append(1)
                            # print("3rd 1std MOVE IN A ROW", "*_*" * 100)
                            # print(f"move with 1std or more -> % change {round((candle_data[index + 1][4] / candle[4] - 1) * 100, 3)} %")
                            # print(f"current price -> {candle[4]}, next candle -> {candle_data[index + 1][4]} -> next candle -> {candle_data[index + 2][4]}")
                            # print("*_*" * 100)

                            # check if 1std moves are in the same direction
                            if round((candle_data[index + 3][4] / candle[4] - 1) * 100, 2) >= 0 and round((candle_data[index + 2][4] / candle[4] - 1) * 100, 2) >= 0:
                                double_same_dir_std1.append(1)      # this list is mostly used just for length that's why I append 1
                            elif round((candle_data[index + 3][4] / candle[4] - 1) * 100, 2) < 0 and round((candle_data[index + 2][4] / candle[4] - 1) * 100, 2) < 0:
                                double_same_dir_std1.append(1)      # this list is mostly used just for length that's why I append 1

            # removes first element since this is moving window
            candle_gain.pop(0)
            avg.pop(0)



    print("-" * 100)
    print(f"timespan {start_date.date()} -> {end_date.date()}")
    print(f"chance of 2nd 1std move when first 1std happens for {time_frame} and candle range {candle_range} is {round((len(double_std1)/len(std1))*100, 2)} %")
    print(f"chance of 2nd 1std move being in the same direction as first one is {round(len(same_dir_std1)/len(double_std1)*100, 2)} %")
    print(f"chance of 3rd 1std move when 2nd 1std happens for {time_frame} and candle range {candle_range} is {round((len(triple_std1) / len(double_std1)) * 100, 2)} %")
    print(f"chance of 3nd 1std move being in the same direction as 2nd one is {round(len(double_same_dir_std1) / len(triple_std1) * 100, 2)} %")
    print("-" * 100)
def std2_prob_movingWindow(file_path, candle_range, time_frame):

    # format = [open_time, open, high, low, close, volume, close_time, number_of_trades]
    ticker_data = bcd.get_data_by_date(start_date, end_date, file_path)
    candle_data = ticker_data.values.tolist()

    candle_gain = []
    avg = []
    std2 = []
    double_std2 = []
    same_dir_std2 = []

    for index in range(len(candle_data)):
        candle = candle_data[index]         # store current candle data

        if len(avg) < candle_range:
            # index 4 is candle close
            avg.append(candle[4])       # append candle close

            if len(avg) >= 2:
                candle_gain.append(round((avg[-1] / avg[-2] - 1) * 100, 3))

        if len(avg) == candle_range:
            std = round(np.std(candle_gain), 4)     # calculates std for candles gains

            # print("candle gains=>", candle_gain)
            # print("candle gain 1std=", std, "candle gain 2std=", 2*std)
            # print("candle gain -1std=", std * (-1), "candle gain -2std=", (2 * std) * (-1))
            # print("-"*50, "current price", candle[4])

            if index+1 < len(candle_data) and index+2 < len(candle_data):                   # check for the last candle in dataset
                if abs(round((candle_data[index+1][4]/candle[4]-1)*100, 2)) >= (std * 2):    # if candle gain is bigger thant 2std

                    # print("-"*200)
                    # print(f"current price -> {candle[4]}, next candle -> {candle_data[index+1][4]}")
                    # print(f"move with 2std or more -> % change {round((candle_data[index+1][4]/candle[4]-1)*100, 3)} %")
                    # print("-" * 200)
                    std2.append(1)      # this list is mostly used just for length that's why I append 1

                    if abs(round((candle_data[index+2][4]/candle_data[index+1][4]-1)*100, 2)) >= (std * 2):  # if 2nd candle i a row has gain of more than 2std
                        double_std2.append(1)   # this list is mostly used just for length that's why I append 1

                        # print("2nd 2std MOVE IN A ROW" ,"*" * 200)
                        # print(f"move with 2std or more -> % change {round((candle_data[index + 1][4] / candle[4] - 1) * 100, 3)} %")
                        # print(f"current price -> {candle[4]}, next candle -> {candle_data[index + 1][4]} -> next candle -> {candle_data[index + 2][4]}")
                        # print("*" * 200)

                        if round((candle_data[index+2][4]/candle[4]-1)*100, 2) >= 0 and round((candle_data[index+1][4]/candle[4]-1)*100, 2) >= 0:   # if candle gain is positive checks if next candle is also positive
                            same_dir_std2.append(1)     # this list is mostly used just for length that's why I append 1

                        elif round((candle_data[index+2][4]/candle[4]-1)*100, 2) < 0 and round((candle_data[index+1][4]/candle[4]-1)*100, 2) < 0:   # if candle gain is negative checks if next candle is also negative
                            same_dir_std2.append(1)     # this list is mostly used just for length that's why I append 1

            # removes first element since this is moving window
            candle_gain.pop(0)
            avg.pop(0)

    # results
    print("-" * 100)
    print(f"timespan {start_date.date()} -> {end_date.date()}")
    print(f"chance of 2nd 2std move when first 2std happens for {time_frame} and candle range {candle_range} is {round((len(double_std2)/len(std2))*100, 2)} %")
    print(f"chance of 2nd 2std move being in the same direction as first one is {round(len(same_dir_std2)/len(double_std2)*100, 2)} %")
    print("-" * 100)
def market_daily_H_L_distribution(start_date, end_date, file_path, ticker):

    # data format [open_time, open, high, low, close, volume, close_time, number_of_trades]
    data = bcd.get_data_by_date(start_date, end_date, file_path).values.tolist()
    # calculates how many years of data we have
    year_no = end_date.year - start_date.year


    if year_no != 0:
        # if year difference is one create values for both years,
        years = range(start_date.year, start_date.year + year_no + 1, 1)
    else:
        # if year difference is 0, only value is current year
        years = [end_date.year]



    weekly_data = []

    for year in years:  # for each year and each week compare data
        for i in range(1, 53,1):
            week = []
            for j in range(len(data)):
                # if year and week are the as in for loops append daily data to week list
                if data[j][0].isocalendar()[1] == i and data[j][0].isocalendar()[0] == year:
                    week.append(data[j])

            # append week data to weekly data list
            weekly_data.append(week)

    max_days = []
    min_days = []
    for week in range(len(weekly_data)):
        week_max_prc = 0
        week_min_prc = 10000000
        week_max = []
        week_min = []
        # for each day in weekly data
        for day in weekly_data[week]:
            #print(day)
            if day[2] > week_max_prc:   # if price is higher than previous max price
                week_max = day
                week_max_prc = day[2]   # set new high price to current price

            if day[3] < week_min_prc:   # if price is lower than previous lowest price
                week_min = day
                week_min_prc = day[3]   # set new low price to current price


        max_days.append(week_max)
        min_days.append(week_min)

    days = range(1,8,1)
    days_max = []
    days_min = []
    for day in days:
        day_tmp = []
        for i in max_days:
            if len(i) > 0:
                if i[0].isocalendar()[2] == day:
                    day_tmp.append(i)

        days_max.append(day_tmp)

    for day in days:
        day_tmp = []
        for i in min_days:
            if len(i) > 0:
                if i[0].isocalendar()[2] == day:
                    day_tmp.append(i)
        days_min.append(day_tmp)

    sum_max = 0
    sum_min = 0
    for i in days_max:
        sum_max = sum_max + len(i)

    for i in days_min:
        sum_min = sum_min + len(i)


    day_dict = {0:"mon", 1:"tue", 2:"wed", 3:"thu", 4:"fri", 5:"sat", 6:"sun"}

    print("timespan=", start_date.date(), "=>", end_date.date(), "-> " ,ticker)

    d_count = 0     # day counter
    for index in range(len(days_max)):
        day = day_dict[index]
        pct_chance_high = (len(days_max[index])/sum_max)*100
        print(f"chance of high on {day} is {round(pct_chance_high, 2)}%")

        d_count += 1


        text_str = f"{round(pct_chance_high, 2)}%"
        plt.bar(d_count, pct_chance_high, width=0.25)
        plt.text(d_count - 0.1, pct_chance_high + 0.25,text_str)


    plt.title(f"chance of high on for each day of the week for {ticker} in timespan {start_date.date()} -> {end_date.date()}")
    plt.xlabel("day")
    plt.ylabel("%")
    plt.xticks(range(1,8,1))
    plt.grid(alpha=0.3)
    figMan = plt.get_current_fig_manager()
    figMan.window.showMaximized()
    plt.show()


    print("-" * 100)

    d_count = 0     # day counter
    for index in range(len(days_min)):
        day = day_dict[index]
        pct_chance_high = (len(days_min[index]) / sum_max) * 100
        print(f"chance of low on {day} is {round(pct_chance_high, 2)}%")

        d_count += 1


        text_str = f"{round(pct_chance_high, 2)}%"
        plt.bar(d_count, pct_chance_high, width=0.25)
        plt.text(d_count - 0.1, pct_chance_high + 0.25,text_str)


    plt.title(f"chance of low on for each day of the week for {ticker} in timespan {start_date.date()} -> {end_date.date()}")
    plt.xlabel("day")
    plt.ylabel("%")
    plt.xticks(range(1,8,1))
    plt.grid(alpha=0.3)
    figMan = plt.get_current_fig_manager()
    figMan.window.showMaximized()
    plt.show()
def hourly_H_L_distribution(start_date, end_date, file_path, ticker):

    # data format [open_time, open, high, low, close, volume, close_time, number_of_trades]
    data = bcd.get_data_by_date(start_date, end_date, file_path).values.tolist()
    # calculates how many years of data we have
    year_no = end_date.year - start_date.year


    if year_no != 0:
        # if year difference is one create values for both years,
        years = range(start_date.year, start_date.year + year_no + 1, 1)
    else:
        # if year difference is 0, only value is current year
        years = [end_date.year]



    weekly_data = []

    for year in years:  # for each year and each week and each day of the week compare data
        for i in range(1, 53,1):
            week = []
            for day in range(1,8,1):
                tmp_day = []
                for j in range(len(data)):
                    # if year and week are the as in for loops append daily data to week list
                    if data[j][0].isocalendar()[1] == i and data[j][0].isocalendar()[0] == year and data[j][0].isocalendar()[2] == day:
                        tmp_day.append(data[j])     # appends data to current day list

                week.append(tmp_day)    # appends day to the week

            # append week data to weekly data list
            weekly_data.append(week)

    weekday_highs = [[], [], [], [], [], [], []]
    weekday_lows = [[], [], [], [], [], [], []]
    for week in weekly_data:
        for day in week:
            daily_high = 0
            daily_low = 1000000

            daily_high_tmp = []
            daily_low_tmp = []

            for trade in day:
                if daily_high < trade[2]:   # finds the daily high  trade[2]=high
                    daily_high = trade[2]
                    daily_high_tmp = trade

                if daily_low > trade[3]:    # finds the daily low  trade[3]=low
                    daily_low = trade[3]
                    daily_low_tmp = trade

            if len(daily_high_tmp) > 0 and len(daily_low_tmp) > 0:  # if list is not empty

                weekday_highs[daily_high_tmp[0].isocalendar()[2] - 1].append(daily_high_tmp)    # appends trades that contains high for specific day at it's day index
                weekday_lows[daily_low_tmp[0].isocalendar()[2]-1].append(daily_low_tmp)         # appends trades that contains low for specific day at it's day index

    # empty list for data that will be used for stats
    weekday_high_stats = [[], [], [], [], [], [], []]
    weekday_low_stats = [[], [], [], [], [], [], []]


    counter = 1
    for day in weekday_highs:
        # for each day create list of 24h for trades
        daily_highs = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
        for high in day:
            daily_highs[high[0].hour].append(high)  # appends trade that contains high to it's corresponding hour in daily_highs list

        weekday_high_stats[counter-1].append(daily_highs)   # appends day to the week
        counter += 1


    counter = 1
    for day in weekday_lows:
        daily_lows = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
        for low in day:
            daily_lows[low[0].hour].append(low)  # appends trade that contains low to it's corresponding hour in daily_lows list

        weekday_low_stats[counter-1].append(daily_lows) # appends day to the week
        counter += 1



    day_dict = {0: "mon", 1: "tue", 2: "wed", 3: "thu", 4: "fri", 5: "sat", 6: "sun"}

    #HIGHS
    for day in range(len(weekday_high_stats)):
        daily_sum = 0
        for hour in weekday_high_stats[day]:
            for trades in hour:
                daily_sum += len(trades)    # calculates sum for easier % calculation

        print("-" * 100)
        print("timespan=", start_date.date(), "->", end_date.date(), "ticker=", ticker)
        h_count = 0
        for hour in range(len(weekday_high_stats[day])):
            for trades in weekday_high_stats[day][hour]:
                len_trd = len(trades)

                current_day = day_dict[day]
                chance_of_high = round((len_trd/daily_sum)*100, 2)

                print(f"chance of high on {current_day} at hour {h_count} is:", chance_of_high, "%")
                h_count += 1

                # visualization
                text_str = f"{chance_of_high}%"
                plt.bar(h_count-1, chance_of_high, width=0.5)
                plt.text(h_count - 1.25, chance_of_high + 0.25,text_str)

            plt.title(f"chance of high on {current_day} for {ticker} in timespan {start_date.date()} -> {end_date.date()}")
            plt.xlabel("hour")
            plt.ylabel("%")
            plt.xticks(range(0,24,1))
            plt.grid(alpha=0.3)
            figMan = plt.get_current_fig_manager()
            figMan.window.showMaximized()
            plt.show()

            print("-"*100)


    # LOWS
    for day in range(len(weekday_low_stats)):
        daily_sum = 0
        for hour in weekday_low_stats[day]:
            for trades in hour:
                daily_sum += len(trades)     # calculates sum for easier % calculation

        print("-" * 100)
        print("timespan=", start_date.date(), "->", end_date.date(), "ticker=", ticker)
        h_count = 0
        for hour in range(len(weekday_low_stats[day])):
            for trades in weekday_low_stats[day][hour]:
                len_trd = len(trades)

                current_day = day_dict[day]
                chance_of_low = round((len_trd / daily_sum) * 100, 2)

                print(f"chance of low on {current_day} at hour {h_count} is:", chance_of_low, "%")
                h_count += 1
                # visualization
                text_str = f"{chance_of_low}%"
                plt.bar(h_count-1, chance_of_low, width=0.5)
                plt.text(h_count - 1.25, chance_of_low + 0.25, text_str)

            plt.title(f"chance of low on {current_day} for {ticker} in timespan {start_date.date()} -> {end_date.date()}")
            plt.xlabel("hour")
            plt.ylabel("%")
            plt.xticks(range(0, 24, 1))
            plt.grid(alpha=0.3)
            figMan = plt.get_current_fig_manager()
            figMan.window.showMaximized()
            plt.show()

    print("-" * 100)
# ------------------------------------------------------------

# DATA VARIABLES ----------------------------------------------
ticker = "BTCUSDT"
time_frame = "1D"

# file path
file_path = f"{root}/data/{ticker}_{time_frame}.p"

# start date of backtest
startDate = datetime.datetime(2020, 6, 1)
# end date of backtest
endDate = datetime.datetime(2020, 9, 1)
# -------------------------------------------------------------

backtest_data = bcd.get_data_by_date(startDate, endDate, file_path)
backtest_data["candle pct gain"] = round(
    (backtest_data["close"] / backtest_data["open"] - 1) * 100, 3)


def plt_candle_pct_chg(pct_changes, candle_data, close_times):
    labels = []
    for date in close_times:
        date_format = f"{date.date()}/{date.hour}:{date.minute}"
        labels.append(date_format)
        print(date_format)
    closes_x_labels = labels

    plt.subplots_adjust(top=0.95, bottom=0.05, hspace=15)

    close_graph = plt.subplot2grid((6, 1), (0, 0), rowspan=3)