diff --git a/README.md b/README.md index 51e67e4..5402d9f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Wallex-python v.0.0.1 +Wallex-python v.0.0.6 =========================================================== ## Wallex Python SDK diff --git a/examples/async/private/cancel_order.py b/examples/async/private/cancel_order.py new file mode 100644 index 0000000..a266ed2 --- /dev/null +++ b/examples/async/private/cancel_order.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.cancel_order(order_id='ORDER_ID') + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/create_new_order.py b/examples/async/private/create_new_order.py new file mode 100644 index 0000000..e8ee4db --- /dev/null +++ b/examples/async/private/create_new_order.py @@ -0,0 +1,19 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.place_order( + symbol='USDTTMN', + side='buy', + order_type='limit', + quantity=11, + price=25000, + client_id='CLIENT_ID', + ) + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_account_balances.py b/examples/async/private/get_account_balances.py new file mode 100644 index 0000000..e701eea --- /dev/null +++ b/examples/async/private/get_account_balances.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_balances() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_account_crypto_deposit_list.py b/examples/async/private/get_account_crypto_deposit_list.py new file mode 100644 index 0000000..88254da --- /dev/null +++ b/examples/async/private/get_account_crypto_deposit_list.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_crypto_deposit_list() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_account_crypto_withdrawal_list.py b/examples/async/private/get_account_crypto_withdrawal_list.py new file mode 100644 index 0000000..dd922a3 --- /dev/null +++ b/examples/async/private/get_account_crypto_withdrawal_list.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_crypto_withdrawal_list() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_account_open_orders.py b/examples/async/private/get_account_open_orders.py new file mode 100644 index 0000000..72e09b1 --- /dev/null +++ b/examples/async/private/get_account_open_orders.py @@ -0,0 +1,16 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + + data = await client.get_open_orders() + data2 = await client.get_open_orders(symbol='USDTTMN') + + print(data) + print(data2) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_account_recent_trades.py b/examples/async/private/get_account_recent_trades.py new file mode 100644 index 0000000..a3fa3e1 --- /dev/null +++ b/examples/async/private/get_account_recent_trades.py @@ -0,0 +1,20 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + + data = await client.get_trades() + data2 = await client.get_trades(symbol='USDTTMN') + data3 = await client.get_trades(side='buy') + data4 = await client.get_trades(symbol='USDTTMN', side='buy') + + print(data) + print(data2) + print(data3) + print(data4) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_order_status.py b/examples/async/private/get_order_status.py new file mode 100644 index 0000000..9afb940 --- /dev/null +++ b/examples/async/private/get_order_status.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_order_detail(order_id='ORDER_ID') + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_user_banking_accounts.py b/examples/async/private/get_user_banking_accounts.py new file mode 100644 index 0000000..973044b --- /dev/null +++ b/examples/async/private/get_user_banking_accounts.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_banking_accounts() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_user_banking_cards.py b/examples/async/private/get_user_banking_cards.py new file mode 100644 index 0000000..e7ee709 --- /dev/null +++ b/examples/async/private/get_user_banking_cards.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_banking_cards() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/async/private/get_user_fee_data.py b/examples/async/private/get_user_fee_data.py new file mode 100644 index 0000000..7c89003 --- /dev/null +++ b/examples/async/private/get_user_fee_data.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_fee_levels() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/private/get_user_profile_data.py b/examples/async/private/get_user_profile_data.py new file mode 100644 index 0000000..49dc9e3 --- /dev/null +++ b/examples/async/private/get_user_profile_data.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_profile() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/public/get_currencies.py b/examples/async/public/get_currencies.py new file mode 100644 index 0000000..b1b6968 --- /dev/null +++ b/examples/async/public/get_currencies.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_currencies() + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/public/get_market_stats.py b/examples/async/public/get_market_stats.py new file mode 100644 index 0000000..8d5bda5 --- /dev/null +++ b/examples/async/public/get_market_stats.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_markets() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/async/public/get_ohlc_data.py b/examples/async/public/get_ohlc_data.py new file mode 100644 index 0000000..2497d3e --- /dev/null +++ b/examples/async/public/get_ohlc_data.py @@ -0,0 +1,20 @@ +import asyncio +from wallex.async_client import AsyncClient +import datetime + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + + data = await client.get_market_candles( + symbol='USDTTMN', + resolution='1', + _from=datetime.datetime(2022, 7, 1), + _to=datetime.datetime(2022, 7, 10) + ) + + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/public/get_orderbook.py b/examples/async/public/get_orderbook.py new file mode 100644 index 0000000..0f509b1 --- /dev/null +++ b/examples/async/public/get_orderbook.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_market_orders(symbol='USDTTMN') + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/async/public/get_recent_trades.py b/examples/async/public/get_recent_trades.py new file mode 100644 index 0000000..91ac414 --- /dev/null +++ b/examples/async/public/get_recent_trades.py @@ -0,0 +1,12 @@ +import asyncio +from wallex.async_client import AsyncClient + + +async def main(): + client = await AsyncClient.create_client(api_key='PUT YOUR API KEY HERE') + data = await client.get_market_trades(symbol='USDTTMN') + print(data) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/sync/private/cancel_order.py b/examples/sync/private/cancel_order.py new file mode 100644 index 0000000..68c12d6 --- /dev/null +++ b/examples/sync/private/cancel_order.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.cancel_order(order_id='ORDER_ID') + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/create_new_order.py b/examples/sync/private/create_new_order.py new file mode 100644 index 0000000..129cabd --- /dev/null +++ b/examples/sync/private/create_new_order.py @@ -0,0 +1,18 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.place_order( + symbol='USDTTMN', + side='buy', + order_type='limit', + quantity=11, + price=25000, + client_id='CLIENT_ID', + ) + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/get_wallex_markets.py b/examples/sync/private/get_account_balances.py similarity index 72% rename from examples/get_wallex_markets.py rename to examples/sync/private/get_account_balances.py index 07a7d71..1636eea 100644 --- a/examples/get_wallex_markets.py +++ b/examples/sync/private/get_account_balances.py @@ -3,8 +3,8 @@ def main(): client = Client(api_key='PUT YOUR API KEY HERE') - markets = client.get_markets() - print(markets) + data = client.get_balances() + print(data) if __name__ == '__main__': diff --git a/examples/sync/private/get_account_crypto_deposit_list.py b/examples/sync/private/get_account_crypto_deposit_list.py new file mode 100644 index 0000000..99ce79d --- /dev/null +++ b/examples/sync/private/get_account_crypto_deposit_list.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_crypto_deposit_list() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_account_crypto_withdrawal_list.py b/examples/sync/private/get_account_crypto_withdrawal_list.py new file mode 100644 index 0000000..002636b --- /dev/null +++ b/examples/sync/private/get_account_crypto_withdrawal_list.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_crypto_withdrawal_list() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_account_open_orders.py b/examples/sync/private/get_account_open_orders.py new file mode 100644 index 0000000..a7b1126 --- /dev/null +++ b/examples/sync/private/get_account_open_orders.py @@ -0,0 +1,13 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_open_orders() + data2 = client.get_open_orders(symbol='USDTTMN') + print(data) + print(data2) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_account_recent_trades.py b/examples/sync/private/get_account_recent_trades.py new file mode 100644 index 0000000..76e361d --- /dev/null +++ b/examples/sync/private/get_account_recent_trades.py @@ -0,0 +1,18 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_trades() + data2 = client.get_trades(symbol='USDTTMN') + data3 = client.get_trades(side='buy') + data4 = client.get_trades(symbol='USDTTMN', side='buy') + + print(data) + print(data2) + print(data3) + print(data4) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_order_status.py b/examples/sync/private/get_order_status.py new file mode 100644 index 0000000..9d08f0b --- /dev/null +++ b/examples/sync/private/get_order_status.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_order_detail(order_id='ORDER_ID') + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_user_banking_accounts.py b/examples/sync/private/get_user_banking_accounts.py new file mode 100644 index 0000000..911cedf --- /dev/null +++ b/examples/sync/private/get_user_banking_accounts.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_banking_accounts() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_user_banking_cards.py b/examples/sync/private/get_user_banking_cards.py new file mode 100644 index 0000000..1d29d12 --- /dev/null +++ b/examples/sync/private/get_user_banking_cards.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_banking_cards() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_user_fee_data.py b/examples/sync/private/get_user_fee_data.py new file mode 100644 index 0000000..e70fde8 --- /dev/null +++ b/examples/sync/private/get_user_fee_data.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_fee_levels() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/private/get_user_profile_data.py b/examples/sync/private/get_user_profile_data.py new file mode 100644 index 0000000..70b69ab --- /dev/null +++ b/examples/sync/private/get_user_profile_data.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_profile() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/public/get_currencies.py b/examples/sync/public/get_currencies.py new file mode 100644 index 0000000..593135b --- /dev/null +++ b/examples/sync/public/get_currencies.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_currencies() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/public/get_market_stats.py b/examples/sync/public/get_market_stats.py new file mode 100644 index 0000000..d29aa58 --- /dev/null +++ b/examples/sync/public/get_market_stats.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_markets() + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/public/get_ohlc_data.py b/examples/sync/public/get_ohlc_data.py new file mode 100644 index 0000000..a447b58 --- /dev/null +++ b/examples/sync/public/get_ohlc_data.py @@ -0,0 +1,17 @@ +from wallex.client import Client +import datetime + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_market_candles( + symbol='USDTTMN', + resolution='1', + _from=datetime.datetime(2022, 7, 1), + _to=datetime.datetime(2022, 7, 10) + ) + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/public/get_orderbook.py b/examples/sync/public/get_orderbook.py new file mode 100644 index 0000000..032c493 --- /dev/null +++ b/examples/sync/public/get_orderbook.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_market_orders(symbol='USDTTMN') + print(data) + + +if __name__ == '__main__': + main() diff --git a/examples/sync/public/get_recent_trades.py b/examples/sync/public/get_recent_trades.py new file mode 100644 index 0000000..1c38951 --- /dev/null +++ b/examples/sync/public/get_recent_trades.py @@ -0,0 +1,11 @@ +from wallex.client import Client + + +def main(): + client = Client(api_key='PUT YOUR API KEY HERE') + data = client.get_market_trades(symbol='USDTTMN') + print(data) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt index e69de29..079167b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,7 @@ +certifi==2022.6.15 +charset-normalizer==2.1.0 +idna==3.3 +pydantic==1.9.2 +requests==2.28.1 +typing_extensions==4.3.0 +urllib3==1.26.11 diff --git a/setup.cfg b/setup.cfg index 7a1f31d..62b6647 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = wallex-python -version = 0.0.1 +version = 0.0.9 author = Pourya Moghadam author_email = p.moghadam@msn.com description = Wallex Exchange Unofficial API Python Client @@ -8,8 +8,7 @@ long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/wallexchange/wallex-python license = MIT -keywords = wallex exchange api bitcoin ethereum btc eth python client -dependencies= pydantic requests +keywords = wallex exchange crypto api bitcoin ethereum btc eth python client project_urls = Bug Tracker = https://github.com/wallexchange/wallex-python/issues Documentation = https://api-docs.wallex.ir/ diff --git a/setup.py b/setup.py index 36f93da..46c16d4 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="wallex-python", - version="0.0.1", + version="0.0.9", author="Pourya Moghadam", author_email="p.moghadam@msn.com", description="Wallex Exchange Unofficial API Python Client", @@ -14,8 +14,11 @@ long_description_content_type="text/markdown", url="https://github.com/wallexchange/wallex-python", license="MIT", - keywords='wallex exchange api bitcoin ethereum btc eth python', - dependencies=['pydantic', 'requests'], + keywords='wallex crypto exchange api bitcoin ethereum btc eth python', + install_requires=[ + 'requests==2.28.1', + 'pydantic==1.9.2' + ], project_urls={ "Bug Tracker": "https://github.com/wallexchange/wallex-python/issues", "Documentation": "https://api-docs.wallex.ir/", diff --git a/src/wallex/__init__.py b/src/wallex/__init__.py index e69de29..aebbac3 100644 --- a/src/wallex/__init__.py +++ b/src/wallex/__init__.py @@ -0,0 +1,9 @@ +from wallex.client import Client +from wallex.async_client import AsyncClient + + +__all__ = ['Client', 'AsyncClient'] + + +__version__ = '0.0.9' +__author__ = 'Pourya Moghadam' diff --git a/src/wallex/_base_client.py b/src/wallex/_base_client.py new file mode 100644 index 0000000..bf56866 --- /dev/null +++ b/src/wallex/_base_client.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod + + +class _Base(ABC): + ORDER_TYPE_LIMIT = 'LIMIT' + ORDER_TYPE_MARKET = 'MARKET' + + ORDER_SIDE_BUY = 'BUY' + ORDER_SIDE_SELL = 'SELL' + + @abstractmethod + def get_markets(self): + raise NotImplementedError + + @abstractmethod + def get_currencies(self): + raise NotImplementedError + + @abstractmethod + def get_market_orders(self, symbol: str): + raise NotImplementedError + + @abstractmethod + def get_market_trades(self, symbol: str): + raise NotImplementedError + + @abstractmethod + def get_market_candles(self, symbol: str, resolution: str, _from, _to): + raise NotImplementedError + + @abstractmethod + def get_profile(self): + raise NotImplementedError + + @abstractmethod + def get_fee_levels(self): + raise NotImplementedError + + @abstractmethod + def get_banking_cards(self): + raise NotImplementedError + + @abstractmethod + def get_banking_accounts(self): + raise NotImplementedError + + @abstractmethod + def get_balances(self): + raise NotImplementedError + + @abstractmethod + def get_crypto_withdrawal_list(self): + raise NotImplementedError + + @abstractmethod + def get_crypto_deposit_list(self): + raise NotImplementedError + + @abstractmethod + def place_order( + self, symbol: str, order_type: str, side: str, quantity: float, price: float, client_id: str = None + ): + raise NotImplementedError + + @abstractmethod + def get_order_detail(self, order_id: str): + raise NotImplementedError + + @abstractmethod + def cancel_order(self, order_id: str): + raise NotImplementedError + + @abstractmethod + def get_open_orders(self, symbol: str = None): + raise NotImplementedError + + @abstractmethod + def get_trades(self, symbol: str = None, side: str = None): + raise NotImplementedError + + @abstractmethod + def order_market(self, symbol: str, side: str, quantity: float, client_id: str = None): + raise NotImplementedError + + @abstractmethod + def order_limit(self, symbol: str, side: str, quantity: float, price: float, client_id: str = None): + raise NotImplementedError + + @abstractmethod + def order_market_buy(self, symbol: str, quantity: float, client_id: str = None): + raise NotImplementedError + + @abstractmethod + def order_market_sell(self, symbol: str, quantity: float, client_id: str = None): + raise NotImplementedError + + @abstractmethod + def order_limit_buy(self, symbol: str, quantity: float, price: float, client_id: str = None): + raise NotImplementedError + + @abstractmethod + def order_limit_sell(self, symbol: str, quantity: float, price: float, client_id: str = None): + raise NotImplementedError diff --git a/src/wallex/async_client.py b/src/wallex/async_client.py new file mode 100644 index 0000000..ee29e4f --- /dev/null +++ b/src/wallex/async_client.py @@ -0,0 +1,195 @@ +from pydantic import validate_arguments + +from wallex.request import AsyncRequestsApi +from wallex.schema import MarketsResponseModel, CurrenciesResponseModel, MarketOrderDetailModel, ClientSymbolInput, \ + MarketTradesModel, MarketCandlesRequestModel, MarketCandlesResponseModel, AccountFeeLevelsResponseModel, \ + AccountBalancesResponseModel, PlaceOrderRequestModel, PlaceOrderResponseModel, LastTradesResponseModel +from wallex.utils import create_list_by_symbol, create_list + +from wallex._base_client import _Base + + +class AsyncClient(_Base): + """ + :param api_key: str = Wallex API key (required) + :method host_is_online: bool = Returns True if Wallex API is online + :method get_profile: dict = Returns user info + :return: WallexClient + """ + + @validate_arguments + def __init__(self, api_key: str, loop=None) -> None: + self.loop = loop + self._consumer = AsyncRequestsApi( + base_url='https://api.wallex.ir', + headers={"X-API-Key": api_key}, + loop=loop + ) + + @classmethod + async def create_client(cls, api_key: str = None, loop=None): + self = cls(api_key, loop) + + if api_key: + try: + self.client_user = await self.get_profile() + print('client user:', self.client_user['first_name'] + ' ' + self.client_user['last_name']) + except Exception as e: + raise Exception('can not access wallex api online server', e) + + return self + + async def get_markets(self): + res = await self._consumer.get('/v1/markets') + json_res = await res.json() + symbols = json_res['result']['symbols'] + return create_list_by_symbol(symbols, MarketsResponseModel) + + async def get_currencies(self): + res = await self._consumer.get('/v1/currencies/stats') + json_res = await res.json() + return create_list(json_res['result'], CurrenciesResponseModel) + + async def get_market_orders(self, symbol: str): + params = ClientSymbolInput(symbol=symbol) + res = await self._consumer.get('/v1/depth', params=params.dict()) + json_res = await res.json() + return { + 'ask': create_list(json_res['result']['ask'], MarketOrderDetailModel), + 'bid': create_list(json_res['result']['bid'], MarketOrderDetailModel) + } + + async def get_market_trades(self, symbol: str): + params = ClientSymbolInput(symbol=symbol) + res = await self._consumer.get('/v1/trades?', params=params.dict()) + json_res = await res.json() + return { + 'latestTrades': create_list(json_res['result']['latestTrades'], MarketTradesModel) + } + + async def get_market_candles(self, symbol: str, resolution: str, _from, _to): + params = MarketCandlesRequestModel(symbol=symbol, resolution=resolution, start_time=_from, end_time=_to).dict() + res = await self._consumer.get('/v1/udf/history?', params={ + 'symbol': params['symbol'], + 'resolution': params['resolution'], + 'from': int(round(params['start_time'].timestamp())), + 'to': int(round(params['end_time'].timestamp())) + }) + json_res = await res.json() + checked_r = MarketCandlesResponseModel(**json_res) + return checked_r.dict() + + async def get_profile(self): + res = await self._consumer.get('/v1/account/profile') + json_res = await res.json() + return json_res['result'] + + async def get_fee_levels(self): + res = await self._consumer.get('/v1/account/fee') + json_res = await res.json() + return create_list_by_symbol(json_res['result'], AccountFeeLevelsResponseModel) + + # TODO: get 401 auth error when calling this method + async def get_banking_cards(self): + res = await self._consumer.get('/v1/account/card-numbers') + json_res = await res.json() + return json_res['result'] + + # TODO: get 500 server error when calling this method + async def get_banking_accounts(self): + res = await self._consumer.get('/v1/account/ibans') + json_res = await res.json() + return json_res['result'] + + async def get_balances(self): + temp_balances = {} + res = await self._consumer.get('/v1/account/balances') + json_res = await res.json() + balances = create_list_by_symbol(json_res['result']['balances'], AccountBalancesResponseModel) + for balance in balances: + temp_balances.update(balance) + return { + 'balances': temp_balances + } + + async def get_crypto_withdrawal_list(self): + res = await self._consumer.get('/v1/account/crypto-withdrawal') + json_res = await res.json() + return json_res['result'] + + async def get_crypto_deposit_list(self): + res = await self._consumer.get('/v1/account/crypto-deposit') + json_res = await res.json() + return json_res['result'] + + async def place_order( + self, symbol: str, order_type: str, side: str, quantity: float, price: float = None, client_id: str = None + ): + params = PlaceOrderRequestModel( + symbol=symbol, order_type=order_type, side=side, + quantity=quantity, price=price, client_id=client_id + ).dict(exclude_none=True) + res = await self._consumer.post('/v1/account/orders', json=params) + json_res = await res.json() + return PlaceOrderResponseModel(**json_res['result']).dict() + + async def order_market(self, symbol: str, side: str, quantity: float, client_id: str = None): + return await self.place_order( + symbol=symbol, order_type=self.ORDER_TYPE_MARKET, side=side, quantity=quantity, client_id=client_id + ) + + async def order_limit(self, symbol: str, side: str, quantity: float, price: float, client_id: str = None): + return await self.place_order( + symbol=symbol, order_type=self.ORDER_TYPE_LIMIT, side=side, + quantity=quantity, price=price, client_id=client_id + ) + + async def order_market_buy(self, symbol: str, quantity: float, client_id: str = None): + return await self.order_market( + symbol=symbol, side=self.ORDER_SIDE_BUY, quantity=quantity, client_id=client_id + ) + + async def order_market_sell(self, symbol: str, quantity: float, client_id: str = None): + return await self.order_market( + symbol=symbol, side=self.ORDER_SIDE_SELL, quantity=quantity, client_id=client_id + ) + + async def order_limit_buy(self, symbol: str, quantity: float, price: float, client_id: str = None): + return await self.order_limit( + symbol=symbol, side=self.ORDER_SIDE_BUY, quantity=quantity, price=price, client_id=client_id + ) + + async def order_limit_sell(self, symbol: str, quantity: float, price: float, client_id: str = None): + return await self.order_limit( + symbol=symbol, side=self.ORDER_SIDE_SELL, quantity=quantity, price=price, client_id=client_id + ) + + @validate_arguments + async def get_order_detail(self, order_id: str): + res = await self._consumer.get('/v1/account/orders/' + order_id) + json_res = await res.json() + return PlaceOrderResponseModel(**json_res['result']).dict() + + @validate_arguments + async def cancel_order(self, order_id: str): + res = await self._consumer.delete('/v1/account/orders/' + order_id) + json_res = await res.json() + return json_res['result'] + + @validate_arguments + async def get_open_orders(self, symbol: str = None): + res = await self._consumer.get('/v1/account/openOrders', params={'symbol': symbol}) + json_res = await res.json() + return json_res['result'] + + @validate_arguments + async def get_trades(self, symbol: str = None, side: str = None): + res = await self._consumer.get('/v1/account/trades', params={'symbol': symbol, 'side': side}) + json_res = await res.json() + last_trades = create_list(json_res['result']['AccountLatestTrades'], LastTradesResponseModel) + return { + 'AccountLatestTrades': last_trades + } + + async def close(self): + return await self._consumer.close() diff --git a/src/wallex/client.py b/src/wallex/client.py index 0f534cb..ab08c00 100644 --- a/src/wallex/client.py +++ b/src/wallex/client.py @@ -1,12 +1,14 @@ from pydantic import validate_arguments -from src.wallex.request import RequestsApi -from src.wallex.schema import MarketsResponseModel, CurrenciesResponseModel, MarketOrderDetailModel, ClientSymbolInput, \ +from wallex.request import RequestsApi +from wallex.schema import MarketsResponseModel, CurrenciesResponseModel, MarketOrderDetailModel, ClientSymbolInput, \ MarketTradesModel, MarketCandlesRequestModel, MarketCandlesResponseModel, AccountFeeLevelsResponseModel, \ AccountBalancesResponseModel, PlaceOrderRequestModel, PlaceOrderResponseModel, LastTradesResponseModel -from src.wallex.utils import create_list_by_symbol, create_list +from wallex.utils import create_list_by_symbol, create_list +from wallex._base_client import _Base -class Client: + +class Client(_Base): """ :param api_key: str = Wallex API key (required) :method host_is_online: bool = Returns True if Wallex API is online @@ -16,11 +18,11 @@ class Client: @validate_arguments def __init__(self, api_key: str) -> None: - self.client_user = None - self.consumer = RequestsApi( + self._consumer = RequestsApi( base_url='https://api.wallex.ir', headers={"X-API-Key": api_key} ) + try: self.client_user = self.get_profile() print('client user:', self.client_user['first_name'] + ' ' + self.client_user['last_name']) @@ -28,17 +30,17 @@ def __init__(self, api_key: str) -> None: raise Exception('can not access wallex api online server', e) def get_markets(self): - res = self.consumer.get('/v1/markets') + res = self._consumer.get('/v1/markets') symbols = res.json()['result']['symbols'] return create_list_by_symbol(symbols, MarketsResponseModel) def get_currencies(self): - res = self.consumer.get('/v1/currencies/stats') + res = self._consumer.get('/v1/currencies/stats') return create_list(res.json()['result'], CurrenciesResponseModel) def get_market_orders(self, symbol: str): params = ClientSymbolInput(symbol=symbol) - res = self.consumer.get('/v1/depth?', params=params.dict()) + res = self._consumer.get('/v1/depth?', params=params.dict()) return { 'ask': create_list(res.json()['result']['ask'], MarketOrderDetailModel), 'bid': create_list(res.json()['result']['bid'], MarketOrderDetailModel) @@ -46,14 +48,14 @@ def get_market_orders(self, symbol: str): def get_market_trades(self, symbol: str): params = ClientSymbolInput(symbol=symbol) - res = self.consumer.get('/v1/trades?', params=params.dict()) + res = self._consumer.get('/v1/trades?', params=params.dict()) return { 'latestTrades': create_list(res.json()['result']['latestTrades'], MarketTradesModel) } def get_market_candles(self, symbol: str, resolution: str, _from, _to): params = MarketCandlesRequestModel(symbol=symbol, resolution=resolution, start_time=_from, end_time=_to).dict() - res = self.consumer.get('/v1/udf/history?', params={ + res = self._consumer.get('/v1/udf/history?', params={ 'symbol': params['symbol'], 'resolution': params['resolution'], 'from': int(round(params['start_time'].timestamp())), @@ -63,26 +65,26 @@ def get_market_candles(self, symbol: str, resolution: str, _from, _to): return checked_r.dict() def get_profile(self): - res = self.consumer.get('/v1/account/profile') + res = self._consumer.get('/v1/account/profile') return res.json()['result'] def get_fee_levels(self): - res = self.consumer.get('/v1/account/fee') + res = self._consumer.get('/v1/account/fee') return create_list_by_symbol(res.json()['result'], AccountFeeLevelsResponseModel) # TODO: get 401 auth error when calling this method def get_banking_cards(self): - res = self.consumer.get('/v1/account/card-numbers') + res = self._consumer.get('/v1/account/card-numbers') return res.json()['result'] # TODO: get 500 server error when calling this method def get_banking_accounts(self): - res = self.consumer.get('/v1/account/ibans') + res = self._consumer.get('/v1/account/ibans') return res.json()['result'] def get_balances(self): temp_balances = {} - res = self.consumer.get('/v1/account/balances') + res = self._consumer.get('/v1/account/balances') balances = create_list_by_symbol(res.json()['result']['balances'], AccountBalancesResponseModel) for balance in balances: temp_balances.update(balance) @@ -91,40 +93,71 @@ def get_balances(self): } def get_crypto_withdrawal_list(self): - res = self.consumer.get('/v1/account/crypto-withdrawal') + res = self._consumer.get('/v1/account/crypto-withdrawal') return res.json()['result'] def get_crypto_deposit_list(self): - res = self.consumer.get('/v1/account/crypto-deposit') + res = self._consumer.get('/v1/account/crypto-deposit') return res.json()['result'] def place_order(self, symbol: str, order_type: str, side: str, - quantity: float, price: float, client_id: str = None): + quantity: float, price: float = None, client_id: str = None): params = PlaceOrderRequestModel(symbol=symbol, order_type=order_type, side=side, quantity=quantity, price=price, client_id=client_id ).dict(exclude_none=True) - res = self.consumer.post('/v1/account/orders', json=params) + res = self._consumer.post('/v1/account/orders', json=params) return PlaceOrderResponseModel(**res.json()['result']).dict() + def order_market(self, symbol: str, side: str, quantity: float, client_id: str = None): + return self.place_order( + symbol=symbol, order_type=self.ORDER_TYPE_MARKET, side=side, quantity=quantity, client_id=client_id + ) + + def order_limit(self, symbol: str, side: str, quantity: float, price: float, client_id: str = None): + return self.place_order( + symbol=symbol, order_type=self.ORDER_TYPE_LIMIT, side=side, + quantity=quantity, price=price, client_id=client_id + ) + + def order_market_buy(self, symbol: str, quantity: float, client_id: str = None): + return self.order_market( + symbol=symbol, side=self.ORDER_SIDE_BUY, quantity=quantity, client_id=client_id + ) + + def order_market_sell(self, symbol: str, quantity: float, client_id: str = None): + return self.order_market( + symbol=symbol, side=self.ORDER_SIDE_SELL, quantity=quantity, client_id=client_id + ) + + def order_limit_buy(self, symbol: str, quantity: float, price: float, client_id: str = None): + return self.order_limit( + symbol=symbol, side=self.ORDER_SIDE_BUY, quantity=quantity, price=price, client_id=client_id + ) + + def order_limit_sell(self, symbol: str, quantity: float, price: float, client_id: str = None): + return self.order_limit( + symbol=symbol, side=self.ORDER_SIDE_SELL, quantity=quantity, price=price, client_id=client_id + ) + @validate_arguments def get_order_detail(self, order_id: str): - res = self.consumer.get('/v1/account/orders/' + order_id) + res = self._consumer.get('/v1/account/orders/' + order_id) return PlaceOrderResponseModel(**res.json()['result']).dict() @validate_arguments def cancel_order(self, order_id: str): - res = self.consumer.delete('/v1/account/orders/' + order_id) + res = self._consumer.delete('/v1/account/orders/' + order_id) return res.json()['result'] @validate_arguments def get_open_orders(self, symbol: str = None): - res = self.consumer.get('/v1/account/openOrders', params={'symbol': symbol}) + res = self._consumer.get('/v1/account/openOrders', params={'symbol': symbol}) return res.json()['result'] @validate_arguments def get_trades(self, symbol: str = None, side: str = None): - res = self.consumer.get('/v1/account/trades', params={'symbol': symbol, 'side': side}) + res = self._consumer.get('/v1/account/trades', params={'symbol': symbol, 'side': side}) last_trades = create_list(res.json()['result']['AccountLatestTrades'], LastTradesResponseModel) return { 'AccountLatestTrades': last_trades diff --git a/src/wallex/request.py b/src/wallex/request.py index d1a0a01..e4104e6 100644 --- a/src/wallex/request.py +++ b/src/wallex/request.py @@ -1,8 +1,39 @@ +from abc import ABC, abstractmethod + import requests -from src.wallex.utils import error_handler +import aiohttp + +from wallex.utils import error_handler +from wallex.utils import async_error_handler + + +class _Base(ABC): + @abstractmethod + def get(self, url, **kwargs): + raise NotImplementedError + + @abstractmethod + def post(self, url, **kwargs): + raise NotImplementedError + + @abstractmethod + def put(self, url, **kwargs): + raise NotImplementedError + + @abstractmethod + def patch(self, url, **kwargs): + raise NotImplementedError + + @abstractmethod + def delete(self, url, **kwargs): + raise NotImplementedError + @abstractmethod + def close(self): + raise NotImplementedError -class RequestsApi: + +class RequestsApi(_Base): def __init__(self, base_url, **kwargs): self.base_url = base_url self.session = requests.Session() @@ -40,3 +71,35 @@ def __deep_merge(source, destination): else: destination[key] = value return destination + + def close(self): + self.session.close() + + +class AsyncRequestsApi(_Base): + def __init__(self, base_url, **kwargs): + self.base_url = base_url + self.session = aiohttp.ClientSession(**kwargs) + + @async_error_handler + async def get(self, url, **kwargs): + return await self.session.get(self.base_url + url, **kwargs) + + @async_error_handler + async def post(self, url, **kwargs): + return await self.session.post(self.base_url + url, **kwargs) + + @async_error_handler + async def put(self, url, **kwargs): + return await self.session.put(self.base_url + url, **kwargs) + + @async_error_handler + async def patch(self, url, **kwargs): + return await self.session.patch(self.base_url + url, **kwargs) + + @async_error_handler + async def delete(self, url, **kwargs): + return await self.session.delete(self.base_url + url, **kwargs) + + async def close(self): + await self.session.close() diff --git a/src/wallex/schema.py b/src/wallex/schema.py index 783d972..5e67b4e 100644 --- a/src/wallex/schema.py +++ b/src/wallex/schema.py @@ -149,7 +149,7 @@ class PlaceOrderRequestModel(BaseModel): type: str = 'LIMIT' or 'MARKET' side: str = 'BUY' or 'SELL' quantity: float - price: float + price: float = None client_id: str = None class Config: diff --git a/src/wallex/utils.py b/src/wallex/utils.py index 53a1089..2259a19 100644 --- a/src/wallex/utils.py +++ b/src/wallex/utils.py @@ -1,9 +1,12 @@ -from typing import Any +from typing import Callable, Awaitable +from requests import Response +from aiohttp import ClientResponse -def error_handler(func: Any) -> Any: + +def error_handler(func: Callable[[...], Response]) -> ...: def wrapper(*args, **kwargs): - response = func(*args, **kwargs) + response: Response = func(*args, **kwargs) accepted_status_codes = [200, 201, 202, 204] if response.status_code not in accepted_status_codes: raise Exception('call api error', response.json()) @@ -11,6 +14,16 @@ def wrapper(*args, **kwargs): return wrapper +def async_error_handler(func: Callable[[...], Awaitable[ClientResponse]]) -> ...: + async def wrapper(*args, **kwargs): + response: ClientResponse = await func(*args, **kwargs) + accepted_status_codes = [200, 201, 202, 204] + if response.status not in accepted_status_codes: + raise Exception('call api error', await response.json()) + return response + return wrapper + + def create_list_by_symbol(dict_list: list, model) -> list: result = [] for item in dict_list: