Compare commits
17 Commits
montecarlo
...
stock
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f718249a49 | ||
|
|
f5eec79a33 | ||
|
|
23207abe49 | ||
|
|
95b123b605 | ||
|
|
dd1dab1c5c | ||
|
|
8cf1e57d50 | ||
|
|
004cd79738 | ||
|
|
028c297f72 | ||
|
|
dc79f49ef4 | ||
|
|
5b156fe0e6 | ||
|
|
d2f33bef7c | ||
|
|
c369064fd7 | ||
|
|
df6d49d7de | ||
|
|
371b3aeaca | ||
|
|
c0ef623887 | ||
|
|
3187e8104e | ||
|
|
82a323791c |
@@ -10,7 +10,7 @@ config = {
|
||||
},
|
||||
|
||||
'caching': {
|
||||
'driver': 'pickle'
|
||||
'driver': None
|
||||
},
|
||||
|
||||
'logging': {
|
||||
@@ -96,6 +96,9 @@ config = {
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
'Polygon': {
|
||||
'api_key': '',
|
||||
},
|
||||
},
|
||||
|
||||
# changes the metrics output of the backtest
|
||||
|
||||
@@ -251,6 +251,10 @@ def get_config(keys: str, default=None):
|
||||
return CACHED_CONFIG[keys]
|
||||
|
||||
|
||||
def get_nan_indices(array):
|
||||
return np.where(np.isnan(array))[0]
|
||||
|
||||
|
||||
def get_strategy_class(strategy_name):
|
||||
from pydoc import locate
|
||||
|
||||
@@ -520,6 +524,12 @@ def readable_duration(seconds, granularity=2):
|
||||
return ', '.join(result[:granularity])
|
||||
|
||||
|
||||
def reinsert_nan(array, nan_indices):
|
||||
for i in range(nan_indices.shape[0]):
|
||||
array = np.concatenate((array[:nan_indices[i]], [np.nan], array[nan_indices[i]:]))
|
||||
return array
|
||||
|
||||
|
||||
def relative_to_absolute(path: str) -> str:
|
||||
return os.path.abspath(path)
|
||||
|
||||
|
||||
@@ -5,16 +5,14 @@ from jesse.services.db import db
|
||||
|
||||
|
||||
class Candle(peewee.Model):
|
||||
"""
|
||||
|
||||
"""
|
||||
id = peewee.UUIDField(primary_key=True)
|
||||
timestamp = peewee.BigIntegerField()
|
||||
open = peewee.FloatField()
|
||||
close = peewee.FloatField()
|
||||
high = peewee.FloatField()
|
||||
low = peewee.FloatField()
|
||||
volume = peewee.FloatField()
|
||||
open = peewee.FloatField(null = True)
|
||||
close = peewee.FloatField(null = True)
|
||||
high = peewee.FloatField(null = True)
|
||||
low = peewee.FloatField(null = True)
|
||||
volume = peewee.FloatField(null = True)
|
||||
symbol = peewee.CharField()
|
||||
exchange = peewee.CharField()
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ from jesse.models import Candle
|
||||
from jesse.modes.import_candles_mode.drivers import drivers
|
||||
from jesse.modes.import_candles_mode.drivers.interface import CandleExchange
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def run(exchange: str, symbol: str, start_date_str: str, skip_confirmation=False):
|
||||
try:
|
||||
@@ -108,8 +110,11 @@ def run(exchange: str, symbol: str, start_date_str: str, skip_confirmation=False
|
||||
run(exchange, symbol, jh.timestamp_to_time(first_existing_timestamp)[:10], True)
|
||||
return
|
||||
|
||||
# fill absent candles (if there's any)
|
||||
candles = _fill_absent_candles(candles, temp_start_timestamp, temp_end_timestamp)
|
||||
if driver.stock_mode:
|
||||
candles = _fill_absent_candles_with_nan(candles, temp_start_timestamp, temp_end_timestamp)
|
||||
else:
|
||||
# fill absent candles (if there's any)
|
||||
candles = _fill_absent_candles(candles, temp_start_timestamp, temp_end_timestamp)
|
||||
|
||||
# store in the database
|
||||
if skip_confirmation:
|
||||
@@ -245,6 +250,46 @@ def _get_candles_from_backup_exchange(
|
||||
return total_candles
|
||||
|
||||
|
||||
def _fill_absent_candles_with_nan(temp_candles, start_timestamp, end_timestamp):
|
||||
if len(temp_candles) == 0:
|
||||
raise CandleNotFoundInExchange(
|
||||
'No candles exists in the market for this day: {} \n'
|
||||
'Try another start_date'.format(
|
||||
jh.timestamp_to_time(start_timestamp)[:10],
|
||||
)
|
||||
)
|
||||
|
||||
symbol = temp_candles[0]['symbol']
|
||||
exchange = temp_candles[0]['exchange']
|
||||
candles = []
|
||||
loop_length = ((end_timestamp - start_timestamp) / 60000) + 1
|
||||
|
||||
i = 0
|
||||
while i < loop_length:
|
||||
candle_for_timestamp = pydash.find(
|
||||
temp_candles, lambda c: c['timestamp'] == start_timestamp)
|
||||
|
||||
if candle_for_timestamp is None:
|
||||
candles.append({
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': exchange,
|
||||
'timestamp': start_timestamp,
|
||||
'open': np.nan,
|
||||
'high': np.nan,
|
||||
'low': np.nan,
|
||||
'close': np.nan,
|
||||
'volume': np.nan
|
||||
})
|
||||
# candle is present
|
||||
else:
|
||||
candles.append(candle_for_timestamp)
|
||||
|
||||
start_timestamp += 60000
|
||||
i += 1
|
||||
return candles
|
||||
|
||||
|
||||
def _fill_absent_candles(temp_candles, start_timestamp, end_timestamp):
|
||||
if len(temp_candles) == 0:
|
||||
raise CandleNotFoundInExchange(
|
||||
@@ -305,3 +350,4 @@ def _fill_absent_candles(temp_candles, start_timestamp, end_timestamp):
|
||||
|
||||
def _insert_to_database(candles):
|
||||
Candle.insert_many(candles).on_conflict_ignore().execute()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from .binance_futures import BinanceFutures
|
||||
from .bitfinex import Bitfinex
|
||||
from .coinbase import Coinbase
|
||||
from .testnet_binance_futures import TestnetBinanceFutures
|
||||
from .polygon import Polygon
|
||||
|
||||
drivers = {
|
||||
'Binance': Binance,
|
||||
@@ -10,4 +11,5 @@ drivers = {
|
||||
'Testnet Binance Futures': TestnetBinanceFutures,
|
||||
'Bitfinex': Bitfinex,
|
||||
'Coinbase': Coinbase,
|
||||
'Polygon': Polygon,
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ class CandleExchange(ABC):
|
||||
"""
|
||||
|
||||
"""
|
||||
def __init__(self, name, count, sleep_time):
|
||||
def __init__(self, name, count, sleep_time, stock_mode = False):
|
||||
self.name = name
|
||||
self.count = count
|
||||
self.sleep_time = sleep_time
|
||||
self.backup_exchange: CandleExchange = None
|
||||
self.stock_mode = stock_mode
|
||||
|
||||
@abstractmethod
|
||||
def init_backup_exchange(self):
|
||||
|
||||
64
jesse/modes/import_candles_mode/drivers/polygon.py
Normal file
64
jesse/modes/import_candles_mode/drivers/polygon.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from polygon import RESTClient
|
||||
from requests import HTTPError
|
||||
|
||||
import jesse.helpers as jh
|
||||
from .interface import CandleExchange
|
||||
|
||||
|
||||
class Polygon(CandleExchange):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('Polygon', 5000, 0.5, stock_mode=True)
|
||||
|
||||
try:
|
||||
api_key = jh.get_config('env.exchanges.Polygon.api_key')
|
||||
except:
|
||||
raise ValueError("Polygon api_key missing in config.py")
|
||||
|
||||
self.restclient = RESTClient(api_key)
|
||||
|
||||
def init_backup_exchange(self):
|
||||
self.backup_exchange = None
|
||||
|
||||
def get_starting_time(self, symbol):
|
||||
|
||||
return None
|
||||
|
||||
def fetch(self, symbol, start_timestamp):
|
||||
|
||||
base = jh.base_asset(symbol)
|
||||
# Check if symbol exists. Raises HTTP 404 if it doesn't.
|
||||
try:
|
||||
details = self.restclient.reference_ticker_details(base)
|
||||
except HTTPError:
|
||||
raise ValueError("Symbol ({}) probably doesn't exist.".format(base))
|
||||
|
||||
payload = {
|
||||
'unadjusted': 'false',
|
||||
'sort': 'asc',
|
||||
'limit': self.count,
|
||||
}
|
||||
|
||||
# Polygon takes string dates not timestamps
|
||||
start = jh.timestamp_to_date(start_timestamp)
|
||||
end = jh.timestamp_to_date(start_timestamp + (self.count) * 60000)
|
||||
response = self.restclient.stocks_equities_aggregates(base, 1, 'minute', start, end, **payload)
|
||||
|
||||
data = response.results
|
||||
|
||||
candles = []
|
||||
|
||||
for d in data:
|
||||
candles.append({
|
||||
'id': jh.generate_unique_id(),
|
||||
'symbol': symbol,
|
||||
'exchange': self.name,
|
||||
'timestamp': int(d['t']),
|
||||
'open': float(d['o']),
|
||||
'close': float(d['c']),
|
||||
'high': float(d['h']),
|
||||
'low': float(d['l']),
|
||||
'volume': int(d['v'])
|
||||
})
|
||||
|
||||
return candles
|
||||
@@ -21,11 +21,11 @@ def generate_candle_from_one_minutes(timeframe,
|
||||
|
||||
return np.array([
|
||||
candles[0][0],
|
||||
candles[0][1],
|
||||
candles[-1][2],
|
||||
candles[:, 3].max(),
|
||||
candles[:, 4].min(),
|
||||
candles[:, 5].sum(),
|
||||
candles[0][1] if not np.isnan(candles[:, 1]).any() else np.nan,
|
||||
candles[-1][2] if not np.isnan(candles[:, 2]).any() else np.nan,
|
||||
candles[:, 3].max() if not np.isnan(candles[:, 3]).any() else np.nan,
|
||||
candles[:, 4].min() if not np.isnan(candles[:, 4]).any() else np.nan,
|
||||
candles[:, 5].sum() if not np.isnan(candles[:, 5]).any() else np.nan,
|
||||
])
|
||||
|
||||
|
||||
|
||||
@@ -90,6 +90,10 @@ config = {
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
'Polygon': {
|
||||
'api_key': '',
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
@@ -7,6 +7,7 @@ newtulipy~=0.4.2
|
||||
numpy~=1.19.4
|
||||
pandas==1.2.0
|
||||
peewee~=3.14.0
|
||||
polygon-api-client~=0.1.9
|
||||
psycopg2-binary~=2.8.6
|
||||
pydash~=4.9.0
|
||||
pytest==6.2.1
|
||||
|
||||
1
setup.py
1
setup.py
@@ -13,6 +13,7 @@ REQUIRED_PACKAGES = [
|
||||
'numpy',
|
||||
'pandas',
|
||||
'peewee',
|
||||
'polygon-api-client',
|
||||
'psycopg2-binary',
|
||||
'pydash',
|
||||
'pytest',
|
||||
|
||||
@@ -211,6 +211,12 @@ def test_get_config():
|
||||
assert jh.get_config('env.logging.order_submission', 2020) is True
|
||||
|
||||
|
||||
def test_get_nan_indices():
|
||||
arr = np.array([0, 11, 22, np.nan, 33, 44, 54, 55, np.nan, np.nan, 20])
|
||||
ind = jh.get_nan_indices(arr)
|
||||
assert (ind == np.array([3, 8, 9])).all()
|
||||
|
||||
|
||||
def test_get_strategy_class():
|
||||
from jesse.strategies import Strategy
|
||||
assert issubclass(jh.get_strategy_class("Test01"), Strategy)
|
||||
@@ -405,6 +411,14 @@ def test_readable_duration():
|
||||
assert jh.readable_duration(604312) == "6 days, 23 hours"
|
||||
|
||||
|
||||
def test_reinsert_nan():
|
||||
arr = np.array([0, 11, 22, np.nan, 33, 44, 54, 55, np.nan, np.nan, 20])
|
||||
arr_without_nan = arr[~np.isnan(arr)]
|
||||
ind = jh.get_nan_indices(arr)
|
||||
arr_with_nan = jh.reinsert_nan(arr_without_nan, ind)
|
||||
assert ((arr == arr_with_nan) | (np.isnan(arr) & np.isnan(arr_with_nan))).all()
|
||||
|
||||
|
||||
def test_relative_to_absolute():
|
||||
from pathlib import Path
|
||||
assert jh.relative_to_absolute("tests/test_helpers.py") == str(Path(__file__).absolute())
|
||||
|
||||
Reference in New Issue
Block a user