MCD Vaults Tracker는 Maker 프로토콜의 담보자산 별 vault에 대한 담보자산 예치 수량, deposit, withdrawl, owner 등의 data를 제공하는 사이트이다. Maker 프로토콜 내 ETH-A 예치의 시계열 자료를 찾던중 동 사이트를 발견하게 되었다.

그런데 사이트 내에서 해당 시계열 자료를 직접적으로 제공하지 않고 있으며, Vaults history 탭에서 7일 간의 과거 자료만을 다운로드 받아 이용에 불편한 점이 있다.

그러나 다행히도 해당 사이트는 API를 제공하고 있기 때문에 이를 통해 원하는 자료를 다운받을 수 있다. 이번 포스트에서는 API 이용 방법을 배워봄과 동시에 MCD vault tracker에서 ETH-A의 예치량 data를 다운받아 보는 과정을 실습한다. (API 이용 방법을 자세히 설명해준 친구에게 큰 감사를 전한다.)


MCD Vault Tracker는 API 이용에 관한 설명서를 제공한다. 해당 설명서를 통해 어떤식으로 API에 접근하고 원하는 data를 어떻게 요청해서 받을지를 알 수 있다.

아래에서는 다음과 같은 과정을 통해 API에서 ETH-A 예치량 정보를 다운받는다. 또한 아래와 같은 과정을 python class형태로 작성하여 향후 다른 분석시에 쉽게 이용할 수 있도록 한다.

  • API 서비스 이용을 위해 사이트에 사용자 등록(register)
  • 사용자 등록 정보로 사이트에 login하여 access-token 발급(한번 발급 받은 token은 60분간 유효함)
  • 발급받은 access-token을 이용하여 ETH-A vault data 등 다운로드

1. MCD Vault Tracker API 활용을 위한 MakerDaoAPI class 정의

API 이용의 첫번째 단계로 MCD Vault Tracker에 사용자 등록절차를 수행한다. MakerDaoAPI라는 class를 생성하고 그 안에 사용자를 등록하는 __register_user method를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
### Library import
import requests # HTTP를 호출할 때 이용
import pandas as pd # 향후 data 처리시 이용

### class 정의
class MakerDaoAPI:
    ## baseurl 정의, API 설명서에서 지정한 baseurl로 설정
    base = 'https://data-api.makerdao.network'
    ## class 변수 설정
    # __를 변수명 왼쪽에 적어주면 해당 변수는 선언된 클래스 안에서만 해당 이름으로 사용가능. 외부 모듈안에서는 __email, __password 등의 변수 사용이 불가
    __email = ''
    __password = ''
    __full_name =''
    __access_token = ''

    ## register_user method 정의, 설명서에서 '/vl/users/register'를 이용하여 사용자 등록하도록 되어있음
    # 설명서에 사용자 등록(register user)의 경우 POST method를 이용하도록 되어 있음.
    # POST방식은 전송해야할 데이터를 HTTP 메시지의 body에 담아서 전달하므로 body 변수안에 설명서에서 요구하는 정보를 담아야함
    def __register_user(self):
        # base url에 사용자 등록을 위한 부분 추가
        addr = self.base+'/v1/users/register'
        # body에 설명서에서 요구하는 password, email등 정보 설정
        body = {
            'password': self.__password,
            'email': self.__email,
            'full_name': self.__full_name,
        }
        # header 정의.
        # API 설명서에 body의 content-type을 'application/json'으로 전달하라고 명시되어 있음
        header = {
            'Content-Type': 'application/json'
        }
        # requests 라이브러리의 post method로 데이터 전송한 결과를 resp에 저장
        resp = requests.post(addr, json=body, headers=header)
        # post의 결과를 json형태로 프린트(post가 제대로 되어있는지 확인하기 위함) 
        print(resp.json())

다음은 사용자 등록에 사용한 email, password 등의 정보를 이용하여 사이트에 login 하고 access-token을 발급받는 __login method를 정의한다. 해당 method도 MakerDaoAPI class안에 method로 정의한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    ## login method 정의
    # 사용자등록(registration)과 마찬가지로 POST 방식으로 data를 전송
    def __login(self):
        # base url에 login을 위한 부분 추가
        addr = self.base+'/v1/login/access-token'

        # 사용자 등록시 설정한 email, password 등을 body에 정의
        body = {
            'username': self.__email,
            'password': self.__password,
        }
        # header 정의, 설명서에서 content type을 application/x-www-form-urlencoded로 할 것을 요구
        headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        }

        # requests 라이브러리의 post method로 데이터 전송한 결과를 resp에 저장 
        resp = requests.post(addr, data=body, headers=headers)
        # POST 결과를 json으로 출력하고, status_code도 출력
        print(resp.json(), resp.status_code)
        # status_code가 200이면 제대로된 응답이 온 것이고 아닌경우 오류 메시지 띄우기
        if resp.status_code != 200:
                        raise Exception('login failed')
        # access token 프린트
        print(f"access_token: {resp.json()['access_token']}")
        # access token을 반환. 이는 향후 뒤에서 정의할 __init__ 생성자에 __login method 실행으로 token값을 저장해 놓기 위함
        return resp.json()['access_token'] 

이제 발급받은 access token을 이용해서 Maker 프로토콜 내 vault들의 현재 정보를 요청하는 current_vault method와 vault들의 일자별(historical) 정보를 요청하는 vault_history method를 class 안에 정의한다. vault_history method는 Vaults history 페이지에서 확인가능한 아래와 같은 table의 raw data를 불러올 수 있게 한다. (출처: MCD Vaults Tracker)

아래 method 정의에서 세부적인 파라미터 값은 API 설명서의 ‘Read Vault History Daily’부분을 참고 하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    ## current_vault method 정의
    # limit 파라미터는 vault 자료 호출시 한번에 반환할 data의 최대량을 지정 
    def current_vault(self, limit):
        # base url에 자료 요청을 위한 부분 추가, ilk 파라미터를 ETH-A로 고정하여 ETH-A vault 자료만 요청
        addr = self.base+f'/v1/vaults/current_state?ilk=ETH-A&limit={limit}'
        # header 지정, API에 자료를 요청하기 위해서는 위에서 발급받은 token을 header에 포함해야함
        headers = {'Authorization': f"Bearer {self.__access_token}"}
        # GET 방식으로 자료를 요청
        resp = requests.get(addr, headers = headers)
        # json형태로 결과 반환
        return resp.json()

    ## vault_history method 정의
    # 세부적인 코드는 위의 current_value method와 동일
    # date_gt는 자료 조회 시작일, date_lte는 자료 조회 종료일을 의미(gt: greater than, lte: less than or equal 임에 유의) 
    def vault_history(self, limit, date_gt, date_lte):
        addr = self.base+f'/v1/experimental/vault_history_daily?ilk=ETH-A&limit={limit}&date_gt={date_gt}&date_lte={date_lte}'

        headers = {'Authorization': f"Bearer {self.__access_token}"}
        resp = requests.get(addr, headers = headers)
        return resp.json()

마지막으로 class에 생성자 method를 정의한다. 생성자 method를 이용하면 class를 호출하여 인스턴스 생성시 사용자 등록 및 login을 자동으로 수행한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    ## 생성자 정의
    # *args는 여려개의 인자(arguments)를 함수로 받을때 이용
    # **kwargs는 여러개의 인자를 딕셔너리 형태(key, value가 있는)로 전달할때 이용
    # 함수 정의시 일반 인자-*args, **kwargs 순서로 적어야함.(참고: brunch.co.kr/@princox/180)
    def __init__(self, *args, **kwargs):
        self.__email = kwargs['email']
        # get method는 dict['key']와 같이 value를 돌려주지만 존재하지 않는 key를 입력할때 오류를 내지않고 'none'을 반환
        self.__password = kwargs.get('password')
        self.__full_name = kwargs.get('full_name')

        # 확인을 위한 email, password, full_name 프린트
        print(self.__email, self.__password, self.__full_name)

        # register method 실행
        self.__register_user()
        # login method 실행 후 token을 __access_token 변수에 저장
        self.__access_token = self.__login()

