cbar indicators + ml enhancements

This commit is contained in:
David Brazda
2023-12-15 18:02:45 +01:00
parent a70e2adf45
commit 85d4916320
33 changed files with 892 additions and 99 deletions

View File

@ -97,9 +97,7 @@ def init(state: StrategyState):
#pripadne udelat refresh kazdych x-iterací
state.vars['sell_in_progress'] = False
state.vars.mode = None
state.vars.last_tick_price = 0
state.vars.last_50_deltas = []
state.vars.last_tick_volume = 0
state.vars.next_new = 0
state.vars.last_buy_index = None
state.vars.last_exit_index = None
@ -114,9 +112,15 @@ def init(state: StrategyState):
state.vars.blockbuy = 0
#models
state.vars.loaded_models = {}
#INITIALIZE CBAR INDICATORS - do vlastni funkce
#state.cbar_indicators['ivwap'] = []
state.vars.last_tick_price = 0
state.vars.last_tick_volume = 0
state.vars.last_tick_trades = 0
state.cbar_indicators['tick_price'] = []
state.cbar_indicators['tick_volume'] = []
state.cbar_indicators['tick_trades'] = []
state.cbar_indicators['CRSI'] = []
initialize_dynamic_indicators(state)

View File

@ -5,6 +5,7 @@ from pathlib import Path
#directory for generated images and basic reports
MEDIA_DIRECTORY = Path(__file__).parent.parent.parent / "media"
RUNNER_DETAIL_DIRECTORY = Path(__file__).parent.parent.parent / "runner_detail"
#location of strat.log - it is used to fetch by gui
LOG_FILE = Path(__file__).parent.parent / "strat.log"

View File

@ -0,0 +1 @@
#PLACEHOLDER TO RUNNER_DETAILS SERVICES - refactored

View File

@ -14,7 +14,7 @@ from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeSt
from datetime import datetime
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
from threading import Thread, current_thread, Event, enumerate
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR,BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_LOG_SURROUNDING_TRADES,BT_FILL_CONDITION_BUY_LIMIT,BT_FILL_CONDITION_SELL_LIMIT, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN, MEDIA_DIRECTORY
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR,BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_LOG_SURROUNDING_TRADES,BT_FILL_CONDITION_BUY_LIMIT,BT_FILL_CONDITION_SELL_LIMIT, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN, MEDIA_DIRECTORY, RUNNER_DETAIL_DIRECTORY
import importlib
from alpaca.trading.requests import GetCalendarRequest
from alpaca.trading.client import TradingClient
@ -38,6 +38,9 @@ from v2realbot.strategyblocks.indicators.indicators_hub import populate_dynamic_
from v2realbot.interfaces.backtest_interface import BacktestInterface
import os
from v2realbot.reporting.metricstoolsimage import generate_trading_report_image
import msgpack
import gzip
import os
#import gc
#from pyinstrument import Profiler
#adding lock to ensure thread safety of TinyDB (in future will be migrated to proper db)
@ -1227,6 +1230,7 @@ def delete_archived_runners_byBatchID(batch_id: str):
print(f"error delete")
return res, f"ERROR deleting {batch_id} : {val}"
#delete runner in archive and archive detail and runner logs
#predelano do JEDNE TRANSAKCE
def delete_archived_runners_byIDs(ids: list[UUID]):
@ -1297,6 +1301,32 @@ def delete_archive_header_byID(id: UUID):
# region ARCHIVE DETAIL
#FILE SERVICES
#during runner_detail refactoring to file here https://chat.openai.com/c/b18eab9d-1e1c-413b-b25c-ccc180f3b3c2
#vcetne migrace
def get_rd_fn(runner_id):
return str(RUNNER_DETAIL_DIRECTORY / f"{runner_id}.msgpack.gz")
# Helper functions for file operations
def save_to_file(runner_id, data):
file_path = get_rd_fn(runner_id)
with gzip.open(file_path, "wb") as f:
f.write(msgpack.packb(data))
def read_from_file(runner_id):
file_path = get_rd_fn(runner_id)
if os.path.exists(file_path):
with gzip.open(file_path, "rb") as f:
return msgpack.unpackb(f.read())
return None
def delete_file(runner_id):
file_path = get_rd_fn(runner_id)
if os.path.exists(file_path):
os.remove(file_path)
return True
return False
#returns number of deleted elements
def delete_archive_detail_byID(id: UUID):
conn = pool.get_connection()

View File

View File

View File

View File

View File

View File

View File

View File

View File

@ -348,7 +348,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
#Plot 8 Cumulative profit - bud 1 den nebo vice dni + pridame pod to vyvoj ceny
# Extract the closing prices and times
closing_prices = bars.get('close',[])
closing_prices = bars.get('close',[]) if bars is not None else []
#times = bars['time'] # Assuming this is a list of pandas Timestamp objects
times = pd.to_datetime(bars['time']) if bars is not None else [] # Ensure this is a Pandas datetime series
# # Plot the closing prices over time
@ -372,7 +372,8 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
ax2.tick_params(axis='y', labelcolor='orange')
# Set the limits for the x-axis to cover the full range of 'times'
axs[1, 3].set_xlim(times.min(), times.max())
if isinstance(times, pd.DatetimeIndex):
axs[1, 3].set_xlim(times.min(), times.max())
sns.lineplot(x=exit_times, y=cumulative_profits, ax=axs[1, 3], color='limegreen')
axs[1, 3].scatter(max_profit_time, max_profit, color='green', label='Max Profit')
axs[1, 3].scatter(min_profit_time, min_profit, color='red', label='Min Profit')

View File

@ -125,7 +125,7 @@ $(document).ready(function() {
require(["vs/editor/editor.main"], () => {
model_editor_json = monaco.editor.create(document.getElementById('toml-editor-container'), {
value: response.cfg_toml ? response.cfg_toml : JSON.stringify(response,null,4),
value: response.cfg_toml ? response.cfg_toml + ((response.history) ? "\nHISTORY:\n" + JSON.stringify(response.history,null,4) : "") : JSON.stringify(response,null,4),
language: 'toml',
theme: 'tomlTheme-dark',
automaticLayout: true,

View File

@ -744,6 +744,7 @@ class StrategyState:
self.ilog_save = ilog_save
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
self.sl_optimizer_long = optimsl.SLOptimizer(ptm.TradeDirection.LONG)
self.cache = defaultdict(dict)
bars = {'high': [],
'low': [],

View File

@ -9,20 +9,24 @@ def populate_cbar_tick_price_indicator(data, state: StrategyState):
try:
tick_price = data['close']
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
tick_delta_trades = data['trades'] - state.vars.last_tick_trades
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
state.cbar_indicators.tick_trades[-1] = tick_delta_trades
except:
pass
state.ilog(lvl=0,e=f"TICK PRICE CBARV {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume)
state.ilog(lvl=0,e=f"TICK PRICE CBARV {tick_price} VOLUME {tick_delta_volume} TRADES {tick_delta_trades} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume, prev_trades=state.vars.last_tick_trades)
state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume']
state.vars.last_tick_trades = data['trades']
if conf_bar == 1:
#pri potvrzem CBARu nulujeme counter volume pro tick based indicator
state.vars.last_tick_volume = 0
state.vars.last_tick_trades = 0
state.vars.next_new = 1
#pro standardní CBARy
@ -30,6 +34,7 @@ def populate_cbar_tick_price_indicator(data, state: StrategyState):
if conf_bar == 1:
#pri potvrzem CBARu nulujeme counter volume pro tick based indicator
state.vars.last_tick_volume = 0
state.vars.last_tick_trades = 0
state.vars.next_new = 1
@ -45,13 +50,16 @@ def populate_cbar_tick_price_indicator(data, state: StrategyState):
#tick_price = round2five(data['close'])
tick_price = data['close']
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
tick_delta_trades = data['trades'] - state.vars.last_tick_trades
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
state.cbar_indicators.tick_trades[-1] = tick_delta_trades
except:
pass
state.ilog(lvl=0,e=f"TICK PRICE CBAR {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume)
state.ilog(lvl=0,e=f"TICK PRICE CBAR {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume, prev_trades=state.vars.last_tick_trades)
state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume']
state.vars.last_tick_volume = data['volume']
state.vars.last_tick_trades = data['trades']

View File

@ -23,49 +23,49 @@ import importlib
#OBECNA trida pro statefull indicators - realized by class with the same name, deriving from parent IndicatorBase class
#todo v initu inicializovat state.classed_indicators a ve stopu uklidit - resetovat
def classed(state, params, name):
funcName = "classed"
if params is None:
return -2, "params required"
init_params = safe_get(params, "init", None) #napr sekce obcahuje threshold = 1222, ktere jdou kwargs do initu fce
#next_params = safe_get(params, "next", None)
#List of sources, ktere jde do nextu (muze jit i vice serie)
#Do nextu jde ve stejnojmenném parametru
next_sources = safe_get(params, "next", []) #this will map to the sources_dict
next_mapping = safe_get(params, "next_mapping", next_sources) #this will dictate the final name of the key in sources_dict
#ukládáme si do cache incializaci
cache = safe_get(params, "CACHE", None)
if cache is None:
if len(next_sources) != len(next_mapping):
return -2, "next and next_mapping length must be the same"
# Vytvorime dictionary pro kazdy source a priradime serii
#source_dict = {name: get_source_series(state, name) for name in next_sources}
#TBD toto optimalizovat aby se nevolalo pri kazde iteraci
source_dict = {new_key: get_source_series(state, name)
for name, new_key in zip(next_sources, next_mapping)}
params["CACHE"] = {}
params["CACHE"]["source_dict"] = source_dict
else:
source_dict = params["CACHE"]["source_dict"]
try:
funcName = "classed"
if params is None:
return -2, "params required"
init_params = safe_get(params, "init", {}) #napr sekce obcahuje threshold = 1222, ktere jdou kwargs do initu fce
#next_params = safe_get(params, "next", None)
#List of sources, ktere jde do nextu (muze jit i vice serie)
#Do nextu jde ve stejnojmenném parametru
class_name = safe_get(params, "class_name", None)
if class_name is None:
return -2, "class_name required"
next_sources = safe_get(params, "next", []) #this will map to the sources_dict
next_mapping = safe_get(params, "next_mapping", next_sources) #this will dictate the final name of the key in sources_dict
if state.cache.get(name, None) is None:
if len(next_sources) != len(next_mapping):
return -2, "next and next_mapping length must be the same"
# Vytvorime dictionary pro kazdy source a priradime serii
#source_dict = {name: get_source_series(state, name) for name in next_sources}
#TBD toto optimalizovat aby se nevolalo pri kazde iteraci
source_dict = {new_key: get_source_series(state, name)
for name, new_key in zip(next_sources, next_mapping)}
state.cache[name]["source_dict"] = source_dict
else:
source_dict = state.cache[name]["source_dict"]
if name not in state.classed_indicators:
classname = name
class_module = importlib.import_module("v2realbot.strategyblocks.indicators.custom.classes."+classname)
indicatorClass = getattr(class_module, classname)
class_module = importlib.import_module("v2realbot.strategyblocks.indicators.custom.classes."+class_name)
indicatorClass = getattr(class_module, class_name)
instance = indicatorClass(state=state, **init_params)
print("instance vytvorena", instance)
state.classed_indicators[name] = instance
state.ilog(lvl=1,e=f"IND CLASS {name} INITIALIZED", **params)
#state.ilog(lvl=1,e=f"IND CLASS {name} INITIALIZED", **params)
if len(source_dict) >0:
val = state.classed_indicators[name].next(**source_dict)
else:
val = state.classed_indicators[name].next()
state.ilog(lvl=1,e=f"IND CLASS {name} NEXT {val}", **params)
#state.ilog(lvl=1,e=f"IND CLASS {name} NEXT {val}", **params)
return 0, val
except Exception as e:

View File

@ -0,0 +1,60 @@
from collections import deque
#import time
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
#WIP
class BarBasedOpenCloseOIO(IndicatorBase):
"""
Bar Based Order Imbalance Oscillator (OIO) focusing on the imbalance between buying and selling
pressure in the market inside each bar. Is is reset when new bar arrives.
Accept continous cbar, stores on the level of bar.
Průběžně počítá imbalanci předchozích cen (od open do close) v rámci daného baru.
"""
def __init__(self, state=None):
super().__init__(state)
self.old_data = deque() # Stores tuples of (price, volume)
self.prev_index = None
def next(self, index, close):
index, new_price = index[-1], close[-1]
# Remove old data when a new bar comes
if self.prev_index is not None and self.prev_index != index:
self.old_data.clear()
self.prev_index = index
upward_pressure = 0
downward_pressure = 0
# Calculate price changes and pressures, optionally weighted by volume
for old_price in self.old_data:
price_change = new_price - old_price
if price_change > 0:
upward_pressure += price_change
elif price_change < 0:
downward_pressure += abs(price_change)
# Add new tick data with volume
self.old_data.append(new_price)
# Calculate OIO
total_pressure = upward_pressure + downward_pressure
if total_pressure > 0:
oio = (upward_pressure - downward_pressure) / total_pressure
else:
oio = 0 # OIO is zero if there's no pressure in either direction
return oio
# Example usage
# oio_indicator_weighted = BarBasedOpenCloseOIO(use_volume_weighting=True)
# oio_indicator_unweighted = BarBasedOpenCloseOIO(use_volume_weighting=False)
# Example tick data: [(index, close, volume)]
# old_data = [(1, 100, 500), (1, 101, 600), ..., (2, 102, 550), ...]
# for data in old_data:
# oio_value_weighted = oio_indicator_weighted.next(*data)
# oio_value_unweighted = oio_indicator_unweighted.next(*data)
# print(f"Weighted Bar-Based OIO: {oio_value_weighted:.2f}, Unweighted Bar-Based OIO: {oio_value_unweighted:.2f}")

View File

@ -0,0 +1,116 @@
from collections import deque
#import time
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
#WIP
class TickBasedOIO(IndicatorBase):
"""
POZOR NIZE JE WEIGHTED VARIANTA - zakomentovana, mozna v budoucnu vyuzit
tick-based Order Imbalance Oscillator (OIO) focusing on the imbalance between buying and selling
pressure in the market, calculates the difference in cumulative price changes in both directions
over a specified window of time. This approach assumes that larger price movements in one direction
indicate stronger market pressure from either buyers or sellers.
"""
def __init__(self, window_size_seconds=5, state=None):
super().__init__(state)
self.window_size_seconds = window_size_seconds
self.tick_data = deque()
def next(self, time, close):
new_time, new_price = time[-1], close[-1]
# Remove old data outside the time window
while self.tick_data and new_time - self.tick_data[0][0] > self.window_size_seconds:
self.tick_data.popleft()
upward_pressure = 0
downward_pressure = 0
# Calculate price changes and pressures
for old_time, old_price in self.tick_data:
price_change = new_price - old_price
if price_change > 0:
upward_pressure += price_change
elif price_change < 0:
downward_pressure += abs(price_change)
# Add new tick data
self.tick_data.append((new_time, new_price))
# Calculate OIO
if upward_pressure + downward_pressure > 0:
oio = (upward_pressure - downward_pressure) / (upward_pressure + downward_pressure)
else:
oio = 0 # OIO is zero if there's no pressure in either direction
return oio
# # Example usage
# oio_indicator = TickBasedOIO(window_size_seconds=5)
# # Example tick data: (timestamp, price)
# tick_data = [(1, 100), (2, 101), (3, 102), (4, 100), (5, 99), (6, 98), (7, 97), (8, 98), (9, 99), (10, 100)]
# for data in tick_data:
# oio_value = oio_indicator.next(data)
# print(f"Tick-Based OIO: {oio_value:.2f}")
#WEIGHTED VARIANT
# from collections import deque
# from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
# class TickBasedOIO(IndicatorBase):
# """
# Tick-based Order Imbalance Oscillator (OIO) focusing on the imbalance between buying and selling
# pressure in the market, calculates the difference in cumulative price changes in both directions
# over a specified window of time, optionally weighted by volume.
# """
# def __init__(self, window_size_seconds=5, use_volume_weighting=False, state=None):
# super().__init__(state)
# self.window_size_seconds = window_size_seconds
# self.use_volume_weighting = use_volume_weighting
# self.tick_data = deque()
# def next(self, time, close, volume):
# new_time, new_price, new_volume = time[-1], close[-1], volume[-1]
# # Remove old data outside the time window
# while self.tick_data and new_time - self.tick_data[0][0] > self.window_size_seconds:
# self.tick_data.popleft()
# upward_pressure = 0
# downward_pressure = 0
# # Calculate price changes and pressures, optionally weighted by volume
# for old_time, old_price, old_volume in self.tick_data:
# price_change = new_price - old_price
# weighted_change = price_change * old_volume if self.use_volume_weighting else price_change
# if price_change > 0:
# upward_pressure += weighted_change
# elif price_change < 0:
# downward_pressure += abs(weighted_change)
# # Add new tick data with volume
# self.tick_data.append((new_time, new_price, new_volume))
# # Calculate OIO
# total_pressure = upward_pressure + downward_pressure
# if total_pressure > 0:
# oio = (upward_pressure - downward_pressure) / total_pressure
# else:
# oio = 0 # OIO is zero if there's no pressure in either direction
# return oio
# Example usage
# oio_indicator_weighted = TickBasedOIO(window_size_seconds=5, use_volume_weighting=True)
# oio_indicator_unweighted = TickBasedOIO(window_size_seconds=5, use_volume_weighting=False)
# Example tick data: (timestamp, price, volume)
# tick_data = [(1, 100, 500), (2, 101, 600), ..., (10, 100, 550)]
# for data in tick_data:
# oio_value_weighted = oio_indicator_weighted.next(*data)
# oio_value_unweighted = oio_indicator_unweighted.next(*data)
# print(f"Weighted Tick-Based OIO: {oio_value_weighted:.2f}, Unweighted Tick-Based OIO: {oio_value_unweighted:.2f}")

View File

@ -0,0 +1,26 @@
from collections import deque
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
class TickCount(IndicatorBase):
"""
Calculates the count of ticks within a rolling time window in seconds.
"""
def __init__(self, window_size_seconds=5, state=None):
super().__init__(state)
self.window_size_seconds = window_size_seconds
self.tick_times = deque()
self.tick_count = 0
def next(self, timestamp):
timestamp = timestamp[-1]
# Remove old timestamps outside the time window
while self.tick_times and timestamp - self.tick_times[0] > self.window_size_seconds:
self.tick_times.popleft()
self.tick_count -= 1
# Add new timestamp
self.tick_times.append(timestamp)
self.tick_count += 1
return self.tick_count

View File

@ -1,9 +1,9 @@
import numpy as np
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
#usecase - pocitat variance ticku
# v ramci BARu - posilame sem index a resetujeme pri naslednem indxu
# do budoucna mo
#TOTO predelat - jsou tu dva UC - na ticku (per position and time), uvnitr baru (per index baru)
#rozdelit tyto a predelat
class TickVariance(IndicatorBase):
def __init__(self, state, window_size=1):
"""

View File

@ -0,0 +1,28 @@
from collections import deque
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
class TickVolumeWindow(IndicatorBase):
"""
Calculates volume in the rolling time window in seconds.
"""
def __init__(self, window_size_seconds=5, state=None):
super().__init__(state)
self.window_size_seconds = window_size_seconds
self.volume = 0
self.time_start = None
def next(self, timestamp, volume):
timestamp = timestamp[-1]
volume = volume[-1]
if self.time_start is None:
self.time_start = timestamp
if self.time_start + self.window_size_seconds < timestamp:
self.volume = 0
self.time_start = timestamp
self.volume += volume
return self.volume

View File

@ -0,0 +1,200 @@
from collections import deque
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
class VolumeNeeded2Move(IndicatorBase):
"""
VOLUME NEEDED FOR MOVEMENT
This indicator measures the volume required to move the price by a specified amount either upwards or downwards.
The track_mode parameter determines the direction of price movement to be tracked.
When the threshold for the opposite movement direction is reached, the indicator resets but returns the previous value.
TODO ted se volume neresetuje, ale porad nacita ??? !!
NOTE pozor dela to neco trohcu jinyho
## pocita to average volume per unit of price movement (kolik VOLUME stála(spotřebovala) kumulativně 1 jednotuka pohybu - to je zajimavý)
- tzn. uloží si to za danou jednotku volume k jejimu dosazeni a ulozi si
TOTO VOLUME, JEDNOTKA k dosazeni
45666, 0.03
36356536, 0.03
33333, 0.03
a pak spocte prumer kolik prumerne stala jednotka za cele predchozi obdobi a toto cele vraci
projit, jak by to spravne melo fungovat pro UP and DOWN
1) zkusit vracet ty konkretni nekumulativni hodnoty za obdobi prekonani (nebo prumernou "cenu" jednotky)
2) kumulovat podle okna (tzn. ze vracim prumernou cenu za jednotku za rolling window)
TODO vrácena verze s absolutním thresholdem, která fungocvala.
projít a zkusit relativní threshold nebo nejak uložit natvrdo ten pct ve formě ticku a ten používat po celou dobu, aby se nám neměnil
PCT verze zde:
#https://chat.openai.com/share/954a3481-f43f-43ee-8cb5-c2278384fa20
referenční obrázky:https://trading.mujdenik.eu/xwiki/bin/view/Trading-platform/Indicators-detail/VolumeNeeded2Move/
případně si vytvořit ref runny z této abs verze
TODO pridat jeste time_window, kdy pro CUM bude vracet jen za dane okno (promyslet)
"""
#TYPE - last or cumulative
def __init__(self, price_movement_threshold, track_mode='up', return_type="cum", state=None):
super().__init__(state)
self.price_movement_threshold_tmp = price_movement_threshold
self.price_movement_threshold = None
self.track_mode = track_mode
self.price_volume_data = deque() # Stores tuples of (price, volume)
self.last_price = None
self.accumulated_volume = 0
self.last_avg_volume_per_price_movement = 0
self.return_type = return_type
def next(self, close, volume):
new_price = close[-1]
new_volume = volume[-1]
#pri prvni iteraci udelame z pct thresholdu fixni tick a ten pak pouzivame
if self.price_movement_threshold is None:
self.price_movement_threshold = (new_price / 100) * self.price_movement_threshold_tmp
print("threshold in ticks:",self.price_movement_threshold )
# Initialize last_price if not set
if self.last_price is None:
self.last_price = new_price
# Accumulate volume
self.accumulated_volume += new_volume
# Calculate price change
price_change = new_price - self.last_price
# Check if the price movement threshold is reached
reset_indicator = False
if (self.track_mode == 'up' and price_change >= self.price_movement_threshold) or \
(self.track_mode == 'down' and price_change <= -self.price_movement_threshold):
self.price_volume_data.append((self.accumulated_volume, abs(price_change)))
reset_indicator = True
elif (self.track_mode == 'up' and price_change <= -self.price_movement_threshold) or \
(self.track_mode == 'down' and price_change >= self.price_movement_threshold):
reset_indicator = True
# Reset if threshold is reached for either direction
if reset_indicator:
self.accumulated_volume = 0
self.last_price = new_price
if len(self.price_volume_data) > 0:
#print("pred resetem",self.price_volume_data)
total_volume, total_price_change = zip(*self.price_volume_data)
#print("total volume, price change",total_volume,total_price_change)
#CELKEM PRUMER VOLUME za danou zemnu
if self.return_type=="cum":
self.last_avg_volume_per_price_movement = sum(total_volume)/len(total_volume) #/ sum(total_price_change)
#(last)
else:
#KONKRETNI zA PREDCHOZI CAST
self.last_avg_volume_per_price_movement = total_volume[-1]
return self.last_avg_volume_per_price_movement
#PCT VARIANT
# from collections import deque
# from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
# class VolumePriceMovementIndicator(IndicatorBase):
# """
# This indicator measures the volume required to move the price by a specified percentage.
# It tracks the accumulated volume since the last time the price moved by the predefined threshold.
# """
# def __init__(self, price_movement_threshold_pct, state=None):
# super().__init__(state)
# # Price movement threshold is now a percentage
# self.price_movement_threshold_pct = price_movement_threshold_pct / 100.0 # Convert to decimal
# self.price_volume_data = deque() # Stores tuples of (price, volume)
# self.last_price = None
# self.accumulated_volume = 0
# def next(self, index, close, volume):
# new_price = close[-1]
# new_volume = volume[-1]
# # Initialize last_price if not set
# if self.last_price is None:
# self.last_price = new_price
# # Accumulate volume
# self.accumulated_volume += new_volume
# # Calculate percentage price change relative to the last price
# price_change_pct = abs((new_price - self.last_price) / self.last_price)
# # Check if price movement threshold is reached
# if price_change_pct >= self.price_movement_threshold_pct:
# # Threshold reached, record the data and reset
# self.price_volume_data.append((self.accumulated_volume, price_change_pct))
# self.accumulated_volume = 0
# self.last_price = new_price
# # Compute average volume per percentage of price movement
# if len(self.price_volume_data) > 0:
# total_volume, total_price_change_pct = zip(*self.price_volume_data)
# avg_volume_per_pct_price_movement = sum(total_volume) / sum(total_price_change_pct)
# else:
# avg_volume_per_pct_price_movement = 0
# return avg_volume_per_pct_price_movement
##puvodni absolutni funkcni
# def __init__(self, price_movement_threshold, track_mode='up', state=None):
# super().__init__(state)
# self.price_movement_threshold = price_movement_threshold
# self.track_mode = track_mode
# self.price_volume_data = deque() # Stores tuples of (price, volume)
# self.last_price = None
# self.accumulated_volume = 0
# self.last_avg_volume_per_price_movement = 0
# def next(self, close, volume):
# new_price = close[-1]
# new_volume = volume[-1]
# # Initialize last_price if not set
# if self.last_price is None:
# self.last_price = new_price
# # Accumulate volume
# self.accumulated_volume += new_volume
# # Calculate price change
# price_change = new_price - self.last_price
# # Check if the price movement threshold is reached
# reset_indicator = False
# if (self.track_mode == 'up' and price_change >= self.price_movement_threshold) or \
# (self.track_mode == 'down' and price_change <= -self.price_movement_threshold):
# self.price_volume_data.append((self.accumulated_volume, abs(price_change)))
# reset_indicator = True
# elif (self.track_mode == 'up' and price_change <= -self.price_movement_threshold) or \
# (self.track_mode == 'down' and price_change >= self.price_movement_threshold):
# reset_indicator = True
# # Reset if threshold is reached for either direction
# if reset_indicator:
# self.accumulated_volume = 0
# self.last_price = new_price
# if len(self.price_volume_data) > 0:
# print("pred resetem",self.price_volume_data)
# total_volume, total_price_change = zip(*self.price_volume_data)
# print("total volume, price change",total_volume,total_price_change)
# self.last_avg_volume_per_price_movement = sum(total_volume) / sum(total_price_change)
# return self.last_avg_volume_per_price_movement

View File

@ -0,0 +1,209 @@
from collections import deque
from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
class VolumeNeeded2Movev2(IndicatorBase):
"""
VOLUME NEEDED FOR MOVEMENT
This indicator measures the volume required to move the price by a specified amount either upwards or downwards.
The track_mode parameter determines the direction of price movement to be tracked.
When the threshold for the opposite movement direction is reached, the indicator resets but returns the previous value.
TODO ted se volume neresetuje, ale porad nacita ??? !!
NOTE pozor dela to neco trohcu jinyho
## pocita to average volume per unit of price movement (kolik VOLUME stála(spotřebovala) kumulativně 1 jednotuka pohybu - to je zajimavý)
- tzn. uloží si to za danou jednotku volume k jejimu dosazeni a ulozi si
TOTO VOLUME, JEDNOTKA k dosazeni
45666, 0.03
36356536, 0.03
33333, 0.03
a pak spocte prumer kolik prumerne stala jednotka za cele predchozi obdobi a toto cele vraci
projit, jak by to spravne melo fungovat pro UP and DOWN
1) zkusit vracet ty konkretni nekumulativni hodnoty za obdobi prekonani (nebo prumernou "cenu" jednotky)
2) kumulovat podle okna (tzn. ze vracim prumernou cenu za jednotku za rolling window)
TODO vrácena verze s absolutním thresholdem, která fungocvala.
projít a zkusit relativní threshold nebo nejak uložit natvrdo ten pct ve formě ticku a ten používat po celou dobu, aby se nám neměnil
PCT verze zde:
#https://chat.openai.com/share/954a3481-f43f-43ee-8cb5-c2278384fa20
referenční obrázky:https://trading.mujdenik.eu/xwiki/bin/view/Trading-platform/Indicators-detail/VolumeNeeded2Move/
případně si vytvořit ref runny z této abs verze
TODO pridat jeste time_window, kdy pro CUM bude vracet jen za dane okno (promyslet)
"""
#TYPE - last or cumulative
def __init__(self, price_movement_threshold, rolling_window_seconds = None, track_mode='up', return_type="cum", state=None):
super().__init__(state)
self.price_movement_threshold_tmp = price_movement_threshold
self.price_movement_threshold = None
self.rolling_window_seconds = rolling_window_seconds
self.track_mode = track_mode
self.price_volume_data = deque() # Stores tuples of (price, volume)
self.last_price = None
self.accumulated_volume = 0
self.last_avg_volume_per_price_movement = 0
self.return_type = return_type
def next(self, close, volume, time):
new_price = close[-1]
new_volume = volume[-1]
new_timestamp = time[-1]
#pri prvni iteraci udelame z pct thresholdu fixni tick a ten pak pouzivame
if self.price_movement_threshold is None:
self.price_movement_threshold = (new_price / 100) * self.price_movement_threshold_tmp
print("threshold in ticks:",self.price_movement_threshold )
# Initialize last_price if not set
if self.last_price is None:
self.last_price = new_price
# Keep only the items where the timestamp is within the window
# Efficiently remove old data
if self.rolling_window_seconds is not None:
while self.price_volume_data and self.price_volume_data[0][0] < new_timestamp - self.rolling_window_seconds:
self.price_volume_data.popleft()
# Accumulate volume
self.accumulated_volume += new_volume
# Calculate price change
price_change = new_price - self.last_price
# Check if the price movement threshold is reached
reset_indicator = False
if (self.track_mode == 'up' and price_change >= self.price_movement_threshold) or \
(self.track_mode == 'down' and price_change <= -self.price_movement_threshold):
self.price_volume_data.append((new_timestamp, self.accumulated_volume, abs(price_change)))
reset_indicator = True
#pokud prekonalo druhym smerem, nulujeme, volume si bere druhy indikator
elif (self.track_mode == 'up' and price_change <= -self.price_movement_threshold) or \
(self.track_mode == 'down' and price_change >= self.price_movement_threshold):
reset_indicator = False
self.accumulated_volume = 0
self.last_price = new_price
if reset_indicator:
# Compute average efficiently
if len(self.price_volume_data) > 0:
total_volume = sum(v for _, v, _ in self.price_volume_data)
num_items = len(self.price_volume_data)
if self.return_type == "cum":
self.last_avg_volume_per_price_movement = total_volume / num_items
else:
self.last_avg_volume_per_price_movement = self.price_volume_data[-1][1]
self.accumulated_volume = 0
self.last_price = new_price
return self.last_avg_volume_per_price_movement
return self.last_avg_volume_per_price_movement
#PCT VARIANT
# from collections import deque
# from v2realbot.strategyblocks.indicators.custom.classes.indicatorbase import IndicatorBase
# class VolumePriceMovementIndicator(IndicatorBase):
# """
# This indicator measures the volume required to move the price by a specified percentage.
# It tracks the accumulated volume since the last time the price moved by the predefined threshold.
# """
# def __init__(self, price_movement_threshold_pct, state=None):
# super().__init__(state)
# # Price movement threshold is now a percentage
# self.price_movement_threshold_pct = price_movement_threshold_pct / 100.0 # Convert to decimal
# self.price_volume_data = deque() # Stores tuples of (price, volume)
# self.last_price = None
# self.accumulated_volume = 0
# def next(self, index, close, volume):
# new_price = close[-1]
# new_volume = volume[-1]
# # Initialize last_price if not set
# if self.last_price is None:
# self.last_price = new_price
# # Accumulate volume
# self.accumulated_volume += new_volume
# # Calculate percentage price change relative to the last price
# price_change_pct = abs((new_price - self.last_price) / self.last_price)
# # Check if price movement threshold is reached
# if price_change_pct >= self.price_movement_threshold_pct:
# # Threshold reached, record the data and reset
# self.price_volume_data.append((self.accumulated_volume, price_change_pct))
# self.accumulated_volume = 0
# self.last_price = new_price
# # Compute average volume per percentage of price movement
# if len(self.price_volume_data) > 0:
# total_volume, total_price_change_pct = zip(*self.price_volume_data)
# avg_volume_per_pct_price_movement = sum(total_volume) / sum(total_price_change_pct)
# else:
# avg_volume_per_pct_price_movement = 0
# return avg_volume_per_pct_price_movement
##puvodni absolutni funkcni
# def __init__(self, price_movement_threshold, track_mode='up', state=None):
# super().__init__(state)
# self.price_movement_threshold = price_movement_threshold
# self.track_mode = track_mode
# self.price_volume_data = deque() # Stores tuples of (price, volume)
# self.last_price = None
# self.accumulated_volume = 0
# self.last_avg_volume_per_price_movement = 0
# def next(self, close, volume):
# new_price = close[-1]
# new_volume = volume[-1]
# # Initialize last_price if not set
# if self.last_price is None:
# self.last_price = new_price
# # Accumulate volume
# self.accumulated_volume += new_volume
# # Calculate price change
# price_change = new_price - self.last_price
# # Check if the price movement threshold is reached
# reset_indicator = False
# if (self.track_mode == 'up' and price_change >= self.price_movement_threshold) or \
# (self.track_mode == 'down' and price_change <= -self.price_movement_threshold):
# self.price_volume_data.append((self.accumulated_volume, abs(price_change)))
# reset_indicator = True
# elif (self.track_mode == 'up' and price_change <= -self.price_movement_threshold) or \
# (self.track_mode == 'down' and price_change >= self.price_movement_threshold):
# reset_indicator = True
# # Reset if threshold is reached for either direction
# if reset_indicator:
# self.accumulated_volume = 0
# self.last_price = new_price
# if len(self.price_volume_data) > 0:
# print("pred resetem",self.price_volume_data)
# total_volume, total_price_change = zip(*self.price_volume_data)
# print("total volume, price change",total_volume,total_price_change)
# self.last_avg_volume_per_price_movement = sum(total_volume) / sum(total_price_change)
# return self.last_avg_volume_per_price_movement

View File

@ -23,8 +23,8 @@ def divergence(state, params, name):
val = round((abs(float(source1_series[-1]) - float(source2_series[-1])))/float(source1_series[-1]),4)
elif mode == "rel":
val = round(float(source1_series[-1]) - float(source2_series[-1]),4)
elif mode == "reln":
val = round((float(source1_series[-1]) - float(source2_series[-1]))/float(source1_series[-1]),4)
elif mode == "reln": #div = a+b / a-b will give value between -1 and 1
val = round((float(source1_series[-1]) - float(source2_series[-1]))/(float(source1_series[-1])+float(source2_series[-1])),4)
elif mode == "pctabs":
val = pct_diff(num1=float(source1_series[-1]),num2=float(source2_series[-1]), absolute=True)
elif mode == "pct":

View File

@ -7,6 +7,9 @@ from traceback import format_exc
import numpy as np
from collections import defaultdict
"""
Předpokladm, že buď používáme 1) bar+standard indikator 2) cbars indicators - zatim nepodporovano spolecne (jine time rozliseni)
"""
def model(state, params, ind_name):
funcName = "model"
if params is None:
@ -28,10 +31,26 @@ def model(state, params, ind_name):
try:
mdl = state.vars.loaded_models[name]
if len(state.bars["close"]) < mdl.input_sequences:
return 0, 0
#return -2, f"too soon - not enough data for seq {seq=}"
value = mdl.predict(state.bars, state.indicators)
#Optimalizovano, aby se v kazde iteraci nemusel volat len
if state.cache.get(name, {}).get("skip_init", False) is False:
if mdl.use_cbars is False:
if len(state.bars["close"]) < mdl.input_sequences:
return 0, 0
else:
state.cache[name]["skip_init"] = True
state.cache[name]["indicators"] = state.indicators
state.cache[name]["bars"] = state.bars if mdl.use_bars else {}
#return -2, f"too soon - not enough data for seq {seq=}"
else:
if len(state.cbar_indicators["time"]) < mdl.input_sequences:
return 0, 0
else:
state.cache[name]["skip_init"] = True
state.cache[name]["indicators"] = state.cbar_indicators
state.cache[name]["bars"] = state.bars if mdl.use_bars else {}
value = mdl.predict(state.cache[name]["bars"], state.cache[name]["indicators"])
return 0, value
except Exception as e:
printanyway(str(e)+format_exc())

View File

@ -1,7 +1,7 @@
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.strategyblocks.indicators.helpers import get_source_series
from v2realbot.strategyblocks.indicators.helpers import get_source_series, find_index_optimized
from rich import print as printanyway
from traceback import format_exc
import numpy as np
@ -13,13 +13,15 @@ def slope(state, params, name):
source = safe_get(params, "source", None)
source_series = get_source_series(state, source)
lookback_type = safe_get(params, "lookback_type", "positions")
lookback_unit = safe_get(params, "lookback_unit", "position")
lookback = safe_get(params, "lookback", 5)
lookback_priceline = safe_get(params, "lookback_priceline", None) #bars|close
lookback_series = get_source_series(state, lookback_priceline)
#ukládáme si do cache incializaci
cache = safe_get(params, "CACHE", None)
match lookback_type:
case "positions":
match lookback_unit:
case "position":
try:
lookbackprice = lookback_series[-lookback-1]
lookbacktime = state.bars.updated[-lookback-1]
@ -27,17 +29,22 @@ def slope(state, params, name):
max_delka = len(lookback_series)
lookbackprice =lookback_series[-max_delka]
lookbacktime = state.bars.updated[-max_delka]
case "seconds":
#předpokládáme, že lookback_priceline je ve formě #bars|close
#abychom ziskali relevantní time
split_index = lookback_priceline.find("|")
if split_index == -1:
return -2, "for time it is required in format bars|close"
dict_name = lookback_priceline[:split_index]
time_series = getattr(state, dict_name)["time"]
case "second":
if state.cache.get(name, None) is None:
#předpokládáme, že lookback_priceline je ve formě #bars|close
#abychom ziskali relevantní time
split_index = lookback_priceline.find("|")
if split_index == -1:
return -2, "for time it is required in format bars|close"
dict_name = lookback_priceline[:split_index]
time_series = getattr(state, dict_name)["time"]
state.cache[name]["time_series"] = time_series
else:
time_series = state.cache[name]["time_series"]
lookback_idx = find_index_optimized(time_list=time_series, seconds=lookback)
lookbackprice = lookback_series[lookback_idx]
lookbacktime = time_series[lookback_idx]
lookbacktime = time_series[lookback_idx]
#výpočet úhlu - a jeho normalizace
currval = source_series[-1]
@ -46,24 +53,3 @@ def slope(state, params, name):
state.ilog(lvl=1,e=f"INSIDE {name}:{funcName} {slope} {source=} {lookback=}", currval_source=currval, lookbackprice=lookbackprice, lookbacktime=lookbacktime, **params)
return 0, slope
"""
TODO pripadne dat do
Finds index of first value less than X seconds
This version assumes:
time_list is always non-empty and sorted.
There's always a timestamp at least 5 seconds before the current time.
"""
def find_index_optimized(time_list, seconds):
current_time = time_list[-1]
threshold = current_time - seconds
left, right = 0, len(time_list) - 1
while left < right:
mid = (left + right) // 2
if time_list[mid] < threshold:
left = mid + 1
else:
right = mid
return left if time_list[left] >= threshold else None

View File

@ -1,7 +1,7 @@
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.strategyblocks.indicators.helpers import get_source_series
from v2realbot.strategyblocks.indicators.helpers import get_source_series, find_index_optimized
from rich import print as printanyway
from traceback import format_exc
import numpy as np
@ -26,12 +26,18 @@ def target(state, params, name):
source_series = get_source_series(state, source)
#zatim podporovano jen bar - dokud nepridame o level vyse do "custom" save_to_past na urovni casu
window_length_value = safe_get(params, "window_length_value", None)
window_length_unit= safe_get(params, "window_length_unit", "bar")
window_length_unit= safe_get(params, "window_length_unit", "position")
smoothing_window= int(safe_get(params, "smoothing_window", 1)) #pocet jednotek casu, kdy se signal drzi (zatm jen pro cas, TODO pro pozice)
no_move_threshold= safe_get(params, "no_move_threshold", 0.033)
pct_change_full_scale = safe_get(params, "pct_change_full_scale", 0.1) #pct change that is considered for full scaling factor
move_base= safe_get(params, "move_base", 0.2) #base output value for move up/down, to this value scaling factor is added based on magnitude of change
scaling_factor= safe_get(params, "scaling_factor", 0.8)# This represents the maximum additional value to be added to 0.5, based on steepness of the move
#ukládáme si do cache incializaci
lookback_idx = None
#workaround for state
#params["last_activated_up"] = None
#params["last_activated_down"] = None
if window_length_value is None or source is None:
return -2, "window_length_value/source required"
@ -42,16 +48,65 @@ def target(state, params, name):
future_price = float(source_series[-1])
try:
current_price = float(source_series[-window_length_value])
except IndexError:
return 0, val
#LOOKUP of current_price (price in the past)
if window_length_unit == "position":
try:
current_price = float(source_series[-window_length_value])
except IndexError:
return 0, val
#seconds type, predpoklada opet source v kompletnim tvaru a predpoklada save_to_past typu seconds
else:
if state.cache.get(name, None) is None:
split_index = source.find("|")
if split_index == -1:
return -2, "for second based window length, source is required in format bars|close"
dict_name = source[:split_index]
time_series = getattr(state, dict_name)["time"]
state.cache[name]["time_series"] = time_series
else:
time_series = state.cache[name]["time_series"]
lookback_idx = find_index_optimized(time_list=time_series, seconds=window_length_value)
current_price = source_series[lookback_idx]
upper_move_threshold = add_pct(no_move_threshold, current_price)
lower_move_threshold = add_pct(-no_move_threshold, current_price)
#5 a 7
def fill_skipped_indx(pastidx, curridx):
#mame mezi indexy mezeru, iterujeme mezi temito a doplnime predchozi hodnoty primo do indikatoru
if pastidx + 1 < curridx:
for i in range(pastidx+1, curridx):
ind_dict = get_source_series(state, name)
ind_dict[i] = ind_dict[i-1]
if params.get("last_returned_idx", None) is not None:
#pokud je mezi poslednim indexem a aktualnim dira pak je vyplnime predchozi hodnotou
fill_skipped_indx(params["last_returned_idx"],lookback_idx)
#no move
if lower_move_threshold <= future_price <= upper_move_threshold:
#SMOOTHING WINDOW (3 secs) - workaround of state
#pokud no move, ale jsme 2 sekundy po signálu tak vracíme predchozi hodnotu (realizováno vrácním -2)
#zatim pouze pro casove okno
#po osvedceni do conf
if params.get("last_activated_up",None) is not None:
#jsme v okno, vracime predchozi hodnotu
last_plus_window = float(time_series[params["last_activated_up"]]) + smoothing_window
if last_plus_window > float(time_series[lookback_idx]):
val = params["last_returned_val"]
#okno prekroceno, nulujeme a vracime 0
else:
params["last_activated_up"] = None
elif params.get("last_activated_down",None) is not None:
last_plus_window = float(time_series[params["last_activated_down"]]) + smoothing_window
if last_plus_window > float(time_series[lookback_idx]):
val = params["last_returned_val"]
else:
params["last_activated_down"] = None
params["last_returned_val"] = val
params["last_returned_idx"] = lookback_idx
return 0, val
#calculates weight based on magnitude of change
@ -64,13 +119,21 @@ def target(state, params, name):
#price is bigger than threshold
if upper_move_threshold < future_price:
params["last_activated_down"] = None
magnitude_val = calculate_factor(current_price, future_price)
return 0, move_base + magnitude_val
params["last_activated_up"] = lookback_idx
params["last_returned_val"] = move_base + magnitude_val
params["last_returned_idx"] = lookback_idx
return 0, params["last_returned_val"]
#price is bigger than threshold
if lower_move_threshold > future_price:
params["last_activated_up"] = None
magnitude_val = calculate_factor(current_price, future_price)
return 0, - move_base - magnitude_val
params["last_activated_down"] = lookback_idx
params["last_returned_val"] = - move_base - magnitude_val
params["last_returned_idx"] = lookback_idx
return 0, params["last_returned_val"]
def pct_delta(base, second_number):

View File

@ -6,6 +6,7 @@ from v2realbot.indicators.indicators import ema
from traceback import format_exc
import importlib
import v2realbot.strategyblocks.indicators.custom as ci
from v2realbot.strategyblocks.indicators.helpers import find_index_optimized
def populate_dynamic_custom_indicator(data, state: StrategyState, name):
@ -43,6 +44,8 @@ def populate_dynamic_custom_indicator(data, state: StrategyState, name):
# např. 5 - znamená ulož hodnotu indikatoru 5 barů dozadu namísto posledni hodnoty - hodí se pro vytvareni targetu pro ML trening
save_to_past = int(safe_get(options, "save_to_past", 0))
save_to_past_unit = safe_get(options, "save_to_past_unit", "position")
def is_time_to_run():
# on_confirmed_only = true (def. False)
@ -135,6 +138,17 @@ def populate_dynamic_custom_indicator(data, state: StrategyState, name):
state.vars.indicators[name]["last_run_time"] = datetime.fromtimestamp(data["updated"]).astimezone(zoneNY)
state.vars.indicators[name]["last_run_index"] = data["index"]
#pomocna funkce
def save_to_past_func(indicators_dict,name,save_to_past_unit, steps, new_val):
if save_to_past_unit == "position":
indicators_dict[name][-1-steps]=new_val
#time
else:
##find index X seconds ago
lookback_idx = find_index_optimized(time_list=indicators_dict["time"], seconds=steps)
indicators_dict[name][lookback_idx]=new_val
# - volame custom funkci pro ziskani hodnoty indikatoru
# - tu ulozime jako novou hodnotu indikatoru a prepocteme MAcka pokud je pozadovane
# - pokud cas neni, nechavame puvodni, vcetna pripadneho MAcka
@ -145,15 +159,18 @@ def populate_dynamic_custom_indicator(data, state: StrategyState, name):
custom_function = eval(subtype)
res_code, new_val = custom_function(state, custom_params, name)
if res_code == 0:
indicators_dict[name][-1-save_to_past]=new_val
save_to_past_func(indicators_dict,name,save_to_past_unit, save_to_past, new_val)
state.ilog(lvl=1,e=f"IND {name} {subtype} VAL FROM FUNCTION: {new_val}", lastruntime=state.vars.indicators[name]["last_run_time"], lastrunindex=state.vars.indicators[name]["last_run_index"], save_to_past=save_to_past)
#prepocitame MA if required
if MA_length is not None:
src = indicators_dict[name][-MA_length:]
MA_res = ema(src, MA_length)
MA_value = round(MA_res[-1],7)
indicators_dict[name+"MA"][-1-save_to_past]=MA_value
save_to_past_func(indicators_dict,name+"MA",save_to_past_unit, save_to_past, MA_value)
state.ilog(lvl=0,e=f"IND {name}MA {subtype} {MA_value}",save_to_past=save_to_past)
return
else:
err = f"IND ERROR {name} {subtype}Funkce {custom_function} vratila {res_code} {new_val}."

View File

@ -2,6 +2,28 @@ from v2realbot.utils.utils import isrising, isfalling,isfallingc, isrisingc, zon
#from v2realbot.strategy.base import StrategyState
from traceback import format_exc
"""
TODO pripadne dat do
Finds index of first value less than X seconds
This version assumes:
time_list is always non-empty and sorted.
There's always a timestamp at least 5 seconds before the current time.
"""
def find_index_optimized(time_list, seconds):
current_time = time_list[-1]
threshold = current_time - seconds
left, right = 0, len(time_list) - 1
while left < right:
mid = (left + right) // 2
if time_list[mid] < threshold:
left = mid + 1
else:
right = mid
return left if time_list[left] >= threshold else None
#ZATIM tyto zkopirovany SEM DO HELPERS
#podle toho jak se osvedci se zakl.indikatory to s state
#zatim se mi to moc nezda

View File

@ -63,7 +63,7 @@ def populate_all_indicators(data, state: StrategyState):
else:
pass
#toto je spíše interní ukládání tick_price a tick_volume - s tím pak mohou pracovat jak bar based tak tick based indikatory
#toto je spíše interní ukládání tick_price a tick_volume a tick_tradenct - s tím pak mohou pracovat jak bar based tak tick based indikatory
#TODO do budoucna prejmenovat state.cbar_indicators na state.tick_indicators
populate_cbar_tick_price_indicator(data, state)

View File

@ -58,7 +58,7 @@ def initialize_dynamic_indicators(state):
modelname = safe_get(indsettings["cp"], 'name', None)
modelversion = safe_get(indsettings["cp"], 'version', "1")
if modelname is not None:
state.vars.loaded_models[modelname] = ml.load_model(modelname, modelversion, MODEL_DIR)
state.vars.loaded_models[modelname] = ml.load_model(modelname, modelversion, None, MODEL_DIR)
if state.vars.loaded_models[modelname] is not None:
printanyway(f"model {modelname} loaded")
else:

View File

@ -2,6 +2,7 @@ from v2realbot.config import DATA_DIR
from v2realbot.utils.utils import json_serial
from uuid import UUID, uuid4
import orjson
import json
from datetime import datetime
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
from v2realbot.common.db import pool, insert_queue
@ -59,7 +60,7 @@ def insert_log_multiple_queue(runner_id:UUID, loglist: list):
def get_log_window(runner_id: UUID, timestamp_from: float = 0, timestamp_to: float = 9682851459):
conn = pool.get_connection()
try:
conn.row_factory = lambda c, r: orjson.loads(r[0])
conn.row_factory = lambda c, r: json.loads(r[0])
c = conn.cursor()
res = c.execute(f"SELECT data FROM runner_logs WHERE runner_id='{str(runner_id)}' AND time >={timestamp_from} AND time <={timestamp_to} ORDER BY time")
finally: