Skip to content

Commit

Permalink
Merge branch 'master' into yakir/feat/delete-orders
Browse files Browse the repository at this point in the history
  • Loading branch information
yakir4123 committed Jun 17, 2024
2 parents f07587e + 191b137 commit 0619518
Show file tree
Hide file tree
Showing 17 changed files with 359 additions and 13 deletions.
3 changes: 2 additions & 1 deletion jesse/enums/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ class exchanges:
BITGET_USDT_PERPETUAL_TESTNET = 'Bitget USDT Perpetual Testnet'
DYDX_PERPETUAL = "Dydx Perpetual"
DYDX_PERPETUAL_TESTNET = "Dydx Perpetual Testnet"

APEX_PRO_PERPETUAL_TESTNET = 'Apex Pro Perpetual Testnet'
APEX_PRO_PERPETUAL = 'Apex Pro Perpetual'

class migration_actions:
ADD = 'add'
Expand Down
2 changes: 2 additions & 0 deletions jesse/indicators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,5 @@
from .wt import wt
from .zlema import zlema
from .zscore import zscore
from .waddah_attr_explosion import waddah_attar_explosion
from .stiffness import stiffness
8 changes: 6 additions & 2 deletions jesse/indicators/macd.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ def macd(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, sign
:return: MACD(macd, signal, hist)
"""
candles = slice_candles(candles, sequential)

source = get_candle_source(candles, source_type=source_type)
if len(candles.shape) == 1:
source = candles
else:
candles = slice_candles(candles, sequential)
source = get_candle_source(candles, source_type=source_type)

macd_val, macdsignal, macdhist = talib.MACD(source, fastperiod=fast_period, slowperiod=slow_period,
signalperiod=signal_period)

Expand Down
7 changes: 5 additions & 2 deletions jesse/indicators/stddev.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ def stddev(candles: np.ndarray, period: int = 5, nbdev: float = 1, source_type:
: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.STDDEV(source, timeperiod=period, nbdev=nbdev)

return res if sequential else res[-1]
59 changes: 59 additions & 0 deletions jesse/indicators/stiffness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from collections import namedtuple

from .sma import sma
from .ema import ema
from .stddev import stddev

import numpy as np

from jesse.helpers import get_candle_source, slice_candles

Stiffness = namedtuple('Stiffness', ['stiffness', 'threshold'])


def stiffness(candles: np.ndarray, ma_length: int = 100, stiff_length: int = 60, stiff_smooth: int = 3, threshold: int = 90, source_type: str = "close") -> Stiffness:
"""
@author daviddtech
credits: https://www.tradingview.com/script/MOw6mUQl-Stiffness-Indicator-DaviddTech
STIFNESS - Stifness
:param candles: np.ndarray
:param ma_length: int - default: 100
:param stiff_length: int - default: 60
:param stiff_smooth: int - default: 3
:param threshold: int - default: 90
:param source_type: str - default: "close"
:return: Stiffness(stiffness, threshold)
"""
if len(candles.shape) == 1:
source = candles
else:
candles = slice_candles(candles, False)
source = get_candle_source(candles, source_type=source_type)

bound_stiffness = sma(source, ma_length, sequential=True) - 0.2 * \
stddev(source, ma_length, sequential=True)
sum_above_stiffness = _count_price_exceed_series(source, bound_stiffness, stiff_length)
stiffness = ema(np.array(sum_above_stiffness) * 100 / stiff_length, period=stiff_smooth)

return Stiffness(stiffness, threshold)


def _count_price_exceed_series(close_prices, art_series, length):
ex_counts = []

for i in range(len(close_prices)):
if i < length:
ex_counts.append(0)
continue
count = 0

for j in range(i - length + 1, i + 1):
if close_prices[j] > art_series[j]:
count += 1

ex_counts.append(count)

return ex_counts
53 changes: 53 additions & 0 deletions jesse/indicators/waddah_attr_explosion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from collections import namedtuple
from .macd import macd
from .sma import sma
from .stddev import stddev
import numpy as np
from jesse.helpers import get_candle_source, slice_candles

WaddahAttarExplosionTuple = namedtuple(
'WaddahAttarExplosionTuple', ['explosion_line', 'trend_power', 'trend_direction']
)


def waddah_attar_explosion(candles: np.ndarray, sensitivity: int = 150, fast_length: int = 20, slow_length: int = 40, channel_length: int = 20, mult: float = 2.0, source_type: str = "close") -> WaddahAttarExplosionTuple:
"""
@author LazyBear
credits: https://www.tradingview.com/v/iu3kKWDI/
WADDAH_ATTAR_EXPLOSION - Waddah Attar Explosion
:param candles: np.ndarray
:param sensitivity: int - default: 150
:param fast_length: int - default: 20
:param slow_length: int - default: 40
:param channel_length: int - default: 20
:param mult: float - default: 2.0
:param source_type: str - default: "close"
:return: WaddahAttarExplosionTuple(explosion_line, trend_power, trend_direction)
"""
if len(candles.shape) == 1:
source = candles
else:
candles = slice_candles(candles, False)
source = get_candle_source(candles, source_type=source_type)

t1 = (macd(source, fast_period=fast_length, slow_period=slow_length)[0] -
macd(source[:-1], fast_period=fast_length, slow_period=slow_length)[0])*sensitivity
trend = 1 if t1 >= 0 else -1
e1 = _calc_bb_upper(source, channel_length, mult) - _calc_bb_lower(source, channel_length, mult)

return WaddahAttarExplosionTuple(e1, t1, trend)


def _calc_bb_upper(source, length, mult):
basis = sma(source, length)
dev = mult * stddev(source, length)
return basis + dev


def _calc_bb_lower(source, length, mult):
basis = sma(source, length)
dev = mult * stddev(source, length)
return basis - dev
30 changes: 30 additions & 0 deletions jesse/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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 @@ -297,7 +298,36 @@
'live_trading': True,
},
'required_live_plan': 'premium'
},

exchanges_enums.APEX_PRO_PERPETUAL_TESTNET: {
'name': exchanges_enums.APEX_PRO_PERPETUAL_TESTNET,
'url': 'https://testnet.pro.apex.exchange/trade/BTCUSD',
'fee': 0.0005,
'type': 'futures',
'supported_leverage_modes': ['cross'],
'supported_timeframes': APEX_PRO_TIMEFRAMES,
'modes': {
'backtesting': False,
'live_trading': True,
},
'required_live_plan': 'premium'
},

exchanges_enums.APEX_PRO_PERPETUAL: {
'name': exchanges_enums.APEX_PRO_PERPETUAL,
'url': 'https://pro.apex.exchange/trade/BTCUSD',
'fee': 0.0005,
'type': 'futures',
'supported_leverage_modes': ['cross'],
'supported_timeframes': APEX_PRO_TIMEFRAMES,
'modes': {
'backtesting': False,
'live_trading': True,
},
'required_live_plan': 'premium'
}

}

# list of supported exchanges for backtesting
Expand Down
74 changes: 74 additions & 0 deletions jesse/modes/import_candles_mode/drivers/Apex/ApexProMain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import requests
import jesse.helpers as jh
from jesse.modes.import_candles_mode.drivers.interface import CandleExchange
from typing import Union
from jesse import exceptions
from .apex_pro_utils import timeframe_to_interval


class ApexProMain(CandleExchange):
def __init__(self, name: str, rest_endpoint: str) -> None:
from jesse.modes.import_candles_mode.drivers.Binance.BinanceSpot import BinanceSpot

super().__init__(name=name, count=200, rate_limit_per_second=10, backup_exchange_class=BinanceSpot)
self.name = name
self.endpoint = rest_endpoint

def get_starting_time(self, symbol: str) -> int:
dashless_symbol = jh.dashless_symbol(symbol)
payload = {
'symbol': dashless_symbol,
'interval': 'W',
'limit': 200,
'start': 1514811660
}

response = requests.get(self.endpoint + '/v2/klines', params=payload)
self.validate_response(response)

if 'data' not in response.json():
raise exceptions.ExchangeInMaintenance(response.json()['msg'])
elif response.json()['data'] == {}:
raise exceptions.InvalidSymbol('Exchange does not support the entered symbol. Please enter a valid symbol.')

data = response.json()['data'][dashless_symbol]
# Reverse the data list
data = data[::-1]

return int(data[1]['t'])

def fetch(self, symbol: str, start_timestamp: int, timeframe: str = '1m') -> Union[list, None]:
dashless_symbol = jh.dashless_symbol(symbol)
interval = timeframe_to_interval(timeframe)

payload = {
'symbol': dashless_symbol,
'interval': interval,
'start': int(start_timestamp / 1000),
'limit': self.count
}

response = requests.get(self.endpoint + '/v2/klines', params=payload)
# check data exist in response.json

if 'data' not in response.json():
raise exceptions.ExchangeInMaintenance(response.json()['msg'])
elif response.json()['data'] == {}:
raise exceptions.InvalidSymbol('Exchange does not support the entered symbol. Please enter a valid symbol.')

data = response.json()['data'][dashless_symbol]

return [
{
'id': jh.generate_unique_id(),
'exchange': self.name,
'symbol': symbol,
'timeframe': timeframe,
'timestamp': int(d['t']),
'open': float(d['o']),
'close': float(d['c']),
'high': float(d['h']),
'low': float(d['l']),
'volume': float(d['v'])
} for d in data
]
10 changes: 10 additions & 0 deletions jesse/modes/import_candles_mode/drivers/Apex/ApexProPerpetual.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from .ApexProMain import ApexProMain
from jesse.enums import exchanges


class ApexProPerpetual(ApexProMain):
def __init__(self) -> None:
super().__init__(
name=exchanges.APEX_PRO_PERPETUAL,
rest_endpoint='https://pro.apex.exchange/api'
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from .ApexProMain import ApexProMain
from jesse.enums import exchanges


class ApexProPerpetualTestnet(ApexProMain):
def __init__(self) -> None:
super().__init__(
name=exchanges.APEX_PRO_PERPETUAL_TESTNET,
rest_endpoint='https://testnet.pro.apex.exchange/api'
)
Empty file.
65 changes: 65 additions & 0 deletions jesse/modes/import_candles_mode/drivers/Apex/apex_pro_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from jesse.enums import timeframes


def timeframe_to_interval(timeframe: str) -> str:
"""
Convert a timeframe string to an interval in seconds.
"""
if timeframe == timeframes.MINUTE_1:
return '1'
elif timeframe == timeframes.MINUTE_3:
return '3'
elif timeframe == timeframes.MINUTE_5:
return '5'
elif timeframe == timeframes.MINUTE_15:
return '15'
elif timeframe == timeframes.MINUTE_30:
return '30'
elif timeframe == timeframes.HOUR_1:
return '60'
elif timeframe == timeframes.HOUR_2:
return '120'
elif timeframe == timeframes.HOUR_4:
return '240'
elif timeframe == timeframes.HOUR_6:
return '360'
elif timeframe == timeframes.HOUR_12:
return '720'
elif timeframe == timeframes.DAY_1:
return 'D'
elif timeframe == timeframes.WEEK_1:
return 'W'
else:
raise ValueError('Invalid timeframe: {}'.format(timeframe))


def interval_to_timeframe(interval: str) -> str:
"""
Convert an interval in seconds to a timeframe string.
"""
if interval == '1':
return timeframes.MINUTE_1
elif interval == '3':
return timeframes.MINUTE_3
elif interval == '5':
return timeframes.MINUTE_5
elif interval == '15':
return timeframes.MINUTE_15
elif interval == '30':
return timeframes.MINUTE_30
elif interval == '60':
return timeframes.HOUR_1
elif interval == '120':
return timeframes.HOUR_2
elif interval == '240':
return timeframes.HOUR_4
elif interval == '360':
return timeframes.HOUR_6
elif interval == '720':
return timeframes.HOUR_12
elif interval == 'D':
return timeframes.DAY_1
elif interval == 'W':
return timeframes.WEEK_1
else:
raise ValueError('Invalid interval: {}'.format(interval))
4 changes: 4 additions & 0 deletions jesse/modes/import_candles_mode/drivers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from jesse.modes.import_candles_mode.drivers.DyDx.DydxPerpetualTestnet import DydxPerpetualTestnet
from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpotTestnet import BybitSpotTestnet
from jesse.modes.import_candles_mode.drivers.Bybit.BybitSpot import BybitSpot
from jesse.modes.import_candles_mode.drivers.Apex.ApexProPerpetualTestnet import ApexProPerpetualTestnet
from jesse.modes.import_candles_mode.drivers.Apex.ApexProPerpetual import ApexProPerpetual


drivers = {
Expand All @@ -34,6 +36,8 @@
exchanges.FTX_PERPETUAL_FUTURES: FTXPerpetualFutures,
exchanges.BITGET_USDT_PERPETUAL: BitgetUSDTPerpetual,
exchanges.BITGET_USDT_PERPETUAL_TESTNET: BitgetUSDTPerpetualTestnet,
exchanges.APEX_PRO_PERPETUAL_TESTNET: ApexProPerpetualTestnet,
exchanges.APEX_PRO_PERPETUAL: ApexProPerpetual,

# Spot
exchanges.FTX_SPOT: FTXSpot,
Expand Down
Loading

0 comments on commit 0619518

Please sign in to comment.