@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import requests
|
||||
|
||||
import jesse.helpers as jh
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
2
setup.py
2
setup.py
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user