Skip to content

Commit

Permalink
Merge pull request #20 from 8ball030/feat/async_client
Browse files Browse the repository at this point in the history
Feat/async client
  • Loading branch information
8ball030 authored Jul 1, 2024
2 parents 4448f3a + 0d19e20 commit b1280a9
Show file tree
Hide file tree
Showing 10 changed files with 575 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ site/
.history

*trader
agent

node_modules
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ price = client.get_product(DEFAULT_SYMBOL)
print(price)
```

Fopr a demonstration of the async client please refer to the file in examples/async_client.py


## Installation
Expand Down
105 changes: 105 additions & 0 deletions examples/run_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""
Example of a client that connects to a server and sends a message.
"""

import asyncio
import os
from pprint import pprint

from hundred_x.async_client import AsyncHundredXClient
from hundred_x.enums import Environment, OrderSide, OrderType, TimeInForce


async def main():
key = os.getenv("HUNDRED_X_PRIVATE_KEY")
subaccount_id = 0

if not key:
raise ValueError("HUNDRED_X_PRIVATE_KEY environment variable is not set.")

client = AsyncHundredXClient(Environment.PROD, key, subaccount_id=subaccount_id)

print(f"Using Wallet: {client.public_key}")
print(f"Using Subaccount ID: {client.subaccount_id}")

# In order to hit the authenticated endpoints, we need to login.
await client.login()

# We check the initial balance.
print("Initial Balance")
response = await client.get_spot_balances()
pprint(response)

# We first get the symbol.
print("Symbol")
response = await client.get_symbol("btcperp")
pprint(response)

# We create an order.
print("Create Order")
response = await client.create_order(
subaccount_id=subaccount_id,
product_id=response['productId'], # This is the product_id for the symbol 'btcperp
quantity=0.001,
price=round(int(response['markPrice']) * 0.99 / 1e18), # This is the current price of the symbol 'btcperp'
side=OrderSide.BUY,
order_type=OrderType.LIMIT,
time_in_force=TimeInForce.GTC,
)

# We check the open orders.
print("Open Orders")
response = await client.get_open_orders("btcperp")
pprint(response)

# We cancel the order.
print("Cancel Order")
response = await client.cancel_order(order_id=response[0]["id"], product_id=response[0]["productId"])
pprint(response)

# We check the positions.
print("Positions")
response = await client.get_position()
pprint(response)

# We check if we can cancel and replace an order.
# We check the open orders.
print("Open Orders")
response = await client.get_open_orders("btcperp")
pprint(response)

# We create an order.
print("Create Order")

response = await client.create_order(
subaccount_id=subaccount_id,
product_id=response[0]["productId"],
quantity=0.001,
price=round(int(response[0]["price"]) * 0.99 / 1e18),
side=OrderSide.BUY,
order_type=OrderType.LIMIT,
time_in_force=TimeInForce.GTC,
)

# We check the open orders.
print("Open Orders")
response = await client.get_open_orders("btcperp")
pprint(response)

# We cancel and replace the order.
print("Cancel and Replace Order")
response = await client.cancel_and_replace_order(
order_id_to_cancel=response[0]["id"],
product_id=response[0]["productId"],
quantity=0.002,
price=round(int(response[0]["price"]) * 0.99 / 1e18),
side=OrderSide.BUY,
)
pprint(response)

# We cancel all orders.
response = await client.cancel_all_orders(subaccount_id=subaccount_id, product_id=1002)


if __name__ == "__main__":
asyncio.run(main())
120 changes: 111 additions & 9 deletions hundred_x/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,130 @@
Async client for the HundredX API
"""

from typing import Any

import httpx

from hundred_x.client import HundredXClient
from hundred_x.constants import LOGIN_MESSAGE
from hundred_x.eip_712 import LoginMessage
from hundred_x.exceptions import ClientError
from hundred_x.utils import from_message_to_payload


class AsyncHundredXClient(HundredXClient):
"""
Asynchronous client for the HundredX API.
"""

async def list_products(self):
async def get_symbol(self, symbol: str = None):
"""
Get the symbol infos.
If symbol is None, return all symbols.
"""
response = await super().get_symbol(symbol)
if symbol:
return response[0]
else:
return response

async def get_depth(self, symbol: str, **kwargs) -> Any:
"""
Get the depth for a specific symbol.
"""
return await super().get_depth(symbol, **kwargs)

async def get_trade_history(self, symbol: str, lookback: int = 10, **kwargs) -> Any:
"""
Get the trade history for a specific symbol.
if symbol is None, return all trade history.
"""
return await super().get_trade_history(symbol, lookback, **kwargs)

async def get_position(self, symbol: str = None):
"""
Get the position for a specific symbol.
"""
return await super().get_position(symbol)

async def get_spot_balances(self):
"""
Get the spot balances.
"""
return await super().get_spot_balances()

async def get_open_orders(self, symbol: str = None):
"""
Get the open orders for a specific symbol.
"""
return await super().get_open_orders(symbol)

async def create_order(self, *args, **kwargs):
"""
Create and send order.
"""
return await super().create_order(*args, **kwargs)

async def cancel_order(self, *args, **kwargs):
"""
Cancel an order.
"""
return await super().cancel_order(*args, **kwargs)

async def cancel_and_replace_order(self, *args, **kwargs):
"""
Cancel and replace an order.
"""
return await super().cancel_and_replace_order(*args, **kwargs)

async def cancel_all_orders(self, subaccount_id: int, product_id: int):
"""
List all products available on the exchange.
Cancel all orders.
"""
return super().list_products()
return await super().cancel_all_orders(subaccount_id, product_id)

async def get_product(self, symbol: str):
async def login(self):
"""
Get a specific product available on the exchange.
Login to the exchange.
"""
return super().get_product(symbol)
response = await self.create_authenticated_session_with_service()
if response is None:
raise Exception("Failed to login")

async def get_server_time(self):
async def send_message_to_endpoint(
self, endpoint: str, method: str, message: dict = {}, authenticated: bool = True, params: dict = {}
):
"""
Get the server time.
Send a message to an endpoint.
"""
return super().get_server_time()
if not self._validate_function(
endpoint,
):
raise ClientError(f"Invalid endpoint: {endpoint}")
payload = from_message_to_payload(message)

async with httpx.AsyncClient() as client:
response = await client.request(
method,
self.rest_url + endpoint,
params=params,
headers={} if not authenticated else self.authenticated_headers,
json=payload,
)

if response.status_code != 200:
raise Exception(
f"Failed to send message: {response.text} {response.status_code} {self.rest_url} {payload}"
)
return response.json()

async def create_authenticated_session_with_service(self):
login_payload = self.generate_and_sign_message(
LoginMessage,
message=LOGIN_MESSAGE,
timestamp=self._current_timestamp(),
**self.get_shared_params(),
)
response = await self.send_message_to_endpoint("/v1/session/login", "POST", login_payload, authenticated=False)
self.session_cookie = response.get("value")
return response
Loading

0 comments on commit b1280a9

Please sign in to comment.