981 lines
40 KiB
Python
981 lines
40 KiB
Python
import win32com.client
|
|
import time
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
import pandas as pd
|
|
from enum import Enum
|
|
#import plotly.graph_objects as go
|
|
from stockpredictor.analysis.Common import Common
|
|
|
|
# enum 주문 상태 세팅용
|
|
class EorderBS(Enum):
|
|
buy = 1 # 매수
|
|
sell= 2 # 매도
|
|
none =3
|
|
|
|
class orderData:
|
|
def __init__(self):
|
|
self.dicEx = {EorderBS.buy: "매수", EorderBS.sell: "매도", EorderBS.none: "없음"}
|
|
self.orderNum = 0
|
|
self.bs = EorderBS.none # 0 : buy 1: sell
|
|
self.code = ""
|
|
self.amount = 0
|
|
self.price = 0
|
|
|
|
def debugPrint(self):
|
|
print(self.dicEx.get(self.bs), self.code, self.orderNum, self.amount, self.price)
|
|
|
|
|
|
class HTS:
|
|
|
|
objCpCybos = None
|
|
objCpCodeMgr = None
|
|
common = None
|
|
|
|
stock = []
|
|
|
|
def __init__(self):
|
|
self.common = Common()
|
|
#self.connect()
|
|
return
|
|
|
|
def connect(self):
|
|
# 연결 여부 체크
|
|
self.objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
|
|
bConnect = self.objCpCybos.IsConnect
|
|
if (bConnect == 0):
|
|
print("PLUS가 정상적으로 연결되지 않음. ")
|
|
exit()
|
|
return
|
|
|
|
def all_stocks(self):
|
|
# 종목코드 리스트 구하기
|
|
self.objCpCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
|
|
codeList = self.objCpCodeMgr.GetStockListByMarket(1) # 거래소
|
|
codeList2 = self.objCpCodeMgr.GetStockListByMarket(2) # 코스닥
|
|
|
|
print("거래소 종목코드", len(codeList))
|
|
for i, code in enumerate(codeList):
|
|
secondCode = self.objCpCodeMgr.GetStockSectionKind(code)
|
|
name = self.objCpCodeMgr.CodeToName(code)
|
|
stdPrice = self.objCpCodeMgr.GetStockStdPrice(code)
|
|
print(i, code, secondCode, stdPrice, name)
|
|
|
|
print("코스닥 종목코드", len(codeList2))
|
|
for i, code in enumerate(codeList2):
|
|
secondCode = self.objCpCodeMgr.GetStockSectionKind(code)
|
|
name = self.objCpCodeMgr.CodeToName(code)
|
|
stdPrice = self.objCpCodeMgr.GetStockStdPrice(code)
|
|
print(i, code, secondCode, stdPrice, name)
|
|
|
|
print("거래소 + 코스닥 종목코드 ", len(codeList) + len(codeList2))
|
|
return
|
|
|
|
# 차트 데이터 구하기
|
|
def getChartData(self, stock_code):
|
|
# 차트 객체 구하기
|
|
objStockChart = win32com.client.Dispatch("CpSysDib.StockChart")
|
|
|
|
objStockChart.SetInputValue(0, 'A'+stock_code) # 종목 코드 - 삼성전자
|
|
objStockChart.SetInputValue(1, ord('2')) # 개수로 조회
|
|
objStockChart.SetInputValue(4, 100) # 최근 100일 치
|
|
objStockChart.SetInputValue(5, [0, 2, 3, 4, 5, 8]) # 날짜,시가,고가,저가,종가,거래량
|
|
objStockChart.SetInputValue(6, ord('D')) # '차트 주가 - 일간 차트 요청
|
|
objStockChart.SetInputValue(9, ord('1')) # 수정주가 사용
|
|
objStockChart.BlockRequest()
|
|
|
|
len = objStockChart.GetHeaderValue(3)
|
|
|
|
print("날짜", "시가", "고가", "저가", "종가", "거래량")
|
|
print("==============================================-")
|
|
|
|
for i in range(len):
|
|
day = objStockChart.GetDataValue(0, i)
|
|
open = objStockChart.GetDataValue(1, i)
|
|
high = objStockChart.GetDataValue(2, i)
|
|
low = objStockChart.GetDataValue(3, i)
|
|
close = objStockChart.GetDataValue(4, i)
|
|
vol = objStockChart.GetDataValue(5, i)
|
|
print(day, open, high, low, close, vol)
|
|
|
|
return
|
|
|
|
# 주식 현재가 조회
|
|
def currentStock(self, stock_code):
|
|
# 현재가 객체 구하기
|
|
self.objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
|
|
self.objStockMst.SetInputValue(0, 'A'+stock_code) # 종목 코드 - 삼성전자
|
|
self.objStockMst.BlockRequest()
|
|
|
|
# 현재가 통신 및 통신 에러 처리
|
|
rqStatus = self.objStockMst.GetDibStatus()
|
|
rqRet = self.objStockMst.GetDibMsg1()
|
|
print("통신상태", rqStatus, rqRet)
|
|
if rqStatus != 0:
|
|
exit()
|
|
|
|
# 현재가 정보 조회
|
|
code = self.objStockMst.GetHeaderValue(0) # 종목코드
|
|
name = self.objStockMst.GetHeaderValue(1) # 종목명
|
|
time = self.objStockMst.GetHeaderValue(4) # 시간
|
|
cprice = self.objStockMst.GetHeaderValue(11) # 종가
|
|
diff = self.objStockMst.GetHeaderValue(12) # 대비
|
|
open = self.objStockMst.GetHeaderValue(13) # 시가
|
|
high = self.objStockMst.GetHeaderValue(14) # 고가
|
|
low = self.objStockMst.GetHeaderValue(15) # 저가
|
|
offer = self.objStockMst.GetHeaderValue(16) # 매도호가
|
|
bid = self.objStockMst.GetHeaderValue(17) # 매수호가
|
|
vol = self.objStockMst.GetHeaderValue(18) # 거래량
|
|
vol_value = self.objStockMst.GetHeaderValue(19) # 거래대금
|
|
|
|
# 예상 체결관련 정보
|
|
exFlag = self.objStockMst.GetHeaderValue(58) # 예상체결가 구분 플래그
|
|
exPrice = self.objStockMst.GetHeaderValue(55) # 예상체결가
|
|
exDiff = self.objStockMst.GetHeaderValue(56) # 예상체결가 전일대비
|
|
exVol = self.objStockMst.GetHeaderValue(57) # 예상체결수량
|
|
|
|
print("코드", code)
|
|
print("이름", name)
|
|
print("시간", time)
|
|
print("종가", cprice)
|
|
print("대비", diff)
|
|
print("시가", open)
|
|
print("고가", high)
|
|
print("저가", low)
|
|
print("매도호가", offer)
|
|
print("매수호가", bid)
|
|
print("거래량", vol)
|
|
print("거래대금", vol_value)
|
|
|
|
if (exFlag == ord('0')):
|
|
print("장 구분값: 동시호가와 장중 이외의 시간")
|
|
elif (exFlag == ord('1')):
|
|
print("장 구분값: 동시호가 시간")
|
|
elif (exFlag == ord('2')):
|
|
print("장 구분값: 장중 또는 장종료")
|
|
|
|
print("예상체결가 대비 수량")
|
|
print("예상체결가", exPrice)
|
|
print("예상체결가 대비", exDiff)
|
|
print("예상체결수량", exVol)
|
|
|
|
return
|
|
|
|
# 주식 현금 매수주문
|
|
def requestOrder(self, type, stock_code, count, price):
|
|
# type = 2: buy, type=1: sell
|
|
# 주문 초기화
|
|
objTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
|
|
initCheck = objTrade.TradeInit(0)
|
|
if (initCheck != 0):
|
|
print("주문 초기화 실패")
|
|
exit()
|
|
|
|
# 주식 매수 주문
|
|
acc = objTrade.AccountNumber[0] # 계좌번호
|
|
accFlag = objTrade.GoodsList(acc, 1) # 주식상품 구분
|
|
# acc = "782446178"
|
|
# accFlag[0] = "01"
|
|
objStockOrder = win32com.client.Dispatch("CpTrade.CpTd0311")
|
|
objStockOrder.SetInputValue(0, type) # 1: 매도, 2: 매수
|
|
objStockOrder.SetInputValue(1, acc) # 계좌번호
|
|
objStockOrder.SetInputValue(2, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
|
|
objStockOrder.SetInputValue(3, "A"+stock_code) # 종목코드
|
|
objStockOrder.SetInputValue(4, count) # 매수수량 count주
|
|
objStockOrder.SetInputValue(5, price) # 주문단가 - price 원
|
|
objStockOrder.SetInputValue(7, "0") # 주문 조건 구분 코드, 0: 기본 1: IOC 2:FOK
|
|
objStockOrder.SetInputValue(8, "01") # 주문호가 구분코드 - 01: 보통
|
|
|
|
# 매수 주문 요청
|
|
nRet = objStockOrder.BlockRequest()
|
|
if (nRet != 0):
|
|
print("order error", nRet)
|
|
return
|
|
|
|
rqStatus = objStockOrder.GetDibStatus()
|
|
rqRet = objStockOrder.GetDibMsg1()
|
|
print("통신상태", rqStatus, rqRet)
|
|
if rqStatus != 0:
|
|
return
|
|
|
|
orderNum = objStockOrder.GetHeaderValue(0)
|
|
|
|
if (type == "1"):
|
|
print ("(SELL", count, price, ")")
|
|
else:
|
|
print ("(BUY", count, price, ")")
|
|
return orderNum
|
|
|
|
# 계좌 잔고 확인
|
|
def requstJango(self):
|
|
jangoDic = {}
|
|
|
|
objTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
|
|
initCheck = objTrade.TradeInit(0)
|
|
if (initCheck != 0):
|
|
print("주문 초기화 실패")
|
|
exit()
|
|
|
|
# 주식 매수 주문
|
|
acc = objTrade.AccountNumber[0] # 계좌번호
|
|
accFlag = objTrade.GoodsList(acc, 1) # 주식상품 구분
|
|
|
|
objRq = win32com.client.Dispatch("CpTrade.CpTd6033")
|
|
|
|
objRq.SetInputValue(0, acc) # 계좌번호
|
|
objRq.SetInputValue(1, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
|
|
objRq.SetInputValue(2, 50) # 요청 건수(최대 50)
|
|
dicflag1 = {ord(' '): '현금',
|
|
ord('Y'): '융자',
|
|
ord('D'): '대주',
|
|
ord('B'): '담보',
|
|
ord('M'): '매입담보',
|
|
ord('P'): '플러스론',
|
|
ord('I'): '자기융자',
|
|
}
|
|
while True:
|
|
objRq.BlockRequest()
|
|
# 통신 및 통신 에러 처리
|
|
rqStatus = objRq.GetDibStatus()
|
|
rqRet = objRq.GetDibMsg1()
|
|
#print("통신상태", rqStatus, rqRet)
|
|
if rqStatus != 0:
|
|
return False
|
|
|
|
cnt = objRq.GetHeaderValue(7)
|
|
if cnt > 3:
|
|
return jangoDic
|
|
|
|
for i in range(cnt):
|
|
item = {}
|
|
code = objRq.GetDataValue(12, i) # 종목코드
|
|
item['종목코드'] = code
|
|
item['종목명'] = objRq.GetDataValue(0, i) # 종목명
|
|
item['대출일'] = objRq.GetDataValue(2, i) # 대출일
|
|
item['잔고수량'] = objRq.GetDataValue(7, i) # 체결잔고수량
|
|
item['매도가능'] = objRq.GetDataValue(15, i)
|
|
item['장부가'] = objRq.GetDataValue(17, i) # 체결장부단가
|
|
# item['평가금액'] = self.objRq.GetDataValue(9, i) # 평가금액(천원미만은 절사 됨)
|
|
# item['평가손익'] = self.objRq.GetDataValue(11, i) # 평가손익(천원미만은 절사 됨)
|
|
# 매입금액 = 장부가 * 잔고수량
|
|
item['매입금액'] = item['장부가'] * item['잔고수량']
|
|
item['현재가'] = 0
|
|
item['대비'] = 0
|
|
item['거래량'] = 0
|
|
|
|
# 잔고 추가
|
|
# key = (code, item['현금신용'],item['대출일'] )
|
|
key = code
|
|
jangoDic[key] = item
|
|
|
|
if len(jangoDic) >= 3: # 최대 3 종목만,
|
|
break
|
|
|
|
if len(jangoDic) >= 3:
|
|
break
|
|
|
|
if (objRq.Continue == False):
|
|
break
|
|
|
|
check = False
|
|
for item in jangoDic:
|
|
if item:
|
|
check = True
|
|
break
|
|
if not check:
|
|
return None
|
|
return jangoDic
|
|
|
|
# 예약 주문 내역 조회 및 미체결 리스트 구하기
|
|
def requestOrderList(self):
|
|
# type = 2: buy, type=1: sell
|
|
orderList = []
|
|
# 주문 초기화
|
|
objTrade = win32com.client.Dispatch("CpTrade.CpTdUtil")
|
|
initCheck = objTrade.TradeInit(0)
|
|
if (initCheck != 0):
|
|
print("주문 초기화 실패")
|
|
exit()
|
|
|
|
# 주식 매수 주문
|
|
acc = objTrade.AccountNumber[0] # 계좌번호
|
|
accFlag = objTrade.GoodsList(acc, 1) # 주식상품 구분
|
|
|
|
objResult = win32com.client.Dispatch("CpTrade.CpTd9065")
|
|
objResult.SetInputValue(0, acc) # 계좌번호
|
|
objResult.SetInputValue(1, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
|
|
objResult.SetInputValue(2, 20)
|
|
|
|
while True:
|
|
objResult.BlockRequest()
|
|
if objResult.GetDibStatus() != 0:
|
|
print("통신상태", objResult.GetDibStatus(), objResult.GetDibMsg1())
|
|
return False
|
|
|
|
cnt = objResult.GetHeaderValue(4)
|
|
if cnt == 0:
|
|
break
|
|
|
|
for i in range(cnt):
|
|
i1 = objResult.GetDataValue(1, i) # 주문구분(매수 또는 매도)
|
|
i2 = objResult.GetDataValue(2, i) # 코드
|
|
i3 = objResult.GetDataValue(3, i) # 주문 수량
|
|
i4 = objResult.GetDataValue(4, i) # 주문호가구분
|
|
i5 = objResult.GetDataValue(6, i) # 예약번호
|
|
i6 = objResult.GetDataValue(12, i) # 처리구분내용 - 주문취소 또는 주문예정
|
|
i7 = objResult.GetDataValue(9, i) # 주문단가
|
|
i8 = objResult.GetDataValue(11, i) # 주문번호
|
|
i9 = objResult.GetDataValue(12, i) # 처리구분코드
|
|
i10 = objResult.GetDataValue(13, i) # 거부코드
|
|
i11 = objResult.GetDataValue(14, i) # 거부내용
|
|
print(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11)
|
|
|
|
# 미체결
|
|
if (i6 == "주문예정"):
|
|
item = orderData()
|
|
item.orderNum = i5
|
|
if (i1 == "매수"):
|
|
item.bs = EorderBS.buy
|
|
else:
|
|
item.bs = EorderBS.sell
|
|
item.code = i2
|
|
item.amount = i3
|
|
item.price = i7
|
|
|
|
orderList.append(item)
|
|
|
|
# 연속 처리 체크 - 다음 데이터가 없으면 중지
|
|
if objResult.Continue == False:
|
|
break
|
|
|
|
return orderList
|
|
|
|
# 주식 현재가 조회
|
|
def writeStockData(self, stock_codes, given_day):
|
|
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
|
|
bConnect = objCpCybos.IsConnect
|
|
if (bConnect == 0):
|
|
print("PLUS가 정상적으로 연결되지 않음. ")
|
|
exit()
|
|
|
|
# 차트 객체 구하기
|
|
objStockChart = win32com.client.Dispatch("CpSysDib.StockChart")
|
|
|
|
for stock_code in stock_codes:
|
|
outfp = open("./data/"+stock_code+"_"+given_day+".csv", mode="w", encoding="utf-8")
|
|
objStockChart.SetInputValue(0, 'A' + stock_code) # 종목 코드
|
|
objStockChart.SetInputValue(1, ord('1')) # 1: 기간으로 조회, 2: 개수로 조회
|
|
objStockChart.SetInputValue(2, given_day) # 기간 조회 시, 시작일
|
|
objStockChart.SetInputValue(3, given_day) # 기간 조회 시, 종료일
|
|
objStockChart.SetInputValue(4, 400) # 조회 시 가져오는 Line 개수
|
|
objStockChart.SetInputValue(5, [0, 1, 2, 3, 4, 5, 8]) # 날짜,시간,시가,고가,저가,종가,거래량
|
|
objStockChart.SetInputValue(6, ord('m')) # '차트 주가 - 월(M), 주(W), 일(D), 시(H), 분(m), 초(S) 차트 요청
|
|
objStockChart.SetInputValue(7, 1)
|
|
objStockChart.SetInputValue(9, ord('1')) # 수정주가 사용
|
|
objStockChart.BlockRequest()
|
|
|
|
size = objStockChart.GetHeaderValue(3)
|
|
|
|
outfp.write("%s,%s,%s,%s,%s,%s,%s\n" % ("날짜", "시간", "시가", "고가", "저가", "종가", "거래량"))
|
|
for i in range(size - 1, -1, -1):
|
|
day = objStockChart.GetDataValue(0, i)
|
|
time = objStockChart.GetDataValue(1, i)
|
|
start = objStockChart.GetDataValue(2, i)
|
|
high = objStockChart.GetDataValue(3, i)
|
|
low = objStockChart.GetDataValue(4, i)
|
|
close = objStockChart.GetDataValue(5, i)
|
|
vol = objStockChart.GetDataValue(6, i)
|
|
outfp.write("%d,%s,%d,%d,%d,%d,%d\n" % (day, str(time).zfill(4), start, high, low, close, vol))
|
|
outfp.close()
|
|
|
|
return
|
|
|
|
|
|
def write(self, day, result):
|
|
#날짜,시간,시가,고가,저가,종가,거래량
|
|
#20210909,900,2070,2070,2070,2070,0
|
|
|
|
outFp = open(day+".csv", mode="w", encoding="UTF-8")
|
|
outFp.write("날짜,시간,시가,고가,저가,종가,거래량\n")
|
|
for i in range(len(result["time"])):
|
|
outFp.write("%s,%s,%s,%s,%s,%s,%s\n"%(
|
|
result["time"][i].strftime('%Y%m%d'),
|
|
result["time"][i].strftime('%H%M'),
|
|
result["open"][i],
|
|
result["high"][i],
|
|
result["low"][i],
|
|
result["close"][i],
|
|
result["vol"][i]))
|
|
outFp.close()
|
|
return
|
|
|
|
# 주식 현재가 조회
|
|
def getRealTime(self, stock_code, given_day, result):
|
|
int_given_day = int(given_day)
|
|
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
|
|
bConnect = objCpCybos.IsConnect
|
|
if (bConnect == 0):
|
|
print("PLUS가 정상적으로 연결되지 않음. ")
|
|
exit()
|
|
|
|
# 차트 객체 구하기
|
|
objStockChart = win32com.client.Dispatch("CpSysDib.StockChart")
|
|
|
|
objStockChart.SetInputValue(0, 'A'+stock_code) # 종목 코드
|
|
objStockChart.SetInputValue(1, ord('1')) # 1: 기간으로 조회, 2: 개수로 조회
|
|
objStockChart.SetInputValue(2, given_day) # 기간 조회 시, 시작일
|
|
objStockChart.SetInputValue(3, given_day) # 기간 조회 시, 종료일
|
|
objStockChart.SetInputValue(4, 400) # 조회 시 가져오는 Line 개수
|
|
objStockChart.SetInputValue(5, [0, 1, 2, 3, 4, 5, 8]) # 날짜,시간,시가,고가,저가,종가,거래량
|
|
objStockChart.SetInputValue(6, ord('m')) # '차트 주가 - 월(M), 주(W), 일(D), 시(H), 분(m), 초(S) 차트 요청
|
|
objStockChart.SetInputValue(7, 1)
|
|
objStockChart.SetInputValue(9, ord('1')) # 수정주가 사용
|
|
objStockChart.BlockRequest()
|
|
|
|
size = objStockChart.GetHeaderValue(3)
|
|
|
|
#print("날짜", "시간", "시가", "고가", "저가", "종가", "거래량")
|
|
start_time = datetime.strptime(given_day + " 090000", '%Y%m%d %H%M%S')
|
|
for i in range(size-1, -1, -1):
|
|
int_day = objStockChart.GetDataValue(0, i)
|
|
int_time = objStockChart.GetDataValue(1, i)
|
|
|
|
if int_day < int_given_day:
|
|
continue
|
|
time = datetime.strptime(str(int_day)+" "+str(int_time).zfill(4)+"00", '%Y%m%d %H%M%S')
|
|
if time < start_time:
|
|
continue
|
|
|
|
open = objStockChart.GetDataValue(2, i)
|
|
close = objStockChart.GetDataValue(5, i)
|
|
high = objStockChart.GetDataValue(3, i)
|
|
low = objStockChart.GetDataValue(4, i)
|
|
vol = objStockChart.GetDataValue(6, i)
|
|
|
|
if len(result["check"]) == 0:
|
|
result["check"].add(start_time)
|
|
result["time"].append(start_time)
|
|
result["open"].append(open)
|
|
result["close"].append(open)
|
|
result["high"].append(open)
|
|
result["low"].append(open)
|
|
result["vol"].append(0)
|
|
|
|
if time not in result["check"]:
|
|
result["check"].add(time)
|
|
result["time"].append(time)
|
|
|
|
result["open"].append(open)
|
|
result["close"].append(close)
|
|
result["high"].append(high)
|
|
result["low"].append(low)
|
|
result["vol"].append(vol)
|
|
|
|
return
|
|
|
|
def getCSV(self, fileName, given_day, result):
|
|
data = pd.read_csv(fileName)
|
|
|
|
days = data.날짜
|
|
time = data.시간
|
|
open = data.시가
|
|
close = data.종가
|
|
high = data.고가
|
|
low = data.저가
|
|
vol = data.거래량
|
|
start_time = datetime.strptime(given_day + " 090000", '%Y%m%d %H%M%S')
|
|
|
|
for i in range(len(data)):
|
|
temp = datetime.strptime(str(days[i]) + " " + str(time[i]).zfill(4)+"00", '%Y%m%d %H%M%S')
|
|
if temp < start_time:
|
|
continue
|
|
|
|
if temp not in result["check"]:
|
|
result["check"].add(temp)
|
|
result["time"].append(temp)
|
|
result["open"].append(open[i])
|
|
result["close"].append(close[i])
|
|
result["high"].append(high[i])
|
|
result["low"].append(low[i])
|
|
result["vol"].append(vol[i])
|
|
return
|
|
|
|
def analyze(self, result):
|
|
df = pd.DataFrame(result["close"])
|
|
|
|
max20 = df.rolling(window=10).mean()
|
|
stddev20 = df.rolling(window=10).std()
|
|
upper_df = max20 + (stddev20 * 2) # 상단 볼린저 밴드
|
|
lower_df = max20 - (stddev20 * 2) # 하단 볼린저 밴드
|
|
|
|
size = len(result["open"])
|
|
window = 5
|
|
open = result["open"]
|
|
close = result["close"]
|
|
high = result["high"]
|
|
low = result["low"]
|
|
vol = result["vol"]
|
|
|
|
close_df = pd.DataFrame(close)
|
|
avg5_list = close_df.rolling(window=3).mean().fillna(close[0]).values.tolist()
|
|
avg5 = [item[0] for item in avg5_list]
|
|
avg20_list = close_df.rolling(window=5).mean().fillna(close[0]).values.tolist()
|
|
avg20 = [item[0] for item in avg20_list]
|
|
avg60_list = close_df.rolling(window=10).mean().fillna(close[0]).values.tolist()
|
|
avg60 = [item[0] for item in avg60_list]
|
|
avg120_list = close_df.rolling(window=20).mean().fillna(close[0]).values.tolist()
|
|
avg120 = [item[0] for item in avg120_list]
|
|
avg240_list = close_df.rolling(window=40).mean().fillna(close[0]).values.tolist()
|
|
avg240 = [item[0] for item in avg240_list]
|
|
|
|
upper, lower = [], []
|
|
for i in range(len(upper_df)):
|
|
if i < window:
|
|
upper.append(upper_df.values[window - 1][0])
|
|
lower.append(lower_df.values[window - 1][0])
|
|
else:
|
|
upper.append(upper_df.values[i][0])
|
|
lower.append(lower_df.values[i][0])
|
|
|
|
point_temp = result["time"]
|
|
|
|
temp = {"Date": point_temp, "Open": open, "High": high, "Low": low, "Close": close, "Volume": vol, "avg5": avg5, "avg20": avg20, "avg60": avg60, "avg120": avg120, "avg240": avg240}
|
|
data = pd.DataFrame(temp)
|
|
df_final_time = pd.DatetimeIndex(point_temp)
|
|
data.index = df_final_time
|
|
|
|
return data, upper, lower
|
|
|
|
def draw(self, stock_code, given_day, data, upper, lower, bsLine):
|
|
buy_line = bsLine['buy']
|
|
sell_line = bsLine['sell']
|
|
|
|
# 그래프 설정을 위한 변수를 생성한다.
|
|
data['Open'] = pd.to_numeric(data['Open'])
|
|
data['High'] = pd.to_numeric(data['High'])
|
|
data['Low'] = pd.to_numeric(data['Low'])
|
|
data['Close'] = pd.to_numeric(data['Close'])
|
|
data['Volume'] = pd.to_numeric(data['Volume'])
|
|
data['avg5'] = pd.to_numeric(data['avg5'])
|
|
data['avg20'] = pd.to_numeric(data['avg20'])
|
|
data['avg60'] = pd.to_numeric(data['avg60'])
|
|
data['avg120'] = pd.to_numeric(data['avg120'])
|
|
data['avg240'] = pd.to_numeric(data['avg240'])
|
|
|
|
buy_colors = []
|
|
for i in range(len(buy_line)):
|
|
if buy_line[i] < 0:
|
|
buy_colors.append("#ffffff")
|
|
buy_line[i] = lower[0]
|
|
else:
|
|
buy_colors.append("#ff00ff")
|
|
sell_colors = []
|
|
for i in range(len(sell_line)):
|
|
if sell_line[i] < 0:
|
|
sell_colors.append("#ffffff")
|
|
sell_line[i] = lower[0]
|
|
else:
|
|
sell_colors.append("#00ced1")
|
|
|
|
# 그래프를 설정한다.
|
|
buy_check = go.Scatter(x=data['Date'], y=buy_line, mode='markers', name="buy", marker=dict(size=14, color=buy_colors, line_width=0))
|
|
sell_check = go.Scatter(x=data['Date'], y=sell_line, mode='markers', name="sell", marker=dict(size=14, color=sell_colors, line_width=0))
|
|
bolinger_upper = go.Scatter(x=data['Date'], y=upper, name="upper", line_color='#8B4513')
|
|
bolinger_lower = go.Scatter(x=data['Date'], y=lower, name="lower", line_color='#8B4513')
|
|
avg5 = go.Scatter(x=data['Date'], y=data['avg5'], name="avg5", line_color='#FF0000')
|
|
avg20 = go.Scatter(x=data['Date'], y=data['avg20'], name="avg20", line_color='#F43B86')
|
|
avg60 = go.Scatter(x=data['Date'], y=data['avg60'], name="avg60", line_color='#F0A500')
|
|
avg120 = go.Scatter(x=data['Date'], y=data['avg120'], name="avg120", line_color='#14279B')
|
|
avg240 = go.Scatter(x=data['Date'], y=data['avg240'], name="avg240", line_color='#000000')
|
|
|
|
|
|
candle_stick = go.Candlestick(x=data['Date'], open=data['Open'], high=data['High'], low=data['Low'], close=data['Close'], increasing_line_color='red', decreasing_line_color='blue')
|
|
|
|
# 그래프를 그린다.
|
|
fig = go.Figure(data=[candle_stick, bolinger_upper, bolinger_lower, buy_check, sell_check, avg5, avg20, avg60, avg120, avg240])
|
|
fig.update_layout(title=stock_code + "_" + given_day)
|
|
fig.show()
|
|
return
|
|
|
|
def checkStatus(self, STOCK, last_index):
|
|
status = set()
|
|
|
|
# 정배열 체크
|
|
temp_status = self.common.check_RightArrange(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 20일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, last_index, '20')
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 60일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, last_index, '60')
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 120일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, last_index, '120')
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 240일선 돌파
|
|
#temp_status = self.common.check_Dolpa_Jiji(STOCK, last_index, '240')
|
|
#if temp_status != "":
|
|
# status.add(temp_status)
|
|
|
|
# 20일선 지지 매수가 추천
|
|
temp_status = self.common.check_Dolpa_Jiji_20(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 음봉인데 어제보다 종가가 더 높은 경우
|
|
# 이 경우 정배열 상태인지도 함께 체크를 한다.
|
|
higher_umbong_status = self.common.checkHigherUmbong(STOCK, last_index)
|
|
if higher_umbong_status != "":
|
|
status.add(temp_status)
|
|
|
|
# GOLDENCROSS#1은 바로 매수하지 않고, 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
|
|
# GOLDENCROSS#2은 바로 매수 가능
|
|
# GOLDENCROSS#3은 바로 매수 가능
|
|
temp_status = self.common.check_golded_cross(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# YANGBONG
|
|
# 어제 음봉 이후 장대양봉이었다면, 매수
|
|
temp_status = self.common.checkLongYangBongAfterUmBong(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# Doji
|
|
# 하락 추세에서 도지가 나오면 매수
|
|
temp_status = self.common.checkDoji(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# Gravestone
|
|
# 상승 추세에서 그레이브스톤이 나오면 매도
|
|
temp_status = self.common.checkGravestone(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# Dragonfly
|
|
# 하락 추세에서 드레곤플라이가 나오면 매수
|
|
temp_status = self.common.checkDragonfly(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# Hammer
|
|
temp_status = self.common.checkHammer(STOCK, last_index)
|
|
# 하락 추세에서 해머가 나오면 매수
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# Hangingman
|
|
temp_status = self.common.checkHangingman(STOCK, last_index)
|
|
# 상승 추세에서 행잉맨이 나오면 매도
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 상승장악형 (Engulfing) - 다음 날도 양봉이라면 매수
|
|
# 하락 추세에서 상승장악형이 나오면 매수
|
|
temp_status = self.common.checkEngulfingHigh(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 하락장악형 (Engulfing)
|
|
# 상승 추세에서 하락장악형이 나오면 매도
|
|
temp_status = self.common.checkEngulfingLow(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 상승 포아형 (Harami)
|
|
# 하락 추세에서 상승포아형이 나오면 매수
|
|
temp_status = self.common.checkHaramiHigh(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 하락 포아형 (Harami)
|
|
# 상승 추세에서 하락포아형이 나오면 매도
|
|
temp_status = self.common.checkHaramiLow(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 관통형 (piercing)
|
|
# 하락 추세에서 관통형이 나오면 매수
|
|
temp_status = self.common.checkPiercing(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 흑운형 (Dark-cloud)
|
|
# 상승 추세에서 흑운형이 나오면 매도
|
|
temp_status = self.common.checkDarkCloud(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 샛별 (Morning start)
|
|
# 하락 추세에서 샛별형이 나오면 매수
|
|
temp_status = self.common.checkMorningstar(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
# 저녁별 (Evening start)
|
|
# 상승 추세에서 저녁별형이 나오면 매도
|
|
temp_status = self.common.checkEveningstar(STOCK, last_index)
|
|
if temp_status != "":
|
|
status.add(temp_status)
|
|
|
|
return status
|
|
|
|
def checkTransaction_Realtime(self, data, upper, lower):
|
|
size = len(data["Close"])
|
|
STOCK = []
|
|
for i in range(size):
|
|
STOCK.append({'volume': data['Volume'][i], 'close': data["Close"][i], 'open': data["Open"][i], 'high': data["High"][i], 'low': data["Low"][i], 'avg5': data["avg5"][i], 'avg20': data["avg20"][i], 'avg60': data["avg60"][i], 'avg120': data["avg120"][i]})
|
|
|
|
bsLine = {}
|
|
bsLine['buy'] = [-1 for i in range(size)]
|
|
bsLine['sell'] = [-1 for i in range(size)]
|
|
|
|
i = size - 1
|
|
status = self.checkStatus(STOCK, i)
|
|
count_1 = 0
|
|
if "arrange_" in status: count_1 += 1
|
|
if "arrange_" in status and "HIGHERUMBONG_" in status: count_1 += 1
|
|
if "20_" in status: count_1 += 1
|
|
if "60_" in status: count_1 += 1
|
|
if "120_" in status: count_1 += 1
|
|
if "240_" in status: count_1 += 1
|
|
if "5-20_" in status: count_1 += 1
|
|
if "GOLDEN#2_" in status: count_1 += 1
|
|
if "GOLDEN#3_" in status: count_1 += 1
|
|
if "UMYANG_" in status: count_1 += 1
|
|
if "DOJI_" in status: count_1 += 1
|
|
if "DRAGONEFLY_" in status: count_1 += 1
|
|
if "HAMMER_" in status: count_1 += 1
|
|
if "ENHIGH_" in status: count_1 += 1
|
|
if "HAHIGH_" in status: count_1 += 1
|
|
if "PIERCING_" in status: count_1 += 1
|
|
if "MORNINGSTAR_" in status: count_1 += 1
|
|
count_0 = 0
|
|
if "GRAVESTONE_" in status: count_0 += 1
|
|
if "HANGINGMAN_" in status: count_0 += 1
|
|
if "ENLOW_" in status: count_0 += 1
|
|
if "HALOW_" in status: count_0 += 1
|
|
if "DARKCLOUD_" in status: count_0 += 1
|
|
if "EVENINGSTAR" in status: count_0 += 1
|
|
|
|
# real time 에서는 현재 기점에 사고 파는 가격을 표기한다.
|
|
if count_0 == 0 and count_1 > 0:
|
|
bsLine['buy'][i] = STOCK[i]['close'] - 5
|
|
bsLine['sell'][i] = STOCK[i]['close']
|
|
if count_0 > 0:
|
|
bsLine['buy'][i] = 0
|
|
bsLine['sell'][i] = STOCK[i]['close'] + 5
|
|
|
|
return bsLine['buy'][i], bsLine['sell'][i]
|
|
|
|
|
|
def checkTransaction_Simulation(self, data, upper, lower):
|
|
size = len(data["Close"])
|
|
STOCK = []
|
|
for i in range(size):
|
|
STOCK.append({'volume': data['Volume'][i], 'close': data["Close"][i], 'open': data["Open"][i], 'high': data["High"][i], 'low': data["Low"][i], 'avg5': data["avg5"][i], 'avg20': data["avg20"][i], 'avg60': data["avg60"][i], 'avg120': data["avg120"][i]})
|
|
|
|
bsLine = {}
|
|
bsLine['buy'] = [-1 for i in range(len(lower))]
|
|
bsLine['sell'] = [-1 for i in range(len(lower))]
|
|
|
|
for i in range(5, size-5):
|
|
status = self.checkStatus(STOCK, i)
|
|
count_1 = 0
|
|
if "arrange_" in status: count_1 += 1
|
|
if "arrange_" in status and "HIGHERUMBONG_" in status: count_1 += 1
|
|
if "20_" in status: count_1 += 1
|
|
if "60_" in status: count_1 += 1
|
|
if "120_" in status: count_1 += 1
|
|
if "240_" in status: count_1 += 1
|
|
if "5-20_" in status: count_1 += 1
|
|
if "GOLDEN#2_" in status: count_1 += 1
|
|
if "GOLDEN#3_" in status: count_1 += 1
|
|
if "UMYANG_" in status: count_1 += 1
|
|
if "DOJI_" in status: count_1 += 1
|
|
if "DRAGONEFLY_" in status: count_1 += 1
|
|
if "HAMMER_" in status: count_1 += 1
|
|
if "ENHIGH_" in status: count_1 += 1
|
|
if "HAHIGH_" in status: count_1 += 1
|
|
if "PIERCING_" in status: count_1 += 1
|
|
if "MORNINGSTAR_" in status: count_1 += 1
|
|
count_0 = 0
|
|
if "GRAVESTONE_" in status: count_0 += 1
|
|
if "HANGINGMAN_" in status: count_0 += 1
|
|
if "ENLOW_" in status: count_0 += 1
|
|
if "HALOW_" in status: count_0 += 1
|
|
if "DARKCLOUD_" in status: count_0 += 1
|
|
if "EVENINGSTAR" in status: count_0 += 1
|
|
|
|
# 시뮬레이션은 이번에 사고 파는 것으로 판단했기 때문에, 다음 봉에서 사고 판 위치를 표시한다.
|
|
if count_0 == 0 and count_1 > 0:
|
|
bsLine['buy'][i + 1] = STOCK[i]['close'] - 5
|
|
bsLine['sell'][i + 2] = STOCK[i]['close']
|
|
#if count_0 > 0:
|
|
# bsLine['sell'][i + 2] = STOCK[i]['close'] + 5
|
|
|
|
return bsLine
|
|
|
|
|
|
def simulate(self, stock_code, given_day):
|
|
#timecheckList = pd.read_csv("timecheck.csv").values.tolist()
|
|
#timecheck = {datetime.strptime(given_day + " " + str(second).zfill(6), '%Y%m%d %H%M%S'): False for second, check in timecheckList}
|
|
|
|
result = {"check": set(),
|
|
"time": [],
|
|
"open": [],
|
|
"close": [],
|
|
"high": [],
|
|
"low": [],
|
|
"vol": []}
|
|
|
|
# 데이터를 가지고 온다.
|
|
self.getCSV("./data/"+stock_code+"_"+given_day+".csv", given_day, result)
|
|
|
|
# 분석을 통해서 볼린저밴드 상/하단을 계산한다.
|
|
data, upper, lower = self.analyze(result)
|
|
|
|
# 사야 할 시점과 팔아야 할 시점을 체크한다.
|
|
bsLine = self.checkTransaction_Simulation(data, upper, lower)
|
|
|
|
# 그래프를 그린다.
|
|
self.draw(stock_code, given_day, data, upper, lower, bsLine)
|
|
|
|
# 가져온 만큼 데이터를 누적해서 파일로 작성한다.
|
|
# self.write(given_day, result)
|
|
|
|
return
|
|
|
|
def buyRealTime(self, stock_code, given_day):
|
|
PREVIOUS_PRICE = 0
|
|
BUY_COUNT = 200
|
|
TOTAL_BUY_AMT = 0
|
|
|
|
logFp = open(given_day+".log", "w")
|
|
|
|
timecheckList = pd.read_csv("timecheck.csv").values.tolist()
|
|
timecheck = {given_day + " " + str(second).zfill(6):False for second, check in timecheckList}
|
|
|
|
result = {"check": set(),
|
|
"time": [],
|
|
"open": [],
|
|
"close": [],
|
|
"high": [],
|
|
"low": [],
|
|
"vol": []}
|
|
|
|
print ("START...")
|
|
while datetime.strptime(given_day + " 083000", '%Y%m%d %H%M%S') < datetime.now() < datetime.strptime(given_day + " 151500", '%Y%m%d %H%M%S'):
|
|
second = datetime.now().strftime('%Y%m%d %H%M%S')
|
|
|
|
if second in timecheck and not timecheck[second]:
|
|
print("TIMECHECK", second)
|
|
logFp.write("%s,%s,\n" %("TIMECHECK", second))
|
|
logFp.flush()
|
|
|
|
# 데이터를 가지고 온다.
|
|
self.getRealTime(stock_code, given_day, result)
|
|
|
|
# 분석을 통해서 볼린저밴드 상/하단을 계산한다.
|
|
data, upper, lower = self.analyze(result)
|
|
|
|
# 사야 할 시점/가격과 팔아야 할 시점/가격을 체크한다.
|
|
bs_buy_price, bs_sell_price = self.checkTransaction_Realtime(data, upper, lower)
|
|
|
|
if bs_buy_price > 0:
|
|
if PREVIOUS_PRICE > 0:
|
|
if PREVIOUS_PRICE > bs_buy_price:
|
|
if BUY_COUNT > 240:
|
|
BUY_COUNT = 240
|
|
if BUY_COUNT <= 140:
|
|
BUY_COUNT = 140
|
|
BUY_COUNT += 10
|
|
elif PREVIOUS_PRICE < bs_buy_price:
|
|
if BUY_COUNT > 250:
|
|
BUY_COUNT = 260
|
|
if BUY_COUNT <= 150:
|
|
BUY_COUNT = 160
|
|
BUY_COUNT -= 10
|
|
|
|
PREVIOUS_PRICE = bs_buy_price
|
|
|
|
# 매수 주문
|
|
# 현재까지 매입금액이 7백만원 이하일 때만 매수를 한다.
|
|
if TOTAL_BUY_AMT < 5000000:
|
|
self.requestOrder("2", stock_code, BUY_COUNT , bs_buy_price)
|
|
|
|
## 매도 주문 (아래 잔고를 체크해서 매도를 호출하는 것으로 시도한다.)
|
|
#time.sleep(60)
|
|
#self.requestOrder("1", stock_code, BUY_COUNT , price + 5)
|
|
print("BUY", second, bs_buy_price)
|
|
logFp.write("%s,%s, %d\n" % ("BUY", second, bs_buy_price))
|
|
logFp.flush()
|
|
|
|
# 가져온 만큼 데이터를 누적해서 파일로 작성한다.
|
|
self.write(given_day, result)
|
|
|
|
timecheck[second] = True
|
|
else:
|
|
#print("NONE", second)
|
|
logFp.write("%s,%s,\n" % ("NONE", second))
|
|
logFp.flush()
|
|
|
|
# 만약 잔고가 있으면 장부가보다 5원 높게 매도한다.
|
|
jangoDic = self.requstJango()
|
|
if jangoDic and len(jangoDic.keys()) > 0:
|
|
for code in jangoDic:
|
|
TOTAL_BUY_AMT = jangoDic[code]['매입금액']
|
|
if jangoDic[code]['매도가능'] > 0:
|
|
# 장부가 가격의 마지막 자리를 0으로 만든다. (2090 -> 2090, 2092 -> 2090, 2098 -> 2090)
|
|
sell_price = int(jangoDic[code]['장부가'] / 10) * 10
|
|
# 장부가의 마지막 자리수를 가져온다.
|
|
last_number = int(jangoDic[code]['장부가']) % 10
|
|
if last_number in [0, 1, 2]:
|
|
# 장부가의 마지막 자리수가 0,1,2 라면 (2090, 2091, 2092 -> 2095 에 매도)
|
|
self.requestOrder("1", stock_code, jangoDic[code]['매도가능'], sell_price + 5)
|
|
elif last_number in [3, 4, 5, 6]:
|
|
# 장부가의 마지막 자리수가 3,4,5,6 라면 (2093, 2094, 2095, 2096 -> 2100 에 매도)
|
|
self.requestOrder("1", stock_code, jangoDic[code]['매도가능'], sell_price + 10)
|
|
else:
|
|
# 장부가의 마지막 자리수가 7,8,9 라면 (2097, 2098, 2099 -> 2105 에 매도)
|
|
self.requestOrder("1", stock_code, jangoDic[code]['매도가능'], sell_price + 15)
|
|
|
|
time.sleep(0.9)
|
|
|
|
logFp.close()
|
|
return
|
|
|
|
if __name__ == "__main__":
|
|
|
|
today = datetime.today()
|
|
|
|
PROJECT_HOME = os.path.join(os.path.dirname(os.path.join(os.path.dirname(__file__))))
|
|
RESOURCE_DIR = PROJECT_HOME + "/resources/analysis/"+today.strftime("%Y%m%d")
|
|
|
|
stock_codes = ["252670", "122630"]
|
|
given_days = ['20210901','20210902','20210903','20210906','20210907','20210908','20210909','20210910','20210913','20210914','20210915','20210916','20210917','20210923','20210924','20210927','20210928','20210929','20210930','20211001']
|
|
|
|
hts = HTS()
|
|
|
|
#hts.all_stocks()
|
|
#hts.getChartData(stock_codes)
|
|
#hts.currentStock(stock_codes)
|
|
#for given_day in given_days:
|
|
#hts.writeStockData(stock_codes, given_day)
|
|
#for stock_code in stock_codes:
|
|
#hts.simulate(stock_code, given_day)
|
|
|
|
given_day = datetime.today().strftime('%Y%m%d')
|
|
hts.buyRealTime(stock_codes[0], given_day)
|
|
|
|
print ("done...")
|