본문 바로가기
사이드 프로젝트/주식자동 매매 프로그램

Python 주식 자동매매4 - 주식거래 자동화 프로그램 만들기

by 카프리썬_ 2021. 2. 5.
728x90

프로젝트 히스토리 보러가기▼

2021.02.05 - Python 주식 자동매매1 | 주식거래 자동화 개요 및 구조

2021.02.05 - Python 주식 자동매매2 - 개발환경설치 및 대신증권 API 구조

2021.02.05 - Python 주식 자동매매3 - 특정종목 Slack 노티 테스트

2021.02.05 - Python 주식 자동매매4 - 주식거래 자동화 프로그램 만들기

 

 

 

아래의 내용은 유튜브 '조코딩'님의 '파이썬으로 주식거래 자동화만들기'를 참고한 내용입니다.

또한 코드는 파이썬증권 데이터분석 github를 참고하였습니다.

 

앞서서 삼성전자 현재가를 슬랙알람으로 받는걸 테스트해봤는데, 이제 진짜로 주식거래 자동화 프로그램을 만들어보자.

 

 

1. 요구사항

1) 거래가 빈번하게 이루어져서 수수료가 적어야한다. 

그래서 주식(수수료: 약 0.269%)보다 ETF(수수료: 약 0.019%)가 비용적인 측면에서 유리하다.

다만, 변동성은 ETF보다 주식이 더 클 수 있기 때문에 하이리스크,하이리턴을 선호하면 주식종목으로 변경가능

2) ETF를 중심으로 자동거래를 한다면, 유동성공급자가 활동하는 시간을 고려햔다.

그래서 유동성공급자가 활동하는 시간인 9:05~15:20이므로 해당 프로그램은 적어도 15:15분에 모든 물량을 매도한다.

 

2. 제약사항

1) 필요한 라이브러리 설치 

pip install pandas

pip install slacker

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

2) 슬랙 봇 토큰 및 채널명 입력 

slack = Slacker('xoxb-본인의 봇 토큰입력')

def dbgout(message):
    """인자로 받은 문자열을 파이썬 셸과 슬랙으로 동시에 출력한다."""
    print(datetime.now().strftime('[%m/%d %H:%M:%S]'), message)
    strbuf = datetime.now().strftime('[%m/%d %H:%M:%S] ') + message
    slack.chat.post_message('#채널명', strbuf)

 

3) 크레온plus 로그인

한계점 - 대신 해당 파이썬코드를 실행하기 위해서는 사전에 크레온plus가 로그인되어 있어야함

대안책 - 매번 로그인하기 귀찮으니까 autoConnect.py 프로그램 돌려놓기

github내용참고 : github.com/INVESTAR/StockAnalysisInPython/blob/master/08_Volatility_Breakout/ch08_01_AutoConnect.py

 

INVESTAR/StockAnalysisInPython

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

github.com

4) 작업스케쥴러 등록

크레온plus로그인 및 자동매매 프로그램을 자동으로 실행하기 위해서 윈도우 작업스케쥴러 등록

한계점 - 대신 항상 windows컴퓨터가 항상 켜져있어야함 

대안책 - aws의 ligthsail을 이용해서 윈도우pc 인스턴스를 run, 대신 사용한만큼 비용부과 

 

 

3. 코드리뷰

1) 메인함수로직

symbol_list =[] # 자동매매를 원하는 종목코드

bought_list = [] # 매수가 완료된 종목들을 담아두는 목적

target_buy_count = 4 # 매수할 종목 수

buy_percent = 0.25   # 몇퍼센트씩 살건지

 

stocks = get_stock_balance('ALL')      # 보유한 모든 종목 조회

total_cash = int(get_current_cash())   # 100% 증거금 주문 가능 금액 조회

buy_amount = total_cash * buy_percent  # 종목별 주문 금액 계산

 

while True : 반복적으로 단기매매 진행

IF 토요일이나 일요일이면 -> 자동종료

IF 장이 시작할때(9시~9시5분) 아직도 남아있는 종목이 있으면 -> 모두판다

IF 시작시간~매도시간(9시5분~15시15분)이면 -> 자동매매 진행(buy_etf), 30분마다 현재 잔고확인(get_stock_balance)

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)
    # 30분마다 현재 잔고확인 
    if t_now.minute == 30 and 0 <= t_now.second <= 5:
    	get_stock_balance('ALL')

IF 장이 끝나갈때(15시15분~15시20분) -> 모두판다 

IF 장이 끝났을때 (15시 20분 이후) -> 프로그램 종료

 

2) 크레온API모듈

각 모듈별 API 사용방식 : money2.daishin.com/e5/mboard/ptype_basic/HTS_Plus_Helper/DW_Basic_List_Page.aspx?boardseq=284

# 크레온 플러스 공통 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')  #매수,매도 

 

3) 함수

def buy_etf(code):

3가지 전략에 따라서 자동매매를 수행한다.

이때 매개변수code는 매수할종목인 symbol_list에서 하나씩 꺼내온 값

 

[0] 예외처리 : 이미 매수완료한 종목이면 더이상 매수하지 않음 -> return false

 

[1] 매수할 종목의 현재가(current_price)알아오기 : get_current_price(code) 

 

 

[2] 매수전략확인

2.매수목표가(target_price) 알아오기 : get_target_price(code) -> 전략1.변동성돌파전략

3.5일 이동평균가(ma5_price) 알아오기 : get_movingaverage(code, 5) ->전략2. 이동평균선 5일 

3.10일 이동평균가(ma5_price) 알아오기 : get_movingaverage(code, 10) -> 전략3. 이동평균선 10일

 

[3] 매수주문 설정 

매수할종목의 현재가가 현재가격이 전략1,전략2, 전략3을 모두 만족할때 자동매매진행

if current_price > target_price and current_price > ma5_price and current_price > ma10_price: 

cpTradeUtil.TradeInit() API의 거래모듈 시작

매수,주식상품, 종목코드, 매수할수량, FOK, 최유리 방식

 

  acc = cpTradeUtil.AccountNumber[0]      # 계좌번호
  accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체,1:주식,2:선물/옵션
 
  cpOrder.SetInputValue(0, "2")        # 2: 매수
  cpOrder.SetInputValue(1, acc)        # 계좌번호
  cpOrder.SetInputValue(2, accFlag[0]) # 상품구분 - 주식 상품 중 첫번째
  cpOrder.SetInputValue(3, code)       # 종목코드
  cpOrder.SetInputValue(4, buy_qty)    # 매수할 수량
  cpOrder.SetInputValue(7, "2")        # 주문조건 0:기본, 1:IOC, 2:FOK
  cpOrder.SetInputValue(8, "12")       # 주문호가 1:보통, 3:시장가, 5:조건부, 12:최유리, 13:최우선 

주문조건 : IOC / FOK

IOC 방식 : 5000주 산다고 했는데, 10주만 파는사람만 있으면 10주만 사고 남은수량 취소하는 방식

FOK 방식 : 5000주 산다고 했는데, 10주만 파는사람만 있으면 그냥 아예 안삼.

 

출처 : 조코딩

주문호가 : 최유리/최우선

최유리 : 가장 유리하게 매매할 수 있는 가격, 팔려고 내논 사람 중에서 가장 싼걸 사는 방식 (11660원)

최우선 : 우선 대기하는 가장 높은 가격, (매수한다면 11625원)

즉, 최유리는 비싸거나 쌀수도 있지만 당장 매매가 가능하고, 최우선은 매매가 될수도 있고 안될수도 있음

 

[4]매수주문 요청

위의 매수주문 설정사항으로 매수주문을 진행

ret = cpOrder.BlockRequest() 

매수종목 및 매수수량 반환 : stock_name, bought_qty = get_stock_balance(code)

 

[5]매수결과 Slack 전달

매수수량이 1개라도 있으면, 이미 산 종목으로 표시하고 슬랙메세지 전달

 if bought_qty > 0:
 bought_list.append(code) #이미산 종목 표시
 dbgout("`buy_etf("+ str(stock_name) + ' : ' + str(code) + ") -> " + str(bought_qty) + "EA bought!" + "`")

 

def get_current_price(code): 

cpTradeUtil.TradeInit() API의 거래모듈 시작

증거금 100% 주문가능금액 리턴

 cpTradeUtil.TradeInit()
 acc = cpTradeUtil.AccountNumber[0]    # 계좌번호
 accFlag = cpTradeUtil.GoodsList(acc, 1) # -1:전체, 1:주식, 2:선물/옵션
 cpCash.SetInputValue(0, acc)              # 계좌번호
 cpCash.SetInputValue(1, accFlag[0])      # 상품구분 - 주식 상품 중 첫번째
 cpCash.BlockRequest() 
 return cpCash.GetHeaderValue(9) # 증거금 100% 주문 가능 금액

 

def get_target_price(code): 

변동성돌파전략에 따라서 매수목표가를 리턴

지난날 고가와 지난날 저가의 차이를 계산하고 여기에 k(0.5)만큼 곱한값이 

오늘시가에서 이정도 변동폭만큼 오르면! 그때 값이 목표값이 된다.

target_price = today_open + (lastday_high - lastday_low) * 0.5 
        return target_price

def get_movingaverage(codewindow):

이동평균선 : 주가의 이동평균을 구해서 평균값을 이은선. 일자별로 5일평균, 10일평균을 묶어서 낸 그래프

보통 이동평균선보다 낮을때는 하락세를 보이고, 이동평균선보다 현재주가가 높을때는 상승세를 보인다.

 

def check_creon_system(): 

def get_ohlc(codeqty):

def get_stock_balance(code): 

def get_current_cash():

def sell_all():

 

반응형