This commit is contained in:
lz_db
2025-11-16 12:31:03 +08:00
commit 0fab423a18
1451 changed files with 743213 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# -----------------------------------------------------------------------------
from ccxt.test.exchange.base.test_account import test_account # noqa E402
from ccxt.test.exchange.base.test_balance import test_balance # noqa E402
from ccxt.test.exchange.base.test_borrow_interest import test_borrow_interest # noqa E402
from ccxt.test.exchange.base.test_borrow_rate import test_borrow_rate # noqa E402
from ccxt.test.exchange.base.test_currency import test_currency # noqa E402
from ccxt.test.exchange.base.test_deposit_withdrawal import test_deposit_withdrawal # noqa E402
from ccxt.test.exchange.base.test_funding_rate_history import test_funding_rate_history # noqa E402
from ccxt.test.exchange.base.test_last_price import test_last_price # noqa E402
from ccxt.test.exchange.base.test_ledger_entry import test_ledger_entry # noqa E402
from ccxt.test.exchange.base.test_leverage_tier import test_leverage_tier # noqa E402
from ccxt.test.exchange.base.test_liquidation import test_liquidation # noqa E402
from ccxt.test.exchange.base.test_margin_mode import test_margin_mode # noqa E402
from ccxt.test.exchange.base.test_margin_modification import test_margin_modification # noqa E402
from ccxt.test.exchange.base.test_market import test_market # noqa E402
from ccxt.test.exchange.base.test_ohlcv import test_ohlcv # noqa E402
from ccxt.test.exchange.base.test_open_interest import test_open_interest # noqa E402
from ccxt.test.exchange.base.test_order import test_order # noqa E402
from ccxt.test.exchange.base.test_order_book import test_order_book # noqa E402
from ccxt.test.exchange.base.test_position import test_position # noqa E402
from ccxt.test.exchange.base.test_status import test_status # noqa E402
from ccxt.test.exchange.base.test_ticker import test_ticker # noqa E402
from ccxt.test.exchange.base.test_trade import test_trade # noqa E402
from ccxt.test.exchange.base.test_trading_fee import test_trading_fee # noqa E402

View File

@@ -0,0 +1,26 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_account(exchange, skipped_properties, method, entry):
format = {
'info': {},
'code': 'BTC',
'type': 'spot',
'id': '12345',
}
empty_allowed_for = ['code', 'id']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['code'])

View File

@@ -0,0 +1,56 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.precise import Precise # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_balance(exchange, skipped_properties, method, entry):
format = {
'free': {},
'used': {},
'total': {},
'info': {},
}
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format)
log_text = test_shared_methods.log_template(exchange, method, entry)
#
codes_total = list(entry['total'].keys())
codes_free = list(entry['free'].keys())
codes_used = list(entry['used'].keys())
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, codes_total, 'total')
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, codes_free, 'free')
test_shared_methods.assert_non_emtpy_array(exchange, skipped_properties, method, codes_used, 'used')
all_codes = exchange.array_concat(codes_total, codes_free)
all_codes = exchange.array_concat(all_codes, codes_used)
codes_length = len(codes_total)
free_length = len(codes_free)
used_length = len(codes_used)
assert (codes_length == free_length) or (codes_length == used_length), 'free and total and used codes have different lengths' + log_text
for i in range(0, len(all_codes)):
code = all_codes[i]
# testSharedMethods.assertCurrencyCode (exchange, skippedProperties, method, entry, code);
assert code in entry['total'], 'code ' + code + ' not in total' + log_text
assert code in entry['free'], 'code ' + code + ' not in free' + log_text
assert code in entry['used'], 'code ' + code + ' not in used' + log_text
total = exchange.safe_string(entry['total'], code)
free = exchange.safe_string(entry['free'], code)
used = exchange.safe_string(entry['used'], code)
assert total is not None, 'total is undefined' + log_text
assert free is not None, 'free is undefined' + log_text
assert used is not None, 'used is undefined' + log_text
assert Precise.string_ge(total, '0'), 'total is not positive' + log_text
assert Precise.string_ge(free, '0'), 'free is not positive' + log_text
assert Precise.string_ge(used, '0'), 'used is not positive' + log_text
sum_free_used = Precise.string_add(free, used)
assert Precise.string_eq(total, sum_free_used), 'free and used do not sum to total' + log_text

View File

@@ -0,0 +1,35 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_borrow_interest(exchange, skipped_properties, method, entry, requested_code, requested_symbol):
format = {
'info': {},
'account': 'BTC/USDT',
'currency': 'USDT',
'interest': exchange.parse_number('0.1444'),
'interestRate': exchange.parse_number('0.0006'),
'amountBorrowed': exchange.parse_number('30.0'),
'timestamp': 1638230400000,
'datetime': '2021-11-30T00:00:00.000Z',
}
empty_allowed_for = ['account']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['currency'], requested_code)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, entry['account'], requested_symbol)
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'interest', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'interestRate', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'amountBorrowed', '0')

View File

@@ -0,0 +1,32 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_borrow_rate(exchange, skipped_properties, method, entry, requested_code):
format = {
'info': {},
'currency': 'USDT',
'timestamp': 1638230400000,
'datetime': '2021-11-30T00:00:00.000Z',
'rate': exchange.parse_number('0.0006'),
'period': 86400000,
}
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['currency'], requested_code)
#
# assert (borrowRate['period'] === 86400000 || borrowRate['period'] === 3600000) # Milliseconds in an hour or a day
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'period', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'rate', '0')

View File

@@ -0,0 +1,85 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_currency(exchange, skipped_properties, method, entry):
format = {
'id': 'btc',
'code': 'BTC',
}
# todo: remove fee from empty
empty_allowed_for = ['name', 'fee']
# todo: info key needs to be added in base, when exchange does not have fetchCurrencies
is_native = exchange.has['fetchCurrencies'] and exchange.has['fetchCurrencies'] != 'emulated'
currency_type = exchange.safe_string(entry, 'type')
if is_native:
format['info'] = {}
# todo: 'name': 'Bitcoin', # uppercase string, base currency, 2 or more letters
format['withdraw'] = True # withdraw enabled
format['deposit'] = True # deposit enabled
format['precision'] = exchange.parse_number('0.0001') # in case of SIGNIFICANT_DIGITS it will be 4 - number of digits "after the dot"
format['fee'] = exchange.parse_number('0.001')
format['networks'] = {}
format['limits'] = {
'withdraw': {
'min': exchange.parse_number('0.01'),
'max': exchange.parse_number('1000'),
},
'deposit': {
'min': exchange.parse_number('0.01'),
'max': exchange.parse_number('1000'),
},
}
format['type'] = 'crypto' # crypto, fiat, leverage, other
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'type', ['fiat', 'crypto', 'leveraged', 'other', None]) # todo: remove undefined
# only require "deposit" & "withdraw" values, when currency is not fiat, or when it's fiat, but not skipped
if currency_type != 'crypto' and ('depositForNonCrypto' in skipped_properties):
empty_allowed_for.append('deposit')
if currency_type != 'crypto' and ('withdrawForNonCrypto' in skipped_properties):
empty_allowed_for.append('withdraw')
if currency_type == 'leveraged' or currency_type == 'other':
empty_allowed_for.append('precision')
#
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['code'])
# check if empty networks should be skipped
networks = exchange.safe_dict(entry, 'networks', {})
network_keys = list(networks.keys())
network_keys_length = len(network_keys)
if network_keys_length == 0 and ('skipCurrenciesWithoutNetworks' in skipped_properties):
return
#
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
#
test_shared_methods.check_precision_accuracy(exchange, skipped_properties, method, entry, 'precision')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'fee', '0')
if not ('limits' in skipped_properties):
limits = exchange.safe_value(entry, 'limits', {})
withdraw_limits = exchange.safe_value(limits, 'withdraw', {})
deposit_limits = exchange.safe_value(limits, 'deposit', {})
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, withdraw_limits, 'min', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, withdraw_limits, 'max', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, deposit_limits, 'min', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, deposit_limits, 'max', '0')
# max should be more than min (withdrawal limits)
min_string_withdrawal = exchange.safe_string(withdraw_limits, 'min')
if min_string_withdrawal is not None:
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, withdraw_limits, 'max', min_string_withdrawal)
# max should be more than min (deposit limits)
min_string_deposit = exchange.safe_string(deposit_limits, 'min')
if min_string_deposit is not None:
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, deposit_limits, 'max', min_string_deposit)
# check valid ID & CODE
test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, entry, entry['id'], entry['code'])

View File

@@ -0,0 +1,50 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_deposit_withdrawal(exchange, skipped_properties, method, entry, requested_code, now):
format = {
'info': {},
'id': '1234',
'txid': '0x1345FEG45EAEF7',
'timestamp': 1502962946216,
'datetime': '2017-08-17 12:42:48.000',
'network': 'ETH',
'address': '0xEFE3487358AEF352345345',
'addressTo': '0xEFE3487358AEF352345123',
'addressFrom': '0xEFE3487358AEF352345456',
'tag': 'smth',
'tagTo': 'smth',
'tagFrom': 'smth',
'type': 'deposit',
'amount': exchange.parse_number('1.234'),
'currency': 'USDT',
'status': 'ok',
'updated': 1502962946233,
'fee': {},
}
empty_allowed_for = ['address', 'addressTo', 'addressFrom', 'tag', 'tagTo', 'tagFrom'] # below we still do assertion for to/from
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['currency'], requested_code)
#
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'status', ['ok', 'pending', 'failed', 'rejected', 'canceled'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'type', ['deposit', 'withdrawal'])
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', '0')
test_shared_methods.assert_fee_structure(exchange, skipped_properties, method, entry, 'fee')
if entry['type'] == 'deposit':
test_shared_methods.assert_type(exchange, skipped_properties, entry, 'addressFrom', format)
else:
test_shared_methods.assert_type(exchange, skipped_properties, entry, 'addressTo', format)

View File

@@ -0,0 +1,29 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_funding_rate_history(exchange, skipped_properties, method, entry, symbol):
format = {
'info': {},
'symbol': 'BTC/USDT:USDT',
'timestamp': 1638230400000,
'datetime': '2021-11-30T00:00:00.000Z',
'fundingRate': exchange.parse_number('0.0006'),
}
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'fundingRate', '-100')
test_shared_methods.assert_less(exchange, skipped_properties, method, entry, 'fundingRate', '100')

View File

@@ -0,0 +1,31 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_last_price(exchange, skipped_properties, method, entry, symbol):
format = {
'info': {},
'symbol': 'ETH/BTC',
'timestamp': 1502962946216,
'datetime': '2017-09-01T00:00:00',
'price': exchange.parse_number('1.234'),
'side': 'buy',
}
empty_allowed_for = ['timestamp', 'datetime', 'side', 'price'] # binance sometimes provides empty prices for old pairs
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
#
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'price', '0')
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'side', ['buy', 'sell', None])

View File

@@ -0,0 +1,45 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_ledger_entry(exchange, skipped_properties, method, entry, requested_code, now):
format = {
'info': {},
'id': 'x1234',
'currency': 'BTC',
'account': 'spot',
'referenceId': 'foo',
'referenceAccount': 'bar',
'status': 'ok',
'amount': exchange.parse_number('22'),
'before': exchange.parse_number('111'),
'after': exchange.parse_number('133'),
'fee': {},
'direction': 'in',
'timestamp': 1638230400000,
'datetime': '2021-11-30T00:00:00.000Z',
'type': 'deposit',
}
empty_allowed_for = ['referenceId', 'referenceAccount', 'id']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['currency'], requested_code)
#
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'direction', ['in', 'out'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'type', ['trade', 'transaction', 'margin', 'cashback', 'referral', 'transfer', 'fee'])
# testSharedMethods.assertInArray (exchange, skippedProperties, method, entry, 'account', ['spot', 'swap', .. ]); # todo
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'before', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'after', '0')

View File

@@ -0,0 +1,33 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_leverage_tier(exchange, skipped_properties, method, entry):
format = {
'tier': exchange.parse_number('1'),
'minNotional': exchange.parse_number('0'),
'maxNotional': exchange.parse_number('5000'),
'maintenanceMarginRate': exchange.parse_number('0.01'),
'maxLeverage': exchange.parse_number('25'),
'info': {},
}
empty_allowed_for = ['maintenanceMarginRate']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
#
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'tier', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'minNotional', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'maxNotional', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'maxLeverage', '1')
test_shared_methods.assert_less_or_equal(exchange, skipped_properties, method, entry, 'maintenanceMarginRate', '1')

View File

@@ -0,0 +1,50 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.precise import Precise # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_liquidation(exchange, skipped_properties, method, entry, symbol):
format = {
'info': {},
'symbol': 'ETH/BTC',
'contracts': exchange.parse_number('1.234'),
'contractSize': exchange.parse_number('1.234'),
'price': exchange.parse_number('1.234'),
'baseValue': exchange.parse_number('1.234'),
'quoteValue': exchange.parse_number('1.234'),
'timestamp': 1502962946216,
'datetime': '2017-09-01T00:00:00',
}
# todo: atm, many exchanges fail, so temporarily decrease stict mode
empty_allowed_for = ['timestamp', 'datetime', 'quoteValue', 'baseValue', 'previousClose', 'price', 'contractSize', 'contracts']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
log_text = test_shared_methods.log_template(exchange, method, entry)
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contracts', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contractSize', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'price', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'baseValue', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'quoteValue', '0')
contracts = exchange.safe_string(entry, 'contracts')
contract_size = exchange.safe_string(entry, 'contractSize')
price = exchange.safe_string(entry, 'price')
base_value = exchange.safe_string(entry, 'baseValue')
if contracts and contract_size:
assert Precise.string_eq(base_value, Precise.string_mul(contracts, contract_size)), 'baseValue == contracts * contractSize' + log_text
if price:
assert Precise.string_eq(base_value, Precise.string_mul(Precise.string_mul(contracts, contract_size), price)), 'quoteValue == contracts * contractSize * price' + log_text
# if singular was called, then symbol needs to be asserted
if method == 'watchLiquidations' or method == 'fetchLiquidations':
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)

View File

@@ -0,0 +1,24 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_margin_mode(exchange, skipped_properties, method, entry):
format = {
'info': {},
'symbol': 'BTC/USDT:USDT',
'marginMode': 'cross',
}
empty_allowed_for = ['symbol']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)

View File

@@ -0,0 +1,35 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_margin_modification(exchange, skipped_properties, method, entry):
format = {
'info': {},
'type': 'add',
'amount': exchange.parse_number('0.1'),
'total': exchange.parse_number('0.29934828'),
'code': 'USDT',
'symbol': 'ADA/USDT:USDT',
'status': 'ok',
}
empty_allowed_for = ['status', 'symbol', 'code', 'total', 'amount']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_currency_code(exchange, skipped_properties, method, entry, entry['code'])
#
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'total', '0')
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'type', ['add', 'reduce', 'set'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'status', ['ok', 'pending', 'canceled', 'failed'])
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol')

View File

