Add back static API version in headers
							parent
							
								
									88f6f89cbb
								
							
						
					
					
						commit
						f89b408f64
					
				| 
						 | 
					@ -18,7 +18,7 @@
 | 
				
			||||||
Kucoin broker backend
 | 
					Kucoin broker backend
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from typing import Any, Optional, Literal
 | 
					from typing import Any, Optional, Literal, AsyncGenerator
 | 
				
			||||||
from contextlib import asynccontextmanager as acm
 | 
					from contextlib import asynccontextmanager as acm
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ import math
 | 
				
			||||||
import base64
 | 
					import base64
 | 
				
			||||||
import hmac
 | 
					import hmac
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
 | 
					import wsproto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import asks
 | 
					import asks
 | 
				
			||||||
import tractor
 | 
					import tractor
 | 
				
			||||||
| 
						 | 
					@ -39,6 +40,10 @@ from piker._cacheables import open_cached_client
 | 
				
			||||||
from piker.log import get_logger
 | 
					from piker.log import get_logger
 | 
				
			||||||
from ._util import DataUnavailable
 | 
					from ._util import DataUnavailable
 | 
				
			||||||
from piker.pp import config
 | 
					from piker.pp import config
 | 
				
			||||||
 | 
					from ..data._web_bs import (
 | 
				
			||||||
 | 
					    open_autorecon_ws,
 | 
				
			||||||
 | 
					    NoBsWs,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
log = get_logger(__name__)
 | 
					log = get_logger(__name__)
 | 
				
			||||||
_ohlc_dtype = [
 | 
					_ohlc_dtype = [
 | 
				
			||||||
| 
						 | 
					@ -53,6 +58,21 @@ _ohlc_dtype = [
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_config() -> dict[str, Any]:
 | 
				
			||||||
 | 
					    conf, path = config.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    section = conf.get("kucoin")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO: document why we send this, basically because logging params for cryptofeed
 | 
				
			||||||
 | 
					    conf["log"] = {}
 | 
				
			||||||
 | 
					    conf["log"]["disabled"] = True
 | 
				
			||||||
 | 
					    breakpoint()
 | 
				
			||||||
 | 
					    if section is None:
 | 
				
			||||||
 | 
					        log.warning("No config section found for kucoin in config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return section
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Client:
 | 
					class Client:
 | 
				
			||||||
    def __init__(self) -> None:
 | 
					    def __init__(self) -> None:
 | 
				
			||||||
        self._pairs: dict[str, any] = {}
 | 
					        self._pairs: dict[str, any] = {}
 | 
				
			||||||
| 
						 | 
					@ -64,9 +84,11 @@ class Client:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        config = get_config()
 | 
					        config = get_config()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ("key_id" in config) and \
 | 
					        if (
 | 
				
			||||||
            ("key_secret" in config) and \
 | 
					            ("key_id" in config)
 | 
				
			||||||
                ("key_passphrase" in config):
 | 
					            and ("key_secret" in config)
 | 
				
			||||||
 | 
					            and ("key_passphrase" in config)
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
            self._authenticated = True
 | 
					            self._authenticated = True
 | 
				
			||||||
            self._key_id = config["key_id"]
 | 
					            self._key_id = config["key_id"]
 | 
				
			||||||
            self._key_secret = config["key_secret"]
 | 
					            self._key_secret = config["key_secret"]
 | 
				
			||||||
| 
						 | 
					@ -74,30 +96,30 @@ class Client:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _gen_auth_req_headers(
 | 
					    def _gen_auth_req_headers(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        action: Literal["POST", "GET", "PUT", "DELETE"],
 | 
					        action: Literal["POST", "GET"],
 | 
				
			||||||
        endpoint: str,
 | 
					        endpoint: str,
 | 
				
			||||||
        api_v: str = "v2",
 | 
					        api_v: str = "v2",
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        '''
 | 
					        """
 | 
				
			||||||
        https://docs.kucoin.com/#authentication
 | 
					        https://docs.kucoin.com/#authentication
 | 
				
			||||||
        '''
 | 
					        """
 | 
				
			||||||
        now = int(time.time() * 1000)
 | 
					        now = int(time.time() * 1000)
 | 
				
			||||||
        path = f'/api/{api_v}{endpoint}'
 | 
					        path = f"/api/{api_v}{endpoint}"
 | 
				
			||||||
        str_to_sign = str(now) + action + path
 | 
					        str_to_sign = str(now) + action + path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        signature = base64.b64encode(
 | 
					        signature = base64.b64encode(
 | 
				
			||||||
            hmac.new(
 | 
					            hmac.new(
 | 
				
			||||||
                self._key_secret.encode('utf-8'),
 | 
					                self._key_secret.encode("utf-8"),
 | 
				
			||||||
                str_to_sign.encode('utf-8'),
 | 
					                str_to_sign.encode("utf-8"),
 | 
				
			||||||
                hashlib.sha256
 | 
					                hashlib.sha256,
 | 
				
			||||||
            ).digest()
 | 
					            ).digest()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        passphrase = base64.b64encode(
 | 
					        passphrase = base64.b64encode(
 | 
				
			||||||
            hmac.new(
 | 
					            hmac.new(
 | 
				
			||||||
                self._key_secret.encode('utf-8'),
 | 
					                self._key_secret.encode("utf-8"),
 | 
				
			||||||
                self._key_passphrase.encode('utf-8'),
 | 
					                self._key_passphrase.encode("utf-8"),
 | 
				
			||||||
                hashlib.sha256
 | 
					                hashlib.sha256,
 | 
				
			||||||
            ).digest()
 | 
					            ).digest()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,17 +128,16 @@ class Client:
 | 
				
			||||||
            "KC-API-TIMESTAMP": str(now),
 | 
					            "KC-API-TIMESTAMP": str(now),
 | 
				
			||||||
            "KC-API-KEY": self._key_id,
 | 
					            "KC-API-KEY": self._key_id,
 | 
				
			||||||
            "KC-API-PASSPHRASE": passphrase,
 | 
					            "KC-API-PASSPHRASE": passphrase,
 | 
				
			||||||
            "KC-API-KEY-VERSION": api_v[1]
 | 
					            "KC-API-KEY-VERSION": "2",
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _request(
 | 
					    async def _request(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        action: Literal["POST", "GET", "PUT", "DELETE"],
 | 
					        action: Literal["POST", "GET"],
 | 
				
			||||||
        endpoint: str,
 | 
					        endpoint: str,
 | 
				
			||||||
        api_v: str = "v2",
 | 
					        api_v: str = "v2",
 | 
				
			||||||
        headers: dict = {}
 | 
					        headers: dict = {},
 | 
				
			||||||
    ) -> Any:
 | 
					    ) -> Any:
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self._authenticated:
 | 
					        if self._authenticated:
 | 
				
			||||||
            headers = self._gen_auth_req_headers(action, endpoint, api_v)
 | 
					            headers = self._gen_auth_req_headers(action, endpoint, api_v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,6 +150,11 @@ class Client:
 | 
				
			||||||
            print(f'KUCOIN ERROR: {res.json()["msg"]}')
 | 
					            print(f'KUCOIN ERROR: {res.json()["msg"]}')
 | 
				
			||||||
            breakpoint()
 | 
					            breakpoint()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _get_ws_token(self, private: bool) -> str:
 | 
				
			||||||
 | 
					        token_type = "private" if private else "public"
 | 
				
			||||||
 | 
					        token = await self._request("POST", f"/bullet-{token_type}", "v1")
 | 
				
			||||||
 | 
					        return token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def get_pairs(
 | 
					    async def get_pairs(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
    ) -> dict[str, Any]:
 | 
					    ) -> dict[str, Any]:
 | 
				
			||||||
| 
						 | 
					@ -175,6 +201,7 @@ class Client:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def last_trades(self, sym: str):
 | 
					    async def last_trades(self, sym: str):
 | 
				
			||||||
        trades = await self._request("GET", f"/accounts/ledgers?currency={sym}", "v1")
 | 
					        trades = await self._request("GET", f"/accounts/ledgers?currency={sym}", "v1")
 | 
				
			||||||
 | 
					        breakpoint()
 | 
				
			||||||
        return trades.items
 | 
					        return trades.items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def get_bars(
 | 
					    async def get_bars(
 | 
				
			||||||
| 
						 | 
					@ -247,21 +274,6 @@ def fqsn_to_cf_sym(fqsn: str, pairs: dict[str, any]) -> str:
 | 
				
			||||||
    return pair_data["baseCurrency"] + "-" + pair_data["quoteCurrency"]
 | 
					    return pair_data["baseCurrency"] + "-" + pair_data["quoteCurrency"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_config() -> dict[str, Any]:
 | 
					 | 
				
			||||||
    conf, path = config.load()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    section = conf.get('kucoin')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # TODO: document why we send this, basically because logging params for cryptofeed
 | 
					 | 
				
			||||||
    conf["log"] = {}
 | 
					 | 
				
			||||||
    conf["log"]["disabled"] = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if section is None:
 | 
					 | 
				
			||||||
        log.warning("No config section found for deribit in kucoin")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return section
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@acm
 | 
					@acm
 | 
				
			||||||
async def get_client():
 | 
					async def get_client():
 | 
				
			||||||
    client = Client()
 | 
					    client = Client()
 | 
				
			||||||
| 
						 | 
					@ -312,6 +324,23 @@ async def stream_quotes(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        last_trades = await client.last_trades(sym)
 | 
					        last_trades = await client.last_trades(sym)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # @acm
 | 
				
			||||||
 | 
					        # async def subscribe(ws: wsproto.WSConnection):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        token = await client._get_ws_token(True)
 | 
				
			||||||
 | 
					        async with open_autorecon_ws(
 | 
				
			||||||
 | 
					            f"wss://ws-api-spot.kucoin.com/?token=={token}&[connectId={12345}]"
 | 
				
			||||||
 | 
					        ) as ws:
 | 
				
			||||||
 | 
					            msg_gen = stream_messageS(ws)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def stream_messageS(ws: NoBsWs) -> AsyncGenerator[NoBsWs, dict]:
 | 
				
			||||||
 | 
					    timeouts = 0
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        with trio.move_on_after(3) as cs:
 | 
				
			||||||
 | 
					            msg = await ws.recv_msg()
 | 
				
			||||||
 | 
					            print(f"msg: {msg}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@acm
 | 
					@acm
 | 
				
			||||||
async def open_history_client(
 | 
					async def open_history_client(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue