update
This commit is contained in:
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='ttools',
|
||||
version='0.4.1',
|
||||
version='0.4.2',
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
'vectorbtpro',
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
from .vbtutils import AnchoredIndicator, create_mask_from_window, isrising, isfalling, isrisingc, isfallingc, trades2entries_exits, figs2cell
|
||||
from .vbtindicators import register_custom_inds
|
||||
from .vbtindicators import register_custom_inds
|
||||
from .utils import find_dotenv, AGG_TYPE, zoneNY, zonePRG, zoneUTC
|
||||
30
ttools/config.py
Normal file
30
ttools/config.py
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from appdirs import user_data_dir
|
||||
from ttools.utils import find_dotenv, AGG_TYPE, RecordType, StartBarAlign, zoneNY, zonePRG, zoneUTC
|
||||
import os
|
||||
import pytz
|
||||
|
||||
#Trade can be shared with v2realbot, agg cache not (we use df, but v2realbot uses Queue - will be changed in the future, when vectorized agg is added to v2realbot)
|
||||
DATA_DIR = user_data_dir("v2realbot", False) # or any subfolder, if not sharing cache with v2realbot
|
||||
LOCAL_TRADE_CACHE = DATA_DIR + "/tradecache:new/" # +daily_file
|
||||
LOCAL_AGG_CACHE = DATA_DIR + "/aggcache_new/" #+ cache_file
|
||||
RECTYPE = "BAR"
|
||||
#AGG conditions -defaults
|
||||
EXCLUDE_CONDITIONS = ['C','O','4','B','7','V','P','W','U','Z','F']
|
||||
MINSIZE = 100
|
||||
RECORD_TYPE = RecordType.BAR #loader supports only BAR type (no cbars)
|
||||
|
||||
#Load env variables
|
||||
ENV_FILE = find_dotenv(__file__)
|
||||
print(ENV_FILE)
|
||||
if load_dotenv(ENV_FILE, verbose=True) is False:
|
||||
print(f"Error loading.env file {ENV_FILE}. Now depending on ENV VARIABLES set externally.")
|
||||
else:
|
||||
print(f"Loaded env variables from file {ENV_FILE}")
|
||||
|
||||
#Alpaca accounts
|
||||
ACCOUNT1_LIVE_API_KEY = os.getenv('ACCOUNT1_LIVE_API_KEY')
|
||||
ACCOUNT1_LIVE_SECRET_KEY = os.getenv('ACCOUNT1_LIVE_SECRET_KEY')
|
||||
ACCOUNT1_PAPER_API_KEY = os.getenv('ACCOUNT1_PAPER_API_KEY')
|
||||
ACCOUNT1_PAPER_SECRET_KEY = os.getenv('ACCOUNT1_PAPER_SECRET_KEY')
|
||||
56
ttools/loaders.py
Normal file
56
ttools/loaders.py
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
from ctypes import Union
|
||||
from dotenv import load_dotenv
|
||||
from appdirs import user_data_dir
|
||||
from ttools.utils import find_dotenv
|
||||
from ttools.config import *
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
print(DATA_DIR)
|
||||
|
||||
def load_data(symbol: Union[str, list], day_start: datetime, day_end: datetime, agg_type: AGG_TYPE, resolution: Union[str, int], excludes: list = EXCLUDE_CONDITIONS, minsize: int = MINSIZE, ext_hours: bool = False, align: StartBarAlign =StartBarAlign.ROUND, as_df: bool = False, force_reload: bool = False) -> None:
|
||||
"""
|
||||
Returns requested aggregated data for give symbol(s)
|
||||
- if requested agg data already exists in cache, returns it
|
||||
- otherwise get the trades (from trade cache or Alpaca) and aggregate them and store to cache
|
||||
|
||||
For location of both caches, see config.py
|
||||
|
||||
Note both trades and agg cache are daily_files
|
||||
|
||||
LOCAL_TRADE_CACHE
|
||||
LOCAL_AGG_CACHE
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : Union[str, list]
|
||||
Symbol or list of symbols
|
||||
day_start : datetime
|
||||
Start date, zone aware
|
||||
day_end : datetime
|
||||
End date, zone aware
|
||||
agg_type : AGG_TYPE
|
||||
Type of aggregation
|
||||
resolution : Union[str, int]
|
||||
Resolution of aggregation nased on agg_type
|
||||
excludes : list
|
||||
List of trade conditions to exclude
|
||||
minsize : int
|
||||
Minimum size of trade to be included
|
||||
ext_hours : bool
|
||||
If True, requests extended hours data
|
||||
align : StartBarAlign
|
||||
How to align first bar RANDOM vs ROUND
|
||||
as_df : bool
|
||||
If True, returns dataframe, otherwise returns vbt Data object
|
||||
force_reload : bool
|
||||
If True, forces cache reload (doesnt use cache both for trade and agg data)
|
||||
|
||||
Returns
|
||||
-------
|
||||
DF or vbt.Data object
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
140
ttools/utils.py
Normal file
140
ttools/utils.py
Normal file
@ -0,0 +1,140 @@
|
||||
from pathlib import Path
|
||||
from enum import Enum
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Tuple
|
||||
import pytz
|
||||
import calendar
|
||||
|
||||
#Zones
|
||||
zoneNY = pytz.timezone('US/Eastern')
|
||||
zoneUTC = pytz.utc
|
||||
zonePRG = pytz.timezone('Europe/Amsterdam')
|
||||
|
||||
def split_range(start: datetime, stop: datetime, period: str = "Y") -> List[Tuple[datetime, datetime]]:
|
||||
"""
|
||||
Splits a range of dates into a list of (start, end) tuples, where end is exclusive (start of next range)
|
||||
|
||||
Args:
|
||||
start (datetime): start date
|
||||
stop (datetime): end date
|
||||
period (str): 'Y', 'M', 'W', or 'D' for year, month, week, or day
|
||||
|
||||
|
||||
Returns:
|
||||
List[Tuple[datetime, datetime]]: list of (start, end) tuples
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
year_ranges = split_range(day_start, day_stop, period="M")
|
||||
for start_date,end_date in year_ranges:
|
||||
print(start_date,end_date)
|
||||
|
||||
>>> 2023-01-15 09:30:00-05:00 2023-02-01 00:00:00-05:00
|
||||
>>> 2023-02-01 00:00:00-05:00 2023-03-01 00:00:00-05:00
|
||||
>>> 2023-03-01 00:00:00-05:00 2023-04-01 00:00:00-05:00
|
||||
>>> 2023-04-01 00:00:00-05:00 2023-05-01 00:00:00-05:00
|
||||
>>> 2023-05-01 00:00:00-05:00 2023-06-01 00:00:00-05:00
|
||||
```
|
||||
|
||||
"""
|
||||
if start > stop:
|
||||
raise ValueError("Start date must be before stop date")
|
||||
|
||||
ranges = []
|
||||
current_start = start
|
||||
|
||||
while current_start < stop:
|
||||
if period == "Y":
|
||||
next_period_start = datetime(current_start.year + 1, 1, 1, tzinfo=current_start.tzinfo)
|
||||
elif period == "M":
|
||||
next_year = current_start.year + (current_start.month // 12)
|
||||
next_month = (current_start.month % 12) + 1
|
||||
next_period_start = datetime(next_year, next_month, 1, tzinfo=current_start.tzinfo)
|
||||
elif period == "W":
|
||||
next_period_start = current_start + timedelta(weeks=1)
|
||||
elif period == "D":
|
||||
next_period_start = current_start + timedelta(days=1)
|
||||
else:
|
||||
raise ValueError("Invalid period specified. Choose from 'Y', 'M', 'W', or 'D'.")
|
||||
|
||||
# Set the end of the current period or stop if within the same period
|
||||
current_end = min(next_period_start, stop)
|
||||
|
||||
# Append the (start, end) tuple to ranges
|
||||
ranges.append((zoneNY.localize(current_start), zoneNY.localize(current_end)))
|
||||
|
||||
# Move to the start of the next period
|
||||
current_start = next_period_start
|
||||
|
||||
return ranges
|
||||
|
||||
|
||||
|
||||
def find_dotenv(start_path):
|
||||
"""
|
||||
Searches for a .env file in the given directory or its parents and returns the path.
|
||||
|
||||
Args:
|
||||
start_path: The directory to start searching from.
|
||||
|
||||
Returns:
|
||||
Path to the .env file if found, otherwise None.
|
||||
"""
|
||||
current_path = Path(start_path)
|
||||
for _ in range(6): # Limit search depth to 5 levels
|
||||
dotenv_path = current_path / '.env'
|
||||
if dotenv_path.exists():
|
||||
return dotenv_path
|
||||
current_path = current_path.parent
|
||||
return None
|
||||
|
||||
# def get_daily_tradecache_file():
|
||||
# return Path(DATA_DIR) / "tradecache"
|
||||
|
||||
# def get_daily_aggcache_file():
|
||||
# #nazev obsahuje i child class
|
||||
# #a take excludes result = ''.join(self.excludes.sort())
|
||||
# self.excludes.sort() # Sorts the list in place
|
||||
# excludes_str = ''.join(map(str, self.excludes)) # Joins the sorted elements after converting them to strings
|
||||
# cache_file = self.__class__.__name__ + '-' + self.symbol + '-' + str(int(date_from.timestamp())) + '-' + str(int(date_to.timestamp())) + '-' + str(self.rectype) + "-" + str(self.resolution) + "-" + str(self.minsize) + "-" + str(self.align) + '-' + str(self.mintick) + str(self.exthours) + excludes_str + '.cache.gz'
|
||||
# file_path = DATA_DIR + "/aggcache/" + cache_file
|
||||
# #print(file_path)
|
||||
# return file_path
|
||||
|
||||
|
||||
#create enum AGG_TYPE
|
||||
class AGG_TYPE(str, Enum):
|
||||
"""
|
||||
Enum class for aggregation types.
|
||||
ohlcv - time based ohlcv (time as resolution)
|
||||
ohlcv_vol - volume based ohlcv (volume as resolution)
|
||||
ohlcv_dol - dollar volume based ohlcv (dollar amount as resolution)
|
||||
ohlcv_renko - renko based ohlcv (brick size as resolution)
|
||||
"""
|
||||
OHLCV = 'ohlcv'
|
||||
OHLCV_VOL = 'ohlcv_vol'
|
||||
OHLCV_DOL = 'ohlcv_dol'
|
||||
OHLCV_RENKO = 'ohlcv_renko'
|
||||
|
||||
class RecordType(str, Enum):
|
||||
"""
|
||||
Represents output of aggregator
|
||||
"""
|
||||
|
||||
BAR = "bar"
|
||||
CBAR = "cbar"
|
||||
CBARVOLUME = "cbarvolume"
|
||||
CBARDOLLAR = "cbardollar"
|
||||
CBARRENKO = "cbarrenko"
|
||||
TRADE = "trade"
|
||||
|
||||
|
||||
class StartBarAlign(str, Enum):
|
||||
"""
|
||||
Represents first bar start time alignement according to timeframe
|
||||
ROUND = bar starts at 0,5,10 (for 5s timeframe)
|
||||
RANDOM = first bar starts when first trade occurs
|
||||
"""
|
||||
ROUND = "round"
|
||||
RANDOM = "random"
|
||||
Reference in New Issue
Block a user