# 명 세 서 ##############################################################################
"""
명세서
1. 사용전략
가. stock_analysis 중 value_1 중 per, pbr, pcr, psr, dy 활용
2. 사용 DB
가. db_name = "stock_auto_trading"
나. table_name = "select_per_pbr_pcr_psr_dy"
[변경이력]
(23.10.24)
마켓타이밍 3,5,10 적용하자, 장기전략이니까
access 토큰 하루에 한번만 발급하게
(23.11.07)
마켓타이밍 매수 시작전 확인하게 바꿈 : 오전에 확인하니까 장마감에 놓쳐버림
장중에도 atr 매수/매도, 7% 매도는 적용하도록 변경
(23.11.)
마켓타이밍 매수 시작전 확인하게 바꿈 : 오전에 확인하니까 놓쳐버림
장중에도 atr 매수/매도, 7% 매도는 적용하도록 변경
(23.11.20)
장중 atr 매수는 마켓타이밍이 True일때만 사게 만듬
(23.12.10)
마켓타이밍 : 코스닥 → 코스피 사용
(24.02.15)
-7% 매도 후 해당 종목없다고 에러남
어디서 에러가 났는지 모르겠는데(이거 for문 안에 조건문에 사용되는것도 bought_Data → bought_data.copy()로 바꿔야 할거 같음
15시 이전for문에 대해서 바꿈
(24.02.19)
-7% 매도 후 해당 종목없다고 에러남
어디서 에러가 났는지 모르겠는데(이거 for문 안에 조건문에 사용되는것도 bought_Data → bought_data.copy()로 바꿔야 할거 같음
: 그게 아니고 bought_list를 실시간 업데이트 해서 돌려야 함
"""
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
# 장이 열리는날이 아니면 프로그램 종료 ##################################################################
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'가치(per,pbr,pcr,psr,배당) 시작합니다.')
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
### 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()
sd = hk.Stock_data()
buy = hk.Order_buy()
sell = hk.Order_sell()
acc = hk.Account_Info()
dbu = hk.DB_Updater() # 디비업데이터
hk_cm = hk.Common_util()
mt = anal.Market_timing()
ma = anal.Ma()
bb = anal.Bb()
anal_sd = anal.Stock_data()
atr = anal.ATR()
#############################################################################
### 공통함수 선언 ##############################################################
# 20일 전보다 크다
def get_market_timing():
code = 'KS11' # 코스닥지수
buy_signal = mt.compare_to_ndays_ago(code=code, period=20)
return buy_signal
###############################################################################
### 매매 로직 ##################################################################
"""
1. 바이시그널이 : 매수
종가에 적용
최초매수
보유일 90일 이상은 매도(?)
보유일 90일 미만은 보유
최초매수는 보유금액의 50%,
2. 바이시그널이 : 매도
전부 매도
서브데이터 : 리밸런싱 필요로 변경
3. 종목별 수익률
종가에 적용
-7%면 매도(이건 안넣는게 좋은지 않좋은지 검증필요, 젠포트 넣어봤는데 오히려수익이 준다. 하지만 별차이 안남)
atr_count가 0보다 큰데 -2atr이면 매도
6. 터틀 atr 적용
1 atr 상승시 매수 2atr 하락시 매도
5. 남은돈은 kofr에 투자한다.
7. 100분할해서 50개에 나눠서 투자하고 나머지 50%는 atr 불타기 한다.(최대10회)
"""
atexit.register(send_message, f"=프로그램 종료됨=")
t_now = dt.datetime.now()
today_date_str = t_now.strftime('%Y-%m-%d')
buy_signal = get_market_timing()
total_amount, current_cash, bought_list = acc.update_account_info() # 총잔고, 가용현금, 보유종목
print(bought_list)
max_number_of_holding_stocks = 50
buy_unit = total_amount / 100 # 1회차 1%씩
# 디비 접속 및 종목가져오기
db_name = "stock_auto_trading"
table_name = "select_per_pbr_pcr_psr_dy"
engine = pymysql.connect(host='localhost', user='root', password='njmr0623$$', db=f'{db_name}', charset='utf8')
purchase_df = pd.read_sql(f"select * from {table_name} where 기준일 = (select max(기준일) from {table_name})", con=engine)
purchase_df = purchase_df.set_index(keys='종목코드', drop=False)
purchase_dict = purchase_df.T.to_dict()
print(purchase_dict)
### 구매목록 데이터 관리하기 ############################################
# 불러오기 or 최초만들기
try:
with open("bought_data.json", 'r', encoding='utf-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
except FileNotFoundError: # 파일 없으면 최초로 만듬
with open("bought_data.json", 'w', encoding='utf-8') as temp_out:
bought_data = {}
# date_update
bought_data['date'] = today_date_str
# sub_data 초기화
if len(bought_list) > 0:
for code in bought_list:
name = sd.get_stock_name(code)
current_price = sd.get_current_price(code)
stock_atr = atr.get_atr_value_ndays(code, 10)
bought_data[code] = {'name': name,
'first_buy_date': today_date_str,
'last_buy_price': current_price, # 매수시 update
'stock_atr': stock_atr, # 매일 초기화
'add_price': int(current_price + 1 * stock_atr),
# 매일 초기화 &매수시 update, atr이 매일 바뀌니까, 거래시 update,#최종매수가보다 1atr 높음녀 1unit 추가
'loss_cut': int(current_price - 2 * stock_atr),
# 매일 초기화 &매수시 update, atr이 매일 바뀌니까,#거래시 update, # 최종매수가보다 2atr 낮은값, 매도조건
'atr_trans_cnt': 0} # 매도시 update, #atr 불타기 10회까지만 허용
json.dump(bought_data, temp_out, ensure_ascii=False)
print(bought_data)
# sub data 관리
# 오늘 날짜라면 오늘 처음이거나 중간에 멈추고 다시시작한거다 기존데이터에 불러오고
# 오늘 날짜 아니라면 atr 초기화 해주자
if t_now - dt.datetime.strptime(bought_data['date'], '%Y-%m-%d') < dt.timedelta(days=1):
with open("bought_data.json", 'r', encoding='utf-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 어제파일이라면 오늘거로 업데이트 해준다.
else:
with open("bought_data.json", 'r', encoding='utf-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
bought_data['date'] = today_date_str # 오늘날짜 업데이트
if len(bought_list) > 0:
for code in bought_list:
# (case_01) bought_list에도 있고 bought_data에도 있다면 atr 업데이트 한다.
if code in bought_data.copy().keys():
name = sd.get_stock_name(code)
stock_atr = atr.get_atr_value_ndays(code, 10)
# 값 업데이트 및 초기화
bought_data[code]['name'] = name
bought_data[code]['stock_atr'] = stock_atr
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
# (case_02) 없다면 누락된거다. 신규로 데이터 만들자
elif code not in bought_data.copy().keys():
name = sd.get_stock_name(code)
current_price = sd.get_current_price(code)
stock_atr = atr.get_atr_value_ndays(code, 10)
bought_data[code] = {'name': name,
'first_buy_date': today_date_str,
'last_buy_price': current_price, # 매수시 update
'stock_atr': stock_atr, # 매일 초기화
'add_price': int(current_price + 1 * stock_atr),
'loss_cut': int(current_price - 2 * stock_atr),
'atr_trans_cnt': 0}
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# data파일에는 있는데 bought_lit에 없다면 삭제한다.
for code in bought_data.copy().keys():
if code == "date":
continue
else:
if code not in bought_list:
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
### 로직 #########################################################################################################
send_message(f"가치1(per,pbr,pcr,psr,배당)_시작합니다.")
send_message(f'[1회 매수금액] : {buy_unit:,}원')
acc.account_info_to_msg()
send_message(f"{purchase_dict}")
buy_signal = get_market_timing()
send_message(f"buy_signal : {buy_signal}")
while True:
try:
t_now = dt.datetime.now()
t_0900 = t_now.replace(hour=9, minute=0, second=0, microsecond=0)
t_0930 = t_now.replace(hour=9, minute=0, 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=10, 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)
t_1530 = t_now.replace(hour=15, minute=30, second=0, microsecond=0)
print(f'{t_now}_Gen_04 동작 중 입니다.')
# 장중에는 보유종목 가지고 매도조건을 감시간다.
if t_0900 < t_now < t_1510:
total_amount, current_cash, bought_list = acc.update_account_info() # 총잔고, 가용현금, 보유종목
time.sleep(10)
if len(bought_list) > 0:
for code in bought_list: # bought_list는 위에서 구했을거다. 장중에는 기존 bought 리스트 가지고만 거래한다.
current_price = sd.get_current_price(code)
hold_qty, hold_amt, yield_rate, yield_amt = acc.get_one_stock_balance(code)
if code == 'date': # 종목코드 아닌거 패스하기
continue
# ATR 매수
if current_price > bought_data.copy()[code]['add_price'] and bought_data.copy()[code][
'atr_trans_cnt'] < 10 and buy_signal == True:
send_message(f'{bought_data[code]["name"]}_ATR 추가 매수합니다.')
if current_cash > buy_unit:
buy.bid_1(code, buy_unit)
stock_atr = atr.get_atr_value_ndays(code, 10)
##수정필요 이것도 성공했을때만 업데이트 해야하는데.... 주문정보를 가져와야한다.
bought_data[code]['atr_trans_cnt'] = current_price
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
elif current_cash < buy_unit:
hk_cm.make_money_using_kofr(buy_unit - current_cash)
buy.bid_1(code, buy_unit)
##수정필요 이것도 성공했을때만 업데이트 해야하는데.... 주문정보를 가져와야한다.
bought_data[code]['atr_trans_cnt'] = current_price
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 7% 하락시 매도
if yield_rate < -7:
send_message(f'{bought_data[code]["name"]}_-7% 하락 매도')
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# ATR 매도
if current_price < bought_data.copy()[code]['loss_cut'] and bought_data.copy()[code][
'atr_trans_cnt'] > 0:
send_message(f'{bought_data[code]["name"]}_ATR 매도 합니다.')
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 종가부근에서 마켓타이밍에 따라 매매 한다.
if t_1510 < t_now: # 어차피 하루에 한번만 돌릴거다
buy_signal = get_market_timing()
send_message(f'마켓타이밍 : {buy_signal}')
if buy_signal == True:
send_message(f'매수조건 만족되어 매수 시작합니다.')
for code in purchase_dict.copy().keys():
current_price = sd.get_current_price(code)
total_amount, current_cash, bought_list = acc.update_account_info() # 총잔고, 가용현금, 보유종목
hold_qty, hold_amt, yield_rate, yield_amt = acc.get_one_stock_balance(code)
# 최초매수
if code not in bought_data.keys() and len(bought_list) < 50:
send_message(f'{purchase_dict[code]["종목명"]}_최초 매수합니다.')
# 현재캐쉬와 살금액 비교해서 모자라면 돈만들어서 사기
if current_cash > buy_unit:
buy.bid_1(code, buy_unit)
stock_atr = atr.get_atr_value_ndays(code, 10)
# 형태 셋팅
bought_data[code] = {'name': purchase_dict[code]['종목명'],
'first_buy_date': today_date_str,
'last_buy_price': current_price, # 매수시 update
'stock_atr': stock_atr, # 매일 초기화
'add_price': 0,
'loss_cut': 0,
'atr_trans_cnt': 0}
bought_data[code]['first_buy_date'] = today_date_str
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
elif current_cash < buy_unit:
hk_cm.make_money_using_kofr(buy_unit - current_cash)
buy.bid_1(code, buy_unit)
stock_atr = atr.get_atr_value_ndays(code, 10)
# 최초매수니까 딕셔너리에 데이터 없다. 형태 셋팅
bought_data[code] = {'name': purchase_dict[code]['종목명'],
'first_buy_date': today_date_str,
'last_buy_price': current_price, # 매수시 update
'stock_atr': stock_atr, # 매일 초기화
'add_price': 0,
'loss_cut': 0,
'atr_trans_cnt': 0}
bought_data[code]['first_buy_date'] = today_date_str
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# ATR 매수
if current_price > bought_data[code]['add_price'] and bought_data[code]['atr_trans_cnt'] < 10:
send_message(f'{bought_data[code]["name"]}_ATR 추가 매수합니다.')
# 현재캐쉬와 살금액 비교해서 모자라면 돈만들어서 사기
if current_cash > buy_unit:
buy.bid_1(code, buy_unit)
bought_data[code]['first_buy_date'] = today_date_str
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
elif current_cash < buy_unit:
hk_cm.make_money_using_kofr(buy_unit - current_cash)
buy.bid_1(code, buy_unit)
bought_data[code]['first_buy_date'] = today_date_str
bought_data[code]['last_buy_price'] = current_price
bought_data[code]['add_price'] = int(bought_data[code]['last_buy_price'] + 1 * stock_atr)
bought_data[code]['loss_cut'] = int(bought_data[code]['last_buy_price'] - 2 * stock_atr)
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 7% 하락시 매도
if yield_rate < -7:
send_message(f'{bought_data[code]["name"]}_-7% 하락 매도')
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# ATR 매도
if current_price < bought_data[code]['loss_cut'] and bought_data[code]['atr_trans_cnt'] > 0:
send_message(f'{bought_data[code]["name"]}_ATR 매도 합니다.')
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 보유 90일 이후 매도
if t_now - dt.datetime.strptime(bought_data[code]['first_buy_date'], '%Y-%m-%d') > dt.timedelta(
days=90):
send_message(f'{bought_data[code]["name"]}_90일 이후 매도 합니다.')
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 마켓타이밍 불만족시 모두 매도
elif buy_signal == False:
send_message(f'마켓타이밍 조건불만족하여 전량 매도합니다.')
total_amount, current_cash, bought_list = acc.update_account_info() # 총잔고, 가용현금, 보유종목
for code in bought_list:
if code == "423160":
continue
if code in bought_data.copy().keys():
send_message(f'{bought_data[code]["name"]}_매도합니다.')
hold_qty, hold_amt, yield_rate, yield_amt = acc.get_one_stock_balance(code)
sell.ask_1(code, hold_qty)
del bought_data[code]
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
# 남은 돈은 kofr 사기
hk_cm.buy_kofr()
send_message('프로그램 종료합니다')
# 만약 계좌에 해당 종목이 없다면 주문은 했지만 안사진거다. bought data에서 삭제하자
bought_list = acc.get_holding_stock_list()
for code in bought_data.copy().keys():
if code == "date":
continue
else:
if code not in bought_list:
del bought_data[code]
# 저장하기, 불러오기
with open("bought_data.json", 'w', encoding='UTF-8') as temp_out: # 저장
json.dump(bought_data, temp_out, ensure_ascii=False)
with open("bought_data.json", 'r', encoding='UTF-8') as temp_in: # 불러오기
bought_data = json.load(temp_in)
exit(0)
# try~except 에러사항 발생시 조치사항 (시작) #############################################################################
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)
# try~except 에러사항 발생시 조치사항 (시작) #############################################################################
# try~except 에러사항 발생시 조치사항 (시작) #############################################################################
To embed this project on your website, copy the following code and paste it into your website's HTML: