Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
__pycache__/
test.py

target/
!.mvn/wrapper/maven-wrapper.jar

Expand Down
271 changes: 139 additions & 132 deletions DexilonClientImpl.py

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions ErrorBody.py

This file was deleted.

8 changes: 0 additions & 8 deletions OrderErrorInfo.py

This file was deleted.

10 changes: 7 additions & 3 deletions SessionClient.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
from typing import Set

import requests

from exceptions import DexilonAPIException, DexilonRequestException, DexilonAuthException


class SessionClient:

def __init__(self, base_url: str, headers: dict = {}) -> None:
STATUS_CODES_TO_PROCESS: Set[int] = {200, 400, 401}

self.STATUS_CODES_TO_PROCESS = {200, 400, 401}
def __init__(self, base_url: str, headers: dict = {}) -> None:

self.base_url: str = base_url
self.session: requests.Session = requests.Session()
self.session.headers.update(headers)

self.update_headers(headers)

def update_headers(self, headers: dict = {}) -> None:
self.session.headers.update(headers)
Expand All @@ -20,6 +23,7 @@ def delete_header(self, header_name: str) -> None:
self.session.headers.pop(header_name)

def request(self, method: str, path: str, params: dict = None, data: dict = None) -> dict:

request = requests.Request(
method=method.upper(),
url=self.base_url + path,
Expand Down
27 changes: 25 additions & 2 deletions exceptions.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from responses import ErrorBody


class DexilonAPIException(Exception):

def __init__(self, response):
self.code = 0
try:
json_res = response.json()
except ValueError:
self.message = 'Invalid JSON error message from Dexilon: {}'.format(response.text)
self.message = 'Invalid JSON error message from Dexilon: {}'.format(
response.text)
else:
self.code = json_res['errors']['code']
self.message = json_res['errors']['message']
Expand All @@ -25,9 +29,28 @@ def __str__(self):
return 'DexilonRequestException: %s' % self.message


class DexilonAuthException(Exception) :
class DexilonAuthException(Exception):
def __init__(self, message):
self.message = message

def __str__(self):
return 'DexilonAuthException: %s' % self.message


class DexilonErrorBodyException(Exception):
def __init__(self, error_body: ErrorBody):
self.error_body = error_body

def __str__(self):
return 'ErrorBodyException: %s' % self.error_body


class OrderErrorInfo(Exception):

def __init__(self, client_order_id: str, state: str, message: str):
self.client_order_id = client_order_id
self.state = state
self.message = message

def __str__(self):
return 'OrderErrorInfo: client_order_id=%s state=%s message=%s' % (self.client_order_id, self.state, self.message)
8 changes: 3 additions & 5 deletions responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@
from pydantic import BaseModel, Field
from pydantic.main import Optional


class ErrorBody(BaseModel):
code: int
name: str
details: List[str]


class DebugInfo(BaseModel):
correlationId: Optional[str]
stackTrace: Optional[str]

class ServiceResponse(BaseModel):
errorBody: Optional[ErrorBody]
debugInfo: Optional[DebugInfo]


class AvailableSymbol(BaseModel):
symbol: str
Expand Down Expand Up @@ -107,7 +105,7 @@ class FullOrderInfo(BaseModel):
side: str
status: str
createdAt: datetime
updatedAt: datetime
updatedAt: Optional[datetime]


class LeverageEvent(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion tests/authentication_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from DexilonClientImpl import DexilonClientImpl
from OrderErrorInfo import OrderErrorInfo
from exceptions import OrderErrorInfo # FAKE
from responses import FullOrderInfo


Expand Down
55 changes: 37 additions & 18 deletions tests/trading_integration_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from DexilonClientImpl import DexilonClientImpl
from OrderErrorInfo import OrderErrorInfo
from responses import FullOrderInfo, LeverageUpdateInfo


Expand All @@ -11,51 +10,68 @@ class TestTradingIntegration:
# TEST_PRIVATE_KEY = '7bf5ae9f4d080107e105212f40cdc3d897d1101e69fd8f0a36e5bb648055ff52'

def setup(self):
self.test_instance = DexilonClientImpl(self.TEST_METAMASK_ADDRESS, self.TEST_PRIVATE_KEY)
self.test_instance.change_api_url('https://dex-dev2-api.cronrate.com/api/v1')
self.test_instance = DexilonClientImpl(
self.TEST_METAMASK_ADDRESS, self.TEST_PRIVATE_KEY)
self.test_instance.change_api_url(
'https://dex-dev2-api.cronrate.com/api/v1')

def test_create_market_order(self):
full_order_info = self.test_instance.market_order('TEST_MARKET_ORDER_1', 'eth_usdc', 'SELL', 0.20)
assert isinstance(full_order_info, FullOrderInfo) and full_order_info.orderId is not None
full_order_info = self.test_instance.market_order(
'TEST_MARKET_ORDER_1', 'eth_usdc', 'SELL', 0.20)
assert isinstance(
full_order_info, FullOrderInfo) and full_order_info.orderId is not None

def test_create_market_order_with_rejected_state(self):
order_submit_result = self.test_instance.market_order('TEST_MARKET_ORDER_1', 'eth_usdc', 'BUY', 1000000000.00)
order_submit_result = self.test_instance.market_order(
'TEST_MARKET_ORDER_1', 'eth_usdc', 'BUY', 1000000000.00)
assert isinstance(order_submit_result, OrderErrorInfo)
assert 'NEW_ORDER_REJECTED' in order_submit_result.state

def test_create_limit_order(self):
full_order_info = self.test_instance.limit_order('TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1650.0, 0.2)
assert isinstance(full_order_info, FullOrderInfo) and full_order_info.orderId is not None
full_order_info = self.test_instance.limit_order(
'TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1000.0, 0.2)
if type(full_order_info) is not FullOrderInfo:
print('Error:', full_order_info.message)
print('Order:', full_order_info)
assert isinstance(
full_order_info, FullOrderInfo) and full_order_info.orderId is not None
return full_order_info

def test_create_limit_order_with_rejected_state(self):
full_order_info = self.test_instance.limit_order('TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 3200.0, 100.0)
full_order_info = self.test_instance.limit_order(
'TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 3200.0, 100.0)
assert isinstance(full_order_info, OrderErrorInfo)

def test_should_cancel_all_orders(self):
result = self.test_instance.cancel_all_orders()
print(result)
assert result

def test_should_cancel_order(self):
result = self.test_instance.cancel_order('TESTORDERID', 'eth_usdc')
assert isinstance(result, OrderErrorInfo)

def test_should_create_and_cancel_order_sucessfully(self):
full_order_info = self.test_instance.limit_order('TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1200.0, 0.2)
full_order_info = self.test_instance.limit_order(
'TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1200.0, 0.2)
assert isinstance(full_order_info, FullOrderInfo)
canceled_order_info = self.test_instance.cancel_order(full_order_info.orderId, full_order_info.symbol)
canceled_order_info = self.test_instance.cancel_order(
full_order_info.orderId, full_order_info.symbol)
assert isinstance(canceled_order_info, FullOrderInfo)

def test_should_get_account_info(self):
account_info_result = self.test_instance.get_account_info()
assert account_info_result is not None

def test_should_get_all_open_orders(self): #
full_order_info = self.test_instance.limit_order('TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1200.0, 0.2)
def test_should_get_all_open_orders(self):
full_order_info = self.test_instance.limit_order(
'TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1200.0, 0.2)

open_orders = self.test_instance.get_open_orders()
assert len(open_orders) > 0

self.test_instance.cancel_order(full_order_info.orderId, full_order_info.symbol)
self.test_instance.cancel_order(
full_order_info.orderId, full_order_info.symbol)

# def test_should_get_order_info(self): #
# full_order_info = self.test_instance.limit_order('TEST_LIMIT_ORDER_2', 'eth_usdc', 'BUY', 1200.0, 0.2)
Expand All @@ -68,19 +84,22 @@ def test_should_get_all_open_orders(self): #
# assert isinstance(cancel_result, FullOrderInfo)

def test_should_error_on_cancel_wrong_order(self):
cancel_result = self.test_instance.cancel_order('RANDOMID1', 'eth_usdc')
cancel_result = self.test_instance.cancel_order(
'RANDOMID1', 'eth_usdc')
assert isinstance(cancel_result, OrderErrorInfo)

def test_should_parse_response_for_illegal_char_in_cancel_request(self):
cancel_result = self.test_instance.cancel_order('RANDOM-ID-1', 'eth_usdc')
cancel_result = self.test_instance.cancel_order(
'RANDOM-ID-1', 'eth_usdc')
assert isinstance(cancel_result, OrderErrorInfo)

def test_should_process_error_for_limit_order_submit(self):
order_submit_result = self.test_instance.limit_order('TEST_MARKET_ORDER_1', 'eth_usdc', 'BUY', 1600.00, 0.10)
order_submit_result = self.test_instance.limit_order(
'TEST_MARKET_ORDER_1', 'eth_usdc', 'BUY', 1600.00, 0.10)
assert isinstance(order_submit_result, OrderErrorInfo)
if isinstance(order_submit_result, OrderErrorInfo):
assert 'REJECTED' in order_submit_result.state

def test_should_set_leverage(self):
leverage_update = self.test_instance.set_leverage('eth_usdc', 1)
assert isinstance(leverage_update, LeverageUpdateInfo)
assert isinstance(leverage_update, LeverageUpdateInfo)