Skip to content

Commit

Permalink
Merge branch 'dashboard-v1'
Browse files Browse the repository at this point in the history
  • Loading branch information
saleh-mir committed Jul 25, 2024
2 parents f3fd2ab + bf57cf4 commit 89dd558
Show file tree
Hide file tree
Showing 116 changed files with 1,905 additions and 714 deletions.
226 changes: 164 additions & 62 deletions jesse/__init__.py

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions jesse/controllers/exchange_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from starlette.responses import JSONResponse

from jesse.modes.import_candles_mode import CandleExchange
from jesse.modes.import_candles_mode.drivers import drivers, driver_names


def get_exchange_supported_symbols(exchange: str) -> JSONResponse:
arr = []

try:
driver: CandleExchange = drivers[exchange]()
except KeyError:
raise ValueError(f'{exchange} is not a supported exchange. Supported exchanges are: {driver_names}')

try:
arr = driver.get_available_symbols()
except Exception as e:
return JSONResponse({
'error': str(e)
}, status_code=500)

return JSONResponse({
'data': arr
}, status_code=200)
4 changes: 3 additions & 1 deletion jesse/exceptions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class SymbolNotFound(Exception):


class RouteNotFound(Exception):
pass
def __init__(self, symbol, timeframe):
message = f"Date route is required but missing: symbol='{symbol}', timeframe='{timeframe}'"
super().__init__(message)


class InvalidRoutes(Exception):
Expand Down
41 changes: 39 additions & 2 deletions jesse/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,33 @@ def dashy_symbol(symbol: str) -> str:
if compare_symbol == symbol:
return s

if symbol.endswith('EUR'):
return symbol[:-3] + '-EUR'
if symbol.endswith('EUT'):
return symbol[:-3] + '-EUT'
if symbol.endswith('GBP'):
return symbol[:-3] + '-GBP'
if symbol.endswith('JPY'):
return symbol[:-3] + '-JPY'
if symbol.endswith('MIM'):
return symbol[:-3] + '-MIM'
if symbol.endswith('TRY'):
return symbol[:-3] + '-TRY'
if symbol.endswith('USD'):
return symbol[:-3] + '-USD'
if symbol.endswith('UST'):
return symbol[:-3] + '-UST'
if symbol.endswith('USDT'):
return symbol[:-4] + '-USDT'
if symbol.endswith('USDC'):
return symbol[:-4] + '-USDC'
if symbol.endswith('USDS'):
return symbol[:-4] + '-USDS'
if symbol.endswith('USDP'):
return symbol[:-4] + '-USDP'
if symbol.endswith('USDU'):
return symbol[:-4] + '-USDU'

if len(symbol) > 7 and symbol.endswith('SUSDT'):
# ex: SETHSUSDT => SETH-SUSDT
return symbol[:-5] + '-' + symbol[-5:]
Expand Down Expand Up @@ -385,7 +412,7 @@ def is_paper_trading() -> bool:


def is_unit_testing() -> bool:
"""Returns True if the code is running by running pytest, False otherwise."""
"""Returns True if the code is running by running pytest or PyCharm's test runner, False otherwise."""
# Check if the PYTEST_CURRENT_TEST environment variable is set.
if os.environ.get("PYTEST_CURRENT_TEST"):
return True
Expand All @@ -395,7 +422,11 @@ def is_unit_testing() -> bool:
if script_name in ["pytest", "py.test"]:
return True

# Otherwise, the code is not running by running pytest.
# Check if the code is being executed from PyCharm's test runner.
if os.environ.get("PYCHARM_HOSTED"):
return True

# Otherwise, the code is not running by running pytest or PyCharm's test runner.
return False


Expand Down Expand Up @@ -469,6 +500,11 @@ def now_to_timestamp(force_fresh=False) -> int:
return arrow.utcnow().int_timestamp * 1000


# for use with peewee
def now_to_datetime():
return arrow.utcnow().datetime


def current_1m_candle_timestamp():
return arrow.utcnow().floor('minute').int_timestamp * 1000

Expand Down Expand Up @@ -767,6 +803,7 @@ def _print_error(msg: str) -> None:
print('\n')
print(color('========== critical error =========='.upper(), 'red'))
print(color(msg, 'red'))
print(color('====================================', 'red'))

@lru_cache
def timeframe_to_one_minutes(timeframe: str) -> int:
Expand Down
2 changes: 2 additions & 0 deletions jesse/indicators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@
from .stiffness import stiffness
from .ttm_squeeze import ttm_squeeze
from .support_resistance_with_break import support_resistance_with_breaks
from .squeeze_momentum import squeeze_momentum
from .hull_suit import hull_suit
60 changes: 60 additions & 0 deletions jesse/indicators/hull_suit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from collections import namedtuple

from .wma import wma
from .ema import ema
import numpy as np
from jesse.helpers import get_candle_source, slice_candles

HullSuit = namedtuple('HullSuit', ['s_hull', 'm_hull', 'signal'])


