Fixed tests, updated transaction structs

api-breakage
Morty Space 5 years ago
parent 42ffe96ae4
commit fe5e088d0a
  1. 2
      azure-pipelines.yml
  2. 2
      docs/source/conf.py
  3. 6
      src/cryptocom/exchange/__init__.py
  4. 2
      src/cryptocom/exchange/api.py
  5. 7
      src/cryptocom/exchange/market.py
  6. 50
      src/cryptocom/exchange/private.py
  7. 97
      src/cryptocom/exchange/structs.py
  8. 8
      tests/test_api.py
  9. 6
      tests/test_market.py
  10. 30
      tests/test_private.py

@ -18,7 +18,7 @@ variables:
value: 16bcfb0958d99f11456f8d80aeb5800d567724471e151fe6e74a4b329b45dcb6
pool:
vmImage: 'ubuntu-latest'
vmImage: 'debian-latest'
strategy:
maxParallel: 1

@ -18,7 +18,7 @@ from cryptocom.exchange import __version__
# -- Project information -----------------------------------------------------
project = 'cryptocom-exchange'
copyright = '2020, goincrypto.com'
copyright = '2020-2021'
author = 'Yaroslav Rudenok [MortySpace]'
# The short X.Y version

@ -1,6 +1,7 @@
from .structs import (
Order, OrderSide, OrderStatus, OrderType, Pair, Period, Candle,
MarketTrade, Coin, PrivateTrade
MarketTrade, Coin, PrivateTrade, Timeframe, DepositStatus, Deposit,
WithdrawalStatus, Withdrawal
)
from .market import Exchange
from .private import Account
@ -11,8 +12,9 @@ __all__ = [
'Order', 'OrderSide', 'OrderStatus', 'OrderType',
'pairs', 'Pair', 'coins', 'Coin',
'Period', 'Candle', 'MarketTrade', 'PrivateTrade',
'Timeframe', 'Deposit', 'Withdrawal', 'DepositStatus', 'WithdrawalStatus',
'Exchange', 'Account',
'ApiError', 'ApiProvider'
]
__version__ = '0.8.1'
__version__ = '0.9'

@ -33,7 +33,7 @@ class ApiProvider:
self.retries = retries
# NOTE: do not change this, due to crypto.com rate-limits
self.semaphore = asyncio.Semaphore(40)
self.semaphore = asyncio.Semaphore(20)
if not auth_required:
return

