월 옵션은 매월 둘째 주 목요일 만기입니다.

만기일을 찾을 수 있도록 time 함수를 이용하여 어찌 어찌할 수 있겠지만 이 정도는 그냥 손으로 만드는 것이 더  효율적입니다.

 

제가 사용하는 일자를 공유합니다. 한땀 한땀 달력보고 입력한 것이니 맞을 것으로 예상되나, 혹시 틀린 날이 있다면 댓글 부탁드립니다.

 

option_end_days = {
        2010 : {1:14, 2:11,  3:11,  4:8, 5:13, 6:10,  7:8, 8:12, 9:9, 10:14, 11:11,  12:9},
        2011 : {1:13, 2:10,  3:10,  4:14, 5:12, 6:9,  7:14, 8:11, 9:8, 10:13, 11:10,  12:8},
        2012 : {1:12, 2:9,  3:8,  4:12, 5:10, 6:14,  7:12, 8:9, 9:13, 10:11, 11:8,  12:13},
        2013 : {1:10, 2:14,  3:14,  4:11, 5:9, 6:13,  7:11, 8:8, 9:12, 10:10, 11:14,  12:12},
        2014 : {1:9, 2:13,  3:13,  4:10, 5:8, 6:12,  7:10, 8:14, 9:11, 10:8, 11:13,  12:11},
        2015 : {1:8, 2:12,  3:12,  4:9, 5:14, 6:11,  7:9, 8:13, 9:10, 10:8, 11:12,  12:10},
        2016 : {1:14, 2:11,  3:10,  4:14, 5:12, 6:9,  7:14, 8:11, 9:8, 10:13, 11:10,  12:8},

        2017 : {1:12, 2:9,  3:9,  4:13, 5:11, 6:8,  7:13, 8:10, 9:14, 10:12, 11:9,  12:14},
        2018 : {1:11, 2:8,  3:8,  4:12, 5:10, 6:14, 7:12, 8:9,  9:13, 10:11, 11:8,  12:13},
        2019 : {1:10, 2:14, 3:14, 4:11, 5:9,  6:13, 7:11, 8:8,  9:11, 10:10, 11:14, 12:12},
        2020 : {1:9,  2:13, 3:12, 4:9,  5:14, 6:11, 7:9,  8:13, 9:10, 10:8,  11:12, 12:10},
        2021 : {1:14, 2:10, 3:11, 4:8,  5:13, 6:10, 7:8,  8:12, 9:9,  10:14, 11:11, 12:9},
        2022 : {1:13, 2:10, 3:10, 4:14, 5:12, 6:9,  7:14, 8:11, 9:8,  10:13, 11:10, 12:8},
        2023 : {1:12, 2:8, 3:10, 4:14, 5:12, 6:9,  7:14, 8:11, 9:8,  10:13, 11:10, 12:8},
        2024 : {1:11, 2:8, 3:14, 4:11, 5:9, 6:13,  7:11, 8:8, 9:12,  10:10, 11:14, 12:12},
     }

 

 

반응형

설정

트랙백

댓글

지난 1편에 소개 글에 이어 실시간 TR을 추가하는 방법에 대하여 기술합니다.

 

어떻게 하면 초보자가 쉽게 코딩할 수 있을지 고민을 해 보았는데요.

초보자의 경우에는 가급적 어려운 부분이 가려져 있는 것이 좋을 듯합니다. websocket, send, recv 등등 

 

내가 필요한 실시간 TR Code 값과 메세지를 받는 부분, 그리고 관심있는 ticker 정도만 전달할 수 있다면 보다 쉽게 개발을 할 수 있을 것 같습니다.

 

단언컨데 더 이상 쉬울 수 없는 방법을 소개하고자합니다.

 

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

본 셈플은 크게 두개의 .py로 이루어져 있습니다.

 

open_api_real_lib.py   # 실시간 real tr을 요청하는 부분. 일반 개발자는 신경안써도 되는 부분

my_open_api_real.py  # 개발하고자하는 real tr에 대한 코드, 일반 개발자가 짤 코드

 

open_api_real_lib.py 에 있는 내용은 관심있으신 분들만 코드를 보세요.

 

이 글에서는 my_open_api_real.py를 만드는 방법에 대해서만 기술합니다.

 

이전 글에 있던 sample 코드는 아래와 같은 방식으로 개발이 가능합니다. 이전 셈플에 비하여 관련없는 부분은 모두 삭제가 되어서 코드가 간단하고 보기도 좋습니다.

 

from open_api_real_lib import main_loop 

# 메세지 처리하는 함수
def on_future_sise(data):
    price    = float(data['price'])
    vol      = int(data['cvolume'])
    offerho1 = float(data['offerho1'])
    bidho1   = float(data['bidho1'])

    print(format(price, '5.2f'), format(vol, '4d'), '    ', format(offerho1, '5.2f'), '  ', format(bidho1, '5.2f'))
    
if __name__ == '__main__':
    # 실시간 TR 메세지 정의
    FUTURE_SISE       = 'FC0'
    
    # 관심있는 ticker 정보
    ticker = '101V3000'     # 2024년 3월 만기 선물

    main_loop(FUTURE_SISE, on_future_sise, ticker)

 

 

기본적인 구조는 실시간 TR을 정의하고 해당하는 TR에서 오는 메세지를 처리하는 함수를 만들면 됩니다. 

 

여기에 선물 호가창을 추가하는 방법에 대하여 정리합니다.

 

