583 lines
17 KiB
Python
583 lines
17 KiB
Python
import os
|
|
|
|
import arrow
|
|
import numpy as np
|
|
import pytest
|
|
|
|
import jesse.helpers as jh
|
|
|
|
|
|
def test_app_currency():
|
|
from jesse.routes import router
|
|
from jesse.enums import exchanges, timeframes
|
|
router.initiate(
|
|
[{'exchange': exchanges.BITFINEX, 'symbol': 'ETH-USD', 'timeframe': timeframes.HOUR_3, 'strategy': 'Test19'}])
|
|
assert jh.app_currency() == 'USD'
|
|
|
|
|
|
def test_app_mode():
|
|
assert jh.app_mode() == 'backtest'
|
|
|
|
|
|
def test_arrow_to_timestamp():
|
|
arrow_time = arrow.get('2015-08-01')
|
|
assert jh.arrow_to_timestamp(arrow_time) == 1438387200000
|
|
|
|
|
|
def test_base_asset():
|
|
assert jh.base_asset('BTC-USDT') == 'BTC'
|
|
assert jh.base_asset('BTC-USD') == 'BTC'
|
|
assert jh.base_asset('DEFI-USDT') == 'DEFI'
|
|
assert jh.base_asset('DEFI-USD') == 'DEFI'
|
|
|
|
|
|
def test_binary_search():
|
|
arr = [0, 11, 22, 33, 44, 54, 55]
|
|
|
|
assert jh.binary_search(arr, 22) == 2
|
|
assert jh.binary_search(arr, 222) == -1
|
|
|
|
|
|
def test_clean_orderbook_list():
|
|
assert np.array_equal(
|
|
jh.clean_orderbook_list([
|
|
['10', '11'],
|
|
['11', '14'],
|
|
['12', '13'],
|
|
['13', '133'],
|
|
['14', '12'],
|
|
]),
|
|
[
|
|
[10, 11],
|
|
[11, 14],
|
|
[12, 13],
|
|
[13, 133],
|
|
[14, 12],
|
|
]
|
|
)
|
|
|
|
|
|
def test_color():
|
|
msg_text = 'msg'
|
|
msg_color = 'black'
|
|
assert jh.color(msg_text, msg_color) == '\x1b[30mmsg\x1b[0m'
|
|
|
|
|
|
def test_convert_number():
|
|
old_max = 119
|
|
old_min = 40
|
|
new_max = 4.0
|
|
new_min = 0.5
|
|
old_value = 41
|
|
|
|
assert jh.convert_number(old_max, old_min, new_max, new_min, old_value) == 0.5443037974683544
|
|
|
|
|
|
def test_dashless_symbol():
|
|
assert jh.dashless_symbol('BTC-USD') == 'BTCUSD'
|
|
assert jh.dashless_symbol('BTC-USDT') == 'BTCUSDT'
|
|
assert jh.dashless_symbol('1INCH-USDT') == '1INCHUSDT'
|
|
assert jh.dashless_symbol('SC-USDT') == 'SCUSDT'
|
|
|
|
# make sure that it works even if it's already dashless
|
|
assert jh.dashless_symbol('BTCUSDT') == 'BTCUSDT'
|
|
|
|
|
|
def test_dashy_symbol():
|
|
assert jh.dashy_symbol('BTCUSD') == 'BTC-USD'
|
|
assert jh.dashy_symbol('BTCUSDT') == 'BTC-USDT'
|
|
assert jh.dashy_symbol('BTC-USDT') == 'BTC-USDT'
|
|
|
|
|
|
def test_date_diff_in_days():
|
|
date_1 = arrow.get('2015-12-23 18:40:48', 'YYYY-MM-DD HH:mm:ss')
|
|
date_2 = arrow.get('2017-11-15 13:18:20', 'YYYY-MM-DD HH:mm:ss')
|
|
diff = jh.date_diff_in_days(date_1, date_2)
|
|
assert diff == 692
|
|
|
|
|
|
def test_date_to_timestamp():
|
|
assert jh.date_to_timestamp('2015-08-01') == 1438387200000
|
|
|
|
|
|
def test_dna_to_hp():
|
|
strategy_hp = [
|
|
{'name': 'hp1', 'type': float, 'min': 0.01, 'max': 1.0, 'default': 0.09},
|
|
{'name': 'hp2', 'type': int, 'min': 1, 'max': 10, 'default': 2},
|
|
]
|
|
dna = ".6"
|
|
assert jh.dna_to_hp(strategy_hp, dna) == {'hp1': 0.08518987341772151, 'hp2': 3}
|
|
|
|
|
|
def test_dump_exception():
|
|
# uses database, which is not existing during testing
|
|
pass
|
|
|
|
|
|
def test_estimate_average_price():
|
|
assert jh.estimate_average_price(100, 7200, 0, 0) == 7200
|
|
|
|
with pytest.raises(TypeError):
|
|
jh.estimate_average_price(100, 7200, 0, None)
|
|
jh.estimate_average_price(100, 7200, None, 0)
|
|
jh.estimate_average_price(100, None, 0, 0)
|
|
jh.estimate_average_price(None, 7200, 0, 0)
|
|
|
|
|
|
def test_estimate_PNL():
|
|
# profit
|
|
assert jh.estimate_PNL(2, 50, 60, 'long') == 20
|
|
assert jh.estimate_PNL(2, 60, 50, 'short') == 20
|
|
|
|
# loss
|
|
assert jh.estimate_PNL(2, 50, 60, 'short') == -20
|
|
assert jh.estimate_PNL(2, 60, 50, 'long') == -20
|
|
|
|
# profit with fee
|
|
assert jh.estimate_PNL(1, 10, 20, 'long', 0.002) == 9.94
|
|
# loss with fee
|
|
assert jh.estimate_PNL(1, 10, 20, 'short', 0.002) == -10.06
|
|
|
|
with pytest.raises(TypeError):
|
|
jh.estimate_PNL(1, 200, 220, 1)
|
|
jh.estimate_PNL(1, 200, 'invalid_input', 'short')
|
|
jh.estimate_PNL(1, 'invalid_input', 220, 'short')
|
|
jh.estimate_PNL('invalid_input', 200, 220, 'short')
|
|
|
|
|
|
def test_estimate_PNL_percentage():
|
|
# profit
|
|
assert jh.estimate_PNL_percentage(1, 200, 220, 'long') == 10
|
|
assert jh.estimate_PNL_percentage(1, 200, 180, 'short') == 10
|
|
|
|
# loss
|
|
assert jh.estimate_PNL_percentage(1, 200, 180, 'long') == -10
|
|
assert jh.estimate_PNL_percentage(1, 200, 220, 'short') == -10
|
|
|
|
with pytest.raises(TypeError):
|
|
jh.estimate_PNL_percentage(1, 200, 220, 1)
|
|
jh.estimate_PNL_percentage(1, 200, 'invalid_input', 'short')
|
|
jh.estimate_PNL_percentage(1, 'invalid_input', 220, 'short')
|
|
jh.estimate_PNL_percentage('invalid_input', 200, 220, 'short')
|
|
|
|
|
|
def test_file_exists():
|
|
assert jh.file_exists('tests/test_helpers.py') is True
|
|
|
|
|
|
def test_floor_with_precision():
|
|
assert jh.floor_with_precision(1.123) == 1
|
|
assert jh.floor_with_precision(1.123, 1) == 1.1
|
|
assert jh.floor_with_precision(1.123, 2) == 1.12
|
|
assert jh.floor_with_precision(1.123, 3) == 1.123
|
|
assert jh.floor_with_precision(1.123, 4) == 1.123
|
|
|
|
|
|
def test_format_currency():
|
|
assert jh.format_currency(100_000_000) == '100,000,000'
|
|
assert jh.format_currency(100_000_000.23) == '100,000,000.23'
|
|
|
|
|
|
def test_generate_unique_id():
|
|
assert jh.is_valid_uuid(jh.generate_unique_id()) is True
|
|
assert jh.is_valid_uuid('asdfasdfasdfasfsadfsd') is False
|
|
|
|
|
|
def test_get_candle_source():
|
|
candle = np.array(([1575547200000, 146.51, 147.03, 149.02, 146.51, 64788.46651],
|
|
[1553817660000, 4092.56783507, 4092.5, 4092.56783507, 4092.5, 9.0847059]))
|
|
close = jh.get_candle_source(candle, source_type="close")
|
|
assert close[-1] == 4092.5
|
|
high = jh.get_candle_source(candle, source_type="high")
|
|
assert high[-1] == 4092.56783507
|
|
low = jh.get_candle_source(candle, source_type="low")
|
|
assert low[-1] == 4092.5
|
|
open = jh.get_candle_source(candle, source_type="open")
|
|
assert open[-1] == 4092.56783507
|
|
volume = jh.get_candle_source(candle, source_type="volume")
|
|
assert volume[-1] == 9.0847059
|
|
hl2 = jh.get_candle_source(candle, source_type="hl2")
|
|
assert hl2[-1] == 4092.533917535
|
|
hlc3 = jh.get_candle_source(candle, source_type="hlc3")
|
|
assert hlc3[-1] == 4092.52261169
|
|
ohlc4 = jh.get_candle_source(candle, source_type="ohlc4")
|
|
assert ohlc4[-1] == 4092.533917535
|
|
|
|
|
|
def test_get_config(monkeypatch):
|
|
# assert when config does NOT exist (must return passed default)
|
|
assert jh.get_config('aaaaaaa', 2020) == 2020
|
|
# assert when config does exist
|
|
assert jh.get_config('env.logging.order_submission', 2020) is True
|
|
# assert env is took
|
|
monkeypatch.setenv("ENV_DATABASES_POSTGRES_HOST", "db")
|
|
assert jh.get_config('env.databases.postgres_host', 'default') == 'db'
|
|
monkeypatch.delenv("ENV_DATABASES_POSTGRES_HOST")
|
|
# assert env is took with space
|
|
monkeypatch.setenv("ENV_EXCHANGES_BINANCE_FUTURES_SETTLEMENT_CURRENCY", 'BUSD')
|
|
assert jh.get_config('env.exchanges.Binance Futures.settlement_currency', 'USDT') == 'BUSD'
|
|
monkeypatch.delenv("ENV_EXCHANGES_BINANCE_FUTURES_SETTLEMENT_CURRENCY")
|
|
|
|
|
|
def test_get_strategy_class():
|
|
from jesse.strategies import Strategy
|
|
assert issubclass(jh.get_strategy_class("Test01"), Strategy)
|
|
|
|
|
|
def test_insecure_hash():
|
|
assert jh.insecure_hash("test") == "098f6bcd4621d373cade4e832627b4f6"
|
|
|
|
|
|
def test_insert_list():
|
|
my_list = [0, 1, 2, 3]
|
|
|
|
assert jh.insert_list(2, 22, my_list) == [0, 1, 22, 2, 3]
|
|
assert jh.insert_list(0, 22, my_list) == [22, 0, 1, 2, 3]
|
|
assert jh.insert_list(-1, 22, my_list) == [0, 1, 2, 3, 22]
|
|
|
|
# assert list is untouched
|
|
assert my_list == [0, 1, 2, 3]
|
|
|
|
|
|
def test_is_backtesting():
|
|
assert jh.is_backtesting() is True
|
|
|
|
|
|
def test_is_collecting_data():
|
|
assert jh.is_collecting_data() is False
|
|
|
|
|
|
def test_is_debuggable():
|
|
debug_item = 'order_submission'
|
|
assert jh.is_debuggable(debug_item) is False
|
|
|
|
|
|
def test_is_debugging():
|
|
assert jh.is_debugging() is False
|
|
|
|
|
|
def test_is_importing_candles():
|
|
assert jh.is_importing_candles() is False
|
|
|
|
|
|
def test_is_live():
|
|
assert jh.is_live() is False
|
|
|
|
|
|
def test_is_livetrading():
|
|
assert jh.is_livetrading() is False
|
|
|
|
|
|
def test_is_optimizing():
|
|
assert jh.is_optimizing() is False
|
|
|
|
|
|
def test_is_paper_trading():
|
|
assert jh.is_paper_trading() is False
|
|
|
|
|
|
def test_is_test_driving():
|
|
assert jh.is_test_driving() is False
|
|
|
|
|
|
def test_is_unit_testing():
|
|
assert jh.is_unit_testing() is True
|
|
|
|
|
|
def test_key():
|
|
exchange = "Exchange"
|
|
symbol = "BTC-USD"
|
|
timeframe = "6h"
|
|
assert jh.key(exchange, symbol) == "Exchange-BTC-USD"
|
|
assert jh.key(exchange, symbol, timeframe) == "Exchange-BTC-USD-6h"
|
|
|
|
|
|
def test_max_timeframe():
|
|
assert jh.max_timeframe(['1m', '3m']) == '3m'
|
|
assert jh.max_timeframe(['3m', '5m']) == '5m'
|
|
assert jh.max_timeframe(['15m', '5m']) == '15m'
|
|
assert jh.max_timeframe(['30m', '15m']) == '30m'
|
|
assert jh.max_timeframe(['30m', '1h']) == '1h'
|
|
assert jh.max_timeframe(['1h', '2h']) == '2h'
|
|
assert jh.max_timeframe(['2h', '3h']) == '3h'
|
|
assert jh.max_timeframe(['4h', '3h']) == '4h'
|
|
assert jh.max_timeframe(['6h', '4h']) == '6h'
|
|
assert jh.max_timeframe(['8h', '4h']) == '8h'
|
|
assert jh.max_timeframe(['6h', '1D']) == '1D'
|
|
|
|
|
|
def test_normalize():
|
|
assert jh.normalize(10, 0, 20) == 0.5
|
|
assert jh.normalize(20, 0, 20) == 1
|
|
assert jh.normalize(0, 0, 20) == 0
|
|
|
|
|
|
def test_now_to_timestamp():
|
|
from jesse.store import store
|
|
assert jh.now_to_timestamp() == store.app.time
|
|
|
|
|
|
def test_np_ffill():
|
|
arr = np.array([0, 1, np.nan, np.nan])
|
|
res = jh.np_ffill(arr)
|
|
expected = np.array([0, 1, 1, 1])
|
|
|
|
np.equal(res, expected)
|
|
|
|
|
|
def test_np_shift():
|
|
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
res = jh.np_shift(arr, -3)
|
|
expected = np.array([4, 5, 6, 7, 8, 9, 0, 0, 0])
|
|
|
|
np.equal(res, expected)
|
|
|
|
|
|
def test_opposite_side():
|
|
assert jh.opposite_side('buy') == 'sell'
|
|
assert jh.opposite_side('sell') == 'buy'
|
|
|
|
|
|
def test_opposite_type():
|
|
assert jh.opposite_type('long') == 'short'
|
|
assert jh.opposite_type('short') == 'long'
|
|
|
|
|
|
def test_orderbook_insertion_index_search():
|
|
ascending_arr = [
|
|
[10, 232],
|
|
[11, 33232],
|
|
[12, 233],
|
|
[33, 21323],
|
|
[44, 23123],
|
|
[55, 2321],
|
|
[66, 23213]
|
|
]
|
|
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [7, 2]) == (False, 0)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [2, 2]) == (False, 0)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [32, 2]) == (False, 3)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [34, 2]) == (False, 4)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [1, 2]) == (False, 0)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [66, 2]) == (True, 6)
|
|
assert jh.orderbook_insertion_index_search(ascending_arr, [77, 2]) == (False, 7)
|
|
|
|
descending_arr = [
|
|
[66, 232],
|
|
[55, 33232],
|
|
[44, 233],
|
|
[33, 21323],
|
|
[2, 23123],
|
|
]
|
|
|
|
assert jh.orderbook_insertion_index_search(descending_arr, [77, 2], ascending=False) == (False, 0)
|
|
assert jh.orderbook_insertion_index_search(descending_arr, [2, 2], ascending=False) == (True, 4)
|
|
assert jh.orderbook_insertion_index_search(descending_arr, [65, 2], ascending=False) == (False, 1)
|
|
assert jh.orderbook_insertion_index_search(descending_arr, [1, 2], ascending=False) == (False, 5)
|
|
|
|
|
|
def test_orderbook_trim_price():
|
|
# bids
|
|
assert jh.orderbook_trim_price(101.12, False, .1) == 101.1
|
|
assert jh.orderbook_trim_price(101.1, False, .1) == 101.1
|
|
|
|
assert jh.orderbook_trim_price(10.12, False, .01) == 10.12
|
|
assert jh.orderbook_trim_price(10.1, False, .01) == 10.1
|
|
assert jh.orderbook_trim_price(10.122, False, .01) == 10.12
|
|
assert jh.orderbook_trim_price(1.1223, False, .001) == 1.122
|
|
|
|
# asks
|
|
assert jh.orderbook_trim_price(101.12, True, .1) == 101.2
|
|
assert jh.orderbook_trim_price(101.1, True, .1) == 101.1
|
|
assert jh.orderbook_trim_price(10.12, True, .01) == 10.12
|
|
assert jh.orderbook_trim_price(10.122, True, .01) == 10.13
|
|
assert jh.orderbook_trim_price(1.1223, True, .001) == 1.123
|
|
|
|
|
|
def test_prepare_qty():
|
|
assert jh.prepare_qty(10, 'sell') == -10
|
|
assert jh.prepare_qty(-10, 'buy') == 10
|
|
|
|
with pytest.raises(ValueError):
|
|
jh.prepare_qty(-10, 'invalid_input')
|
|
|
|
|
|
def test_python_version():
|
|
import sys
|
|
assert jh.python_version() == sys.version_info[:2]
|
|
|
|
|
|
def test_quote_asset():
|
|
assert jh.quote_asset('BTC-USDT') == 'USDT'
|
|
assert jh.quote_asset('DEFI-USDT') == 'USDT'
|
|
assert jh.quote_asset('DEFI-EUR') == 'EUR'
|
|
|
|
|
|
def test_random_str():
|
|
assert len(jh.random_str(10)) == 10
|
|
|
|
|
|
def test_readable_duration():
|
|
assert jh.readable_duration(604312) == "6 days, 23 hours"
|
|
|
|
|
|
def test_relative_to_absolute():
|
|
from pathlib import Path
|
|
assert jh.relative_to_absolute("tests/test_helpers.py") == str(Path(__file__).absolute())
|
|
|
|
|
|
def test_round_price_for_live_mode():
|
|
np.testing.assert_equal(
|
|
jh.round_price_for_live_mode(np.array([0.0003209123456, 0.0004209123456]), 7),
|
|
np.array([0.0003209, 0.0004209])
|
|
)
|
|
|
|
|
|
def test_round_qty_for_live_mode():
|
|
np.testing.assert_equal(
|
|
jh.round_qty_for_live_mode(np.array([100.3209123456, 100.4299123456]), 2),
|
|
np.array([100.32, 100.42])
|
|
)
|
|
|
|
np.testing.assert_equal(
|
|
jh.round_qty_for_live_mode(np.array([0]), 1),
|
|
np.array([0.1])
|
|
)
|
|
|
|
np.testing.assert_equal(
|
|
jh.round_qty_for_live_mode(np.array([0]), 2),
|
|
np.array([0.01])
|
|
)
|
|
|
|
np.testing.assert_equal(
|
|
jh.round_qty_for_live_mode(np.array([0]), 3),
|
|
np.array([0.001])
|
|
)
|
|
|
|
|
|
def test_round_decimals_down():
|
|
assert jh.round_decimals_down(100.329, 2) == 100.32
|
|
|
|
|
|
def test_secure_hash():
|
|
assert jh.secure_hash('test') == "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
|
|
|
|
|
def test_should_execute_silently():
|
|
assert jh.should_execute_silently() is True
|
|
|
|
|
|
def test_side_to_type():
|
|
assert jh.side_to_type("buy") == "long"
|
|
assert jh.side_to_type("sell") == "short"
|
|
|
|
# make sure title case works as well
|
|
assert jh.side_to_type("Buy") == "long"
|
|
assert jh.side_to_type("Sell") == "short"
|
|
|
|
|
|
def test_string_after_character():
|
|
assert jh.string_after_character('btcusdt@bookTicker', '@') == 'bookTicker'
|
|
assert jh.string_after_character('9000|24628', '|') == '24628'
|
|
|
|
|
|
def test_style():
|
|
assert jh.style('test', 'bold') == "\x1b[1mtest\x1b[0m"
|
|
assert jh.style('test', 'u') == "\x1b[4mtest\x1b[0m"
|
|
|
|
|
|
def test_terminate_app():
|
|
# uses database, which is not existing during testing
|
|
pass
|
|
|
|
|
|
def test_timeframe_to_one_minutes():
|
|
assert jh.timeframe_to_one_minutes('1m') == 1
|
|
assert jh.timeframe_to_one_minutes('3m') == 3
|
|
assert jh.timeframe_to_one_minutes('5m') == 5
|
|
assert jh.timeframe_to_one_minutes('15m') == 15
|
|
assert jh.timeframe_to_one_minutes('30m') == 30
|
|
assert jh.timeframe_to_one_minutes('1h') == 60
|
|
assert jh.timeframe_to_one_minutes('2h') == 60 * 2
|
|
assert jh.timeframe_to_one_minutes('3h') == 60 * 3
|
|
assert jh.timeframe_to_one_minutes('4h') == 60 * 4
|
|
assert jh.timeframe_to_one_minutes('6h') == 60 * 6
|
|
assert jh.timeframe_to_one_minutes('8h') == 60 * 8
|
|
assert jh.timeframe_to_one_minutes('1D') == 60 * 24
|
|
|
|
|
|
def test_timestamp_to_arrow():
|
|
arrow_time = arrow.get('2015-08-01')
|
|
assert jh.timestamp_to_arrow(1438387200000) == arrow_time
|
|
|
|
|
|
def test_timestamp_to_date():
|
|
assert jh.timestamp_to_date(1438387200000) == "2015-08-01"
|
|
|
|
|
|
def test_timestamp_to_time():
|
|
assert jh.timestamp_to_time(1558770180000) == '2019-05-25T07:43:00+00:00'
|
|
|
|
|
|
def test_today_to_timestamp():
|
|
assert jh.today_to_timestamp() == arrow.utcnow().floor('day').int_timestamp * 1000
|
|
|
|
|
|
def test_type_to_side():
|
|
assert jh.type_to_side('long') == 'buy'
|
|
assert jh.type_to_side('short') == 'sell'
|
|
|
|
|
|
def test_unique_list():
|
|
a = [
|
|
('Binance', 'BTC', '1m'),
|
|
('Binance', 'BTC', '5m'),
|
|
('Binance', 'BTC', '15m'),
|
|
('Binance', 'BTC', '5m'),
|
|
('Binance', 'BTC', '1m'),
|
|
('Binance', 'BTC', '15m'),
|
|
]
|
|
|
|
expected = [
|
|
('Binance', 'BTC', '1m'),
|
|
('Binance', 'BTC', '5m'),
|
|
('Binance', 'BTC', '15m'),
|
|
]
|
|
|
|
assert jh.unique_list(a) == expected
|
|
|
|
|
|
def test_closing_side():
|
|
assert jh.closing_side('Long') == 'sell'
|
|
assert jh.closing_side('Short') == 'buy'
|
|
|
|
|
|
def test_merge_dicts():
|
|
client = {
|
|
'extra': {
|
|
'name': 'Saleh',
|
|
'new_key': 12
|
|
},
|
|
'age': 28
|
|
}
|
|
|
|
server = {
|
|
'extra': {
|
|
'name': 'Ocean',
|
|
'water': 100
|
|
},
|
|
}
|
|
|
|
expected_result = {'age': 28, 'extra': {'name': 'Ocean', 'water': 100, 'new_key': 12}}
|
|
|
|
assert expected_result == jh.merge_dicts(client, server)
|
|
|
|
|
|
def test_get_pid():
|
|
assert os.getpid() == jh.get_pid()
|
|
|
|
|
|
def test_convert_to_env_name():
|
|
assert jh.convert_to_env_name('Testnet Binance Futures') == 'TESTNET_BINANCE_FUTURES'
|
|
assert jh.convert_to_env_name('Testnet Binance') == 'TESTNET_BINANCE'
|