StatArb / StatBot / Execution / main_execution.py
main_execution.py
Raw
# Remove Pandas Future Warnings
import warnings

from matplotlib.transforms import TransformNode
warnings.simplefilter(action='ignore', category=FutureWarning)

# General Imports
from config_execution_api import signal_positive_ticker
from config_execution_api import signal_negative_ticker
from config_execution_api import volatility_check
from config_execution_api import profit_target
from config_execution_api import spread_exit
from config_execution_api import hedge_ratio
from config_execution_api import stop_loss_fail_safe
from func_get_price import get_latest_price
from func_position_calls import open_position_confirmation
from func_position_calls import active_position_confirmation
from func_trade_management import manage_new_trades
from func_execution_calls import set_leverage
from func_close_positions import close_all_positions
from func_get_zscore import get_latest_zscore
#from config_ws_connect import ws_public          # <- original code websocket method
from config_ws_connect import ws_public_get_orderbook   # <- new websocket method
from config_ws_connect import subs_public
from func_save_status import save_status
from requests.exceptions import ConnectionError, Timeout, RequestException
from exceptions import FailedRequestError, InvalidRequestError
from json.decoder import JSONDecodeError
from config_execution_api import signal_trigger_thresh
from func_get_time import convert_time
from func_position_calls import get_closed_pnl_info
from func_position_calls import get_position_info
from tabulate import tabulate
from func_check_volatility import check_volatility
from func_calcultions import get_trade_details
import time
import datetime
import schedule as sd
import pandas as pd
import os.path
import os
import sys

