촬리의늘솔길

주식 자동매매 종합반 2주차 일지 본문

✍~2022/주식자동매매

주식 자동매매 종합반 2주차 일지

리촬리 2021. 8. 12. 18:27

01. 왜 일반 투자자들은 항상 돈을 잃는 것일까?

1) 리처드 세일러, 경제심리학!(부제: 우리는 돈을 잃을 수 밖에 없도록 만들어졌다.)

  1. 투자자의 심리를 연구하여 2017년 노벨 경제학상 수상
  2. 경제이론이 아닌 인간의 심리를 투자와 연관시킨 첫 사례
  3. 유명한 경제이론들은 가장 중요한 투자자의 심리라는 변수를 배제 한 것이라고 지적
  4. 시장이 상승 및 하락 할 때 모두 결국 돈을 잃을 수 밖에 없는 사람의 심리를 발표
    • 시장이 상승 할 때 - 돈이 계속 벌리는 느낌에 계속 돈을 넣다가 결국 파산
    • 시장이 하락 할 때 - 주가가 싸게 느껴져서 계속 돈을 넣다가 결국 파산

 

 

2) 멘탈이 너무 약해서 문제

 

  1. 내가 가지고 있는 주식을 믿을 수 없다!
    • "대강 이렇게 하면 되지 않을까?" 매매법으로 인해 주가 하락 시 버틸 수 없음
    • "너한테만 알려주는건데..." 매매법은 설거지 가능성이 매우 높음 
    • 이미 수익권이지만 더 오를 것 같아서 가지고 있다가 결국 마이너스로..
    • 이미 충분히 올랐다가 생각해서 팔았는데 더 많이 올라가서 씁쓸한 마음..
    • 항상 쫓기듯한 마음을 가지고 있기 때문에 평정심을 유지 할 수 없음이익을 봐도 주식을 팔아도 기분이 좋지 않음

3) 모호한 투자원칙으로 인한 손실누적

  • 확실한 투자원칙을 만들고 지켜야 하지만 기분에 따라 매매기법이 달라짐
  • 투자원칙을 어떻게 만들어야 하는지 알기 힘든 것도 문제

4) 정보력의 한계

 

  • 막대한 자금을 운용하는 기관과 외국인에 비해 우리는 정보력의 한계 존재
  • 우리의 귀에 들어온 정보는 가장 마지막에 전달되는 정보
  •  

 

5) 시장을 과연 이길 수 있을까?

  1. 시간은 우리의 편!
    • 분기마다 성과를 내야하는 기관 및 외국인과 달리 우리는 시간에 구애받지 않음
    • 자산이 줄어들면 기다리고 자산이 불어나도 기다리고...
  2. 백테스팅을 통한 투자원칙 확립!
    • 감에 의존하지 않고 실제 과거 데이터를 기반으로 투자기법을 확립
    • 과거 수익률을 통해 미래 수익률을 보장할 순 없음
    • 과거 수익률이 좋았던 것이 안좋았던 것보다 수익률이 높을 가능성 높음
  3. 백테스팅을 통한 멘탈강화!
    • 시장은 항상 꾸준히 움직임! 일희일비 하지 말자!
    • 과거 10년 이상의 데이터를 통해 상승, 횡보, 하락 시 나의 투자기법 수익률을 확인

백테스팅을 통해 나의 투자원칙을 확립해보자! 어짜피 나는 지금당장은 벌이가 없고,

나중에 돈이 생겼을때 어떻게 투자하면되는지 익힌상태가 되어있을테니까!

 

02. 이제 드디어 백테스팅을 해 볼 차례입니다!

 

6) 백테스팅(Back Testing)이란?!

    1. 백테스팅은 Back(뒤) Testing(실험하기), 즉 지나온 과거를 대상으로 실험해보는 것
      • 실제 돈으로 매매를 하는 것이 아닌 과거 데이터를 통해 가상매매를 하는 것을 말함
    2. 백테스팅의 반댓말은 포워드 테스팅(Forward Testing)
      • 백테스팅을 끝내고 실제 돈을 입금하여 테스트 하는 것을 말함

 