GUI가 아닌 command 창에 실시간 정보를 출력하기 때문에 창 하나에 한 TR을 출력하는 방식을 적용해야합니다. 실행할 때 마다 소스를 변경해서 실시간 호가창/실시간 체결 정보를 보는 것은 비효율적이니 간단하게 메뉴 방식으로 실행시 선택하는 방식을 사용해보겠습니다.

 

우선 선물 호가창 정보가 왔을 때 어떻게 출력할 것인지를 코딩합니다. 본 예제에서는 호가창 아래 위 2개만 출력합니다. 최대 5개까지 출력할 수 있으니 관심있으시분 들은 수정하여 사용하세요.

참고로 선물 호가창 TR을 통해서 받는 정보의 형태는 api 문서를 참고하세요.

def on_future_order_book(data):
    print(data['hotime'])
    print('--------------------------------')
    print(' 건수  수량   가격   수량  건수')
    print('--------------------------------')
    print(format(int(data['offercnt2']), '4,d'),
          format(int(data['offerrem2']), '4,d'), ' ',
          format(data['offerho2']))
    print(format(int(data['offercnt1']), '4,d'),
          format(int(data['offerrem1']), '4,d'), ' ',
          format(data['offerho1']))
    print(format(' ',  '11s'), 
          format(data['bidho1']), 
          format(int(data['bidcnt1']), '5,d'),
          format(int(data['bidrem1']), '5,d'))
    print(format(' ', '11s'), 
          format(data['bidho2']), 
          format(int(data['bidcnt2']), '5,d'),
          format(int(data['bidrem2']), '5,d'))

 

다음으로 추가하고 하는 실시간 TR 코드를 작성합니다.

if __name__ == '__main__':
    # TR codes
    FUTURE_SISE       = 'FC0'
    FUTURE_ORDER_BOOK = 'FH0'  # <- 새로 추가

 

간단한 선택 메뉴를 만듭니다.

    print('선택')
    
    # 새로운 REAL TR 추가시 print문에도 추가
    print('1: 선물시세  2:선물호가창')
    sel = input()

 

입력 값에 따라 실행할 TR code와 메세지를 받을 함수명을 정의합니다.

    if sel == '1' :
        tr_code = FUTURE_SISE
        func    = on_future_sise

    elif sel == '2' :  # <-  추가
        tr_code = FUTURE_ORDER_BOOK  
        func    = on_future_order_book

 

입력받은 Real tr code값, 불려질 함수명, ticker 정보를 open api를 담당하는 곳으로 전달합니다.

    main_loop(tr_code, func, ticker)

 

실행방법입니다.

 

1번 선물 시세를 선택하면 잠시 후 선택한 real tr 값을 처리한 결과가 화면에 출력됩니다.

 

 

동시에 여러 개의 실시간 TR 정보를 보고 싶으면 command 창을 여러 개 띄워서 메뉴 창에서 원하는 번호를 선택하면 됩니다.

 

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

본 lib를 사용하면 websocket에 대한 내용은 모두 몰라도 되기 때문에 보다 쉽게 open api를 이용할 수 있을 것으로 예상됩니다.

 

다음에는 화면을 좀 더 잘 볼 수 있게 하는 방법과 실시간 시세 정보를 가공해서 보여주는 방법에 대하여 기술하도록 하겠습니다.  

이런식으로 말이죠.  상승/하락시 색 변경, 체결 수량 10개 이상만 출력

 

 

 

-----------

소스코드는 아래 위치에 있습니다.  두 파일을 모두 다운받으세요.

https://github.com/multizone-quant/api_ex/blob/main/my_open_api_real.py

https://github.com/multizone-quant/api_ex/blob/main/open_api_real_lib.py

반응형

설정

트랙백

댓글

선물 실시간 데이터를 받을 일이 생겼습니다. 기존 xing api로 작업을 해서 돌리고 있는데, Open API에 실시간 TR이 있다는 말을 듣고 확인해보았습니다.

 

websocket을 이용하는 방식이라 개발하기 상당히 쉽게 되어 있습니다. 반면에 xing api는 callback 설정하고 등등 절차가 상당히 복잡합니다. 향후 기능 추가를 생각해서 이번 기회에 Open Api로 바꾸기로 했습니다.

 

ebest open api 사이트에 가면 실시간 TR 사용법에 대하여 기술되어 있는 글이 있습니다.

https://openapi.ebestsec.co.kr/howto-sample 

 

전체적인 흐름을 잘 정리해놓았으나 결정적으로 함수를 부르는 main 부분이 없습니다. 

 

그래서 동작하는 전체 소스를 공유하고자 합니다. Open API를 이용하여 실시간 데이터를 받고자 하는 경우에 유용하게 사용될 것입니다.

 

import requests
import json
import time
import asyncio
import websockets

APP_KEY = "본인의 key"
SECRET_KEY = "본인의 sec key"

 

우선 필요한 package import한 후 본인의 Open API 키를 넣습니다.  만약 관련 package가 없다고 나오면 아래와 같이 설치합니다.

 

pip install websockets

pip install asyncio

 

아래 코드는 그냥 묻지도 따지지도 말고 그냥 사용합니다.

# 이하 수정할 필요없는 부분
BASE_URL        = "https://openapi.ebestsec.co.kr:8080"
BASE_URL_WEBS   = "wss://openapi.ebestsec.co.kr:9443/websocket"

   
# 파생인의 쉼터 꿈에님 코드 인용
def get_token():
    PATH = "/oauth2/token"
    headers = {"content-type": "application/x-www-form-urlencoded"}
    body = {
        "appkey": APP_KEY,
        "appsecretkey": SECRET_KEY,
        "grant_type": "client_credentials",
        "scope": "oob"
    }
    result = requests.post(BASE_URL+PATH, headers=headers, data=body)

    body = result.json()

    return body["access_token"]


# 선물 실시간 시세용 message
def reg_future_real(cd, ticker):
    dt = {
        'header': {
            "token": get_token(),
            "tr_type": "3"  #3:실시간 등록
        },  
        'body' : {
            "tr_cd": cd,
            "tr_key": ticker,     
        }         
    }
    return json.dumps(dt) # json을 string으로 변환

지금 만들고자 하는 함수는 실시간 선물 시세입니다. 선물 실시간 체결이 되면 Open API를 통하여 아래 함수로 호출이 옵니다.

# 체결수량 출력
def on_future_sise(data):
    price    = float(data['price'])
    vol      = int(data['cvolume'])
    offerho1 = float(data['offerho1'])
    bidho1   = float(data['bidho1'])

    print(format(price, '5.2f'), format(vol, '4d'), format(offerho1, '5.2f'), format(bidho1, '5.2f'))

실제로 많은 정보들이 data에 들어옵니다. 자세한 정보는 KOSPI200 선물시세를 선택하면 나옵니다.

 

이제는 on_future_sise() 라는 함수가 호출되는 과정에 대하여 간단하게 정리합니다.

 

우선 코드부터 보죠.  KOSPI200 선물시세를 받을 수 있는 TR의 CODE는 'FC0'입니다. 

 

아래 real_api()라는 함수를 보면 async라는 문자가 앞에 있습니다. async with를 사용하는 경우도 있고, await와 함수명을 사용하는 경우도 있습니다. 비동기 함수를 사용하기 위한 절차라고 생각하시고 그냥 무시하셔도 됩니다. 앞으로 real_api 함수에서 connect/send/recv 부분은 손댈필요가 없기 때문입니다.

 

FUTURE_SISE       = 'FC0'

# 웹 소켓 관련 신경쓸 필요없음
import websockets
async def real_api(real_code, ticker):
    while True:
    # 웹 소켓에 접속을 합니다.
        async with websockets.connect(BASE_URL_WEBS) as websocket:
            str = reg_future_real(real_code, ticker)

            # 웹 소켓 서버로 데이터를 전송합니다.
            await websocket.send(str);
            print('wait')
            time.sleep(2)
            
            while True:
                # 웹 소켓 서버로 부터 메시지가 오면 콘솔에 출력합니다.
                data_s = await websocket.recv();
                data = json.loads(data_s)
                if data['body'] == None: # 시세가 바로 오지 않음 None이면 waiting
                    time.sleep(1)
                    continue
                if real_code == FUTURE_SISE:
                    on_future_sise(data['body'])

 

그래도 무슨 일을 하는지는 알아야겠지요?

reg_future_real() 함수는 실시간 TR을 등록하는 일을 합니다. 앞으로 이 함수에 등록하는 TR에서 주는 실시간 데이터를 받겠다는 의미입니다. 그 다음부터는 ebest server에서 전달하는 실시간 데이터를 받아서 사용할 함수로 전달하면 끝입니다.

 

이 중 특이한 부분은 websocket에서 받은 데이터를 json 형태로 변경시키는 아래 부분입니다. 실제로 recv한 data_s는 string 문자열입니다. 긴 문자열로 이루어진 데이터 중 원하는 값을 뽑아내기가 어렵기 때문에 json 형태로 변경해주는 절차가 필요합니다. 그게 바로 json.load입니다. 앞으로 data는 "key":value 형태로 접근이 가능합니다.

                data_s = await websocket.recv();
#                print(data_s)
                data = json.loads(data_s)

 

여기까지는 ebest sample에 나오는 내용과  비슷합니다.  결정적으로  ebest 문서에는 이렇게 많은 함수를 호출하는 부분이 빠져있는데요.

 

이렇게 만든 함수를 호출하는 방법은 다음과 같습니다.

if __name__ == '__main__':
    ticker = '101V3000'     # 2024년 3월 만기 선물
    cd = FUTURE_SISE
    asyncio.run(real_api(cd, ticker))

 

간단하죠.. 그냥 asyncio.run() 함수를 부르면 됩니다. 이때 인자로 위에서 만든 real_api(cd, ticker) 를 넣어줍니다. 즉 비동기적으로 real_api()를 실행하라의 의미입니다.

 

asyncio.run(real_api(cd, ticker))

 

결론적으로 websocket으로 연결하고 어쩌고 하는 부분은 모두 모르셔도 됩니다.

실제 선물이 체결되면 아래 함수로 data에 관련 정보가 저장된 상태로 아래 함수가 불리어진다는 것만 인지하면 됩니다.

def on_future_sise(data):

 

이 함수에서 여러 다양한 일들을 할 수 있습니다. 예를들어 10계약 이상 거래한 것만 출력하자 등등 (이건 다음 글에서 정리해보겠습니다.)

 

sample 소스코드는 아래 github에 있습니다.

https://github.com/multizone-quant/api_ex/blob/main/open_api_real_sample.py

 

 

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

ebest open api 실시간 TR을 python으로 개발하는 방법을 간단하게 기술하였습니다.

다음에는 이러한 기본 코드를 바탕으로 빌드업을 해보겠습니다.

 

- 선물 거래량 n개 이상만 출력하기

- 선물 orderbook 실시간 정보 출력하기  

반응형

설정

트랙백

댓글

OpenAPI가 나오면서 API 접근성이 많이 개선이 된 것 같습니다.

하지만 처음 자동매매를 시작하시는 분들이 참고할만한 자료가 많지 않아서 진도가 잘 나가지 않을텐데요. 그래서 문서를 보고 직접 코드를 만드는 절차를 자세하게 기술하고자 합니다.

 

이베스트 증권 OpenAPI를 이용하기 위해서는 API key를 발급받아야합니다. 아래 내용 참고하세요.

 

API key를 받았다면 아래 API Guide로 이동합니다.

https://openapi.ebestsec.co.kr/apiservice


여기에 보시면 Open API가 지원하는 다양한 TR 정보들에 대한 자세한 정보가 나옵니다. 

 

"주식/시세/기간별주가" 어떻게 코딩을 하는지 알려드리겠습니다.

API Guide에 나오는 내용입니다.

 

기본정보에 나오는 값도 코드에 사용을 하여야하니 일단 참고만 합니다.

 

다음은 요청시에 필요한 header 정보입니다.

 

뭔가 좀 복잡한데요. t1305를 이용하기 위해서는 header에 이런 정보를 넣어야한다는 의미입니다.

 

큰 틀은 get_token() 함수를 이용하여 token을 받은 후 본인이 원하는 함수를 호출하는 형식입니다.

token = get_token()

ticker = '069500' # kodex 200
qrycnt = 2          # 몇 개를 받을지
dwmcode = 1         # 일봉:1,  주봉:2, 월봉:3

# infos : candle info
infos = get_stock_dwm_info(ticker, qrycnt, dwmcode)
for info in infos : 
    print(json.dumps(info, indent=4))

 

 

일반적으로 t1305를 한번만 부르지 않기 때문에 아래와 같이 함수를 만듭니다. 함수를 만들때에는 인자는 header와 body에 들어갈 정보를 보고 적절하게 만드시면 됩니다. 함수 작성하는 방법 default 값을 assign하는 방법 등은 파이썬 교재를 통하여 습득하시기 바랍니다.

 

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

t1305에 해당하는 get_stock_dwm_info()를 만드는 과정을 설명합니다.

 

header 부분입니다.

 

PATH는 기본정보의 URL 값을 입력하세요.

연속여부는 edate 값에 따라 결정합니다.

content-type과 authorization에 들어가는 부분은 꿈에님 코드를 활용하였습니다. 이 부분은 예제에 나오는 함수를 그냥 사용하시면 됩니다.

 

다음으로 각 항목은 tr 마다 모두 틀리기 각 tr에 대하여 guide 참고하여 적절한 값을 넣어야합니다. 만약 api가 작동하지 않는다면 header에 넣어주는 정보가 틀렸다는 의미이므로, guide를 다시 한번 자세하게 읽어서 정확한 값을 넣어주셔야 합니다. 특히 type 부분을 잘 보셔야 합니다. string인지 int인지 자리수는 몇자리인지 잘 확인하셔야 합니다.

 

def get_stock_dwm_info(ticker, qrycnt, dwmcode=1, edate = ' ', tr_cont_key=''):
    PATH = "/stock/market-data"
    # 연속여부 판단
    cont = 'N'
    if tr_cont_key != '' :
        cont = 'Y'
        
    headers = {
            "content-type": "application/json; charset=utf-8", "authorization": "Bearer "+token,
            "tr_cd": "t1305", 
            "tr_cont": cont, 
            "tr_cont_key": tr_cont_key
    }

 

 

다음은 body 부분입니다. header와 마찬가지로 정확한 값을 입력해야합니다. 

 

 

date 필드는 처음조회시와 연속조회시 다른 값이 들어갑니다. 일반적으로 한번에 받을 수 있는 자료의 크기가 정해져있으므로, 그 이상 받고 싶은 경우에는 연속조회를 해야합니다. 연속조회 방식은 이전 글 참고하세요.

 

body에 대응하는 실제 코드입니다. body와 1:1로 위치 맞춰서 적절한 값이 설정되도록 합니다.

    body = {
        "t1305InBlock": { 
            "shcode": ticker, 
            "dwmcode": dwmcode,   # 1@일, 2@주, 3@월
            "date": edate,   # 처음 조회시는 Space 연속 조회시에 이전 조회한 OutBlock의 date 값으로 설정
            "idx": 0,       # 무시
            "cnt": qrycnt   # 건수
            } 
        }

 

이제 query를 할 준비가 끝났습니다. query를 던집니다.

    result = requests.post(BASE_URL+PATH, headers=headers, data=json.dumps(body))
    
    header = result.headers
    body = result.json()

 

결과는 header와 body를 통하여 확인이 가능합니다.

 

header에는 tr 정보가 들어있습니다. 혹시라도 답이 제대로 나오지 않는다면 header 정보를 확인하시면 됩니다. 정상적인 경우에는 무시하셔도 됩니다.

 

body에는 query한 결과가 들어있습니다. 

 

body를 보시면 아래 두 key 값으로 결과 값이 저장되어 있습니다. 만약 연속조회를 한다면 "t1305OutBlock"에 있는 date 값을 이용하여 재 조회를 하시면 됩니다. 이렇게 글은 쉽게 썼지만 처음 접하시는 분들은 무슨 말이냐 하실텐데, 이전 선물 tick 받는 소스 참고하여 스스로 공부해보시기 바랍니다.

 

t1305OutBlock1에는 우리가 원하던 candle 정보가 들어있습니다.

 

query 결과는 json 형태로 돌려주는데 이 값을 출력할 때  아래 방식을 사용하면 보시기 편할겁니다.

        print(json.dumps(body, indent=4))

 

마지막으로 함수에서 돌려줄 때 OpenAPI로 받은 값을 그냥 돌려주면 사용하는 쪽에서 힘들어집니다. 앞으로 다양한 API를 이용할텐데 그때마다 t1305OutBlock1 이런 식으로 정해진 값을 key 사용하면 불편해집니다. 따라서 단순하게 dict list로 돌려주는 것이 사용하기 편해집니다. 아래와 같이요.

    return body["t1305OutBlock1"]

 

이제는 받은 값을 출력해봐야겠죠.

# infos : candle info
infos = get_stock_dwm_info(ticker, qrycnt, dwmcode)
for info in infos : 
    print(json.dumps(info, indent=4))

 

출력결과입니다.

 잘 나오는군요.

 

만약 query 하고자 하는 개수가 100개라면 이런식으로 출력하면 너무 양이 많습니다. 시간도 많이 걸리고요. 

이런 경우에는 1-2개만 잘 받는지 확인하고 그 다음부터는 print 문을 빼시면 됩니다.

 

query한 결과를 화면 출력하였다면 이번에는 파일에 저장하는 방법입니다.

 

저장하는 방법도 여러가지가 있겠지만 저는 csv 파일 형태로 저장하는 것을 선호합니다.

 

저장할 때와 읽을 때 사용하는 함수도 포함해놓았으니 필요할 때 사용하시기 바랍니다. 아래는 코드 사용 예입니다.

# 저장    
fname = ticker + '_day.csv'
save_to_file_csv(fname, infos)

# 읽기
data = read_csv_to_dict(fname)
print('읽은 값')
for info in data : 
    print(json.dumps(info, indent=4))

 

cvs 파일 형태로 저장이 되어 있으므로 excel을 이용하면 뭔가 하기 편합니다.

 

 

 

간단하게 Open API로 본인만의 파이썬 코드를 만드는 방법을 정리해보았습니다. OpenAPI는 많은 TR이 있으므로, 본인이 필요한 TR이 생기면 그때 그때 같은 방식으로 함수를 만들어서 사용하시면 됩니다.

 

개발하시다가 잘 안되는 경우가 생기면 댓글로 남겨주세요.

 

 

소스코드는 아래 github에 있습니다.  OpenAPI_Stock_Candle.py

https://github.com/multizone-quant/api_ex

 

GitHub - multizone-quant/api_ex

Contribute to multizone-quant/api_ex development by creating an account on GitHub.

github.com

 

반응형

설정

트랙백

댓글

기존 Xing Api에 비하여 Open Api가 사용하기 간편합니다. 우선 32bit cuda 설치할 필요가 없는 것이 엄청난 장점이고요. 그외 코드가 간단하기 때문에 앞으로는 Open API를 주로 사용할 듯 합니다.

 

이번에는 Open API를 이용하여 선물 tick 데이터를 받는 방법에 관하여 정리합니다. 

간단하게 생각하고 개발을 시작했는데, tick 데이터를 받는 API에 버그가 있어서 회피 코드 만들고 확인하느라 시간이 좀 많이 걸렸습니다. API 버그는 이전 글 참고하세요.

 

Xing API도 마찬가지지만 Open API도 10분 최대 qeury는 200회 횟수 제한이 있습니다. 문제는 tick 데이터는 200회 이상 받아야 하는 점인데요. 10분 기다렸다가 남은 부분 받으면 되지만 10분 기다리는 것이 아까운 것 같아서 약간의 편법을 동원했습니다. 다시 실행하는 경우에는 다시 10분에 200회 다운이 가능하다는 점을 활용하여 200회 query 후 종료한 후 다시 실행하는 방식을 사용합니다.

 

다시 실행할 때 이전에 받던 시간 다음부터 받아야하기 때문에 과거 정보가 필요합니다. 이것은 실행 중에  생성했다가 삭제하므로 신경쓰지 않으셔도 됩니다.

그래서 download 프로그램을 2번 연속으로 실행하여야 하므로 프로그램에서 알려주는 메세지에 따라서 진행하시면 됩니다. python이 없으시다면 python 부터 설치하세요.

 

OpenAPI를 이용하기 위해서는 key를 생성해야합니다. 아래 글은 꿈에님의 글에서 캡쳐해왔습니다.

 

KEY를 발급받으셨다고 가정하고 사용방법을 설명합니다.

 

우선 아래 깃허브에서 파일 두개를 다운 받은 후 다음의 절차대로 실행하시면 됩니다.

 

1. pip install websockets

 

2. tick-down.py 파일을 열어서 본인의 KEY 정보 추가

    APP_KEY = "본인의 key"
    SECRET_KEY = "본인의 sec key"

 

3. down하고자 하는 일자와 ticker(참고로 선물 ticker는 mts에서 선물을 클릭해보면 나옵니다.)

    date   = '20240125'
    ticker = '101V3000'

4. tick-download.py 실행

   python tick-download.py
   한번 더 실행하라는 메세지가 나오면 "위 화살표"를 눌러서 한번 더 실행


5. download 끝났으면 tick-download .py 실행
   python tick-download.py


6. down 받은 tick data는 future_tick 폴더에 있음

 

실행화면입니다.

tick-down.py 첫 번째 실행

 

두 번째 실행

 

tick-merge 실행

 

사용하기 최대한 간단하게 만들었으니, 사용 중 문제가 생기면 댓글 달아주세요. 바로 조치하도록 하겠습니다.

 

그리고 중간에 download받은 각각의 파일은 future_tick_seq 폴더에 기록이 되는데 계속 파일이 쌓입니다. 주기적으로 들어가셔서 지워주세요.

 