@@ -0,0 +1,241 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.precise import Precise # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_market(exchange, skipped_properties, method, market):
format = {
'id': 'btcusd',
'symbol': 'BTC/USD',
'base': 'BTC',
'quote': 'USD',
'taker': exchange.parse_number('0.0011'),
'maker': exchange.parse_number('0.0009'),
'baseId': 'btc',
'quoteId': 'usd',
'active': False,
'type': 'spot',
'linear': False,
'inverse': False,
'spot': False,
'swap': False,
'future': False,
'option': False,
'margin': False,
'contract': False,
'contractSize': exchange.parse_number('0.001'),
'expiry': 1656057600000,
'expiryDatetime': '2022-06-24T08:00:00.000Z',
'optionType': 'put',
'strike': exchange.parse_number('56000'),
'settle': 'XYZ',
'settleId': 'Xyz',
'precision': {
'price': exchange.parse_number('0.001'),
'amount': exchange.parse_number('0.001'),
'cost': exchange.parse_number('0.001'),
},
'limits': {
'amount': {
'min': exchange.parse_number('0.01'),
'max': exchange.parse_number('1000'),
},
'price': {
'min': exchange.parse_number('0.01'),
'max': exchange.parse_number('1000'),
},
'cost': {
'min': exchange.parse_number('0.01'),
'max': exchange.parse_number('1000'),
},
},
'marginModes': {
'cross': True,
'isolated': False,
},
'info': {},
}
# temporary: only test QUANTO markets where that prop exists (todo: add in type later)
if 'quanto' in market:
format['quanto'] = False # whether the market is QUANTO or not
# define locals
spot = market['spot']
contract = market['contract']
swap = market['swap']
future = market['future']
option = market['option']
index = exchange.safe_bool(market, 'index') # todo: unify
is_index = (index is not None) and index
linear = market['linear']
inverse = market['inverse']
quanto = exchange.safe_bool(market, 'quanto') # todo: unify
is_quanto = (quanto is not None) and quanto
#
empty_allowed_for = ['margin']
if not contract:
empty_allowed_for.append('contractSize')
empty_allowed_for.append('linear')
empty_allowed_for.append('inverse')
empty_allowed_for.append('quanto')
empty_allowed_for.append('settle')
empty_allowed_for.append('settleId')
if not future and not option:
empty_allowed_for.append('expiry')
empty_allowed_for.append('expiryDatetime')
if not option:
empty_allowed_for.append('optionType')
empty_allowed_for.append('strike')
test_shared_methods.assert_structure(exchange, skipped_properties, method, market, format, empty_allowed_for)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, market, 'symbol')
log_text = test_shared_methods.log_template(exchange, method, market)
# check taker/maker
# todo: check not all to be within 0-1.0
test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'taker', '-100')
test_shared_methods.assert_less(exchange, skipped_properties, method, market, 'taker', '100')
test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'maker', '-100')
test_shared_methods.assert_less(exchange, skipped_properties, method, market, 'maker', '100')
# validate type
valid_types = ['spot', 'margin', 'swap', 'future', 'option', 'index', 'other']
test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'type', valid_types)
# validate subTypes
valid_sub_types = ['linear', 'inverse', 'quanto', None]
test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'subType', valid_sub_types)
# check if 'type' is consistent
checked_types = ['spot', 'swap', 'future', 'option']
for i in range(0, len(checked_types)):
type = checked_types[i]
if market[type]:
assert type == market['type'], 'market.type (' + market['type'] + ') not equal to "' + type + '"' + log_text
# check if 'subType' is consistent
if swap or future:
checked_sub_types = ['linear', 'inverse']
for i in range(0, len(checked_sub_types)):
sub_type = checked_sub_types[i]
if market[sub_type]:
assert sub_type == market['subType'], 'market.subType (' + market['subType'] + ') not equal to "' + sub_type + '"' + log_text
# margin check (todo: add margin as mandatory, instead of undefined)
if spot:
# for spot market, 'margin' can be either true/false or undefined
test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'margin', [True, False, None])
else:
# otherwise, it must be false or undefined
test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'margin', [False, None])
# check mutually exclusive fields
if spot:
assert not contract and linear is None and inverse is None and not option and not swap and not future, 'for spot market, none of contract/linear/inverse/option/swap/future should be set' + log_text
else:
# if not spot, any of the below should be true
assert contract and (future or swap or option or is_index), 'for non-spot markets, any of (future/swap/option/index) should be set' + log_text
contract_size = exchange.safe_string(market, 'contractSize')
# contract fields
if contract:
if is_quanto:
assert linear is False, 'linear must be false when "quanto" is true' + log_text
assert inverse is False, 'inverse must be false when "quanto" is true' + log_text
else:
# if false or undefined
assert inverse is not None, 'inverse must be defined when "contract" is true' + log_text
assert linear is not None, 'linear must be defined when "contract" is true' + log_text
assert linear != inverse, 'linear and inverse must not be the same' + log_text
# contract size should be defined
assert (('contractSize' in skipped_properties) or contract_size is not None), '"contractSize" must be defined when "contract" is true' + log_text
# contract size should be above zero
assert ('contractSize' in skipped_properties) or Precise.string_gt(contract_size, '0'), '"contractSize" must be > 0 when "contract" is true' + log_text
# settle should be defined
assert ('settle' in skipped_properties) or (market['settle'] is not None and market['settleId'] is not None), '"settle" & "settleId" must be defined when "contract" is true' + log_text
else:
# linear & inverse needs to be undefined
assert linear is None and inverse is None and quanto is None, 'market linear and inverse (and quanto) must be undefined when "contract" is false' + log_text
# contract size should be undefined
assert contract_size is None, '"contractSize" must be undefined when "contract" is false' + log_text
# settle should be undefined
assert (market['settle'] is None) and (market['settleId'] is None), '"settle" must be undefined when "contract" is false' + log_text
# future, swap and option should be mutually exclusive
if market['future']:
assert not market['swap'] and not market['option'] and not is_index, 'market swap and option must be false when "future" is true' + log_text
elif market['swap']:
assert not market['future'] and not market['option'], 'market future and option must be false when "swap" is true' + log_text
elif market['option']:
assert not market['future'] and not market['swap'], 'market future and swap must be false when "option" is true' + log_text
# check specific fields for options & futures
if option or future:
# future or option markets need 'expiry' and 'expiryDatetime'
assert market['expiry'] is not None, '"expiry" must be defined when "future" is true' + log_text
assert market['expiryDatetime'] is not None, '"expiryDatetime" must be defined when "future" is true' + log_text
# expiry datetime should be correct
iso_string = exchange.iso8601(market['expiry'])
assert market['expiryDatetime'] == iso_string, 'expiryDatetime ("' + market['expiryDatetime'] + '") must be equal to expiry in iso8601 format "' + iso_string + '"' + log_text
test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'expiry', '0')
if option:
# strike should be defined
assert (('strike' in skipped_properties) or market['strike'] is not None), '"strike" must be defined when "option" is true' + log_text
test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'strike', '0')
# optionType should be defined
assert (('optionType' in skipped_properties) or market['optionType'] is not None), '"optionType" must be defined when "option" is true' + log_text
test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'optionType', ['put', 'call'])
else:
# if not option, then strike and optionType should be undefined
assert market['strike'] is None, '"strike" must be undefined when "option" is false' + log_text
assert market['optionType'] is None, '"optionType" must be undefined when "option" is false' + log_text
elif spot:
# otherwise, expiry needs to be undefined
assert (market['expiry'] is None) and (market['expiryDatetime'] is None), '"expiry" and "expiryDatetime" must be undefined when it is not future|option market' + log_text
# check precisions
precision_keys = list(market['precision'].keys())
precision_keys_len = len(precision_keys)
assert precision_keys_len >= 2, 'precision should have "amount" and "price" keys at least' + log_text
for i in range(0, len(precision_keys)):
price_or_amount_key = precision_keys[i]
# only allow very high priced markets (wher coin costs around 100k) to have a 5$ price tickSize
is_exclusive_pair = market['baseId'] == 'BTC'
is_non_spot = not spot # such high precision is only allowed in contract markets
is_price = price_or_amount_key == 'price'
is_tick_size_5 = Precise.string_eq('5', exchange.safe_string(market['precision'], price_or_amount_key))
if is_non_spot and is_price and is_exclusive_pair and is_tick_size_5:
continue
if not ('precision' in skipped_properties):
test_shared_methods.check_precision_accuracy(exchange, skipped_properties, method, market['precision'], price_or_amount_key)
is_inactive_market = market['active'] is False
# check limits
limits_keys = list(market['limits'].keys())
limits_keys_length = len(limits_keys)
assert limits_keys_length >= 3, 'limits should have "amount", "price" and "cost" keys at least' + log_text
for i in range(0, len(limits_keys)):
key = limits_keys[i]
limit_entry = market['limits'][key]
if is_inactive_market:
continue # check limits
if not ('limits' in skipped_properties):
# min >= 0
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, limit_entry, 'min', '0')
# max >= 0
test_shared_methods.assert_greater(exchange, skipped_properties, method, limit_entry, 'max', '0')
# max >= min
min_string = exchange.safe_string(limit_entry, 'min')
if min_string is not None:
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, limit_entry, 'max', min_string)
# check currencies
test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['baseId'], market['base'])
test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['quoteId'], market['quote'])
test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['settleId'], market['settle'])
# check ts
test_shared_methods.assert_timestamp(exchange, skipped_properties, method, market, None, 'created')
# margin modes
if not ('marginModes' in skipped_properties):
margin_modes = exchange.safe_dict(market, 'marginModes') # in future, remove safeDict
assert 'cross' in margin_modes, 'marginModes should have "cross" key' + log_text
assert 'isolated' in margin_modes, 'marginModes should have "isolated" key' + log_text
test_shared_methods.assert_in_array(exchange, skipped_properties, method, margin_modes, 'cross', [True, False, None])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, margin_modes, 'isolated', [True, False, None])

View File