""" RUN STATBOT """
if __name__ == "__main__":

    # Initial printout
    print("StatBot initiated...")

    # Initialise variables
    status_dict = {"message": "starting..."}
    order_long = {}
    order_short = {}
    signal_sign_positive = False
    signal_side = ""
    kill_switch = 0

    start_time = 0
    long_order_qty = 0
    long_order_time = ""
    long_order_price = 0
    short_order_qty = 0
    short_order_time = ""
    short_order_price = 0

    long_ticker = ""
    short_ticker = ""
    long_ticker_pnl = 0
    short_ticker_pnl = 0
    zscore_entry = 0
    zscore_exit = 0

    exception = False
    
    low_volatility = True

    long_pnl_un = 0
    short_pnl_un = 0
    long_pos_value = 0
    short_pos_value = 0
    net_pnl_un = 0
    net_pos_value = 0

    neg_price = 0
    pos_price = 0
    spread = 0

    # Save status
    save_status(status_dict)

    # Set leverage in case forgotten to do so on the platform
    print("Setting leverage...")
    set_leverage(signal_positive_ticker)
    set_leverage(signal_negative_ticker)

    # Commence bot
    print("Seeking trades...")
    while True:

        # Keep alive
        try:
            #trade = ws_public.fetch(subs_public[0])        # <- original code websocket method
            trade = ws_public_get_orderbook(subs_public[0]) # <- new websocket method
            # pos_orderbook = ws_public.fetch(f"orderBookL2_25.{signal_positive_ticker}")
            # neg_orderbook = ws_public.fetch(f"orderBookL2_25.{signal_negative_ticker}")
            
            # print("\n")
            # print('-------------------------------------------------------------------')
            # print("Main")
            # print(pos_orderbook) 
            # print(neg_orderbook)
            # print('-------------------------------------------------------------------')
            # print("\n")

        except (ConnectionError, Timeout, RequestException, JSONDecodeError, FailedRequestError, InvalidRequestError) as e:
            print(e)
            continue
        # Pause - protect API
        time.sleep(3)


        # Check if open trades already exist
        is_p_ticker_open = open_position_confirmation(signal_positive_ticker)
        is_n_ticker_open = open_position_confirmation(signal_negative_ticker)
        is_p_ticker_active = active_position_confirmation(signal_positive_ticker)
        is_n_ticker_active = active_position_confirmation(signal_negative_ticker)
        checks_all = [is_p_ticker_open, is_n_ticker_open, is_p_ticker_active, is_n_ticker_active]
        is_manage_new_trades = not any(checks_all)

        print("Manage new trade: ", is_manage_new_trades)
        # Save status
        status_dict["message"] = "Initial checks made..."
        status_dict["checks"] = checks_all
        save_status(status_dict)

        if not is_manage_new_trades and kill_switch == 0: # Close pending position(s) which was/were failed to be closed (e.g. cancelled)
            kill_switch = 2

        # Check for signal and place new trades
        if is_manage_new_trades and kill_switch == 0:
            status_dict["message"] = "Managing new trades..."
            save_status(status_dict)
            print("Manage new trade")
            kill_switch, signal_side, long_order_qty, long_order_time, long_order_price, short_order_qty, short_order_time, short_order_price, zscore_entry, exception = manage_new_trades(kill_switch)
            #print(kill_switch, signal_side, long_order_qty, long_order_time, long_order_price, short_order_qty, short_order_time, short_order_price, zscore_entry, exception)
            print(f"")

            if long_order_time and short_order_time:
                start_time = convert_time(long_order_time)   #min(convert_time(long_order_time), convert_time(short_order_time))
            if exception:
                continue
        print("Kill switch: ",kill_switch)

        # Managing open kill switch if positions change or should reach 2
        # Check for signal to be false
        if kill_switch == 1:

            # Get and save the latest z-score
            zscore, signal_sign_positive, exception = get_latest_zscore()
            if exception:
                continue
            # Get and print datetime
            now = datetime.datetime.now()
            current_time = now.strftime("%Y-%m-%d %H:%M")
            print(f"Datetime: {current_time},", f"Z-score: {round(zscore,2)}")
            print("Signal side (kill switch=1): ",signal_side) 
            # Bot 2 exit - not hot <--Copied from Bonus
            # if abs(zscore) < signal_trigger_thresh:
            #     kill_switch = 2
            #     zscore_exit = zscore
            
            #Close positions - Mean Reversion Exit (Option #1)
            # if signal_side == "positive" and zscore < -signal_trigger_thresh: # zscore < -signal_trigger_thresh for extreme opposite exit
                # kill_switch = 2
                # long_ticker = signal_positive_ticker
                # short_ticker = signal_negative_ticker
                # zscore_exit = zscore    
            # if signal_side == "negative" and zscore >= signal_trigger_thresh: # zscore >= signal_trigger_thresh for extreme opposite exit
                # kill_switch = 2
                # long_ticker = signal_negative_ticker
                # short_ticker = signal_positive_ticker
                # zscore_exit = zscore 

            if signal_side == "positive":
                long_ticker = signal_positive_ticker
                short_ticker = signal_negative_ticker
                if zscore < -signal_trigger_thresh:
                    kill_switch =2
                    zscore_exit = zscore 
            if signal_side == "negative":
                long_ticker = signal_negative_ticker
                short_ticker = signal_positive_ticker
                if zscore >= signal_trigger_thresh:     
                    kill_switch =2
                    zscore_exit = zscore 

            _, _, long_pnl_un, long_pos_value, exception = get_position_info(long_ticker)
            print("Long ticker: ", long_ticker, " Long PNL un: ", long_pnl_un, " Long POS value: ", long_pos_value )
            if exception:
                continue
            _, _, short_pnl_un, short_pos_value, exception = get_position_info(short_ticker)
            print("Short ticker: ", short_ticker, " Short PNL un: ", short_pnl_un, " Long POS value: ", short_pos_value )
            if exception:
                continue            
            net_pnl_un = long_pnl_un + short_pnl_un
            net_pos_value = long_pos_value + short_pos_value

            print("Net POS value: ",net_pos_value," Net PNL value: ", net_pnl_un, " Current Profit ratio: ", net_pnl_un/(net_pos_value-net_pnl_un), "\n")
            if (net_pnl_un/(net_pos_value-net_pnl_un)) < -stop_loss_fail_safe:
                kill_switch = 2

            print("Current PnL: ", round(net_pnl_un/(net_pos_value-net_pnl_un),2),"vs Stop Loss: ", -stop_loss_fail_safe)    

            # # Put back to zero if trades are closed <-- DON'T UNCOMMENT this original code.  See remarks below
            # print("Manage new trade: ", is_manage_new_trades, "Kill switch: ", kill_switch)
            # if is_manage_new_trades and kill_switch != 2:  <- This will always force kill_switch = 0 in the 1st round of loop b4 is_manage_new_trade changes status
            #     kill_switch = 0

            # Close positions - Profit% Exit (Option #2)
            # if signal_side == "positive":
            #     long_ticker = signal_positive_ticker
            #     short_ticker = signal_negative_ticker
            # if signal_side == "negative":
            #     long_ticker = signal_negative_ticker
            #     short_ticker = signal_positive_ticker 

            # _, _, long_pnl_un, long_pos_value, exception = get_position_info(long_ticker)
            # print("Long ticker: ", long_ticker, " Long PNL un: ", long_pnl_un, " Long POS value: ", long_pos_value )
            # if exception:
            #     continue
            # _, _, short_pnl_un, short_pos_value, exception = get_position_info(short_ticker)
            # print("Short ticker: ", short_ticker, " Short PNL un: ", short_pnl_un, " Long POS value: ", short_pos_value )
            # if exception:
            #     continue            
            # net_pnl_un = long_pnl_un + short_pnl_un
            # net_pos_value = long_pos_value + short_pos_value

            # print("Net POS value: ",net_pos_value," Net PNL value: ", net_pnl_un, " Current Profit ratio: ", net_pnl_un/(net_pos_value-net_pnl_un), "\n")
            # if (net_pnl_un/(net_pos_value-net_pnl_un))> profit_target or (net_pnl_un/(net_pos_value-net_pnl_un)) < -stop_loss_fail_safe:
            #     kill_switch = 2

            # print("Current PnL: ", net_pnl_un/(net_pos_value-net_pnl_un),"vs Profit Target: ", profit_target, "vs Stop Loss: ", -stop_loss_fail_safe)

            # Close positions - Zero Spread Exit (Option #3)
            # if signal_side == "positive":
            #     long_ticker = signal_positive_ticker
            #     short_ticker = signal_negative_ticker
            #     pos_price = get_latest_price(long_ticker,'Buy')
            #     neg_price = get_latest_price(short_ticker, 'Sell')
            #     spread = neg_price - hedge_ratio * pos_price
            #     if spread < spread_exit:
            #         kill_switch = 2
            #     print("Long ticker: ", long_ticker, " Long price: ", pos_price, " Short ticker: ", short_ticker, " Short price: ", neg_price)   
            # else:
            #     long_ticker = signal_negative_ticker
            #     short_ticker = signal_positive_ticker
            #     neg_price = get_latest_price(long_ticker,'Buy')
            #     pos_price = get_latest_price(short_ticker, 'Sell')
            #     spread = hedge_ratio * pos_price - neg_price
            #     if spread < spread_exit:
            #         kill_switch = 2
            #     print("Long ticker: ", long_ticker, " Long price: ", neg_price, " Short ticker: ", short_ticker, " Short price: ", pos_price)

            # print("Current Spread: ", spread, "\n")

            # _, _, long_pnl_un, long_pos_value, exception = get_position_info(long_ticker)
            # print("Long ticker: ", long_ticker, " Long PNL un: ", long_pnl_un, " Long POS value: ", long_pos_value )
            # if exception:
            #     continue
            # _, _, short_pnl_un, short_pos_value, exception = get_position_info(short_ticker)
            # print("Short ticker: ", short_ticker, " Short PNL un: ", short_pnl_un, " Long POS value: ", short_pos_value )
            # if exception:
            #     continue            
            # net_pnl_un = long_pnl_un + short_pnl_un
            # net_pos_value = long_pos_value + short_pos_value

            # print("Net POS value: ",net_pos_value," Net PNL value: ", net_pnl_un, " Current Profit ratio: ", net_pnl_un/(net_pos_value-net_pnl_un), "\n")
            # if (net_pnl_un/(net_pos_value-net_pnl_un)) < -stop_loss_fail_safe:
            #     kill_switch = 2

            # print("Current PnL: ", net_pnl_un/(net_pos_value-net_pnl_un),"vs Stop Loss: ", -stop_loss_fail_safe)

        # Close all active orders and positions
        if kill_switch == 2:
            
            if volatility_check:
                low_volatility = not any ([check_volatility(signal_positive_ticker), check_volatility(signal_negative_ticker)])
    
            if low_volatility:
                print("Low Volatile Market - Ready for Exit")
            
                print("Closing all positions...")
                status_dict["message"] = "Closing existing trades..."
                save_status(status_dict)
                kill_switch, close_long_order_qty, close_long_order_time, close_long_order_price, close_short_order_qty, close_short_order_time, close_short_order_price = close_all_positions(kill_switch, long_ticker, short_ticker)
                
                long_ticker_pnl,_ = get_closed_pnl_info(long_ticker,start_time)
                short_ticker_pnl,_ = get_closed_pnl_info(short_ticker,start_time)

                print("-------------")
                print("Trade Summary")
                print("-------------")
                df = pd.DataFrame({
                'Long symbol' : [long_ticker],
                'Open Long qty' : [long_order_qty],
                'Open Long time' : [long_order_time],
                'Open Long price' : [long_order_price],
                'Close Long time' : [close_long_order_time],
                'Close Long price' : [close_long_order_price],
                'Long PnL' : [round(long_ticker_pnl,2)],
                'Short symbol' : [short_ticker],
                'Open Short qty' : [short_order_qty],
                'Open Short time' : [short_order_time],
                'Open Short price' : [short_order_price],
                'Close Short time' : [close_short_order_time],
                'Close Short price' : [close_short_order_price],
                'Short PnL' : [round(short_ticker_pnl,2)],
                'Net PnL' : [round((long_ticker_pnl+short_ticker_pnl),2)],
                'Z score thld' : [signal_trigger_thresh],
                'Z score entry' : [round(zscore_entry,2)],
                'Z score exit' : [round(zscore_exit,2)]})

                print(tabulate(df, headers='keys', tablefmt='psql', showindex=False))

                if(os.path.isfile("4_trade_summary.csv")):
                    df.to_csv("4_trade_summary.csv", mode='a', index= False,header=False)
                else:
                    df.to_csv("4_trade_summary.csv", index= False)

                print("File for trade summary saved.")

                # Sleep for 5 seconds
                time.sleep(5)