이하영

simulator기능 구현

import datetime
from sqlalchemy import *
import pymysql
from pandas import DataFrame
from pandas import *
import config
from Logger import *
import cf
class Simulator_Api:
class Simulator_Func:
def __init__(self,simul_num,op,db_name):
self.simul_num=int(simul_num)
if op=="real":
if self.simul_num==-1:
self.set_date()
elif op=='reset':
self.op='reset'
self.simul_reset=True
self.set_variable()
self.rotate_date()
elif op=='real':
self.op='real'
self.simul_reset=False
self.db_name=db_name
self.set_variable()
elif op=='continue':
self.op='continue'
self.simul_reset=False
self.set_variable()
self.rotate_date()
else:
logger.error("Invalid option")
# 오늘 날짜를 설정하는 함수
def set_date(self):
self.today=datetime.datetime.today().strftime("%Y%m%d")
self.today_with_time=datetime.datetime.today().strftime("%Y%m%d%H%M")
self.today_form=datetime.datetime.strptime(self.today,"%Y%m%d").date()
# 사용되는 변수를 설정하는 함수
def set_variable(self):
self.set_date()
self.set_database()
self.simul_end_date=self.today # 시뮬레이션이 끝나는 날짜
self.start_min="0900" # 장 시작 시간 : 9시
self.use_min=False # 분별 시뮬레이션을 사용하는 변수
self.use_nine=True # 9시에만 거래를 수행하는 변수
self.buy_stop=False # 거래를 중지하는 변수
self.trade_check_num=False # 실시간 조건 매수 옵션. 분별 시뮬레이팅의 경우 True, 일별 시뮬레이팅의 경우 False
print("simul_num : ",self.simul_num)
if self.simul_num==1:
self.simul_start_date="20190101"
# 매수/매도 알고리즘 설정
self.buy_algorithm=1
self.sell_algorithm=1
# 시뮬레이션 변수 설정
self.start_invest_price=1000000 # 초기 투자자금
self.invest_unit=100000 # 매수 금액 단위
self.limit_money=300000 # 자산 중 최소로 남겨 둘 금액
self.sell_point=10 # 익절 수익률 기준
self.losscut_point=-2 # 손절 수익률 기준
self.invest_limit_rate=1.01 # 매수하는 순간 종목의 최신 종가보다 가격이 n%이상 상승했을 때는 매수하지 않음
self.invest_min_limit_rate=0.98 # 매수하는 순간 종목의 최신 종가보다 가격이 n%이상 하락했을 경우 매수하지 않음
elif self.simul_num==2:
self.simul_start_date="20190101"
self.buy_algorithm=2
self.sell_algorithm=2
self.start_invest_price=1000000
self.invest_unit=100000
self.limit_money=300000
self.sell_point=10
self.losscut_point=-2
self.invest_buy_limit_rate=1.01
self.invest_sell_limit_rate=0.98
elif self.simul_num==3:
self.simul_start_date = "20190101"
self.buy_algorithm = 3
self.sell_algorithm = 3
self.start_invest_price = 1000000
self.invest_unit = 100000
self.limit_money = 300000
self.sell_point = 10
self.losscut_point = -2
self.invest_buy_limit_rate = 1.01
self.invest_sell_limit_rate = 0.98
else:
logger.error("Invalid simul_num")
self.set_db_control()
if self.op!='real':
self.set_table()
self.get_date_for_simul()
self.total_valuation_profit=0 # 매도 종목들에 대한 총 수익
self.sum_valuation_profit=0 # 실제 수익 = 매도종목에 대한 수익 + 현재 보유 종목의 수익
self.total_invest_price=self.start_invest_price # 총자산 : 투자금액 + 실제 수익
self.total_purchase_price=0 # 투자금
self.d2_deposit=self.start_invest_price # 예수금 = 초기자본 + 매도수익 - 투자금
self.update_balance() # 일별 정산
self.tax_rate=0.0025 # 거래 세금
self.fees_rate=0.00015 # 거래 수수료
# 매수/매도 알고리즘 선택 -> 차후 알고리즘 별로 구현 및 재설정
self.buy_algorithm=1
self.sell_algorithm=1
self.simul_reset_lock=False # 시뮬레이터를 멈춘 지점부터 다시 돌릴 것인지를 선택하는 변수
# 투자에서 사용할 변수 설정
self.per_invest=1000000 # 한 항목 당 구매할 금액
# database를 control하기 위한 eninge 및 connection을 저장하는 함수
def set_db_control(self):
# simul database에 접속하는 engine
if self.op=='real':
self.engine_simul=create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
cf.db_port + "/" + str(self.db_name), encoding='utf-8')
else:
self.db_name="simul"+str(self.simul_num)
self.engine_simul=create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
cf.db_port + "/" + str(self.db_name), encoding='utf-8')
# daily_craw database에 접속하는 engine
# daily_craw : 각 종목에 대해 날짜별 데이터를 저장하는 데이터베이스
self.engine_daily_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
cf.db_port + "/daily_craw" , encoding='utf-8')
# min_craw database에 접속하는 engine
# min_craw : 각 종목에 대해 분별 데이터를 저장하는 데디터베이스
self.engine_craw = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
cf.db_port + "/min_craw", encoding='utf-8')
# daily_buy_list database에 접속하는 engine
# daily_buy_list : 날짜별로 종목에 대한 데이터를 저장하는 데이터베이스
self.engine_daily_buy_list = create_engine("mysql+pymysql://" + cf.db_id + ":" + cf.db_pw + "@" + cf.db_ip + ":" +
cf.db_port + "/daily_buy_list", encoding='utf-8')
# mysql에 접속하는 객체 생성
self.db_conn = pymysql.connect(
host=cf.db_ip,
port=int(cf.db_port),
user=cf.db_id,
password=cf.db_pw,
charset='utf8'
)
# 데이터베이스와 테이블을 설정
def set_table(self):
# simul_reset==True인 경우, 시뮬레이터를 초기화하고 다시 구축
if self.simul_reset:
self.init_database()
# simul_reset==False인 경우, 시뮬레이터를 초기화하지 않고 마지막으로 끝난 시점 부터 다시 구동
else:
# 시뮬레이터 데이터베이스, transaction 테이블, account_balance 테이블이 모두 존재하는 경우 이어서 시뮬레이션
if (self.is_simul_database_exist() and
self.is_simul_table_exist(self.db_name,"transaction") and
self.is_simul_table_exist(self.db_name,"account_balance")):
self.init_df_transaction()
self.init_df_account_balance()
self.last_simul_date=self.get_last_date_account_balance()
# 필요한 데이터베이스와 테이블이 모두 존재하지 않는 경우, 다시 생성
else:
self.init_database()
self.simul_reset=True
# 시뮬레이터 데이터베이스 초기화
def init_database(self):
self.drop_database()
self.create_database()
self.init_df_account_balance()
self.init_df_transaction()
self.sell_point=10 # 매도 수익률
self.limit_money=1000000 # 최소로 남겨놓을 금액
# 시뮬레이터 데이터베이스 삭제
def drop_database(self):
if self.is_simul_database_exist():
query=f"drop DATABASE {self.db_name}"
self.db_conn.cursor().execute(query)
self.db_conn.commit()
# 특정 데이터베이스 내에 특정 테이블이 존재하는지 확인하는 함수
def is_table_exist(self,db_name,table_name):
query="select 1 from information_schema.tables where table_schema='%s' and table_name='%s'"
result=self.engine_simul.execute(query%(db_name,table_name)).fetchall()
# 시뮬레이터 데이터베이스 생성
def create_database(self):
if not self.is_simul_database_exist():
query=f"create DATABASE {self.db_name}"
self.db_conn.cursor().execute(query)
self.db_conn.commit()
# 시뮬레이터 데이터베이스가 존재하는지 확인하는 함수
def is_simul_database_exist(self):
query = f"SELECT 1 FROM Information_schema.SCHEMATA WHERE SCHEMA_NAME = '{self.db_name}'"
result=self.engine_daily_buy_list.execute(query).fetchall()
if len(result):
return True
else:
return False
# 시뮬레이터 데이터베이스 안에 특정 이름을 가진 테이블이 존재하는지 확인하는 함수
def is_simul_table_exist(self,db_name,table_name):
query = f"select 1 from information_schema.tables where table_schema = '{db_name}' and table_name = '{table_name}'"
result=self.engine_simul.execute(query).fetchall()
if len(result)==1:
return True
else:
return False
# 데이터베이스 연결 설정
def set_database(self):
if self.op=="real":
self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+
":"+config.db_port+"/"+str(self.db_name),encoding='utf-8')
else:
self.db_name='simulator'+str(self.simul_num)
self.engine_simul=create_engine("mysql+pymysql://"+config.db_id+":"+config.db_pw+"@"+config.db_ip+
":"+config.db_port+"/"+str(self.db_name),encoding='utf-8')
self.engine_daily = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip +
":" + config.db_port + "/daily_info" , encoding='utf-8')
self.engine_stock = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip +
":" + config.db_port + "/stock_info", encoding='utf-8')
self.engine_minute = create_engine("mysql+pymysql://" + config.db_id + ":" + config.db_pw + "@" + config.db_ip +
":" + config.db_port + "/minute_info", encoding='utf-8')
self.conn=pymysql.connect(
host=config.db_ip,
port=int(config.db_port),
user=config.db_id,
password=config.db_pw,
charset='utf8'
)
# account_balance table 생성
def init_df_account_balance(self):
a_balance={'id':[]}
self.account_balance=DataFrame(a_balance,
columns=['date', 'today_earning_rate', 'sum_valuation_profit', 'total_profit',
'today_profit','today_profitcut_count', 'today_losscut_count',
'today_profitcut','today_losscut','d2_deposit', 'total_possess_count',
'today_buy_count', 'total_invest',
'sum_item_total_purchase', 'total_evaluation', 'today_rate',
'today_invest_price','today_sell_price', 'volume_limit', 'sell_point',
'invest_unit','limit_money', 'total_profitcut',
'total_losscut','total_profitcut_count','total_losscut_count',
'today_buy_today_sell_count','today_buy_today_possess_count',
'today_buy_today_profitcut_count','today_buy_today_losscut_count',
'today_buy_today_profitcut_rate','today_buy_today_losscut_rate'],
index=a_balance['id'])
# transaction table 생성
def init_df_transaction(self):
trs={'id':[]}
self.df_transaction=DataFrame(trs,
columns=['id', 'order_num', 'code', 'code_name', 'rate', 'purchase_rate',
'purchase_price','present_price', 'valuation_price','valuation_profit',
'holding_amount', 'buy_date', 'item_total_purchase','chegyul_check',
'invest_unit','sell_date', 'sell_price', 'sell_rate', 'rate_std',
'rate_std_mod_val','rate_std_htr', 'rate_htr','rate_std_mod_val_htr',
'yes_close', 'close', 'd1_diff_rate', 'd1_diff',
'open', 'high','low','volume',
'clo5', 'clo10', 'clo20', 'clo60','clo120',
"clo5_diff_rate", "clo10_diff_rate","clo20_diff_rate",
"clo60_diff_rate", "clo120_diff_rate"])
# account_balance(잔고 데이터)에 저장된 가장 최근의 날짜를 가져오는 함수
def get_last_date_account_balance(self):
query = "SELECT date from account_balance order by date desc limit 1"
result=self.engine_simul.execute(query).fechall()
return result[0][0]
# 시뮬레이팅 할 날짜 리스트를 가져오는 함수
# 장이 열렸던 날을 self.date_rows에 담기 위해 gs글로벌의 date값을 이용
def get_date_for_simul(self):
query = f"select date from `gs글로벌` " \
f"where date >= '{self.simul_start_date}' and date <= '{self.simul_end_date}' group by date"
self.date_rows = self.engine_daily_craw.execute(query).fetchall()
# 거래한 데이터를 바탕으로 거래이력(transaction) 테이블을 정산하는 함수
def update_balance(self):
# transaction table이 존재하지 않을 경우 return
if not self.is_simul_table_exist(self.db_name,"transaction"):
return
query="select sum(valuation_profit) from transaction"
self.sum_valuation_profit=self.engine_simul.execute(query).fethcall()[0][0] # 총수익금 (종목별 평가 금액의 합)
self.total_invest_price=self.start_invest_price+self.sum_valuation_profit # 총자산
query="select sum(item_total_purchase) from transaction where sell_date='%s'"
self.total_purchase_price=self.engine_simul.execute(query%(0)).fethcall()[0][0] # 총투자금액
if self.total_purchase_price is None:
self.total_purchase_price=0
query="select sum(valuation_profit) from transaction where sell_date!='%s'"
self.total_valuation_profit=self.engine_simul.execute(query%(0)).fetchall()[0][0] # 매도 종목들에 대한 수익
if self.total_valuation_profit is None:
self.total_valuation_profit=0
self.d2_deposit=self.start_invest_price+self.total_valuation_profit-self.total_purchase_price # 예수금
# 날짜별로 돌아가면서 시뮬레이션을 실행하는 함수
def rotate_date(self):
for i in range(self.date_rows):
date_rows_today=self.date_rows[i][0] # 시뮬레이팅 할 날짜
date_rows_yesterday=self.date_rows[i-1][0] # 시뮬레이팅 하루 전 날짜
# simul_reset=False인 경우, 시뮬레이터를 멈춘 지점부터 다시 시작
if not self.simul_reset and not self.simul_reset_lock:
if int(date_rows_today)<=int(self.last_simul_date):
continue
else:
self.simul_reset_lock=True
# 분별 시뮬레이션
if self.use_min:
self.simul_by_min(date_rows_today,date_rows_yesterday,i)
# 일별 시뮬레이션
else:
self.simul_by_date(date_rows_today,date_rows_yesterday,i)
# 시뮬레이션을 완료한 후 account_balance 테이블 업데이트
self.arrange_account_balance()
# 하루에 대한 시뮬레이션이 끝난 뒤 해당 날짜의 잔고(account_balance)정보를 업데이트하는 함수
def arrange_account_balance(self):
if self.engine_simul.dialect.has_table(self.engine_simul,"account_balance"):
len_date=self.get_len_account_balance_data()
query="select date from account_balance"
rows=self.engine_simul.execute(query).fetchall()
logger.debug("account balance 최종 정산")
# 날짜별 주식 데이터를 저장해 놓은 데이터베이스에서 가장 최근 날짜를 가져오는 함수
def get_latest_date(self):
query="select table_name from information_schema.tables where table_schema='daily_info' and " \
"table_name like '%s' order by table_name desc limit 1"
result=self.engine_daily.execute(query%("20%%")).fetchall()
if len(result)==0:
for i in range(len_date):
# today_buy_count : 오늘 매수한 종목의 수
query="update account_balance " \
"set " \
"today_buy_count=(select count(*) from (select code from transaction where buy_date like '%s')) "\
"where date='%s'"
self.engine_simul.execute(query%("%%"+str(rows[i][0])+"%%",rows[i][0]))
# today_buy_today_sell_count : 오늘 매수하고 오늘 매도한 종목의 수
query="update account_balance " \
"set " \
"today_buy_today_sell_count=" \
"(select count(*) from " \
"(select code from transaction where buy_date like '%s' and sell_date!=0 group by code)) " \
"where date='%s'"
self.engine_simul.execute(query%("%%"+str(rows[i][0])+"%%",rows[i][0]))
# today_buy_today_possess_count : 오늘 매수하였으나 매도하지 않은 종목의 수
query="update account_balance " \
"set" \
"today_buy_today_possess_count=" \
"(select count(*) from " \
"(select code from transaction where buy_date like '%s' and sell_date='%s' group by code)) " \
"where date='%s'"
self.engine_simul.execute(query%("%%"+rows[i][0]+"%%",0,rows[i][0]))
# today_buy_today_profitcut_count : 오늘 매수하고 익절한 종목의 수
query="update account_balance " \
"set " \
"today_buy_today_profitcut_count=" \
"(select count(*) from " \
"(select code from transaction " \
"where buy_date like '%s' and sell_date like '%s' and sell_rate>'%s' group by code)) " \
"where date='%s'"
self.engine_simul.execute(query%("%%" + rows[i][0] + "%%", "%%" + rows[i][0] + "%%", 0, rows[i][0]))
# today_buy_today_profitcut_rate : 오늘 매수하고 익절한 종목의 수익률
query = "update account_balance " \
"set " \
"today_buy_today_profitcut_rate=round(today_buy_today_profitcut_count /today_buy_count *100,2) "\
"where date = '%s'"
self.engine_simul.execute(query%(rows[i][0]))
# today_buy_today_losscut_count : 오늘 매수하고 손절한 종목의 수
query = "update account_balance " \
"set " \
"today_buy_today_losscut_count=" \
"(select count(*) from " \
"(select code from transaction " \
"where buy_date like '%s' and sell_date like '%s' and sell_rate < '%s' group by code)) " \
"WHERE date='%s'"
self.engine_simul.execute(query%("%%" + rows[i][0] + "%%", "%%" + rows[i][0] + "%%", 0, rows[i][0]))
# today_buy_today_losscut_rate : 오늘 매수하고 손절한 종목의 수익률
query = "update account_balance " \
"set " \
"today_buy_today_losscut_rate=round(today_buy_today_losscut_count /today_buy_count *100,2) " \
"WHERE date = '%s'"
self.engine_simul.execute(query%(rows[i][0]))
# total_profitcut_count : 총 익절한 종목의 수
query = "update account_balance " \
"set " \
"total_profitcut_count=" \
"(select count(*) from " \
"(select code from transaction where sell_rate >= '%s' group by code)) "\
"WHERE date='%s'"
self.engine_simul.execute(query%(0, rows[i][0]))
# total_profitcut : 총 익절한 금액 (매도가 - 매수가)
query = "update account_balance " \
"set " \
"total_profitcut=sum" \
"(select sell_price-purchase_price from transaction " \
"where sell_price>=purchase_price group by code))" \
"WHERE date = '%s'"
self.engine_simul.execute(query%(rows[i][0]))
# total_losscut_count : 총 손절한 종목의 수
query = "update account_balance " \
"set " \
"total_losscut_count=" \
"(select count(*) from " \
"(select code from transaction where sell_rate < '%s' group by code )) " \
"WHERE date='%s'"
self.engine_simul.execute(query%(0,rows[i][0]))
# total_losscut : 총 손절한 금액 (매수가 - 매도가)
query = "update account_balance " \
"set " \
"total_losscut=sum" \
"(select purchase_price-sell_price from transaction " \
"where purchase_price>=sell_price)) " \
"where date='%s'"
self.engine_simul.execute(query%(rows[i][0]))
print("account balance 정산 완료")
# account_balance에 저장된 일자를 반환하는 함수
def get_len_account_balance_data(self):
query="select date from account_balance"
rows=self.engine_simul.execute(query).fetchall()
return len(rows)
# 분별 시뮬레이팅
def simul_by_min(self, date_rows_today, date_rows_yesterday, i):
print("************************** date: " + date_rows_today)
# 시뮬레이팅 전에 변수 초기화
self.set_daily_variable()
# daily_buy_list에 시뮬레이팅 할 날짜에 해당하는 테이블과 전 날 테이블이 존재하는지 확인
if self.is_date_exist(date_rows_today) and self.is_date_exist(date_rows_yesterday):
self.db_to_realtime_daily_buy_list(date_rows_today, date_rows_yesterday, i) # 매수리스트 확인
self.trading_by_min(date_rows_today, date_rows_yesterday, i) # 시뮬레이팅 시작
self.db_to_account_balance(date_rows_today) # 잔고 테이블 업데이트
# transaction 테이블이 존재하고, 현재 보유 중인 종목이 있는 경우, 해당 종목에 대해 데이터베이스 업데이트
if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
self.update_transaction_by_date(date_rows_today, option='ALL')
else:
print("테이블이 존재하지 않습니다")
# 매일 시뮬레이션이 돌기 전에 변수를 초기화하는 함수
def set_daily_variable(self):
self.buy_stop=False
self.today_invest_price=0
# daily_buy_list에 특정 날짜의 이름을 가진 테이블이 존재하는지 확인하는 함수
def is_date_exist(self,date):
query = f"select 1 from information_schema.tables where table_schema ='daily_buy_list' and table_name ='{date}'"
result = self.engine_daily_buy_list.execute(query).fetchall()
if len(result) == 1:
return True
else:
return False
# 매수 할 종목의 리스트를 선정하는 함수
def db_to_realtime_daily_buy_list(self,date_rows_today,date_rows_yesterday,i):
to_buy_list=None
# (5,20) 골든크로스
if self.buy_algorithm == 1:
query=f"select * from '{date_rows_yesterday}' " \
f"where yes_clo20>yes_clo5 and clo5>clo20 and close<'{self.invest_unit}' group by code"
to_buy_list=self.engine_daily_buy_list.execute(query).fetchall()
# (20,60) 골든크로스
elif self.buy_algorithm == 2:
query = f"select * from '{date_rows_yesterday}' " \
f"where yes_clo40 > yes_clo5 and clo5 > clo40 and close < '{self.invest_unit}' group by code"
to_buy_list = self.engine_daily_buy_list.execute(query).fetchall()
else:
logger.error("Invalid Algorithm Setting...")
# 알고리즘에 의해 선택된 항목이 존재한다면 데이터베이스에 해당 항목 업데이트
if len(to_buy_list) > 0:
df_to_buy_list = DataFrame(to_buy_list,
columns=['index', 'index2', 'date', 'check_item', 'code',
'code_name', 'd1_diff','d1_diff_rate',
'close', 'open', 'high','low', 'volume',
'clo5', 'clo10', 'clo20', 'clo60', 'clo120',
"clo5_diff_rate", "clo10_diff_rate", "clo20_diff_rate",
"clo60_diff_rate", "clo120_diff_rate",
'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60','yes_clo120',
'vol5', 'vol10', 'vol20', 'vol60', 'vol120'])
# to_buy_list 중 종목 code가 6자리의 정수로 된 항목만 선택
to_buy_list['code'] = to_buy_list['code'].apply(lambda x: "{:0>6d}".format(int(x)))
# 시뮬레이터
if self.op != 'real':
to_buy_list['check_item'] = int(0)
to_buy_list.to_sql('realtime_daily_buy_list', self.engine_simul, if_exists='replace')
# 현재 보유 중인 종목은 매수 리스트(realtime_daily_buy_list) 에서 제거
if self.is_simul_table_exist(self.db_name, "transaction"):
query = "delete from realtime_daily_buy_list " \
"where code in " \
"(select code from transaction where sell_date = '0')"
self.engine_simul.execute(query)
# realtime_daily_buy_list 테이블에 저장 된 종목들을 저장
self.get_realtime_daily_buy_list()
# 모의투자 / 실전투자
else:
to_buy_list['check_item'] = int(0)
to_buy_list.to_sql('realtime_daily_buy_list', self.engine_simul, if_exists='replace')
# 현재 보유 중인 종목들은 삭제
query = "delete from realtime_daily_buy_list where code in (select code from possessed_item)"
self.engine_simul.execute(sql)
else:
self.len_df_realtime_daily_buy_list = 0
# realtime_daily_buy_list 테이블의 매수 리스트를 가져오는 함수
def get_realtime_daily_buy_list(self):
# check_item = 매수 했을 시 날짜. 매수 하지 않았을 때는 0
query = "select * from realtime_daily_buy_list where check_item = '0' group by code"
realtime_daily_buy_list=self.engine_simul.execute(query).fetchall()
self.df_realtime_daily_buy_list=DataFrame(realtime_daily_buy_list,
columns=['index', 'index2', 'date', 'check_item', 'code',
'code_name', 'd1_diff','d1_diff_rate',
'close', 'open', 'high','low', 'volume',
'clo5', 'clo10', 'clo20', 'clo60', 'clo120',
"clo5_diff_rate", "clo10_diff_rate", "clo20_diff_rate",
"clo60_diff_rate", "clo120_diff_rate",
'yes_clo5', 'yes_clo10', 'yes_clo20', 'yes_clo60','yes_clo120',
'vol5', 'vol10', 'vol20', 'vol60', 'vol120'])
self.len_df_realtime_daily_buy_list = len(self.df_realtime_daily_buy_list)
# 분별 시뮬레이팅 함수
# 기능 : 새로운 종목 매수 / 보유종목 매도 / 보유종목 업데이트
def trading_by_min(self,date_rows_today,date_rows_yesterday,i):
self.show_info(date_rows_today)
# 현재 보유중인 종목이 존재한다면, 보유 종목의 주가를 업데이트
if self.is_simul_table_exist(self.db_name,"transaction"):
self.update_transaction_by_date(date_rows_today,option='OPEN')
# 분별 시간 데이터를 가져오기
self.get_min_data_for_simul(date_rows_today)
if len(self.min_data_rows)!=0:
# 분 단위로 시뮬레이팅
for t in range(len(self.min_data_rows)):
min=self.min_data_rows[t][0]
# 현재 보유 종목이 있는 경우
if self.is_simul_table_exist(self.db_name,"account_balance"):
self.show_info(min)
# 종목 데이터 업데이트
self.update_transaction_by_min(min)
self.update_transaction_etc()
# 매도
self.auto_trade_sell_stock(min,i)
# 매수를 진행할 금액이 남아있는 경우
if not self.buy_stop and self.check_balance():
# 매수할 종목리스트 저장
self.get_realtime_daily_buy_list()
# 매수할 종목이 존재한다면 매수
if self.len_df_realtime_daily_buy_list>0:
self.auto_trade_buy_stock(min,date_rows_today,date_rows_yesterday)
else:
print("금일 매수할 종목이 없습니다")
# 보유 종목이 없는 경우
else:
if not self.buy_stop and self.check_balance():
self.auto_trade_buy_stock(min,date_rows_today,date_rows_yesterday)
if not self.buy_stop and self.use_nine:
print("9시 매수를 종료합니다")
self.buy_stop=True
# 시뮬레이팅 정보를 출력하는 함수
def show_info(self,date):
print("simulator num : ",self.simul_num)
if self.is_simul_table_exist(self.db_name,"transaction"):
print("simulating 시간 : ",date)
print("보유종목 수 : ",self.get_count_possess_item())
# 현재 보유하고 있는 종목수를 반환하는 함수
def get_count_possess_item(self):
query="select count(*) from transaction where sell_date='0'"
result=self.engine_simul.execute(query).fetchall()
return result[0][0]
# 보유 종목의 주가를 일별로 업데이트하는 함수
def update_transaction_by_date(self,date,option='ALL'):
possess_item_list=self.get_data_from_possess_item()
if len(possess_item_list)==0:
print("보유 종목이 없습니다")
for i in range(len(possess_item_list)):
code_name=possess_item_list[i][0]
result=self.get_current_price_by_date(code_name,date)
if result==False:
continue
d1_diff_rate = result[0][0]
close = result[0][1]
open = result[0][2]
high = result[0][3]
low = result[0][4]
volume = result[0][5]
clo5 = result[0][6]
clo10 = result[0][7]
clo20 = result[0][8]
clo60 = result[0][9]
clo120 = result[0][10]
if open:
self.db_to_transaction_update(code_name,d1_diff_rate,close,open,high,low,volume,
clo5,clo10,clo20,clo60,clo120,option)
else:
continue
# 보유한 종목의 이름을 반환하는 함수
def get_data_from_possess_item(self):
query="select code_name from transaction where sell_date='0'"
result=self.engine_simul.execute(query).fetchall()
return result[0][0]
# daily_buy_list를 통해 특정 날짜에 해당하는 주가정보를 가져오는 함수
def get_current_price_by_date(self,code_name,date):
query = f"select d1_diff_rate, close, open, high, low, volume, clo5, clo10, clo20, clo60, clo120 from `{date}` " \
f"where code_name = '{code_name}' group by code"
result=self.engine_daily_buy_list.execute(query).fetchall()
if len(result)==1:
return result
else:
return False
# 현재의 주가를 거래이력(transaction)에 있는 보유 종목에 반영
def db_to_transaction_update(self,code_name,d1_diff_rate,close,open,high,low,volume,
clo5,clo10,clo20,clo60,clo120,option='ALL'):
# 모의투자 / 실전투자의 경우 현재가를 종가로 업데이트
if self.op=='real':
present_price=close
# 시뮬레이터의 경우 현재가를 시가로 업데이트
else:
present_price=open
# option==ALL이면 모든 데이터를 업데이트
if option=="ALL":
query = f"update transaction " \
f"set " \
f"d1_diff_rate = {d1_diff_rate}, " \
f"close = {close}, open = {open}, high = {high}, low = {low}, volume = {volume}, " \
f"present_price = {present_price}, " \
f"clo5 = {clo5}, clo10 = {clo10}, clo20 = {clo20}, clo60 = {clo60}, clo120 = {clo120} " \
f"where code_name = '{code_name}' and sell_date = {0}"
# option==open이면 open,present_price값만 업데이트
else:
query = f"update transaction " \
f"set " \
f"open = {open}, present_price = {present_price} " \
f"where code_name = '{code_name}' and sell_date = {0}"
self.engine_simul.execute(query)
# 분별 데이터를 가져오는 함수
def get_min_data_for_simul(self,simul_start_date):
simul_start_date_min=simul_start_date+self.start_min # 장 시작시간 = 9시
simul_end_date_min=simul_start_date+"1530" # 장 마감시간 = 3시 30분
query = f"select date from `gs글로벌` " \
f"where date >= '{simul_start_date_min}' and date <='{simul_end_date_min}' and open != 0 group by date"
self.min_data_rows = self.engine_craw.execute(query).fetchall()
# 거래이력(transaction)의 시세(close) 데이터를 분 별로 업데이트
def update_transaction_by_min(self,min):
possess_item=self.get_data_from_possess_item()
for i in range(len(possess_item)):
code_name=possess_item[i][0]
current_close_price=self.get_current_close_price_by_min(code_name,min)
if current_close_price:
self.db_to_transaction_update_present_price_by_min(code_name,current_close_price)
else:
continue
# 분별 현재 종가(close)를 가져오는 함수
# 분별 데이터에서 종가는 현재가를 의미하므로 1분마다 시세를 가져오는 함수
def get_current_close_price_by_min(self,code_name,min):
query=f"select close from '{code_name}' " \
f"where date='{min}' and open!=0 and volume!=0 order by sum_volume desc limit 1"
result=self.engine_craw.execute(query).fethcall()
if len(result)==1:
return result[0][0]
else:
return False
# 보유한 종목에 현재가를 실시간으로 업데이트하는 함수
def db_to_transaction_update_present_price_by_min(self,code_name,current_close_price):
query=f"update transaction set present_price={current_close_price} " \
f"where code_name={code_name} and sell_date='{0}'"
self.engine_simul.execute(query)
# 잔고 테이블의 주가 이외의 기타 정보를 업데이트
def update_transaction_etc(self):
# valuation_price : 평가금액
# 평가금액 = (보유주 현재가 * 보유주 수량) - (총 구매금액 * 수수료) - (보유주 현재가 * 보유주 수량 * (수수료 + 세금))
query = f"update transaction " \
f"set " \
f"valuation_price = " \
f"round((present_price * holding_amount) - item_total_purchase * {self.fees_rate} - " \
f"present_price*holding_amount*{self.fees_rate + self.tax_rate}) " \
f"where sell_date = '{0}'"
self.engine_simul.execute(query)
# rate : 현재 실시간 수익률
# valuation_profit : 수익 금액
# 수익 금액 = 평가금액 - 총 구매금액
# 수익률 = 수익금액 / 구매금액 * 100
query = "update account_balance " \
"set " \
"rate= round((valuation_price - item_total_purchase)/item_total_purchase*100,2), " \
"valuation_profit = valuation_price - item_total_purchase " \
"where sell_date = '0';"
self.engine_simul.execute(query)
# 매도 함수
def auto_trade_sell_stock(self,date,i):
# 매도할 리스트를 저장
sell_list=self.get_sell_list(i)
for j in range(len(sell_list)):
sell_code=sell_list[j][0]
sell_rate=sell_list[j][1]
present_price=sell_list[j][2]
valuation_profit=sell_list[j][3]
if sell_rate<0:
print("손절 매도!")
print("=========================================")
print("종목 코드 : ",sell_code)
print("수익 : ",valuation_profit)
print("수익률 : ",sell_rate)
print("=========================================")
else:
print("익절 매도!")
print("=========================================")
print("종목 코드 : ",sell_code)
print("수익 : ",valuation_profit)
print("수익률 : ",sell_rate)
print("=========================================")
# 매도 결과를 데이터베이스에 반영
self.send_sell_order(date,present_price,sell_rate,sell_code)
# 매도한 결과를 transaction에 반영하는 함수
def send_sell_order(self,min,sell_price,sell_rate,code):
query=f"update transaction " \
f"set " \
f"sell_date='{min}', sell_price='{sell_price}', sell_rate='{sell_rate}' " \
f"where code='{code}' and sell_date='{0}' order by buy_date desc limit 1"
self.engine_simul.execute(query)
# 매도 후 정산
self.update_balance()
# 매도함수를 이용하여 매도할 종목을 가져오는 함수
def get_sell_list(self,i):
sell_list=None
# (5,20) 데드크로스
# 또는 손절 기준 수익률 이하로 떨어졌을 경우 매도
if self.sell_algorithm==1:
query=f"select code,rate, present_price,valuation_price from transaction " \
f"where sell_date='0' and (clo5<clo20 or rate<='{self.losscut_point}') group by code"
sell_list=self.engine_simul.execute(query).fetchall()
# (20,60) 데드크로스
elif self.sell_algorithm==2:
query=f"select code,rate, present_price,valuation_price from transaction " \
f"where sell_date='0' and (clo20<clo60 or rate<='{self.losscut_point}') group by code"
sell_list=self.engine_simul.execute(query).fetchall()
# RSI
#elif self.sell_algorithm==3:
# RMI
#elif self.sell_algorithm==4:
else:
logger.error("Invalid sell algorithm setting...")
return sell_list
# 남은 금액을 확인하는 함수
# 남은 금액이 최소 보유금 + 투자단위금액 이상이면 True, 아니면 False 반환
def check_balance(self):
if int(self.d2_deposit)>=int(self.limit_money)+int(self.invest_unit):
return True
else:
return False
# 매수 함수
def auto_trade_buy_stock(self,min,date_rows_today,date_rows_yesterday):
# self.df_realtime_daily_buy_list에 있는 모든 종목을 매수
for i in range(self.len_df_realtime_daily_buy_list):
if self.check_balance():
code=self.df_realtime_daily_buy_list.loc[i,'code'].rjust(6,"0")
code_name=self.df_realtime_daily_buy_list.loc[i,'code_name']
# 분별 시뮬레이팅인 경우
if self.use_min:
# 분별 종목데이터가 존재하지 않는 항목이라면 매수하지 않음
if not self.is_min_craw_table_exist(code_name):
continue
# 일별 시뮬레이팅인 경우
else:
# 일별 종목데이터가 존재하지 않는 항목이라면 매수하지 않음
if not self.is_daily_craw_table_exist(code_name):
continue
# 일별 시뮬레이팅인 경우
if not self.use_min:
price=self.get_current_open_price_by_date(code,date_rows_today)
else:
price=self.get_current_close_price_by_min(code_name,min)
# 전날 종가를 저장
yes_close=self.get_yes_close_price_by_date(code,date_rows_yesterday)
# 종목명 또는 시작가가 존재하지 않는다면 매수하지 않음
if code_name==False or price==0 or price==False:
continue
# 분별 시뮬레이팅인 경우
if self.use_min and not self.use_nine and self.trade_check_num:
open=self.get_current_open_price_by_date(code,date_rows_today) # 시가
sum_volume=self.get_current_volume_by_min(code_name,min) # 당일 누적 거래량
if open and sum_volume:
# 실시간 매수조건에 부합하지 않는 경우 매수하지 않음
if not self.check_trade(self.df_realtime_daily_buy_list.loc[i],open,price,sum_volume):
continue
self.send_buy_order(min,code,code_name,price,yes_close,i)
else:
break
# min_craw 데이터베이스에 code_name 테이블이 존재하는지 확인하는 함수
def is_min_craw_table_exist(self,code_name):
query = f"select 1 from information_schema.tables where table_schema = 'min_craw' and table_name = '{code_name}'"
result = self.engine_craw.execute(query).fetchall()
if len(result) == 1:
return True
else:
return False
# daily_craw 데이터베이스에 code_name 테이블이 존재하는지 확인하는 함수
def is_daily_craw_table_exist(self,code_name):
query = f"select 1 from information_schema.tables where table_schema = 'daily_craw' and table_name = '{code_name}'"
result = self.engine_daily_craw.execute(query).fetchall()
if len(result) == 1:
return True
else:
return False
# 특정 날짜의 테이블에서 특정 종목의 시가를 가져오는 함수
def get_current_open_price_by_date(self,code,date):
query=f"select open from `{date}` where code = '{code}' group by code"
open=self.engine_daily_buy_list.execute(query).fetchall()
if len(open)==1:
return open[0][0]
else:
return False
# 전날 종가(close)를 가져오는 함수
def get_yes_close_price_by_date(self,code,date):
query = f"select close from `{date}` where code = '{code}' group by code"
result = self.engine_daily_buy_list.execute(query).fetchall()
if len(result) == 1:
return result[0][0]
else:
return False
# 분별 현재 누적 거래량을 가져오는 함수
def get_current_volume_by_min(self,code_name,min):
query= f"select sum_volume from `{code_name}` " \
f"where date = '{min}' and open != 0 and volume !=0 order by sum_volume desc limit 1"
result = self.engine_craw.execute(query).fetchall()
if len(result) == 1:
return result[0][0]
else:
return False
# 실시간 주가를 분석하여 매수 여부를 결정하는 함수
# param : df_row - 매수 종목 리스트
# open_price - 시가
# current_price - 현재가
# current_sum_volume - 현재 누적 거래량
# return : True인 경우 매수, False인 경우 매수하지 않음
def check_trade(self,df_row,open_price,current_price,current_sum_volume):
code_name = df_row['code_name']
yes_vol20 = df_row['vol20']
yes_close = df_row['close']
yes_high = df_row['high']
yes_low = df_row['low']
yes_volume = df_row['volume']
# 실시간 거래 대금 체크 알고리즘
# 어제 종가 보다 현재가가 증가했고, 거래 대금이 어제 거래대금에 비해서 x배 올라갔을 때 매수
if self.trade_check_num == 1:
yes_total_tr_price = yes_close * yes_volume # 전날 거래 대금
current_total_tr_price = current_price * current_sum_volume # 현재 거래 대금
if current_price > yes_close and current_total_tr_price > yes_total_tr_price * self.volume_up:
return True
else:
return False
# 현재가가 매수 가격 최저 범위와 매수 가격 최고 범위 안에 들어와 있다면 매수
elif self.trade_check_num == 2:
min_buy_limit = int(yes_close) * self.invest_min_limit_rate # 매수가격 최저 범위
max_buy_limit = int(yes_close) * self.invest_limit_rate # 매수가격 최고 범위
if min_buy_limit < current_price < max_buy_limit:
return True
else:
return False
# 래리 윌리엄스 변동성 돌파 알고리즘(매수)
elif self.trade_check_num == 3:
# 변동폭(range): 전일 고가(yes_high)에서 전일 저가(yes_low)를 뺀 가격
# 매수시점 : 현재가 > 시작가 + (변동폭 * k) [k는 0~1 사이 수]
range = yes_high - yes_low
if open_price + range * self.rarry_k < current_price:
return True
else:
return False
# transaction_history 테이블의 Dataframe 생성
def df_transaction_history(self):
df_temp={'id':[]}
self.df_th=DataFrame(df_temp,
columns=['id','order_num','code','code_name','rate','purchase_rate',
'purchase_price','present_price','valuation_price','valuation_profit',
'holding_amount','buy_date','total_purchase_price','contract_check','per_invest',
'sell_date','sell_price','sell_rate'
])
# daily_info 데이터베이스에서 특정 날짜에서 특정 코드에 해당하는 정보만 가져오는 함수수
def get_daily_info_by_code(self,code,date):
query="select * from {} where code='{}' group by code"
daily_info=self.engine_daily.execute(query.format(date,code)).fetchall()
df_daily_info=DataFrame(daily_info,
columns=['index','index2','date','check_item','code',
'code_name','dff_rate',
'close','open','high','low','volume',
'avg5','avg10','avg20','avg60','avg120',
'prev_avg5','prev_avg10','prev_avg20','prev_avg60','prev_avg120',
'avg5_diff_rate','avg10_diff_rate','avg20_diff_rate','avg60_diff_rate',
'avg120_diff_rate',
'vol5','vol10','vol20','vol60','vol120'])
return df_daily_info
\ No newline at end of file
else:
logger.error("Invalid trade_check_num")
# 데이터베이스에 매수 내역 업데이트
def send_buy_order(self,date,code,code_name,price,yes_close,j):
# 가격이 invest_unit보다 작은 경우 매수
if price < self.invest_unit:
# 매수하는 경우 account_balance에 해당 내역을 추가
self.db_to_transaction(date,self.df_realtime_daily_buy_list,j,code,code_name,price,yes_close)
# 매수를 성공적으로 했으면 realtime_daily_buy_list 테이블의 check_item 에 매수 시간을 설정
self.update_realtime_daily_buy_list(code, date)
# 정산
self.update_balance()
# transaction 테이블에 추가하는 함수
def db_to_transaction(self,min,df,index,code,code_name,purchase_price,yesterday_close):
self.df_transaction.loc[0,'code']=code
self.df_transaction.loc[0,'code_name']=code_name
self.df_transaction.loc[0,'rate']=float(-0.33)
if yesterday_close:
self.df_transaction.loc[0, 'purchase_rate'] = round(
(float(purchase_price) - float(yesterday_close)) / float(yesterday_close) * 100, 2)
self.df_transaction.loc[0, 'purchase_price'] = purchase_price
self.df_transaction.loc[0, 'present_price'] = purchase_price
self.df_transaction.loc[0, 'holding_amount'] = int(self.invest_unit / purchase_price)
self.df_transaction.loc[0, 'buy_date'] = min
self.df_transaction.loc[0, 'item_total_purchase'] = self.df_transaction.loc[0, 'purchase_price'] * \
self.df_transaction.loc[0, 'holding_amount']
self.today_invest_price = self.today_invest_price + self.df_transaction.loc[0, 'item_total_purchase']
self.df_transaction.loc[0, 'chegyul_check'] = 0
self.df_transaction.loc[0, 'id'] = 0
self.df_transaction.loc[0, 'invest_unit'] = self.invest_unit
self.df_transaction.loc[0, 'yes_close'] = yesterday_close
self.df_transaction.loc[0, 'close'] = df.loc[index, 'close']
self.df_transaction.loc[0, 'open'] = df.loc[index, 'open']
self.df_transaction.loc[0, 'high'] = df.loc[index, 'high']
self.df_transaction.loc[0, 'low'] = df.loc[index, 'low']
self.df_transaction.loc[0, 'volume'] = df.loc[index, 'volume']
self.df_transaction.loc[0, 'd1_diff_rate'] = float(df.loc[index, 'd1_diff_rate'])
self.df_transaction.loc[0, 'clo5'] = df.loc[index, 'clo5']
self.df_transaction.loc[0, 'clo10'] = df.loc[index, 'clo10']
self.df_transaction.loc[0, 'clo20'] = df.loc[index, 'clo20']
self.df_transaction.loc[0, 'clo60'] = df.loc[index, 'clo40']
self.df_transaction.loc[0, 'clo120'] = df.loc[index, 'clo60']
if df.loc[index, 'clo5_diff_rate'] is not None:
self.df_transaction.loc[0, 'clo5_diff_rate'] = float(df.loc[index, 'clo5_diff_rate'])
if df.loc[index, 'clo10_diff_rate'] is not None:
self.df_transaction.loc[0, 'clo10_diff_rate'] = float(df.loc[index, 'clo10_diff_rate'])
if df.loc[index, 'clo20_diff_rate'] is not None:
self.df_transaction.loc[0, 'clo20_diff_rate'] = float(df.loc[index, 'clo20_diff_rate'])
if df.loc[index, 'clo60_diff_rate'] is not None:
self.df_transaction.loc[0, 'clo40_diff_rate'] = float(df.loc[index, 'clo40_diff_rate'])
if df.loc[index, 'clo120_diff_rate'] is not None:
self.df_transaction.loc[0, 'clo60_diff_rate'] = float(df.loc[index, 'clo60_diff_rate'])
self.df_transaction.loc[0, 'valuation_profit'] = int(0)
self.df_transaction=self.df_transaction.fillna(0)
# 거래이력 테이블이 이미 존재한다면 데이터 추가
if self.is_simul_table_exist(self.db_name, "transaction"):
self.df_transaction.to_sql('transaction', self.engine_simul, if_exists='append')
# 테이블이 존재하지 않는다면 생성
else:
self.df_transaction.to_sql('transaction', self.engine_simul, if_exists='replace')
# 매수할 리스트(realtime_daily_buy_list)에서 특정 종목을 매수한 경우 check_item칼럼에 매수한 날짜를 저장하는 함수
def update_realtime_daily_buy_list(self,code,min):
query = f"update realtime_daily_buy_list set check_item = '{min}' where code = '{code}'"
self.engine_simul.execute(query)
# account_balance(잔고) 테이블을 생성 및 데이터를 추가하는 함수
def db_to_account_balance(self,date_rows_today):
self.update_balance()
# 거래이력(transaction)테이블이 존재하지 않는다면 return
if not self.is_simul_table_exist(self.db_name,"transaction"):
return
columns = ['date', 'today_earning_rate', 'sum_valuation_profit', 'total_profit',
'today_profit', 'today_profitcut_count', 'today_losscut_count',
'today_profitcut', 'today_losscut', 'd2_deposit', 'total_possess_count',
'today_buy_count','total_invest',
'sum_item_total_purchase', 'total_evaluation', 'today_rate',
'today_invest_price', 'today_sell_price', 'volume_limit', 'sell_point',
'invest_unit', 'limit_money', 'total_profitcut',
'total_losscut', 'total_profitcut_count', 'total_losscut_count']
self.account_balance.loc[0,'date']=date_rows_today # 구매일
self.account_balance.loc[0, 'sum_valuation_profit'] = self.sum_valuation_profit # 총평가금액
self.account_balance.loc[0, 'total_profit'] = self.total_valuation_profit # 총수익
self.account_balance.loc[0, 'today_profit'] = self.get_today_profit(date_rows_today) # 당일수익
self.account_balance.loc[0, 'today_profitcut_count'] = self.get_today_profitcut_count(date_rows_today) # 당일 익절종목 수
self.account_balance.loc[0, 'today_losscut_count'] = self.get_today_losscut_count(date_rows_today) # 당일 손절종목 수
self.account_balance.loc[0, 'today_profitcut'] = self.get_sum_today_profitcut(date_rows_today) # 당일 익절금액
self.account_balance.loc[0, 'today_losscut'] = self.get_sum_today_losscut(date_rows_today) # 당일 손절금액
self.account_balance.loc[0, 'd2_deposit'] = self.d2_deposit # 예수금
self.account_balance.loc[0, 'total_possess_count'] = self.get_total_possess_count() # 보유종목 수
self.account_balance.loc[0, 'today_buy_count'] = 0 # 오늘 구매한 종목수
self.account_balance.loc[0, 'total_invest'] = self.total_invest_price # 총 투자금액
self.account_balance.loc[0, 'sum_item_total_purchase'] = self.get_sum_item_total_purchase() # 총매입금액
self.account_balance.loc[0, 'total_evaluation'] = self.get_sum_valuation_price() # 총평가금액
try:
self.account_balance.loc[0, 'today_rate'] = round(
(float(self.account_balance.loc[0, 'total_evaluation']) -
float(self.account_balance.loc[0, 'sum_item_total_purchase'])) /
float(self.account_balance.loc[0, 'sum_item_total_purchase']) * (100 - 0.33), 2) # 당일 기준 수익률
except ZeroDivisionError:
pass
self.account_balance.loc[0, 'today_invest_price'] = float(self.today_invest_price) # 당일 투자금액
self.account_balance.loc[0, 'today_sell_price'] = self.get_sum_today_sell_price(date_rows_today)
self.account_balance.loc[0, 'volume_limit'] = self.volume_limit
self.account_balance.loc[0, 'sell_point'] = self.sell_point
self.account_balance.loc[0, 'invest_unit'] = self.invest_unit
self.account_balance.loc[0, 'limit_money'] = self.limit_money
self.account_balance.loc[0, 'total_profitcut'] = self.get_sum_total_profitcut()
self.account_balance.loc[0, 'total_losscut'] = self.get_sum_total_losscut()
self.account_balance.loc[0, 'total_profitcut_count'] = self.get_sum_total_profitcut_count()
self.account_balance.loc[0, 'total_losscut_count'] = self.get_sum_total_losscut_count()
# 데이터베이스에 테이블 추가
self.account_balance.to_sql('account_balance', self.engine_simul, if_exists='append')
# 매도종목에 대한 당일 수익
query = f"update account_balance " \
f"set " \
f"today_earning_rate =round(today_profit / total_invest * 100, 2) WHERE date='{date_rows_today}'"
self.engine_simul.execute(query)
# 당일 수익을 계산하는 함수
def get_today_profit(self,date):
query="SELECT sum(valuation_profit) from transaction where sell_date like '%s'"
result=self.engine_simul.execute(query%("%%"+date+"%%")).fetchall()
return result[0][0]
# 당일 익절 종목 수를 반환하는 함수
def get_today_profitcut_count(self,date):
query = "SELECT count(code) from transaction where sell_date like '%s' and sell_rate>='%s'"
return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
# 당일 손절 종목 수를 반환하는 함수
def get_today_losscut_count(self, date):
query = "SELECT count(code) from transaction where sell_date like '%s' and sell_rate<'%s'"
return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
# 당일 익절 금액을 반환하는 함수
def get_sum_today_profitcut(self, date):
query = "SELECT sum(valuation_profit) from transaction where sell_date like '%s' and valuation_profit >= '%s' "
return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
# 당일 손절 금액을 반환하는 함수
def get_sum_today_losscut(self, date):
query = "SELECT sum(valuation_profit) from transaction where sell_date like '%s' and valuation_profit < '%s' "
return self.engine_simul.execute(query % ("%%" + date + "%%", 0)).fetchall()[0][0]
# 총 보유한 종목 수를 반환하는 함수
def get_total_possess_count(self):
query = "select count(code) from transaction where sell_date = '%s'"
return self.engine_simul.execute(query % (0)).fetchall()[0][0]
# 총 매입금액을 계산하는 함수
def get_sum_item_total_purchase(self):
query = "SELECT sum(item_total_purchase) from transaction where sell_date = '%s'"
result = self.engine_simul.execute(query % (0)).fetchall()[0][0]
if result is not None:
return result
else:
return 0
# 총평가금액을 계산하는 함수
def get_sum_valuation_price(self):
query = "SELECT sum(valuation_price) from transaction where sell_date = '%s'"
result = self.engine_simul.execute(query % (0)).fetchall()[0][0]
if result is not None:
return result
else:
return 0
# 당일 매도금액을 계산하는 함수
def get_sum_today_sell_price(self, date):
query = "SELECT sum(valuation_price) from transaction where sell_date like '%s'"
return self.engine_simul.execute(query % ("%%" + date + "%%")).fetchall()[0][0]
# 총 익절금액을 계산하는 함수
def get_sum_total_profitcut(self):
query = "SELECT sum(valuation_profit) from transaction where sell_date != 0 and valuation_profit >= '%s' "
return self.engine_simul.execute(query % (0)).fetchall()[0][0]
# 총 손절금액을 계산하는 함수
def get_sum_total_losscut(self):
query = "SELECT sum(valuation_profit) from transaction where sell_date != 0 and valuation_profit < '%s' "
return self.engine_simul.execute(query % (0)).fetchall()[0][0]
# 총 익절종목수를 반환하는 함수
def get_sum_total_profitcut_count(self):
query = "select count(code) from transaction where sell_date != 0 and valuation_profit >= '%s'"
return self.engine_simul.execute(query % (0)).fetchall()[0][0]
# 총 손절종목수를 반환하는 함수
def get_sum_total_losscut_count(self):
query = "select count(code) from transaction where sell_date != 0 and valuation_profit < '%s' "
return self.engine_simul.execute(query % (0)).fetchall()[0][0]
# 보유종목의 종목명을 반환하는 함수
def get_data_from_possessed_item(self):
query="SELECT code_name from transaction where sell_date = '%s'"
return self.engine_simul.execute(query % (0)).fetchall()
# 날짜별 시뮬레이팅 함수
def simul_by_date(self,date_rows_today,date_rows_yesterday,i):
# 시뮬레이팅 전 변수 초기화
self.set_daily_variable()
# daily_buy_list에 시뮬레이팅 할 날짜에 해당하는 테이블과 전 날 테이블이 존재하는지 확인
if self.is_date_exist(date_rows_today) and self.is_date_exist(date_rows_yesterday):
# 당일 매수 할 종목 저장
self.db_to_realtime_daily_buy_list(date_rows_today, date_rows_yesterday, i)
# 매수/매도
self.trading_by_date(date_rows_today, date_rows_yesterday, i)
if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
# 보유 중인 종목들의 주가를 일별로 업데이트
self.update_transaction_by_date(date_rows_today,option="ALL")
# 정산
self.db_to_account_balance(date_rows_today)
else:
print("날짜 테이블이 존재하지 않습니다.")
# 날짜별 거래 함수
def trading_by_date(self,date_rows_today,date_rows_yesterday,i):
self.show_info(date_rows_today)
# 보유주 정보 업데이트
if self.is_simul_table_exist(self.db_name, "transaction") and len(self.get_data_from_possessed_item()) != 0:
self.update_transaction_by_date(date_rows_today,option="OPEN")
self.update_transaction_etc()
# 매도
self.auto_trade_sell_stock(date_rows_today,i)
# 매수할 잔액이 존재한다면 매수
if self.check_balance():
self.auto_trade_buy_stock(str(date_rows_today) + "0900", date_rows_today, date_rows_yesterday)
# 매도 리스트가 존재하지 않는다면 매수만 진행
else:
if self.check_balance():
self.auto_trade_buy_stock(str(date_rows_today) + "0900", date_rows_today, date_rows_yesterday)
\ No newline at end of file
......
from Open_Api import *
from collections import OrderedDict
from pandas import DataFrame
import time
from Daily_Info import *
from Stock_Info import *
......@@ -23,27 +24,40 @@ class Collector_Api():
"today_buy_list,stock_info,min_info,daily_info from setting_data"
result=self.engine_bot.execute(query).fetchall()
today=self.open_api.today
# 오늘 날짜에 종목리스트가 업데이트 되지 않았다면 업데이트를 실행
if result[0][0]!=self.open_api.today:
if result[0][0]!=today:
self.open_api.get_balance()
self.get_code_list()
# 계좌정보 업데이트
if result[0][1]!=self.open_api.today or result[0][2]!=self.open_api.today:
if result[0][1]!=today or result[0][2]!=today:
self.check_balance()
self.open_api.set_per_invest()
# 현재 보유종목 테이블 업데이트
if result[0][2]!=self.open_api.today:
if result[0][2]!=today:
self.open_api.get_posses_item()
self.open_api.setting_data_posses_stock()
if result[0][3]!=self.open_api.today:
# 수익률 테이블 업데이트
if result[0][3]!=today:
self.update_today_profit_list()
if result[0][7]!=self.open_api.today:
# stock_info 테이블 업데이트
if result[0][7]!=today:
self.check_stock_info()
if result[0][9]!=today:
self.check_daily_info()
if result[0][4]!=today:
self.open_api.check_contract()
self.open_api.final_check_contract()
if result[0][6]!=today:
self.realtime_daily_info_check()
# 코스피, 코스닥 리스트 확인 및 데이터베이스 업데이트
......@@ -256,7 +270,7 @@ class Collector_Api():
query = "UPDATE setting_data SET today_profit='%s' limit 1"
self.engine_bot.execute(query % (self.open_api.today))
def check_daily_info(self):
def check_stock_info(self):
self.update_daily_info()
query="update setting_data set daily_info='%s'"
self.engine_bot.execute(query%(self.open_api.today))
......@@ -273,12 +287,102 @@ class Collector_Api():
code=code_list[i][0]
code_name=code_list[i][1]
check_item_sort=self.set_minute_info_table(code,code_name)
check_item_sort=self.set_stock_info_table(code,code_name)
self.open_api.engine_daily.execute(query%(check_item_sort,code))
def set_minute_info_table(self,code,code_name):
df=self.open_api.
# stock_info 데이터베이스에 테이블 생성 및 추가
def set_stock_info_table(self,code,code_name):
df=self.open_api.get_date_data(code,code_name,self.open_api.today)
df=DataFrame(df,
columns=['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
'close', 'open', 'high','low','volume',
'clo5', 'clo10', 'clo20', 'clo60','clo120',
'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','pre_clo120',
'vol5', 'vol10', 'vol20', 'vol60','vol120'])
df=df.sort_values(by=['date'],ascending=True)
df['code']=code
df['code_name']=code_name
df['d1_diff_rate']=float((df['close']-df['close'].shift(1))/df['close'].shift(1)*100)
df['clo5']=df['close'].rolling(window=5).mean()
df['clo10']=df['close'].rolling(window=10).mean()
df['clo20']=df['close'].rolling(window=20).mean()
df['clo60']=df['close'].rolling(window=60).mean()
df['clo120']=df['close'].rolling(window=120).mean()
df['pre_col5']=df['clo5'].shift(1)
df['pre_col10']=df['clo10'].shift(1)
df['pre_col20']=df['clo20'].shift(1)
df['pre_col60']=df['clo60'].shift(1)
df['pre_col120']=df['clo120'].shift(1)
df['vlo5']=df['volume'].rolling(window=5).mean()
df['vlo10']=df['volume'].rolling(window=10).mean()
df['vlo20']=df['volume'].rolling(window=20).mean()
df['vlo60']=df['volume'].rolling(window=60).mean()
df['vlo120']=df['volume'].rolling(window=120).mean()
# 이미 테이블이 존재한다면 저장된 가장 최신의 날짜 이후의 데이터만 저장
if self.engine_bot.dialect.has_table(self.open_api.engine_stock,code_name):
df=df[df.date>self.open_api.get_latest_date_from_stock_info(code_name)]
df[['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
'close', 'open', 'high','low','volume',
'clo5', 'clo10', 'clo20', 'clo60','clo120',
'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','yes_clo120',
'vol5', 'vol10', 'vol20', 'vol60','vol120']]=\
df[['date', 'check_item', 'code', 'code_name', 'd1_diff_rate',
'close', 'open', 'high','low','volume',
'clo5', 'clo10', 'clo20', 'clo60','clo120',
'pre_clo5', 'pre_clo10', 'pre_clo20', 'pre_clo60','yes_clo120',
'vol5', 'vol10', 'vol20', 'vol60','vol120']].fillna(0).astype(int)
df.to_sql(name=code_name,con=self.open_api.engine_stock,if_exists='append')
check_item_sort = 1
return check_item_sort
def check_daily_info(self):
self.daily_info.create_daily_table()
query="update setting_data set daily_info='%s'"
self.engine_bot.execute(query%(self.open_api.today))
def realtime_daily_info_check(self):
if self.open_api.simul_api.is_date_exist(self.open_api.today):
self.open_api.simul_api.get_date_for_simul()
self.open_api.simul_api.choose_to_buy_list(self.open_api.today,self.open_api.today,len(self.open_api.simul_api.date_rows))
if self.open_api.sf.is_date_exist(self.open_api.today):
logger.debug("daily_buy_list DB에 {} 테이블이 있습니다. jackbot DB에 realtime_daily_buy_list 테이블을 생성합니다".format(self.open_api.today))
self.open_api.sf.get_date_for_simul()
# 첫 번째 파라미터는 여기서는 의미가 없다.
# 두 번째 파라미터에 오늘 일자를 넣는 이유는 매수를 하는 시점인 내일 기준으로 date_rows_yesterday가 오늘 이기 때문
self.open_api.sf.db_to_realtime_daily_buy_list(self.open_api.today, self.open_api.today, len(self.open_api.sf.date_rows))
# all_item_db에서 open, clo5~120, volume 등을 오늘 일자 데이터로 업데이트 한다.
self.open_api.sf.update_all_db_by_date(self.open_api.today)
self.open_api.rate_check()
# realtime_daily_buy_list(매수 리스트) 테이블 세팅을 완료 했으면 아래 쿼리를 통해 setting_data의 today_buy_list에 오늘 날짜를 찍는다.
sql = "UPDATE setting_data SET today_buy_list='%s' limit 1"
self.engine_JB.execute(sql % (self.open_api.today))
else:
logger.debug(
"""daily_buy_list DB에 {} 테이블이 없습니다. jackbot DB에 realtime_daily_buy_list 테이블을 생성 할 수 없습니다.
realtime_daily_buy_list는 daily_buy_list DB 안에 오늘 날짜 테이블이 만들어져야 생성이 됩니다.
realtime_daily_buy_list 테이블을 생성할 수 없는 이유는 아래와 같습니다.
1. 장이 열리지 않은 날 혹은 15시 30분 ~ 23시 59분 사이에 콜렉터를 돌리지 않은 경우
2. 콜렉터를 오늘 날짜 까지 돌리지 않아 daily_buy_list의 오늘 날짜 테이블이 없는 경우
""".format(self.open_api.today))
app=QApplication(sys.argv)
......
......@@ -329,13 +329,13 @@ class Open_Api(QAxWidget):
def create_setting_data(self):
df_data={"limit_money":[],"per_invest":[],"max_per_invest":[],"min_per_invest":[],"set_per_invest":[],
"code_update":[],"today_finish":[],"balance_to_db":[],"posses_stocks":[],"today_profit":[],
"contract_check":[],"db_to_daily_info":[],"today_buy_list":[],"stock_info":[],"min_info":[],
"daily_info":[]}
"contract_check":[],"final_contract_check":[],"db_to_daily_info":[],"today_buy_list":[],
"stock_info":[],"min_info":[],"daily_info":[]}
df_setting_data=DataFrame(df_data,
columns=['limit_money','per_invest','max_per_invest','min_per_invest','set_per_invest',
'code_update','today_finish','balance_to_db','posses_stocks','today_profit',
'contract_check','db_to_daily_info','today_buy_list','stock_info','min_info',
'daily_info'])
'contract_check','final_contract_check','db_to_daily_info','today_buy_list',
'stock_info','min_info','daily_info'])
df_setting_data.loc[0,'limit_money']=int(0)
df_setting_data.loc[0,'per_invest']=int(0)
......@@ -350,11 +350,12 @@ class Open_Api(QAxWidget):
df_setting_data.loc[0,'today_profit']=float(0)
df_setting_data.loc[0,'contract_check']=str(0)
df_setting_data.loc[0,'final_contract_check']=str(0)
df_setting_data.loc[0,'db_to_daily_info']=str(0)
df_setting_data.loc[0,'today_buy_list']=str(0)
df_setting_data.loc[0,'stock_info']=str(0)
df_setting_data.loc[0,'min_info']=str(0)
df_setting_data.loc[0,'min_info']=str(0)
df_setting_data.loc[0,'daily_info']=str(0)
df_setting_data.to_sql("setting_data",self.engine_bot,if_exists="replace")
......@@ -588,9 +589,113 @@ class Open_Api(QAxWidget):
self.opt10073_output['multi'].append([date,code,code_name,amount,today_profit,earning_rate])
# 특정 종목의 일자별 거래 데이터 조회 함수
def get_date_data(self,code,code_name,date):
self.ohlcv=defaultdict(list)
self.set_input_value("종목코드",code)
self.set_input_value("기준일자",date)
self.set_input_value("수정주가구분",1)
# 한번에 600일치의 데이터를 가져온다
self.comm_rq_data("opt10081_req","opt10081",0,"0101")
# stock_info 테이블이 존재하지 않는다면 600일치 이상의 전체 데이터를 가져와야 하므로 다음 페이지가 존재하지 않을 때까지 조회
if not self.is_stock_table_exist(code_name):
while self.remained_data==True:
self.set_input_value("종목코드",code)
self.set_input_value("기준일자",date)
self.set_input_value("수정주가구분",1)
self.comm_rq_data("opt10081_req","opt10081",2,"0101")
if len(self.ohlcv)==0:
return []
if self.ohlcv['date']=='':
return []
df=DataFrame(self.ohlcv,columns=['date','open','high','low','close','volume'])
return df
# stock_info 데이터베이스가 존재하는지 확인하는 함수
def is_stock_table_exist(self,code_name):
query = "select 1 from information_schema.tables where table_schema ='stock_info' and table_name = '{}'"
result=self.engine_stock.execute(query.format(code_name)).fetchall()
if result:
return True
else:
return False
# 주식 일봉차트 조회 요청
# 주식의 날짜별 데이터를 저장하는 함수
def collector_opt10081(self,sRQName,sTrCode):
ohlcv_cnt=self._get_repeat_cnt(sTrCode,sRQName)
for i in range(ohlcv_cnt):
date=self._get_comm_data(sTrCode,sRQName,i,"일자")
open=self._get_comm_data(sTrCode,sRQName,i,"시가")
high=self._get_comm_data(sTrCode,sRQName,i,"고가")
low=self._get_comm_data(sTrCode,sRQName,i,"저가")
close=self._get_comm_data(sTrCode,sRQName,i,"현재가")
volume=self._get_comm_data(sTrCode,sRQName,i,"거래량")
self.ohlcv['date'].append(date)
self.ohlcv['open'].append(int(open))
self.ohlcv['high'].append(int(high))
self.ohlcv['low'].append(int(low))
self.ohlcv['close'].append(int(close))
self.ohlcv['volume'].append(int(volume))
# stock_info의 특정 종목 테이블에서 마지막으로 저장된 날짜를 가져오는 함수
# 저장된 데이터가 없다면 '0'문자를 반환
def get_latest_date_from_stock_info(self,code_name):
query="select date from {} order by date desc limit 1"
result=self.engine_stock.execute(query.format(code_name)).fetchall()
if len(result):
return result[0][0]
else:
return str(0)
def check_contract(self):
query="select code from transaction_history where contract_check='1' and (sell date='0' or sell_date='')"
rows=self.engine_bot.execute(query).fetchall()
for r in rows:
self.set_input_value("종목코드",r.code)
self.set_input_value("조회구분",1)
self.set_input_value("계좌번호",self.account_no)
self.comm_rq_data("opt10076_req","opt10076",0,"0350")
query="update transaction_history set contract_check='0' where code='{}' and sell_date='0'"
# 거래가 완료된 항목은 주문번호가 존재하지 않음
# 거래가 완료된 항목에 대해서 contract_check항목을 '0'으로 업데이트
if not self.not_contract['주문번호']:
self.engine_bot.execute(query.format(r.code))
# 미체결 수량이 없을 경우 contract_check항목을 '0'으로 업데이트
elif self.not_contract['미체결수량']==0:
logger.debug("미체결 항목이 없습니다")
self.engine_bot.execute(query.format(r.code))
else:
logger.debug("미체결 종목이 존재합니다")
# posses_item테이블에는 존재하지만 transaction_history테이블에 존재하지 않는 항목에 대해 업데이트
def final_check_contract(self):
query="select code from transaction_history" \
"where" \
"sell_date='%s' or sell_date='%s' and code not in (select code from posses_item) and contract_check!='%s'"
result=self.engine_bot.execute(query%(0,"",1)).fetchall()
num=len(result)
for i in range(num):
self.sell_check(result[i][0])
query="update setting_data set final_contract_check='%s'"
self.engine_bot.execute(query%(self.today))
# 가지고 있던 종목을 판매하여 posses_item테이블에 내역이 존재하지 않지만 transaction_history에 매도처리가 되지 않은 항목 업데이트
def sell_check(self,code):
query="update transaction_history" \
"set" \
"contract_check='%s', sell_date='%s'" \
"where code='%s' and sell_date='%s'"
self.engine_bot.execute(query%(0,self.today_time,code,0))
if __name__=="__main__":
......