@@ -0,0 +1,33 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_ohlcv(exchange, skipped_properties, method, entry, symbol, now):
format = [1638230400000, exchange.parse_number('0.123'), exchange.parse_number('0.125'), exchange.parse_number('0.121'), exchange.parse_number('0.122'), exchange.parse_number('123.456')]
empty_not_allowed_for = [0, 1, 2, 3, 4, 5]
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_not_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now, 0)
log_text = test_shared_methods.log_template(exchange, method, entry)
#
assert len(entry) >= 6, 'ohlcv array length should be >= 6;' + log_text
if not ('roundTimestamp' in skipped_properties):
test_shared_methods.assert_round_minute_timestamp(exchange, skipped_properties, method, entry, 0)
high = exchange.safe_string(entry, 2)
low = exchange.safe_string(entry, 3)
test_shared_methods.assert_less_or_equal(exchange, skipped_properties, method, entry, '1', high)
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, '1', low)
test_shared_methods.assert_less_or_equal(exchange, skipped_properties, method, entry, '4', high)
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, '4', low)
assert (symbol is None) or (isinstance(symbol, str)), 'symbol ' + symbol + ' is incorrect' + log_text # todo: check with standard symbol check

View File

@@ -0,0 +1,32 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_open_interest(exchange, skipped_properties, method, entry):
format = {
'symbol': 'BTC/USDT',
'openInterestAmount': exchange.parse_number('3544581864.598'),
'openInterestValue': exchange.parse_number('3544581864.598'),
'timestamp': 1649373600000,
'datetime': '2022-04-07T23:20:00.000Z',
'info': {},
}
empty_allowed_for = ['symbol', 'timestamp', 'openInterestAmount', 'openInterestValue', 'datetime']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol')
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
#
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'openInterestAmount', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'openInterestValue', '0')

View File

