Sourcery optimize

This commit is contained in:
Markus
2021-07-25 11:00:50 +02:00
parent a661f20a63
commit eb6809f8a2
51 changed files with 265 additions and 352 deletions

View File

@@ -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:

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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]

View File

@@ -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

View File

@@ -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:

View File

@@ -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]

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -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:

View File

@@ -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]

View File

@@ -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):

View File

@@ -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]

View File

@@ -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')

View File

@@ -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']

View File

@@ -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:

View File

@@ -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():

View File

@@ -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)

View File

@@ -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')

View File

@@ -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()

View File

@@ -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:

View File

@@ -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]:

View File

@@ -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'))

View File

@@ -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"))

View File

@@ -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][:]):

View File

@@ -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')

View File

@@ -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]

View File

@@ -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))

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: