├── phemex ├── __init__.py ├── exceptions.py └── client.py ├── README.md └── examples └── rest.py /phemex/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a python example code for Phemex API. 2 | It implements a subset of APIs of Phemex -------------------------------------------------------------------------------- /phemex/exceptions.py: -------------------------------------------------------------------------------- 1 | 2 | class PhemexAPIException(Exception): 3 | 4 | def __init__(self, response): 5 | self.code = 0 6 | try: 7 | json_res = response.json() 8 | except ValueError: 9 | self.message = 'Invalid error message: {}'.format(response.text) 10 | else: 11 | if 'code' in json_res: 12 | self.code = json_res['code'] 13 | self.message = json_res['msg'] 14 | else: 15 | self.code = json_res['error']['code'] 16 | self.message = json_res['error']['message'] 17 | self.status_code = response.status_code 18 | self.response = response 19 | self.request = getattr(response, 'request', None) 20 | 21 | def __str__(self): # pragma: no cover 22 | return 'HTTP(code=%s), API(errorcode=%s): %s' % (self.status_code, self.code, self.message) -------------------------------------------------------------------------------- /examples/rest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | 5 | dir_path = os.path.dirname(os.path.realpath(__file__)) 6 | sys.path.append(dir_path + "/../") 7 | 8 | from phemex.client import Client 9 | from phemex.exceptions import PhemexAPIException 10 | 11 | # Create a testnet client 12 | client = Client("api_key", "api_secret", True) 13 | 14 | # Get account and positions 15 | r = client.query_account_n_positions(Client.CURRENCY_BTC) 16 | print(r) 17 | r = client.query_account_n_positions(Client.CURRENCY_USD) 18 | print(r) 19 | try: 20 | r = client.query_account_n_positions("BTC1") 21 | print(r) 22 | except PhemexAPIException as e: 23 | print(e) 24 | 25 | # Place a new order, priceEp is scaled price, check our API doc for more info about scaling 26 | # https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#scalingfactors 27 | try: 28 | r = client.place_order({ 29 | "symbol":Client.SYMBOL_BTCUSD, 30 | "clOrdID":"JackTest1" + str(time.time()), 31 | "side":Client.SIDE_BUY, 32 | "orderQty":10, 33 | "priceEp": 95000000, 34 | "ordType":Client.ORDER_TYPE_LIMIT, 35 | "timeInForce" : Client.TIF_GOOD_TILL_CANCEL}) 36 | print("response:" + str(r)) 37 | ordid = r["data"]["orderID"] 38 | print(ordid) 39 | except PhemexAPIException as e: 40 | print(e) 41 | 42 | # Send replace if this order not filled yet 43 | try: 44 | r = client.amend_order( 45 | Client.SYMBOL_BTCUSD, 46 | ordid, 47 | {"priceEp": 95500000}) 48 | print("response:" + str(r)) 49 | except PhemexAPIException as e: 50 | print(e) 51 | 52 | # Cancel one order 53 | try: 54 | r = client.cancel_order( 55 | Client.SYMBOL_BTCUSD, 56 | ordid) 57 | print("response:" + str(r)) 58 | except PhemexAPIException as e: 59 | print(e) 60 | 61 | # Cancel all active orders 62 | try: 63 | client.cancel_all_normal_orders(Client.SYMBOL_BTCUSD) 64 | except PhemexAPIException as e: 65 | print(e) 66 | 67 | # Cancel all conditional orders 68 | try: 69 | client.cancel_all_untriggered_conditional_orders(Client.SYMBOL_BTCUSD) 70 | except PhemexAPIException as e: 71 | print(e) 72 | 73 | # Cancel all orders 74 | try: 75 | client.cancel_all(Client.SYMBOL_BTCUSD) 76 | except PhemexAPIException as e: 77 | print(e) 78 | 79 | # Set leverage 80 | try: 81 | # Set 0 to change back to cross margin 82 | # Set to 10x 83 | r = client.change_leverage(Client.SYMBOL_BTCUSD, 10) 84 | print("response:" + str(r)) 85 | except PhemexAPIException as e: 86 | print(e) 87 | 88 | # Set risklimit for 150 BTC 89 | try: 90 | r = client.change_risklimit(Client.SYMBOL_BTCUSD, 150) 91 | print("response:" + str(r)) 92 | except PhemexAPIException as e: 93 | print(e) 94 | 95 | # Get all active orders 96 | try: 97 | r = client.query_open_orders(Client.SYMBOL_BTCUSD) 98 | print("response:" + str(r)) 99 | except PhemexAPIException as e: 100 | print(e) 101 | 102 | try: 103 | print(client.query_24h_ticker("BTCUSD")) 104 | except PhemexAPIException as e: 105 | print(e) -------------------------------------------------------------------------------- /phemex/client.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hmac 3 | import hashlib 4 | import json 5 | import requests 6 | import time 7 | 8 | from math import trunc 9 | from .exceptions import PhemexAPIException 10 | 11 | class Client(object): 12 | 13 | MAIN_NET_API_URL = 'https://api.phemex.com' 14 | TEST_NET_API_URL = 'https://testnet-api.phemex.com' 15 | 16 | CURRENCY_BTC = "BTC" 17 | CURRENCY_USD = "USD" 18 | 19 | SYMBOL_BTCUSD = "BTCUSD" 20 | SYMBOL_ETHUSD = "ETHUSD" 21 | SYMBOL_XRPUSD = "XRPUSD" 22 | 23 | SIDE_BUY = "Buy" 24 | SIDE_SELL = "Sell" 25 | 26 | ORDER_TYPE_MARKET = "Market" 27 | ORDER_TYPE_LIMIT = "Limit" 28 | 29 | TIF_IMMEDIATE_OR_CANCEL = "ImmediateOrCancel" 30 | TIF_GOOD_TILL_CANCEL = "GoodTillCancel" 31 | TIF_FOK = "FillOrKill" 32 | 33 | ORDER_STATUS_NEW = "New" 34 | ORDER_STATUS_PFILL = "PartiallyFilled" 35 | ORDER_STATUS_FILL = "Filled" 36 | ORDER_STATUS_CANCELED = "Canceled" 37 | ORDER_STATUS_REJECTED = "Rejected" 38 | ORDER_STATUS_TRIGGERED = "Triggered" 39 | ORDER_STATUS_UNTRIGGERED = "Untriggered" 40 | 41 | def __init__(self, api_key=None, api_secret=None, is_testnet=False): 42 | self.api_key = api_key 43 | self.api_secret = api_secret 44 | self.api_URL = self.MAIN_NET_API_URL 45 | if is_testnet: 46 | self.api_URL = self.TEST_NET_API_URL 47 | 48 | self.session = requests.session() 49 | 50 | def _send_request(self, method, endpoint, params={}, body={}): 51 | expiry = str(trunc(time.time()) + 60) 52 | query_string = '&'.join(['{}={}'.format(k,v) for k,v in params.items()]) 53 | message = endpoint + query_string + expiry 54 | body_str = "" 55 | if body: 56 | body_str = json.dumps(body, separators=(',', ':')) 57 | message += body_str 58 | signature = hmac.new(self.api_secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256) 59 | self.session.headers.update({ 60 | 'x-phemex-request-signature': signature.hexdigest(), 61 | 'x-phemex-request-expiry': expiry, 62 | 'x-phemex-access-token': self.api_key, 63 | 'Content-Type': 'application/json'}) 64 | 65 | url = self.api_URL + endpoint 66 | if query_string: 67 | url += '?' + query_string 68 | response = self.session.request(method, url, data=body_str.encode()) 69 | if not str(response.status_code).startswith('2'): 70 | raise PhemexAPIException(response) 71 | try: 72 | res_json = response.json() 73 | except ValueError: 74 | raise PhemexAPIException('Invalid Response: %s' % response.text) 75 | if "code" in res_json and res_json["code"] != 0: 76 | raise PhemexAPIException(response) 77 | if "error" in res_json and res_json["error"]: 78 | raise PhemexAPIException(response) 79 | return res_json 80 | 81 | def query_account_n_positions(self, currency:str): 82 | """ 83 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#querytradeaccount 84 | """ 85 | return self._send_request("get", "/accounts/accountPositions", {'currency':currency}) 86 | 87 | def place_order(self, params={}): 88 | """ 89 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#placeorder 90 | """ 91 | return self._send_request("post", "/orders", body=params) 92 | 93 | def amend_order(self, symbol, orderID, params={}): 94 | """ 95 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#622-amend-order-by-orderid 96 | """ 97 | params["symbol"] = symbol 98 | params["orderID"] = orderID 99 | return self._send_request("put", "/orders/replace", params=params) 100 | 101 | def cancel_order(self, symbol, orderID): 102 | """ 103 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#623-cancel-single-order 104 | """ 105 | return self._send_request("delete", "/orders/cancel", params={"symbol": symbol, "orderID": orderID}) 106 | 107 | def _cancel_all(self, symbol, untriggered_order=False): 108 | """ 109 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#625-cancel-all-orders 110 | """ 111 | return self._send_request("delete", "/orders/all", 112 | params={"symbol": symbol, "untriggered": str(untriggered_order).lower()}) 113 | 114 | def cancel_all_normal_orders(self, symbol): 115 | self._cancel_all(symbol, untriggered_order=False) 116 | 117 | def cancel_all_untriggered_conditional_orders(self, symbol): 118 | self._cancel_all(symbol, untriggered_order=True) 119 | 120 | def cancel_all(self, symbol): 121 | self._cancel_all(symbol, untriggered_order=False) 122 | self._cancel_all(symbol, untriggered_order=True) 123 | 124 | def change_leverage(self, symbol, leverage=0): 125 | """ 126 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#627-change-leverage 127 | """ 128 | return self._send_request("PUT", "/positions/leverage", params={"symbol":symbol, "leverage": leverage}) 129 | 130 | def change_risklimit(self, symbol, risk_limit=0): 131 | """ 132 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#628-change-position-risklimit 133 | """ 134 | return self._send_request("PUT", "/positions/riskLimit", params={"symbol":symbol, "riskLimit": risk_limit}) 135 | 136 | def query_open_orders(self, symbol): 137 | """ 138 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#6210-query-open-orders-by-symbol 139 | """ 140 | return self._send_request("GET", "/orders/activeList", params={"symbol": symbol}) 141 | 142 | def query_24h_ticker(self, symbol): 143 | """ 144 | https://github.com/phemex/phemex-api-docs/blob/master/Public-API-en.md#633-query-24-hours-ticker 145 | """ 146 | return self._send_request("GET", "/md/ticker/24hr", params={"symbol": symbol}) 147 | --------------------------------------------------------------------------------