'파이썬' 태그의 글 목록 :: Horizontal Grays S2

정말 오랫동안 건드리지도 않았던 비전 일을 해야할일이 갑자기 생겨버렸다.

 

카메라도 렌즈도 내가 선택안했는데 남이 선택해놓은걸....

 

여튼 Basler 카메라, 리코 12mm c마운트 렌즈, GiGE 인터페이스 이거 달랑 인계 받고 시작하게 되었다.

 

GiGE 인터페이스라고 별다른 것은 없더라 이더넷 케이블로 연결하고 Basler 홈페이지에서 프로그램 다운받은 후

 

pylon viewer 실행 시켜서 보면 보인다.

 

어라.. 근데 흑백이다. 사람에게 보여줄게 아니므로 흑백도 상관없겠다만 프로젝트를 생각하면 컬러가 유리할듯한데...

 

일단 회사에 있는 로지텍 웹캠 C930c를 연결해서 이걸로 진행해보기로 한다.

 

지금까지는 C++ 에 opencv를 사용했는데

 

이번엔 파이썬에 opencv를 사용해보기로 결정

 

1. python 홈페이지에서 파이썬을 다운로드/ 설치  그냥 최신 3.10.4 다운로드 했다.

당연히 환경변수 path에 들어가야 한다.  (설치할때 체크하면 ok)

Download Python | Python.org

 

2. vscode 다운로드/설치

Download Visual Studio Code - Mac, Linux, Windows

 

Download Visual Studio Code - Mac, Linux, Windows

Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows. Download Visual Studio Code to experience a redefined code editor, optimized for building and debugging modern web and cloud applications.

code.visualstudio.com

 

3. 폴더 하나 만들고 그 안에서 작업

 

일단 vscode 실행시키고 터미널에서 

pip install opencv-python 

하면 opencv가 깔린다. 세상 편하다.. 

이게 뭐 문제점은 없는지 걱정이다 ㅋㅋㅋ

 

여튼 비전이 내 전공도 아니고 까먹을까봐 여기에 기록해가면서 또 진행해보려고 한다.

 

주식자동화 그것도 해야하는데... 귀찮네 ㅠㅜ

 

책을 아직도 다 읽진 않았지만 - 앞으로도 다 읽진 않겠지만 - 

매수종목 선정 방법 관련해서는 자세히 나온것이 없다. 

그래서 임의로 내가 방법을 세우고 프로그래밍해려고 한다.

 

검색해보면 이러한 종목이 오를 것이다라는 여러 이야기 들이 있지만 복잡한것까지는 잘 모르겠다.

내 경험상으로도 그렇고 검색 결과로도 그렇고 일단 거래량이 가장 중요한 것 같다.

그래서 일단 단순하게

 

1. 이틀전 거래량 - 하루전 거래량 으로 거래량 증가폭이 높은 순으로 정렬해서 최상단 20종목 추리고

2. 그중 하루전 거래량 기준으로 절대 거래량 높은 순으로 10종목 추려서 매수종목 리스트에 넣는다.

 

이런 단순한 방법을 우선 취해보기로 했다. 

이렇게 한 근거는 일단 없다. 이렇게 해보고 결과가 안좋으면 수정해야겠다.

 

크레온 플러스 API 예제를 이용하면 더 편할것 같기도 한데 이게 제한이 있다.

   조회 제한  실시간 제한
시세
오브젝트
15초에 최대 60건으로제한
초과 요청시 첫 요청으로부터 15초가
지날 때까지 내부적으로기다림
최대 400건의요청으로제한
초과요청시오류
주문관련오브젝트 15초에 최대 20건으로제한
초과 요청시 첫 요청으로부터 15초가
지날 때까지 요청함수(Request, BlockRequest, BlockRequest2)에서
4를 반환
제한 없음

그래서 일단 네이버 스크래핑을 위주로 이용해보기로 한다.

 

먼저 처음 예제였던 종목정보 구하기 예제를 차후에 파일로 가공할 것을 감안하여 조금 수정하고

실행해서 전체종목의 코드를 구해봤다. (이건 크레온 플러스 API 이용)

 

import win32com.client

# 연결 여부 체크
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
bConnect = objCpCybos.IsConnect
if (bConnect == 0):
    print("PLUS가 정상적으로 연결되지 않음. ")
    exit()
 
# 종목코드 리스트 구하기
objCpCodeMgr = win32com.client.Dispatch("CpUtil.CpCodeMgr")
codeList = objCpCodeMgr.GetStockListByMarket(1) #거래소
codeList2 = objCpCodeMgr.GetStockListByMarket(2) #코스닥
 
 
print("번호",",","종목코드",",","종목명",",","구분코드",",","금액")
for i, code in enumerate(codeList):
    secondCode = objCpCodeMgr.GetStockSectionKind(code)
    name = objCpCodeMgr.CodeToName(code)
    stdPrice = objCpCodeMgr.GetStockStdPrice(code)
    print(i,",",code,",", name,",", secondCode,",", stdPrice)
    
for i, code in enumerate(codeList2):
    secondCode = objCpCodeMgr.GetStockSectionKind(code)
    name = objCpCodeMgr.CodeToName(code)
    stdPrice = objCpCodeMgr.GetStockStdPrice(code)
    print(i+len(codeList),",",code,",", name,",", secondCode,",", stdPrice)
 
#print("거래소 + 코스닥 종목코드 ",len(codeList) + len(codeList2))

실행할 때 단지 아래와 같이 하면 print 결과값을 파일로 저장한다.

python test1.py > codelist.txt

 

이로써 codelist.txt라는 종목정보파일이 만들어졌다.

이 파일의 끝을 보면 "거래소 + 코스닥 종목코드  3306" 으로 총 3306 종목이 있슴을 알 수 있고

위 그림과 같이 ETF도 포함되어 있슴을 알 수 있다. 

 

