Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial structure #1

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 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
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,79 @@
# client-py

## Install

```console
pip install git+https://github.com/OmniTrade/client-py.git
```

## Usage

Require the client in your code by adding:


```python
from omnitradeClient import Client
from omnitradeClient import StreamingClient
```

You can use both the public or private API.

### Public
------

Instance your public client by using:

```python
public_client = Client()
```

#### .get_public

Use **`.get_public`** to make a GET request to an URL, as the following example:

```python
client_public.get_public('/api/v2/markets')
```

### Private
------

When using the private client, you also have to specify your access and private keys in the instantiation:

```python
private_client = Client({ access_key: 'your_access_key', secret_key: 'your_secret_key' })
```

You can also set a timeout and/or endpoit by adding the arguments:

```python
private_client = Client({ access_key: 'your_access_key', secret_key: 'your_secret_key', timeout: 60, endpoint: 'https://omnitrade.io/' })
```
#### .get

Use **`.get`** to make a GET request to an URL, you can also set the required arguments, as the following example:

```python
private_client.get('/api/v2/orders', { market: 'btcbrl' })
```

#### .post

Use **`.post`** to make a POST request to an URL, you can also set the required arguments, as the following example:

```python
private_client.post('/api/v2/order', { market: 'btcbrl', side: 'buy', volume: '0.42', price: '4200.0' })
```

### WebSocket
------

```python
streaming_client = StreamingClient(callbackFunction, {'access_key': '123456', 'secret_key': '123412'})
```

This `callbackFunction` must to receive one parameter.

## Licence

OmniTrade (C) All Rights Reserved.
46 changes: 46 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
asn1crypto==0.24.0
AST==0.0.2
asyncio==3.4.3
bleach==3.1.1
certifi==2019.11.28
chardet==3.0.4
crypto==1.4.1
cryptography==2.1.4
docutils==0.16
enum34==1.1.6
funcsigs==1.0.2
gevent==1.4.0
greenlet==0.4.15
hashlib==20081119
http==0.2
idna==2.9
ipaddress==1.0.17
keyring==10.6.0
keyrings.alt==3.0
logging==0.4.9.6
mock==3.0.5
Naked==0.1.31
nose==1.3.7
omnitrade-client==0.1
omnitradeClient==0.2
pkginfo==1.5.0.1
pycrypto==2.6.1
Pygments==2.5.2
pygobject==3.26.1
PyTestStub==0.0.4
python-apt==1.6.5+ubuntu0.2
pyxdg==0.25
PyYAML==5.3
readme-renderer==24.0
requests==2.23.0
requests-mock==1.7.0
requests-toolbelt==0.9.1
SecretStorage==2.3.1
shellescape==3.8.1
six==1.14.0
tqdm==4.43.0
twine==1.15.0
urllib3==1.25.8
webencodings==0.5.1
websocket==0.2.1
websocket-client==0.57.0
19 changes: 19 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from setuptools import find_packages
from setuptools import setup

setup(
name="omnitradeClient",
version="0.2",
license="MIT license",
description="Python client for Omnitrade API",
author="Omnitrade",
author_email="@codeminer42.com",
url="https://github.com/OmniTrade/client-py",
packages=find_packages("src"),
package_dir={"": "src/"},
include_package_data=True,
zip_safe=False,
keywords=["omnitrade", "api"],
install_requires=open("requirements.txt").readlines(),
extras_require={}
)
2 changes: 2 additions & 0 deletions src/omnitradeClient/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from client import Client
gabrielbaldao marked this conversation as resolved.
Show resolved Hide resolved
from streaming_client import StreamingClient
30 changes: 30 additions & 0 deletions src/omnitradeClient/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import hashlib
import urllib

class Auth(object):
def __init__(self, access_key, secret_key):
self.access_key = access_key
self.secret_key = secret_key

def signed_challenge(self, challenge):
signature = hashlib.pbkdf2_hmac('sha256', self.secret_key,"{access_key}{challenge}".format(access_key = self.access_key, challenge = challenge), 100000 )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about creating a const to keep this 100000 value?


return { 'auth': { 'access_key': self.access_key, 'answer': signature } }

def signed_params(self, verb, path, params = {}):
gabrielbaldao marked this conversation as resolved.
Show resolved Hide resolved
params = self.__format_params(params)

signature = self.__sign(verb, path, urllib.urlencode(params))
gabrielbaldao marked this conversation as resolved.
Show resolved Hide resolved
params['signature'] = signature
return params

def __sign(self, verb, path, params):
return hashlib.pbkdf2_hmac('sha256', self.secret_key, self.__payload(verb, path, params), 100000)

def __payload(self, verb, path, params):
return "#{verb}|#{path}|#{params}".format(verb = verb.upper(), path = path, params = params)

def __format_params(self, params):
params['access_key'] = self.access_key
params['tonce'] = int(time.time()) * 1000
return params
57 changes: 57 additions & 0 deletions src/omnitradeClient/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import requests
import urllib
from auth import Auth
from ordered_dict import ordered_dict

OMNITRADE_URL = 'https://omnitrade.io'
class Client(object):
def __init__(self, options={}):
gabrielbaldao marked this conversation as resolved.
Show resolved Hide resolved
self.auth = None
self.__setup_auth_keys(options)

self.endpoint = options['endpoint'] if 'endpoint' in options.keys() else OMNITRADE_URL
self.timeout = options['timeout'] if 'timeout' in options.keys() else 60

def get_public(self, path, params={}):
uri, params = self.__parameters('GET',path, params)
return self.__get_request(uri, params)


def get(self, path, params={}):
self.__check_auth()

uri, params = self.__parameters('GET',path, params)
return self.__get_request(uri, params)

def post(self, path, params={}):
self.__check_auth()

uri, params = self.__parameters('POST', path, params)
return self.__post_request(uri, params)

def __setup_auth_keys(self, options):
if not('access_key' in options.keys() and 'secret_key' in options.keys()):
return

self.access_key = options['access_key']
self.secret_key = options['secret_key']
self.auth = Auth(self.access_key, self.secret_key)


def __check_auth(self):
if self.auth == None:
raise Exception('Missing access key and/or secret key')

def __get_request(self, uri, params):
return requests.get(uri, params = urllib.urlencode(params))

def __post_request(self, uri, params):
return requests.post(uri, params = urllib.urlencode(params))

def __parameters(self, action, path, params):
uri = '{endpoint}{path}'.format(endpoint=self.endpoint, path=path)
dict_params = self.auth.signed_params(action, path, params) if self.auth != None else params

return uri, ordered_dict(dict_params)


7 changes: 7 additions & 0 deletions src/omnitradeClient/ordered_dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def ordered_dict(dict_params):
params = map(lambda key,value: (key, value), dict_params.keys(), dict_params.values())
params.sort(key=take_key)
return params

def take_key(item):
return item[0]
47 changes: 47 additions & 0 deletions src/omnitradeClient/streaming_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import websocket
import logging
import json
import ast
from client import Client

logging.basicConfig(level=logging.INFO)

OMNITRADE_URL = 'wss://omnitrade.com:8080'
class StreamingClient(Client):
def __init__(self, callback, options = {}):
super(StreamingClient, self).__init__(options)
self.endpoint = options['endpoint'] if 'endpoint' in options.keys() else OMNITRADE_URL
self.logger = options['logger'] if 'logger' in options.keys() else logging

websocket.enableTrace(True)