merge된 최종 tick 데이타는 future_tick 폴더에 차곡차곡 쌓입니다.

 

Ebest API 오류로 우여곡절을 겪었으나 코드가 완성이 되어서 기쁩니다. 그 과정에 많은 도움을 주신 꾸준님과 OpenAPI 샘플을 공유해주신 꿈에님께도 감사드립니다. 

 

소스코드가 있는 곳

https://github.com/multizone-quant/api_ex/tree/main

 

 

반응형

설정

트랙백

댓글

선물 tick 데이터를 연속으로 받는 중 발견한 오류입니다.

tick 데이터는 msec 해상도를 가지는데, msec까지 같은 경우가 가끔 있습니다. 그런데 연속 조회시 마지막 데이터와 다음 조회시에 받을 데이터가 같은 msec인 경우에 연속 조회시 받을 수 있는 두가지 오류가 발견되었습니다.

 

일단 원 데이터를 보겠습니다.

아래는 2024년 1월 29일 선물 tick 데이터입니다. 09:39:36초에 총 4번의 거래가 발생하였음을 알 수 있습니다.

 

첫 번째 오류는 최근에 받은 tick 자료에 msec이 같은 tick이 더 있는 경우에 발생하는 오류입니다. 

아래 자료 중 밑에 있는 것은 이전에 받은 tick 정보입니다. 09:39:36초에 2건의 거래가 있었음을 알려줍니다. querycnt 값이 차서 이 상태로 돌려줍니다. 이때 cts_time 정보는 0939363755가 돌아옵니다. API ref를 보면 돌려주는 cts_time 정보를 다음 연속 query할 때 입력으로 사용하라고 명시되어 있습니다.  다음 query의 결과값은 0939363755 보다 큰 값을 가진 tick 정보를 돌려주는 방식입니다. 이 가정에서 오류가 발생하는데요.

 

앞에서 설명하였듯이 09:39:36초에는 총 4건의 거래가 있습니다. 다음 query에서는 이 전에 몇 건을 보냈는 정보를 관리하지 않아서 무조건 cts_time보다 큰 값을 가진 tick을 돌려줍니다. 따라서 미처 받지 못한 2건은 사라지게 됩니다.

 

ebest api를 수정할 방법은 없으니 회피 방법을 찾아봅니다.

 

cts_time을 전달할 때 query에서 준 값이 아닌 tick 정보 중 최근 값보다 큰 값을 전달하면 될 듯합니다. 기본 가정은 최신 tick 시간에는 중복된 데이타가 있다고 가정을 하는 것이죠. 이렇게 해보니 msec이 중복된 tick 정보는 모두 받을 수 있는데  또 한가지 처리하여할 문제가 발생합니다. 바로 중복 저장 문제입니다.

 

수정한 방식으로 연속 query를 하면 아래와 같이 09:39:36초 데이터가 중복으로 받아집니다. api 입장에서는 0939363755 틱을 이전에 받았는지 알 수가 없는 것이죠. 그래서 중복된 모든 tick 정보를 다시 전달해줍니다. 

 

이 또한 어쩔 수 없는 것이므로, 중복된 tick을 제거하는 방식으로 처리하여야합니다.

 

방법은 간단한데요. 이전에 받은 자료 중 같은 시간에 해당하는 tick의 수를 계산해서 이번에 받은 자료에서 그 만큼 차감하여 저장하면 됩니다. 이렇게 하면 아래와 같이 중복된 자료가 사라짐을 알 수 있습니다.

 

결과적으로 msec이 같은 tick 정보를 모두 받을 수 있고, 중복된 tick 정보도 제거할 수 있었습니다.

 

이런 과정없이 tick을 저장하신 분들이 msec이 같은 경우에 이빨이 빠져있을 수 있으니 이점 참고하시기 바랍니다.

 

반응형

설정

트랙백

댓글

TradingView에서 인기 높은 지표 중의 하나가 Super Trend입니다. 추세의 시작 시점과 끝을 알려주는 지표로 많은 사랑을 받고 있습니다.

 

https://www.tradingview.com/v/r6dAP7yi/

 

SuperTrend — KivancOzbilgic tarafından gösterge

SuperTrend is one of the most common ATR based trailing stop indicators. In this version you can change the ATR calculation method from the settings. Default method is RMA, when the alternative method is SMA. The indicator is easy to use and gives an accur

tr.tradingview.com

 

Apple 차트에 대한 SuperTrend signal입니다. Apple이 추세를 잘 그리는 종목이라 진입 시점이 잘 맞는 것 같습니다.

 

 

SuperTrend의 pine script는 아래와 같습니다.

https://www.tradingview.com/v/r6dAP7yi/
//@version=4
study("Supertrend", overlay = true, format=format.price, precision=2, resolution="")

Periods = input(title="ATR Period", type=input.integer, defval=10)
src = input(hl2, title="Source")
Multiplier = input(title="ATR Multiplier", type=input.float, step=0.1, defval=3.0)
changeATR= input(title="Change ATR Calculation Method ?", type=input.bool, defval=true)
showsignals = input(title="Show Buy/Sell Signals ?", type=input.bool, defval=true)
highlighting = input(title="Highlighter On/Off ?", type=input.bool, defval=true)

atr2 = sma(tr, Periods)
atr= changeATR ? atr(Periods) : atr2
up=src-(Multiplier*atr)
up1 = nz(up[1],up)
up := close[1] > up1 ? max(up,up1) : up
dn=src+(Multiplier*atr)
dn1 = nz(dn[1], dn)
dn := close[1] < dn1 ? min(dn, dn1) : dn
trend = 1
trend := nz(trend[1], trend)
trend := trend == -1 and close > dn1 ? 1 : trend == 1 and close < up1 ? -1 : trend

upPlot = plot(trend == 1 ? up : na, title="Up Trend", style=plot.style_linebr, linewidth=2, color=color.green)
buySignal = trend == 1 and trend[1] == -1

plotshape(buySignal ? up : na, title="UpTrend Begins", location=location.absolute, style=shape.circle, size=size.tiny, color=color.green, transp=0)
plotshape(buySignal and showsignals ? up : na, title="Buy", text="Buy", location=location.absolute, style=shape.labelup, size=size.tiny, color=color.green, textcolor=color.white, transp=0)
dnPlot = plot(trend == 1 ? na : dn, title="Down Trend", style=plot.style_linebr, linewidth=2, color=color.red)
sellSignal = trend == -1 and trend[1] == 1
plotshape(sellSignal ? dn : na, title="DownTrend Begins", location=location.absolute, style=shape.circle, size=size.tiny, color=color.red, transp=0)
plotshape(sellSignal and showsignals ? dn : na, title="Sell", text="Sell", location=location.absolute, style=shape.labeldown, size=size.tiny, color=color.red, textcolor=color.white, transp=0)
mPlot = plot(ohlc4, title="", style=plot.style_circles, linewidth=0)
longFillColor = highlighting ? (trend == 1 ? color.green : color.white) : color.white
shortFillColor = highlighting ? (trend == -1 ? color.red : color.white) : color.white
fill(mPlot, upPlot, title="UpTrend Highligter", color=longFillColor)
fill(mPlot, dnPlot, title="DownTrend Highligter", color=shortFillColor)
alertcondition(buySignal, title="SuperTrend Buy", message="SuperTrend Buy!")
alertcondition(sellSignal, title="SuperTrend Sell", message="SuperTrend Sell!")
changeCond = trend != trend[1]
alertcondition(changeCond, title="SuperTrend Direction Change", message="SuperTrend has changed direction!")

 

TradingView를 사용한다면 SuperTrend를 바로 사용할 수 있지만 본인이 직접 자동매매를 하는 경우에는 pine script를 본인이 사용하는 컴퓨터 언어로 변환작업을 해야합니다.

 

이에 SuperTrend 지표를 python으로 변환하는 과정을 정리하고자 합니다.

다음 편에서는 SuperTrend 지표를 만드는 방법에 대하여 정리하면서 해당하는 python code를 함께 정리하도록 하겠습니다.

 

반응형

설정

트랙백

댓글

지난 번에 옵션 양매도에 대하여 백테스트 한 결과를 공유하였습니다.

https://money-expert.tistory.com/90
[시스템트레이딩] 수익나는 옵션 매도 전략(8)

 

그때 결론은 수익이 나지 않지 않았는데요. 최근 만기일에 손매매를 해보니 조금씩 수익이 나는 것 같습니다. 그래서 다시 한번 검토해보기로 했습니다.

 

기존 백테스트 관련 소스를 들여다보니, 코드가 상당히 복잡해서 다시 작성하였습니다.

 

옵션 양매도 전에 일단 옵션 nake 매수/매도 부터 먼저 개발을 진행하고 있습니다. 우선 백테스트부터 해야겠죠. 지금까지 확인한 결과를 공유합니다.

 

결론적으로 아주 단순한 전략인 이평 5/10일 골드/데드만으로도 수익이 납니다. 단 콜/풋 nake 매도의 경우에만 수익이 납니다. 

 

1분 봉으로 백테스트 한 결과는 아래와 같습니다.

 

한마디로 요약하면

 

콜옵션매도 천국/옵션매수 지옥

 

옵션의 특성을 이해하면 당연한 결과일 수 있습니다. 시간이 지날수록 프리미엄이 감소하는 세타의 의력을 다시 한번 느꼈습니다. 

 

콜매수한 종목 중 손실이 가장 많이 난 경우를 확인해보니 추세가 나오지 않은 상태에서 지속적으로 손절이 누적되면서 손실이 나는 현상을 보여주고 있습니다.

 

 

반면에 콜 매도의 경우에는 지수가 큰 폭으로 상승한 11/13일에도 수익이 나고 있습니다. 아래 그래프에서 확인할 수 있듯이 중간 중간 하락시에 수익을 잘 챙기고 있습니다. 대세 상승 구간에서는 빠른 손절 혹은 진입 자체를 하지 않습니다. 즉 이기는 게임을 하고 있습니다.

 

 

참고로 이번에 확인한 전략은 1 분봉 기준 MA5/MA10 골드크로그/데드 크로스로만 백테스트한 것이기 때문에 개선의 여지는 충분이 있습니다.

 

결론적으로 만기일 콜/풋은 매도 전략이 잘 먹힌다 입니다.

 

양매도 가기 전에 nake 매도에서 수익이 나는 것을 확인하다 보니 우선은 nake 매도 쪽으로 좀 더 검토하도록 하겠습니다.

 

반응형

설정

트랙백

댓글

예스트레이더(이하 예트)에서 시스템을 만들기 위해서는 언어의 한 종류인 YesLanguage를 배워야합니다. YesLanguage 로 원하는 수식을 자유롭게 만들기 위해서는 많은 공부가 필요합니다.

 

다행히 QnA 란에 질문하면 관리자가 친절하게 답변을 해 줍니다만, 매번 그렇게 하는 것도 어렵습니다. 스스로 공부를 많이 해서 불편함 없이 사용을 할 수 있다면 좋겠죠?

 