7)백테스팅은 어디서 어떻게 하는 걸까?

  1. 인텔리퀀트(링크) 사이트를 통해 백테스팅 가능
    • 국내주식을 대상으로 백테스팅을 할 수 있는 플랫폼 제공
    • Block(난이도: 쉬움) 또는 JavaScript 코딩(난이도: 어려움)을 통해 백테스팅 가능

 

03. IntelliQuant 가입을 해봅시다!

04. IntelliQuant 사용 첫걸음!

블록 알고리즘 생성

 

    1. '스튜디오' 메뉴 클릭!
    2. '새 블록 알고리즘' 메뉴 클릭!
    3. 블록 알고리즘 생성 완료! 하나하나 알아봅시다

11) 첫 백테스팅 도전

 

블록의 내용을 아래와 같이 초기화 시키기!

 

유니버스 : 주식종목들에 대한 집합군,  어떤 종목들을 가지고 거래를 할지 정해줌

우선주는 배당금을 받기위해 회사에서 만든거라 수익률이 좋지않으므로, 제외

시가총액은 모든종목을 하겠다는 것.

시가총액 : 상위 100%이상이라는 말은 꼴등이상 전부

유동성: 가장중요 , 유동성이 커야 슬리피지 적음

 

PER와 PBR 체크!

2010-01-01 부터 현재까지로 기간 설정 후 백테스트 시작!

 

결과 확인!

 

12) 고급자를 위한 백테스팅

'스크립트로 내보내기' 클릭!

 

알고리즘 목록에서 새롭게 생성 된 스크립트 확인!

 

 

내부로 들어가면 JavaScript로 구성 된 코드 확인 가능!

 

05. 키움 API 사용 전 준비

키움 API 사용 등록

    1. 키움 OpenAPI 페이지 접속(링크)!
    2. '사용 신청하러 가기' 버튼 클릭!
  •  서비스 사용 등록!
  •  등록 완료!
  • 14) 키움 API 설치
    1. 키움 OpenAPI 페이지 접속(링크)!
    2. 키움 OpenAPI + 모듈 다운로드 및 설치

06. 주식 자동매매 프로그램 코딩 전 준비!

python ,pycharm 설치하기.

64bit깔려있어도 32bit깔아야한대.

07. 키움 API 사용 첫걸음!

18) 첫 키움 API 사용 실습

 

# -*-coding: utf-8 -*-
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QThread
from threading import Lock, Thread
from collections import deque
from sys import argv
from time import sleep
from json import dumps
from os import kill, getpid


class SyncRequestDecorator:
    """키움 API 비동기 함수 데코레이터
    """

    @staticmethod
    def kiwoom_sync_request(func):
        def func_wrapper(self, *args, **kwargs):
            self.request_thread_worker.request_queue.append((func, args, kwargs))

        return func_wrapper

    @staticmethod
    def kiwoom_sync_callback(func):
        def func_wrapper(self, *args, **kwargs):
            # print("[%s] 키움 함수 콜백: %s %s" % (func.__name__, args, kwargs))

            func(self, *args, **kwargs)  # 콜백 함수 호출
            if self.request_thread_worker.request_thread_lock.locked():
                self.request_thread_worker.request_thread_lock.release()  # 요청 쓰레드 잠금 해제

            # 재시도 횟수 초기화
            myWindow._kiwoom.request_thread_worker.retry_count = 0

        return func_wrapper


class RequestThreadWorker(QObject):
    def __init__(self):
        """요청 쓰레드
        """
        super().__init__()
        self.request_queue = deque()
        self.request_thread_lock = Lock()

        # 간혹 요청에 대한 결과가 콜백으로 오지 않음
        # 마지막 요청을 저장해 뒀다가 일정 시간이 지나도 결과가 안오면 재요청
        self.retry_timer = None
        self.max_retry = 3
        self.retry_count = 0

    def retry(self, request):
        self.retry_count = self.retry_count + 1

        if self.retry_count == self.max_retry:
            kill(getpid(), 9)

        # print("[%s] 키움 함수 재시도: %s %s" % (request[0].__name__, request[1], request[2]))

        self.request_queue.appendleft(request)

    def run(self):
        last_request = None
        while True:
            # 큐에 요청이 있으면 하나 뺌
            # 없으면 블락상태로 있음

            try:
                request = self.request_queue.popleft()
            except IndexError as e:
                if self.request_thread_lock.locked():
                    if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                        self.request_thread_lock.release()
                        self.retry(last_request)
                    else:
                        self.request_thread_lock.release()

                sleep(0.2)
                continue

            # 요청에대한 결과 대기
            if not self.request_thread_lock.acquire(blocking=True, timeout=5):
                if self.request_thread_lock.locked():
                    self.request_thread_lock.release()
                # 요청 실패
                sleep(0.2)

                self.request_queue.appendleft(request)
                self.retry(last_request)  # 실패한 요청 재시도
            else:
                last_request = request
                # 요청 실행
                # print("[%s] 키움 함수 실행: %s %s" % (request[0].__name__, request[1], request[2]))
                request[0](trader, *request[1], **request[2])

            sleep(1)  # 0.2초 이상 대기 후 마무리


