upbit api를 이용하여 매수/매도 주문을 넣어보자.

안녕하세요.

 

upbit api를 활용하여 간단히 매수 / 매도 주문을 넣는 예제를 작성해보겠습니다.

 

자동매매의 가장 기본이 되는 기능일텐데 우선 API 문서 및 예제 코드를 통해 소스분석을 해보겠습니다.

 


제가 보고있는 API 레퍼런스는 아래의 주소에 해당하는 API 입니다.

 

https://docs.upbit.com/reference/%EC%A3%BC%EB%AC%B8%ED%95%98%EA%B8%B0

 

업비트 개발자 센터

업비트 Open API 사용을 위한 개발 문서를 제공 합니다.업비트 Open API 사용하여 다양한 앱과 프로그램을 제작해보세요.

docs.upbit.com

 

 

주문 요청 파라미터

market은 어떤 종목을 주문할 것인지에 대한 정보입니다. 처음에는 KRW나 BTC, USDT처럼 거래단위인줄 알았는데 주식으로 치면 종목코드로 보면 될 것 같습니다.

해당 마켓 코드는 https://api.upbit.com/v1/market/all?isDetails=false 전체 코드를 여기서 확인하실 수 있습니다. 별도로 토큰이 필요없는 api라 브라우저에서도 확인이 가능합니다.

 

side는 bid(매도) / ask(매수) 타입으로 갈립니다. 여기까진 쉽네요.

 

다음에는 ord_type을 먼저 설명하겠습니다. 제 전략은 시장가 매수/매도 만을 활용하고자 합니다. 스켈핑 초단타를 목적으로 자동매매 시스템을 개발할 것이기 때문에 지정가는 사용하지 않을 옵션입니다. 따라서 여기서 시장가란 현재 비트코인의 가격이 요동치고 있을때 호가창에 머무는 순간의 가격에 매수/매도 주문을 요청하는 것이고, 지정가란 매수/매도자가 지정된 가격으로 주문을 요청하는 것입니다.

 

volumeprice에 대해서 제 견해와 다른 분들의 사용처에 따라 기능정의가 다를것 같습니다. 우선 저는 지정가주문(ord_type : limit)을 활용하지 않을 생각이기 때문에 매도시 volume 값만을 보내고 매수시 price 값만을 보낼 것입니다. 

 


예제코드를 확인해보겠습니다.

 

해당 예제코드는 테스트 메소드에서 실행하였습니다. 별도로 main 메소드로 돌려도 상관없습니다만 스프링 환경에서 진행하고자 테스트 코드로 실행하였습니다.

    @Test
    void contextLoads() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        주문예제();
    }

 

    private void 주문예제() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        주문하기(null, "6000", BID); // 6000KRW 매수 후

        List<AccountsResult> accountsResults = 전체계좌조회();
        Optional<String> btcBalance = accountsResults.stream()
                .filter(result -> result.getCurrency().equals("BTC"))
                .map(AccountsResult::getBalance)
                .findFirst();

        if (btcBalance.isPresent()) {
            주문하기(btcBalance.get(), null, ASK); // 전량 매도
        }
    }
  • 제 예제 소스의 시나리오는 6,000KRW의 비트코인을 매수 후, 전체 계좌를 조회하여 비트코인의 balance(잔량) 값을 확보한 후, 전량매도를 하는 것입니다.
  • 6,000KRW로 진행하는 것은 매수 후 수수료 0.05%에 의해 최소 매도 가치인 5,000KRW을 하회하기 때문입니다.

 

전체계좌조회() 해당 메소드의 구현부는 https://sas-study.tistory.com/430 해당 포스팅에 정리되어 있습니다.

 

주문하기()

    private void 주문하기(String volume, 
                        String price, 
                        OrdSideType ordSideType) throws NoSuchAlgorithmException, UnsupportedEncodingException {
                        
        HashMap<String, String> params = new HashMap<>();
        params.put("market", "KRW-BTC");
        params.put("side", ordSideType.getType());
        switch (ordSideType) {
            case ASK:
                // 매도
                params.put("volume", volume); // 매도수량 필수
                params.put("ord_type", OrdType.MARKET.getType()); // 시장가 매도
                break;
            case BID:
                // 매수
                params.put("price", price); // 매수 가격
                params.put("ord_type", OrdType.PRICE.getType()); // 시장가 매수
                break;
            default:
                return;
        }

        ArrayList<String> queryElements = new ArrayList<>();
        for(Map.Entry<String, String> entity : params.entrySet()) {
            queryElements.add(entity.getKey() + "=" + entity.getValue());
        }

        String queryString = String.join("&", queryElements.toArray(new String[0]));

        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update(queryString.getBytes("UTF-8"));

        String queryHash = String.format("%0128x", new BigInteger(1, md.digest()));

        Algorithm algorithm = Algorithm.HMAC256(secretKey);
        String jwtToken = JWT.create()
                .withClaim("access_key", accessKey)
                .withClaim("nonce", UUID.randomUUID().toString())
                .withClaim("query_hash", queryHash)
                .withClaim("query_hash_alg", "SHA512")
                .sign(algorithm);

        String authenticationToken = "Bearer " + jwtToken;

        try {
            HttpClient client = HttpClientBuilder.create().build();
            HttpPost request = new HttpPost(serverUrl + "/v1/orders");
            request.setHeader("Content-Type", "application/json");
            request.addHeader("Authorization", authenticationToken);
            request.setEntity(new StringEntity(JsonUtil.toJson(params)));

            HttpResponse response = client.execute(request);
            HttpEntity entity = response.getEntity();

            System.out.println(EntityUtils.toString(entity, "UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

모든 소스는 Upbit API 레퍼런스 페이지에 있는 예제 소스를 기반으로 작성하였습니다.

눈여겨 보실부분은 일단 매수/매도 타입(side) 에 따라 price, volume 파라미터를 다르게 셋팅했다는 점인데요. 많은 점을 보완해야하겠지만 이정도로 셋팅을 해놓는다면 시장가 주문에 대한 처리는 어느정도 충분하다고 생각합니다.

 

그리고 혹시 만약 업비트에서의 50%, 25%, 10% 매도 이런 기능을 개발하고 싶으시다면

balance * 0.5 / balance * 0.25 / balance * 0.1

이렇게 할 수도 있겠네요. 업비트 인터페이스보다 좀더 유연하게 30%, 60%까지도 구현이 가능하지 않을까 싶습니다. 


 

이정도로는 택도 없겠지만 사실 어느정도 input과 output이 결정이 났고 이제 중간 인터페이스를 개발해야하는데,, 자동투자는 이게 제일 어렵습니다. 바로 어떤 알고리즘으로 매수를 할지, 매도를 할지 두가지를 결정하는 방정식을 개발해야하는데요. 이 부분이 많이 고민되는 영역입니다. 천천히 고민은 하되 우선 기본기능부터 얼른 만들어 놓고 농도깊은 고민을 해보고 싶습니다.

댓글

Designed by JB FACTORY