이번에 돌파 전략인 던키안 4주 채널 전략에 대한 백테스를 하게 되었는데, 제가 개발한 결과가 맞는지 검증하기 위하여 예트를 이용하기로 했습니다. 오랜만에 접속을 해보니 사용법이 가물가물하는군요.

 

이전 글 다시 읽어보면서 감을 찾은 후 YesLanguage를 열었습니다. 하지만 제가 원하는 바를 YesLanguage로 표현하는게 쉽지가 않더군요. 그래서 혹시나 하는 마음에 이와 유사한 수식이 있는 인터넷에서 찾아보았습니다.

 

다행히 제가 원했던 예제가 있었습니다. 

https://blog.naver.com/chartist/222633747003

 

 

주식시장을 이긴 전략들에 나오는 전략을 모두 YesLanguage로 만들어 올려놓으셨더군요. 덕분에 YesLanguage를 더 잘 이해할 수 있게 될 듯 합니다. 감사!!

 

이 글에 나오는 전략을 예트에서 실행하기 위한 절차를 자세하게 설명합니다.

 

이 post에 나오는 전략을 사용하기 위해서는 한가지 선행해야할 것이 있습니다. 바로 전략에서 사용하는 사용자 함수를 미리 복사를 해야하는 것인데요.

 

아래 글에 가서 WeekHigh.yfu, WeekLow.yfu를 다운 받은 후 YesTrader가 설치된 곳에 가서 복사를 해야합니다.

 

https://blog.naver.com/chartist/222633688599

 

설치 위치를 변경하지 않았다면 C 드라이브 밑 아래 위치에 복사하시면 됩니다.

C:\예스트레이더(x64)\YesLang\Functions

 

그리고 각 전략을 설명한 page에 있는 .ysg 파일을 아래 폴더에 복사하시면 됩니다.

 

이제 던키안의 4주 채널을 시뮬해볼 수 있는 준비가 마무리되었습니다.

 

YesTrader를 실행한 후 YesLanguage를 실행하면 방금 복사한 전략/함수들을 가져올 것인지 물어봅니다. Yes를 하시면 됩니다.

 

다음은 시스템 트레이딩을 실행하는 절차입니다.

1.시스템트레이딩 밑에 있는 시뮬레이션 차트를 누른다.

2. 원하는 종목/기간/봉 종류를 선택한다.

3. 빨간색을 표시된 시스템적용을 누른 후 좀전에 추가한 시스템을 선택한다.

4. 시뮬한 결과가 차트에 표시가 됨

5. 시스템 성능 보고서를 열어서 성과를 확인한다.

 

 

결과를 보면 원글에 나오는 성과보고서와 조금 차이가 나는데요.

원글은 평가 기간이 21/12/30일까지인 반면에 제가 돌려본 기간은 23/10월까지 입니다.

원글은 국내 지수에 대한 수익 그래프입니다(아래 왼쪽 그래프).

나스닥에 대하여 어떻 결과가 나올지 궁금하여 돌려본 결과는 오른쪽에 있습니다.

 

나스닥 상승시에는 수익그래프도 아주 이쁘게 우상향하고 있습니다만, 최근까지 반영한 시뮬 결과는 혼조 그자체이군요.

그 이유는 2022년 부터 나스닥이 하락을 했기 때문입니다.

결국 던키안4주채널 전략은 상승장에서 상승, 하락장에서는 하락하는 특징을 가지고 있다고 보시면 될 것 같습니다.

대부분의 추세 돌파 전략의 경우에는 이러한 단점을 가지고 있기 때문에 상승장에서만 적용하는 필터를 적용하는 경우가 많이 있습니다.

 

전략마다 통하는 시점이 있기 때문에 언제 어떤 전략을 돌려야 하는지를 선택하는 것 또한 중요한 전략인 듯 합니다.

 

반응형

설정

트랙백

댓글

ebest api에서 t8413을 이용하여 특정 주식의 일봉 자료를 얻을 수 있습니다. 자료에 보면 jongchk와 rate라는 항목이 있는데 여기에 주가 변동 사항에 대하여 정리가 되어 있습니다.

 

이동 평균선을 구할 때 이 항목을 잘 확인을 해야하는데요. 이걸 무시하면 엉뚱한 결과가 나올 수 있습니다.

 

삼성전자 예를 들어보겠습니다. 아시다시피 삼성전자는 액면가 5,000원을 100으로 변경하는 액면분할을 하였습니다. 당시 일봉자료를 보면 아래와 같은 값이 표기되어 있습니다.

 jongchk : 4100

rate : -98

 

그 의미를 이해하려면 4100을 hex 값으로 변환하여야 합니다. 4100의 hex값은 1004가 됩니다. 위에 그림에서 보면 0x1000은 기준가 조정이고 마지막 4는 액면분할을 의미합니다. 

 

 

그리고 rate값인 -98은 액면가가 98% 줄어들었다는 의미입니다.

주가 보정을 위해서는 아래와 같은 방식으로 하면 됩니다.

삼성전자 2018/05/03 주가 2,650,000  

수정주가 => 2,650,000 * (100+rate)/100  = 53,000

 

반대로 액면병합인 경우에는 rate 값이 양수로 표기됩니다.

 

액면병합, 연말 배당, 유상증자 등으로 권리변동이 계속 발생하므로 변경이 생긴 원인을 파악하고 이에 맞는 적절한 가격 계산을 해 주어야 합니다.

 

반응형

설정

트랙백

댓글