Revert "Sourcery (#316)"

This reverts commit cff9e69446.
This commit is contained in:
Saleh Mir
2022-02-14 13:56:57 +01:00
parent 46e4ef5557
commit fe74aadc15
60 changed files with 381 additions and 238 deletions

View File

@@ -212,7 +212,11 @@ def run() -> None:
# read port from .env file, if not found, use default
from jesse.services.env import ENV_VALUES
port = int(ENV_VALUES['APP_PORT']) if 'APP_PORT' in ENV_VALUES else 9000
if 'APP_PORT' in ENV_VALUES:
port = int(ENV_VALUES['APP_PORT'])
else:
port = 9000
# run the main application
uvicorn.run(fastapi_app, host="0.0.0.0", port=port, log_level="info")

View File

@@ -77,7 +77,9 @@ class Sandbox(Exchange):
store.orders.get_order_by_id(self.name, symbol, order_id).cancel()
def get_exec_inst(self, flags: list) -> Union[str, None]:
return flags[0] if flags else None
if flags:
return flags[0]
return None
def _fetch_precisions(self) -> None:
pass

View File

@@ -113,7 +113,7 @@ def dashy_symbol(symbol: str) -> str:
if compare_symbol == symbol:
return s
return f'{symbol[:3]}-{symbol[3:]}'
return f"{symbol[0:3]}-{symbol[3:]}"
def date_diff_in_days(date1: arrow.arrow.Arrow, date2: arrow.arrow.Arrow) -> int:
@@ -314,7 +314,10 @@ def insert_list(index: int, item, arr: list) -> list:
"""
helper to insert an item in a Python List without removing the item
"""
return arr + [item] if index == -1 else arr[:index] + [item] + arr[index:]
if index == -1:
return arr + [item]
return arr[:index] + [item] + arr[index:]
def is_backtesting() -> bool:
@@ -740,7 +743,7 @@ def timeframe_to_one_minutes(timeframe: str) -> int:
try:
return dic[timeframe]
except KeyError:
all_timeframes = list(class_iter(timeframes))
all_timeframes = [timeframe for timeframe in class_iter(timeframes)]
raise InvalidTimeframe(
f'Timeframe "{timeframe}" is invalid. Supported timeframes are {", ".join(all_timeframes)}.')
@@ -878,7 +881,10 @@ def float_or_none(item):
"""
Return the float of the value if it's not None
"""
return None if item is None else float(item)
if item is None:
return None
else:
return float(item)
def str_or_none(item, encoding='utf-8'):
@@ -889,7 +895,9 @@ def str_or_none(item, encoding='utf-8'):
return None
else:
# return item if it's str, if not, decode it using encoding
return item if isinstance(item, str) else str(item, encoding)
if isinstance(item, str):
return item
return str(item, encoding)
def get_settlement_currency_from_exchange(exchange: str):
@@ -913,7 +921,14 @@ def is_notebook():
try:
shell = get_ipython().__class__.__name__
# Jupyter notebook or qtconsole
return shell == 'ZMQInteractiveShell'
if shell == 'ZMQInteractiveShell':
return True
elif shell == 'TerminalInteractiveShell':
# Terminal running IPython
return False
else:
# Other type (?)
return False
except NameError:
# Probably standard Python interpreter
return False

View File

@@ -25,4 +25,7 @@ def acosc(candles: np.ndarray, sequential: bool = False) -> AC:
res = ao - talib.SMA(ao, 5)
mom = talib.MOM(res, timeperiod=1)
return AC(res, mom) if sequential else AC(res[-1], mom[-1])
if sequential:
return AC(res, mom)
else:
return AC(res[-1], mom[-1])

View File

@@ -24,4 +24,7 @@ def ao(candles: np.ndarray, sequential: bool = False) -> AO:
mom = talib.MOM(res, timeperiod=1)
return AO(res, mom) if sequential else AO(res[-1], mom[-1])
if sequential:
return AO(res, mom)
else:
return AO(res[-1], mom[-1])

View File

