###############################################################################
"""
명세서
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)
To embed this project on your website, copy the following code and paste it into your website's HTML: