OHLC's use timestamps instead of string dates
This commit is contained in:
@@ -10,8 +10,10 @@ dec = Decimal
|
|||||||
def now():
|
def now():
|
||||||
return datetime.now(timezone.utc)
|
return datetime.now(timezone.utc)
|
||||||
|
|
||||||
def timestamp():
|
def timestamp(when=None):
|
||||||
return int(datetime.now().timestamp())
|
if when is None:
|
||||||
|
when = datetime.now()
|
||||||
|
return int(when.timestamp())
|
||||||
|
|
||||||
def from_timestamp(ts):
|
def from_timestamp(ts):
|
||||||
return datetime.fromtimestamp(ts, timezone.utc)
|
return datetime.fromtimestamp(ts, timezone.utc)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from omegaconf.errors import OmegaConfBaseException
|
|||||||
|
|
||||||
from .schema import Config
|
from .schema import Config
|
||||||
from .standard_accounts import default_accounts_config
|
from .standard_accounts import default_accounts_config
|
||||||
from .standard_tokens import default_token_config
|
|
||||||
|
|
||||||
schema = OmegaConf.structured(Config())
|
schema = OmegaConf.structured(Config())
|
||||||
|
|
||||||
@@ -20,7 +19,6 @@ def load_config():
|
|||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
result:ConfigDict = OmegaConf.merge(
|
result:ConfigDict = OmegaConf.merge(
|
||||||
schema,
|
schema,
|
||||||
load_tokens(),
|
|
||||||
load_accounts(),
|
load_accounts(),
|
||||||
from_toml('pool.toml'),
|
from_toml('pool.toml'),
|
||||||
from_toml('dexorder.toml'),
|
from_toml('dexorder.toml'),
|
||||||
@@ -31,15 +29,6 @@ def load_config():
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def load_tokens():
|
|
||||||
token_conf = OmegaConf.create({'tokens': default_token_config})
|
|
||||||
try:
|
|
||||||
OmegaConf.merge(schema, token_conf)
|
|
||||||
return token_conf
|
|
||||||
except OmegaConfBaseException as _x:
|
|
||||||
raise ConfigException(f'Error while processing default tokens:\n{_x}')
|
|
||||||
|
|
||||||
|
|
||||||
def load_accounts():
|
def load_accounts():
|
||||||
accounts_conf = OmegaConf.create({'accounts': default_accounts_config})
|
accounts_conf = OmegaConf.create({'accounts': default_accounts_config})
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -21,19 +21,8 @@ class Config:
|
|||||||
polling: float = 0 # seconds between queries for a new block. 0 disables polling and uses a websocket subscription on ws_url instead
|
polling: float = 0 # seconds between queries for a new block. 0 disables polling and uses a websocket subscription on ws_url instead
|
||||||
backfill: int = 0 # if not 0, then runner will initialize an empty database by backfilling from the given block height
|
backfill: int = 0 # if not 0, then runner will initialize an empty database by backfilling from the given block height
|
||||||
|
|
||||||
tokens: list['TokenConfig'] = field(default_factory=list)
|
|
||||||
|
|
||||||
account: Optional[str] = None # may be a private key or an account alias
|
account: Optional[str] = None # may be a private key or an account alias
|
||||||
accounts: Optional[dict[str,str]] = field(default_factory=dict) # account aliases
|
accounts: Optional[dict[str,str]] = field(default_factory=dict) # account aliases
|
||||||
deployments: Optional[dict[str,str]] = field(default_factory=dict)
|
deployments: Optional[dict[str,str]] = field(default_factory=dict)
|
||||||
min_gas: str = '0'
|
min_gas: str = '0'
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TokenConfig:
|
|
||||||
name: str
|
|
||||||
symbol: str
|
|
||||||
decimals: int
|
|
||||||
chain: str
|
|
||||||
address: str
|
|
||||||
abi: Optional[str] = None
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import Optional, NamedTuple, Reversible, Union
|
|||||||
|
|
||||||
from cachetools import LFUCache
|
from cachetools import LFUCache
|
||||||
|
|
||||||
from dexorder import dec, config, from_isotime, minutely, from_timestamp
|
from dexorder import dec, config, from_timestamp, timestamp
|
||||||
from dexorder.base.chain import current_chain
|
from dexorder.base.chain import current_chain
|
||||||
from dexorder.blockstate import BlockDict, DiffItem, current_blockstate
|
from dexorder.blockstate import BlockDict, DiffItem, current_blockstate
|
||||||
from dexorder.blockstate.diff import DiffEntryItem
|
from dexorder.blockstate.diff import DiffEntryItem
|
||||||
@@ -24,16 +24,16 @@ OHLC_PERIODS = [
|
|||||||
|
|
||||||
OHLC_DATE_ROOT = datetime(2009, 1, 4, tzinfo=timezone.utc) # Sunday before Bitcoin Genesis
|
OHLC_DATE_ROOT = datetime(2009, 1, 4, tzinfo=timezone.utc) # Sunday before Bitcoin Genesis
|
||||||
|
|
||||||
# OHLC's are stored as [time, open, high, low, close] string values. If there was no data during the interval,
|
# OHLC's are stored as [timestamp, open, high, low, close] with prices as string values. If there was no data during
|
||||||
# then open, high, and low are None but the close value is carried over from the previous interval.
|
# the interval, then open, high, and low are None but the close value is carried over from the previous interval.
|
||||||
OHLC = list[str] # typedef
|
OHLC = list[Union[None,int,str]] # typedef
|
||||||
|
|
||||||
|
|
||||||
def opt_dec(v):
|
def opt_dec(v):
|
||||||
return None if v is None else dec(v)
|
return None if v is None else dec(v)
|
||||||
|
|
||||||
def dt(v):
|
def dt(v):
|
||||||
return v if isinstance(v, datetime) else from_isotime(v)
|
return v if isinstance(v, datetime) else from_timestamp(v)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NativeOHLC:
|
class NativeOHLC:
|
||||||
@@ -50,7 +50,7 @@ class NativeOHLC:
|
|||||||
@property
|
@property
|
||||||
def ohlc(self) -> OHLC:
|
def ohlc(self) -> OHLC:
|
||||||
return [
|
return [
|
||||||
minutely(self.start),
|
timestamp(self.start),
|
||||||
None if self.open is None else str(self.open),
|
None if self.open is None else str(self.open),
|
||||||
None if self.high is None else str(self.high),
|
None if self.high is None else str(self.high),
|
||||||
None if self.low is None else str(self.low),
|
None if self.low is None else str(self.low),
|
||||||
@@ -154,13 +154,13 @@ class OHLCRepository:
|
|||||||
return # do not track symbols which have not been explicity set up
|
return # do not track symbols which have not been explicity set up
|
||||||
p = str(price)
|
p = str(price)
|
||||||
historical = []
|
historical = []
|
||||||
updated = [OHLC((minutely(ohlc_start_time(time, period)), p, p, p, p))]
|
updated = [OHLC((timestamp(ohlc_start_time(time, period)), p, p, p, p))]
|
||||||
# log.debug(f'\tcreated new bars {updated}')
|
# log.debug(f'\tcreated new bars {updated}')
|
||||||
else:
|
else:
|
||||||
updated = update_ohlc(historical[-1], period, time, price)
|
updated = update_ohlc(historical[-1], period, time, price)
|
||||||
# drop any historical bars that are older than we need
|
# drop any historical bars that are older than we need
|
||||||
oldest_needed = from_timestamp(current_blockstate.get().root_block.timestamp) - period # cover the root block time plus one period prior
|
oldest_needed = from_timestamp(current_blockstate.get().root_block.timestamp) - period # cover the root block time plus one period prior
|
||||||
trim = (oldest_needed - from_isotime(historical[0][0])) // period
|
trim = (oldest_needed - from_timestamp(historical[0][0])) // period
|
||||||
if trim > 0:
|
if trim > 0:
|
||||||
historical = historical[trim:]
|
historical = historical[trim:]
|
||||||
|
|
||||||
@@ -168,8 +168,8 @@ class OHLCRepository:
|
|||||||
if not historical or not updated:
|
if not historical or not updated:
|
||||||
updated = historical + updated
|
updated = historical + updated
|
||||||
else:
|
else:
|
||||||
last_bar = from_isotime(historical[-1][0])
|
last_bar = from_timestamp(historical[-1][0])
|
||||||
first_updated = from_isotime(updated[0][0])
|
first_updated = from_timestamp(updated[0][0])
|
||||||
overlap = (first_updated - last_bar) // period + 1
|
overlap = (first_updated - last_bar) // period + 1
|
||||||
updated = historical[:-overlap] + updated if overlap > 0 else historical + updated
|
updated = historical[:-overlap] + updated if overlap > 0 else historical + updated
|
||||||
# log.debug(f'\tnew recents: {updated}')
|
# log.debug(f'\tnew recents: {updated}')
|
||||||
@@ -187,17 +187,17 @@ class OHLCRepository:
|
|||||||
if not chunk:
|
if not chunk:
|
||||||
chunk = [ohlc]
|
chunk = [ohlc]
|
||||||
else:
|
else:
|
||||||
start = from_isotime(chunk[0][0])
|
start = from_timestamp(chunk[0][0])
|
||||||
index = (time - start) // period
|
index = (time - start) // period
|
||||||
# log.debug(f'save {symbol} {ohlc_name(period)} chunk {start} index {index} <= {len(chunk)}')
|
# log.debug(f'save {symbol} {ohlc_name(period)} chunk {start} index {index} <= {len(chunk)}')
|
||||||
if index > len(chunk):
|
if index > len(chunk):
|
||||||
log.error(f'chunk gap: {index} > {len(chunk)} {symbol} {period_name(period)} {ohlc}'+''.join(f'\n\t{c}' for c in chunk))
|
log.error(f'chunk gap: {index} > {len(chunk)} {symbol} {period_name(period)} {ohlc}'+''.join(f'\n\t{c}' for c in chunk))
|
||||||
exit(1)
|
exit(1)
|
||||||
if index == len(chunk):
|
if index == len(chunk):
|
||||||
assert from_isotime(chunk[-1][0]) + period == time
|
assert from_timestamp(chunk[-1][0]) + period == time
|
||||||
chunk.append(ohlc)
|
chunk.append(ohlc)
|
||||||
else:
|
else:
|
||||||
assert from_isotime(chunk[index][0]) == time
|
assert from_timestamp(chunk[index][0]) == time
|
||||||
chunk[index] = ohlc
|
chunk[index] = ohlc
|
||||||
self.save_chunk(symbol, period, chunk)
|
self.save_chunk(symbol, period, chunk)
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ class OHLCRepository:
|
|||||||
def save_chunk(self, symbol: str, period: timedelta, chunk: list[OHLC]):
|
def save_chunk(self, symbol: str, period: timedelta, chunk: list[OHLC]):
|
||||||
if not chunk:
|
if not chunk:
|
||||||
return
|
return
|
||||||
path = self.chunk_path(symbol, period, from_isotime(chunk[0][0]))
|
path = self.chunk_path(symbol, period, from_timestamp(chunk[0][0]))
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
try:
|
try:
|
||||||
with open(path, 'w') as file:
|
with open(path, 'w') as file:
|
||||||
|
|||||||
Reference in New Issue
Block a user