###############################################################################
"""
명세서

1. 로드맵 : https://[Log in to view URL]
    (참고자료)
        https://[Log in to view URL]
        나중에 강환국 이평선 유투브 추가

2. 사용전략 : k-올웨더 120일_EMA & 20일_EMA

[[변경이력]]
(24.01.17) 최초생성


"""
##############################################################################


# 장이 열리는날이 아니면 프로그램 종료 ##########################################################
import exchange_calendars as ecals
import datetime as dt
from Util_discord_message import send_message

XKRX = ecals.get_calendar("XKRX")  # 한국 코드
ismarketopen = XKRX.is_session(dt.date.today().strftime("%Y-%m-%d"))  # 오늘은 개장일인지 확인

if ismarketopen == False:
    exit(0)
else:
    send_message(f'[연금저축_part_01]_K-올웨더 자산배분 시작합니다')
#############################################################################################
###########################################################################################


### 나머지 import #########################################################################
import pymysql, yaml, sys, atexit, requests, time, json, math
import pandas as pd
import Util_Hankook_KR_V3_devel_231114 as hk
import Util_Analysis_V3_devel_231114 as anal
from Util_get_access_token import get_access_token_daily

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


# 사용계좌 선택 / 디스코드 선택 ################################################################
with open('00_config.yaml', encoding='utf-8') as f:
    _cfg = yaml.load(f, Loader=yaml.FullLoader)
app_key = _cfg['APP_KEY']
app_secret = _cfg['APP_SECRET']
acc_no = _cfg['CANO']
acc_flag = _cfg['ACNT_PRDT_CD']
url_base = _cfg['URL_BASE']
discord_url = _cfg['DISCORD_WEBHOOK_URL']
###########################################################################################
###########################################################################################


### 토큰 및 클래스 선언 #########################################################
access_token = get_access_token_daily()
hk_sd = hk.Stock_data()
buy = hk.Order_buy()
sell = hk.Order_sell()
acc = hk.Account_Info()
dbu = hk.DB_Updater()  # 디비업데이터
anal_sd = anal.Stock_data()
anal_id = anal.Index_data()
anal_etf = anal.Etf_data()
anal_mt = anal.Market_timing()
anal_ma = anal.Ma()
anal_bb = anal.Bb()
anal_atr = anal.ATR()


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


### 공통함수 선언 ##############################################################

# 120 ema, 20ema 보다 크다면 산다
def get_buy_signal(code):
    period_1 = 20
    period_2 = 120

    a = hk_sd.get_current_price(code)
    b = anal_ma.get_ema_value(code, period_1)
    c = anal_ma.get_ema_value(code, period_2)

    if a > b and a > c:
        signal = True
    else:
        signal = False

    return signal


# 미사용
def get_div_rate_using_ma(code):
    a = anal_sd.get_current_price(code)
    ma_1 = anal_sd.get_price_before_ndays(code, -20)
    ma_2 = anal_sd.get_price_before_ndays(code, -40)
    ma_3 = anal_sd.get_price_before_ndays(code, -60)
    ma_4 = anal_sd.get_price_before_ndays(code, -80)
    ma_5 = anal_sd.get_price_before_ndays(code, -100)
    ma_6 = anal_sd.get_price_before_ndays(code, -120)
    ma_7 = anal_sd.get_price_before_ndays(code, -140)
    ma_8 = anal_sd.get_price_before_ndays(code, -160)
    ma_9 = anal_sd.get_price_before_ndays(code, -180)
    ma_10 = anal_sd.get_price_before_ndays(code, -200)
    ma_11 = anal_sd.get_price_before_ndays(code, -220)
    ma_12 = anal_sd.get_price_before_ndays(code, -240)
    print(a, ma_1, ma_3, ma_6, ma_9, ma_12)

    division_no = 12

    if a < ma_1:
        division_no -= 1
    if a < ma_2:
        division_no -= 1
    if a < ma_3:
        division_no -= 1
    if a < ma_4:
        division_no -= 1
    if a < ma_5:
        division_no -= 1
    if a < ma_6:
        division_no -= 1
    if a < ma_7:
        division_no -= 1
    if a < ma_8:
        division_no -= 1
    if a < ma_9:
        division_no -= 1
    if a < ma_10:
        division_no -= 1
    if a < ma_11:
        division_no -= 1
    if a < ma_12:
        division_no -= 1

    if division_no <= 1:
        division_no = 1

    print(f'division_no : {division_no}')

    division_rate = division_no / 12

    return division_rate