def hull_suit(candles: np.ndarray, mode_switch: str = 'Hma', length: int = 55, length_mult: float = 1.0, source_type: str = 'close', sequential: bool = False) -> HullSuit:
"""
@author InSilico
credits: https://www.tradingview.com/script/hg92pFwS-Hull-Suite/
HullSuit - Hull Suit
:param candles: np.ndarray
:param mode_switch: str - default: 'Hma'
:param length: int - default: 55
:param length_mult: float - default: 1.0
:param source_type: str - default: "closes"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
if len(candles.shape) == 1:
source = candles
else:
candles = slice_candles(candles, sequential)
source = get_candle_source(candles, source_type=source_type)

mode_len = int(length * length_mult)
if mode_switch == 'Hma':
mode = wma(2*wma(source, mode_len / 2, sequential=True) - wma(source,
mode_len, sequential=True), round(mode_len ** 0.5), sequential=True)
elif mode_switch == 'Ehma':
mode = ema(2*ema(source, mode_len / 2, sequential=True) - ema(source,
mode_len, sequential=True), round(mode_len ** 0.5), sequential=True)
elif mode_switch == 'Thma':
mode = wma(3*wma(source, mode_len / 6, sequential=True) - wma(source, mode_len / 4, sequential=True) -
wma(source, mode_len / 2, sequential=True), mode_len / 2, sequential=True)

s_hull = []
m_hull = []
signal = []
for i in range(len(mode)):
if i > 1:
s_hull.append(mode[i - 2])
m_hull.append(mode[i])
signal.append('buy' if mode[i - 2] < mode[i] else 'sell')
else:
s_hull.append(None)
m_hull.append(None)
signal.append(None)

if sequential:
return HullSuit(s_hull, m_hull, signal)
else:
return HullSuit(s_hull[-1], m_hull[-1], signal[-1])
90 changes: 90 additions & 0 deletions jesse/indicators/squeeze_momentum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from collections import namedtuple

from .bollinger_bands import bollinger_bands
from .sma import sma
from .trange import trange
from .linearreg import linearreg
from .stddev import stddev
import numpy as np

SqueezeMomentum = namedtuple('SqueezeMomentum', ['squeeze', 'momentum', 'momentum_signal'])


def squeeze_momentum(candles: np.ndarray, length: int = 20, mult: float = 2.0, length_kc: int = 20, mult_kc: float = 1.5, sequential: bool = True) -> SqueezeMomentum:
"""
@author lazyBear
credits: https://www.tradingview.com/script/nqQ1DT5a-Squeeze-Momentum-Indicator-LazyBear/
squeeze_momentum
:param candles: np.ndarray
:length: int - default: 20
:mult: float - default: 2.0
:length_kc: float - default: 2.0
:mult_kc: float - default: 1.5
:sequential: bool - default: True
:return: SqueezeMomentum(squeeze, momentum, momentum_signal)
"""
# calculate bollinger bands
basis = sma(candles, length, sequential=True)
dev = mult_kc * stddev(candles, length, sequential=True)
upper_bb = basis + dev
lower_bb = basis - dev

# calculate KC
ma = sma(candles, length_kc, sequential=True)
range_ma = sma(trange(candles, sequential=True), period=length_kc, sequential=True)
upper_kc = ma + range_ma * mult_kc
lower_kc = ma - range_ma * mult_kc

sqz = []
for i in range(len(lower_bb)):
sqz_on = (lower_bb[i] > lower_kc[i]) and (upper_bb[i] < upper_kc[i])
sqz_off = (lower_bb[i] < lower_kc[i]) and (upper_bb[i] > upper_kc[i])
noSqz = (sqz_on == False) and (sqz_off == False)
sqz.append(0 if noSqz else (-1 if sqz_on else 1))

highs = np.nan_to_num(_highest(candles[:, 3], length_kc), 0)
lows = np.nan_to_num(_lowest(candles[:, 4], length_kc), 0)
sma_arr = np.nan_to_num(sma(candles, period=length_kc, sequential=True))

momentum = []
for i in range(len(highs)):
momentum.append(candles[:, 2][i] - ((highs[i] + lows[i]) / 2 + sma_arr[i]) / 2)

momentum = linearreg(np.array(momentum), period=length_kc, sequential=True)

momentum_signal = []
for i in range(len(momentum) - 1):
if momentum[i + 1] > 0:
momentum_signal.append(1 if momentum[i + 1] > momentum[i] else 2)
else:
momentum_signal.append(-1 if momentum[i + 1] < momentum[i] else -2)

if sequential:
return SqueezeMomentum(sqz, momentum, momentum_signal)
else:
return SqueezeMomentum(sqz[-1], momentum[-1], momentum_signal[-1])


def _highest(values, length):
# Ensure values is a NumPy array for efficient computation
values = np.asarray(values)
# Initialize an array to hold the highest values
highest_values = np.full(values.shape, np.nan)
# Compute the highest value for each window
for i in range(length - 1, len(values)):
highest_values[i] = np.max(values[i - length + 1:i + 1])
return highest_values


def _lowest(values, length):
# Ensure values is a NumPy array for efficient computation
values = np.asarray(values)
# Initialize an array to hold the lowest values
lowest_values = np.full(values.shape, np.nan)
# Compute the lowest value for each window
for i in range(length - 1, len(values)):
lowest_values[i] = np.min(values[i - length + 1:i + 1])
return lowest_values
10 changes: 6 additions & 4 deletions jesse/indicators/wma.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


def wma(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
float, np.ndarray]:
"""
WMA - Weighted Moving Average
Expand All @@ -18,9 +18,11 @@ def wma(candles: np.ndarray, period: int = 30, source_type: str = "close", seque
:return: float | np.ndarray
"""
candles = slice_candles(candles, sequential)
if len(candles.shape) == 1:
source = candles
else:
candles = slice_candles(candles, sequential)
source = get_candle_source(candles, source_type=source_type)

source = get_candle_source(candles, source_type=source_type)
res = talib.WMA(source, timeperiod=period)

return res if sequential else res[-1]
35 changes: 21 additions & 14 deletions jesse/info.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
from jesse.enums import exchanges as exchanges_enums, timeframes

JESSE_API_URL = 'https://api1.jesse.trade/api'
# JESSE_API_URL = 'http://localhost:8040/api'
JESSE_WEBSITE_URL = 'https://jesse.trade'
# JESSE_WEBSITE_URL = 'http://localhost:8040'
# JESSE_API_URL = 'https://api1.jesse.trade/api'
JESSE_API_URL = 'http://localhost:8040/api'
# JESSE_WEBSITE_URL = 'https://jesse.trade'
JESSE_WEBSITE_URL = 'http://localhost:8040'

BYBIT_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1]
FTX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1]
BINANCE_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_8, timeframes.HOUR_12, timeframes.DAY_1]
COINBASE_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.HOUR_1, timeframes.HOUR_6, timeframes.DAY_1]
BITGET_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.HOUR_12, timeframes.DAY_1]
DYDX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1]
APEX_PRO_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1]
BYBIT_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30,
timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1]
FTX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30,
timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_12, timeframes.DAY_1]
BINANCE_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_3, timeframes.MINUTE_5, timeframes.MINUTE_15, timeframes.MINUTE_30,
timeframes.HOUR_1, timeframes.HOUR_2, timeframes.HOUR_4, timeframes.HOUR_6, timeframes.HOUR_8, timeframes.HOUR_12, timeframes.DAY_1]
COINBASE_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5,
timeframes.MINUTE_15, timeframes.HOUR_1, timeframes.HOUR_6, timeframes.DAY_1]
BITGET_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15,
timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.HOUR_12, timeframes.DAY_1]
DYDX_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15,
timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1]
APEX_PRO_TIMEFRAMES = [timeframes.MINUTE_1, timeframes.MINUTE_5, timeframes.MINUTE_15,
timeframes.MINUTE_30, timeframes.HOUR_1, timeframes.HOUR_4, timeframes.DAY_1]

exchange_info = {
# BYBIT_USDT_PERPETUAL
Expand Down Expand Up @@ -217,14 +224,14 @@
# COINBASE_SPOT
exchanges_enums.COINBASE_SPOT: {
'name': exchanges_enums.COINBASE_SPOT,
'url': 'https://pro.coinbase.com',
'fee': 0.005,
'url': 'https://www.coinbase.com/advanced-trade/spot/BTC-USD',
'fee': 0.0003,
'type': 'spot',
'supported_leverage_modes': ['cross', 'isolated'],
'supported_timeframes': COINBASE_TIMEFRAMES,
'modes': {
'backtesting': True,
'live_trading': False,
'live_trading': True,
},
'required_live_plan': 'premium'
},
Expand Down
38 changes: 38 additions & 0 deletions jesse/models/ExchangeApiKeys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import json
import peewee
from jesse.services.db import database

if database.is_closed():
database.open_connection()


class ExchangeApiKeys(peewee.Model):
id = peewee.UUIDField(primary_key=True)
exchange_name = peewee.CharField()
name = peewee.CharField(unique=True)
api_key = peewee.CharField()
api_secret = peewee.CharField()
additional_fields = peewee.TextField()
created_at = peewee.DateTimeField()

class Meta:
from jesse.services.db import database

database = database.db

def __init__(self, attributes=None, **kwargs) -> None:
peewee.Model.__init__(self, attributes=attributes, **kwargs)

if attributes is None:
attributes = {}

for a in attributes:
setattr(self, a, attributes[a])

def get_additional_fields(self) -> dict:
return json.loads(self.additional_fields)


# if database is open, create the table
if database.is_open():
ExchangeApiKeys.create_table()
Loading

0 comments on commit 89dd558

Please sign in to comment.