Exemplo n.º 1
0
class CexWebSocketOrderIdIssuer(object):
  def __init__(self):
    self.nonce_issuer = NonceIssuer()

  def next_order_id(self):
    nonce = self.nonce_issuer.next_nonce()
    return "oid-{}".format(nonce)
Exemplo n.º 2
0
 def __init__(self, conf):
     self.conf = conf
     self.nonce_issuer = NonceIssuer()
Exemplo n.º 3
0
class CexClient(object):
    def __init__(self, conf):
        self.conf = conf
        self.nonce_issuer = NonceIssuer()

    def _get_signature(self, nonce):
        message = str(nonce) + self.conf.user_id + self.conf.key
        signature = hmac.new(str(self.conf.secret),
                             msg=str(message),
                             digestmod=hashlib.sha256).hexdigest().upper()
        return signature

    def _get_basic_private_data(self):
        nonce = self.nonce_issuer.next_nonce()
        signature = self._get_signature(nonce)
        return {
            'nonce': nonce,
            'signature': signature,
            'key': self.conf.key,
        }

    @public
    def get_orderbook(self, equity_pair, depth=20):
        """
    거래소의 Orderbook을 조회한다.
    :param equity_pair: eth/btc 와 같은 형태의 equity pair 문자열
    :param depth: 조회할 Orderbook의 depth
    :return: Orderbook 객체
    """
        if depth <= 0:
            raise ValueError(
                'Depth should be bigger than 0 : {}'.format(depth))
        target_equity, base_equity = equity_pair.split("/")
        url = "https://cex.io/api/order_book/{}/{}/".format(
            target_equity.upper(), base_equity.upper())
        params = dict(depth=depth, )
        r = requests.get(url, params=params, timeout=5)
        response = json.loads(r.content)
        return CexParser.parse_orderbook_response(response)

    @private
    def get_balance(self):
        """
    private API
    {
      "timestamp": "1509886058",
      "BTC": {
          "available": "0.48696717",
          "orders": "0.00000000"
      },
      "ETH": {
          "available": "9.82155000",
          "orders": "0.10000000"
      },
      ...
    }
    현재 limit에 걸린 물량은 orders에 올라가고, 아닌 물량은 available에 보이는 듯
    :return:
    """
        url = "https://cex.io/api/balance/"
        data = self._get_basic_private_data()

        r = requests.post(url, data=data)
        return json.loads(r.content)

    @private
    def get_myfee(self):
        url = "https://cex.io/api/get_myfee/"
        data = self._get_basic_private_data()

        r = requests.post(url, data=data)
        j = json.loads(r.content)
        CexResponseHandler.raise_if_exception(j)
        return j

    @private
    def place_buy_order(self, amount, price, currency='btc'):
        """
    Only for BTC/USD now, can be fixed with changed url
    :return:
      complete: Boolean
      price: String float
      amount: String float
      time: long
      type: String
      id: String long
      pending: String float
    """
        url = "https://cex.io/api/place_order/{}/USD".format(currency.upper())
        data = self._get_basic_private_data()

        data['type'] = 'buy'
        data['amount'] = amount
        data['price'] = price

        r = requests.post(url, data=data)
        j = json.loads(r.content)
        CexResponseHandler.raise_if_exception(j)
        return j

    @private
    def place_sell_order(self, amount, price, currency='btc'):
        url = "https://cex.io/api/place_order/{}/USD".format(currency.upper())
        data = self._get_basic_private_data()

        data['type'] = 'sell'
        data['amount'] = amount
        data['price'] = price

        r = requests.post(url, data=data)
        j = json.loads(r.content)
        CexResponseHandler.raise_if_exception(j)
        return j

    @private
    def cancel_order(self, id):
        url = "https://cex.io/api/cancel_order/"
        data = self._get_basic_private_data()

        data['id'] = id
        r = requests.post(url, data=data)
        if r.content == "true":
            return r.content
        else:
            raise CexException("Cancel Order [{}] is failed.".format(id))

    @private
    def get_active_orders(self):
        """
    일단 BTC/USD 만을 기준으로 합니다.
    :return: list of
      id: String
      time: String, Long
      type: String (buy/sell)
      price: String, float
      amount: String, float
      pending: String, float
      symbol1: String
      symbol2: String
    """
        url = "https://cex.io/api/open_orders/BTC/USD"
        data = self._get_basic_private_data()

        r = requests.post(url, data=data)
        return json.loads(r.content)

    @private
    def get_active_orders_with_id(self, ids):
        """
    e: String
    ok: String
    data : List of List
      inside list : [order_id, total_volume, pending_volume]
    :param ids:
    :return:
    """
        if len(ids) == 0:
            raise CexException("Number of id should be bigger than 0")

        url = "https://cex.io/api/active_orders_status"
        data = self._get_basic_private_data()

        data['orders_list'] = ids
        r = requests.post(url, data=data)
        j = json.loads(r.content)
        CexResponseHandler.raise_if_exception(j)
        return j

    @private
    def get_open_positions(self, currency):
        """
    :param currency: String, 'btc', 'eth', 'bch'
    :return:
    """
        url = "https://cex.io/api/open_positions/{}/USD".format(
            currency.upper())
        data = self._get_basic_private_data()

        r = requests.post(url, data=data)
        j = json.loads(r.content)
        CexResponseHandler.raise_if_exception(j)
        return j
Exemplo n.º 4
0
 def __init__(self):
   self.nonce_issuer = NonceIssuer()
Exemplo n.º 5
0
class BitfinexClient(object):
  def __init__(self, conf):
    self.conf = conf
    self.nonce_issuer = NonceIssuer()

  def _get_payload(self, body):
    string = json.dumps(body)
    return base64.b64encode(string)

  def _get_signature(self, payload):
    return hmac.new(str(self.conf.secret), msg=payload, digestmod=hashlib.sha384).hexdigest()

  def _get_basic_private_headers(self, body):
    payload = self._get_payload(body)
    signature = self._get_signature(payload)
    return {
      'X-BFX-APIKEY': self.conf.key,
      'X-BFX-PAYLOAD': payload,
      'X-BFX-SIGNATURE': signature,
    }

  def _get_basic_private_body(self, path):
    return {
      'nonce': str(self.nonce_issuer.next_nonce()),
      'request': path,
    }

  def _make_url(self, path):
    return 'https://api.bitfinex.com' + path

  def _post(self, path, body):
    private_body = self._get_basic_private_body(path)
    body.update(private_body)

    url = self._make_url(path)
    headers = self._get_basic_private_headers(body)

    r = requests.get(url, headers=headers, timeout=5)
    return json.loads(r.content)

  @public
  def get_orderbook(self, equity_pair, depth=20):
    """
    거래소의 Orderbook을 조회한다.
    :param equity_pair: eth/btc 와 같은 형태의 equity pair 문자열
    :param depth: 조회할 Orderbook의 depth
    :return: Orderbook 객체
    """
    if depth <= 0:
      raise ValueError('Depth should be bigger than 0 : {}'.format(depth))
    bitfinex_equity_pair = EQUITY_PAIRS.to_exchange_pair(equity_pair)
    url = 'https://api.bitfinex.com/v1/book/{}'.format(bitfinex_equity_pair)
    params = dict(
      depth=depth,
    )
    r = requests.get(url, params=params, timeout=5)
    response = json.loads(r.content)
    return BitfinexParser.parse_orderbook_response(response, bitfinex_equity_pair)

  @private
  def get_balance(self):
    path = '/v1/balances'
    return self._post(path, {})

  @private
  def get_summary(self):
    path = '/v1/summary'
    return self._post(path, {})

  @private
  def get_margin_information(self):
    path = '/v1/margin_infos'
    return self._post(path, {})

  @private
  def get_active_positions(self):
    path = '/v1/positions'
    return self._post(path, {})
Exemplo n.º 6
0
class CoinoneClient(object):
  def __init__(self, conf):
    self.conf = conf
    self.nonce_issuer = NonceIssuer()

  @staticmethod
  def public_base_url():
    return 'https://api.coinone.co.kr/'

  @staticmethod
  def base_url():
    return 'https://api.coinone.co.kr/v2/'

  @staticmethod
  def base_url_v1():
    return 'https://api.coinone.co.kr/v1/'

  def get_encoded_payload(self, payload):
    payload[u'nonce'] = self.nonce_issuer.next_nonce()

    dumped_json = json.dumps(payload)
    encoded_json = base64.b64encode(dumped_json)
    return encoded_json

  def get_signature(self, encoded_payload):
    signature = hmac.new(str(self.conf.secret_key).upper(), str(encoded_payload), hashlib.sha512)
    return signature.hexdigest()

  def post(self, url, payload):
    encoded_payload = self.get_encoded_payload(payload)
    headers = {
      'Content-type': 'application/json',
      'User-Agent': constants.USER_AGENT,
      'X-COINONE-PAYLOAD': encoded_payload,
      'X-COINONE-SIGNATURE': self.get_signature(encoded_payload)
    }
    r = requests.post(url, headers=headers, data=encoded_payload, timeout=10)
    j = json.loads(r.content)
    if 'errorCode' in j:
      if j['errorCode'] != "0":
        if 'errorMsg' in j:
          error_message = j['errorMsg']
          raise CoinoneException(error_message)
        else:
          error_code = j['errorCode']
          if error_code == "131":
            raise CoinoneNonceException('Coinone Exception with nonce')

          raise CoinoneException('Coinone Exception with error code[{}]'.format(error_code))
    return j

  def get(self, url, params, timeout=5):
    ## public get 만 이 형태일 수도 있습니다.
    headers = {
      'Content-type': 'application/json',
      'User-Agent': constants.USER_AGENT,
    }
    r = requests.get(url, headers=headers, params=params, timeout=timeout)
    j = json.loads(r.content)
    if 'errorCode' in j:
      if j['errorCode'] != "0":
        if 'errorMsg' in j:
          error_message = j['errorMsg']
          raise CoinoneException(error_message)
        else:
          error_code = j['errorCode']
          if error_code == "131":
            raise CoinoneNonceException('Coinone Exception with nonce')

          raise CoinoneException('Coinone Exception with error code[{}]'.format(error_code))
    return j

  def get_balance(self):
    """
    계좌에 남은 KRW, BTC, ETH 등을 가져옵니다.
    """
    url = CoinoneClient.base_url() + "account/balance/"
    payload = {"access_token": self.conf.access_token}
    return self.post(url, payload)

  @public
  def get_orderbook(self, equity_pair):
    """
    거래소의 Orderbook을 조회한다.
    Korbit의 경우 파라메터에 depth가 없다.
    :param equity_pair: eth/btc 와 같은 형태의 equity pair 문자열
    :return: Orderbook 객체
    """
    currency = EQUITY_PAIRS.to_exchange_pair(equity_pair)
    url = 'http://api.coinone.co.kr/orderbook/'
    params = dict(
      currency=currency
    )
    response = self.get(url, params=params, timeout=5)
    return CoinoneParser.parse_orderbook_response(response)

  def buy_currency(self, currency, krw):
    """
    시장가로 currency를 krw만큼 사달라고 요청합니다. (qty가 아니라 krw임을 주의할 것)
    """
    url = CoinoneClient.base_url() + "order/market_buy/"
    payload = {
      "access_token": self.conf.access_token,
      "currency": currency,
      "price": krw,
    }
    return self.post(url, payload)

  def sell_currency(self, currency, qty):
    """
    시장가로 currency를 qty만큼 팔아달라고 요청합니다.
    """
    url = CoinoneClient.base_url() + "order/market_sell/"
    payload = {
      "access_token": self.conf.access_token,
      "currency": currency,
      "qty": qty,
    }
    return self.post(url, payload)

  def my_completed_orders(self, currency):
    """
    내 계정으로 특정 currency에 대해서 일어났던 거래를 전부 불러옵니다. pagination이 안되어 있어서 나중에 문제를 일으킬 수 있습니다.
    """
    url = CoinoneClient.base_url() + 'order/complete_orders/'
    payload = {
      "access_token": self.conf.access_token,
      "currency": currency,
    }
    return self.post(url, payload)

  def my_completed_orders_of_id(self, currency, order_id):
    """
    :param currency:
    :param order_id:
    :return: list of completed_order - {orderId, fee, timestamp, price, qty, feeRate, type}
    """
    # order_id 가 처음에 request를 보낼때는 소문자로 오는데 나중에 completed_order에서 가져올 때는 대문자로 옴...
    order_id = order_id.upper()

    url = CoinoneClient.base_url() + 'order/complete_orders/'
    payload = {
      "access_token": self.conf.access_token,
      "currency": currency,
    }
    response = self.post(url, payload)

    result = []
    if response['result'] == 'success':
      completed_orders = response['completeOrders']
      for completed_order in completed_orders:
        if completed_order['orderId'] == order_id:
          result.append(completed_order)
      return result
    else:
      raise Exception('result code is not success : ' + response)

  def buy_limit(self, currency, price, qty):
    """
    market limit 가격으로 특정 currency를 qty만큼 사달라고 요청합니다. BTC/KRW buy 거래입니다.
    :param currency:
    :param price:
    :param qty:
    :return:
    """
    if not self.check_price_format(currency, price):
      raise ValueError()

    url = CoinoneClient.base_url() + 'order/limit_buy/'
    payload = {
      "access_token": self.conf.access_token,
      'currency': currency,
      'price': int(price),
      'qty': qty,
    }
    return self.post(url, payload)

  def sell_limit(self, currency, price, qty):
    """
    market limit 가격으로 특정 currency를 qty만큼 팔아달라고 요청합니다. BTC/KRW sell 거래입니다.
    :param currency:
    :param price:
    :param qty:
    :return:
    """
    if not self.check_price_format(currency, price):
      raise ValueError()

    url = CoinoneClient.base_url() + 'order/limit_sell/'
    payload = {
      "access_token": self.conf.access_token,
      'currency': currency,
      'price': int(price),
      'qty': qty,
    }
    return self.post(url, payload)

  def check_price_format(self, currency, price):
    if currency.lower() == 'btc':
      return price % 1000 == 0
    elif currency.lower() == 'eth':
      return price % 100 == 0
    elif currency.lower() == 'bch':
      return price % 500 == 0
    elif currency.lower() == 'xrp':
      return price % 1 == 0
    else:
      raise NotImplementedError("Price unit of {} is not informed.".format(currency))

  def floor_price_format(self, currency, price):
    if currency.lower() == 'btc':
      return 1000 * math.floor(price / 1000.0)
    elif currency.lower() == 'eth':
      return 100 * math.floor(price / 100.0)
    elif currency.lower() == 'bch':
      return 500 * math.floor(price / 500.0)
    elif currency.lower() == 'xrp':
      return 1 * math.floor(price / 1)
    else:
      raise NotImplementedError("Price unit of {} is not informed.".format(currency))

  def ceil_price_format(self, currency, price):
    if currency.lower() == 'btc':
      return 1000 * math.ceil(price / 1000.0)
    elif currency.lower() == 'eth':
      return 100 * math.ceil(price / 100.0)
    elif currency.lower() == 'bch':
      return 500 * math.ceil(price / 500.0)
    elif currency.lower() == 'xrp':
      return 1 * math.ceil(price / 1)
    else:
      raise NotImplementedError("Price unit of {} is not informed.".format(currency))

  def get_2fa_number(self, type):
    """
    이거 뭐 폰으로 문자가 와? ㅋㅋㅋ 이러면 쓸 수가 없는데...
    :param type:
    :return:
    """
    url = CoinoneClient.base_url() + 'transaction/auth_number/'
    payload = {
      "access_token": self.conf.access_token,
      'type': type,
      'nonce': self.nonce_issuer.next_nonce()
    }
    return self.post(url, payload)

  def get_transaction_history(self, currency):
    url = CoinoneClient.base_url() + 'transaction/history/'
    payload = {
      "access_token": self.conf.access_token,
      'currency': currency,
      'nonce': self.nonce_issuer.next_nonce()
    }
    return self.post(url, payload)
Exemplo n.º 7
0
 def __init__(self, config):
     self.config = config
     self.nonce_issuer = NonceIssuer()
     self.authenticated_requester = OkexAuthenticatedRequester(config)