def make_money(required_money):
    korf_code = "423160"  # kodex kofr
    hold_qty, hold_amt, yield_rate, yield_amt = acc.get_one_stock_balance(korf_code)
    current_price = anal_sd.get_current_price(korf_code)
    print(f'코퍼 보유금액 : {hold_amt}')

    if hold_amt < required_money:
        send_message(f'돈이 없어요. 입금을 더 하세요')
    elif hold_amt > required_money:
        send_message(f'KORF 매도 해서 돈을 만듭니다')
        qty = int(required_money / current_price + 1)
        sell.bid_1(korf_code, qty)

    time.sleep(1)  # 매도 될때까지 기다린다.

    return


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


### 매매 로직 ##################################################################
if __name__ == '__main__':
    atexit.register(send_message, f"=프로그램 종료됨=")

    # 중요 변수
    invest_rate = 0.3  # 계좌 내 해당전략 비율, 다른전략과 함께사용, 초기테스트시 사용

    file_path_1 = 'rebalancing_data.json'

    purchase_list = ['102110', '232080', '360750', '133690','132030','261220', '385560', '471230', '304660', '305080']


    # 일자 구하기
    t_now = dt.datetime.now()
    today_date_str = t_now.strftime('%Y-%m-%d')
    today_ym_str = t_now.strftime('%Y-%m')

    # 기본변수 초기화 등
    total_amount, current_cash, bought_list = acc.update_account_info()  # 총잔고, 가용현금, 보유종목

    invest_money = total_amount * invest_rate

    # 주문 한번 나간거 다시 안나가게
    send_buy_order_list = []
    send_sell_order_list = []

    # 딕셔너리 키용
    ym_date_dict_key = 'ym_date'  # 딕셔너리 key
    buy_sig_dict_key = 'buy_signal'
    try_count_dict_key = 'try_count'
    name_dict_key = 'name'
    buy_amount_dict_key = 'buy_amount'
    weight_dict_key = 'weight'
    buy_qty_dict_key = "buy_qty"

    onetime_001, onetime_002, onetime_003, onetime_004, onetime_005 = True, True, True, True, True

    while True:
        try:

            t_now = dt.datetime.now()
            t_0905 = t_now.replace(hour=9, minute=5, second=0, microsecond=0)
            t_0920 = t_now.replace(hour=9, minute=20, second=0, microsecond=0)
            t_1500 = t_now.replace(hour=15, minute=0, second=0, microsecond=0)
            t_1510 = t_now.replace(hour=15, minute=15, second=0, microsecond=0)
            t_1515 = t_now.replace(hour=15, minute=15, second=0, microsecond=0)
            t_1520 = t_now.replace(hour=15, minute=20, second=0, microsecond=0)

            if t_now < t_1510:
                time.sleep(60)

            # 종가부근에서 수행한다.
            if t_1510 < t_now:

                # 리밸런싱용 데이타 (file_1)
                # 월간리밸런싱이니까 연-월을 만들고 없다면 최초거나 다음달이 된거니까 각 자산별 배분비율별로 purchase 데이터를 만든다.
                while onetime_001 == True:

                    onetime_001 = False

                    try:
                        with open(file_path_1, 'r', encoding='utf-8') as json_file:
                            rebalancing_data = json.load(json_file)

                    except FileNotFoundError:  # 파일 없으면 최초로 만듬
                        send_message("리밸런싱 데이터 최초 생성합니다")

                        rebalancing_data = {}

                        rebalancing_data[ym_date_dict_key] = today_date_str

                        purchase_list = ['102110', '232080', '360750', '133690','132030','261220', '385560', '471230', '304660', '305080']

                        # 1
                        rebalancing_data['102110'] = {name_dict_key: 'TIGER 200',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 2
                        rebalancing_data['232080'] = {name_dict_key: 'TIGER 코스닥150',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 3
                        rebalancing_data['360750'] = {name_dict_key: 'TIGER 미국 S&P500',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 4
                        rebalancing_data['133690'] = {name_dict_key: 'TIGER 미국나스닥100',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                       
                        # 5
                        rebalancing_data['132030'] = {name_dict_key: 'KODEX 골드선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.075,
                                                      buy_qty_dict_key: 0}

                        # 6
                        rebalancing_data['261220'] = {name_dict_key: 'KODEX WTI원유선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.075,
                                                      buy_qty_dict_key: 0}

                        # 7
                        rebalancing_data['385560'] = {name_dict_key: 'KBSTAR KIS국고채30년Enhanced',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 8
                        rebalancing_data['471230'] = {name_dict_key: 'KODEX 국고채10년액티브',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 9
                        rebalancing_data['304660'] = {name_dict_key: 'KODEX 미국채울트라30년선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 10
                        rebalancing_data['305080'] = {name_dict_key: 'TIGER 미국채10년선물 미국채울트라30년선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        

                        for code in purchase_list:
                            rebalancing_data[code][buy_sig_dict_key] = get_buy_signal(code)
                            
                            rebalancing_data[code][weight_dict_key] *= 1                            

                            current_price = hk_sd.get_current_price(code)
                            
                            rebalancing_data[code][buy_amount_dict_key] = int(
                                invest_money * rebalancing_data[code][weight_dict_key])

                            buy_qty = int(rebalancing_data[code][buy_amount_dict_key] / current_price)
                            rebalancing_data[code][buy_qty_dict_key] = buy_qty

                            send_message(
                                f'{rebalancing_data[code][name_dict_key]}_매수금액 : {rebalancing_data[code][buy_amount_dict_key]} / 매수수량 : {buy_qty}')
                            print(rebalancing_data[code])

                        # 저장하기
                        with open(file_path_1, 'w', encoding='utf-8') as json_file:
                            json.dump(rebalancing_data, json_file, ensure_ascii=False)
                        with open(file_path_1, 'r', encoding='utf-8') as json_file:
                            rebalancing_data = json.load(json_file)


                    

                    # 일간 단위 업데이트 
                    if rebalancing_data[ym_date_dict_key] != today_date_str:

                        send_message(f'일자가 바뀌어서 리밸런싱 데이터 갱신합니다.')

                        rebalancing_data = {}

                        rebalancing_data[ym_date_dict_key] = today_date_str

                        purchase_list = ['102110', '232080', '360750', '133690','132030','261220', '385560', '471230', '304660', '305080']

                        # 1
                        rebalancing_data['102110'] = {name_dict_key: 'TIGER 200',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 2
                        rebalancing_data['232080'] = {name_dict_key: 'TIGER 코스닥150',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 3
                        rebalancing_data['360750'] = {name_dict_key: 'TIGER 미국 S&P500',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                        # 4
                        rebalancing_data['133690'] = {name_dict_key: 'TIGER 미국나스닥100',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.0875,
                                                      buy_qty_dict_key: 0}

                       
                        # 5
                        rebalancing_data['132030'] = {name_dict_key: 'KODEX 골드선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.075,
                                                      buy_qty_dict_key: 0}

                        # 6
                        rebalancing_data['261220'] = {name_dict_key: 'KODEX WTI원유선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.075,
                                                      buy_qty_dict_key: 0}

                        # 7
                        rebalancing_data['385560'] = {name_dict_key: 'KBSTAR KIS국고채30년Enhanced',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 8
                        rebalancing_data['471230'] = {name_dict_key: 'KODEX 국고채10년액티브',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 9
                        rebalancing_data['304660'] = {name_dict_key: 'KODEX 미국채울트라30년선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        # 10
                        rebalancing_data['305080'] = {name_dict_key: 'TIGER 미국채10년선물 미국채울트라30년선물(H)',
                                                      buy_sig_dict_key: False,
                                                      try_count_dict_key: 0,
                                                      buy_amount_dict_key: 0,
                                                      weight_dict_key: 0.125,
                                                      buy_qty_dict_key: 0}

                        

                        for code in purchase_list:
                            rebalancing_data[code][buy_sig_dict_key] = get_buy_signal(code)
                            
                            rebalancing_data[code][weight_dict_key] *= 1                            

                            current_price = hk_sd.get_current_price(code)
                            
                            rebalancing_data[code][buy_amount_dict_key] = int(
                                invest_money * rebalancing_data[code][weight_dict_key])

                            buy_qty = int(rebalancing_data[code][buy_amount_dict_key] / current_price)
                            rebalancing_data[code][buy_qty_dict_key] = buy_qty

                            send_message(
                                f'{rebalancing_data[code][name_dict_key]}_매수금액 : {rebalancing_data[code][buy_amount_dict_key]} / 매수수량 : {buy_qty}')
                            print(rebalancing_data[code])

                        # 저장하기
                        with open(file_path_1, 'w', encoding='utf-8') as json_file:
                            json.dump(rebalancing_data, json_file, ensure_ascii=False)
                        with open(file_path_1, 'r', encoding='utf-8') as json_file:
                            rebalancing_data = json.load(json_file)

                    # 리밸런싱 데이터 출력하기
                    for item in rebalancing_data.items():
                        send_message(f"{item}")

                bought_list = acc.get_holding_stock_list()

                for code in rebalancing_data.keys():

                    # 키중에 data는 아무것도 안한다.
                    if code == "ym_date":
                        continue


                    # 키가 코드인 것들만
                    else:
                        hold_qty, hold_amt, yield_rate, yield_amt = acc.get_one_stock_balance(code)

                        print(hold_qty)

                        print(rebalancing_data[code])

                        gap_qty = rebalancing_data[code][buy_qty_dict_key] - hold_qty

                        ask_1, bid_1 = hk_sd.get_ask_bid_price(code)
                        ask_1, bid_1 = int(ask_1), int(bid_1)

                        etf_nav = anal_etf.get_etf_nav(code)

                        # 매도 로직
                        if code not in send_sell_order_list:

                            # buy_시그널 매도면 해당종목 전량 매도
                            if rebalancing_data[code][buy_sig_dict_key] == False and code in bought_list:
                                # 사고 파는건 nav랑 비교해서 0.5%이내면
                                sell.ask_1(code, hold_qty)
                                send_sell_order_list.append(code)


                            

                        # 매수 로직
                        if code not in send_buy_order_list:
                            # 매수

                            if gap_qty > 0 and rebalancing_data[code][
                                buy_sig_dict_key] == True and bid_1 < etf_nav * 1.005:

                                buy_amount_adj = gap_qty * ask_1

                                if rebalancing_data[code][try_count_dict_key] < 3:

                                    send_message(f'{rebalancing_data[code][name_dict_key]}_매수 진행합니다')

                                    current_cash = acc.get_possible_order_cash()

                                    if current_cash > buy_amount_adj:
                                        buy.bid_1_qty(code, gap_qty)
                                        send_buy_order_list.append(code)
                                        rebalancing_data[code][try_count_dict_key] += 1
                                    if current_cash < buy_amount_adj:
                                        a = buy_amount_adj - current_cash
                                        make_money(a)
                                        buy.bid_1_qty(code, gap_qty)
                                        send_buy_order_list.append(code)
                                        rebalancing_data[code][try_count_dict_key] += 1

                                elif rebalancing_data[code][try_count_dict_key] == 3:
                                    send_message(f'{rebalancing_data[code][name_dict_key]}_시장가 매수 진행합니다')

                                    current_cash = acc.get_possible_order_cash()

                                    if current_cash > buy_amount_adj:
                                        buy.market_qty(code, gap_qty)
                                        send_buy_order_list.append(code)
                                        rebalancing_data[code][try_count_dict_key] += 1
                                    if current_cash < buy_amount_adj:
                                        a = buy_amount_adj - current_cash
                                        make_money(a)
                                        buy.market_qty(code, gap_qty)
                                        send_buy_order_list.append(code)
                                        rebalancing_data[code][try_count_dict_key] += 1

                                elif rebalancing_data[code][try_count_dict_key] > 3:
                                    send_message(f'[ERROR]_{rebalancing_data[code][name_dict_key]}_시장가 매수 후 에도 리밸런싱 중 ')

            if t_1515 < t_now:  # 유동성공급자 9시5분부터, 초반에는 변동성이 심하니 한20분정도로 이거 종가로 바꾸자
                # 저장하기
                with open(file_path_1, 'w', encoding='utf-8') as json_file:
                    json.dump(rebalancing_data, json_file, ensure_ascii=False)

                # 남은 돈 kofr 사기
                current_cash = acc.get_possible_order_cash()
                if current_cash > 500000:
                    buy.ask_1("423160", current_cash)

                send_message(f'리밸런싱 끝내고 종료합니다')
                exit(0)

        # maxretry
        except requests.packages.urllib3.exceptions.MaxRetryError:
            send_message(f'Max Retry Error 60초 대기 후 재실행')
            time.sleep(60)
        except requests.packages.urllib3.exceptions.ConnectionError:
            send_message(f'Connection 에러발생 60초 대기 후 재실행')
            time.sleep(60)
        # 위에거랑 차이가 뭐지
        except requests.exceptions.ConnectionError:
            send_message(f'Connection 에러 2번째 발생 60초 대기 후 재실행')
            time.sleep(60)
        # 최상위 에러는 마지막에 놔둬야한다. 그리고 위 에러말고는 종료하게 만든다.
        except Exception as e:
            send_message(f"[오류 발생] : {e}")
            exit(0)


Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: