본문 바로가기
업비트 자동매매 만들기

2022.03.01부터 적용되는 Upbit Open API 변경사항

by 코코코인 2022. 2. 23.
728x90

 

참고 : https://docs.upbit.com/changelog/open-api-%EB%B3%80%EA%B2%BD%EC%82%AC%ED%95%AD-%EC%95%88%EB%82%B4


안녕하세요 블로그 주인입니다.

2022년 03월 01일부터 API변경사항이 있습니다.

 

Upbit Open API 변경사항

 

"업비트 자동매매 만들기"의 소스에서

주문하기, 취소주문, 주문수정 등(=Post)을 서버로 데이터를 보낼경우 

기존에는 key1=value1&key2=value2.... (=Query String)이런 형식으로 서버로 데이터를 보냈지만, 

앞으로는 Json Body로만 받겠다는 의미입니다.

 

아래의 수정사항을 간단하게 설명하면,

Post함수를 만든 후, 인증 토큰은 Query String형태로 만들고,

Dictionary<string, string> 형태로 받은 데이터를 JsonConvert.SerializeObject( object )함수로
Json형태로 바꿔주어

request.AddJsonBody로 추가해줍니다.

 

  • 수정사항1  
    UpbitAPI - Param.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using RestSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace upbit.UpbitAPI {
    public class Param {
        private string upbitAccessKey;
        private string upbitSecretKey;

        private DateTime dt_1970_01_01;   //timestamp를 계산하기 위한 변수
        private const string baseUrl = "https://api.upbit.com";


        public Param(string upbitAccessKey, string upbitSecretKey) {
            //APIClass에서 받은 키를 입력
            this.upbitAccessKey = upbitAccessKey;
            this.upbitSecretKey = upbitSecretKey;

            this.dt_1970_01_01 = new DateTime(1970, 1, 1);
        }
        /*------------------------------  수정부분 시작 ------------------------------*/
        public string Post(string path, Dictionary<string, string> parameters, Method method) {
            // POST, DELETE, UPDATE 방식을 포함하는듯?

            StringBuilder queryStringSb = GetQueryString(parameters);
            var tokenSb = JWT_param(queryStringSb.ToString()); // 입력받은 변수를 JWT토큰으로 변환
            var token = tokenSb.ToString();

            var client = new RestClient(baseUrl);
            var request = new RestRequest(path, method);

            // JsonConvert.SerializeObject(parameters);  // dictionary to Json
            request.AddJsonBody(  JsonConvert.SerializeObject(parameters)   );  // add Json to body
            request.AddHeader("Content-Type", "application/json");
            request.AddHeader("Authorization", token);

            var response = client.Execute(request);

            try {
                if (response.IsSuccessful) {
                    return response.Content;
                }
                else {
                    return null;
                }
            }
            catch {
                return null;
            }

        }
        /*------------------------------  수정부분 끝    ------------------------------*/
        public string Get(string path, Dictionary<string, string> parameters, Method method) {

            StringBuilder queryStringSb = GetQueryString(parameters);

            var tokenSb = JWT_param(queryStringSb.ToString()); // 입력받은 변수를 JWT토큰으로 변환
            var token = tokenSb.ToString();                            

            queryStringSb.Insert(0, "?");      // 링크에 ?를 붙임으로 파라미터를 사용한다는 의미
            queryStringSb.Insert(0, path);

            // 여기까지오면 queryString는
            // '/path?key1=value1&key2=value2 ....' 이러한 형태가 된다. 이것을 RestRequest에 넣어주면 된다.

            var client = new RestClient(baseUrl);       // RestSharp 클라이언트 생성
            var request = new RestRequest(queryStringSb.ToString(), method);
            request.AddHeader("Content-Type", "application/json");
            request.AddHeader("Authorization", token);

            queryStringSb.Clear(); queryStringSb = null;
            tokenSb.Clear(); tokenSb = null;
            parameters.Clear(); parameters = null;

            var response = client.Execute(request);
            
            try {
                if (response.IsSuccessful) {
                    return response.Content;
                }
                else {
                    return null;
                }
            }
            catch {
                return null;
            }

        }
        public StringBuilder GetQueryString(Dictionary<string, string> parameters) {
            // Dictionary 형태로 받은 key = value 형태를 
            // ?key1=value1&key2=value2 ... 형태로 만들어줌
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> pair in parameters) {
                builder.Append(pair.Key).Append("=").Append(pair.Value).Append("&");
            }

            if (builder.Length > 0) {
                builder.Length = builder.Length - 1; // 마지막 &를 제거하기 위함.
            }
            return builder;
        }
        public StringBuilder JWT_param(string queryString) {

            SHA512 sha512 = SHA512.Create();
            byte[] queryHashByteArray = sha512.ComputeHash(Encoding.UTF8.GetBytes(queryString));
            string queryHash = BitConverter.ToString(queryHashByteArray).Replace("-", "").ToLower();

            TimeSpan diff = DateTime.Now - dt_1970_01_01;
            var nonce = Convert.ToInt64(diff.TotalMilliseconds);

            var payload = new JwtPayload
                    {
                        { "access_key", this.upbitAccessKey },
                        { "nonce", nonce  },
                        { "query_hash", queryHash },
                        { "query_hash_alg", "SHA512" }
                    };

            byte[] keyBytes = Encoding.Default.GetBytes(this.upbitSecretKey);
            var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyBytes);
            var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "HS256");
            var header = new JwtHeader(credentials);
            var secToken = new JwtSecurityToken(header, payload);

            var jwtToken = new JwtSecurityTokenHandler().WriteToken(secToken);

            StringBuilder returnStr = new StringBuilder();
            returnStr.Append("Bearer "); // 띄어쓰기 한칸 있어야함 주의!
            returnStr.Append(jwtToken);

            return returnStr;
        }


    }
}

 

 

 

 

 

 

 

 

 

 

 

  • 수정사항2
    UpbitAPI - APIClass.cs
    주문 관련된 함수에서 Param.Get → Param.Post로 변경
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using upbit.UpbitAPI.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace upbit.UpbitAPI {

    public class APIClass {

        private Param param;
        private NoParam noparam;

        public APIClass(string upbitAccessKey, string upbitSecretKey) {
            param = new Param(upbitAccessKey, upbitSecretKey);
            noparam = new NoParam(upbitAccessKey, upbitSecretKey);
        }
        /*--------------------- EXCHANGE API ---------------------*/
        public List<Account> GetAccount() {
            // 자산 - 전체 계좌 조회
            var data = noparam.Get("/v1/accounts", RestSharp.Method.GET);
            if (data != null ) {
                return JsonConvert.DeserializeObject<List<Account>>(data);
            }
            else {
                return null;
            }
        }
        public OrderChance GetOrderChance(string market) {
            // 주문 - 주문 가능 정보
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add( "market", market );
            var data = param.Get("/v1/orders/chance", parameters, RestSharp.Method.GET);
            if ( data != null  ) {
                return JsonConvert.DeserializeObject<OrderChance>(data);
            }
            else {
                return null;
            }
        }
        public Order GetOrder(string uuid) {
            // 주문 - 개별 주문 조회
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("uuid", uuid);
            var data = param.Get("/v1/order", parameters, RestSharp.Method.GET);
            if (data != null) {
                return JsonConvert.DeserializeObject<Order>(data);
            }
            else {
                return null;
            }
        }
        
        public CancelOrder CancelOrder(string uuid) {
            // 주문 - 주문 취소 접수
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("uuid", uuid);
            var data = param.Post("/v1/order", parameters, RestSharp.Method.DELETE);
            if ( data != null ) {
                return JsonConvert.DeserializeObject<CancelOrder>(data);
            }
            else {
                return null;
            }
        }
        public MakeOrder MakeOrderLimit(string market, OrderSide orderSide, double volume, double price ) {
            // 주문 - 주문하기 - 지정가 매수&매도
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("side", orderSide.ToString());
            parameters.Add("volume", volume.ToString());
            parameters.Add("price", price.ToString());
            parameters.Add("ord_type", "limit");

            var data = param.Post("/v1/orders", parameters, RestSharp.Method.POST);
            if ( data != null ) {
                return JsonConvert.DeserializeObject<MakeOrder>(data);
            }
            else {
                return null;
            }
        }
        public MakeOrderMarketBuy MakeOrderMarketBuy(string market, double price ) {
            // 주문 - 주문하기 - 시장가매수

            /* 주문 가격. (지정가, 시장가 매수 시 필수)
            ex) KRW-BTC 마켓에서 1BTC당 1,000 KRW로 거래할 경우, 값은 1000 이 된다.
            ex) KRW-BTC 마켓에서 1BTC당 매도 1호가가 500 KRW 인 경우,
            시장가 매수 시 값을 1000으로 세팅하면 2BTC가 매수된다.
            (수수료가 존재하거나 매도 1호가의 수량에 따라 상이할 수 있음)  
            --> 결론 : price는 원화가치인듯 */

            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("side", OrderSide.bid.ToString());
            parameters.Add("price", price.ToString());
            parameters.Add("ord_type", "price");
            var data = param.Post("/v1/orders", parameters, RestSharp.Method.POST);

            if (data != null) {
                return JsonConvert.DeserializeObject<MakeOrderMarketBuy>(data);
            }
            else {
                return null;
            }
           
        }
        public MakeOrderMarketSell MakeOrderMarketSell(string market, double volume ) {
            // 주문 - 주문하기 - 시장가매도
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("side", OrderSide.ask.ToString() );
            parameters.Add("volume", volume.ToString());
            parameters.Add("ord_type", "market");
            var data = param.Post("/v1/orders", parameters, RestSharp.Method.POST);
            if (data != null) {
                return JsonConvert.DeserializeObject<MakeOrderMarketSell>(data);
            }
            else {
                return null;
            }
        }


        /*--------------------- QUOTATION API ---------------------*/

        public List<MarketAll> GetMarketAll() {
            // 시세 종목 조회 - 마켓 코드 조회
            var data = noparam.Get("/v1/market/all", RestSharp.Method.GET);
            return JsonConvert.DeserializeObject<List<MarketAll>>(data);

        }
        public List<CandleMinute> GetCandleMinutes( string market, MinuteUnit unit, DateTime to=default(DateTime) , int count = 1 ) {
            // 시세 캔들 조회 - 분(Minute) 캔들
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("to", to.ToString("yyyy-MM-dd HH:mm:ss"));
            parameters.Add("count", count.ToString());
            var data = param.Get(String.Join("", "/v1/candles/minutes/", (int)unit), parameters, RestSharp.Method.GET);

            if (data != null) {
                return JsonConvert.DeserializeObject<List<CandleMinute>>(data);
            }
            else {
                return null;
            }

        }

        public List<CandleDay> GetCandleDays(string market,  DateTime to = default(DateTime), int count = 1) {
            // 시세 캔들 조회 - 일(Day) 캔들
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("to", to.ToString("yyyy-MM-dd HH:mm:ss"));
            parameters.Add("count", count.ToString());
            var data = param.Get("/v1/candles/days", parameters, RestSharp.Method.GET);

            if (data != null) {
                return JsonConvert.DeserializeObject<List<CandleDay>>(data);
            }
            else {
                return null;
            }
        }
        public List<CandleWeek> GetCandleWeeks(string market, DateTime to = default(DateTime), int count = 1) {
            // 시세 캔들 조회 - 주(Week) 캔들
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("to", to.ToString("yyyy-MM-dd HH:mm:ss"));
            parameters.Add("count", count.ToString());
            var data = param.Get("/v1/candles/weeks", parameters, RestSharp.Method.GET);

            if (data != null) {
                return JsonConvert.DeserializeObject<List<CandleWeek>>(data);
            }
            else {
                return null;
            }

        }
        public List<CandleMonth> GetCandleMonths(string market, DateTime to = default(DateTime), int count = 1) {
            // 시세 캔들 조회 - 월(Month) 캔들
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("market", market);
            parameters.Add("to", to.ToString("yyyy-MM-dd HH:mm:ss"));
            parameters.Add("count", count.ToString());
            var data = param.Get("/v1/candles/months", parameters, RestSharp.Method.GET);
            if (data != null) {
                return JsonConvert.DeserializeObject<List<CandleMonth>>(data);
            }
            else {
                return null;
            }
        }

        public List<Ticker> GetTicker(string markets) {
            // 시세 Ticker조회 - 현재가정보
            // market을 콤마로 구분하여 입력한다. 
            // ex) "KRW-BTC, KRW-ETH, ....."
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("markets", markets);
            var data = param.Get("/v1/ticker", parameters, RestSharp.Method.GET);
            
            if (data != null) {
                return JsonConvert.DeserializeObject<List<Ticker>>(data);
            }
            else {
                return null;
            }
            
           
        }
        public List<OrderBook> GetOrderBook(string markets) {
            // 시세 호가 정보 조회 - 호가 정보 조회
            // market을 콤마로 구분하여 입력한다. 
            // ex) "KRW-BTC, KRW-ETH, ....."
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            parameters.Add("markets", markets);
            var data = param.Get("/v1/orderbook", parameters, RestSharp.Method.GET);
            
            if (data != null) {
                return JsonConvert.DeserializeObject<List<OrderBook>>(data);
            }
            else {
                return null;
            }

        }

        public enum OrderSide {
            bid,    // 매수
            ask     // 매도
        }
        public enum MinuteUnit {
            _1 = 1, 
            _3 = 3,
            _5 = 5,
            _10 = 10,
            _15 = 15,
            _30 = 30,
            _60 = 60,
            _240 = 240

        }


    }

    

}

 

 

 

댓글