이제 MakerDaoAPI class를 실행하여 제대로 작동하는지 여부를 확인하는 test code를 작성하고 작동시켜본다. 작동 여부 확인은 class를 정의한 python 파일에서 수행하며 다른 분석에 동 class를 활용할 때는 import MakerDaoAPI 로 class를 호출하여 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
## class 작동여부 확인을 위한 test code
# 앞서 정의한 class가 독립적으로 실행되었다면 __name__변수는 __main__이라는 문자를 바인딩하지만 다른 파일에 임포트된 경우에는 자신의 파일명을 바인딩한다.
# 즉, 다른 파일에서 해당 class를 호출하면 __name__변수가 파일명을 바인딩하기 때문에 아래와 같은 test code는 작동하지 않는다. 
if __name__=='__main__':
    # kwargs에 사용자 등록변수 설정
    kwargs = {
        'email': 'aacd123bf@naver.com',
        'password': '1234',
        'full_name': 'user_aaa',
    }

    # api라는 인스턴스 생성
    api = MakerDaoAPI(**kwargs)
    print(api.current_vault(1))

# class 실행결과
aacd123bf@naver.com 1234 user_aaa
{'email': 'aacd123bf@naver.com', 'is_active': True, 'is_superuser': False, 'full_name': 'user_aaa', 'id': 34}
{'access_token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDk2OTQ2MDEsInN1YiI6IjM0In0.0xoPyMswnq5W9Hjeyjqk_VVc56JdcWWpdHYq5zbJjog', 'token_type': 'bearer'} 200
access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDk2OTQ2MDEsInN1YiI6IjM0In0.0xoPyMswnq5W9Hjeyjqk_VVc56JdcWWpdHYq5zbJjog
[{'vault': '0x007a449f', 'ilk': 'ETH-A', 'collateral': 0.0, 'principal': 0.0, 'paid_fees': 50.693006, 'debt': 0.0, 'accrued_fees': 0.0, 'osm_price': 3040.0, 'mkt_price': 3039.6210911565927, 'ratio': 1.45, 'liquidation_price': None, 'available_debt': 0.0, 'available_collateral': 0.0, 'owner': None, 'ds_proxy': None, 'urn': '0x007a449f73ffdb1ca215572062c653d119c02c65', 'art': 0, 'block_created': 11080251, 'time_created': '2020-10-18T13:43:12', 'last_block': 14565131, 'last_time': '2022-04-11T15:12:48'}]

test code의 실행 결과 사용자 등록정보, access token이 print되며 현재 상태의 vault 1개 자료가 제대로 반환되었음을 알 수 있다.

2. MakerDaoAPI class를 활용한 일자별 ETH-A vault data 다운로드

앞서 정의한 MakerDaoAPI class중 vault_history method를 이용하여 일자별 ETH-A vault data를 다운로드해본다.

MakerDaoAPI class는 mcdvaultapi.py라는 python 파일로 저장해두었으므로 분석을 위한 python 파일에서 해당 class를 이용하기 위에서는 동 class를 import해와야한다. 이때 class 파일의 test code는 작동하지 않음에 유의한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# importing class and library

import mcdvaultapi
import pandas as pd

## 사용자 등록을 위한 정보 설정(실제 사용중인 메일계정을 입력하지 않아도 무방)
kwargs = {
        'email': 'acafdddbgg@naver.com',
        'password': '1234',
        'full_name': 'user_aaa',
    }

## MakerDaoAPI class의 인스턴스(vault) 생성 
vault = mcdvaultapi.MakerDaoAPI(**kwargs)

## vault 인스턴스의 vault_history method를 이용하여 일자별 history data저장
## 불러오는 데이터 수를 10개로 제한하고 2022.4.18일 데이터만 불러오기 위해 인자를 아래와 같이 설정.
data = vault.vault_history(10, '2022-04-17', '2022-04-18')

## data 결과 확인(dictionary 형태)
>>> data[0]
{'day': '2022-04-18', 'vault': '0x0a66f704', 'ilk': 'ETH-A', 'collateral_eod': 0.0, 'principal_eod': 0.0, 'debt_eod': 4.547473508864641e-13, 'fees_eod': 1.2350090625805024, 'withdraw': 0.0, 'deposit': 0.0, 'principal_generate': 0.0, 'principal_payback': 0.0, 'debt_generate': 0.0, 'debt_payback': 0.0, 'fees': 0.0}

## data 전처리를 위해 data를 dataframe형태로 변환
df = pd.DataFrame(data)

## 2022.4.18일 기준 'collateral_eod', 'withdraw', 'deposit' 자료만을 추출하여 합계 구하기
df = df.pivot_table(df[['collateral_eod', 'withdraw', 'deposit']], 'day', aggfunc = 'sum) 

## df 결과
            collateral_eod  deposit  withdraw
day                                          
2022-04-18        84.82216      0.0       0.0

Leave a comment