cbar indicators + ml enhancements
This commit is contained in:
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
1
v2realbot/controller/runner_details.py
Normal file
1
v2realbot/controller/runner_details.py
Normal file
@ -0,0 +1 @@
|
||||
#PLACEHOLDER TO RUNNER_DETAILS SERVICES - refactored
|
||||
@ -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()
|
||||
|
||||
0
v2realbot/endpoints/__init__.py
Normal file
0
v2realbot/endpoints/__init__.py
Normal file
0
v2realbot/endpoints/archived_runners.py
Normal file
0
v2realbot/endpoints/archived_runners.py
Normal file
0
v2realbot/endpoints/batches.py
Normal file
0
v2realbot/endpoints/batches.py
Normal file
0
v2realbot/endpoints/configs.py
Normal file
0
v2realbot/endpoints/configs.py
Normal file
0
v2realbot/endpoints/models.py
Normal file
0
v2realbot/endpoints/models.py
Normal file
0
v2realbot/endpoints/runners.py
Normal file
0
v2realbot/endpoints/runners.py
Normal file
0
v2realbot/endpoints/stratins.py
Normal file
0
v2realbot/endpoints/stratins.py
Normal file
0
v2realbot/endpoints/testlists.py
Normal file
0
v2realbot/endpoints/testlists.py
Normal 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')
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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': [],
|
||||
|
||||
@ -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']
|
||||
@ -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:
|
||||
|
||||
@ -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}")
|
||||
@ -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}")
|
||||
@ -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
|
||||
@ -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):
|
||||
"""
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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":
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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
|
||||
@ -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):
|
||||
|
||||
@ -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}."
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
Reference in New Issue
Block a user