@@ -34,5 +34,8 @@ def cksp(candles: np.ndarray, p: int = 10, x: float = 1.0, q: int = 9, sequenti
SS0 = talib.MIN(candles_low, q) + x * atr
SS = talib.MIN(SS0, q)
return CKSP(LS, SS) if sequential else CKSP(LS[-1], SS[-1])
if sequential:
return CKSP(LS, SS)
else:
return CKSP(LS[-1], SS[-1])

View File

@@ -23,4 +23,7 @@ def di(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DI:
MINUS_DI = talib.MINUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
PLUS_DI = talib.PLUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
return DI(PLUS_DI, MINUS_DI) if sequential else DI(PLUS_DI[-1], MINUS_DI[-1])
if sequential:
return DI(PLUS_DI, MINUS_DI)
else:
return DI(PLUS_DI[-1], MINUS_DI[-1])

View File

@@ -23,4 +23,7 @@ def dm(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DM:
MINUS_DI = talib.MINUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
PLUS_DI = talib.PLUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
return DM(PLUS_DI, MINUS_DI) if sequential else DM(PLUS_DI[-1], MINUS_DI[-1])
if sequential:
return DM(PLUS_DI, MINUS_DI)
else:
return DM(PLUS_DI[-1], MINUS_DI[-1])

View File

@@ -29,4 +29,7 @@ def eri(candles: np.ndarray, period: int = 13, matype: int = 1, source_type: str
bull = candles[:, 3] - ema
bear = candles[:, 4] - ema
return ERI(bull, bear) if sequential else ERI(bull[-1], bear[-1])
if sequential:
return ERI(bull, bear)
else:
return ERI(bull[-1], bear[-1])

View File

@@ -33,7 +33,10 @@ def frama(candles: np.ndarray, window: int = 10, FC: int = 1, SC: int = 300, seq
res = frame_fast(candles, n, SC, FC)
return res if sequential else res[-1]
if sequential:
return res
else:
return res[-1]
@njit

View File

@@ -36,7 +36,9 @@ def fwma(candles: np.ndarray, period: int = 5, source_type: str = "close", seque
def fibonacci(n: int = 2) -> np.ndarray:
"""Fibonacci Sequence as a numpy array"""
n = (int(fabs(n)) if n >= 0 else 2) - 1
n = int(fabs(n)) if n >= 0 else 2
n -= 1
a, b = 1, 1
result = np.array([a])
@@ -46,4 +48,7 @@ def fibonacci(n: int = 2) -> np.ndarray:
result = np.append(result, a)
fib_sum = np.sum(result)
return result / fib_sum if fib_sum > 0 else result
if fib_sum > 0:
return result / fib_sum
else:
return result

View File

@@ -37,4 +37,7 @@ def kdj(candles: np.ndarray, fastk_period: int = 9, slowk_period: int = 3, slowk
d = ma(k, period=slowd_period, matype=slowd_matype, sequential=True)
j = 3 * k - 2 * d
return KDJ(k, d, j) if sequential else KDJ(k[-1], d[-1], j[-1])
if sequential:
return KDJ(k, d, j)
else:
return KDJ(k[-1], d[-1], j[-1])

View File

@@ -42,4 +42,7 @@ def kst(candles: np.ndarray, sma_period1: int = 10, sma_period2: int = 10, sma_p
3 * aroc3[aroc3.size - aroc4.size:] + 4 * aroc4
signal = talib.SMA(line, signal_period)
return KST(line, signal) if sequential else KST(line[-1], signal[-1])
if sequential:
return KST(line, signal)
else:
return KST(line[-1], signal[-1])

View File

@@ -30,4 +30,7 @@ def mama(candles: np.ndarray, fastlimit: float = 0.5, slowlimit: float = 0.05, s
mama_val, fama = talib.MAMA(source, fastlimit=fastlimit, slowlimit=slowlimit)
return MAMA(mama_val, fama) if sequential else MAMA(mama_val[-1], fama[-1])
if sequential:
return MAMA(mama_val, fama)
else:
return MAMA(mama_val[-1], fama[-1])

View File

@@ -27,4 +27,7 @@ def msw(candles: np.ndarray, period: int = 5, source_type: str = "close", sequen
s = same_length(candles, msw_sine)
l = same_length(candles, msw_lead)
return MSW(s, l) if sequential else MSW(s[-1], l[-1])
if sequential:
return MSW(s, l)
else:
return MSW(s[-1], l[-1])

View File

@@ -31,7 +31,10 @@ def pma(candles: np.ndarray, source_type: str = "hl2", sequential: bool = False)
predict, trigger = pma_fast(source)
return PMA(predict, trigger) if sequential else PMA(predict[-1], trigger[-1])
if sequential:
return PMA(predict, trigger)
else:
return PMA(predict[-1], trigger[-1])
@njit

View File

@@ -41,5 +41,8 @@ def rsmk(candles: np.ndarray, candles_compare: np.ndarray, lookback: int = 90, p
signal = ma(res, period=signal_period, matype=signal_matype, sequential=True)
return RSMK(res, signal) if sequential else RSMK(res[-1], signal[-1])
if sequential:
return RSMK(res, signal)
else:
return RSMK(res[-1], signal[-1])

View File

@@ -37,4 +37,7 @@ def stoch(candles: np.ndarray, fastk_period: int = 14, slowk_period: int = 3, sl
k = ma(stoch_val, period=slowk_period, matype=slowk_matype, sequential=True)
d = ma(k, period=slowd_period, matype=slowd_matype, sequential=True)
return Stochastic(k, d) if sequential else Stochastic(k[-1], d[-1])
if sequential:
return Stochastic(k, d)
else:
return Stochastic(k[-1], d[-1])

View File

@@ -34,4 +34,7 @@ def stochf(candles: np.ndarray, fastk_period: int = 5, fastd_period: int = 3, fa
k = 100 * (candles_close - ll) / (hh - ll)
d = ma(k, period=fastd_period, matype=fastd_matype, sequential=True)
return StochasticFast(k, d) if sequential else StochasticFast(k[-1], d[-1])
if sequential:
return StochasticFast(k, d)
else:
return StochasticFast(k[-1], d[-1])

View File

@@ -32,4 +32,7 @@ def vpci(candles: np.ndarray, short_range: int = 5, long_range: int = 25, sequen
VPCIS = talib.SMA(VPCI_val * candles[:, 5], short_range) / talib.SMA(candles[:, 5], short_range)
return VPCI(VPCI_val, VPCIS) if sequential else VPCI(VPCI_val[-1], VPCIS[-1])
if sequential:
return VPCI(VPCI_val, VPCIS)
else:
return VPCI(VPCI_val[-1], VPCIS[-1])

View File

@@ -13,4 +13,4 @@ class NpEncoder(json.JSONEncoder):
elif isinstance(obj, np.ndarray):
return obj.tolist()
else:
return super().default(obj)
return super(NpEncoder, self).default(obj)

View File

@@ -40,15 +40,24 @@ class Position:
@property
def mark_price(self) -> float:
return self.current_price if not jh.is_live() else self._mark_price
if not jh.is_live():
return self.current_price
return self._mark_price
@property
def funding_rate(self) -> float:
return 0 if not jh.is_live() else self._funding_rate
if not jh.is_live():
return 0
return self._funding_rate
@property
def next_funding_timestamp(self) -> Union[int, None]:
return None if not jh.is_live() else self._next_funding_timestamp
if not jh.is_live():
return None
return self._next_funding_timestamp
@property
def value(self) -> float:
@@ -109,7 +118,10 @@ class Position:
if self.exchange.type == 'spot':
return 1
return self.strategy.leverage if self.strategy else np.nan
if self.strategy:
return self.strategy.leverage
else:
return np.nan
@property
def entry_margin(self) -> float:

View File

@@ -211,7 +211,7 @@ def load_candles(start_date_str: str, finish_date_str: str) -> Dict[str, Dict[st
f'There are missing candles between {start_date_str} => {finish_date_str}')
# cache it for near future calls
cache.set_value(cache_key, tuple(candles_tuple), expire_seconds=60**2 * 24 * 7)
cache.set_value(cache_key, tuple(candles_tuple), expire_seconds=60 * 60 * 24 * 7)
candles[key] = {
'exchange': exchange,

View File

@@ -189,7 +189,7 @@ def download_file(mode: str, file_type: str, session_id: str = None):
path = f'storage/trading-view-pine-editor/{session_id}.txt'
filename = f'backtest-{session_id}.txt'
elif mode == 'optimize' and file_type == 'log':
path = 'storage/logs/optimize-mode.txt'
path = f'storage/logs/optimize-mode.txt'
# filename should be "optimize-" + current timestamp
filename = f'optimize-{jh.timestamp_to_date(jh.now(True))}.txt'
else:

View File

@@ -54,11 +54,10 @@ class BybitPerpetual(CandleExchange):
payload = {
'interval': 1,
'symbol': dashless_symbol,
'from': start_timestamp // 1000,
'from': int(start_timestamp / 1000),
'limit': self.count,
}
response = requests.get(self.endpoint, params=payload)
# Exchange In Maintenance

View File

@@ -1,3 +1,4 @@
import requests
import jesse.helpers as jh

View File

@@ -54,11 +54,10 @@ class TestnetBybitPerpetual(CandleExchange):
payload = {
'interval': 1,
'symbol': dashless_symbol,
'from': start_timestamp // 1000,
'from': int(start_timestamp / 1000),
'limit': self.count,
}
response = requests.get(self.endpoint, params=payload)
# Exchange In Maintenance

View File

@@ -183,7 +183,7 @@ class Optimizer(ABC):
population_len = len(self.population)
if population_len == 0:
raise IndexError('population is empty')
count = population_len // 100
count = int(population_len / 100)
if count == 0:
count = 1
random_index = np.random.choice(population_len, count, replace=False)

View File

@@ -22,6 +22,8 @@ def run(
start_date: str,
finish_date: str,
optimal_total: int,
csv: bool,
json: bool
) -> None:
from jesse.config import config, set_config
config['app']['trading_mode'] = 'optimize'
@@ -58,7 +60,7 @@ def run(
click.clear()
optimizer = Optimizer(
training_candles, testing_candles, optimal_total, cpu_cores, start_date, finish_date
training_candles, testing_candles, optimal_total, cpu_cores, csv, json, start_date, finish_date
)
# start the process
@@ -81,10 +83,9 @@ def _get_training_and_testing_candles(start_date_str: str, finish_date_str: str)
training_candles[key] = {
'exchange': candles[key]['exchange'],
'symbol': candles[key]['symbol'],
'candles': candles[key]['candles'][:divider_index],
'candles': candles[key]['candles'][0:divider_index],
}
testing_candles[key] = {
'exchange': candles[key]['exchange'],
'symbol': candles[key]['symbol'],

View File

@@ -98,7 +98,7 @@ def get_fitness(
score = total_effect_rate * ratio_normalized
# if score is numpy nan, replace it with 0.0001
if np.isnan(score):
logger.log_optimize_mode('Score is nan. DNA is invalid')
logger.log_optimize_mode(f'Score is nan. DNA is invalid')
score = 0.0001
# elif jh.is_debugging():
else:
@@ -120,7 +120,7 @@ def get_fitness(
'PNL': round(testing_data_metrics['net_profit_percentage'], 2)
}
else:
logger.log_optimize_mode('Less than 5 trades in the training data. DNA is invalid')
logger.log_optimize_mode(f'Less than 5 trades in the training data. DNA is invalid')
score = 0.0001
return score, training_log, testing_log

View File

@@ -82,7 +82,9 @@ class RouterClass:
self.routes.append(Route(r["exchange"], r["symbol"], r["timeframe"], r["strategy"], None))
def set_market_data(self, routes: List[Any]) -> None:
self.market_data = [Route(*r) for r in routes]
self.market_data = []
for r in routes:
self.market_data.append(Route(*r))
def set_extra_candles(self, extra_candles: list) -> None:
self.extra_candles = extra_candles

View File

@@ -28,7 +28,7 @@ class Cache:
else:
self.db = {}
def set_value(self, key: str, data: Any, expire_seconds: int = 60**2) -> None:
def set_value(self, key: str, data: Any, expire_seconds: int = 60 * 60) -> None:
if self.driver is None:
return
@@ -92,7 +92,7 @@ def cached(method):
def decorated(self, *args, **kwargs):
cached_method = self._cached_methods.get(method)
if cached_method is None:
cached_method = lru_cache(method)
cached_method = lru_cache()(method)
self._cached_methods[method] = cached_method
return cached_method(self, *args, **kwargs)

View File

@@ -15,10 +15,14 @@ class Database:
self.db: PostgresqlExtDatabase = None
def is_closed(self) -> bool:
return True if self.db is None else self.db.is_closed()
if self.db is None:
return True
return self.db.is_closed()
def is_open(self) -> bool:
return False if self.db is None else not self.db.is_closed()
if self.db is None:
return False
return not self.db.is_closed()
def close_connection(self) -> None:
if self.db:

View File

@@ -40,4 +40,4 @@ if jh.is_jesse_project():
# raise FileNotFoundError('.env file is missing from within your local project. This usually happens when you\'re in the wrong directory. You can create one by running "cp .env.example .env"')
if not jh.is_unit_testing() and ENV_VALUES['PASSWORD'] == '':
raise OSError('You forgot to set the PASSWORD in your .env file')
raise EnvironmentError('You forgot to set the PASSWORD in your .env file')

View File

@@ -46,7 +46,7 @@ def install(is_live_plugin_already_installed: bool, strict: bool):
is_64_bit = platform.machine().endswith('64')
print('is_64_bit', is_64_bit)
if not is_64_bit:
raise NotImplementedError('Only 64-bit machines are supported')
raise NotImplementedError(f'Only 64-bit machines are supported')
is_arm = platform.machine().startswith('arm')
print('is_arm', is_arm)

View File

@@ -175,6 +175,10 @@ def hyperparameters(routes_arr: list) -> list:
if routes_arr[0].strategy.hp is None:
return []
return [[
hp = []
# only for the first route
for key in routes_arr[0].strategy.hp:
hp.append([
key, routes_arr[0].strategy.hp[key]
] for key in routes_arr[0].strategy.hp]
])
return hp

View File

@@ -34,34 +34,34 @@ def run():
def _candle(migrator):
fields = []
if 'candle' in database.db.get_tables():
candle_columns = database.db.get_columns('candle')
fields = []
_migrate(migrator, fields, candle_columns, 'candle')
def _completed_trade(migrator):
fields = []
if 'completedtrade' in database.db.get_tables():
completedtrade_columns = database.db.get_columns('completedtrade')
fields = []
_migrate(migrator, fields, completedtrade_columns, 'completedtrade')
def _daily_balance(migrator):
fields = []
if 'dailybalance' in database.db.get_tables():
dailybalance_columns = database.db.get_columns('dailybalance')
fields = []
_migrate(migrator, fields, dailybalance_columns, 'dailybalance')
def _log(migrator):
fields = []
if 'log' in database.db.get_tables():
log_columns = database.db.get_columns('log')
fields = []
_migrate(migrator, fields, log_columns, 'log')
@@ -79,26 +79,26 @@ def _order(migrator):
def _orderbook(migrator):
fields = []
if 'orderbook' in database.db.get_tables():
orderbook_columns = database.db.get_columns('orderbook')
fields = []
_migrate(migrator, fields, orderbook_columns, 'orderbook')
def _ticker(migrator):
fields = []
if 'ticker' in database.db.get_tables():
ticker_columns = database.db.get_columns('ticker')
fields = []
_migrate(migrator, fields, ticker_columns, 'ticker')
def _trade(migrator):
fields = []
if 'trade' in database.db.get_tables():
trade_columns = database.db.get_columns('trade')
fields = []
_migrate(migrator, fields, trade_columns, 'trade')
@@ -136,10 +136,12 @@ def _migrate(migrator, fields, columns, table):
)
print(
f"'{field['name']}' field successfully updated to accept to reject nullable values in the '{table}' table.")
elif field['action'] == 'add':
migrate(
migrator.add_column(table, field['name'], field['type'])
)
print(f"'{field['name']}' field successfully added to '{table}' table.")
# if column name doesn't not already exist
else:
print(f"'{field['name']}' field does not exist in '{table}' table.")
if field['action'] == 'add':
migrate(
migrator.add_column(table, field['name'], field['type'])
)
print(f"'{field['name']}' field successfully added to '{table}' table.")
else:
print(f"'{field['name']}' field does not exist in '{table}' table.")

View File

@@ -28,7 +28,9 @@ class Progressbar:
@property
def current(self):
return 100 if self.is_finished else round(self.index / self.length * 100, 1)
if self.is_finished:
return 100
return round(self.index / self.length * 100, 1)
@property
def average_execution_seconds(self):
@@ -36,7 +38,9 @@ class Progressbar:
@property
def remaining_index(self):
return 0 if self.is_finished else self.length - self.index
if self.is_finished:
return 0
return self.length - self.index
@property
def estimated_remaining_seconds(self):

View File

@@ -28,7 +28,7 @@ if jh.is_jesse_project():
def sync_publish(event: str, msg):
if jh.is_unit_testing():
raise OSError('sync_publish() should be NOT called during testing. There must be something wrong')
raise EnvironmentError('sync_publish() should be NOT called during testing. There must be something wrong')
sync_redis.publish(
f"{ENV_VALUES['APP_PORT']}:channel:1", json.dumps({
@@ -51,7 +51,7 @@ async def async_publish(event: str, msg):
def process_status(pid=None) -> str:
if jh.is_unit_testing():
raise OSError('process_status() is not meant to be called in unit tests')
raise EnvironmentError('process_status() is not meant to be called in unit tests')
if pid is None:
pid = jh.get_pid()

View File

@@ -144,12 +144,15 @@ def portfolio_metrics() -> dict:
def info() -> List[List[Union[str, Any]]]:
return [[
return [
[
jh.timestamp_to_time(w['time'])[11:19],
f"{w['message'][:70]}.."
if len(w['message']) > 70
else w['message'],
] for w in store.logs.info[::-1][:5]]
]
for w in store.logs.info[::-1][0:5]
]
def watch_list() -> List[List[Union[str, str]]]:
@@ -181,12 +184,15 @@ def watch_list() -> List[List[Union[str, str]]]:
def errors() -> List[List[Union[str, Any]]]:
return [[
return [
[
jh.timestamp_to_time(w['time'])[11:19],
f"{w['message'][:70]}.."
if len(w['message']) > 70
else w['message'],
] for w in store.logs.errors[::-1][:5]]
]
for w in store.logs.errors[::-1][0:5]
]
def orders():
@@ -213,4 +219,4 @@ def orders():
'created_at': o.created_at,
'canceled_at': o.canceled_at,
'executed_at': o.executed_at,
} for o in route_orders[::-1][:5]]
} for o in route_orders[::-1][0:5]]

View File

@@ -42,6 +42,7 @@ def load_required_candles(exchange: str, symbol: str, start_date_str: str, finis
# if cache exists
if cached_value:
candles_tuple = cached_value
# not cached, get and cache for later calls in the next 5 minutes
else:
# fetch from database
candles_tuple = tuple(
@@ -56,7 +57,7 @@ def load_required_candles(exchange: str, symbol: str, start_date_str: str, finis
)
# cache it for near future calls
cache.set_value(cache_key, candles_tuple, expire_seconds=60**2 * 24 * 7)
cache.set_value(cache_key, candles_tuple, expire_seconds=60 * 60 * 24 * 7)
candles = np.array(candles_tuple)

View File

@@ -20,7 +20,7 @@ def generate(name: str) -> JSONResponse:
shutil.copytree(f'{dirname}/ExampleStrategy', path)
# replace 'ExampleStrategy' with the name of the new strategy
with open(f"{path}/__init__.py") as fin:
with open(f"{path}/__init__.py", "rt") as fin:
data = fin.read()
data = data.replace('ExampleStrategy', name)
with open(f"{path}/__init__.py", "wt") as fin:

View File

@@ -2,7 +2,7 @@ from tabulate import tabulate
from typing import Union
def key_value(data, title: str, alignments: Union[list, tuple] = None, uppercase_title: bool = True) -> None:
table = list(data)
table = [d for d in data]
if alignments is None:
print(tabulate(table, headers=[title.upper() if uppercase_title else title, ''], tablefmt="presto"))

View File

@@ -9,5 +9,6 @@ def validate_routes(router) -> None:
'No routes found. Please add at least one route at: routes.py\nMore info: https://docs.jesse.trade/docs/routes.html#routing')
# validation for number of routes in the live mode
if jh.is_live() and len(router.routes) > 5:
logger.broadcast_error_without_logging('Too many routes (not critical, but use at your own risk): Using that more than 5 routes in live/paper trading is not recommended because exchange WS connections are often not reliable for handling that much traffic.')
if jh.is_live():
if len(router.routes) > 5:
logger.broadcast_error_without_logging('Too many routes (not critical, but use at your own risk): Using that more than 5 routes in live/paper trading is not recommended because exchange WS connections are often not reliable for handling that much traffic.')

View File

@@ -291,7 +291,11 @@ class CandlesState:
# no need to worry for forming candles when timeframe == 1m
if timeframe == '1m':
arr: DynamicNumpyArray = self.get_storage(exchange, symbol, '1m')
return np.zeros((0, 6)) if len(arr) == 0 else arr[:]
if len(arr) == 0:
return np.zeros((0, 6))
else:
return arr[:]
# other timeframes
dif, long_key, short_key = self.forming_estimation(exchange, symbol, timeframe)
long_count = len(self.get_storage(exchange, symbol, timeframe))
@@ -324,7 +328,11 @@ class CandlesState:
# no need to worry for forming candles when timeframe == 1m
if timeframe == '1m':
arr: DynamicNumpyArray = self.get_storage(exchange, symbol, '1m')
return np.zeros((0, 6)) if len(arr) == 0 else arr[-1]
if len(arr) == 0:
return np.zeros((0, 6))
else:
return arr[-1]
# other timeframes
dif, long_key, short_key = self.forming_estimation(exchange, symbol, timeframe)
long_count = len(self.get_storage(exchange, symbol, timeframe))
@@ -336,4 +344,7 @@ class CandlesState:
timeframe, self.storage[short_key][short_count - dif:short_count],
True
)
return np.zeros((0, 6)) if long_count == 0 else self.storage[long_key][-1]
if long_count == 0:
return np.zeros((0, 6))
else:
return self.storage[long_key][-1]

View File

@@ -73,7 +73,9 @@ class Strategy(ABC):
self.broker = Broker(self.position, self.exchange, self.symbol, self.timeframe)
if self.hp is None and len(self.hyperparameters()) > 0:
self.hp = {dna['name']: dna['default'] for dna in self.hyperparameters()}
self.hp = {}
for dna in self.hyperparameters():
self.hp[dna['name']] = dna['default']
@property
def _price_precision(self) -> int:
@@ -1208,9 +1210,14 @@ class Strategy(ABC):
@property
def all_positions(self) -> Dict[str, Position]:
return {r.symbol: r.strategy.position for r in self.routes}
positions_dict = {}
for r in self.routes:
positions_dict[r.symbol] = r.strategy.position
return positions_dict
@property
def portfolio_value(self) -> float:
total_position_values = sum(p.pnl for key, p in self.all_positions.items())
total_position_values = 0
for key, p in self.all_positions.items():
total_position_values += p.pnl
return (total_position_values + self.capital) * self.leverage

View File

@@ -4,21 +4,28 @@ from jesse.strategies import Strategy
class TestCanCancelEntryOrdersAfterOpenPositionLong1(Strategy):
def before(self) -> None:
if self.price == 12:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'LIMIT', 'ACTIVE', 9)
self._fake_order(2, 'LIMIT', 'ACTIVE', 8)
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'ACTIVE'
assert self.orders[1].price == 9
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'ACTIVE'
assert self.orders[2].price == 8
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'CANCELED'
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'CANCELED'
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
def should_long(self) -> bool:
return self.price == 10

View File

@@ -4,21 +4,28 @@ from jesse.strategies import Strategy
class TestCanCancelEntryOrdersAfterOpenPositionLong2(Strategy):
def before(self) -> None:
if self.price == 12:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'LIMIT', 'ACTIVE', 9)
self._fake_order(2, 'LIMIT', 'ACTIVE', 8)
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'ACTIVE'
assert self.orders[1].price == 9
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'ACTIVE'
assert self.orders[2].price == 8
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'CANCELED'
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'CANCELED'
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
def should_long(self) -> bool:
return self.price == 10

View File

@@ -4,21 +4,28 @@ from jesse.strategies import Strategy
class TestCanCancelEntryOrdersAfterOpenPositionShort1(Strategy):
def before(self) -> None:
if self.price == 12:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'STOP', 'ACTIVE', 9)
self._fake_order(2, 'STOP', 'ACTIVE', 8)
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'STOP'
assert self.orders[1].status == 'ACTIVE'
assert self.orders[1].price == 9
assert self.orders[2].type == 'STOP'
assert self.orders[2].status == 'ACTIVE'
assert self.orders[2].price == 8
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'STOP'
assert self.orders[1].status == 'CANCELED'
assert self.orders[2].type == 'STOP'
assert self.orders[2].status == 'CANCELED'
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
def should_long(self) -> bool:
return False

View File

@@ -4,21 +4,28 @@ from jesse.strategies import Strategy
class TestCanCancelEntryOrdersAfterOpenPositionShort2(Strategy):
def before(self) -> None:
if self.price == 12:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'STOP', 'ACTIVE', 9)
self._fake_order(2, 'STOP', 'ACTIVE', 8)
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'STOP'
assert self.orders[1].status == 'ACTIVE'
assert self.orders[1].price == 9
assert self.orders[2].type == 'STOP'
assert self.orders[2].status == 'ACTIVE'
assert self.orders[2].price == 8
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'STOP'
assert self.orders[1].status == 'CANCELED'
assert self.orders[2].type == 'STOP'
assert self.orders[2].status == 'CANCELED'
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
def should_long(self) -> bool:
return False

View File

@@ -10,21 +10,23 @@ class TestMultipleEntryOrdersUpdateEntryLongPositions(Strategy):
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'ACTIVE'
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'CANCELED'
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'CANCELED'
self._fake_order(3, 'MARKET', 'EXECUTED', 13)
self._fake_order(4, 'LIMIT', 'ACTIVE', 10)
assert self.orders[3].type == 'MARKET'
assert self.orders[3].status == 'EXECUTED'
assert self.orders[3].price == 13
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
assert self.orders[4].type == 'LIMIT'
assert self.orders[4].status == 'ACTIVE'
assert self.orders[4].price == 10
def should_long(self) -> bool:
return self.price == 10

View File

@@ -6,19 +6,33 @@ import jesse.helpers as jh
class TestMultipleEntryOrdersUpdateEntryShortPositions(Strategy):
def before(self) -> None:
if self.price == 12:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'LIMIT', 'ACTIVE', 20)
elif self.price == 15:
self._fake_order(0, 'MARKET', 'EXECUTED', 10)
self._fake_order(1, 'LIMIT', 'CANCELED', 20)
self._fake_order(2, 'LIMIT', 'CANCELED', 21)
self._fake_order(3, 'MARKET', 'EXECUTED', 13)
self._fake_order(4, 'LIMIT', 'ACTIVE', 22)
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
def _fake_order(self, i, type, status, price):
assert self.orders[i].type == type
assert self.orders[i].status == status
assert self.orders[i].price == price
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'ACTIVE'
assert self.orders[1].price == 20
if self.price == 15:
assert self.orders[0].type == 'MARKET'
assert self.orders[0].status == 'EXECUTED'
assert self.orders[0].price == 10
assert self.orders[1].type == 'LIMIT'
assert self.orders[1].status == 'CANCELED'
assert self.orders[1].price == 20
assert self.orders[2].type == 'LIMIT'
assert self.orders[2].status == 'CANCELED'
assert self.orders[2].price == 21
assert self.orders[3].type == 'MARKET'
assert self.orders[3].status == 'EXECUTED'
assert self.orders[3].price == 13
assert self.orders[4].type == 'LIMIT'
assert self.orders[4].status == 'ACTIVE'
assert self.orders[4].price == 22
def should_long(self) -> bool:
return False

View File

@@ -9,7 +9,7 @@ class TestPortfolioValue(Strategy):
# print('\nstarting: self.portfolio_value', self.portfolio_value)
assert self.portfolio_value == 10_000
elif self.index == 10:
if self.index == 10:
# print('=========')
# print(self.symbol, 'value', self.available_margin, self.positions['ETH-USDT'].value, self.positions['BTC-USDT'].value)
# print(self.symbol, 'PNL', self.positions['ETH-USDT'].pnl, self.positions['BTC-USDT'].pnl)

View File

@@ -79,7 +79,10 @@ def crossed(series1: np.ndarray, series2: Union[float, int, np.ndarray], directi
if direction is None:
return cross_above or cross_below
return cross_above if direction == "above" else cross_below
if direction == "above":
return cross_above
else:
return cross_below
def estimate_risk(entry_price: float, stop_price: float) -> float:

View File

@@ -37,7 +37,7 @@ REQUIRED_PACKAGES = [
'aiofiles'
]
with open("README.md", encoding="utf-8") as f:
with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()
setup(

View File

@@ -16,13 +16,12 @@ def test_backtesting_one_route():
]
config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures'
candles = {}
key = jh.key(exchanges.SANDBOX, 'BTC-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': range_candles(5 * 20),
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': range_candles(5 * 20)
}
# run backtest (dates are fake just to pass)

View File

@@ -45,7 +45,7 @@ def test_fill_absent_candles_beginning_middle_end():
assert candles[-1]['timestamp'] == smaller_data_set[-1]['timestamp']
# Should fill if candles in the middle are absent
candles = smaller_data_set[:3] + smaller_data_set[5:7]
candles = smaller_data_set[0:3] + smaller_data_set[5:7]
assert len(candles) == 5
candles = importer._fill_absent_candles(candles, start, end)
assert len(candles) == 7
@@ -53,7 +53,7 @@ def test_fill_absent_candles_beginning_middle_end():
assert candles[-1]['timestamp'] == smaller_data_set[-1]['timestamp']
# Should fill if candles in the ending are absent
candles = smaller_data_set[:5]
candles = smaller_data_set[0:5]
assert len(candles) == 5
candles = importer._fill_absent_candles(candles, start, end)
assert len(candles) == 7

View File

@@ -128,29 +128,18 @@ def test_filters():
def test_forming_candles():
reset_config()
routes = [
{
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'timeframe': timeframes.MINUTE_5,
'strategy': 'Test19',
}
]
{'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'timeframe': timeframes.MINUTE_5, 'strategy': 'Test19'}
]
extra_routes = [
{
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'timeframe': timeframes.MINUTE_15,
}
{'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'timeframe': timeframes.MINUTE_15}
]
candles = {}
key = jh.key(exchanges.SANDBOX, 'BTC-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': test_candles_0,
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': test_candles_0
}
backtest_mode.run(False, {}, routes, extra_routes, '2019-04-01', '2019-04-02', candles)
@@ -177,21 +166,15 @@ def test_is_smart_enough_to_open_positions_via_market_orders():
set_up()
routes = [
{
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'timeframe': timeframes.MINUTE_1,
'strategy': 'Test05',
}
{'exchange': exchanges.SANDBOX, 'symbol': 'ETH-USDT', 'timeframe': timeframes.MINUTE_1, 'strategy': 'Test05'}
]
candles = {}
key = jh.key(exchanges.SANDBOX, 'ETH-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1,
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1
}
# run backtest (dates are fake just to pass)
@@ -228,21 +211,15 @@ def test_is_smart_enough_to_open_positions_via_stop_orders():
set_up()
routes = [
{
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'timeframe': timeframes.MINUTE_5,
'strategy': 'Test06',
}
{'exchange': exchanges.SANDBOX, 'symbol': 'ETH-USDT', 'timeframe': timeframes.MINUTE_5, 'strategy': 'Test06'}
]
candles = {}
key = jh.key(exchanges.SANDBOX, 'ETH-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1,
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1
}
# run backtest (dates are fake just to pass)
@@ -301,13 +278,12 @@ def test_modifying_stop_loss_after_part_of_position_is_already_reduced_with_stop
list(range(1, 10)) + list(range(10, 1, -1))
)
candles = {}
key = jh.key(exchanges.SANDBOX, 'BTC-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': generated_candles,
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'BTC-USDT',
'candles': generated_candles
}
backtest_mode.run(False, {}, routes, [], '2019-04-01', '2019-04-02', candles)
@@ -712,21 +688,15 @@ def test_updating_stop_loss_and_take_profit_after_opening_the_position():
set_up()
routes = [
{
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'timeframe': timeframes.MINUTE_1,
'strategy': 'Test07',
}
{'exchange': exchanges.SANDBOX, 'symbol': 'ETH-USDT', 'timeframe': timeframes.MINUTE_1, 'strategy': 'Test07'}
]
candles = {}
key = jh.key(exchanges.SANDBOX, 'ETH-USDT')
candles = {
key: {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1,
}
candles[key] = {
'exchange': exchanges.SANDBOX,
'symbol': 'ETH-USDT',
'candles': test_candles_1
}
# run backtest (dates are fake just to pass)

View File

@@ -20,10 +20,10 @@ def test_routes():
store.reset(True)
# now assert it's working as expected
assert set(config['app']['trading_exchanges']) == {exchanges.SANDBOX, exchanges.BITFINEX}
assert set(config['app']['trading_symbols']) == {'BTC-USD', 'ETH-USD'}
assert set(config['app']['trading_timeframes']) == {timeframes.HOUR_3, timeframes.MINUTE_15}
assert set(config['app']['considering_exchanges']) == {exchanges.SANDBOX, exchanges.BITFINEX}
assert set(config['app']['considering_symbols']) == {'BTC-USD', 'ETH-USD', 'EOS-USD'}
assert set(config['app']['considering_timeframes']) == {
timeframes.MINUTE_1, timeframes.HOUR_3, timeframes.MINUTE_15, timeframes.HOUR_1}
assert set(config['app']['trading_exchanges']) == set([exchanges.SANDBOX, exchanges.BITFINEX])
assert set(config['app']['trading_symbols']) == set(['BTC-USD', 'ETH-USD'])
assert set(config['app']['trading_timeframes']) == set([timeframes.HOUR_3, timeframes.MINUTE_15])
assert set(config['app']['considering_exchanges']) == set([exchanges.SANDBOX, exchanges.BITFINEX])
assert set(config['app']['considering_symbols']) == set(['BTC-USD', 'ETH-USD', 'EOS-USD'])
assert set(config['app']['considering_timeframes']) == set(
[timeframes.MINUTE_1, timeframes.HOUR_3, timeframes.MINUTE_15, timeframes.HOUR_1])

View File

@@ -75,12 +75,11 @@ def test_get_candles_including_forming():
candles_to_add = range_candles(14)
store.candles.batch_add_candle(candles_to_add, 'Sandbox', 'BTC-USD', '1m')
store.candles.add_candle(
generate_candle_from_one_minutes('5m', candles_to_add[:5], False),
'Sandbox',
'BTC-USD',
'5m',
generate_candle_from_one_minutes(
'5m', candles_to_add[0:5], False
),
'Sandbox', 'BTC-USD', '5m'
)
store.candles.add_candle(
generate_candle_from_one_minutes(
'5m', candles_to_add[5:10], False
@@ -115,7 +114,7 @@ def test_get_forming_candle():
set_up()
candles_to_add = range_candles(13)
store.candles.batch_add_candle(candles_to_add[:4], 'Sandbox', 'BTC-USD', '1m')
store.candles.batch_add_candle(candles_to_add[0:4], 'Sandbox', 'BTC-USD', '1m')
forming_candle = store.candles.get_current_candle('Sandbox', 'BTC-USD', '5m')
assert forming_candle[0] == candles_to_add[0][0]
assert forming_candle[1] == candles_to_add[0][1]