기존 [시스템트레이딩] API를 이용한 자동매매(3) 프로그램은 ticker 하나를 매매하는 예제입니다. ticker 여러 개를 동시에 매매하는 예제에 대한 요청이 있어서 프로그램 업그래이드를 하였습니다.

 

기존 프로그램에서는 ticker 하나를 매매하므로 Trader() 클래스 하나만 사용하였지만 ticker 여러 개를 동시에 매매를 하기 위해서는 Trader()를 관리하는 class가 추가로 필요합니다. 이를 위하여 TraderMgr() 클래스를 추가하였습니다.

 

그리고 simultation을 위하여 사용하는 가상 exchange class를 수정하였습니다. 기존 가상거래소 class는 매매 기능만 있었으나, 특정 거래소의 현재가를 받아서 가상 매매를 하는 방식으로 수정하였습니다.

 

만약 upbit이 아닌 다른 거래소 시세를 이용하여 가상 매매를 한다면 아래 코드에서 해당하는 거래소로 변경하면 됩니다.

class DummyExchange(Exchange):
    def __init__(self, exchange_name, access, secret):
        super().__init__()
        self.type = ''
        self.name = 'dummy'
        self.holding = []
        if exchange_name == 'upbit' :
           self.exchange = MyUpbit(access, secret)

        self.seq_uuid = int(time.time())

 

여러 ticker를 동시에 매매하기 위하여 수정한 부분에 대하여 설명합니다.

 

우선 사용할 거래소를 생성합니다. 매매 로직에 대한 test를 먼저하여야 하므로 일단은 DummyExchange를 사용합니다. logic test가 끝나면 DummyExchange를 지우고 upbit 클래스를 사용하시면 됩니다.

#    upbit = MyUpbit(access, secret)
    upbit = DummyExchange('upbit', access, secret)

이 프로그램에서는 4개의 trading 변수를 사용하지만, 일반적으로 더 많은 변수를 사용합니다. 변수가 많아질수록 class에 넘겨주는 코드가 길어집니다. 그래서 tr_param라는 class를 만들었습니다. 변수가 추가될 때 마다 tr_params class에 추가하면 됩니다.

 

    # trading용 parameter 설정
    # 모든 ticker에 대하여 같은 설정 사용. 만약 ticker별로 별도 설정 값이 필요한 경우에는 ticker별로 설정 필요
    buy_perc = 0.03  # 시작가 대비 3% 오르면 매수
    sell_perc = 0.01 # 매수가 대비 1% 오르면 매도(익절)
    losscut = 0.01   # 매수가 대비 1% 내리면 losscut 매도(손절)
    seed_for_each_ticker = 10000
    
    tr_param = tr_params(buy_perc, sell_perc, losscut, seed_for_each_ticker)

trading에 사용할 ticker를 전달하는 방법은 여러가지가 있습니다. list 변수에 하나씩 넣어도 되지만, ticker가 많은 경우에는 부담이 갑니다. 따라서 별도 파일을 만들어서 원하는 ticker를 기록하도록 기능 추가하였습니다.

 

현재 거래할 ticker 명은  tr_tickers.txt 파일에 저장되어 있습니다. 

형태는 단순한 list입니다. 거래하고자하는 ticker를 추가/삭제하시면 됩니다.

 

[
	"KRW-MANA",
	"KRW-ANKR",
	"KRW-NPXS",
	"KRW-OBSR"
]

trader를 모아놓은 TraderMgr를 생성할 때 필요한 인자를 전달해주고, 매매를 위한 준비를 하는 함수인 prepare_to_start() 함수를 호출합니다. 이 함수에서는 ticker의 시작가, trading logic 등등 생성합니다.

    fname = '.\\tr_tickers.txt' 
    trader_mgr = TraderMgr(upbit, fname, tr_param)
    trader_mgr.prepare_to_start()

이번 예제에서는 REST 방식으로 10초에 한번씩 시세를 받아서 매매하는 방식으로 개발을 하였습니다.

 

        # 10초에 한번씩 최근 거래 값을 받아서 자동매매
        while(1) :
            trader_mgr.do_trading()
            time.sleep(10)

 websocket을 사용하는 경우에도 비슷한 방식으로 개발이 가능합니다.

 

새로 추가한 함수인 TraderMgr class의 do_trading()은 아래와 같습니다. trading할 ticker들의 현재 가격을 얻은 후 각 ticker별 trader에게 시세를 전달하여 매매 여부를 처리하도록 합니다. 기존 프로그램과의 차이는 main에서 특정 ticker용 trader의 do_trading() 함수를 부르는 부분이 TraderMgr class에 등록된 ticker용 trader를 찿아서 호출하는 방식으로 변경된 것입니다.

 

    # 주기적으로 call된다. 현재가를 읽어서 trading여부를 판단한다.
    def do_trading(self) :
        # trading에 사용될 tickers에 대하여 현재 가격을 받는다.
        prices = self.exchange.get_cur_price_all(self.int_tickers)
        if 'error' in prices[0]:
            print('could not get prepare_to_start::get_cur_price_all()', prices[0]['error']['message'], self.int_tickers)
            return

        if len(prices[0]) != len(self.int_tickers) :
            print("abnormal, ticker cnt is not the same ", len(prices), len(self.int_tickers))

        for ticker in self.traders :
            trader = self.traders[ticker]
            trader.do_trading(prices[0][ticker])  # 각 ticker용 trader에게 현재 시세 전달, trading 여부 처리

빠른 결과 확인을 위하여 매수 후 매수가 대비 1% 이상 오르면 익절, 매수 가격 대비 1% 이상 내리면 손절하도록 설정하였습니다.

 

    buy_perc = 0.03  # 시작가 대비 3% 오르면 매수
    sell_perc = 0.01 # 매수가 대비 1% 오르면 매도(익절)
    losscut = 0.01   # 매수가 대비 1% 내리면 losscut 매도(손절)

 

upbit 거래소 시세를 이용한 가상거래소에서 자동 매매 동작 결과입니다. 정상적으로 동작하고 있습니다. 실 거래소에서 매매는 아직 확인해보지 않았습니다. 익절 혹은 손절 후 재 매매하기 위해서는 매매 조건을 정교하게 정의하여야 합니다. 다. 자동 재매매를 위하여 매매 조건에 대하여 고민을 한 후 관련 기능도 update하도록 하겠습니다.

 

 

관련 소스코드는 아래 github에 저장되어 있습니다.

github.com/multizone-quant/system-trading-crypto-multi

반응형

설정

트랙백

댓글

개발하기 좋은 추운 주말입니다. 이럴때는 조용이 집에서 개발에 집중할 시간을 벌 수 있어서 좋은 것 같습니다.

 

websocket을 이용한 자동 매매 예제에서 매매 로직의 시초가를 입력을 하였었는데요. test 용으로는 문제가 없지만 제대로 전략을 돌리려면 본인이 원하는 시작값을 설정할 수 있어야 합니다.

 

오늘 소개할 방법은 오늘 시초가를 로직의 시작가로 설정하는 방법입니다. 오늘 시초가를 받아오는 방법은 크게 두가지가 있습니다. 하나는 오늘 일봉을 읽는 것이고, 두 번째는 현재 시세를 받는 것입니다. 현재 시세를 받으면 오늘 시초가가 들어있습니다. 

 

그리고 websocket의 경우에 이유는 정확하지 않지만 연결이 끊어지는 경우가 종종 있습니다. 또한 매매할 coin이 많으면 websocket으로 모든 코인의 실시간 체결 데이터를 받기에 부담이 생깁니다. 따라서 전 종목을 대상으로 매매할 종목을 찾는 경우에는 현실적으로 현재 시초가를 받아서 처리하는 것이 대안이 될 수 있습니다.

 

그래서 이번에는  현재 시세를 받아서 매매하는 방법도 함께 소개하도록 하겠습니다.

 

우선 현재 시세를 받아오는 방법입니다. 해당 함수는 get_cur_price_all() 입니다. 입력은 원하는 암호화폐명이 기술된 list입니다. upbit에 등록된 모든 암호화폐 코드를 입력하면 전 종목 시세를 한번에 얻어올 수 있습니다. return되는 형태는 list이며 list의 첫 번째 항목에 dict 형태로 저장되어 있습니다. dict의 key는 암호화폐명입니다. return되는 값은 아주 다양한 정보가 들어있습니다. 그 중 시작가는 'opening_price'입니다.

 

이렇게 받은 시작가격을 로직의 시작가격으로 설정하면 됩니다.

 

    ticker = 'KRW-SBD'	
	info = upbit.get_cur_price_all([ticker])
    if 'error' not in info[0] :
        start_price = info[0][ticker]['opening_price']
        
    tr_logic.set_start_price(start_price)        

다음으로는 주기적으로 현재 시세를 받아서 기존 자동매매 프로그램을 작동시키는 방법입니다.

 

앞에서 설명한 함수인 get_cur_price_all()를 사용하면 됩니다. get_cur_price_all()에서 돌려주는 자료의 형태는 websocket에서 돌려주는 형식과 틀립니다. 하지만 기존 trader를 수정하지 않고 사용하기 위하여 websocket을 사용하던 형태로 변환하여 사용하면 됩니다. 이를 위하여 만든 함수가 make_info_from_upbit_tickers()입니다. 나머지 부분은 수정할 필요없습니다. 이렇게 거래 부분은 시세와 독립적으로 개발을 하면 향후 다른 거래소 혹은 다른 방식으로 거래하는 경우에도 수정할 부분을 최소화할 수 있습니다.

 

지금 예제는 코인 하나에 대하여 동작하지만, 복수개의 코인에 대하여 동작하도록 수정할 수 있습니다. 

방법은 원하는 코인에 대하여 trader를 여러 개 만들어서 list를 만듭니다. 원하는 코인 리스트를 만든 후 get_cur_price_all()에 해당 코인명을 넣으면 됩니다. 

    USING_WEBSOCKET = 0
    # websocket 실간 시세를 이용하여 자동매매하기
    if USING_WEBSOCKET :
    	:
        :
    else :
        # 10초에 한번씩 최근 거래 값을 받아서 자동매매
        target_coins = [ticker] 
        while(1) :
            prices = upbit.get_cur_price_all(target_coins) # 원하는 코인을 list로 넣는다.
            if 'error' not in prices :
                ticker = target_coins[0]
                info = make_info_from_upbit_tickers(prices[0][ticker])
                trader.do_trading(info)

            time.sleep(10)
        

 

다음에는 ebest api를 추가하는 방법에 대하여 기술하도록 하겠습니다. 증권사 API는 암호화폐 API와는 많이 틀립니다. 하지만 매매에서 사용하는 api는 몇 개 없으므로 기존 pyupbit의 api와 같은 형태로 부를 수 있도록 개발할 예정입니다. 이렇게 되면 기존 프로그램에서 아래에 있는 거래소 생성하는 부분만 변경하면 됩니다. 거래소와 관련된 부분을 독립하여 개발을 하면 향후 다른 거래소를 추가하는 경우에도 수정할 부분을 최소화할 수 있습니다.

 

    upbit = MyUpbit(access, secret)
반응형

설정

트랙백

댓글