self.callback = callback
self.ws = websocket.WebSocketApp(self.endpoint,
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
self.ws.on_open = self.on_open
self.ws.run_forever()

def on_message(self, message):
msg = ast.literal_eval(message.decode())

key = msg.keys()[0]
data = msg[key]
if data != None and key == 'challenge':
self.ws.send(str(self.auth.signed_challenge(data)).encode())
else:
try:
self.callback(msg)
except Exception as e:
self.logger.error("Failed to process message: {error}".format(error=e))

def on_error(self, error):
self.logger.info(error)

def on_close(self):
self.ws.close()
self.logger.info("### closed ###")

def on_open(self):
self.logger.info("Connected")
20 changes: 20 additions & 0 deletions src/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import unittest
from omnitradeClient.auth import Auth

ACCESS_KEY = '123456'
SECRET_KEY = '123456'
class TestAuthMethods(unittest.TestCase):
def test_signed_challenge(self):
auth = Auth(ACCESS_KEY, SECRET_KEY)

subject = auth.signed_challenge('challenge')

self.assertEqual(subject.keys(), ['auth'])
self.assertEqual(subject['auth'].keys(), ['access_key', 'answer'])

def test_signed_params(self):
auth = Auth(ACCESS_KEY, SECRET_KEY)

subject = auth.signed_params('GET', 'api/v2/order_markets', { "volume": 0, "total": 0 })

self.assertEqual(subject.keys(), ['volume', 'access_key', 'tonce', 'total', 'signature'])
33 changes: 33 additions & 0 deletions src/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import unittest
import json
from nose.tools import assert_is_none, assert_list_equal
from mock import patch, Mock

from omnitradeClient.client import Client

OMNITRADE_URL = 'https://omnitrade.io'
class TestClientMethods(unittest.TestCase):

def test_get_public_valid_request(self):
public_client = Client()
market_path = '/api/v2/markets'
markets = [{"id": "btcbrl", "name": "BTC/BRL"}, {"id": "ltcbrl", "name": "LTC/BRL"}, {"id": "bchbrl", "name": "BCH/BRL"}, {"id": "btgbrl", "name": "BTG/BRL"}, {"id": "ethbrl", "name": "ETH/BRL"}, {"id": "dashbrl", "name": "DASH/BRL"}, {"id": "dcrbrl", "name": "DCR/BRL"}, {"id": "ltcbtc", "name": "LTC/BTC"}, {"id": "bchbtc", "name": "BCH/BTC"}, {"id": "btgbtc", "name": "BTG/BTC"}, {"id": "ethbtc", "name": "ETH/BTC"}, {"id": "dashbtc", "name": "DASH/BTC"}, {"id": "dcrbtc", "name": "DCR/BTC"}, {"id": "ltceth", "name": "LTC/ETH"}, {"id": "bcheth", "name": "BCH/ETH"}, {"id": "btgeth", "name": "BTG/ETH"}, {"id": "dasheth", "name": "DASH/ETH"}, {"id": "dcreth", "name": "DCR/ETH"}, {"id": "xrpbrl", "name": "XRP/BRL"}, {"id": "xrpbtc", "name": "XRP/BTC"}, {"id": "xrpeth", "name": "XRP/ETH"}, {"id": "mftbrl", "name": "MFT/BRL"}, {"id": "mftbtc", "name": "MFT/BTC"}, {"id": "mfteth", "name": "MFT/ETH"}, {"id": "btcusdc", "name": "BTC/USDC"}, {"id": "ltcusdc", "name": "LTC/USDC"}, {"id": "bchusdc", "name": "BCH/USDC"}, {"id": "btgusdc", "name": "BTG/USDC"}, {"id": "ethusdc", "name": "ETH/USDC"}, {"id": "dashusdc", "name": "DASH/USDC"}, {"id": "dcrusdc", "name": "DCR/USDC"}, {"id": "xrpusdc", "name": "XRP/USDC"}, {"id": "bnbbtc", "name": "BNB/BTC"}, {"id": "bnbusdc", "name": "BNB/USDC"}]

response = public_client.get_public(market_path)

self.assertEqual(response.json(), markets)

def test_post_request(self):
access_key = 'FUkESEYRJmO42MfqXcgJfm73GfIMw61qogExtcX7' #TODO improve this requests tests

client = Client({'access_key': access_key,'secret_key': '123456'})
response = client.post('/api/v2/orders/clear', {'side': 'sell' })

self.assertEqual(response.status_code, 201)

def test_post_request(self):
access_key = 'FUkESEYRJmO42MfqXcgJfm73GfIMw61qogExtcX7' #TODO improve this requests tests

client = Client({'access_key': access_key,'secret_key': '123456'})
response = client.get('/api/v2/order', {'id': 1 })
self.assertEqual(response.status_code, 404)
10 changes: 10 additions & 0 deletions src/tests/test_ordered_dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import unittest
from omnitradeClient.ordered_dict import ordered_dict

class TestOrderedDictMethods(unittest.TestCase):

def test_ordered_dict(self):
dictionary = { 'a': 1, 'b': 2, 'c': 3}

self.assertEqual(ordered_dict(dictionary), [('a', 1),('b', 2),('c', 3)])