이 txt파일을 엑셀에서 읽어서 금일거래량, 전일거래량, 거래량증가비 열을 추가하고 codelist.xlsx 로 저장했다.

 

우선 파이썬에서 엑셀 데이터를 다루기 위해서 openpyxl을 설치해주도록 한다.

pip install openpyxl을 cmd 창에서 실행

 

 

-----------------------------------------------------------------------------------------------------------------------------------

이렇게 하다가 문제가 발생했다.

여튼 엑셀데이터의 종목코드를 읽고 거래량을 구하는 과정에서 현재 거래정지된 종목의 경우 에러가 발생하는 것이었다.

여기서 예외처리를 할까 하다가 전면 수정을 하기로 하고 다시 프로그램을 작성했다.

종목코드 자체를 매일 실시간으로 읽어서 진행하기로...

 

검색해보니 pykrx 라고 주식관련 라이브러리가 아주 잘되어 있었고 어차피  엑셀을 이용해도 이 라이브러리를 이용했어야 했다. ("GitHub - sharebook-kr/pykrx: KRX 주식 정보 스크래핑" 참고)

 

기본적인 구상은 다음과 같다.

매일 장을 종료하면 이 프로그램을 실행하여

1. 종목코드를 get_market_ticker_list() 로 읽어와서

2. get_market_ohlcv_by_date() 로 거래량 정보를 읽고 가공 후 DataFrame에 집어넣고

3. 각 DataFrame을 거래량 별로 정렬 후 상위 추출, 거래량 변화율 별로 정렬 후 상위 추출

4. ETF는 70%, KOSPI/KOSDAQ에서는 30%로 구성해서 20개 종목을 최종 List 만들어서

5. 파일로 저장

6. 다음날 자동거래 프로그램에서 매수대상 리스트는 이 파일을 읽어서 20개의 후보군으로 실행

 

파이썬을 잘 모르기도 하고 혼자서 단순하게 막 짜다보니 좀 비효율적인 프로그램이 된것 같긴 하지만 일단 테스트로 돌려본 결과 잘 돌아가긴 한다.

 

내일 자동거래 프로그램을 백테스팅용으로 수정을 좀 하고 목,금 백테스팅을 좀 하고 4월부터는 실제거래를 해봐야겠다.

 

 

from pykrx import stock
import time,sys
import pandas as pd
from datetime import datetime,timedelta

#DataFrame define
kospi_stock_list = pd.DataFrame(columns=['stock_code','price','yesterday_volume','today_volume','volume_ratio'])
kosdaq_stock_list = pd.DataFrame(columns=['stock_code','price','yesterday_volume','today_volume','volume_ratio'])
etf_stock_list = pd.DataFrame(columns=['stock_code','price','yesterday_volume','today_volume','volume_ratio'])

today = datetime.today()- timedelta(0)      #오늘날짜 구하기 timedelta(1)을 빼면 어제날짜
yesterday = datetime.today()- timedelta(1)
str_today = today.strftime("%Y%m%d")        #구한 날짜를 String type으로
str_yesterday = yesterday.strftime("%Y%m%d")        #구한 날짜를 String type으로

print(str_today)
print(str_yesterday)
bt_day = stock.get_nearest_business_day_in_a_week(date=str_today)   #오늘 날짜 집어넣고 장을 여는 가장 가까운 날을 구함 오늘 장을 열면 오늘 날짜 반환
by_day = stock.get_nearest_business_day_in_a_week(date=str_yesterday)
print("btday=",bt_day)
print("byday=",by_day)
if bt_day != str_today: #오늘 날짜와 구한 날짜가 같지 않다는건 오늘 장을 열지 않는다는 것
    print("오늘은 장을 열지 않으므로 프로그램 종료")
    sys.exit(0)

kospilist = stock.get_market_ticker_list(date=bt_day, market="KOSPI")   #KOSPI 종목코드를 list로 반환
kosdaqlist = stock.get_market_ticker_list(date=bt_day, market="KOSDAQ") #KOSDAQ 종목코드를 list로 반환
etflist = stock.get_etf_ticker_list(date=bt_day)                        #ETF 종목코드를 list로 반환

kospi_stock_list_count = 0      #나중에 Dataframe에 count로 사용할 변수 초기화
kosdaq_stock_list_count = 0
etf_stock_list_count = 0

print("KOSPI 확인 - 종목수 : ",len(kospilist))
for kospi_ticker in kospilist:  #KOSPI 종목
#for i in range(100):   #for test
#    kospi_ticker = kospilist[i]    #for test
    df = stock.get_market_ohlcv_by_date(fromdate=by_day, todate=bt_day, ticker=kospi_ticker)    #KOSPI 종목코드 어제,오늘 관련정보 DataFrame

    yesterday_volume = df.iloc[0]['거래량']     #DataFrame에서 어제 거래량 변수로 입력
    today_volume = df.iloc[1]['거래량']         #DataFrame에서 오늘 거래량 변수로 입력
    today_price = df.iloc[1]['종가']            #DataFrame에서 오늘 종가 변수로 입력
    
    print("종목코드",kospi_ticker)
    print("종가",today_price) 
    print("어제 거래량",yesterday_volume)
    print("오늘 거래량", today_volume)
    if yesterday_volume == 0:                   #어제거래량이 없으면 거래량변화 -100
        volume_ratio = -100
    else:
        volume_ratio = (today_volume-yesterday_volume)/yesterday_volume     #거래량변화율 

    
    new_data = [kospi_ticker,today_price,yesterday_volume,today_volume,volume_ratio]
    kospi_stock_list.loc[kospi_stock_list_count] = new_data     #관련 정보 DataFrame의 행에 추가
    kospi_stock_list_count = kospi_stock_list_count + 1
    time.sleep(1) 

print("KOSDAQ 확인 - 종목수 : ",len(kosdaqlist))
for kosdaq_ticker in kosdaqlist:
#for i in range(100):       #for test
    #kosdaq_ticker = kosdaqlist[i]  #for test
    df = stock.get_market_ohlcv_by_date(fromdate=by_day, todate=bt_day, ticker=kosdaq_ticker)
    
    yesterday_volume = df.iloc[0]['거래량']
    today_volume = df.iloc[1]['거래량']
    today_price = df.iloc[1]['종가']
    
    print("종목코드",kosdaq_ticker)
    print("종가",today_price) 
    print("어제 거래량",yesterday_volume)
    print("오늘 거래량", today_volume)
    if yesterday_volume == 0:
        volume_ratio = -100
    else:
        volume_ratio = (today_volume-yesterday_volume)/yesterday_volume

    
    new_data = [kosdaq_ticker,today_price,yesterday_volume,today_volume,volume_ratio]
    kosdaq_stock_list.loc[kosdaq_stock_list_count] = new_data
    kosdaq_stock_list_count = kosdaq_stock_list_count + 1
    time.sleep(1) 



print("ETF 확인 - 종목수 : ",len(etflist))
for etf_ticker in etflist:
#for i in range(100):   #for test
    #etf_ticker = etflist[i]    #for test
    df = stock.get_market_ohlcv_by_date(fromdate=by_day, todate=bt_day, ticker=etf_ticker)
    
    yesterday_volume = df.iloc[0]['거래량']
    today_volume = df.iloc[1]['거래량']
    today_price = df.iloc[1]['종가']
    
    print("종목코드",etf_ticker)
    print("종가",today_price) 
    print("어제 거래량",yesterday_volume)
    print("오늘 거래량", today_volume)
    if yesterday_volume == 0:
        volume_ratio = -100
    else:
        volume_ratio = (today_volume-yesterday_volume)/yesterday_volume

    
    new_data = [etf_ticker,today_price,yesterday_volume,today_volume,volume_ratio]
    etf_stock_list.loc[etf_stock_list_count] = new_data
    etf_stock_list_count = etf_stock_list_count + 1
    time.sleep(1) 

all_stock_list = pd.concat([kospi_stock_list,kosdaq_stock_list])    #KOSPI와 KOSDAQ은 합쳐서 하나의 DataFrame으로

print("KOSPI")
print(kospi_stock_list)
print("KOSDAQ")
print(kosdaq_stock_list)
print("KOSPI+KOSDAQ")
print(all_stock_list)
print("ETF")
print(etf_stock_list)


all_stock_list.sort_values(['today_volume'],axis=0,ascending=False,inplace=True)    #거래량 우선으로 정렬
sort_volume_list = all_stock_list.head(12)                                          #그중 12개만 추출해서 저장
print("all stock sort by volume")
print(sort_volume_list)
sort_volume_list.sort_values(['volume_ratio'],axis=0,ascending=False,inplace=True)  #12개를 거래량 변화율로 정렬
sort_ratio_list = sort_volume_list.head(6)                                          #그 중 6개만 추출해서 저장
print("all stock sort by volume")
print(sort_ratio_list)

etf_stock_list.sort_values(['today_volume'],axis=0,ascending=False,inplace=True)    #ETF도 같은 과정 
etf_sort_volume_list = etf_stock_list.head(28)
print("ETF stock sort by volume")
print(etf_sort_volume_list)
etf_sort_volume_list.sort_values(['volume_ratio'],axis=0,ascending=False,inplace=True)
etf_sort_ratio_list = etf_sort_volume_list.head(14)             
print("ETF stock sort by volume")
print(etf_sort_ratio_list)

total_stock_list = pd.concat([sort_ratio_list,etf_sort_ratio_list])     #최종 KOSPI,KOSDAQ에서 6개, ETF에서 14개 총 20개 를 하나의 DataFrame으로 합치고
print("Total stock sort by volume")
print(total_stock_list)

final_stock_list = list(total_stock_list['stock_code'])     #DataFrame에서 종목코드만 List로 뽑고
print("final Stock List")
print(final_stock_list)

for i in range(len(final_stock_list)):
    final_stock_list[i] = 'A'+ str(final_stock_list[i])     #List에 'A' 추가해서
print(final_stock_list)

f = open('codelist.txt','w')
f.write(','.join(final_stock_list))     #파일로 저장
f.close()

#symbol_list = open('codelist.txt','r').read().split(',')       #for verify
#print(symbol_list)                                             #for verify

지난 포스팅에서 무엇을 살것인가에서 ETF를, 그리고 ETF 종목의 조회를 어떻게 하는가 봤다

 

그리고 얼마에 살 것인가는 변동성 돌파 전략에서 K값의 결정에 따라 산다 정도로 설명했다.

 

일단 프로그램 전체를 가져와보자.

 

역시 기본적으로 책인 [파이썬 증권 데이터 분석] 에서 제공하는 github의 소스를 이용한다.

StockAnalysisInPython/ch08_03_EtfAlgoTrader.py at master · INVESTAR/StockAnalysisInPython · GitHub

 

GitHub - INVESTAR/StockAnalysisInPython

Contribute to INVESTAR/StockAnalysisInPython development by creating an account on GitHub.

github.com

다만 slack으로 메시지 보내는 방법은 수정되어야 해서 전에 slack 관련 포스팅과 같은 방식으로 수정하였다.

아래와 같이 post_message 함수 정의해서 사용했다.

def post_message(token, channel, text):
    response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer "+token},
        data={"channel": channel,"text": text}
    )
    print(response)

내가 직접 짠 소스가 아니므로 여기에 모두 포스팅 하지는 못하겠고 이정도 수정은 필요하신 분이시라면 어렵지 않게 하실수 있을것이라 생각한다.

 

같은 과정으로 vscode 실행하고 새 파이썬 파일 만들어서 소스 복붙하고 위의 post_message 부분으로 수정하면 된다.

 

일단 나의 경우 AutoTrade.py라고 저장하였고. 이 AutoTrade를 실행하기전에 이전포스팅에서 설명했던 AutoConnect.py를 먼저 실행해서 CREON Plus와 연결을 한다. 그리고 이 AutoTrade.py를 실행하는 것이다.

 

우선 main 부분만 훑어 보자.

if __name__ == '__main__':
    try:
        symbol_list = ['A122630', 'A252670', 'A233740', 'A250780', 'A225130',
             'A280940', 'A261220', 'A217770', 'A295000', 'A176950'] #매수 대상 종목 리스트
        bought_list = []     # 매수 완료된 종목 리스트
        target_buy_count = 5 # 매수할 종목 수
        buy_percent = 0.19   #종목별 매수 비율 5 종목 * 19%씩 매수 = 95% 매수하고 5% 금액 남게 된다.
        printlog('check_creon_system() :', check_creon_system())  # 크레온 접속 점검
        stocks = get_stock_balance('ALL')      # 보유한 모든 종목 조회
        total_cash = int(get_current_cash())   # 100% 증거금 주문 가능 금액 조회
        buy_amount = total_cash * buy_percent  # 종목별 주문 금액 계산
        printlog('100% 증거금 주문 가능 금액 :', total_cash)
        printlog('종목별 주문 비율 :', buy_percent)
        printlog('종목별 주문 금액 :', buy_amount)
        printlog('시작 시간 :', datetime.now().strftime('%m/%d %H:%M:%S'))
        soldout = False

        while True:
            t_now = datetime.now() #현재시간
            t_9 = t_now.replace(hour=9, minute=0, second=0, microsecond=0)  # 9:00
            t_start = t_now.replace(hour=9, minute=5, second=0, microsecond=0) # 9:05 시작 시간
                                                                                      참고로 유동성 공급자 활동시간 9:05 ~ 15:20
            t_sell = t_now.replace(hour=15, minute=15, second=0, microsecond=0) # 15:15 매도 시간
            t_exit = t_now.replace(hour=15, minute=20, second=0,microsecond=0) # 15:20 종료 시간
            today = datetime.today().weekday() #금일 요일
            if today == 5 or today == 6:  # 토요일이나 일요일이면 자동 종료
                printlog('Today is', 'Saturday.' if today == 5 else 'Sunday.')
                sys.exit(0)
            if t_9 < t_now < t_start and soldout == False:
                                            #9:00~9:05 사이에 시작하기전 혹시나 남은 주식이 있으면 다 매도
                soldout = True
                sell_all()
            if t_start < t_now < t_sell :  # AM 09:05 ~ PM 03:15 : 매수
                for sym in symbol_list: # 매수대상 종목리스트에서
                    if len(bought_list) < target_buy_count: #매수 완료한 종목이 목표수보다 작으면
                        buy_etf(sym)    #매수
                        time.sleep(1)
                if t_now.minute == 30 and 0 <= t_now.second <= 5:   #30분마다 잔고 등의 정보를 알림
                    get_stock_balance('ALL')
                    time.sleep(5)
            if t_sell < t_now < t_exit:  # PM 03:15 ~ PM 03:20 : 일괄 매도
                if sell_all() == True:
                    dbgout('`sell_all() returned True -> self-destructed!`')
                    sys.exit(0)
            if t_exit < t_now:  # PM 03:20 ~ :프로그램 종료
                dbgout('`self-destructed!`')
                sys.exit(0)
            time.sleep(3)
    except Exception as ex:
        dbgout('`main -> exception! ' + str(ex) + '`')

일단 설명할 부분은 주석으로 달아놨고

여기서 매수할 종목 대상 리스트는 ETF든 KOSPI든 KOSDAQ이든 미리 알아서 집어넣어두면 된다.

그리고 얼마에 매수할것인지에 대한 결정은 buy_etf()함수에서 불러오는

아래의 get_target_price()에 함수에서 설정되는 K값 0.5로 결정된다.

def get_target_price(code):
    """매수 목표가를 반환한다."""
    try:
        time_now = datetime.now()
        str_today = time_now.strftime('%Y%m%d')
        ohlc = get_ohlc(code, 10)
        if str_today == str(ohlc.iloc[0].name):
            today_open = ohlc.iloc[0].open
            lastday = ohlc.iloc[1]
        else:
            lastday = ohlc.iloc[0]                                      
            today_open = lastday[3]
        lastday_high = lastday[1]
        lastday_low = lastday[2]
        target_price = today_open + (lastday_high - lastday_low) * 0.5
        return target_price
    except Exception as ex:
        dbgout("`get_target_price() -> exception! " + str(ex) + "`")
        return None

현재 K값을 0.5 상수로 두고 있는데 이를 변수로 두던 상수로 두던 값 역시 수정하면 된다.
다만 K값이 낮으면 매수가 쉽고 높으면 어렵다..... 당연하군 ^^;

실제로 이 프로그램으로 매수/매도를 해보질 않았다.
일단 함수단위 테스트를 먼저 해보자.

바로 위 get_target_price()에 KODEX 200(069500) 을 한번 테스트 해보자. 

일단 test2.py 라는 임의로 파이썬 파일을 만들고 AutoTrade.py에서 일부분만 테스트를 위해 좀 잘라서

다음과 같이 테스트프로그램을 작성해서 실행해본다.

import os, sys, ctypes
import win32com.client
import pandas as pd
from datetime import datetime
import requests
import time, calendar

# 크레온 플러스 공통 OBJECT
cpCodeMgr = win32com.client.Dispatch('CpUtil.CpStockCode')
cpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
cpTradeUtil = win32com.client.Dispatch('CpTrade.CpTdUtil')
cpStock = win32com.client.Dispatch('DsCbo1.StockMst')
cpOhlc = win32com.client.Dispatch('CpSysDib.StockChart')
cpBalance = win32com.client.Dispatch('CpTrade.CpTd6033')
cpCash = win32com.client.Dispatch('CpTrade.CpTdNew5331A')
cpOrder = win32com.client.Dispatch('CpTrade.CpTd0311')  

def get_ohlc(code, qty):
    """인자로 받은 종목의 OHLC 가격 정보를 qty 개수만큼 반환한다."""
    cpOhlc.SetInputValue(0, code)           # 종목코드
    cpOhlc.SetInputValue(1, ord('2'))        # 1:기간, 2:개수
    cpOhlc.SetInputValue(4, qty)             # 요청개수
    cpOhlc.SetInputValue(5, [0, 2, 3, 4, 5]) # 0:날짜, 2~5:OHLC
    cpOhlc.SetInputValue(6, ord('D'))        # D:일단위
    cpOhlc.SetInputValue(9, ord('1'))        # 0:무수정주가, 1:수정주가
    cpOhlc.BlockRequest()
    count = cpOhlc.GetHeaderValue(3)   # 3:수신개수
    columns = ['open', 'high', 'low', 'close']
    index = []
    rows = []
    for i in range(count):
        index.append(cpOhlc.GetDataValue(0, i))
        rows.append([cpOhlc.GetDataValue(1, i), cpOhlc.GetDataValue(2, i),
            cpOhlc.GetDataValue(3, i), cpOhlc.GetDataValue(4, i)])
    df = pd.DataFrame(rows, columns=columns, index=index)
    return df

def get_target_price(code):
    """매수 목표가를 반환한다."""
    try:
        time_now = datetime.now()
        str_today = time_now.strftime('%Y%m%d')
        ohlc = get_ohlc(code, 10)
        print(ohlc)
        if str_today == str(ohlc.iloc[0].name):
            today_open = ohlc.iloc[0].open
            lastday = ohlc.iloc[1]
        else:
            lastday = ohlc.iloc[0]                                      
            today_open = lastday[3]
        lastday_high = lastday[1]
        lastday_low = lastday[2]
        target_price = today_open + (lastday_high - lastday_low) * 0.5
        return target_price
    except Exception as ex:
        print("`get_target_price() -> exception! " + str(ex) + "`")
        return None

target_price = get_target_price('A069500')
print("목표가 = " + str(target_price))

2022년 2월 24일 현재 실행결과 아래와 같이 나온다.

즉 KODEX200 의 열흘간 시작가, 최고가, 최저가, 종가 가 출력되고 이를 토대로

K값이 0.5일때 목표가는 \36642.5 원이라는 것이다.

그럼 이제 K값을 변화해서 살펴보자.

K = 0.3 일 때
K = 0.7 일 때

자 각각 K가 0.3, 0.7로 바꿔서 놓고 실행해보면 목표가가 0.3일 때 36561.5원, 0.7일 때 36723.5원 으로 바뀌는 것을 볼 수 있다.

K = 0.3 K = 0.5 K = 0.7
\36561.5 \36642.5 \36723.5

이 매수조건에 현재가가 5일 이동평균선과 10일 이동평균선 위에 있어야 한다는 조건이 추가되어 있는데

이 부분이 buy_etf() 함수에서 get_movingaverage() 함수를 불러 이동평균을 구하고 조건을 추가하는식이다.

...
        ma5_price = get_movingaverage(code, 5)   # 5일 이동평균가
        ma10_price = get_movingaverage(code, 10) # 10일 이동평균가
...
        if current_price > target_price and current_price > ma5_price and current_price > ma10_price:  
         


자 그렇다면 이제

1. 무엇을 살 것인가는 ETF 중에서 또는 관심가는 종목 중 골라서 종목코드를 매수대상 종목리스트 symbol_list에 넣어두면 되고

2. 얼마에 살 것인가는 위에 조건처럼 K값과 이동평균선 조건에 따라서 매수하고 장마감에 매도

이런 아주 기초적인 조건으로 매매는 시작할 수 있게 되겠다.

 

그러고보니 매수대상을 종목리스트에 넣어두는 것은 자동은 아니네 --a 

그리고 추가로 설명할 부분이 주문조건과 주문호가가 있는데 프로그램에서는 ETF를 기본으로 하고 있다보니

거래량이 다른 주식대비 많지 않아 비싼 가격을 지불하지 않도록 [최유리 FOK 매수 주문] 방법으로 매수하고

매도는 [최유리 IOC 매도] 방법으로 매도하도록 되어있다.


비록 아직은 매수대상 종목리스트는 수작업으로 하지만 프로그램의 실행은 윈도우가 알아서 실행하도록 해보자.

 

우선 PC가 켜지는 것은

 1) 켜두거나

 2) BIOS가 지원한다면 BIOS에서 해야하는데 각 회사마다 다르겠지만

   기본적으로 BIOS진입 - Advanced - ACPI Configuration - RTC Alarm Power On 에서 예약 부팅 을 해두면 된다.

 

그리고 PC가 켜지면 윈도우키 + S 를 누른뒤 "작업 스케줄러"로 검색/실행을 하면 아래와 같이 작업스케줄러가 실행이된다.

그리고 좌측 창의 [작업 스케줄러 라이브러리] 클릭하면 가운데 창이 아래 그림처럼 나오는데 그 창에서

우측 클릭 - 새 작업 만들기

그러면 나타나는 창에서

이름과 설명은 원하는 대로 작성하고 형광펜의 '가장 높은 수준의 권한으로 실행' 에 체크

그리고 트리거 탭으로 가서 '새로만들기' 누른뒤 나타나는 창에서 '매일' '오전 8:20' 그리고 아래에 '사용'에 체크

그럼 트리거가 하나 만들어진다.

그리고 동작탭으로 넘어가서 역시 '새로만들기'

설정의 프로그램/스크립트 에서는 찾아보기 버튼을 누른뒤 python.exe가 있는 폴더에 들어가서 선택해주고

인수추가에는 실행할 python 파일 이름 위 그림엔 AutoConnect.py 시작위치는 AutoConnect.py가 있는 폴더의 경로
그리고 확인을 누르면 된다.

 

그리고 같은 방법으로 8:30 정도에 AutoTrade.py도 등록해주면 윈도우즈의 작업스케줄러가 알아서 실행한다.

 


- 지금까지 대부분의 정보는 "파이썬 증권 데이터 분석" 이라는 책의 내용을 보고 정리한 내용이며 일부 현재 동작하지 않는 Slack의 requests 같은 부분은 내가 검색해서 수정하였음을 미리 밝힌다. 그래서 세세한 소스는 직접 책에서 제공하는 github로 링크하였다.

 

- 현재 나도 아직 이 방식으로 주식거래를 시작하지 않았다.  '주식투자자동화' 관련 포스팅은 여기서 끝나는게 아니고

일단 서둘러 해보고 싶은 분들을 위해 1부? 여튼 일단 시작할 수 있는 정도까지 서둘러 포스팅한다.

나는 여기서 종목을 골라서 리스트에 넣는 부분과 K값을 조금씩 조절하고 그에 따른 결과를 자동으로 기록하는 부분까지는 조금 더 프로그램하고 작은 금액이라도 실제 투자해볼 생각이다.

그 부분은 차근차근 여기다 포스팅하고 수익이 나던 손해가 나던 일정 기간 후 역시 결과도 포스팅 해 볼 예정이다.

 

일단은 여기까지!!

이전 포스팅을 보면 늘 실행하기전에 CREON Plus를 실행해야 한다는 말을 쓰곤 했는데

내가 항상 깜빡하고 CREON Plus도 실행하지 않고 실행해서 Error가 발생하곤 했기 때문이다.

추가로 CREON Plus 실행하고 비번 치고, 비번 또 치고 이러는것도 귀찮기도 하고 ㅎ

여튼 그래서 우선 할것은 CREON Plus 자동 실행 프로그램이다.

이 소스는 구매한 책인 [파이썬 증권 데이터 분석] 이 유용하게도 github에 소스도 제공해주는데 여기에 자동 실행 예제가 있다.

 

아래의 링크에 들어가보자.

StockAnalysisInPython/08_Volatility_Breakout at master · INVESTAR/StockAnalysisInPython · GitHub

 

GitHub - INVESTAR/StockAnalysisInPython

Contribute to INVESTAR/StockAnalysisInPython development by creating an account on GitHub.

github.com

위 링크의 8장 말고도 다른 장도 다양한 소스가 있으니 공부에 참고하면 좋을듯하다.

물론 책을 사서 보면서 해야겠지만 ㅎ 나도 책은 아직 보는 중

여기에 보면 ch08_01_AutoConnect.py 이라는 파이썬 파일이 있다.

-------------------------------------------------------------------------------------------------------
from pywinauto import application
import time
import os

os.system('taskkill /IM coStarter* /F /T')
os.system('taskkill /IM CpStart* /F /T')
os.system('taskkill /IM DibServer* /F /T')
os.system('wmic process where "name like \'%coStarter%\'" call terminate')
os.system('wmic process where "name like \'%CpStart%\'" call terminate')
os.system('wmic process where "name like \'%DibServer%\'" call terminate')
time.sleep(5)        

app = application.Application()
app.start('C:\CREON\STARTER\coStarter.exe /prj:cp /id:userid /pwd:pa$$word /pwdcert:certPa$$word /autostart')
time.sleep(60)

-------------------------------------------------------------------------------------------------------

14번째줄 (끝에서 두번째줄) userid 에 ID 넣고, pwd에 password 넣고 pwdcert에 인증서암호 넣고

그리고 Autoconnect.py 로 저장하고 실행하면 끝

소스의 설명은 딱히 할게 없다. 

11번째 줄까지는 혹시 CREON Plus가 실행하고 있으면 끝내고

5초 대기

CREON Plus 를 실행하고 60초 대기 

그리고 이 소스에서 
-------------------------------------------------------------------------------------------------------
from pywinauto import application
import time
import os

os.system('taskkill /IM coStarter* /F /T')
os.system('taskkill /IM CpStart* /F /T')
os.system('taskkill /IM DibServer* /F /T')
os.system('wmic process where "name like \'%coStarter%\'" call terminate')
os.system('wmic process where "name like \'%CpStart%\'" call terminate')
os.system('wmic process where "name like \'%DibServer%\'" call terminate')
time.sleep(5)    

-------------------------------------------------------------------------------------------------------

이 부분만 Disconnect.py로 저장하고 끝낼때 종종 쓰곤 한다. ㅋ

 

자 그럼 이제 주식투자 자동화 프로그램을 해보도록 하자.

 

책에서 설명하는 주식 자동프로그램의 기본전략은 변동성 돌파전략이다. 

그럼 변동성 돌파전략이란 무엇인가?

 

변동성 돌파전략(volatility breakout strategy)이란 선물 트레이더의 대가라고 하는 래리 윌리엄스라는 사람이 개발한 투자전략으로 가격이 전일 가격범위의 K% 이상이 될 때 매수하고 장마감시 매도해서 수익을 실현하는 전략이라고 

책에 설명되어 있다.

 

여기서 중요한건 그럼 무엇을 (어느종목을) 과 언제 (K% 이상이 될 때) 의 결정이 가장 중요하지 싶다.

이건 책을 다 봐야 알것 같은데 책을 아직 다 보지 않았... (다 보면 나오긴 할까?)

여튼 나같은 초보에게 가장 쉽게 이해되는 그림을 첨부해보고자 구글링 해봤지만

그래도 책에 나온 그림이 가장 쉽게 이해되는 그림이었다.

변동성 돌파 전략 (책 파이썬 증권 데이터 분석 371페이지 그림)

그렇다면 수많은 종목 중에서 저렇게 스윙하는 종목들을 매수하게 된다고 가정하고

내 자금을 그 종목 1개만 몰빵해서 사기는 위험하고 그런 종목들 중 몇종목을 살것인지도 결정하기도 해야하겠다.

그리고 하루에 한번은 매수/매도도 해야겠고

그럼 그 수수료도 만만치 않을것이다. 

그래서 수수료를 생각해보면 이전에 대신증권은 0.015% 또는 월정액 15000원 + 0.0088% 라고 했는데....

"세금" 이 있다!!

 

CREON 트레이딩 - 제휴은행 계좌개설 - 크레온 (creontrade.com)

 

크레온

대신증권 온라인 주식 거래 서비스. 비대면계좌개설, 수수료 혜택, 주식시세, 종목추천, 투자정보 제공

www.creontrade.com

 

위 링크를 보면 수수료가 나와있는데 "매매관련세금" 탭이 있는 것을 볼 수 있다. 여기를 보면

KOSPI는 거래세 0.08% + 농특세 0.15% = 0.23%

KOSDAQ은 거래세 0.23%

ETF는 0%


정리하면

  매수 매도 합계
KOSPI 0.015% 0.015% + 0.23% 0.26%
KOSDAQ 0.015% 0.015% + 0.23% 0.26%
ETF 0.015% 0.015% 0.03%

하루에 한번꼴로 매수/매도를 하는데 KOSPI 또는 KOSDAQ과 ETF는 약 10배 가량 차이가 난다!
즉 거래를 자주하는데는 ETF가 유리하다!!

물론 KOSPI나 KOSDAQ이 훨씬 수수료 이상의 수익을 내준다면 상관이 없겠다 ㅎ

일단은 ETF 위주로 진행해보는 걸로 계속 진행해보자

 

그 ETF 중에도 당연히 시가총액이 크고 거래량이 많은 종목 중에서 선택을 하는것이 좋겠다.
그래서 ETF 종목 정보를 구해보도록 하자.

 

1. pip install beautifulsoap4 설치

2. pip install pandas 설치

3. pip install selenium으로 selenium 설치

4. Chrome 브라우저 실행하고 주소창에 chrome://version 입력해서 버전 확인  (나의 경우 98.0.4758.102)

5. https://chromedriver.chromium.org/downloads

 에서 버전에 맞는 chromedriver를 다운받고 압축을 푼다음에 chromedriver.exe 파일을 c:\myPackage 폴더 만든후 이 폴더로 복사

6. c:\myPackage의 chromedriver.exe를 관리자권한으로 실행하도록 설정

7. 아래의 파이썬 증권데이터 분석 책에서 제공하는 github 에서 소스

 StockAnalysisInPython/ch08_02_DynamicPageScraping_NaverETF.py at master · INVESTAR/StockAnalysisInPython · GitHub

 

GitHub - INVESTAR/StockAnalysisInPython

Contribute to INVESTAR/StockAnalysisInPython development by creating an account on GitHub.

github.com

 

8. vscode 실행시켜서 새파일(파이썬파일) 만들기 후 복붙  (나의경우 ScrapingETF.py)

9. 저장 후 터미널에서 python ScrapingETF.py를 실행하였더니!

아.. 이건 또 무슨 에러냐... 구글 검색해본 결과 lxml 이 없는게 이유

그럼 터미널에서 pip install lxml 하면 설치하는게 보인다.

 

10. 그리고 다시 실행하면

위와같이 naver에서 518개의 ETF종목의 정보를 가져온것을 확인할수 있다. 순서는 시가총액 순이다.
pandas로 읽은 이정보를 잘 추려서 이 중에서 어떤 종목을 선택할 것인지 이것도 매우 중요한 요소일것이다.

 

오늘은 일단 여기까지!

 

이 주식자동화를 프로그램을 돌려놓고 수익이 알아서 막 나면 얼마나 좋을까?

하지만 수익은 커녕 손실만 계속 발생한다면 그것도 큰 문제다.

수익이 나던 손실이 나던 적어도 이 프로그램이 현재 뭘하고 있는지 내가 어디에 있던 나에게 알려는 줘야 한다. 

무슨 종목을 얼마에 체결하는지 같은 정보 등등

여튼 최악의 상황에서 원격데스크탑으로든 무슨 수를 써서든 주식용 컴퓨터에 접속을 해서 프로그램을 멈추든 하지 않겠는가.

 

 

이런 정보를 가장 많이들 사용하는 카카오톡으로 그때 그때 보내주면 좋겠는데 뭐 그건 아직 모르겠다.

카카오톡도 api가 있는걸로 아는데 그건 나중에 공부해보는걸로 하고

일단 이것 역시 시키는대로 ^^;;

 

Slack은 업무용 메신저로 엄청 많이 사용되는 것으로 알고 있다. 미국회사던가 여튼 이번에 코로나 사태에 '줌'과 함께 꽤나 좋은 실적을 낸 회사로 알고 있다.

잡설은 그만하고 슬랙에 접속해서 가입을 한다.

GOOLGE로 가입을 해도 되고, 로그인 버튼을 누르고 우측 상단 계정생성을 눌러 계정을 만들면 된다.

 

https://slack.com/

 

Slack은 미래의 업무가 이루어지는 곳입니다

Slack은 여러분의 팀과 소통할 새로운 방법입니다. 이메일보다 빠르고, 더 조직적이며, 훨씬 안전합니다.

slack.com

 

이메일을 입력하면 6자리 인증코드를 확인하는 과정이 나오고

입력했던 본인의 이메일 slack에서 보낸 6자리 인증코드를 입력해서 다음으로 넘어가면  workspace 생성 화면이 나온다.

여기서 workspace 이름을 적당히 지어둔다. 위에선 jslee-stock이라고 지어줬다. 다음을 눌러 다음단계로 넘어가면

채널이름을 설정하는데 위에선 stock이라고 지어줬다. 그리고 다음

여럿이서 같이하는 작업이 아니고 혼자 쓸것이므로 팀원추가가 필요없다 형광펜으로 칠한 이단계 건너뛰기를 하면 기본설정이 끝이난다.

PC에서는 위와같은 초기화면이 뜨고

핸드폰 IOS, Android 모두 Slack 앱이 있는데 다운로드하여 접속하면 아래와 같은 화면이 뜬다.

위의 가입-설정의 워크스페이스와 이름이 jslee-stock과 jslee 로 다른데 가입-설정 과정을 사진으로 남기려고

새로 가입하여 만든 워크스페이스인 jslee-stock과 기존에 쓰던 워크스페이스인 jslee와 이름이 다르게 되었다.

 

다음은 이렇게 만든 slack 워크스페이스에 알람을 보내준 슬랙알람봇을 만들어준기 위해 아래의 링크에 접속한다.

api.slack.com

 

Create an App을 눌러 app을 만든다.

App Name 을 적당히 적고 (위의 예에선 jslee-stock-bot)

App Name 아래의 workspace에서 본인의 워크스페이스를 선택한다.(jslee-stock)

 

그러면 위와같이 App이 만들어지고 기본 정보들이 표시된다.

이제 이 알리봇에 권한을 줘야하는데 

왼쪽 메뉴에서 형광펜으로 칠한  OAuth & Permissions 을 클릭 하고 우측 메인화면을 스크롤 다운하면

Scope / Bot Token Scopes 가 나오는데 여기서 Add an OAuth Scope를 누르면 다음과 같이 여러 권한이 나온다.

이 여러권한 중 우리는 알림봇이 우리의 채팅창에 메시지를 보내는 것이므로

형광펜으로 칠한 chat:write 권한을 선택한다.

그리고 스크롤 업해서 위로 올라가다 보면 위 그림처럼 OAuth Tokens for Your Workspace의 Install to Workspace가 나오는다. 클릭해주자.

그러면 위 그림과 같이 허용할꺼냐고 묻는데 허용!

자 여기서 중요한게 뭐냐면 앞으로 우리가 작성하는 파이썬 프로그램은 이 알림봇에게 메시지를 보낼때

이 알림봇을 구분하는 구분자 같은게 있는데 그게 아래 그림의 Bot User OAuth Token 이다.

이 부분이 나중에 소스에 사용됨을 기억하자.

즉 파이썬을 작성한 코드에서 메시지는 이 Token을 가진 알림봇에 메시지를 전달하고

이 알림봇은 내 채팅창에 메시지를 전달하는 것이다.

 

이를 위해 한가지 더 작업해둘 사항이 있다.

다시 slack 채팅창으로 가서

형광펜 부분을 누른다. 그러면 아래와 같은 창이 뜨는데

통합 탭에서 앱/앱추가 를 눌러 만들어둔 앱을 추가해준다.

 

그러면 아래와 같이 jslee-stock-bot님과 함께 참여했다고 뜬다.

 

자 그럼 이제 이 slack 채팅으로 메시지를 보내보도록 하자.

지난번에 작업했던 대로 vs code 를 실행하여 임의로 파이썬 파일을 만들고 테스트를 해보도록 하자.

일단 msgtest.py 로 작업한다.

 

그 전에 해야할게 있는데 slack으로 메시지를 보내기 위해 파이썬은 requests 라는 것을 사용한다.

그래서 request를 깔아야하는데 역시 pip install requests 로 설치하도록 한다.

역시 이미 설치되어 있어서 위와 같은 메시지가 나온다만 설치되어 있지 않을 경우 설치되는 과정이 표시될 것이다.

 

설치가 되었다면 아래의 소스와 같이 타이핑 해보자

 

import requests
import win32com.client

def post_message(token, channel, text):
    response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer "+token},
        data={"channel": channel,"text": text}
    )
    print(response)
 
myToken = "여기는 본인이 사용하는 고유의 Bot User OAuth Token 값을 넣어야 함!!"

# 연결 여부 체크
objCpCybos = win32com.client.Dispatch("CpUtil.CpCybos")
bConnect = objCpCybos.IsConnect
if (bConnect == 0):
    print("PLUS가 정상적으로 연결되지 않음. ")
    exit()
 
# 현재가 객체 구하기
objStockMst = win32com.client.Dispatch("DsCbo1.StockMst")
objStockMst.SetInputValue(0, 'A005930')   #종목 코드 - 삼성전자
objStockMst.BlockRequest()
 
# 현재가 통신 및 통신 에러 처리
rqStatus = objStockMst.GetDibStatus()
rqRet = objStockMst.GetDibMsg1()
print("통신상태", rqStatus, rqRet)
if rqStatus != 0:
    exit()
 
# 현재가 정보 조회

offer = objStockMst.GetHeaderValue(16)  #매도호가
post_message(myToken,"#stock","삼성전자 현재가:" + str(offer))

(위 소스는 CREON Plus 자료실의 주식현재가 조회 소스에 Slack 메시지 전송을 덧붙인 예제임)

 

위 코드를 작성할때  11행의 반드시 myToken 값은 위의 api.slack.com 에서 표시되는 본인의 Bot User OAuth Token 값을 넣어줘야만 한다.

 

그리고 이 소스를 실행하기전에 CREON Plus 를 반드시 실행하고 위 소스를 실행한다.

실행은 터미널에서 python msgtest.py

그러면 터미널의 결과에서도 아래와 같이 조회가 완료되었고 Response [200] -이게 잘 되었다는 것- 으로 표시되고

다음과 같이 브라우저의 slack 채팅창에서도 메시지가 표시되고

핸드폰에 (slack에 미리 로그인한) 알림이 뜨는 것을 확인할 수 있다.

앞으로 이 파이썬의 requests를 이용해서 여러 정보를 slack의 메시지로 보내고 내가 어디에 있던 핸드폰 또는 브라우저 등으로 확인 할 수 있겠다.

 

오늘은 여기까지!

+ Recent posts