Sourcery optimize
This commit is contained in:
@@ -77,7 +77,7 @@ def color(msg_text: str, msg_color: str) -> str:
|
||||
return click.style(msg_text, fg='magenta')
|
||||
if msg_color == 'cyan':
|
||||
return click.style(msg_text, fg='cyan')
|
||||
if msg_color in ['white', 'gray']:
|
||||
if msg_color in {'white', 'gray'}:
|
||||
return click.style(msg_text, fg='white')
|
||||
|
||||
raise ValueError('unsupported color')
|
||||
@@ -94,9 +94,7 @@ def convert_number(old_max: float, old_min: float, new_max: float, new_min: floa
|
||||
|
||||
old_range = (old_max - old_min)
|
||||
new_range = (new_max - new_min)
|
||||
new_value = (((old_value - old_min) * new_range) / old_range) + new_min
|
||||
|
||||
return new_value
|
||||
return (((old_value - old_min) * new_range) / old_range) + new_min
|
||||
|
||||
|
||||
def dashless_symbol(symbol: str) -> str:
|
||||
@@ -255,7 +253,7 @@ def get_config(keys: str, default: Any = None) -> Any:
|
||||
if not str:
|
||||
raise ValueError('keys string cannot be empty')
|
||||
|
||||
if is_unit_testing() or not keys in CACHED_CONFIG:
|
||||
if is_unit_testing() or keys not in CACHED_CONFIG:
|
||||
if os.environ.get(keys.upper().replace(".", "_").replace(" ", "_")) is not None:
|
||||
CACHED_CONFIG[keys] = os.environ.get(keys.upper().replace(".", "_").replace(" ", "_"))
|
||||
else:
|
||||
@@ -401,8 +399,7 @@ def normalize(x: float, x_min: float, x_max: float) -> float:
|
||||
"""
|
||||
Rescaling data to have values between 0 and 1
|
||||
"""
|
||||
x_new = (x - x_min) / (x_max - x_min)
|
||||
return x_new
|
||||
return (x - x_min) / (x_max - x_min)
|
||||
|
||||
|
||||
def now() -> int:
|
||||
@@ -424,9 +421,16 @@ def np_ffill(arr: np.ndarray, axis: int = 0) -> np.ndarray:
|
||||
idx_shape = tuple([slice(None)] + [np.newaxis] * (len(arr.shape) - axis - 1))
|
||||
idx = np.where(~np.isnan(arr), np.arange(arr.shape[axis])[idx_shape], 0)
|
||||
np.maximum.accumulate(idx, axis=axis, out=idx)
|
||||
slc = [np.arange(k)[tuple([slice(None) if dim == i else np.newaxis
|
||||
for dim in range(len(arr.shape))])]
|
||||
for i, k in enumerate(arr.shape)]
|
||||
slc = [
|
||||
np.arange(k)[
|
||||
tuple(
|
||||
slice(None) if dim == i else np.newaxis
|
||||
for dim in range(len(arr.shape))
|
||||
)
|
||||
]
|
||||
for i, k in enumerate(arr.shape)
|
||||
]
|
||||
|
||||
slc[axis] = idx
|
||||
return arr[tuple(slc)]
|
||||
|
||||
@@ -472,10 +476,10 @@ def orderbook_insertion_index_search(arr, target: int, ascending: bool = True) -
|
||||
lower = 0
|
||||
upper = len(arr)
|
||||
|
||||
if ascending:
|
||||
while lower < upper:
|
||||
x = lower + (upper - lower) // 2
|
||||
val = arr[x][0]
|
||||
while lower < upper:
|
||||
x = lower + (upper - lower) // 2
|
||||
val = arr[x][0]
|
||||
if ascending:
|
||||
if target == val:
|
||||
return True, x
|
||||
elif target > val:
|
||||
@@ -486,20 +490,16 @@ def orderbook_insertion_index_search(arr, target: int, ascending: bool = True) -
|
||||
if lower == x:
|
||||
return False, lower
|
||||
upper = x
|
||||
else:
|
||||
while lower < upper:
|
||||
x = lower + (upper - lower) // 2
|
||||
val = arr[x][0]
|
||||
if target == val:
|
||||
return True, x
|
||||
elif target < val:
|
||||
if lower == x:
|
||||
return False, lower + 1
|
||||
lower = x
|
||||
elif target > val:
|
||||
if lower == x:
|
||||
return False, lower
|
||||
upper = x
|
||||
elif target == val:
|
||||
return True, x
|
||||
elif target < val:
|
||||
if lower == x:
|
||||
return False, lower + 1
|
||||
lower = x
|
||||
elif target > val:
|
||||
if lower == x:
|
||||
return False, lower
|
||||
upper = x
|
||||
|
||||
|
||||
def orderbook_trim_price(p: float, ascending: bool, unit: float) -> float:
|
||||
|
||||
@@ -46,6 +46,4 @@ def numpy_ewma(data: np.ndarray, window: int):
|
||||
pw0 = (1 - alpha) ** (n - 1)
|
||||
mult = data * pw0 * scale_arr
|
||||
cumsums = mult.cumsum()
|
||||
out = cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
return out
|
||||
return cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
@@ -32,15 +32,15 @@ def cg(candles: np.ndarray, period: int = 10, source_type: str = "close", sequen
|
||||
@njit
|
||||
def go_fast(source, period): # Function is compiled to machine code when called the first time
|
||||
res = np.full_like(source, fill_value=np.nan)
|
||||
for i in range(0, source.size):
|
||||
for i in range(source.size):
|
||||
if i > period:
|
||||
num = 0
|
||||
denom = 0
|
||||
for count in range(0, period - 1):
|
||||
for count in range(period - 1):
|
||||
close = source[i - count]
|
||||
if not np.isnan(close):
|
||||
num = num + (1 + count) * close
|
||||
denom = denom + close
|
||||
num += (1 + count) * close
|
||||
denom += close
|
||||
result = -num / denom if denom != 0 else 0
|
||||
res[i] = result
|
||||
return res
|
||||
|
||||
@@ -65,31 +65,28 @@ def go_fast(source, period, threshold): # Function is compiled to machine code
|
||||
|
||||
for j in range(period):
|
||||
jMinusOne = j + 1
|
||||
if np.isnan(source[i - jMinusOne]):
|
||||
X = 0
|
||||
else:
|
||||
X = source[i - jMinusOne]
|
||||
X = 0 if np.isnan(source[i - jMinusOne]) else source[i - jMinusOne]
|
||||
temp = PIx2 * jMinusOne / period
|
||||
Yc = np.cos(temp)
|
||||
Ys = -np.sin(temp)
|
||||
Rx = Rx + X
|
||||
Ix = Ix + X
|
||||
Rxx = Rxx + X * X
|
||||
Ixx = Ixx + X * X
|
||||
Rxy = Rxy + X * Yc
|
||||
Ixy = Ixy + X * Ys
|
||||
Ryy = Ryy + Yc * Yc
|
||||
Iyy = Iyy + Ys * Ys
|
||||
Ry = Ry + Yc
|
||||
Iy = Iy + Ys
|
||||
Rx += X
|
||||
Ix += X
|
||||
Rxx += X * X
|
||||
Ixx += X * X
|
||||
Rxy += X * Yc
|
||||
Ixy += X * Ys
|
||||
Ryy += Yc * Yc
|
||||
Iyy += Ys * Ys
|
||||
Ry += Yc
|
||||
Iy += Ys
|
||||
|
||||
temp_1 = period * Rxx - Rx * Rx
|
||||
temp_2 = period * Ryy - Ry * Ry
|
||||
temp_1 = period * Rxx - Rx**2
|
||||
temp_2 = period * Ryy - Ry**2
|
||||
if temp_1 > 0.0 and temp_2 > 0.0:
|
||||
realPart[i] = (period * Rxy - Rx * Ry) / np.sqrt(temp_1 * temp_2)
|
||||
|
||||
temp_1 = period * Ixx - Ix * Ix
|
||||
temp_2 = period * Iyy - Iy * Iy
|
||||
temp_1 = period * Ixx - Ix**2
|
||||
temp_2 = period * Iyy - Iy**2
|
||||
if temp_1 > 0.0 and temp_2 > 0.0:
|
||||
imagPart[i] = (period * Ixy - Ix * Iy) / np.sqrt(temp_1 * temp_2)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def damiani_volatmeter_fast(source, sed_std, atrvis, atrsed, vis_std,
|
||||
vol = np.full_like(source, 0)
|
||||
t = np.full_like(source, 0)
|
||||
for i in range(source.shape[0]):
|
||||
if not (i < sed_std):
|
||||
if i >= sed_std:
|
||||
vol[i] = atrvis[i] / atrsed[i] + lag_s * (vol[i - 1] - vol[i - 3])
|
||||
anti_thres = np.std(source[i - vis_std:i]) / np.std(source[i - sed_std:i])
|
||||
t[i] = threshold - anti_thres
|
||||
|
||||
@@ -45,9 +45,9 @@ def edcf_fast(source, period):
|
||||
for i in range(period):
|
||||
distance = 0.0
|
||||
for lb in range(1, period):
|
||||
distance = distance + np.power(source[j - i] - source[j - i - lb], 2)
|
||||
num = num + (distance * source[j - i])
|
||||
coefSum = coefSum + distance
|
||||
distance += np.power(source[j - i] - source[j - i - lb], 2)
|
||||
num += distance * source[j - i]
|
||||
coefSum += distance
|
||||
newseries[j] = num / coefSum if coefSum != 0 else 0
|
||||
|
||||
return newseries
|
||||
|
||||
@@ -43,7 +43,7 @@ def fibonacci(n: int = 2) -> np.ndarray:
|
||||
|
||||
result = np.array([a])
|
||||
|
||||
for i in range(0, n):
|
||||
for _ in range(n):
|
||||
a, b = b, a + b
|
||||
result = np.append(result, a)
|
||||
|
||||
|
||||
@@ -55,6 +55,4 @@ def numpy_ewma(data, window):
|
||||
pw0 = (1 - alpha) ** (n - 1)
|
||||
mult = data * pw0 * scale_arr
|
||||
cumsums = mult.cumsum()
|
||||
out = cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
return out
|
||||
return cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
@@ -27,9 +27,5 @@ def kaufmanstop(candles: np.ndarray, period: int = 22, mult: float = 2, directio
|
||||
|
||||
hl_diff = talib.SMA(high - low, period)
|
||||
|
||||
if direction == "long":
|
||||
res = hl_diff * mult - low
|
||||
else:
|
||||
res = hl_diff * mult + high
|
||||
|
||||
res = hl_diff * mult - low if direction == "long" else hl_diff * mult + high
|
||||
return res if sequential else res[-1]
|
||||
|
||||
@@ -64,9 +64,5 @@ def lrsi_fast(alpha, candles):
|
||||
else:
|
||||
cd = cd + l3[i] - l2[i]
|
||||
|
||||
if cu + cd == 0:
|
||||
rsi[i] = 0
|
||||
else:
|
||||
rsi[i] = cu / (cu + cd)
|
||||
|
||||
rsi[i] = 0 if cu + cd == 0 else cu / (cu + cd)
|
||||
return rsi
|
||||
|
||||
@@ -45,11 +45,9 @@ def pascals_triangle(n: int = None) -> np.ndarray:
|
||||
n = int(np.fabs(n)) if n is not None else 0
|
||||
|
||||
# Calculation
|
||||
triangle = np.array([combination(n=n, r=i) for i in range(0, n + 1)])
|
||||
triangle = np.array([combination(n=n, r=i) for i in range(n + 1)])
|
||||
triangle_sum = np.sum(triangle)
|
||||
triangle_weights = triangle / triangle_sum
|
||||
|
||||
return triangle_weights
|
||||
return triangle / triangle_sum
|
||||
|
||||
|
||||
def combination(n, r) -> int:
|
||||
|
||||
@@ -45,12 +45,12 @@ def reflex_fast(ssf, period):
|
||||
ms = np.full_like(ssf, 0)
|
||||
sums = np.full_like(ssf, 0)
|
||||
for i in range(ssf.shape[0]):
|
||||
if not (i < period):
|
||||
if i >= period:
|
||||
slope = (ssf[i - period] - ssf[i]) / period
|
||||
sum = 0
|
||||
for t in range(1, period + 1):
|
||||
sum = sum + (ssf[i] + t * slope) - ssf[i - t]
|
||||
sum = sum / period
|
||||
sum /= period
|
||||
sums[i] = sum
|
||||
|
||||
ms[i] = 0.04 * sums[i] * sums[i] + 0.96 * ms[i - 1]
|
||||
|
||||
@@ -67,18 +67,12 @@ def rsx_fast(source, period):
|
||||
if f90 == 0:
|
||||
f90 = 1.0
|
||||
f0 = 0.0
|
||||
if period - 1.0 >= 5:
|
||||
f88 = period - 1.0
|
||||
else:
|
||||
f88 = 5.0
|
||||
f88 = period - 1.0 if period >= 6 else 5.0
|
||||
f8 = 100.0 * source[i]
|
||||
f18 = 3.0 / (period + 2.0)
|
||||
f20 = 1.0 - f18
|
||||
else:
|
||||
if f88 <= f90:
|
||||
f90 = f88 + 1
|
||||
else:
|
||||
f90 = f90 + 1
|
||||
f90 = f88 + 1 if f88 <= f90 else f90 + 1
|
||||
f10 = f8
|
||||
f8 = 100 * source[i]
|
||||
v8 = f8 - f10
|
||||
@@ -106,10 +100,8 @@ def rsx_fast(source, period):
|
||||
f90 = 0.0
|
||||
if f88 < f90 and v20 > 0.0000000001:
|
||||
v4 = (v14 / v20 + 1.0) * 50.0
|
||||
if v4 > 100.0:
|
||||
v4 = 100.0
|
||||
if v4 < 0.0:
|
||||
v4 = 0.0
|
||||
v4 = min(v4, 100.0)
|
||||
v4 = max(v4, 0.0)
|
||||
else:
|
||||
v4 = 50.0
|
||||
res[i] = v4
|
||||
|
||||
@@ -25,7 +25,10 @@ def sinwma(candles: np.ndarray, period: int = 14, source_type: str = "close", se
|
||||
candles = slice_candles(candles, sequential)
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
sines = np.array([np.sin((i + 1) * np.pi / (period + 1)) for i in range(0, period)])
|
||||
sines = np.array(
|
||||
[np.sin((i + 1) * np.pi / (period + 1)) for i in range(period)]
|
||||
)
|
||||
|
||||
w = sines / sines.sum()
|
||||
swv = sliding_window_view(source, window_shape=period)
|
||||
res = np.average(swv, weights=w, axis=-1)
|
||||
|
||||
@@ -43,6 +43,4 @@ def numpy_ewma(data, window):
|
||||
pw0 = (1 - alpha) ** (n - 1)
|
||||
mult = data * pw0 * scale_arr
|
||||
cumsums = mult.cumsum()
|
||||
out = cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
return out
|
||||
return cumsums * scale_arr[::-1] / weights.cumsum()
|
||||
|
||||
@@ -49,17 +49,16 @@ def symmetric_triangle(n: int = None) -> np.ndarray:
|
||||
|
||||
if n > 2:
|
||||
if n % 2 == 0:
|
||||
front = [i + 1 for i in range(0, floor(n / 2))]
|
||||
front = [i + 1 for i in range(floor(n / 2))]
|
||||
triangle = front + front[::-1]
|
||||
else:
|
||||
front = [i + 1 for i in range(0, floor(0.5 * (n + 1)))]
|
||||
front = [i + 1 for i in range(floor(0.5 * (n + 1)))]
|
||||
triangle = front.copy()
|
||||
front.pop()
|
||||
triangle += front[::-1]
|
||||
|
||||
|
||||
triangle_sum = np.sum(triangle)
|
||||
triangle_weights = triangle / triangle_sum
|
||||
return triangle_weights
|
||||
return triangle / triangle_sum
|
||||
|
||||
|
||||
|
||||
@@ -47,11 +47,11 @@ def trendflex_fast(ssf, period):
|
||||
sums = np.full_like(ssf, 0)
|
||||
|
||||
for i in range(ssf.shape[0]):
|
||||
if not (i < period):
|
||||
if i >= period:
|
||||
sum = 0
|
||||
for t in range(1, period + 1):
|
||||
sum = sum + ssf[i] - ssf[i - t]
|
||||
sum = sum / period
|
||||
sum /= period
|
||||
sums[i] = sum
|
||||
|
||||
ms[i] = 0.04 * sums[i] * sums[i] + 0.96 * ms[i - 1]
|
||||
|
||||
@@ -50,14 +50,15 @@ def voss_fast(source, period, predict, bandwith):
|
||||
s1 = 1 / g1 - np.sqrt(1 / (g1 * g1) - 1)
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
if not (i <= period or i <= 5 or i <= order):
|
||||
if i > period and i > 5 and i > order:
|
||||
filt[i] = 0.5 * (1 - s1) * (source[i] - source[i - 2]) + f1 * (1 + s1) * filt[i - 1] - s1 * filt[i - 2]
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
if not (i <= period or i <= 5 or i <= order):
|
||||
sumc = 0
|
||||
for count in range(order):
|
||||
sumc = sumc + ((count + 1) / float(order)) * voss[i - (order - count)]
|
||||
sumc = sum(
|
||||
((count + 1) / float(order)) * voss[i - (order - count)]
|
||||
for count in range(order)
|
||||
)
|
||||
|
||||
voss[i] = ((3 + order) / 2) * filt[i] - sumc
|
||||
return voss, filt
|
||||
|
||||
@@ -31,9 +31,7 @@ class DynamicNumpyArray:
|
||||
|
||||
if stop < 0:
|
||||
stop = (self.index + 1) - abs(stop)
|
||||
if stop > (self.index + 1):
|
||||
stop = self.index + 1
|
||||
|
||||
stop = min(stop, self.index + 1)
|
||||
return self.array[start:stop]
|
||||
else:
|
||||
if i < 0:
|
||||
@@ -64,11 +62,14 @@ class DynamicNumpyArray:
|
||||
self.array = np.concatenate((self.array, new_bucket), axis=0)
|
||||
|
||||
# drop N% of the beginning values to free memory
|
||||
if self.drop_at is not None:
|
||||
if self.index != 0 and (self.index + 1) % self.drop_at == 0:
|
||||
shift_num = int(self.drop_at / 2)
|
||||
self.index -= shift_num
|
||||
self.array = np_shift(self.array, -shift_num)
|
||||
if (
|
||||
self.drop_at is not None
|
||||
and self.index != 0
|
||||
and (self.index + 1) % self.drop_at == 0
|
||||
):
|
||||
shift_num = int(self.drop_at / 2)
|
||||
self.index -= shift_num
|
||||
self.array = np_shift(self.array, -shift_num)
|
||||
|
||||
self.array[self.index] = item
|
||||
|
||||
|
||||
@@ -42,9 +42,7 @@ class CompletedTrade(peewee.Model):
|
||||
setattr(self, a, attributes[a])
|
||||
|
||||
def toJSON(self) -> dict:
|
||||
orders = []
|
||||
for o in self.orders:
|
||||
orders.append(o.__dict__)
|
||||
orders = [o.__dict__ for o in self.orders]
|
||||
return {
|
||||
"id": self.id,
|
||||
"strategy_name": self.strategy_name,
|
||||
|
||||
@@ -114,13 +114,14 @@ class FuturesExchange(Exchange):
|
||||
base_asset = jh.base_asset(order.symbol)
|
||||
|
||||
# make sure we don't spend more than we're allowed considering current allowed leverage
|
||||
if order.type != order_types.MARKET or skip_market_order:
|
||||
if not order.is_reduce_only:
|
||||
order_size = abs(order.qty * order.price)
|
||||
remaining_margin = self.available_margin()
|
||||
if order_size > remaining_margin:
|
||||
raise InsufficientMargin(
|
||||
f'You cannot submit an order for ${round(order_size)} when your margin balance is ${round(remaining_margin)}')
|
||||
if (
|
||||
order.type != order_types.MARKET or skip_market_order
|
||||
) and not order.is_reduce_only:
|
||||
order_size = abs(order.qty * order.price)
|
||||
remaining_margin = self.available_margin()
|
||||
if order_size > remaining_margin:
|
||||
raise InsufficientMargin(
|
||||
f'You cannot submit an order for ${round(order_size)} when your margin balance is ${round(remaining_margin)}')
|
||||
|
||||
# skip market order at the time of submission because we don't have
|
||||
# the exact order.price. Instead, we call on_order_submission() one
|
||||
|
||||
@@ -179,7 +179,10 @@ class Position:
|
||||
if jh.is_livetrading():
|
||||
return self._liquidation_price
|
||||
|
||||
if self.mode == 'isolated':
|
||||
if self.mode in ['cross', 'spot']:
|
||||
return np.nan
|
||||
|
||||
elif self.mode == 'isolated':
|
||||
if self.type == 'long':
|
||||
return self.entry_price * (1 - self._initial_margin_rate + 0.004)
|
||||
elif self.type == 'short':
|
||||
@@ -187,12 +190,6 @@ class Position:
|
||||
else:
|
||||
return np.nan
|
||||
|
||||
elif self.mode == 'cross':
|
||||
return np.nan
|
||||
|
||||
elif self.mode == 'spot':
|
||||
return np.nan
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ def store_orderbook_into_db(exchange: str, symbol: str, orderbook: np.ndarray) -
|
||||
|
||||
|
||||
def fetch_candles_from_db(exchange: str, symbol: str, start_date: int, finish_date: int) -> tuple:
|
||||
candles_tuple = tuple(
|
||||
return tuple(
|
||||
Candle.select(
|
||||
Candle.timestamp, Candle.open, Candle.close, Candle.high, Candle.low,
|
||||
Candle.volume
|
||||
@@ -195,5 +195,3 @@ def fetch_candles_from_db(exchange: str, symbol: str, start_date: int, finish_da
|
||||
Candle.symbol == symbol
|
||||
).order_by(Candle.timestamp.asc()).tuples()
|
||||
)
|
||||
|
||||
return candles_tuple
|
||||
|
||||
@@ -159,8 +159,7 @@ def _get_candles_from_backup_exchange(exchange: str, backup_driver: CandleExchan
|
||||
# try fetching from market now
|
||||
days_count = jh.date_diff_in_days(jh.timestamp_to_arrow(start_timestamp), jh.timestamp_to_arrow(end_timestamp))
|
||||
# make sure it's rounded up so that we import maybe more candles, but not less
|
||||
if days_count < 1:
|
||||
days_count = 1
|
||||
days_count = max(days_count, 1)
|
||||
if type(days_count) is float and not days_count.is_integer():
|
||||
days_count = math.ceil(days_count)
|
||||
candles_count = days_count * 1440
|
||||
@@ -237,9 +236,8 @@ def _get_candles_from_backup_exchange(exchange: str, backup_driver: CandleExchan
|
||||
return total_candles
|
||||
|
||||
|
||||
def _fill_absent_candles(temp_candles: List[Dict[str, Union[str, Any]]], start_timestamp: int, end_timestamp: int) -> \
|
||||
List[Dict[str, Union[str, Any]]]:
|
||||
if len(temp_candles) == 0:
|
||||
def _fill_absent_candles(temp_candles: List[Dict[str, Union[str, Any]]], start_timestamp: int, end_timestamp: int) -> List[Dict[str, Union[str, Any]]]:
|
||||
if not temp_candles:
|
||||
raise CandleNotFoundInExchange(
|
||||
f'No candles exists in the market for this day: {jh.timestamp_to_time(start_timestamp)[:10]} \n'
|
||||
'Try another start_date'
|
||||
@@ -252,8 +250,7 @@ def _fill_absent_candles(temp_candles: List[Dict[str, Union[str, Any]]], start_t
|
||||
started = False
|
||||
loop_length = ((end_timestamp - start_timestamp) / 60000) + 1
|
||||
|
||||
i = 0
|
||||
while i < loop_length:
|
||||
for _ in range(loop_length):
|
||||
candle_for_timestamp = pydash.find(
|
||||
temp_candles, lambda c: c['timestamp'] == start_timestamp)
|
||||
|
||||
@@ -289,8 +286,6 @@ def _fill_absent_candles(temp_candles: List[Dict[str, Union[str, Any]]], start_t
|
||||
candles.append(candle_for_timestamp)
|
||||
|
||||
start_timestamp += 60000
|
||||
i += 1
|
||||
|
||||
return candles
|
||||
|
||||
|
||||
|
||||
@@ -43,9 +43,7 @@ class Binance(CandleExchange):
|
||||
# since the first timestamp doesn't include all the 1m
|
||||
# candles, let's start since the second day then
|
||||
first_timestamp = int(data[0][0])
|
||||
second_timestamp = first_timestamp + 60_000 * 1440
|
||||
|
||||
return second_timestamp
|
||||
return first_timestamp + 60_000 * 1440
|
||||
|
||||
def fetch(self, symbol, start_timestamp):
|
||||
"""
|
||||
@@ -76,10 +74,7 @@ class Binance(CandleExchange):
|
||||
if response.status_code == 400:
|
||||
raise ValueError(response.json()['msg'])
|
||||
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -89,6 +84,4 @@ class Binance(CandleExchange):
|
||||
'high': float(d[2]),
|
||||
'low': float(d[3]),
|
||||
'volume': float(d[5])
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
@@ -46,9 +46,7 @@ class BinanceFutures(CandleExchange):
|
||||
# since the first timestamp doesn't include all the 1m
|
||||
# candles, let's start since the second day then
|
||||
first_timestamp = int(data[0][0])
|
||||
second_timestamp = first_timestamp + 60_000 * 1440
|
||||
|
||||
return second_timestamp
|
||||
return first_timestamp + 60_000 * 1440
|
||||
|
||||
def fetch(self, symbol, start_timestamp):
|
||||
"""
|
||||
@@ -81,10 +79,7 @@ class BinanceFutures(CandleExchange):
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -94,6 +89,4 @@ class BinanceFutures(CandleExchange):
|
||||
'high': float(d[2]),
|
||||
'low': float(d[3]),
|
||||
'volume': float(d[5])
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
@@ -44,9 +44,7 @@ class BinanceInverseFutures(CandleExchange):
|
||||
# since the first timestamp doesn't include all the 1m
|
||||
# candles, let's start since the second day then
|
||||
first_timestamp = int(data[0][0])
|
||||
second_timestamp = first_timestamp + 60_000 * 1440
|
||||
|
||||
return second_timestamp
|
||||
return first_timestamp + 60_000 * 1440
|
||||
|
||||
def fetch(self, symbol, start_timestamp):
|
||||
"""
|
||||
@@ -77,10 +75,7 @@ class BinanceInverseFutures(CandleExchange):
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -90,9 +85,7 @@ class BinanceInverseFutures(CandleExchange):
|
||||
'high': float(d[2]),
|
||||
'low': float(d[3]),
|
||||
'volume': float(d[5])
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
|
||||
def encode_symbol(symbol: str) -> str:
|
||||
|
||||
@@ -49,9 +49,7 @@ class Bitfinex(CandleExchange):
|
||||
# since the first timestamp doesn't include all the 1m
|
||||
# candles, let's start since the second day then
|
||||
first_timestamp = int(data[0][0])
|
||||
second_timestamp = first_timestamp + 60_000 * 1440
|
||||
|
||||
return second_timestamp
|
||||
return first_timestamp + 60_000 * 1440
|
||||
|
||||
def fetch(self, symbol: str, start_timestamp):
|
||||
# since Bitfinex API skips candles with "volume=0", we have to send end_timestamp
|
||||
@@ -73,10 +71,7 @@ class Bitfinex(CandleExchange):
|
||||
)
|
||||
|
||||
data = response.json()
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -86,6 +81,4 @@ class Bitfinex(CandleExchange):
|
||||
'high': d[3],
|
||||
'low': d[4],
|
||||
'volume': d[5]
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
@@ -57,10 +57,7 @@ class Coinbase(CandleExchange):
|
||||
self._handle_errors(response)
|
||||
|
||||
data = response.json()
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -70,9 +67,7 @@ class Coinbase(CandleExchange):
|
||||
'high': float(d[2]),
|
||||
'low': float(d[1]),
|
||||
'volume': float(d[5])
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
@staticmethod
|
||||
def _handle_errors(response):
|
||||
|
||||
@@ -46,9 +46,7 @@ class TestnetBinanceFutures(CandleExchange):
|
||||
# since the first timestamp doesn't include all the 1m
|
||||
# candles, let's start since the second day then
|
||||
first_timestamp = int(data[0][0])
|
||||
second_timestamp = first_timestamp + 60_000 * 1440
|
||||
|
||||
return second_timestamp
|
||||
return first_timestamp + 60_000 * 1440
|
||||
|
||||
def fetch(self, symbol, start_timestamp):
|
||||
end_timestamp = start_timestamp + (self.count - 1) * 60000
|
||||
@@ -77,10 +75,7 @@ class TestnetBinanceFutures(CandleExchange):
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
return [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
@@ -90,6 +85,4 @@ class TestnetBinanceFutures(CandleExchange):
|
||||
'high': float(d[2]),
|
||||
'low': float(d[3]),
|
||||
'volume': float(d[5])
|
||||
})
|
||||
|
||||
return candles
|
||||
} for d in data]
|
||||
|
||||
@@ -42,11 +42,7 @@ class Genetics(ABC):
|
||||
self.fitness_goal = fitness_goal
|
||||
self.cpu_cores = 0
|
||||
|
||||
if options is None:
|
||||
self.options = {}
|
||||
else:
|
||||
self.options = options
|
||||
|
||||
self.options = {} if options is None else options
|
||||
os.makedirs('./storage/temp/optimize', exist_ok=True)
|
||||
self.temp_path = f"./storage/temp/optimize/{self.options['strategy_name']}-{self.options['exchange']}-{self.options['symbol']}-{self.options['timeframe']}-{self.options['start_date']}-{self.options['finish_date']}.pickle"
|
||||
|
||||
@@ -54,9 +50,10 @@ class Genetics(ABC):
|
||||
raise ValueError('fitness scores must be between 0 and 1')
|
||||
|
||||
# if temp file exists, load data to resume previous session
|
||||
if jh.file_exists(self.temp_path):
|
||||
if click.confirm('Previous session detected. Do you want to resume?', default=True):
|
||||
self.load_progress()
|
||||
if jh.file_exists(self.temp_path) and click.confirm(
|
||||
'Previous session detected. Do you want to resume?', default=True
|
||||
):
|
||||
self.load_progress()
|
||||
|
||||
@abstractmethod
|
||||
def fitness(self, dna: str) -> tuple:
|
||||
@@ -176,13 +173,11 @@ class Genetics(ABC):
|
||||
mommy = self.select_person()
|
||||
daddy = self.select_person()
|
||||
|
||||
dna = ''
|
||||
dna = ''.join(
|
||||
daddy['dna'][i] if i % 2 == 0 else mommy['dna'][i]
|
||||
for i in range(self.solution_len)
|
||||
)
|
||||
|
||||
for i in range(self.solution_len):
|
||||
if i % 2 == 0:
|
||||
dna += daddy['dna'][i]
|
||||
else:
|
||||
dna += mommy['dna'][i]
|
||||
|
||||
fitness_score, fitness_log_training, fitness_log_testing = self.fitness(dna)
|
||||
|
||||
@@ -196,10 +191,7 @@ class Genetics(ABC):
|
||||
def select_person(self) -> Dict[str, Union[str, Any]]:
|
||||
# len(self.population) instead of self.population_size because some DNAs might not have been created due errors
|
||||
random_index = np.random.choice(len(self.population), int(len(self.population) / 100), replace=False)
|
||||
chosen_ones = []
|
||||
|
||||
for r in random_index:
|
||||
chosen_ones.append(self.population[r])
|
||||
chosen_ones = [self.population[r] for r in random_index]
|
||||
|
||||
return pydash.max_by(chosen_ones, 'fitness')
|
||||
|
||||
@@ -465,7 +457,6 @@ class Genetics(ABC):
|
||||
snapshots = {"snapshots": []}
|
||||
snapshots["snapshots"].append(dnas_json['snapshot'])
|
||||
json.dump(snapshots, file, ensure_ascii=False)
|
||||
file.write('\n')
|
||||
else:
|
||||
# file exists - append
|
||||
file.seek(0)
|
||||
@@ -473,4 +464,4 @@ class Genetics(ABC):
|
||||
data["snapshots"].append(dnas_json['snapshot'])
|
||||
file.seek(0)
|
||||
json.dump(data, file, ensure_ascii=False)
|
||||
file.write('\n')
|
||||
file.write('\n')
|
||||
|
||||
@@ -110,9 +110,7 @@ class Optimizer(Genetics):
|
||||
if store.completed_trades.count > 5:
|
||||
training_data = stats.trades(store.completed_trades.trades, store.app.daily_balance)
|
||||
total_effect_rate = log10(training_data['total']) / log10(self.optimal_total)
|
||||
if total_effect_rate > 1:
|
||||
total_effect_rate = 1
|
||||
|
||||
total_effect_rate = min(total_effect_rate, 1)
|
||||
ratio_config = jh.get_config('env.optimization.ratio', 'sharpe')
|
||||
if ratio_config == 'sharpe':
|
||||
ratio = training_data['sharpe_ratio']
|
||||
|
||||
@@ -29,9 +29,8 @@ def run(dna: bool = False) -> None:
|
||||
if not dna:
|
||||
table.multi_value(arr)
|
||||
print('\n')
|
||||
else:
|
||||
if not translated_DNAs_count:
|
||||
print('No DNA string found.')
|
||||
elif not translated_DNAs_count:
|
||||
print('No DNA string found.')
|
||||
|
||||
# extra_candles
|
||||
if not dna:
|
||||
|
||||
@@ -5,23 +5,22 @@ from jesse.store import store
|
||||
|
||||
|
||||
def save_daily_portfolio_balance() -> None:
|
||||
balances = []
|
||||
# # store daily_balance of assets into database
|
||||
# if jh.is_livetrading():
|
||||
# for asset_key, asset_value in e.assets.items():
|
||||
# store_daily_balance_into_db({
|
||||
# 'id': jh.generate_unique_id(),
|
||||
# 'timestamp': jh.now(),
|
||||
# 'identifier': jh.get_config('env.identifier', 'main'),
|
||||
# 'exchange': e.name,
|
||||
# 'asset': asset_key,
|
||||
# 'balance': asset_value,
|
||||
# })
|
||||
balances = [
|
||||
e.assets[jh.app_currency()]
|
||||
for key, e in store.exchanges.storage.items()
|
||||
]
|
||||
|
||||
# add exchange balances
|
||||
for key, e in store.exchanges.storage.items():
|
||||
balances.append(e.assets[jh.app_currency()])
|
||||
|
||||
# # store daily_balance of assets into database
|
||||
# if jh.is_livetrading():
|
||||
# for asset_key, asset_value in e.assets.items():
|
||||
# store_daily_balance_into_db({
|
||||
# 'id': jh.generate_unique_id(),
|
||||
# 'timestamp': jh.now(),
|
||||
# 'identifier': jh.get_config('env.identifier', 'main'),
|
||||
# 'exchange': e.name,
|
||||
# 'asset': asset_key,
|
||||
# 'balance': asset_value,
|
||||
# })
|
||||
|
||||
# add open position values
|
||||
for key, pos in store.positions.storage.items():
|
||||
|
||||
@@ -30,10 +30,7 @@ def init() -> None:
|
||||
|
||||
|
||||
def store_candles(candles: np.ndarray, exchange: str, symbol: str) -> None:
|
||||
arr = []
|
||||
|
||||
for c in candles:
|
||||
arr.append({
|
||||
arr = [{
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': exchange,
|
||||
@@ -43,6 +40,6 @@ def store_candles(candles: np.ndarray, exchange: str, symbol: str) -> None:
|
||||
'high': c[3],
|
||||
'low': c[4],
|
||||
'volume': c[5]
|
||||
})
|
||||
} for c in candles]
|
||||
|
||||
store_candles_from_list(arr)
|
||||
|
||||
@@ -30,11 +30,11 @@ def print_candle(candle: np.ndarray, is_partial: bool, symbol: str) -> None:
|
||||
if jh.should_execute_silently():
|
||||
return
|
||||
|
||||
if is_bullish(candle) and is_partial is True:
|
||||
if is_bullish(candle) and is_partial:
|
||||
candle_form = click.style(' ==', fg='green')
|
||||
elif is_bullish(candle) and is_partial is False:
|
||||
elif is_bullish(candle) and not is_partial:
|
||||
candle_form = click.style('====', bg='green')
|
||||
elif is_bearish(candle) and is_partial is True:
|
||||
elif is_bearish(candle) and is_partial:
|
||||
candle_form = click.style(' ==', fg='red')
|
||||
else:
|
||||
candle_form = click.style('====', bg='red')
|
||||
|
||||
@@ -26,10 +26,7 @@ def candles(candles_array: np.ndarray) -> List[List[str]]:
|
||||
|
||||
|
||||
def routes(routes: List[Any]) -> List[Union[List[str], List[Any]]]:
|
||||
array = []
|
||||
|
||||
# header
|
||||
array.append(['exchange', 'symbol', 'timeframe', 'strategy', 'DNA'])
|
||||
array = [['exchange', 'symbol', 'timeframe', 'strategy', 'DNA']]
|
||||
|
||||
for r in routes:
|
||||
array.append([
|
||||
@@ -51,7 +48,7 @@ def trades(trades_list: list, daily_balance: list) -> dict:
|
||||
starting_balance += store.exchanges.storage[e].starting_assets[jh.app_currency()]
|
||||
current_balance += store.exchanges.storage[e].assets[jh.app_currency()]
|
||||
|
||||
if len(trades_list) == 0:
|
||||
if not trades_list:
|
||||
return None
|
||||
|
||||
df = pd.DataFrame.from_records([t.to_dict() for t in trades_list])
|
||||
@@ -72,7 +69,7 @@ def trades(trades_list: list, daily_balance: list) -> dict:
|
||||
losing_streak = 0 if s_min > 0 else abs(s_min)
|
||||
|
||||
s_max = current_streak.max()
|
||||
winning_streak = 0 if s_max < 0 else s_max
|
||||
winning_streak = max(s_max, 0)
|
||||
|
||||
largest_losing_trade = df['PNL'].min()
|
||||
largest_winning_trade = df['PNL'].max()
|
||||
|
||||
@@ -18,12 +18,21 @@ warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def positions() -> List[Union[List[str], List[Union[Union[str, int, None], Any]]]]:
|
||||
array = []
|
||||
array = [
|
||||
[
|
||||
'type',
|
||||
'strategy',
|
||||
'symbol',
|
||||
'leverage',
|
||||
'opened at',
|
||||
'qty',
|
||||
'entry',
|
||||
'current price',
|
||||
'liq price',
|
||||
'PNL (%)',
|
||||
]
|
||||
]
|
||||
|
||||
# headers
|
||||
array.append([
|
||||
'type', 'strategy', 'symbol', 'leverage', 'opened at', 'qty', 'entry', 'current price', 'liq price', 'PNL (%)'
|
||||
])
|
||||
|
||||
for r in router.routes:
|
||||
pos = r.strategy.position
|
||||
@@ -232,15 +241,15 @@ def portfolio_metrics() -> List[
|
||||
|
||||
|
||||
def info() -> List[List[Union[str, Any]]]:
|
||||
array = []
|
||||
|
||||
for w in store.logs.info[::-1][0:5]:
|
||||
array.append(
|
||||
[
|
||||
jh.timestamp_to_time(w['time'])[11:19],
|
||||
f"{w['message'][:70]}.." if len(w['message']) > 70 else w['message']
|
||||
])
|
||||
return array
|
||||
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][0:5]
|
||||
]
|
||||
|
||||
|
||||
def watch_list() -> Optional[Any]:
|
||||
@@ -260,19 +269,31 @@ def watch_list() -> Optional[Any]:
|
||||
|
||||
|
||||
def errors() -> List[List[Union[str, Any]]]:
|
||||
array = []
|
||||
|
||||
for w in store.logs.errors[::-1][0:5]:
|
||||
array.append([jh.timestamp_to_time(w['time'])[11:19],
|
||||
f"{w['message'][:70]}.." if len(w['message']) > 70 else w['message']])
|
||||
return array
|
||||
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][0:5]
|
||||
]
|
||||
|
||||
|
||||
def orders() -> List[Union[List[str], List[Union[CharField, str, FloatField]]]]:
|
||||
array = []
|
||||
array = [
|
||||
[
|
||||
'symbol',
|
||||
'side',
|
||||
'type',
|
||||
'qty',
|
||||
'price',
|
||||
'flag',
|
||||
'status',
|
||||
'created_at',
|
||||
]
|
||||
]
|
||||
|
||||
# headers
|
||||
array.append(['symbol', 'side', 'type', 'qty', 'price', 'flag', 'status', 'created_at'])
|
||||
|
||||
route_orders = []
|
||||
for r in router.routes:
|
||||
|
||||
@@ -41,10 +41,14 @@ def get_strategy(exchange: str, symbol: str) -> Any:
|
||||
|
||||
def get_route(exchange: str, symbol: str) -> Optional[Any]:
|
||||
from jesse.routes import router
|
||||
r = next(
|
||||
(r for r in router.routes if r.exchange == exchange and r.symbol == symbol),
|
||||
None)
|
||||
return r
|
||||
return next(
|
||||
(
|
||||
r
|
||||
for r in router.routes
|
||||
if r.exchange == exchange and r.symbol == symbol
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def get_all_trading_routes() -> List[Any]:
|
||||
|
||||
@@ -23,14 +23,10 @@ def generate(name: str) -> None:
|
||||
|
||||
shutil.copytree(f'{dirname}/ExampleStrategy', path)
|
||||
|
||||
# replace 'ExampleStrategy' with the name of the new strategy
|
||||
fin = open(f"{path}/__init__.py", "rt")
|
||||
data = fin.read()
|
||||
data = data.replace('ExampleStrategy', name)
|
||||
fin.close()
|
||||
fin = open(f"{path}/__init__.py", "wt")
|
||||
fin.write(data)
|
||||
fin.close()
|
||||
|
||||
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:
|
||||
fin.write(data)
|
||||
# output the location of generated strategy directory
|
||||
print(jh.color(f'Strategy created at: {path}', 'green'))
|
||||
|
||||
@@ -9,10 +9,7 @@ def key_value(data, title, alignments=None, uppercase_title=True) -> None:
|
||||
:param alignments:
|
||||
:param uppercase_title:
|
||||
"""
|
||||
table = []
|
||||
|
||||
for d in data:
|
||||
table.append(d)
|
||||
table = [d for d in data]
|
||||
|
||||
if alignments is None:
|
||||
print(tabulate(table, headers=[title.upper() if uppercase_title else title, ''], tablefmt="presto"))
|
||||
|
||||
@@ -5,10 +5,11 @@ from jesse.store import store
|
||||
|
||||
|
||||
def tradingview_logs(study_name: str) -> None:
|
||||
starting_balance = 0
|
||||
starting_balance = sum(
|
||||
store.exchanges.storage[e].starting_assets[jh.app_currency()]
|
||||
for e in store.exchanges.storage
|
||||
)
|
||||
|
||||
for e in store.exchanges.storage:
|
||||
starting_balance += store.exchanges.storage[e].starting_assets[jh.app_currency()]
|
||||
|
||||
tv_text = f'//@version=4\nstrategy("{study_name}", overlay=true, initial_capital={starting_balance}, commission_type=strategy.commission.percent, commission_value=0.2)\n'
|
||||
for i, t in enumerate(store.completed_trades.trades[::-1][:]):
|
||||
|
||||
@@ -32,10 +32,11 @@ def install_routes() -> None:
|
||||
|
||||
exchange = r.exchange
|
||||
symbol = r.symbol
|
||||
count = 0
|
||||
for ro in router.routes:
|
||||
if ro.exchange == exchange and ro.symbol == symbol:
|
||||
count += 1
|
||||
count = sum(
|
||||
ro.exchange == exchange and ro.symbol == symbol
|
||||
for ro in router.routes
|
||||
)
|
||||
|
||||
if count != 1:
|
||||
raise InvalidRoutes(
|
||||
'each exchange-symbol pair can be traded only once. \nMore info: https://docs.jesse.trade/docs/routes.html#trading-multiple-routes')
|
||||
|
||||
@@ -233,14 +233,12 @@ class CandlesState:
|
||||
short_count = len(self.get_storage(exchange, symbol, '1m'))
|
||||
|
||||
# complete candle
|
||||
if dif == 0:
|
||||
if long_count == 0:
|
||||
return np.zeros((0, 6))
|
||||
else:
|
||||
return self.storage[long_key][-1]
|
||||
# generate forming
|
||||
else:
|
||||
if dif != 0:
|
||||
return generate_candle_from_one_minutes(
|
||||
timeframe, self.storage[short_key][short_count - dif:short_count],
|
||||
True
|
||||
)
|
||||
if long_count == 0:
|
||||
return np.zeros((0, 6))
|
||||
else:
|
||||
return self.storage[long_key][-1]
|
||||
|
||||
@@ -43,11 +43,7 @@ class OrdersState:
|
||||
def count_active_orders(self, exchange: str, symbol: str) -> int:
|
||||
orders = self.get_orders(exchange, symbol)
|
||||
|
||||
c = 0
|
||||
for o in orders:
|
||||
if o.is_active:
|
||||
c += 1
|
||||
return c
|
||||
return sum(bool(o.is_active) for o in orders)
|
||||
|
||||
def count(self, exchange: str, symbol: str) -> int:
|
||||
return len(self.get_orders(exchange, symbol))
|
||||
|
||||
@@ -22,9 +22,10 @@ class TradesState:
|
||||
|
||||
def add_trade(self, trade: np.ndarray, exchange: str, symbol: str) -> None:
|
||||
key = jh.key(exchange, symbol)
|
||||
if not len(self.temp_storage[key]) or trade[0] - self.temp_storage[key][0][0] < 1000:
|
||||
self.temp_storage[key].append(trade)
|
||||
else:
|
||||
if (
|
||||
len(self.temp_storage[key])
|
||||
and trade[0] - self.temp_storage[key][0][0] >= 1000
|
||||
):
|
||||
arr = self.temp_storage[key]
|
||||
buy_arr = np.array(list(filter(lambda x: x[3] == 1, arr)))
|
||||
sell_arr = np.array(list(filter(lambda x: x[3] == 0, arr)))
|
||||
@@ -50,7 +51,7 @@ class TradesState:
|
||||
self.storage[key].append(generated)
|
||||
|
||||
self.temp_storage[key].flush()
|
||||
self.temp_storage[key].append(trade)
|
||||
self.temp_storage[key].append(trade)
|
||||
|
||||
def get_trades(self, exchange: str, symbol: str) -> List[Trade]:
|
||||
key = jh.key(exchange, symbol)
|
||||
|
||||
@@ -71,11 +71,10 @@ class Strategy(ABC):
|
||||
self.position = selectors.get_position(self.exchange, self.symbol)
|
||||
self.broker = Broker(self.position, self.exchange, self.symbol, self.timeframe)
|
||||
|
||||
if self.hp is None:
|
||||
if len(self.hyperparameters()) > 0:
|
||||
self.hp = {}
|
||||
for dna in self.hyperparameters():
|
||||
self.hp[dna['name']] = dna['default']
|
||||
if self.hp is None and len(self.hyperparameters()) > 0:
|
||||
self.hp = {}
|
||||
for dna in self.hyperparameters():
|
||||
self.hp[dna['name']] = dna['default']
|
||||
|
||||
@property
|
||||
def _price_precision(self):
|
||||
@@ -592,11 +591,13 @@ class Strategy(ABC):
|
||||
raise
|
||||
|
||||
# validations: stop-loss and take-profit should not be the same
|
||||
if self.position.is_open:
|
||||
if (self.stop_loss is not None and self.take_profit is not None) and np.array_equal(self.stop_loss,
|
||||
self.take_profit):
|
||||
raise exceptions.InvalidStrategy(
|
||||
'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.')
|
||||
if (
|
||||
self.position.is_open
|
||||
and (self.stop_loss is not None and self.take_profit is not None)
|
||||
and np.array_equal(self.stop_loss, self.take_profit)
|
||||
):
|
||||
raise exceptions.InvalidStrategy(
|
||||
'stop-loss and take-profit should not be exactly the same. Just use either one of them and it will do.')
|
||||
|
||||
def update_position(self):
|
||||
pass
|
||||
@@ -1006,13 +1007,11 @@ class Strategy(ABC):
|
||||
"""
|
||||
Returns all the metrics of the strategy.
|
||||
"""
|
||||
if self.trades_count in self._cached_metrics:
|
||||
return self._cached_metrics[self.trades_count]
|
||||
else:
|
||||
if self.trades_count not in self._cached_metrics:
|
||||
self._cached_metrics[self.trades_count] = metrics.trades(
|
||||
store.completed_trades.trades, store.app.daily_balance
|
||||
)
|
||||
return self._cached_metrics[self.trades_count]
|
||||
return self._cached_metrics[self.trades_count]
|
||||
|
||||
@property
|
||||
def time(self) -> int:
|
||||
|
||||
@@ -8,8 +8,7 @@ class Test30(Strategy):
|
||||
if self.price == 20:
|
||||
self.buy = 1, self.price
|
||||
|
||||
# decrease position size
|
||||
if self.price == 50:
|
||||
elif self.price == 50:
|
||||
self.take_profit = 1, self.price
|
||||
|
||||
# close position with take_profit
|
||||
|
||||
@@ -7,7 +7,7 @@ class TestBeforeMethod(Strategy):
|
||||
if self.index == 0:
|
||||
assert self.vars['counter'] == 10
|
||||
|
||||
if self.index == 2:
|
||||
elif self.index == 2:
|
||||
assert self.vars['counter'] == 100
|
||||
|
||||
return False
|
||||
@@ -28,5 +28,5 @@ class TestBeforeMethod(Strategy):
|
||||
if self.index == 0:
|
||||
self.vars['counter'] = 10
|
||||
|
||||
if self.index == 2:
|
||||
elif self.index == 2:
|
||||
self.vars['counter'] = 100
|
||||
|
||||
@@ -29,10 +29,7 @@ class TestLiquidationInCrossModeForShortTrade(Strategy):
|
||||
return False
|
||||
|
||||
def should_short(self) -> bool:
|
||||
if self.price == 20:
|
||||
return True
|
||||
|
||||
return False
|
||||
return self.price == 20
|
||||
|
||||
def go_long(self):
|
||||
pass
|
||||
|
||||
@@ -66,15 +66,10 @@ def crossed(series1: np.ndarray, series2: Union[float, int, np.ndarray], directi
|
||||
cross_below = np.logical_and(series1 < series2, series1_shifted >= series2_shifted)
|
||||
|
||||
if direction is None:
|
||||
cross_any = np.logical_or(cross_above, cross_below)
|
||||
return cross_any
|
||||
return np.logical_or(cross_above, cross_below)
|
||||
|
||||
if direction == "above":
|
||||
return cross_above
|
||||
else:
|
||||
return cross_below
|
||||
else:
|
||||
if not type(series2) is np.ndarray:
|
||||
if type(series2) is not np.ndarray:
|
||||
series2 = np.array([series2, series2])
|
||||
|
||||
if direction is None or direction == "above":
|
||||
@@ -85,10 +80,10 @@ def crossed(series1: np.ndarray, series2: Union[float, int, np.ndarray], directi
|
||||
if direction is None:
|
||||
return cross_above or cross_below
|
||||
|
||||
if direction == "above":
|
||||
return cross_above
|
||||
else:
|
||||
return cross_below
|
||||
if direction == "above":
|
||||
return cross_above
|
||||
else:
|
||||
return cross_below
|
||||
|
||||
|
||||
def estimate_risk(entry_price: float, stop_price: float) -> float:
|
||||
@@ -201,7 +196,7 @@ def size_to_qty(position_size: float, entry_price: float, precision: int = 3, fe
|
||||
raise TypeError()
|
||||
|
||||
if fee_rate != 0:
|
||||
position_size = position_size * (1 - fee_rate * 3)
|
||||
position_size *= 1 - fee_rate * 3
|
||||
|
||||
return jh.floor_with_precision(position_size / entry_price, precision)
|
||||
|
||||
@@ -250,8 +245,9 @@ def streaks(series: np.ndarray, use_diff=True) -> np.ndarray:
|
||||
streak = np.where(series >= 0, pos - np.maximum.accumulate(np.where(series <= 0, pos, 0)),
|
||||
-neg + np.maximum.accumulate(np.where(series >= 0, neg, 0)))
|
||||
|
||||
res = np.concatenate((np.full((series.shape[0] - streak.shape[0]), np.nan), streak))
|
||||
return res
|
||||
return np.concatenate(
|
||||
(np.full((series.shape[0] - streak.shape[0]), np.nan), streak)
|
||||
)
|
||||
|
||||
|
||||
def signal_line(series: np.ndarray, period: int = 10, matype: int = 0) -> np.ndarray:
|
||||
|
||||
Reference in New Issue
Block a user