@@ -0,0 +1,69 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
from ccxt.test.exchange.base.test_trade import test_trade # noqa E402
def test_order(exchange, skipped_properties, method, entry, symbol, now):
format = {
'info': {},
'id': '123',
'clientOrderId': '1234',
'timestamp': 1649373600000,
'datetime': '2022-04-07T23:20:00.000Z',
'lastTradeTimestamp': 1649373610000,
'symbol': 'XYZ/USDT',
'type': 'limit',
'timeInForce': 'GTC',
'postOnly': True,
'side': 'sell',
'price': exchange.parse_number('1.23456'),
'stopPrice': exchange.parse_number('1.1111'),
'amount': exchange.parse_number('1.23'),
'cost': exchange.parse_number('2.34'),
'average': exchange.parse_number('1.234'),
'filled': exchange.parse_number('1.23'),
'remaining': exchange.parse_number('0.123'),
'status': 'ok',
'fee': {},
'trades': [],
}
empty_allowed_for = ['clientOrderId', 'stopPrice', 'trades', 'timestamp', 'datetime', 'lastTradeTimestamp', 'average', 'type', 'timeInForce', 'postOnly', 'side', 'price', 'amount', 'cost', 'filled', 'remaining', 'status', 'fee'] # there are exchanges that return only order id, so we don't need to strictly requite all props to be set.
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now)
#
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'timeInForce', ['GTC', 'GTK', 'IOC', 'FOK', 'PO'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'status', ['open', 'closed', 'canceled'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'side', ['buy', 'sell'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'postOnly', [True, False])
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'price', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'stopPrice', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'cost', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'average', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'filled', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'remaining', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', exchange.safe_string(entry, 'remaining'))
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'amount', exchange.safe_string(entry, 'filled'))
if not ('trades' in skipped_properties):
skipped_new = exchange.deep_extend(skipped_properties, {
'timestamp': True,
'datetime': True,
'side': True,
})
if entry['trades'] is not None:
for i in range(0, len(entry['trades'])):
test_trade(exchange, skipped_new, method, entry['trades'][i], symbol, now)
test_shared_methods.assert_fee_structure(exchange, skipped_properties, method, entry, 'fee')

View File

@@ -0,0 +1,66 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.precise import Precise # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_order_book(exchange, skipped_properties, method, orderbook, symbol):
format = {
'symbol': 'ETH/BTC',
'asks': [[exchange.parse_number('1.24'), exchange.parse_number('0.453')], [exchange.parse_number('1.25'), exchange.parse_number('0.157')]],
'bids': [[exchange.parse_number('1.23'), exchange.parse_number('0.123')], [exchange.parse_number('1.22'), exchange.parse_number('0.543')]],
'timestamp': 1504224000000,
'datetime': '2017-09-01T00:00:00',
'nonce': 134234234,
}
empty_allowed_for = ['nonce']
# turn into copy: https://discord.com/channels/690203284119617602/921046068555313202/1220626834887282728
orderbook = exchange.deep_extend({}, orderbook)
test_shared_methods.assert_structure(exchange, skipped_properties, method, orderbook, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, orderbook)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, orderbook, 'symbol', symbol)
log_text = test_shared_methods.log_template(exchange, method, orderbook)
# todo: check non-emtpy arrays for bids/asks for toptier exchanges
bids = orderbook['bids']
bids_length = len(bids)
for i in range(0, bids_length):
current_bid_string = exchange.safe_string(bids[i], 0)
if not ('compareToNextItem' in skipped_properties):
next_i = i + 1
if bids_length > next_i:
next_bid_string = exchange.safe_string(bids[next_i], 0)
assert Precise.string_gt(current_bid_string, next_bid_string), 'current bid should be > than the next one: ' + current_bid_string + '>' + next_bid_string + log_text
if not ('compareToZero' in skipped_properties):
# compare price & volume to zero
test_shared_methods.assert_greater(exchange, skipped_properties, method, bids[i], 0, '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, bids[i], 1, '0')
asks = orderbook['asks']
asks_length = len(asks)
for i in range(0, asks_length):
current_ask_string = exchange.safe_string(asks[i], 0)
if not ('compareToNextItem' in skipped_properties):
next_i = i + 1
if asks_length > next_i:
next_ask_string = exchange.safe_string(asks[next_i], 0)
assert Precise.string_lt(current_ask_string, next_ask_string), 'current ask should be < than the next one: ' + current_ask_string + '<' + next_ask_string + log_text
if not ('compareToZero' in skipped_properties):
# compare price & volume to zero
test_shared_methods.assert_greater(exchange, skipped_properties, method, asks[i], 0, '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, asks[i], 1, '0')
if not ('spread' in skipped_properties):
if bids_length and asks_length:
first_bid = exchange.safe_string(bids[0], 0)
first_ask = exchange.safe_string(asks[0], 0)
# check bid-ask spread
assert Precise.string_lt(first_bid, first_ask), 'bids[0][0] (' + first_bid + ') should be < than asks[0][0] (' + first_ask + ')' + log_text

View File

@@ -0,0 +1,60 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_position(exchange, skipped_properties, method, entry, symbol, now):
format = {
'info': {},
'symbol': 'XYZ/USDT',
'timestamp': 1504224000000,
'datetime': '2017-09-01T00:00:00',
'initialMargin': exchange.parse_number('1.234'),
'initialMarginPercentage': exchange.parse_number('0.123'),
'maintenanceMargin': exchange.parse_number('1.234'),
'maintenanceMarginPercentage': exchange.parse_number('0.123'),
'entryPrice': exchange.parse_number('1.234'),
'notional': exchange.parse_number('1.234'),
'leverage': exchange.parse_number('1.234'),
'unrealizedPnl': exchange.parse_number('1.234'),
'contracts': exchange.parse_number('1'),
'contractSize': exchange.parse_number('1.234'),
'marginRatio': exchange.parse_number('1.234'),
'liquidationPrice': exchange.parse_number('1.234'),
'markPrice': exchange.parse_number('1.234'),
'collateral': exchange.parse_number('1.234'),
'marginMode': 'cross',
'side': 'long',
'percentage': exchange.parse_number('1.234'),
}
emptyot_allowed_for = ['liquidationPrice', 'initialMargin', 'initialMarginPercentage', 'maintenanceMargin', 'maintenanceMarginPercentage', 'marginRatio']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, emptyot_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'side', ['long', 'short'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'marginMode', ['cross', 'isolated'])
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'leverage', '0')
test_shared_methods.assert_less_or_equal(exchange, skipped_properties, method, entry, 'leverage', '200')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'initialMargin', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'initialMarginPercentage', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'maintenanceMargin', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'maintenanceMarginPercentage', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'entryPrice', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'notional', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contracts', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'contractSize', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'marginRatio', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'liquidationPrice', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'markPrice', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'collateral', '0')

View File

@@ -0,0 +1,551 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.decimal_to_precision import DECIMAL_PLACES # noqa E402
from ccxt.base.decimal_to_precision import TICK_SIZE # noqa E402
import numbers # noqa E402
import json # noqa E402
from ccxt.base.precise import Precise # noqa E402
from ccxt.base.errors import OnMaintenance # noqa E402
from ccxt.base.errors import OperationFailed # noqa E402
def log_template(exchange, method, entry):
# there are cases when exchange is undefined (eg. base tests)
id = exchange.id if (exchange is not None) else 'undefined'
method_string = method if (method is not None) else 'undefined'
entry_string = exchange.json(entry) if (exchange is not None) else ''
return ' <<< ' + id + ' ' + method_string + ' ::: ' + entry_string + ' >>> '
def is_temporary_failure(e):
return (isinstance(e, OperationFailed)) and (not (isinstance(e, OnMaintenance)))
def string_value(value):
string_val = None
if isinstance(value, str):
string_val = value
elif value is None:
string_val = 'undefined'
else:
string_val = str(value)
return string_val
def assert_type(exchange, skipped_properties, entry, key, format):
if key in skipped_properties:
return None
# because "typeof" string is not transpilable without === 'name', we list them manually at this moment
entry_key_val = exchange.safe_value(entry, key)
format_key_val = exchange.safe_value(format, key)
same_string = (isinstance(entry_key_val, str)) and (isinstance(format_key_val, str))
same_numeric = (isinstance(entry_key_val, numbers.Real)) and (isinstance(format_key_val, numbers.Real))
same_boolean = ((entry_key_val) or (entry_key_val is False)) and ((format_key_val) or (format_key_val is False))
same_array = isinstance(entry_key_val, list) and isinstance(format_key_val, list)
same_object = (isinstance(entry_key_val, dict)) and (isinstance(format_key_val, dict))
result = (entry_key_val is None) or same_string or same_numeric or same_boolean or same_array or same_object
return result
def assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for=None, deep=False):
log_text = log_template(exchange, method, entry)
assert entry is not None, 'item is null/undefined' + log_text
# get all expected & predefined keys for this specific item and ensure thos ekeys exist in parsed structure
allow_empty_skips = exchange.safe_list(skipped_properties, 'allowNull', [])
if empty_allowed_for is not None:
empty_allowed_for = concat(empty_allowed_for, allow_empty_skips)
if isinstance(format, list):
assert isinstance(entry, list), 'entry is not an array' + log_text
real_length = len(entry)
expected_length = len(format)
assert real_length == expected_length, 'entry length is not equal to expected length of ' + str(expected_length) + log_text
for i in range(0, len(format)):
empty_allowed_for_this_key = (empty_allowed_for is None) or exchange.in_array(i, empty_allowed_for)
value = entry[i]
if i in skipped_properties:
continue
# check when:
# - it's not inside "allowe empty values" list
# - it's not undefined
if empty_allowed_for_this_key and (value is None):
continue
assert value is not None, str(i) + ' index is expected to have a value' + log_text
# because of other langs, this is needed for arrays
type_assertion = assert_type(exchange, skipped_properties, entry, i, format)
assert type_assertion, str(i) + ' index does not have an expected type ' + log_text
else:
assert isinstance(entry, dict), 'entry is not an object' + log_text
keys = list(format.keys())
for i in range(0, len(keys)):
key = keys[i]
if key in skipped_properties:
continue
assert key in entry, '"' + string_value(key) + '" key is missing from structure' + log_text
if key in skipped_properties:
continue
empty_allowed_for_this_key = (empty_allowed_for is None) or exchange.in_array(key, empty_allowed_for)
value = entry[key]
# check when:
# - it's not inside "allowe empty values" list
# - it's not undefined
if empty_allowed_for_this_key and (value is None):
continue
# if it was in needed keys, then it should have value.
assert value is not None, '"' + string_value(key) + '" key is expected to have a value' + log_text
# add exclusion for info key, as it can be any type
if key != 'info':
type_assertion = assert_type(exchange, skipped_properties, entry, key, format)
assert type_assertion, '"' + string_value(key) + '" key is neither undefined, neither of expected type' + log_text
if deep:
if isinstance(value, dict):
assert_structure(exchange, skipped_properties, method, value, format[key], empty_allowed_for, deep)
def assert_timestamp(exchange, skipped_properties, method, entry, now_to_check=None, key_name_or_index='timestamp', allow_null=True):
log_text = log_template(exchange, method, entry)
skip_value = exchange.safe_value(skipped_properties, key_name_or_index)
if skip_value is not None:
return # skipped
is_date_time_object = isinstance(key_name_or_index, str)
if is_date_time_object:
assert (key_name_or_index in entry), 'timestamp key "' + key_name_or_index + '" is missing from structure' + log_text
else:
# if index was provided (mostly from fetchOHLCV) then we check if it exists, as mandatory
assert not (entry[key_name_or_index] is None), 'timestamp index ' + string_value(key_name_or_index) + ' is undefined' + log_text
ts = entry[key_name_or_index]
assert ts is not None or allow_null, 'timestamp is null' + log_text
if ts is not None:
assert isinstance(ts, numbers.Real), 'timestamp is not numeric' + log_text
assert isinstance(ts, int), 'timestamp should be an integer' + log_text
min_ts = 1230940800000 # 03 Jan 2009 - first block
max_ts = 2147483648000 # 19 Jan 2038 - max int
assert ts > min_ts, 'timestamp is impossible to be before ' + str(min_ts) + ' (03.01.2009)' + log_text # 03 Jan 2009 - first block
assert ts < max_ts, 'timestamp more than ' + str(max_ts) + ' (19.01.2038)' + log_text # 19 Jan 2038 - int32 overflows # 7258118400000 -> Jan 1 2200
if now_to_check is not None:
max_ms_offset = 60000 # 1 min
assert ts < now_to_check + max_ms_offset, 'returned item timestamp (' + exchange.iso8601(ts) + ') is ahead of the current time (' + exchange.iso8601(now_to_check) + ')' + log_text
def assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now_to_check=None, key_name_or_index='timestamp', allow_null=True):
log_text = log_template(exchange, method, entry)
skip_value = exchange.safe_value(skipped_properties, key_name_or_index)
if skip_value is not None:
return
assert_timestamp(exchange, skipped_properties, method, entry, now_to_check, key_name_or_index)
is_date_time_object = isinstance(key_name_or_index, str)
# only in case if the entry is a dictionary, thus it must have 'timestamp' & 'datetime' string keys
if is_date_time_object:
# we also test 'datetime' here because it's certain sibling of 'timestamp'
assert ('datetime' in entry), '"datetime" key is missing from structure' + log_text
dt = entry['datetime']
assert dt is not None or allow_null, 'timestamp is null' + log_text
if dt is not None:
assert isinstance(dt, str), '"datetime" key does not have a string value' + log_text
# there are exceptional cases, like getting microsecond-targeted string '2022-08-08T22:03:19.014680Z', so parsed unified timestamp, which carries only 13 digits (millisecond precision) can not be stringified back to microsecond accuracy, causing the bellow assertion to fail
# assert (dt === exchange.iso8601 (entry['timestamp']))
# so, we have to compare with millisecond accururacy
dt_parsed = exchange.parse8601(dt)
ts_ms = entry['timestamp']
diff = abs(dt_parsed - ts_ms)
if diff >= 500:
dt_parsed_string = exchange.iso8601(dt_parsed)
dt_entry_string = exchange.iso8601(ts_ms)
assert False, 'datetime is not iso8601 of timestamp:' + dt_parsed_string + '(string) != ' + dt_entry_string + '(from ts)' + log_text
def assert_currency_code(exchange, skipped_properties, method, entry, actual_code, expected_code=None, allow_null=True):
if ('currency' in skipped_properties) or ('currencyIdAndCode' in skipped_properties):
return
log_text = log_template(exchange, method, entry)
assert actual_code is not None or allow_null, 'currency code is null' + log_text
if actual_code is not None:
assert isinstance(actual_code, str), 'currency code should be either undefined or a string' + log_text
assert (actual_code in exchange.currencies), 'currency code ("' + actual_code + '") should be present in exchange.currencies' + log_text
if expected_code is not None:
assert actual_code == expected_code, 'currency code in response ("' + string_value(actual_code) + '") should be equal to expected code ("' + string_value(expected_code) + '")' + log_text
def assert_valid_currency_id_and_code(exchange, skipped_properties, method, entry, currency_id, currency_code, allow_null=True):
# this is exclusive exceptional key name to be used in `skip-tests.json`, to skip check for currency id and code
if ('currency' in skipped_properties) or ('currencyIdAndCode' in skipped_properties):
return
log_text = log_template(exchange, method, entry)
undefined_values = currency_id is None and currency_code is None
defined_values = currency_id is not None and currency_code is not None
assert undefined_values or defined_values, 'currencyId and currencyCode should be either both defined or both undefined' + log_text
assert defined_values or allow_null, 'currency code and id is not defined' + log_text
if defined_values:
# check by code
currency_by_code = exchange.currency(currency_code)
assert currency_by_code['id'] == currency_id, 'currencyId "' + string_value(currency_id) + '" does not match currency id from instance: "' + string_value(currency_by_code['id']) + '"' + log_text
# check by id
currency_by_id = exchange.safe_currency(currency_id)
assert currency_by_id['code'] == currency_code, 'currencyCode ' + string_value(currency_code) + ' does not match currency of id: ' + string_value(currency_id) + log_text
def assert_symbol(exchange, skipped_properties, method, entry, key, expected_symbol=None, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
actual_symbol = exchange.safe_string(entry, key)
if actual_symbol is not None:
assert isinstance(actual_symbol, str), 'symbol should be either undefined or a string' + log_text
if expected_symbol is not None:
assert actual_symbol == expected_symbol, 'symbol in response ("' + string_value(actual_symbol) + '") should be equal to expected symbol ("' + string_value(expected_symbol) + '")' + log_text
defined_values = actual_symbol is not None and expected_symbol is not None
assert defined_values or allow_null, 'symbols are not defined' + log_text
def assert_symbol_in_markets(exchange, skipped_properties, method, symbol):
log_text = log_template(exchange, method, {})
assert (symbol in exchange.markets), 'symbol should be present in exchange.symbols' + log_text
def assert_greater(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None:
assert Precise.string_gt(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected to be > ' + string_value(compare_to) + log_text
def assert_greater_or_equal(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None and compare_to is not None:
assert Precise.string_ge(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected to be >= ' + string_value(compare_to) + log_text
def assert_less(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None and compare_to is not None:
assert Precise.string_lt(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected to be < ' + string_value(compare_to) + log_text
def assert_less_or_equal(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None and compare_to is not None:
assert Precise.string_le(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected to be <= ' + string_value(compare_to) + log_text
def assert_equal(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None and compare_to is not None:
assert Precise.string_eq(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected to be equal to ' + string_value(compare_to) + log_text
def assert_non_equal(exchange, skipped_properties, method, entry, key, compare_to, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_string(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None:
assert not Precise.string_eq(value, compare_to), string_value(key) + ' key (with a value of ' + string_value(value) + ') was expected not to be equal to ' + string_value(compare_to) + log_text
def assert_in_array(exchange, skipped_properties, method, entry, key, expected_array, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
value = exchange.safe_value(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
# todo: remove undefined check
if value is not None:
stingified_array_value = exchange.json(expected_array) # don't use expectedArray.join (','), as it bugs in other languages, if values are bool, undefined or etc..
assert exchange.in_array(value, expected_array), '"' + string_value(key) + '" key (value "' + string_value(value) + '") is not from the expected list : [' + stingified_array_value + ']' + log_text
def assert_fee_structure(exchange, skipped_properties, method, entry, key, allow_null=True):
log_text = log_template(exchange, method, entry)
key_string = string_value(key)
if isinstance(key, int):
assert isinstance(entry, list), 'fee container is expected to be an array' + log_text
assert key < len(entry), 'fee key ' + key_string + ' was expected to be present in entry' + log_text
else:
assert isinstance(entry, dict), 'fee container is expected to be an object' + log_text
assert key in entry, 'fee key "' + key + '" was expected to be present in entry' + log_text
fee_object = exchange.safe_value(entry, key)
assert fee_object is not None or allow_null, 'fee object is null' + log_text
# todo: remove undefined check to make stricter
if fee_object is not None:
assert 'cost' in fee_object, key_string + ' fee object should contain "cost" key' + log_text
if fee_object['cost'] is None:
return # todo: remove undefined check to make stricter
assert isinstance(fee_object['cost'], numbers.Real), key_string + ' "cost" must be numeric type' + log_text
# assertGreaterOrEqual (exchange, skippedProperties, method, feeObject, 'cost', '0'); # fee might be negative in the case of a rebate or reward
assert 'currency' in fee_object, '"' + key_string + '" fee object should contain "currency" key' + log_text
assert_currency_code(exchange, skipped_properties, method, entry, fee_object['currency'])
def assert_timestamp_order(exchange, method, code_or_symbol, items, ascending=True):
for i in range(0, len(items)):
if i > 0:
current_ts = items[i - 1]['timestamp']
next_ts = items[i]['timestamp']
if current_ts is not None and next_ts is not None:
ascending_or_descending = 'ascending' if ascending else 'descending'
comparison = (current_ts <= next_ts) if ascending else (current_ts >= next_ts)
assert comparison, exchange.id + ' ' + method + ' ' + string_value(code_or_symbol) + ' must return a ' + ascending_or_descending + ' sorted array of items by timestamp, but ' + str(current_ts) + ' is opposite with its next ' + str(next_ts) + ' ' + exchange.json(items)
def assert_integer(exchange, skipped_properties, method, entry, key, allow_null=True):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
if entry is not None:
value = exchange.safe_value(entry, key)
assert value is not None or allow_null, 'value is null' + log_text
if value is not None:
is_integer = isinstance(value, int)
assert is_integer, '"' + string_value(key) + '" key (value "' + string_value(value) + '") is not an integer' + log_text
def check_precision_accuracy(exchange, skipped_properties, method, entry, key):
if key in skipped_properties:
return
if exchange.is_tick_precision():
# TICK_SIZE should be above zero
assert_greater(exchange, skipped_properties, method, entry, key, '0')
# the below array of integers are inexistent tick-sizes (theoretically technically possible, but not in real-world cases), so their existence in our case indicates to incorrectly implemented tick-sizes, which might mistakenly be implemented with DECIMAL_PLACES, so we throw error
decimal_numbers = ['2', '3', '4', '5', '6', '7', '8', '9', '11', '12', '13', '14', '15', '16']
for i in range(0, len(decimal_numbers)):
num = decimal_numbers[i]
num_str = num
assert_non_equal(exchange, skipped_properties, method, entry, key, num_str)
else:
# todo: significant-digits return doubles from `this.parseNumber`, so for now can't assert against integer atm
# assertInteger (exchange, skippedProperties, method, entry, key); # should be integer
assert_less_or_equal(exchange, skipped_properties, method, entry, key, '18') # should be under 18 decimals
assert_greater_or_equal(exchange, skipped_properties, method, entry, key, '-8') # in real-world cases, there would not be less than that
def fetch_best_bid_ask(exchange, method, symbol):
log_text = log_template(exchange, method, {})
# find out best bid/ask price
best_bid = None
best_ask = None
used_method = None
if exchange.has['fetchOrderBook']:
used_method = 'fetchOrderBook'
orderbook = exchange.fetch_order_book(symbol)
bids = exchange.safe_list(orderbook, 'bids')
asks = exchange.safe_list(orderbook, 'asks')
best_bid_array = exchange.safe_list(bids, 0)
best_ask_array = exchange.safe_list(asks, 0)
best_bid = exchange.safe_number(best_bid_array, 0)
best_ask = exchange.safe_number(best_ask_array, 0)
elif exchange.has['fetchBidsAsks']:
used_method = 'fetchBidsAsks'
tickers = exchange.fetch_bids_asks([symbol])
ticker = exchange.safe_dict(tickers, symbol)
best_bid = exchange.safe_number(ticker, 'bid')
best_ask = exchange.safe_number(ticker, 'ask')
elif exchange.has['fetchTicker']:
used_method = 'fetchTicker'
ticker = exchange.fetch_ticker(symbol)
best_bid = exchange.safe_number(ticker, 'bid')
best_ask = exchange.safe_number(ticker, 'ask')
elif exchange.has['fetchTickers']:
used_method = 'fetchTickers'
tickers = exchange.fetch_tickers([symbol])
ticker = exchange.safe_dict(tickers, symbol)
best_bid = exchange.safe_number(ticker, 'bid')
best_ask = exchange.safe_number(ticker, 'ask')
#
assert best_bid is not None and best_ask is not None, log_text + ' ' + exchange.id + ' could not get best bid/ask for ' + symbol + ' using ' + used_method + ' while testing ' + method
return [best_bid, best_ask]
def fetch_order(exchange, symbol, order_id, skipped_properties):
fetched_order = None
original_id = order_id
# set 'since' to 5 minute ago for optimal results
since_time = exchange.milliseconds() - 1000 * 60 * 5
# iterate
methods_singular = ['fetchOrder', 'fetchOpenOrder', 'fetchClosedOrder', 'fetchCanceledOrder']
for i in range(0, len(methods_singular)):
singular_fetch_name = methods_singular[i]
if exchange.has[singular_fetch_name]:
current_order = exchange[singular_fetch_name](original_id, symbol)
# if there is an id inside the order, it means the order was fetched successfully
if current_order['id'] == original_id:
fetched_order = current_order
break
#
# search through plural methods
if fetched_order is None:
methods_plural = ['fetchOrders', 'fetchOpenOrders', 'fetchClosedOrders', 'fetchCanceledOrders']
for i in range(0, len(methods_plural)):
plural_fetch_name = methods_plural[i]
if exchange.has[plural_fetch_name]:
orders = exchange[plural_fetch_name](symbol, since_time)
found = False
for j in range(0, len(orders)):
current_order = orders[j]
if current_order['id'] == original_id:
fetched_order = current_order
found = True
break
if found:
break
return fetched_order
def assert_order_state(exchange, skipped_properties, method, order, asserted_status, strict_check):
# note, `strictCheck` is `true` only from "fetchOrder" cases
log_text = log_template(exchange, method, order)
msg = 'order should be ' + asserted_status + ', but it was not asserted' + log_text
filled = exchange.safe_string(order, 'filled')
amount = exchange.safe_string(order, 'amount')
# shorthand variables
status_undefined = (order['status'] is None)
status_open = (order['status'] == 'open')
status_closed = (order['status'] == 'closed')
status_clanceled = (order['status'] == 'canceled')
filled_defined = (filled is not None)
amount_defined = (amount is not None)
condition = None
#
# ### OPEN STATUS
#
# if strict check, then 'status' must be 'open' and filled amount should be less then whole order amount
strict_open = status_open and (filled_defined and amount_defined and filled < amount)
# if non-strict check, then accept & ignore undefined values
nonstrict_open = (status_open or status_undefined) and ((not filled_defined or not amount_defined) or Precise.string_lt(filled, amount))
# check
if asserted_status == 'open':
condition = strict_open if strict_check else nonstrict_open
assert condition, msg
return
#
# ### CLOSED STATUS
#
# if strict check, then 'status' must be 'closed' and filled amount should be equal to the whole order amount
closed_strict = status_closed and (filled_defined and amount_defined and Precise.string_eq(filled, amount))
# if non-strict check, then accept & ignore undefined values
closed_non_strict = (status_closed or status_undefined) and ((not filled_defined or not amount_defined) or Precise.string_eq(filled, amount))
# check
if asserted_status == 'closed':
condition = closed_strict if strict_check else closed_non_strict
assert condition, msg
return
#
# ### CANCELED STATUS
#
# if strict check, then 'status' must be 'canceled' and filled amount should be less then whole order amount
canceled_strict = status_clanceled and (filled_defined and amount_defined and Precise.string_lt(filled, amount))
# if non-strict check, then accept & ignore undefined values
canceled_non_strict = (status_clanceled or status_undefined) and ((not filled_defined or not amount_defined) or Precise.string_lt(filled, amount))
# check
if asserted_status == 'canceled':
condition = canceled_strict if strict_check else canceled_non_strict
assert condition, msg
return
#
# ### CLOSED_or_CANCELED STATUS
#
if asserted_status == 'closed_or_canceled':
condition = (closed_strict or canceled_strict) if strict_check else (closed_non_strict or canceled_non_strict)
assert condition, msg
return
def get_active_markets(exchange, include_unknown=True):
filtered_active = exchange.filter_by(exchange.markets, 'active', True)
if include_unknown:
filtered_undefined = exchange.filter_by(exchange.markets, 'active', None)
return exchange.array_concat(filtered_active, filtered_undefined)
return filtered_active
def remove_proxy_options(exchange, skipped_properties):
proxy_url = exchange.check_proxy_url_settings()
[http_proxy, https_proxy, socks_proxy] = exchange.check_proxy_settings()
# because of bug in transpiled, about `.proxyUrl` being transpiled into `.proxy_url`, we have to use this workaround
exchange.set_property(exchange, 'proxyUrl', None)
exchange.set_property(exchange, 'proxy_url', None)
exchange.set_property(exchange, 'httpProxy', None)
exchange.set_property(exchange, 'http_proxy', None)
exchange.set_property(exchange, 'httpsProxy', None)
exchange.set_property(exchange, 'https_proxy', None)
exchange.set_property(exchange, 'socksProxy', None)
exchange.set_property(exchange, 'socks_proxy', None)
return [proxy_url, http_proxy, https_proxy, socks_proxy]
def set_proxy_options(exchange, skipped_properties, proxy_url, http_proxy, https_proxy, socks_proxy):
exchange.proxy_url = proxy_url
exchange.http_proxy = http_proxy
exchange.https_proxy = https_proxy
exchange.socks_proxy = socks_proxy
def concat(a=None, b=None):
# we use this method temporarily, because of ast-transpiler issue across langs
if a is None:
return b
elif b is None:
return a
else:
result = []
for i in range(0, len(a)):
result.append(a[i])
for j in range(0, len(b)):
result.append(b[j])
return result
def assert_non_emtpy_array(exchange, skipped_properties, method, entry, hint=None):
log_text = log_template(exchange, method, entry)
if hint is not None:
log_text = log_text + ' ' + hint
assert isinstance(entry, list), 'response is expected to be an array' + log_text
if not ('emptyResponse' in skipped_properties):
return
assert len(entry) > 0, 'response is expected to be a non-empty array' + log_text + ' (add "emptyResponse" in skip-tests.json to skip this check)'
def assert_round_minute_timestamp(exchange, skipped_properties, method, entry, key):
if key in skipped_properties:
return
log_text = log_template(exchange, method, entry)
ts = exchange.safe_string(entry, key)
assert Precise.string_mod(ts, '60000') == '0', 'timestamp should be a multiple of 60 seconds (1 minute)' + log_text
def deep_equal(a, b):
return json.dumps(a) == json.dumps(b)
def assert_deep_equal(exchange, skipped_properties, method, a, b):
log_text = log_template(exchange, method, {})
assert deep_equal(a, b), 'two dicts do not match: ' + json.dumps(a) + ' != ' + json.dumps(b) + log_text

View File

@@ -0,0 +1,18 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
def test_status(exchange, skipped_properties, method, entry, now):
assert True, 'testStatus'

View File

@@ -0,0 +1,177 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.base.precise import Precise # noqa E402
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_ticker(exchange, skipped_properties, method, entry, symbol):
format = {
'info': {},
'symbol': 'ETH/BTC',
'timestamp': 1502962946216,
'datetime': '2017-09-01T00:00:00',
'high': exchange.parse_number('1.234'),
'low': exchange.parse_number('1.234'),
'bid': exchange.parse_number('1.234'),
'bidVolume': exchange.parse_number('1.234'),
'ask': exchange.parse_number('1.234'),
'askVolume': exchange.parse_number('1.234'),
'vwap': exchange.parse_number('1.234'),
'open': exchange.parse_number('1.234'),
'close': exchange.parse_number('1.234'),
'last': exchange.parse_number('1.234'),
'previousClose': exchange.parse_number('1.234'),
'change': exchange.parse_number('1.234'),
'percentage': exchange.parse_number('1.234'),
'average': exchange.parse_number('1.234'),
'baseVolume': exchange.parse_number('1.234'),
'quoteVolume': exchange.parse_number('1.234'),
}
# todo: atm, many exchanges fail, so temporarily decrease stict mode
empty_allowed_for = ['timestamp', 'datetime', 'open', 'high', 'low', 'close', 'last', 'baseVolume', 'quoteVolume', 'previousClose', 'bidVolume', 'askVolume', 'vwap', 'change', 'percentage', 'average']
# trick csharp-transpiler for string
if not ('BidsAsks' in str(method)):
empty_allowed_for.append('bid')
empty_allowed_for.append('ask')
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry)
log_text = test_shared_methods.log_template(exchange, method, entry)
# check market
market = None
symbol_for_market = symbol if (symbol is not None) else exchange.safe_string(entry, 'symbol')
if symbol_for_market is not None and (symbol_for_market in exchange.markets):
market = exchange.market(symbol_for_market)
# only check "above zero" values if exchange is not supposed to have exotic index markets
is_standard_market = (market is not None and exchange.in_array(market['type'], ['spot', 'swap', 'future', 'option']))
values_should_be_positive = is_standard_market # || (market === undefined) atm, no check for index markets
if values_should_be_positive and not ('positiveValues' in skipped_properties):
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'open', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'high', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'low', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'close', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'ask', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'bid', '0')
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'average', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'vwap', '0')
# volume can not be negative
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'askVolume', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'bidVolume', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'baseVolume', '0')
test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, entry, 'quoteVolume', '0')
#
# close price
#
last_string = exchange.safe_string(entry, 'last')
close_string = exchange.safe_string(entry, 'close')
assert ((close_string is None) and (last_string is None)) or Precise.string_eq(last_string, close_string), '`last` != `close`' + log_text
open_price = exchange.safe_string(entry, 'open')
#
# base & quote volumes
#
base_volume = exchange.omit_zero(exchange.safe_string(entry, 'baseVolume'))
quote_volume = exchange.omit_zero(exchange.safe_string(entry, 'quoteVolume'))
high = exchange.omit_zero(exchange.safe_string(entry, 'high'))
low = exchange.omit_zero(exchange.safe_string(entry, 'low'))
open = exchange.omit_zero(exchange.safe_string(entry, 'open'))
close = exchange.omit_zero(exchange.safe_string(entry, 'close'))
if not ('compareQuoteVolumeBaseVolume' in skipped_properties):
# assert (baseVolumeDefined === quoteVolumeDefined, 'baseVolume or quoteVolume should be either both defined or both undefined' + logText); # No, exchanges might not report both values
if (base_volume is not None) and (quote_volume is not None) and (high is not None) and (low is not None):
base_low = Precise.string_mul(base_volume, low)
base_high = Precise.string_mul(base_volume, high)
# to avoid abnormal long precision issues (like https://discord.com/channels/690203284119617602/1338828283902689280/1338846071278927912 )
m_precision = exchange.safe_dict(market, 'precision')
amount_precision = exchange.safe_string(m_precision, 'amount')
tolerance = '1.0001'
if amount_precision is not None:
base_low = Precise.string_mul(Precise.string_sub(base_volume, amount_precision), low)
base_high = Precise.string_mul(Precise.string_add(base_volume, amount_precision), high)
else:
# if nothing found, as an exclusion, just add 0.001%
base_low = Precise.string_mul(Precise.string_div(base_volume, tolerance), low)
base_high = Precise.string_mul(Precise.string_mul(base_volume, tolerance), high)
# because of exchange engines might not rounding numbers propertly, we add some tolerance of calculated 24hr high/low
base_low = Precise.string_div(base_low, tolerance)
base_high = Precise.string_mul(base_high, tolerance)
assert Precise.string_ge(quote_volume, base_low), 'quoteVolume should be => baseVolume * low' + log_text
assert Precise.string_le(quote_volume, base_high), 'quoteVolume should be <= baseVolume * high' + log_text
# open and close should be between High & Low
if high is not None and low is not None and not ('compareOHLC' in skipped_properties):
if open is not None:
assert Precise.string_ge(open, low), 'open should be >= low' + log_text
assert Precise.string_le(open, high), 'open should be <= high' + log_text
if close is not None:
assert Precise.string_ge(close, low), 'close should be >= low' + log_text
assert Precise.string_le(close, high), 'close should be <= high' + log_text
#
# vwap
#
vwap = exchange.safe_string(entry, 'vwap')
if vwap is not None:
# todo
# assert (high !== undefined, 'vwap is defined, but high is not' + logText);
# assert (low !== undefined, 'vwap is defined, but low is not' + logText);
# assert (vwap >= low && vwap <= high)
# todo: calc compare
assert not values_should_be_positive or Precise.string_ge(vwap, '0'), 'vwap is not greater than zero' + log_text
if base_volume is not None:
assert quote_volume is not None, 'baseVolume & vwap is defined, but quoteVolume is not' + log_text
if quote_volume is not None:
assert base_volume is not None, 'quoteVolume & vwap is defined, but baseVolume is not' + log_text
ask_string = exchange.safe_string(entry, 'ask')
bid_string = exchange.safe_string(entry, 'bid')
if (ask_string is not None) and (bid_string is not None) and not ('spread' in skipped_properties):
test_shared_methods.assert_greater(exchange, skipped_properties, method, entry, 'ask', exchange.safe_string(entry, 'bid'))
percentage = exchange.safe_string(entry, 'percentage')
change = exchange.safe_string(entry, 'change')
if not ('maxIncrease' in skipped_properties):
#
# percentage
#
max_increase = '100' # for testing purposes, if "increased" value is more than 100x, tests should break as implementation might be wrong. however, if something rarest event happens and some coin really had that huge increase, the tests will shortly recover in few hours, as new 24-hour cycle would stabilize tests)
if percentage is not None:
# - should be above -100 and below MAX
assert Precise.string_ge(percentage, '-100'), 'percentage should be above -100% ' + log_text
assert Precise.string_le(percentage, Precise.string_mul('+100', max_increase)), 'percentage should be below ' + max_increase + '00% ' + log_text
#
# change
#
approx_value = exchange.safe_string_n(entry, ['open', 'close', 'average', 'bid', 'ask', 'vwap', 'previousClose'])
if change is not None:
# - should be between -price & +price*100
assert Precise.string_ge(change, Precise.string_neg(approx_value)), 'change should be above -price ' + log_text
assert Precise.string_le(change, Precise.string_mul(approx_value, max_increase)), 'change should be below ' + max_increase + 'x price ' + log_text
#
# ensure all expected values are defined
#
if last_string is not None:
if percentage is not None:
# if one knows 'last' and 'percentage' values, then 'change', 'open' and 'average' values should be determinable.
assert open_price is not None and change is not None, 'open & change should be defined if last & percentage are defined' + log_text # todo : add average price too
elif change is not None:
# if one knows 'last' and 'change' values, then 'percentage', 'open' and 'average' values should be determinable.
assert open_price is not None and percentage is not None, 'open & percentage should be defined if last & change are defined' + log_text # todo : add average price too
elif open_price is not None:
if percentage is not None:
# if one knows 'open' and 'percentage' values, then 'last', 'change' and 'average' values should be determinable.
assert last_string is not None and change is not None, 'last & change should be defined if open & percentage are defined' + log_text # todo : add average price too
elif change is not None:
# if one knows 'open' and 'change' values, then 'last', 'percentage' and 'average' values should be determinable.
assert last_string is not None and percentage is not None, 'last & percentage should be defined if open & change are defined' + log_text # todo : add average price too
#
# todo: rethink about this
# else {
# assert ((askString === undefined) && (bidString === undefined), 'ask & bid should be both defined or both undefined' + logText);
# }
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)

View File

@@ -0,0 +1,47 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_trade(exchange, skipped_properties, method, entry, symbol, now):
format = {
'info': {},
'id': '12345-67890:09876/54321',
'timestamp': 1502962946216,
'datetime': '2017-08-17 12:42:48.000',
'symbol': 'ETH/BTC',
'order': '12345-67890:09876/54321',
'side': 'buy',
'takerOrMaker': 'taker',
'price': exchange.parse_number('0.06917684'),
'amount': exchange.parse_number('1.5'),
'cost': exchange.parse_number('0.10376526'),
'fees': [],
'fee': {},
}
# todo: add takeOrMaker as mandatory (atm, many exchanges fail)
# removed side because some public endpoints return trades without side
empty_allowed_for = ['fees', 'fee', 'symbol', 'order', 'id', 'takerOrMaker']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, entry, now)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)
#
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'side', ['buy', 'sell'])
test_shared_methods.assert_in_array(exchange, skipped_properties, method, entry, 'takerOrMaker', ['taker', 'maker'])
test_shared_methods.assert_fee_structure(exchange, skipped_properties, method, entry, 'fee')
if not ('fees' in skipped_properties):
# todo: remove undefined check and probably non-empty array check later
if entry['fees'] is not None:
for i in range(0, len(entry['fees'])):
test_shared_methods.assert_fee_structure(exchange, skipped_properties, method, entry['fees'], i)

View File

@@ -0,0 +1,26 @@
import os
import sys
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)
# ----------------------------------------------------------------------------
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
# ----------------------------------------------------------------------------
# -*- coding: utf-8 -*-
from ccxt.test.exchange.base import test_shared_methods # noqa E402
def test_trading_fee(exchange, skipped_properties, method, symbol, entry):
format = {
'info': {},
'symbol': 'ETH/BTC',
'maker': exchange.parse_number('0.002'),
'taker': exchange.parse_number('0.003'),
}
empty_allowed_for = ['tierBased', 'percentage', 'symbol']
test_shared_methods.assert_structure(exchange, skipped_properties, method, entry, format, empty_allowed_for)
test_shared_methods.assert_symbol(exchange, skipped_properties, method, entry, 'symbol', symbol)