2. 해외선물/2-1. 해외선물 자동매매 연구

(키움증권 해외선물 자동매매 파이썬) 21. 매매내역을 엑셀로 기록하기

봄이오네 2023. 10. 28. 10:51
반응형

 

목 차
1. 들어가며
2. 사전설명
3. 코드설명
4. 전체코드
5. 마치며

 

1. 들어가며

지난 글에서는 해외선물 매매의 오버나잇(overnight) 피하기에 대해 알아보았다. 이또한 투자성향에 따라 다르겠지만, 오버나잇은 왠만하면 안하는 방향으로 생각하고 있다. 이유는? 아침에 일어나 수익/손실을 확인했는데, "손실"로 하루를 시작하면 그날 하루가 정말 안 좋게 진행되는 경우가 많았기 때문이다.
 
이번 글에서는 매매내역을 엑셀의 로그 기록하기에 대해 알아볼 것이다. 이 내용 또한 예전에 설명한 적이 있다. 파이참의 결과값을 엑셀로 보내기에 해당하는 글은 링크(https://springcoming.tistory.com/30)에서 확인할 수 있다.
 
사실 자동매매 코드를 작성하는 것은 알고리즘에 따른 진입을 고민해야 하는 것보다는 어렵지 않다. 파이썬 코드에 익숙해질 무렵에는 어떤 패턴에 진입해야 하는지, 하루종일 고민하고 있을 것이다.
 
매매내역을 굳이 엑셀에 남길 필요는 없긴 하다. 영웅문G에서 매매내역을 얼마든지 확인할 수 있기 때문이다. 다만, 필자가 말하고 싶은 것은, 어떤 알고리즘에 들어갔는지 영웅문G에서는 확인할 수 없다. 왜냐하면, 사용자 본인이 설정한 알고리즘이다. 영웅문G에서는 기껏해야 진입/청산값 및 수익/손실, 매매시간 정도만 알 수 있을 것이다. 가장 중요한 것은 진입/청산의 알고리즘을 본인이 파악해야, 어떤 패턴이 수익을 많이내고, 손실을 내는지 확인할 수 있다.
 


2. 사전설명

시스템 구성을 위해 필수요소는 아니지만, 나중에는 "진입 알고리즘"을 여러가지로 설계할 때, 각 패턴의 수익/손실 점을 알아야 매매 결과에 대한 복기도 되는 것이기 때문에 이또한 사용자 성향에 맞혀 사용하기 바란다.
 
필자는 CSV보다는 엑셀을 선호하는 편이다. 엑셀이 무겁고 용량이 크다는 단점이 있지만, 그래도 지금껏 사용한 기간을 생각해보면, 엑셀에 기록하는 것이 좋아보인다.
 


3. 전체코드

데이터 결과를 엑셀에 보내기 위해 행렬의 형태에 진입/청산 결과값들을 담은 후, 엑셀에 보낸다.
※ 필자의 경우, 10가지 데이터를 보낸다. 날짜, 시간, 종목, 수량, 진입/청산 알고리즘 명 등 나중에 매매복기를 할 때 참고가 될만한 요소를 엑셀에 보내둔다.
 

그림1. 엑셀 로그파일을 만들기 위해 17줄~18줄에서 행렬의 형태로 데이터를 만들어준다.

 
6줄 : 판다스를 임포트하되, "pd"로 약칭한다.
17줄 : 엑셀 로그파일을 만들기 위해, 엑셀의 형태(행렬)로 데이터를 만든다.
 

그림2. 엑셀에 데이터를 보내기 위해 행렬 형태에 각각의 데이터를 넣는다.

 
38줄~60줄 : 매매(진입, 청산)가 발생할 때마다 엑셀에 보내는 반복적인 작업을 할 것이므로 함수를 선언해준다.
39줄~49줄 : 날짜, 시간, 종목 코드 등 10가지 데이터를 'date', 'time' 형태에 내용을 추가한다.
51줄 : 판단스 형태의 행렬을 만들어서 17줄에서 미리 선언한 엑셀 로그파일에 넣는다.
 
54줄 : 바탕화면의 경로에 "futures_trading_record.xlsx" 파일의 존재 유무에 따라,
55줄~57줄 : 위 경로에 파일이 있으면 51줄의 데이터를 시트1의 맨 아래줄(max_row)에  추가(append)하라.
58줄~60줄 : 위 경로에 파일이 없으면, 파일을 만들어서 51줄의 데이터를 시트1에 써라(write).
 

그림3. 진입 및 청산 알고리즘에 따라 거래를 하고, 그 결과를 엑셀에 보낸다.

 
 
63줄~68줄 : 현재  rsi가 30이하이면, "주문가능수량 조회(opw30011)하여, long 진입 주문을 실행하라.(주문 코드는 생략)
68줄 : 주문을 넣고 나서, 날짜, 시간, 종목코드 등을 38줄~60줄 함수를 실행하여 그 내용을 엑셀로 보내라.
 
71줄~88줄 : 진입이 매수(매도수구분 2)이면 수익/손실/횡보장 60분 지속이면 청산하라.
78줄,83줄,88줄 : 청산한 결과를 38줄~60줄 함수를 실행하여 그 내용을 엑셀로 보내라.
 

그림4. 주문가능금액에 따라 진입/청산 함수를 실행한다.

 

90줄~116줄 : 주문가능수량 및 체크금액(20,000달러)과 비교하여, 진입(pl_check1)함수 및 청산(pl_check2)를 실행유무를 결정되면, 각각의 진입/청산함수를 실행하라
 


4. 전체코드

아래 < 접은글 >의 코드는 실행되지 않는다. 매매결과에 따라 엑셀로그가 만들어지는 과정을 확인해보면 좋을 것 같다. 기존 코드에서 추가된 곳은 3곳이다. 16줄~18줄(판다스 형태로 만들어준다), 37줄~60줄(엑셀에 보내기), 68줄/78줄/83줄/88줄에 37줄~60줄 함수 실행을 위한 8가지 변수를 넣어준다.
 

더보기
import sys
from PyQt5.QAxContainer import *
from PyQt5.QtWidgets import *
import time
from datetime import datetime
import pandas as pd
import os

class btl_system():
    def __init__(self):
        self.kiwoom = QAxWidget("KFOPENAPI.KFOpenAPICtrl.1")
        
        self.interesting_codes = "NQZ23"
        self.check_money = 20000

        ########## 엑셀 로그파일 만들기 ##########
        self.pl_data = {'date': [], 'time': [], 'futures_code': [], 'position': [], 'market_sort': [],
                        'qty': [], 'enter_liq_reason': [], 'enter_price': [], 'liq_price': [], 'estimate_pl': []}

    ########## 시간 현행화 ###########
    def time_update(self):
        self.date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.traded_date = self.date_time[0:10]
        self.traded_time = self.date_time[11:19]

    ########## 시간별 패턴 구분을 위한 구분 함수 ###########
    def time_pattern_gubun(self):
        now_time1 = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        now_time1 = now_time1[11:16]
        now_time2_m = now_time1[0:2]  # 시간 단위를 문자형으로 추출
        now_time3_s = now_time1[3:5]  # 분 단위를 문자형으로 추출

        now_time3 = now_time2_m + now_time3_s  # 분/초 단위를 하나로 붙임(문자형+문자형)
        now_time4 = int(now_time3)  # 문자형을 숫자형으로 변환
        self.now_time4 = now_time4  # 전역변수로 pl_check1에서 활용

    ########## 엑셀로 진입/청산 로그 기록 ##########
    def trading_record_func(self, date1, time2, codes3, position4, market5, qty6, reason7, enter_price8, liq_price9, estimate_pl10):
        self.pl_data['date'].append(date1)
        self.pl_data['time'].append(time2)
        self.pl_data['futures_code'].append(codes3)
        self.pl_data['position'].append(position4)  # 롱진입, 숏진입 / 롱청산, 숏청산으로 표기할 것
        self.pl_data['market_sort'].append(market5)

        self.pl_data['qty'].append(qty6)
        self.pl_data['enter_liq_reason'].append(reason7)
        self.pl_data['enter_price'].append(enter_price8)
        self.pl_data['liq_price'].append(liq_price9)
        self.pl_data['estimate_pl'].append(estimate_pl10)

        df1 = pd.DataFrame(self.pl_data, columns=['date', 'time', 'futures_code', 'position', 'market_sort',
                                                  'qty', 'enter_liq_reason', 'enter_price', 'liq_price', 'estimate_pl'])

        dir = r'C:\Users\User\Desktop\futures_trading_record.xlsx'  # 경로 설정
        if os.path.exists(dir):
            with pd.ExcelWriter(dir, mode='a', engine='openpyxl', if_sheet_exists='overlay') as writer:
                df1.to_excel(writer, header=False, index=False, sheet_name='sheet1', startrow=writer.sheets['sheet1'].max_row)
        else:
            with pd.ExcelWriter(dir, mode='w', engine='openpyxl') as writer:
                df1.to_excel(writer, index=False, sheet_name='sheet1')

    ########## 진입/청산을 위한 함수 모음 ###########
    def pl_check1(self):
        if self.current_rsi <= 30:
            btl.rq_data_opw30011(self.deposit_num, self.deposit_password, "00", self.interesting_codes, "2", "1", "")  # 주문가능수량
            ### long 진입주문 작성하기

            btl.trading_record_func(self.traded_date, self.traded_time, self.interesting_codes, "long 진입", "시장가", self.orderable_qty, "1-1. rsi 30 이하 매매", self.current_price_d_10002, "", "")  # 6번째는 수익 (진입시 수익없음)
            # 8가지 (날짜, 시간, 종목, 포지션, 수량, 수익, 진입가격, 진입가)

    def pl_check2(self):
        if self.sell_buy_gubun_d == "2":  # 진입이 매수인 경우 (long으로 진입한 291줄~330줄 관련)
            btl.rq_data_opw30011(self.deposit_num, self.deposit_password, "00", self.stock_code_d, "1", "1", "")  # 청산수량 받아오기 위해 실행, # ~~~ self.stock_code_d, "1" 에서 "1"은 "매도"로 설정한다.

            if self.current_price_d_30012 - self.my_price_d_30012 >= self.profit_loss_price:
                ### 청산 주문 작성

                btl.trading_record_func(self.traded_date, self.traded_time, self.stock_code_d, "long 청산 수익", "시장가", self.liquidable_qty, "long 청산 수익", "", self.current_price_d_30012, self.estimate_pl_d_30012)

            elif self.current_price_d_30012 - self.my_price_d_30012 <= -1 * self.profit_loss_price:
                ### 청산 주문 작성

                btl.trading_record_func(self.traded_date, self.traded_time, self.stock_code_d, "long 청산 손실", "시장가", self.liquidable_qty, "long 청산 손실", "", self.current_price_d_30012, self.estimate_pl_d_30012)

            if time.time() > self.ending_time:
                btl.sendorder_func("long청산", self.scrno_number, self.deposit_num, 1, self.stock_code_d, self.liquidable_qty, "0", "0", "1", "0")
                ### 청산 주문 작성
                btl.trading_record_func(self.traded_date, self.traded_time, self.stock_code_d, "long 청산 시간초과", "시장가", self.liquidable_qty, "long 청산 시간초과", "", self.current_price_d_30012, self.estimate_pl_d_30012)

    ########## 시스템 실행 ##########
    def trading_start(self):
        while True:
            
            time.sleep(2)
            btl.rq_data_opw30009(btl.deposit_num, btl.deposit_password, "00")  # 주문가능금액
            btl.time_pattern_gubun()  # 464줄에서 정의

            if btl.orderable_money > btl.check_money:
                print(f"주문가능금액이 {btl.check_money}달러 이상일 때 진입 준비 중")
                btl.pl_check1()
                # time.sleep(10)
                time.sleep(10)

            else:
                print(f"주문가능금액이 {btl.check_money}달러 이하일 때 청산 준비 중")
                btl.pl_check2()
                # time.sleep(15)
                time.sleep(10)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    btl = btl_system()

    btl.trading_start()

    app.exec_()

 


5. 마치며

진입/청산 후의 데이터를 엑셀에 보내는 방법에 대해 알아보았다. 프로그램 구축을 위해 반드시 필요한 내용은 아니지만, 나중을 생각해서 그 내용을 파악은 해놓자. 본인이 생각하는 수준으로 코드를 작성할 때가 되면, 이제는 "진입/청산"에 대해 고민하고 있는 자신을 발견하게 될 것이다.
 
알고리즘으로 고민해야할 시기가 머지않아 다가올 것이다. 그때, 로그로 보내놓은 결과물에 따라 알고리즘 추가/수정할 필요가 있다. 아직 프로그램 구축 초창기라면, 엑셀파일 만들기보다는, 파이썬 명령어에 익숙해 지도록 하자.
 
다음 글에서는 해외선물 자동매매 구축 설명 관련, 마지막 글(22번)이 될 거 같다. "알고리즘" 설정에 대해 간단하게 알아보자.
 ※ 해선 자동매매에 대한 글은 계속 쓸 것이다. 필자는 "해외선물 자동매매 시스템 구축"관련된 설명을 마무리 짓겠다는 말이다.

반응형