PK!l0˔%%aiobinance/__init__.pyfrom aiobinance.client import Client PK!Ʒaiobinance/client.pyfrom asyncio import AbstractEventLoop from aiobinance.modules.account import Account from aiobinance.modules.general import General from aiobinance.modules.market import Market from aiobinance.modules.stream import Stream from aiobinance.network import Network class Client: def __init__(self, api_key: str, api_secret: str, loop: AbstractEventLoop = None, timeout: int = None) -> None: self._http = Network(api_key, api_secret, loop, timeout) self.account = Account(self._http) self.general = General(self._http) self.market = Market(self._http) self.stream = Stream(self._http) async def close(self): await self._http.close() PK!X ppaiobinance/exceptions.pyfrom aiohttp import ClientResponse class BinanceException(Exception): pass class BinanceAPIException(BinanceException): def __init__(self, response: ClientResponse, response_json: dict): self.api_code = response_json['code'] self.api_message = response_json['msg'] self.http_code = response.status self.response = response self.request_info = response.request_info def __str__(self): # pragma: no cover return f'APIError(http_code={self.http_code}, api_code={self.api_code}): {self.api_message}' class BinanceResponseException(BinanceException): pass PK!Q#{{aiobinance/modules/account.pyfrom decimal import Decimal from typing import Union from aiobinance.modules.base import BaseModule from aiobinance.types import OrderSide, OrderType, TimeInForce, OrderResponseType, ApiVersion from aiobinance.utils import check_limit_max, check_mandatory_params class Account(BaseModule): """Account endpoints https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-endpoints """ async def new_order( self, symbol: str, side: OrderSide, order_type: OrderType, quantity: Union[Decimal, int, str], time_in_force: TimeInForce, price: Union[Decimal, int, str], new_client_order_id: str = None, stop_price: Union[Decimal, int, str] = None, iceberq_qty: Union[Decimal, int, str] = None, new_order_resp_type: OrderResponseType = None, recv_window: int = None, test: bool = False ): """Send in a new order. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#new-order--trade """ check_mandatory_params(**locals()) if iceberq_qty is not None and TimeInForce(time_in_force) != TimeInForce.GTC: raise ValueError('Any order with an icebergQty MUST have timeInForce set to GTC.') return await self._post( 'order/test' if test else 'order', params={ 'symbol': symbol, 'side': OrderSide(side), 'type': OrderType(order_type), 'timeInForce': TimeInForce(time_in_force), 'quantity': quantity, 'price': price, 'newClientOrderId': new_client_order_id, 'stopPrice': stop_price, 'icebergQty': iceberq_qty, 'newOrderRespType': new_order_resp_type, 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def get_order( self, symbol: str, order_id: int = None, orig_client_order_id: str = None, recv_window: int = None, ): """Check an order's status. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#query-order-user_data """ if order_id is None and orig_client_order_id is None: raise ValueError('Either orderId or origClientOrderId must be sent.') return await self._get( 'order', sign=True, params={ 'symbol': symbol, 'orderId': order_id, 'origClientOrderId': orig_client_order_id, 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def cancel_order( self, symbol: str, order_id: int = None, orig_client_order_id: str = None, new_client_order_id: str = None, recv_window: int = None, ): """Cancel an active order. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#cancel-order-trade """ if order_id is None and orig_client_order_id is None: raise ValueError('Either orderId or origClientOrderId must be sent.') return await self._delete( 'order', sign=True, params={ 'symbol': symbol, 'orderId': order_id, 'origClientOrderId': orig_client_order_id, 'newClientOrderId': new_client_order_id, 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def open_orders( self, symbol: str = None, recv_window: int = None, ): """Get all open orders on a symbol. Careful when accessing this with no symbol. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#current-open-orders-user_data """ return await self._get( 'openOrders', sign=True, params={ 'symbol': symbol, 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def all_orders( self, symbol: str, order_id: int = None, start_time: int = None, end_time: int = None, limit: int = None, recv_window: int = None, ): """Get all account orders; active, canceled, or filled. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#all-orders-user_data """ return await self._get( 'allOrders', sign=True, params={ 'symbol': symbol, 'orderId': order_id, 'startTime': start_time, 'endTime': end_time, 'limit': check_limit_max(limit), 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def info( self, recv_window: int = None, ): """Get current account information. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-information-user_data """ return await self._get( 'account', sign=True, params={ 'recvWindow': recv_window, }, version=ApiVersion.V3 ) async def trades( self, symbol: str, start_time: int = None, end_time: int = None, from_id: int = None, limit: int = None, recv_window: int = None, ): """Get trades for a specific account and symbol. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-trade-list-user_data """ return await self._get( 'myTrades', sign=True, params={ 'symbol': symbol, 'fromId': from_id, 'startTime': start_time, 'endTime': end_time, 'limit': check_limit_max(limit), 'recvWindow': recv_window, }, version=ApiVersion.V3 ) PK!#aiobinance/modules/base.pyfrom abc import ABC from enum import Enum from typing import Dict, Optional from aiobinance.network import Network from aiobinance.types import ApiVersion class BaseModule(ABC): def __init__(self, network): self._http: Network = network async def _get( self, path: str, sign: bool = False, params: Optional[Dict] = None, version: ApiVersion = ApiVersion.V1 ): return await self._http.get(path, sign=sign, params=self._filter_params(params), version=ApiVersion(version)) async def _post( self, path: str, sign: bool = True, params: Optional[Dict] = None, version: ApiVersion = ApiVersion.V1 ): return await self._http.post(path, version, sign=sign, params=self._filter_params(params)) async def _put( self, path: str, sign: bool = True, params: Optional[Dict] = None, version: ApiVersion = ApiVersion.V1 ): return await self._http.put(path, version, sign=sign, params=self._filter_params(params)) async def _delete( self, path: str, sign: bool = True, params: Optional[Dict] = None, version: ApiVersion = ApiVersion.V1 ): return await self._http.delete(path, version, sign=sign, params=self._filter_params(params)) @staticmethod def _filter_params(params: Dict) -> Dict: return { k: v.value if isinstance(v, Enum) else v for k, v in params.items() if v is not None } PK![z$$aiobinance/modules/general.pyfrom typing import Dict from aiobinance.modules.base import BaseModule class General(BaseModule): """General endpoints https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#general-endpoints """ async def ping(self) -> Dict: """Test connectivity to the Rest API. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#test-connectivity """ return await self._get('ping') async def time(self) -> Dict[str, int]: """Test connectivity to the Rest API and get the current server time. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#check-server-time """ return await self._get('time') async def exchange_info(self) -> Dict: """Current exchange trading rules and symbol information. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#exchange-information """ return await self._get('exchangeInfo') PK!5/aiobinance/modules/market.pyfrom typing import Dict, List, Any, Union from aiobinance.modules.base import BaseModule from aiobinance.types import Interval, ApiVersion from aiobinance.utils import check_limit_max, check_limit_values class Market(BaseModule): """Market Data endpoints https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#market-data-endpoints """ async def order_book(self, symbol: str, limit: int = 100) -> Dict[str, Any]: """Order book https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book """ return await self._get( 'depth', params={ 'symbol': symbol, 'limit': check_limit_values(limit) } ) async def recent_trades(self, symbol: str, limit: int = None) -> List[Dict[str, Any]]: """Get recent trades (up to last 500). https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#recent-trades-list """ return await self._get( 'trades', params={ 'symbol': symbol, 'limit': check_limit_max(limit) } ) async def historical_trades(self, symbol: str, limit: int = None, from_id: int = None) -> List[Dict[str, Any]]: """Get older trades. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#old-trade-lookup-market_data """ return await self._get( 'historicalTrades', params={ 'symbol': symbol, 'limit': check_limit_max(limit), 'fromId': from_id } ) async def agg_trades( self, symbol: str, from_id: int = None, start_time: int = None, end_time: int = None, limit: int = None ) -> Dict: """Get compressed, aggregate trades. Trades that fill at the time, from the same order, with the same price will have the quantity aggregated. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list """ return await self._get( 'historicalTrades', params={ 'symbol': symbol, 'fromId': from_id, 'startTime': start_time, 'endTime': end_time, 'limit': check_limit_max(limit), } ) async def klines( self, symbol: str, interval: Interval, start_time: int = None, end_time: int = None, limit: int = None ) -> List[List]: """Kline/candlestick bars for a symbol. Klines are uniquely identified by their open time. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data """ return await self._get( 'klines', params={ 'symbol': symbol, 'interval': Interval(interval).value, 'startTime': start_time, 'endTime': end_time, 'limit': limit, } ) async def avg_price(self, symbol: str) -> Dict[str, Any]: """Current average price for a symbol. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#current-average-price """ return await self._get( 'avgPrice', params={ 'symbol': symbol, }, version=ApiVersion.V3 ) async def ticker_24hr(self, symbol: str = None) -> Union[Dict[str, Any], List[Dict[str, Any]]]: """24 hour rolling window price change statistics. Careful when accessing this with no symbol. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#current-average-price """ return await self._get( 'ticker/24hr', params={ 'symbol': symbol, } ) async def ticker_price(self, symbol: str = None) -> Union[Dict[str, Any], List[Dict[str, Any]]]: """Latest price for a symbol or symbols. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#symbol-price-ticker """ return await self._get( 'ticker/price', params={ 'symbol': symbol, } ) PK!Կ3W  aiobinance/modules/stream.pyfrom typing import Dict from aiobinance.modules.base import BaseModule from aiobinance.types import ApiVersion class Stream(BaseModule): """Start a new user data stream. The stream will close after 60 minutes unless a keepalive is sent. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#user-data-stream-endpoints """ async def start(self) -> Dict: """Start a new user data stream. The stream will close after 60 minutes unless a keepalive is sent. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#start-user-data-stream-user_stream """ return await self._post('userDataStream', version=ApiVersion.V1) async def keepalive(self, listen_key: str) -> Dict: """Keepalive a user data stream to prevent a time out. User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#keepalive-user-data-stream-user_stream """ return await self._put('userDataStream', params={'listenKey': listen_key}, version=ApiVersion.V1) async def close(self, listen_key: str) -> Dict: """Close out a user data stream. https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#close-user-data-stream-user_stream """ return await self._delete('userDataStream', params={'listenKey': listen_key}, version=ApiVersion.V1) PK!ĚCCaiobinance/network.pyimport asyncio import hashlib import hmac from asyncio import AbstractEventLoop from time import time from typing import Optional, Dict from urllib.parse import urlencode from aiohttp import ClientSession, ClientTimeout, ClientResponse, ContentTypeError from aiobinance.exceptions import BinanceAPIException, BinanceResponseException from aiobinance.types import HttpMethod, ApiVersion class Network: _API_URL = 'https://api.binance.com/api' _PUBLIC_API_VERSION = 'v1' _PRIVATE_API_VERSION = 'v3' def __init__(self, api_key: str, api_secret: str, loop: AbstractEventLoop = None, timeout: int = 10): self._API_KEY = api_key self._API_SECRET = api_secret self._loop = loop or asyncio.get_event_loop() self._session = self._init_session(timeout) def _generate_signature(self, params): return hmac.new( self._API_SECRET.encode('u8'), urlencode(params).encode('u8'), hashlib.sha256 ).hexdigest() def _init_session(self, timeout: int) -> ClientSession: return ClientSession( headers={ 'Accept': 'application/json', 'User-Agent': 'aiobinance/python', 'X-MBX-APIKEY': self._API_KEY }, timeout=ClientTimeout(total=timeout), loop=self._loop ) async def close(self, delay: float = 0.250): '''Graceful shutdown. https://docs.aiohttp.org/en/stable/client_advanced.html#graceful-shutdown ''' await asyncio.sleep(delay) await self._session.close() def _create_api_uri(self, path: str, version: ApiVersion) -> str: return f'{self._API_URL}/{version.value}/{path}' async def _request_api( self, method: HttpMethod, path: str, version: ApiVersion, private: bool = False, params: Optional[dict] = None, ): return await self._request( method, self._create_api_uri(path, version), private, params ) async def _request(self, method: HttpMethod, url: str, sign: bool = False, params: Optional[dict] = None): params = params or {} if sign: params['timestamp'] = self._get_nonce() params['signature'] = self._generate_signature(params) async with self._session as session: http_method = getattr(session, method.value) async with http_method(url, params=params) as response: return await self._handle_response(response) @staticmethod async def _handle_response(response: ClientResponse): try: response_json = await response.json() except ContentTypeError: raise BinanceResponseException(f'Invalid response: {await response.text()!r}') else: if 200 <= response.status < 300: return response_json raise BinanceAPIException(response, response_json) async def get(self, path: str, version: ApiVersion, sign: bool = False, params: Optional[Dict] = None): return await self._request_api(HttpMethod.GET, path, version, sign, params) async def post(self, path: str, version: ApiVersion, sign: bool = False, params: Optional[Dict] = None): return await self._request_api(HttpMethod.POST, path, version, sign, params) async def put(self, path: str, version: ApiVersion, sign: bool = False, params: Optional[Dict] = None): return await self._request_api(HttpMethod.PUT, path, version, sign, params) async def delete(self, path: str, version: ApiVersion, sign: bool = False, params: Optional[Dict] = None): return await self._request_api(HttpMethod.DELETE, path, version, sign, params) @staticmethod def _get_nonce() -> int: return int(time() * 1000) PK!aiobinance/tests/__init__.pyPK!paiobinance/tests/test_client.pyimport asyncio from unittest.mock import patch import pytest from asynctest import CoroutineMock from aiobinance import Client from aiobinance.modules.account import Account from aiobinance.modules.general import General from aiobinance.modules.market import Market from aiobinance.modules.stream import Stream from aiobinance.network import Network @pytest.fixture() async def client(): loop = asyncio.get_event_loop() c = Client('key', 'secret', loop=loop, timeout=55) yield c await c.close() def test_api_key(): with pytest.raises(TypeError): c = Client() @pytest.mark.asyncio async def test_client_init(client): assert isinstance(client._http, Network) assert client._http._API_KEY == 'key' assert client._http._API_SECRET == 'secret' assert client._http._session._timeout.total == 55 @pytest.mark.asyncio async def test_loop(): loop = asyncio.get_event_loop() c = Client('key', 'secret', loop=loop) assert c._http._loop == loop assert c._http._session._loop == loop @pytest.mark.asyncio async def test_modules_init(client): assert isinstance(client.account, Account) assert isinstance(client.general, General) assert isinstance(client.market, Market) assert isinstance(client.stream, Stream) @pytest.mark.asyncio async def test_close_session(client): with patch('aiobinance.network.Network.close', new_callable=CoroutineMock) as m: await client.close() m.assert_called_once_with() PK!aiobinance/tests/test_utils.pyimport pytest from aiobinance.types import OrderType from aiobinance.utils import check_limit_max, check_mandatory_params, check_limit_values def test_check_limit_max(): assert check_limit_max(0) == 0 assert check_limit_max(None) == None assert check_limit_max(limit=500, limit_max=1000) == 500 assert check_limit_max(limit=1000, limit_max=1000) == 1000 with pytest.raises(ValueError): assert check_limit_max(limit=2000, limit_max=1000) def test_check_mandatory_params(): with pytest.raises(KeyError): check_mandatory_params(**{}) with pytest.raises(ValueError): check_mandatory_params(**{'order_type': 'wrong'}) with pytest.raises(ValueError): check_mandatory_params(**{'order_type': OrderType.MARKET}) with pytest.raises(ValueError): check_mandatory_params(**{'order_type': 'MARKET'}) with pytest.raises(ValueError): check_mandatory_params(**{'order_type': 'MARKET', 'quantity': None}) assert check_mandatory_params(**{'order_type': 'MARKET', 'quantity': '123'}) is None def test_check_limit_values(): assert check_limit_values(limit=5, valid_limits=(5, 10)) == 5 with pytest.raises(ValueError): assert check_limit_values(3, (5, 10)) PK!c%aiobinance/types.pyfrom enum import Enum class ApiVersion(Enum): V1 = 'v1' V3 = 'v3' class HttpMethod(Enum): GET = 'get' POST = 'post' PUT = 'put' DELETE = 'delete' class OrderSide(Enum): BUY = 'buy' SELL = 'sell' class OrderType(Enum): LIMIT = 'LIMIT' MARKET = 'MARKET' STOP_LOSS = 'STOP_LOSS' STOP_LOSS_LIMIT = 'STOP_LOSS_LIMIT' TAKE_PROFIT = 'TAKE_PROFIT' TAKE_PROFIT_LIMIT = 'TAKE_PROFIT_LIMIT' LIMIT_MAKER = 'LIMIT_MAKER' class Interval(Enum): MIN1 = '1m' MIN3 = '3m' MIN5 = '5m' MIN15 = '15m' MIN30 = '30m' HOUR1 = '1h' HOUR2 = '2h' HOUR4 = '4h' HOUR6 = '6h' HOUR8 = '8h' HOUR12 = '12h' DAY1 = '1d' DAY3 = '3d' WEEK1 = '1w' MONTH1 = '1M' class TimeInForce(Enum): GTC = 'GTC' # Good Till Cancelled IOC = 'IOC' # Fill Or Kill FOK = 'FOK' # Immediate Or Cancel class OrderResponseType(Enum): ACK = 'ACK' RESULT = 'RESULT' FULL = 'FULL' PK!aiobinance/utils.pyfrom typing import Dict, Tuple, Optional from aiobinance.types import OrderType def check_limit_max(limit: Optional[int], limit_max: int = 1000) -> int: if limit and limit > limit_max: raise ValueError(f'Invalid limit. Limit max: {limit_max}') return limit def check_mandatory_params(**params: Dict): order_type: OrderType = OrderType(params['order_type']) mandatory_params = { OrderType.LIMIT: ('time_in_force', 'quantity', 'price',), OrderType.MARKET: ('quantity',), OrderType.STOP_LOSS: ('quantity', 'stop_price'), OrderType.STOP_LOSS_LIMIT: ('time_in_force', 'quantity', 'price', 'stop_price'), OrderType.TAKE_PROFIT: ('quantity', 'stop_price'), OrderType.TAKE_PROFIT_LIMIT: ('time_in_force', 'quantity', 'price', 'stop_price'), OrderType.LIMIT_MAKER: ('quantity', 'stop_price'), }.get(order_type) for param in mandatory_params: if params.get(param) is None: raise ValueError(f'Mandatory parameters for the order type {order_type.value}: {mandatory_params}') def check_limit_values(limit: int, valid_limits: Tuple[int, ...] = (5, 10, 20, 50, 100, 500, 1000)) -> int: if limit not in valid_limits: raise ValueError(f'Invalid limit. Valid limits: {valid_limits}') return limit PK!H STT aiobinance-0.1.0.dist-info/WHEEL 1 0 нR. \I$ơ7.ZON `h6oi14m,b4>4ɛpK>X;baP>PK!Hx~#aiobinance-0.1.0.dist-info/METADATAN0~=D*Y@DFyIR8m ͳ3E`|P8cV&2hb?Zwn>u :@ŭ59jʴn]=V׊UC&|fE>u9/'H7W=G<"*ӳ$10(m*@XJ(6NGϕ[Qke$61=7ϧ*nctps_O7PK!H~,N!aiobinance-0.1.0.dist-info/RECORD}ɖH}= d2B $b0 2m-k>UT3 *𣙈.0[jE ҆rݨqxR5B+[~A!-1OFNC2+U<b JK ?cy]u ICTXKp˒~J1+Or_׀7^Y_>8oK|=ZH 6dOI#x}/H}-O9$|=5_0G9}ӗc6"oqwZ *<4Wq:Fd#yCE[fcxfAs wws+i)n#Y5!c3_$?eJ|>FJ &I1! (#CJWĴw 5V|Qz{TZƵ֥!!eg0N{ IrH\2y(nwizs*̍o]JK>w=WI! O&q/){x26kRt+x:c\CBĚБUّ؟fFFVҏ^мYҵ`EO ďPK!l0˔%%aiobinance/__init__.pyPK!ƷYaiobinance/client.pyPK!X pp<aiobinance/exceptions.pyPK!Q#{{aiobinance/modules/account.pyPK!#aiobinance/modules/base.pyPK![z$$%aiobinance/modules/general.pyPK!5/*aiobinance/modules/market.pyPK!Կ3W  E<aiobinance/modules/stream.pyPK!ĚCCBaiobinance/network.pyPK!Raiobinance/tests/__init__.pyPK!p