├── LICENSE ├── README.md ├── pybithumb ├── __init__.py ├── client.py ├── core.py ├── util.py └── websocket.py ├── requirements.txt └── setup.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bithumb-api 2 | Python Wrapper for Bithumb API 3 | 4 | ## Installation 5 | ```sh 6 | pip install pybithumb 7 | ``` 8 | 9 | ## Import 10 | ```python 11 | from pybithumb import Bithumb 12 | ``` 13 | 14 | ## Public API 15 | #### 암호화폐 목록 16 | 빗썸이 지원하는 암호화폐 목록을 얻어온다. 17 | ```python 18 | print(Bithumb.get_tickers()) 19 | ``` 20 | ``` 21 | ['BTC', 'ETH', 'DASH', 'LTC', 'ETC', 'XRP', 'BCH', 'XMR', 'ZEC', 'QTUM', 'BTG', 'EOS', 'ICX', 'TRX', 'ELF', 'MCO', 'OMG', 'KNC', 'GNT', 'ZIL', 'WAXP', 'POWR', 'LRC', 'STEEM', 'STRAT', 'AE', 'ZRX', 'REP', 'XEM', 'SNT', 'ADA', 'CTXC', 'BAT', 'WTC', 'THETA', 'LOOM', 'WAVES', 'ITC', 'TRUE', 'LINK', 'RNT', 'ENJ', 'PLX', 'VET', 'MTL', 'INS', 'IOST', 'TMTG', 'QKC', 'BZNT', 'HDAC', 'NPXS', 'LBA', 'WET', 'AMO', 'BSV', 'APIS', 'DAC', 'ORBS', 'VALOR', 'CON', 'ANKR', 'MIX', 'LAMB', 'CRO', 'FX', 'CHR', 'MBL', 'MXC', 'FAB', 'OGO', 'DVP', 'FCT', 'FNB', 'FZZ', 'TRV', 'PCM', 'DAD', 'AOA', 'XSR', 'WOM', 'SOC', 'EM', 'QBZ', 'BOA', 'WPX', 'FLETA', 'BNP', 'SXP', 'COS', 'EL', 'BASIC', 'HC', 'BCD', 'XVG', 'XLM', 'PIVX', 'GXC', 'BHP', 'BTT', 'HYC', 'VSYS', 'IPX', 'WICC', 'LUNA', 'AION', 'COSM'] 22 | ``` 23 | 24 | #### 최근 체결가격 25 | get_current_price 함수는 최근 체결 가격을 조회한다. 26 | ```python 27 | for coin in Bithumb.get_tickers()[:5]: 28 | print(coin, Bithumb.get_current_price(coin)) 29 | ``` 30 | ``` 31 | BTC 8190000.0 32 | ETH 171900.0 33 | DASH 82450.0 34 | LTC 48810.0 35 | ETC 6180.0 36 | ``` 37 | 38 | #### 시장 현황 상세정보 39 | get_market_detail 함수는 00시 기준으로 시가/고가/저가/종가/거래량 정보를 반환한다. 40 | ```python 41 | for coin in Bithumb.get_tickers(): 42 | print(coin, Bithumb.get_market_detail(coin)) 43 | ``` 44 | ``` 45 | BTC (8162000.0, 8240000.0, 8050000.0, 8190000.0, 3117.77267769) 46 | ETH (170500.0, 172600.0, 168000.0, 171500.0, 34816.2510681) 47 | DASH (81950.0, 82700.0, 80600.0, 82450.0, 1651.47875125) 48 | LTC (48440.0, 49410.0, 48270.0, 48810.0, 3344.70736905) 49 | ETC (6150.0, 6250.0, 6075.0, 6180.0, 77986.83409064) 50 | ``` 51 | #### 매수/매도 호가 52 | get_orderbook 함수는 호가 정보를 가져온다. 53 | 기본적으로 5개를 가져오며, limit 파라미터로 30개까지 지정할 수 있다. 54 | ```python 55 | for coin in Bithumb.get_tickers(): 56 | print(coin, Bithumb.get_orderbook(coin)) 57 | ``` 58 | 59 | #### 시간별 가격정보 60 | 시가/종가/고가/저가/거래량 정보를 DataFrame으로 반환한다. 61 | ```python 62 | df = Bithumb.get_candlestick("BTC") 63 | print(df.tail(5)) 64 | ``` 65 | 66 | ```python 67 | open close high low volume 68 | time 69 | 2020-03-30 15:00:00 7740000.0 7848000.0 8019000.0 7683000.0 7913.696718 70 | 2020-03-31 15:00:00 7847000.0 7630000.0 7893000.0 7534000.0 5163.670206 71 | 2020-04-01 15:00:00 7633000.0 8194000.0 8216000.0 7569000.0 9123.583777 72 | 2020-04-02 15:00:00 8193000.0 8162000.0 8499000.0 8057000.0 11354.950247 73 | 2020-04-04 08:00:00 8162000.0 8177000.0 8240000.0 8050000.0 3082.263414 74 | ``` 75 | 76 | chart_intervals 파라미터로 조회 간격을 조정할 수 있다. 77 | - 1m, 3m, 5m, 10m, 30m, 1h, 6h, 12h, 24h 78 | 79 | ```python 80 | df = Bithumb.get_candlestick("BTC", chart_intervals="30m") 81 | print(df.tail(5)) 82 | ``` 83 | 84 | ### 웹소켓 85 | WebSocket을 이용해서 `현재가`, `호가`, `체결`에 대한 정보를 수신한다. 86 | - 첫 번째 파라미터로 수신정보를 입력하며 `ticker`, `orderbook`, `transaction`을 사용할 수 있다. 87 | - 두 번째 파라미터는 구독할 필터를 설정하며 암호화폐의 티커를 입력한다. 현재 버전에서는 원화 시장만을 지원한다. 88 | 89 | ```python 90 | if __name__ == "__main__": 91 | wm = WebSocketManager("ticker", ["BTC_KRW"]) 92 | for i in range(10): 93 | data = wm.get() 94 | print(data) 95 | wm.terminate() 96 | ``` 97 | 주의할 사항은 multiprocessing을 위해 `__name__` guard를 반드시 써줘야한다는 것이다. 98 | 99 | PyQt5와 함께 웹소켓을 사용하는 예제는 다음과 같다. 100 | - 버튼을 클릭하면 웹소켓에서 가격정보를 가져와서 화면에 출력한다. 101 | - https://gist.github.com/mr-yoo/a3d1f8a4152f94cf61e4bc566659cd20 102 | 103 | 104 | ## Private API 105 | #### 로그인 106 | connectkey와 secretkey를 사용해서 로그인한다. 107 | 두 key를 생성하는 방법은 [링크](http://sharebook.kr/x/ZQov)를 참조한다. 108 | ```python 109 | bithumb = Bithumb("conkey", "seckey") 110 | ``` 111 | 112 | #### 수수료 조회 113 | ```python 114 | print(bithumb.get_trading_fee()) 115 | ``` 116 | 117 | #### 잔고 조회 118 | ```python 119 | for coin in Bithumb.get_tickers(): 120 | print(coin, bithumb.get_balance(coin)) 121 | ``` 122 | 123 | #### 매수/매도 주문 124 | 비트코인을 1100만원에 1개 매수/매도한다. 125 | ```python 126 | desc = bithumb.buy_limit_order("BTC", 11000000, 1) 127 | desc = bithumb.sell_limit_order("BTC", 11000000, 1) 128 | ``` 129 | 130 | #### 매수/매도 잔량 확인 131 | ```python 132 | quanity = bithumb.get_outstanding_order(desc) 133 | ``` 134 | 135 | #### 매수/매도 주문 취소 136 | ```python 137 | status = bithumb.cancel_order(desc) 138 | ``` 139 | 140 | -------------------------------------------------------------------------------- /pybithumb/__init__.py: -------------------------------------------------------------------------------- 1 | from pybithumb.client import Bithumb 2 | from .websocket import WebSocketManager 3 | 4 | def get_ohlc(order_currency, payment_currency="KRW"): 5 | return Bithumb.get_ohlc(order_currency, payment_currency) 6 | 7 | 8 | def get_tickers(payment_currency="KRW"): 9 | return Bithumb.get_tickers(payment_currency) 10 | 11 | 12 | def get_market_detail(order_currency, payment_currency="KRW"): 13 | return Bithumb.get_market_detail(order_currency, payment_currency) 14 | 15 | 16 | def get_current_price(order_currency, payment_currency="KRW"): 17 | return Bithumb.get_current_price(order_currency, payment_currency) 18 | 19 | 20 | def get_orderbook(order_currency, payment_currency="KRW", limit=5): 21 | return Bithumb.get_orderbook(order_currency, payment_currency, limit) 22 | 23 | 24 | def get_transaction_history(order_currency, payment_currency="KRW", limit=20): 25 | return Bithumb.get_transaction_history(order_currency, payment_currency, limit) 26 | 27 | def get_candlestick(order_currency, payment_currency="KRW", chart_intervals="24h"): 28 | return Bithumb.get_candlestick(order_currency, payment_currency, chart_intervals) 29 | 30 | # @util.deprecated('Please use get_candlestick() function instead of get_ohlcv().') 31 | def get_ohlcv(order_currency="BTC", payment_currency="KRW", interval="day"): 32 | # for backward compatibility 33 | chart_instervals = { 34 | "day": "24h", 35 | "hour12": "12h", 36 | "hour6": "6h", 37 | "hour": "1h", 38 | "minute30": "30m", 39 | "minute10": "10m", 40 | "minute5": "5m", 41 | "minute3": "3m", 42 | "minute1": "1m", 43 | }[interval] 44 | 45 | return Bithumb.get_candlestick(order_currency, payment_currency, chart_instervals) -------------------------------------------------------------------------------- /pybithumb/client.py: -------------------------------------------------------------------------------- 1 | from pybithumb.core import * 2 | from pandas import DataFrame 3 | import pandas as pd 4 | import datetime 5 | import math 6 | 7 | 8 | class Bithumb: 9 | def __init__(self, conkey, seckey): 10 | self.api = PrivateApi(conkey, seckey) 11 | 12 | @staticmethod 13 | def _convert_unit(unit): 14 | try: 15 | unit = math.floor(unit * 10000) / 10000 16 | return unit 17 | except: 18 | return 0 19 | 20 | @staticmethod 21 | def get_tickers(payment_currency="KRW"): 22 | """ 23 | 빗썸이 지원하는 암호화폐의 리스트 24 | :param payment_currency : KRW 25 | :return: 26 | """ 27 | resp = None 28 | try: 29 | resp = PublicApi.ticker("ALL", payment_currency) 30 | data = resp['data'] 31 | tickers = [k for k, v in data.items() if isinstance(v, dict)] 32 | return tickers 33 | except Exception: 34 | return resp 35 | 36 | @staticmethod 37 | def get_ohlc(order_currency, payment_currency="KRW"): 38 | """ 39 | 최근 24시간 내 암호 화폐의 OHLC의 튜플 40 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 41 | :param payment_currency : KRW 42 | :return : 코인과 (시가, 고가, 저가, 종가) 가 딕셔너리로 저장 43 | { 44 | 'BTC' : (7020000.0, 7093000.0, 6810000.0, 6971000.0) 45 | 'ETH' : ( 720000.0, 703000.0, 681000.0, 697000.0) 46 | } 47 | """ 48 | resp = None 49 | try: 50 | resp = PublicApi.ticker(order_currency, payment_currency)['data'] 51 | if order_currency == "ALL": 52 | del resp['date'] 53 | data = {} 54 | for key in resp: 55 | data[key] = ( 56 | resp[key]['opening_price'], resp[key]['max_price'], 57 | resp[key]['min_price'], resp[key]['closing_price']) 58 | return data 59 | 60 | return { 61 | order_currency: ( 62 | float(resp['opening_price']), float(resp['max_price']), 63 | float(resp['min_price']), 64 | float(resp['closing_price'])) 65 | } 66 | except Exception: 67 | return resp 68 | 69 | @staticmethod 70 | def get_market_detail(order_currency, payment_currency="KRW"): 71 | """ 72 | 거래소 세부 정보 조회 (00시 기준) 73 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 74 | :param payment_currency : KRW 75 | :return : (시가, 고가, 저가, 종가, 거래량) 76 | """ 77 | resp = None 78 | try: 79 | resp = PublicApi.ticker(order_currency, payment_currency) 80 | open = resp['data']['opening_price'] 81 | high = resp['data']['max_price'] 82 | low = resp['data']['min_price'] 83 | close = resp['data']['closing_price'] 84 | volume = resp['data']['units_traded'] 85 | return float(open), float(high), float(low), float(close), float(volume) 86 | except Exception: 87 | return resp 88 | 89 | @staticmethod 90 | def get_current_price(order_currency, payment_currency="KRW"): 91 | """ 92 | 최종 체결 가격 조회 93 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 94 | :param payment_currency : KRW 95 | :return : price 96 | """ 97 | resp = None 98 | try: 99 | resp = PublicApi.ticker(order_currency, payment_currency) 100 | if order_currency != "ALL": 101 | return float(resp['data']['closing_price']) 102 | else: 103 | del resp["data"]['date'] 104 | return resp["data"] 105 | except Exception: 106 | return resp 107 | 108 | @staticmethod 109 | def get_orderbook(order_currency, payment_currency="KRW", limit=5): 110 | """ 111 | 매수/매도 호가 조회 112 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 113 | :param payment_currency : KRW 114 | :return : 매수/매도 호가 115 | """ 116 | resp = None 117 | try: 118 | limit = min(limit, 30) 119 | resp = PublicApi.orderbook(order_currency, payment_currency, limit) 120 | data = resp['data'] 121 | for idx in range(len(data['bids'])) : 122 | data['bids'][idx]['quantity'] = float( 123 | data['bids'][idx]['quantity']) 124 | data['asks'][idx]['quantity'] = float( 125 | data['asks'][idx]['quantity']) 126 | data['bids'][idx]['price'] = float(data['bids'][idx]['price']) 127 | data['asks'][idx]['price'] = float(data['asks'][idx]['price']) 128 | return data 129 | except Exception: 130 | return resp 131 | 132 | @staticmethod 133 | def get_btci(): 134 | try: 135 | data = PublicApi.btci()['data'] 136 | data['date'] = datetime.datetime.fromtimestamp(int(data['date']) / 1e3) 137 | return data 138 | except Exception: 139 | return None 140 | 141 | @staticmethod 142 | def get_transaction_history(order_currency, payment_currency="KRW", limit=20): 143 | resp = None 144 | try: 145 | limit = min(limit, 100) 146 | resp = PublicApi.transaction_history(order_currency, payment_currency, limit) 147 | data = resp['data'] 148 | for idx in range(len(data)): 149 | data[idx]['units_traded'] = float(data[idx]['units_traded']) 150 | data[idx]['price'] = float(data[idx]['price']) 151 | data[idx]['total'] = float(data[idx]['total']) 152 | return data 153 | except Exception as e: 154 | print(e) 155 | return None 156 | 157 | @staticmethod 158 | def get_candlestick(order_currency, payment_currency="KRW", chart_intervals="24h"): 159 | """ 160 | Candlestick API 161 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 162 | :param payment_currency : KRW 163 | :param chart_instervals : 24h {1m, 3m, 5m, 10m, 30m, 1h, 6h, 12h, 24h 사용 가능} 164 | :return : DataFrame (시가, 고가, 저가, 종가, 거래량) 165 | - index : DateTime 166 | """ 167 | try: 168 | resp = PublicApi.candlestick(order_currency=order_currency, payment_currency=payment_currency, chart_intervals=chart_intervals) 169 | if resp.get('status') == '0000': 170 | resp = resp.get('data') 171 | df = DataFrame(resp, columns=['time', 'open', 'close', 'high', 'low', 'volume']) 172 | df = df.set_index('time') 173 | df = df[~df.index.duplicated()] 174 | df = df[['open', 'high', 'low', 'close', 'volume']] 175 | df.index = pd.to_datetime(df.index, unit='ms', utc=True) 176 | df.index = df.index.tz_convert('Asia/Seoul') 177 | df.index = df.index.tz_localize(None) 178 | 179 | return df.astype(float) 180 | except Exception: 181 | return None 182 | 183 | def get_trading_fee(self, order_currency, payment_currency="KRW"): 184 | """ 185 | 거래 수수료 조회 186 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 187 | :param payment_currency : KRW 188 | :return : 수수료 189 | """ 190 | resp = None 191 | try: 192 | resp = self.api.account(order_currency=order_currency, 193 | payment_currency=payment_currency) 194 | return float(resp['data']['trade_fee']) 195 | except Exception: 196 | return resp 197 | 198 | def get_balance(self, currency): 199 | """ 200 | 거래소 회원의 잔고 조회 201 | :param currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 202 | :return : (보유코인, 사용중코인, 보유원화, 사용중원화) 203 | """ 204 | resp = None 205 | try: 206 | resp = self.api.balance(currency=currency) 207 | specifier = currency.lower() 208 | return (float(resp['data']["total_" + specifier]), 209 | float(resp['data']["in_use_" + specifier]), 210 | float(resp['data']["total_krw"]), 211 | float(resp['data']["in_use_krw"])) 212 | except Exception: 213 | return resp 214 | 215 | def buy_limit_order(self, order_currency, price, unit, 216 | payment_currency="KRW"): 217 | """ 218 | 매수 주문 219 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 220 | :param payment_currency : KRW 221 | :param price : 주문 가격 222 | :param unit : 주문 수량 223 | :return : (주문Type, currency, 주문ID) 224 | """ 225 | resp = None 226 | try: 227 | unit = Bithumb._convert_unit(unit) 228 | price = price if payment_currency == "KRW" else f"{price:.8f}" 229 | resp = self.api.place(type="bid", price=price, units=unit, 230 | order_currency=order_currency, 231 | payment_currency=payment_currency) 232 | return "bid", order_currency, resp['order_id'], payment_currency 233 | except Exception: 234 | return resp 235 | 236 | def sell_limit_order(self, order_currency, price, unit, 237 | payment_currency="KRW"): 238 | """ 239 | 매도 주문 240 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 241 | :param payment_currency : KRW 242 | :param price : 주문 가격 243 | :param unit : 주문 수량 244 | :return : (주문Type, currency, 주문ID) 245 | """ 246 | resp = None 247 | try: 248 | unit = Bithumb._convert_unit(unit) 249 | price = price if payment_currency == "KRW" else f"{price:.8f}" 250 | resp = self.api.place(type="ask", price=price, units=unit, 251 | order_currency=order_currency, 252 | payment_currency=payment_currency) 253 | return "ask", order_currency, resp['order_id'], payment_currency 254 | except Exception: 255 | return resp 256 | 257 | def get_outstanding_order(self, order_desc): 258 | """ 259 | 거래 미체결 수량 조회 260 | :param order_desc: (주문Type, currency, 주문ID) 261 | :return : 거래 미체결 수량 262 | """ 263 | resp = None 264 | try: 265 | resp = self.api.orders(type=order_desc[0], 266 | order_currency=order_desc[1], 267 | order_id=order_desc[2], 268 | payment_currency=order_desc[3]) 269 | if resp['status'] == '5600': 270 | return None 271 | # HACK : 빗썸이 데이터를 리스트에 넣어줌 272 | return resp['data'][0]['units_remaining'] 273 | except Exception: 274 | return resp 275 | 276 | def get_order_completed(self, order_desc): 277 | """ 278 | 거래 완료 정보 조회 279 | :param order_desc: (주문Type, currency, 주문ID) 280 | :return : 거래정보 281 | """ 282 | resp = None 283 | try: 284 | resp = self.api.order_detail(type=order_desc[0], 285 | order_currency=order_desc[1], 286 | order_id=order_desc[2], 287 | payment_currency=order_desc[3]) 288 | if resp['status'] == '5600': 289 | return None 290 | # HACK : 빗썸이 데이터를 리스트에 넣어줌 291 | return resp['data'][0] 292 | except Exception: 293 | return resp 294 | 295 | def cancel_order(self, order_desc): 296 | """ 297 | 매수/매도 주문 취소 298 | :param order_desc: (주문Type, currency, 주문ID) 299 | :return : 성공: True / 실패: False 300 | """ 301 | resp = None 302 | try: 303 | resp = self.api.cancel(type=order_desc[0], 304 | order_currency=order_desc[1], 305 | order_id=order_desc[2], 306 | payment_currency=order_desc[3]) 307 | return resp['status'] == '0000' 308 | except Exception: 309 | return resp 310 | 311 | def buy_market_order(self, order_currency, unit, payment_currency="KRW"): 312 | """ 313 | 시장가 매수 314 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 315 | :param payment_currency : KRW 316 | :param unit : 주문수량 317 | :return : 성공 orderID / 실패 메시지 318 | """ 319 | resp = None 320 | try: 321 | unit = Bithumb._convert_unit(unit) 322 | resp = self.api.market_buy(order_currency=order_currency, 323 | payment_currency=payment_currency, 324 | units=unit) 325 | return "bid", order_currency, resp['order_id'], payment_currency 326 | except Exception: 327 | return resp 328 | 329 | def sell_market_order(self, order_currency, unit, payment_currency="KRW"): 330 | """ 331 | 시장가 매도 332 | :param order_currency : BTC/ETH/DASH/LTC/ETC/XRP/BCH/XMR/ZEC/QTUM/BTG/EOS/ICX/VEN/TRX/ELF/MITH/MCO/OMG/KNC 333 | :param payment_currency : KRW 334 | :param unit : 주문수량 335 | :return : 성공 orderID / 실패 메시지 336 | """ 337 | resp = None 338 | try: 339 | unit = Bithumb._convert_unit(unit) 340 | resp = self.api.market_sell(order_currency=order_currency, 341 | payment_currency=payment_currency, 342 | units=unit) 343 | return "ask", order_currency, resp['order_id'], payment_currency 344 | except Exception: 345 | return resp 346 | 347 | def withdraw_coin(self, withdraw_unit:float, target_address:str, destination_tag_or_memo, withdraw_currency:str): 348 | """ 349 | :unit : 출금하고자 하는 코인 수량 350 | :address : 코인 별 출금 주소 351 | :destination : XRP 출금 시 Destination Tag, STEEM 출금 시 입금 메모, XMR 출금 시 Payment ID 352 | :currency : 가상자산 영문 코드. 기본값:BTC 353 | """ 354 | resp = None 355 | try: 356 | unit = Bithumb._convert_unit(withdraw_unit) 357 | resp = self.api.withdraw_coin(units=unit, 358 | address=target_address, 359 | destination=destination_tag_or_memo, 360 | currency=withdraw_currency) 361 | return resp['order_id'] 362 | except Exception: 363 | return resp 364 | 365 | def withdraw_cash(self, target_bank:str, target_account:str, target_amount:int): 366 | """ 367 | :bank : [은행코드_은행명] ex: 011_농협은행 368 | :account : 출금 계좌번호 369 | :price : 출금 KRW 금액 370 | """ 371 | resp = None 372 | try: 373 | resp = self.api.withdraw_coin(bank=target_bank, 374 | account=target_account, 375 | price=target_amount) 376 | return resp['order_id'] 377 | except Exception: 378 | return resp 379 | 380 | if __name__ == "__main__": 381 | # print(Bithumb.get_orderbook("BTC")) 382 | # print(Bithumb.get_current_price("BTC")) 383 | # print(Bithumb.get_current_price("ALL")) 384 | # 1m, 3m, 5m, 10m, 30m, 1h, 6h, 12h, 24h 385 | 386 | df = Bithumb.get_candlestick("BTC", chart_intervals="12h") 387 | print(df[df.duplicated()]) 388 | -------------------------------------------------------------------------------- /pybithumb/core.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import hmac 4 | import time 5 | import urllib 6 | import requests 7 | from requests.adapters import HTTPAdapter 8 | from urllib3.util.retry import Retry 9 | 10 | 11 | class PublicApi: 12 | @staticmethod 13 | def ticker(order_currency, payment_currency="KRW"): 14 | uri = "/public/ticker/{}_{}".format(order_currency, payment_currency) 15 | return BithumbHttp().get(uri) 16 | 17 | @staticmethod 18 | def transaction_history(order_currency, payment_currency="KRW", limit=20): 19 | uri = "/public/transaction_history/{}_{}?count={}".format(order_currency, 20 | payment_currency, 21 | limit) 22 | return BithumbHttp().get(uri) 23 | 24 | @staticmethod 25 | def orderbook(order_currency, payment_currency="KRW", limit=5): 26 | uri = "/public/orderbook/{}_{}?count={}".format(order_currency, 27 | payment_currency, limit) 28 | return BithumbHttp().get(uri) 29 | 30 | @staticmethod 31 | def btci(): 32 | uri = "/public/btci" 33 | return BithumbHttp().get(uri) 34 | 35 | @staticmethod 36 | def candlestick(order_currency, payment_currency="KRW", chart_intervals="24h"): 37 | uri = "/public/candlestick/{}_{}/{}".format(order_currency, payment_currency, 38 | chart_intervals) 39 | return BithumbHttp().get(uri) 40 | 41 | 42 | 43 | class PrivateApi: 44 | def __init__(self, conkey, seckey): 45 | self.http = BithumbHttp(conkey, seckey) 46 | 47 | def account(self, **kwargs): 48 | return self.http.post('/info/account', **kwargs) 49 | 50 | def balance(self, **kwargs): 51 | return self.http.post('/info/balance', **kwargs) 52 | 53 | def place(self, **kwargs): 54 | return self.http.post('/trade/place', **kwargs) 55 | 56 | def orders(self, **kwargs): 57 | return self.http.post('/info/orders', **kwargs) 58 | 59 | def order_detail(self, **kwargs): 60 | return self.http.post('/info/order_detail', **kwargs) 61 | 62 | def cancel(self, **kwargs): 63 | return self.http.post('/trade/cancel', **kwargs) 64 | 65 | def market_buy(self, **kwargs): 66 | return self.http.post('/trade/market_buy', **kwargs) 67 | 68 | def market_sell(self, **kwargs): 69 | return self.http.post('/trade/market_sell', **kwargs) 70 | 71 | def withdraw_coin(self, **kwargs): 72 | return self.http.post('/trade/btc_withdrawal', **kwargs) 73 | 74 | def withdraw_cash(self, **kwargs): 75 | return self.http.post('/trade/krw_withdrawal', **kwargs) 76 | 77 | 78 | class HttpMethod: 79 | def __init__(self): 80 | self.session = self._requests_retry_session() 81 | 82 | def _requests_retry_session(retries=5, backoff_factor=0.3, 83 | status_forcelist=(500, 502, 504), session=None): 84 | s = session or requests.Session() 85 | retry = Retry(total=retries, read=retries, connect=retries, 86 | backoff_factor=backoff_factor, 87 | status_forcelist=status_forcelist) 88 | adapter = HTTPAdapter(max_retries=retry) 89 | s.mount('http://', adapter) 90 | s.mount('https://', adapter) 91 | return s 92 | 93 | @property 94 | def base_url(self): 95 | return "" 96 | 97 | def update_headers(self, headers): 98 | self.session.headers.update(headers) 99 | 100 | def post(self, path, timeout=3, **kwargs): 101 | try: 102 | uri = self.base_url + path 103 | return self.session.post(url=uri, data=kwargs, timeout=timeout).\ 104 | json() 105 | except Exception as x: 106 | print("It failed", x.__class__.__name__) 107 | return None 108 | 109 | def get(self, path, timeout=3, **kwargs): 110 | try: 111 | uri = self.base_url + path 112 | return self.session.get(url=uri, params=kwargs, timeout=timeout).\ 113 | json() 114 | except Exception as x: 115 | print("It failed", x.__class__.__name__) 116 | return None 117 | 118 | 119 | class BithumbHttp(HttpMethod): 120 | def __init__(self, conkey="", seckey=""): 121 | self.API_CONKEY = conkey.encode('utf-8') 122 | self.API_SECRET = seckey.encode('utf-8') 123 | super(BithumbHttp, self).__init__() 124 | 125 | @property 126 | def base_url(self): 127 | return "https://api.bithumb.com" 128 | 129 | def _signature(self, path, nonce, **kwargs): 130 | query_string = path + chr(0) + urllib.parse.urlencode(kwargs) + \ 131 | chr(0) + nonce 132 | h = hmac.new(self.API_SECRET, query_string.encode('utf-8'), 133 | hashlib.sha512) 134 | return base64.b64encode(h.hexdigest().encode('utf-8')) 135 | 136 | def post(self, path, **kwargs): 137 | kwargs['endpoint'] = path 138 | nonce = str(int(time.time() * 1000)) 139 | 140 | self.update_headers({ 141 | 'Api-Key': self.API_CONKEY, 142 | 'Api-Sign': self._signature(path, nonce, **kwargs), 143 | 'Api-Nonce': nonce 144 | }) 145 | return super().post(path, **kwargs) 146 | 147 | 148 | if __name__ == "__main__": 149 | print(PublicApi.ticker("BTC")) -------------------------------------------------------------------------------- /pybithumb/util.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import inspect 3 | import warnings 4 | 5 | 6 | class PyBithumbWraning(UserWarning): 7 | pass 8 | 9 | 10 | def deprecated(instructions): 11 | def decorator(func): 12 | @functools.wraps(func) 13 | def wrapper(*args, **kwargs): 14 | message = 'Call to deprecated function {}. {}'.format( 15 | func.__name__, 16 | instructions) 17 | frame = inspect.currentframe().f_back 18 | warnings.warn_explicit(message, 19 | category=PyBithumbWraning, 20 | filename=inspect.getfile(frame.f_code), 21 | lineno=frame.f_lineno) 22 | return func(*args, **kwargs) 23 | return wrapper 24 | return decorator 25 | -------------------------------------------------------------------------------- /pybithumb/websocket.py: -------------------------------------------------------------------------------- 1 | import websockets 2 | import asyncio 3 | import json 4 | import multiprocessing as mp 5 | 6 | 7 | class WebSocketManager(mp.Process): 8 | """웹소켓을 관리하는 클래스 9 | 10 | 사용 예제: 11 | 12 | >> wm = WebSocketManager("ticker", ["BTC_KRW"]) 13 | >> for i in range(3): 14 | data = wm.get() 15 | print(data) 16 | >> wm.terminate() 17 | 18 | 주의 : 19 | 20 | 재귀적인 호출을 위해 다음의 guard를 반드시 추가해야 한다. 21 | >> if __name__ == "__main__" 22 | 23 | """ 24 | def __init__(self, type: str, symbols: list, ticktype: list=None, qsize: int=1000): 25 | """웹소켓을 컨트롤하는 클래스의 생성자 26 | 27 | Args: 28 | type (str ): 구독 메시지 종류 (ticker/transaction/orderbookdepth) 29 | symbols (list ): 구독할 암호 화폐의 리스트 [BTC_KRW, ETH_KRW, …] 30 | ticktype (list, optional): tick 종류 리스트 (30M/1H/12H/24H/MID) 31 | qsize (int , optional): 메시지를 저장할 Queue의 크기 32 | """ 33 | self.__q = mp.Queue(qsize) 34 | self.alive = False 35 | 36 | self.type = type 37 | self.symbols = symbols 38 | 39 | self.ticktype = ticktype 40 | if self.ticktype == None: 41 | self.ticktype = ["1H"] 42 | super().__init__() 43 | 44 | async def __connect_socket(self): 45 | uri = "wss://pubwss.bithumb.com/pub/ws" 46 | 47 | async with websockets.connect(uri, ping_interval=None) as websocket: 48 | connection_msg = await websocket.recv() 49 | # {"status":"0000","resmsg":"Connected Successfully"} 50 | if "Connected Successfully" not in connection_msg : 51 | print("connection error") 52 | 53 | data = { 54 | "type" : self.type, 55 | 'symbols' : self.symbols, 56 | 'tickTypes': self.ticktype 57 | } 58 | await websocket.send(json.dumps(data)) 59 | 60 | registration_msg = await websocket.recv() 61 | # {"status":"0000","resmsg":"Filter Registered Successfully"} 62 | if "Filter Registered Successfully" not in registration_msg: 63 | print("Registration error") 64 | 65 | while self.alive: 66 | recv_data = await websocket.recv() 67 | self.__q.put(json.loads(recv_data)) 68 | 69 | def run(self): 70 | self.__aloop = asyncio.get_event_loop() 71 | self.__aloop.run_until_complete(self.__connect_socket()) 72 | 73 | def get(self): 74 | if self.alive == False: 75 | self.alive = True 76 | self.start() 77 | return self.__q.get() 78 | 79 | def terminate(self): 80 | self.alive = False 81 | super().terminate() 82 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.9.3 2 | bs4==0.0.1 3 | certifi==2020.12.5 4 | chardet==4.0.0 5 | html5lib==1.1 6 | idna==2.10 7 | numpy==1.19.5 8 | pandas==1.2.1 9 | python-dateutil==2.8.1 10 | pytz==2020.5 11 | requests==2.25.1 12 | six==1.15.0 13 | soupsieve==2.1 14 | urllib3==1.26.3 15 | webencodings==0.5.1 16 | websockets==8.1 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name = 'pybithumb', 5 | version = '1.0.21', 6 | description = 'python wrapper for Bithumb API', 7 | url = 'https://github.com/sharebook-kr/pybithumb', 8 | author = 'Lukas Yoo, Brayden Jo', 9 | author_email = 'jonghun.yoo@outlook.com, pystock@outlook.com', 10 | install_requires=['requests', 'pandas', 'bs4', 'html5lib', 'datetime', 'websockets==9.1'], 11 | license = 'MIT', 12 | packages = ['pybithumb'], 13 | zip_safe = False 14 | ) 15 | --------------------------------------------------------------------------------