class Kiwoom(QObject):
    # Variables

    def __init__(self):
        super().__init__()
        self.ocx = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        self.ocx.OnEventConnect[int].connect(self.OnEventConnect)
        self.ocx.OnReceiveMsg[str, str, str, str].connect(self.OnReceiveMsg)
        self.ocx.OnReceiveTrData[str, str, str, str, str, int, str, str, str].connect(self.OnReceiveTrData)
        self.ocx.OnReceiveRealData[str, str, str].connect(self.OnReceiveRealData)
        self.ocx.OnReceiveChejanData[str, int, str].connect(self.OnReceiveChejanData)
        self.ocx.OnReceiveConditionVer[int, str].connect(self.OnReceiveConditionVer)
        self.ocx.OnReceiveTrCondition[str, str, str, int, int].connect(self.OnReceiveTrCondition)
        self.ocx.OnReceiveRealCondition[str, str, str, str].connect(self.OnReceiveRealCondition)

        # 요청 쓰레드
        self.request_thread_worker = RequestThreadWorker()
        self.request_thread = QThread()
        self.request_thread_worker.moveToThread(self.request_thread)
        self.request_thread.started.connect(self.request_thread_worker.run)
        self.request_thread.start()

    # 로그인
    # 0 - 성공, 음수값은 실패
    @pyqtSlot(result=int)
    def CommConnect(self):
        return self.ocx.dynamicCall("CommConnect()")

    # 로그인 상태 확인
    # 0:미연결, 1:연결완료, 그외는 에러
    @pyqtSlot(result=int)
    def GetConnectState(self):
        res = self.ocx.dynamicCall("GetConnectState()")
        if res == 1:
            print('Online')
        else:
            print('Offline')
        return res

    # 로그 아웃
    @pyqtSlot()
    def CommTerminate(self):
        self.ocx.dynamicCall("CommTerminate()")

    # 로그인한 사용자 정보를 반환한다.
    # “ACCOUNT_CNT” – 전체 계좌 개수를 반환한다.
    # "ACCNO" – 전체 계좌를 반환한다. 계좌별 구분은 ‘;’이다.
    # “USER_ID” - 사용자 ID를 반환한다.
    # “USER_NAME” – 사용자명을 반환한다.
    # “KEY_BSECGB” – 키보드보안 해지여부. 0:정상, 1:해지
    # “FIREW_SECGB” – 방화벽 설정 여부. 0:미설정, 1:설정, 2:해지
    @pyqtSlot(str, result=str)
    def GetLoginInfo(self, tag):
        return self.ocx.dynamicCall("GetLoginInfo(QString)", [tag])

    # Tran 입력 값을 서버통신 전에 입력값일 저장한다.
    @pyqtSlot(str, str)
    def SetInputValue(self, id, value):
        self.ocx.dynamicCall("SetInputValue(QString, QString)", [id, value])

    # 통신 데이터를 송신한다.
    # 0이면 정상
    # OP_ERR_SISE_OVERFLOW – 과도한 시세조회로 인한 통신불가
    # OP_ERR_RQ_STRUCT_FAIL – 입력 구조체 생성 실패
    # OP_ERR_RQ_STRING_FAIL – 요청전문 작성 실패
    # OP_ERR_NONE – 정상처리
    @pyqtSlot(str, str, int, str, result=int)
    def CommRqData(self, rQName, trCode, prevNext, screenNo):
        return self.ocx.dynamicCall("CommRqData(QString, QString, int, QString)", [rQName, trCode, prevNext, screenNo])

    # 수신 받은 데이터의 반복 개수를 반환한다.
    @pyqtSlot(str, str, result=int)
    def GetRepeatCnt(self, trCode, recordName):
        return self.ocx.dynamicCall("GetRepeatCnt(QString, QString)", [trCode, recordName])

    # Tran 데이터, 실시간 데이터, 체결잔고 데이터를 반환한다.
    # 1. Tran 데이터o
    # 2. 실시간 데이터
    # 3. 체결 데이터
    # 1. Tran 데이터
    # sJongmokCode : Tran명
    # sRealType : 사용안함
    # sFieldName : 레코드명
    # nIndex : 반복인덱스
    # sInnerFieldName: 아이템명
    # 2. 실시간 데이터
    # sJongmokCode : Key Code
    # sRealType : Real Type
    # sFieldName : Item Index (FID)
    # nIndex : 사용안함
    # sInnerFieldName:사용안함
    # 3. 체결 데이터
    # sJongmokCode : 체결구분
    # sRealType : “-1”
    # sFieldName : 사용안함
    # nIndex : ItemIndex
    # sInnerFieldName:사용안함
    @pyqtSlot(str, str, str, int, str, result=str)
    def CommGetData(self, jongmokCode, realType, fieldName, index, innerFieldName):
        return self.ocx.dynamicCall("CommGetData(QString, QString, QString, int, QString)",
                                    [jongmokCode, realType, fieldName, index, innerFieldName]).strip()

    # strRealType – 실시간 구분
    # nFid – 실시간 아이템
    # Ex) 현재가출력 - openApi.GetCommRealData(“주식시세”, 10);
    # 참고)실시간 현재가는 주식시세, 주식체결 등 다른 실시간타입(RealType)으로도 수신가능
    @pyqtSlot(str, int, result=str)
    def GetCommRealData(self, realType, fid):
        return self.ocx.dynamicCall("GetCommRealData(QString, int)", [realType, fid]).strip()

    # 주식 주문을 서버로 전송한다.
    # sRQName - 사용자 구분 요청 명
    # sScreenNo - 화면번호[4]
    # sAccNo - 계좌번호[10]
    # nOrderType - 주문유형 (1:신규매수, 2:신규매도, 3:매수취소, 4:매도취소, 5:매수정정, 6:매도정정)
    # sCode, - 주식종목코드
    # nQty – 주문수량
    # nPrice – 주문단가
    # sHogaGb - 거래구분
    # sHogaGb – 00:지정가, 03:시장가, 05:조건부지정가, 06:최유리지정가, 07:최우선지정가, 10:지정가IOC, 13:시장가IOC, 16:최유리IOC, 20:지정가FOK, 23:시장가FOK, 26:최유리FOK, 61:장전시간외종가, 62:시간외단일가, 81:장후시간외종가
    # ※ 시장가, 최유리지정가, 최우선지정가, 시장가IOC, 최유리IOC, 시장가FOK, 최유리FOK, 장전시간외, 장후시간외 주문시 주문가격을 입력하지 않습니다.
    # ex)
    # 지정가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 48500, "00", "");
    # 시장가 매수 - openApi.SendOrder("RQ_1", "0101", "5015123410", 1, "000660", 10, 0, "03", "");
    # 매수 정정 - openApi.SendOrder("RQ_1","0101", "5015123410", 5, "000660", 10, 49500, "00", "1");
    # 매수 취소 - openApi.SendOrder("RQ_1", "0101", "5015123410", 3, "000660", 10, "00", "2");
    # sOrgOrderNo – 원주문번호
    @pyqtSlot(str, str, str, int, str, int, int, str, str, result=int)
    def SendOrder(self, rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo):
        print("sendOrder", rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo)
        return self.ocx.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                                    [rQName, screenNo, accNo, orderType, code, qty, price, hogaGb, orgOrderNo])

    # 체결잔고 데이터를 반환한다.
    @pyqtSlot(int, result=str)
    def GetChejanData(self, fid):
        return self.ocx.dynamicCall("GetChejanData(int)", [fid])

    # 서버에 저장된 사용자 조건식을 가져온다.
    @pyqtSlot(result=int)
    def GetConditionLoad(self):
        res = self.ocx.dynamicCall("GetConditionLoad()")
        if res == 1:
            print('GetConditionLoad() success')
        else:
            print('GetConditionLoad() failed')

    # 조건검색 조건명 리스트를 받아온다.
    # 조건명 리스트(인덱스^조건명)
    # 조건명 리스트를 구분(“;”)하여 받아온다
    @pyqtSlot(result=str)
    def GetConditionNameList(self):
        return self.ocx.dynamicCall("GetConditionNameList()")

    # 조건검색 종목조회TR송신한다.
    # LPCTSTR strScrNo : 화면번호
    # LPCTSTR strConditionName : 조건명
    # int nIndex : 조건명인덱스
    # int nSearch : 조회구분(0:일반조회, 1:실시간조회, 2:연속조회)
    # 1:실시간조회의 화면 개수의 최대는 10개
    @pyqtSlot(str, str, int, int)
    @SyncRequestDecorator.kiwoom_sync_request
    def SendCondition(self, scrNo, conditionName, index, search):
        self.ocx.dynamicCall("SendCondition(QString,QString, int, int)", [scrNo, conditionName, index, search])

    # 실시간 조건검색을 중지합니다.
    # ※ 화면당 실시간 조건검색은 최대 10개로 제한되어 있어서 더 이상 실시간 조건검색을 원하지 않는 조건은 중지해야만 카운트 되지 않습니다.
    @pyqtSlot(str, str, int)
    def SendConditionStop(self, scrNo, conditionName, index):
        self.ocx.dynamicCall("SendConditionStop(QString, QString, int)", [scrNo, conditionName, index])

    # 복수종목조회 Tran을 서버로 송신한다.
    # OP_ERR_RQ_STRING – 요청 전문 작성 실패
    # OP_ERR_NONE - 정상처리
    #
    # sArrCode – 종목간 구분은 ‘;’이다.
    # nTypeFlag – 0:주식관심종목정보, 3:선물옵션관심종목정보
    @pyqtSlot(str, bool, int, int, str, str)
    @SyncRequestDecorator.kiwoom_sync_request
    def CommKwRqData(self, arrCode, next, codeCount, typeFlag, rQName, screenNo):
        self.ocx.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)",
                             [arrCode, next, codeCount, typeFlag, rQName, screenNo])

    # 실시간 등록을 한다.
    # strScreenNo : 화면번호
    # strCodeList : 종목코드리스트(ex: 039490;005930;…)
    # strFidList : FID번호(ex:9001;10;13;…)
    #   9001 – 종목코드
    #   10 - 현재가
    #   13 - 누적거래량
    # strOptType : 타입(“0”, “1”)
    # 타입 “0”은 항상 마지막에 등록한 종목들만 실시간등록이 됩니다.
    # 타입 “1”은 이전에 실시간 등록한 종목들과 함께 실시간을 받고 싶은 종목을 추가로 등록할 때 사용합니다.
    # ※ 종목, FID는 각각 한번에 실시간 등록 할 수 있는 개수는 100개 입니다.
    @pyqtSlot(str, str, str, int, result=int)
    def SetRealReg(self, screenNo, codeList, fidList, optType):
        return self.ocx.dynamicCall("SetRealReg(QString, QString, QString, QString)",
                                    [screenNo, codeList, fidList, optType])

    # 종목별 실시간 해제
    # strScrNo : 화면번호
    # strDelCode : 실시간 해제할 종목코드
    # -화면별 실시간해제
    # 여러 화면번호로 걸린 실시간을 해제하려면 파라메터의 화면번호와 종목코드에 “ALL”로 입력하여 호출하시면 됩니다.
    # SetRealRemove(“ALL”, “ALL”);
    # 개별화면별로 실시간 해제 하시려면 파라메터에서 화면번호는 실시간해제할
    # 화면번호와 종목코드에는 “ALL”로 해주시면 됩니다.
    # SetRealRemove(“0001”, “ALL”);
    # -화면의 종목별 실시간해제
    # 화면의 종목별로 실시간 해제하려면 파라메터에 해당화면번호와 해제할
    # 종목코드를 입력하시면 됩니다.
    # SetRealRemove(“0001”, “039490”);
    @pyqtSlot(str, str)
    def SetRealRemove(self, scrNo, delCode):
        self.ocx.dynamicCall("SetRealRemove(QString, QString)", [scrNo, delCode])

    # 차트 조회한 데이터 전부를 배열로 받아온다.
    # LPCTSTR strTrCode : 조회한TR코드
    # LPCTSTR strRecordName: 조회한 TR명
    # ※항목의 위치는 KOA Studio의 TR목록 순서로 데이터를 가져옵니다.
    # 예로 OPT10080을 살펴보면 OUTPUT의 멀티데이터의 항목처럼 현재가, 거래량, 체결시간등 순으로 항목의 위치가 0부터 1씩 증가합니다.
    @pyqtSlot(str, str, result=str)
    def GetCommDataEx(self, trCode, recordName):
        return dumps(self.ocx.dynamicCall("GetCommDataEx(QString, QString)", [trCode, recordName]))

    # 차트의 특정 조회데이터를 받아온다.
    @pyqtSlot(str, str, int, str, result=str)
    def GetCommData(self, trCode, recordName, nIndex, itemName):
        return self.ocx.dynamicCall("GetCommData(QString, QString, int, QString)",
                                    [trCode, recordName, nIndex, itemName])

    # 리얼 시세를 끊는다.
    # 화면 내 모든 리얼데이터 요청을 제거한다.
    # 화면을 종료할 때 반드시 위 함수를 호출해야 한다.
    # Ex) openApi.DisconnectRealData(“0101”);
    @pyqtSlot(str)
    def DisconnectRealData(self, scnNo):
        self.ocx.dynamicCall("DisconnectRealData(QString)", [scnNo])

    # 종목코드의 한글명을 반환한다.
    # strCode – 종목코드
    # 종목한글명
    @pyqtSlot(str, result=str)
    def GetMasterCodeName(self, code):
        return self.ocx.dynamicCall("GetMasterCodeName(QString)", [code])

    # 국내 주식 시장별 종목코드를 ;로 구분하여 전달
    # strMarket – 종목코드
    # 마켓 구분값
    # 0 : 장내
    # 10 : 코스닥
    # 3 : ELW
    # 8 : ETF
    # 50 : KONEX
    # ...
    @pyqtSlot(str, result=str)
    def GetCodeListByMarket(self, strMarket):
        return self.ocx.dynamicCall("GetCodeListByMarket(QString)", [strMarket])

    # 입력한 종목의 전일가를 전달
    # strCode – 종목코드
    def GetMasterLastPrice(self, code):
        return self.ocx.dynamicCall("GetMasterLastPrice(QString)", [code])

    # 통신 연결 상태 변경시 이벤트
    # nErrCode가 0이면 로그인 성공, 음수면 실패
    def OnEventConnect(self, errCode):
        if errCode == 0:
            print('로그인 성공!')
        else:
            print('Error')
            kill(getpid(), 9)

    # 수신 메시지 이벤트
    def OnReceiveMsg(self, scrNo, rQName, trCode, msg):
        print('_OnReceiveMsg()', scrNo, rQName, trCode, msg)

    # 실시간 시세 이벤트
    def OnReceiveRealData(self, jongmokCode, realType, realData):
        # print('_OnReceiveRealData', jongmokCode, realType, realData)
        pass

    # 체결데이터를 받은 시점을 알려준다.
    # sGubun – 0:주문체결통보, 1:잔고통보, 3:특이신호
    # sFidList – 데이터 구분은 ‘;’ 이다.
    def OnReceiveChejanData(self, gubun, itemCnt, fidList):
        # print('_OnReceiveChejanData()', gubun, itemCnt, fidList)
        pass

    # 로컬에 사용자조건식 저장 성공여부 응답 이벤트
    def OnReceiveConditionVer(self, ret, msg):
        print('_OnReceiveConditionVer()', ret, msg)

    # 편입, 이탈 종목이 실시간으로 들어옵니다.
    # strCode : 종목코드
    # strType : 편입(“I”), 이탈(“D”)
    # strConditionName : 조건명
    # strConditionIndex : 조건명 인덱스
    def OnReceiveRealCondition(self, code, strType, conditionName, conditionIndex):
        print('_OnReceiveRealCondition()', code, strType, conditionName, conditionIndex)

    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrCondition(self, scrNo, codeList, conditionName, index, next, **kwargs):
        print('_OnReceiveTrCondition()', scrNo, codeList, conditionName, index, next)

    # Tran 수신시 이벤트
    @SyncRequestDecorator.kiwoom_sync_callback
    def OnReceiveTrData(self, scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message, splmMsg,
                        **kwargs):
        print('OnReceiveTrData()', scrNo, rQName, trCode, recordName, prevNext, dataLength, errorCode, message,
              splmMsg)