@ -58,10 +58,10 @@ class Exchange:
'public/get-trades', {'instrument_name': pair.name})
return [MarketTrade.from_api(pair, trade) for trade in reversed(data)]
async def get_orderbook(self, pair: Pair, depth: int = 150) -> OrderBook:
"""Get the order book for a particular market."""
async def get_orderbook(self, pair: Pair) -> OrderBook:
"""Get the order book for a particular market, depth always 150."""
data = await self.api.get('public/get-book', {
'instrument_name': pair.name, 'depth': depth})
'instrument_name': pair.name})
buys = [
OrderInBook(*order, pair, OrderSide.BUY)
for order in data[0]['bids']
@ -75,7 +75,6 @@ class Exchange:
async def get_candles(self, pair: Pair, period: Period) -> List[Candle]:
data = await self.api.get('public/get-candlestick', {
'instrument_name': pair.name, 'timeframe': period.value})
return [Candle.from_api(pair, candle) for candle in data]
async def listen_candles(

@ -5,9 +5,9 @@ from typing import List, Dict
from .api import ApiProvider, ApiError
from .market import Exchange
from .structs import (
Pair, OrderSide, OrderStatus, OrderType, Order, Coin, Balance,
OrderExecType, OrderForceType, PrivateTrade, Interest, Withdrawal,
Deposit
Deposit, DepositStatus, Pair, OrderSide, OrderStatus, OrderType, Order,
Coin, Balance, OrderExecType, OrderForceType, PrivateTrade, Interest,
Withdrawal, WithdrawalStatus
)
@ -38,57 +38,60 @@ class Account:
}
async def get_deposit_history(
self, coin: Coin, start_ts: int, end_ts: int, status: int, page: int = 0, page_size: int = 20)\
-> List[Deposit]:
self, coin: Coin, start_ts: int = None, end_ts: int = None,
status: DepositStatus = None, page: int = 0, page_size: int = 20
) -> List[Deposit]:
"""Return all history withdrawals."""
params = {'page_size': page_size, 'page': page}
if coin:
params['currency'] = coin.name
if start_ts:
params['start_ts'] = start_ts
params['start_ts'] = int(start_ts) * 1000
if end_ts:
params['end_ts'] = end_ts
params['end_ts'] = int(end_ts) * 1000
if status:
params['status'] = str(status)
params['status'] = status
data = await self.api.post(
'private/get-deposit-history', {'params': params}) or {}
return [
Deposit.create_from_api(deposit)
for deposit in data.get('deposit_list') or []
Deposit.create_from_api(trx)
for trx in data.get('deposit_list') or []
]
async def get_withdrawal_history(
self, coin: Coin, start_ts: int, end_ts: int, status: int, page: int = 0, page_size: int = 20)\
-> List[Withdrawal]:
"""Return all history withdrawals."""
self, coin: Coin, start_ts: int = None, end_ts: int = None,
status: WithdrawalStatus = None, page: int = 0, page_size: int = 20
) -> List[Withdrawal]:
"""Return all history for withdrawal transactions."""
params = {'page_size': page_size, 'page': page}
if coin:
params['currency'] = coin.name
if start_ts:
params['start_ts'] = start_ts
params['start_ts'] = int(start_ts) * 1000
if end_ts:
params['end_ts'] = end_ts
params['end_ts'] = int(end_ts) * 1000
if status:
params['status'] = str(status)
params['status'] = status
data = await self.api.post(
'private/get-withdrawal-history', {'params': params}) or {}
return [
Withdrawal.create_from_api(withdrawal)
for withdrawal in data.get('withdrawal_list') or []
Withdrawal.create_from_api(trx)
for trx in data.get('withdrawal_list') or []
]
async def get_interest_history(
self, coin: Coin, start_ts: int, end_ts: int, page: int = 0, page_size: int = 20) -> List[Interest]:
self, coin: Coin, start_ts: int = None, end_ts: int = None,
page: int = 0, page_size: int = 20) -> List[Interest]:
"""Return all history interest."""
params = {'page_size': page_size, 'page': page}
if coin:
params['currency'] = coin.name
if start_ts:
params['start_ts'] = start_ts
params['start_ts'] = int(start_ts) * 1000
if end_ts:
params['end_ts'] = end_ts
params['end_ts'] = int(end_ts) * 1000
data = await self.api.post(
'private/margin/get-order-history', {'params': params}) or {}
@ -105,9 +108,10 @@ class Account:
if pair:
params['instrument_name'] = pair.name
if start_ts:
params['start_ts'] = start_ts
params['start_ts'] = int(start_ts) * 1000
if end_ts:
params['end_ts'] = end_ts
params['end_ts'] = int(end_ts) * 1000
data = await self.api.post(
'private/get-order-history', {'params': params}) or {}
return [

@ -1,8 +1,9 @@
import time
from enum import Enum
from enum import Enum, IntEnum
from typing import List, Dict
from dataclasses import dataclass
from datetime import datetime
from cached_property import cached_property
@ -368,53 +369,87 @@ class Interest:
)
class WithdrawalStatus(str, Enum):
PENDING = '0'
PROCESSING = '1'
REJECTED = '2'
PAYMENT_IN_PROGRESS = '3'
PAYMENT_FAILED = '4'
COMPLETED = '5'
CANCELLED = '6'
class DepositStatus(str, Enum):
NOT_ARRIVED = '0'
ARRIVED = '1'
FAILED = '2'
PENDING = '3'
class TransactionType(IntEnum):
WITHDRAWAL = 0
DEPOSIT = 1
@dataclass
class Withdrawal:
class Transaction:
coin: Coin
client_wid: str
fee: float
create_time: int
id: str
update_time: int
amount: float
address: str
status: str
@classmethod
def create_from_api(cls, data: Dict) -> 'Withdrawal':
return cls(
@staticmethod
def _prepare(data):
return dict(
id=data['id'],
coin=Coin(data['currency']),
client_wid=data['client_wid'],
fee=float(data['fee']),
create_time=data['create_time'],
id=data['id'],
update_time=data['update_time'],
create_time=datetime.fromtimestamp(
int(data['create_time']) / 1000),
update_time=datetime.fromtimestamp(
int(data['update_time']) / 1000),
amount=float(data['amount']),
address=data['address'],
status=data['status']
)
@dataclass
class Deposit:
coin: Coin
fee: float
create_time: int
id: str
update_time: int
amount: float
address: str
status: str
class Deposit(Transaction):
status: DepositStatus
@classmethod
def create_from_api(cls, data: Dict) -> 'Deposit':
return cls(
coin=Coin(data['currency']),
fee=float(data['fee']),
create_time=data['create_time'],
id=data['id'],
update_time=data['update_time'],
amount=float(data['amount']),
address=data['address'],
status=data['status']
)
params = cls._prepare(data)
params['status'] = DepositStatus(data['status'])
return cls(**params)
@dataclass
class Withdrawal(Transaction):
client_wid: str
status: WithdrawalStatus
txid: str
@classmethod
def create_from_api(cls, data: Dict) -> 'Withdrawal':
params = cls._prepare(data)
params['client_wid'] = data.get('client_wid', '')
params['status'] = WithdrawalStatus(data['status'])
params['txid'] = data['txid']
return cls(**params)
class Timeframe(IntEnum):
NOW = 0
MINUTES = 60
HOURS = 60 * MINUTES
DAYS = 24 * HOURS
WEEKS = 7 * DAYS
MONTHS = 30 * DAYS
@classmethod
def resolve(cls, seconds: int) -> int:
return seconds + int(time.time())

@ -1,9 +1,17 @@
import os
import time
import pytest
import cryptocom.exchange as cro
def test_timeframe():
days_5 = cro.Timeframe.DAYS * 5
result = cro.Timeframe.resolve(days_5)
assert result - int(time.time()) == days_5
assert cro.Timeframe.resolve(cro.Timeframe.NOW) == int(time.time())
def test_api_args(monkeypatch):
with pytest.raises(ValueError):
cro.Account()

@ -41,8 +41,8 @@ async def test_get_price(exchange: cro.Exchange):
@pytest.mark.asyncio
async def test_get_orderbook(exchange: cro.Exchange):
depth = 50
book = await exchange.get_orderbook(cro.pairs.CRO_USDT, depth=depth)
depth = 150
book = await exchange.get_orderbook(cro.pairs.CRO_USDT)
assert book.buys and book.sells
assert book.sells[0].price > book.buys[0].price
assert book.spread > 0
@ -62,7 +62,7 @@ async def test_listen_candles(exchange: cro.Exchange):
candles = []
pairs = (cro.pairs.CRO_USDC, cro.pairs.USDC_USDT, cro.pairs.BTC_USDT)
count = 0
default_count = 600
default_count = 2
async for candle in exchange.listen_candles(cro.Period.MINS, *pairs):
candles.append(candle)

@ -1,8 +1,12 @@
import time
import asyncio
import pytest
import cryptocom.exchange as cro
from cryptocom.exchange.structs import (
DepositStatus, Timeframe, WithdrawalStatus
)
@pytest.mark.asyncio
@ -23,6 +27,32 @@ async def test_account_get_balance(account: cro.Account):
assert coin in local_coins
@pytest.mark.asyncio
async def test_deposit_withdrawal_history(
exchange: cro.Exchange, account: cro.Account):
transactions = await account.get_withdrawal_history(cro.coins.CRO)
assert transactions
assert transactions[0].status == cro.WithdrawalStatus.COMPLETED
transactions = await account.get_deposit_history(cro.coins.CRO)
assert transactions
assert transactions[0].status == cro.DepositStatus.ARRIVED
transactions = await account.get_deposit_history(
cro.coins.CRO, status=DepositStatus.NOT_ARRIVED)
assert not transactions
transactions = await account.get_withdrawal_history(
cro.coins.CRO, status=WithdrawalStatus.CANCELLED)
assert not transactions
transactions = await account.get_deposit_history(
cro.coins.CRO, start_ts=time.time() - Timeframe.DAYS * 5,
end_ts=Timeframe.resolve(Timeframe.NOW)
)
assert not transactions
@pytest.mark.asyncio
async def test_no_duplicate_mass_limit_orders(
exchange: cro.Exchange, account: cro.Account):

Loading…
Cancel
Save