class SpartaQuant(QMainWindow):
    def __init__(self):
        super().__init__()
        self._kiwoom = Kiwoom()

        t1 = Thread(target=self.main_thread)
        t1.daemon = True
        t1.start()

    def main_thread(self):
        ###############################
        # 1. 로그인                    #
        ###############################

        # 로그인 시도
        self._kiwoom.CommConnect()

        # 로그인 완료 대기
        while True:
            if self._kiwoom.GetLoginInfo("ACCOUNT_CNT") != "":
                break
            print("로그인 대기 중...")
            sleep(5)
        sleep(5)


if __name__ == "__main__":
    app = QApplication(argv)
    myWindow = SpartaQuant()
    trader = myWindow._kiwoom
    app.exec_()

뭐야 왤캐길어 붙여넣기 

 

PyQt5 모듈 설치

 

잠깐! 만약 파이썬 32 bit 와 64 bit가 같이 설치되어 있는 경우 이 방법으로!

Pycharm에서 [메뉴]-[FIle]-[Settings] 클릭

Settings 에서 [Project:...] - [Python Interpreter] 클릭

'+' 버튼 클릭!

검색창에 'PyQt5' 입력!

 

 

코드 실행

계정정보 입력 후 '로그인' 버튼 클릭(주의: 모의투자 체크 필수!!)

로그인 도중 버전처리 팝업확인(주의: 절대 '확인'을 누르지 마세요!)

코드실행 정지 버튼 위치 확인(주의: 절대 정지 버튼을 누르지 마세요!)

버전처리 팝업창의 '확인' 버튼 클릭!

키움증권 창이 없어지면 바로 코드실행 정지 버튼 클릭!(주의: 창이 없어지자마자 빨리 누르셔야 합니다)

버전처리 완료 확인! (웃기다 겁나 긴박해..)

코드 재실행 및 계정정보 입력 후 로그인 성공 확인!

 

 

19) 자동 로그인 설정

로그인 완료 후 설정

키움 트레이 아이콘 오른쪽 마우스 클릭

'계좌비밀번호 저장' 메뉴 클릭

비밀번호 입력 후 '등록'버튼 클릭 및 'AUTO' 체크

프로그램 재시작 후 자동 로그인 확인!

자동로그인 설정을 해제하고 싶다면 c:\OpenAPI\system\AutoLogin.dat 파일 삭제!


숙제를 하기에 앞서.. 난 모르는 용어가 너무 많다. 날잡아서 한번에 정리해야겠다. 오늘은 할수있을만큼만 정리!

(저번에 배운거 다까먹음)

    • PBR이란?
      • Price to Book Ratio의 약자이며 '주당 순자산 비율'을 뜻함
      • $PBR = \frac{현재 주식 가격}{주당 순자산(BPS)}$
      • 순자산과 주식가격을 대비해서 주식가격의 고/저평가를 판단하는 기준점
    • BPS란?
      • Book-value Per Share의 약자이며 '주당순자산' 을 뜻함
      • $BPS = \frac{회사 총 자산}{발행 주식 수}$
      • 회사가 망해도, 회사가 갖고있는 가치이하로 팔리지 않
    • ROA란?
      • Return On Assets의 약자로 '총자산수익률' 이라는 뜻이다.
      • $ROA = \frac{당기순이익}{총자산} \times 100$  : 내 자산대비 얼마나 순이익이 발생했는지
      • 당기순이익 : 1년동안 발생한 순 이익 (순수한 이익)
      • ROA가 높으면 높을수록, 순이익의 많은 발생을 의미(좋음)
    • PER란?
      • Price Earnings Ratio의 약자로 '주가수익비율' 이라는 뜻이다.
      • $PER = \frac{현재주가}{주당 순이익(EPS)}$
      • 주당순이익: 내가 보유한 주식하나당 얼만큼의 이익이 발생하는지
      • per가 낮으면 낮을수록 좋은것.
      • $EPS = \frac{당기 순이익}{발행 주식수}$
  1. PSR

'성장성'을 알고 싶다면 PSR!

 

PSR은 현재 주가를

1주당 매출액을 나타내는 주당매출액

(SPS: Sales Per Share)으로 나눈 값입니다.

주가매출비율(Price Sales Ratio)이라고

불리죠. 

 

어떤 주식의 가격

주당 매출액에 비해 높을수록

PSR은 1.0배 이상을 기록하게 됩니다. 

 

반면 현재 주가가

주당 매출액보다 낮다면

PSR은 1.0배 미만을 밑돌게 됩니다.

나 뭔소리인지 모르겠어요

 

 

PCR(주가현금흐름비율 Price/Cash Flow Ratio)

 

PCR은 주가를 1주당 현금흐름으로 나누어 주가가 주당 현금흐름의 몇 배인지를 가지고 주가의 고평가 

혹은 저평가 정도를 판단하는 지표로 이용된다. PCR은 안정성과 향후 성장잠재력을 중시하는 지표입니다. 

 

                                  주가(종가)

주가현금흐름비율 = --------------------                      

                            1주당 현금흐름(CPS)

 

          세후순이익 + 감가상각비

CPS = ------------------------

                  주식수(평균)

 

ROE : 

ROE는 자본 대비 얼마나 많은 수익을 가져오는가를 알아보는 지표입니다. 공식으로 말하면 순이익/자기자본입니다. 정리하면 상대적으로 매출액, 영업이익, 당기순이익은 높을수록, PER/PBR은 대체로 낮을수록, ROE는 높을수록 좋다는 건데요. 계속 말씀드렸듯 절대적으로 좋은건 없습니다. 

 


숙제

블록 알고리즘 포트폴리오 구성 바꿔서 백테스팅 해보기


전략설정 :

거래 수수료, 거래세, 거래 가격 을 언제가격으로 가정해서 할지, 기본적으로 매매할때마다 어떤식으로 할지 설정

리밸런싱에서는 언제 리밸런싱 할지를 ,

포트폴리오전략은 총 3가지로 구분

1. 투자비중, 종목수

2.유니버스 구성

일차적으로 종목 필터링

기본필터에 있는 종목 추리고

3. 포트폴리오 구성

주식군 유니버스에서 내가 어떤 종목을 리밸러신 하면서 매매할지

그 기준은 종목별 기본지표조합으로 순위별 총합을 구해서 총합에 대한 순위를 매기는 종합점수 체제


오늘의 소감: 

따라하는건 쉬운데, 용어 하나하나 알아보니까

어우 뭐라는지 진짜 하나도 모르겠다 차라리 코딩시켜주세요 너무 어려워요 경제용어..

이걸 활용해서 컨텐츠로 만들어야겠다 싶었음.

728x90

'✍~2022 > 주식자동매매' 카테고리의 다른 글

주식자동매매 종합반 3주차  (0) 2021.08.21
주식 자동매매 종합반 1주차  (0) 2021.08.07