pridan zaklad k custom indikatorum
This commit is contained in:
23
testy/evalscope.py
Normal file
23
testy/evalscope.py
Normal file
@ -0,0 +1,23 @@
|
||||
# def my_function():
|
||||
# return "Hello, World!"
|
||||
|
||||
|
||||
# def call_function_by_name():
|
||||
# func_name = "my_function"
|
||||
# # Use eval to call the function
|
||||
# result = eval(func_name)()
|
||||
|
||||
def my_function():
|
||||
return "Hello, World!"
|
||||
|
||||
|
||||
def call_function_by_name():
|
||||
# Create a closure to capture the my_function function
|
||||
def inner_function():
|
||||
return eval("my_function")()
|
||||
|
||||
result = inner_function()
|
||||
return result
|
||||
|
||||
|
||||
print(call_function_by_name())
|
||||
184
testy/higherhighs.py
Normal file
184
testy/higherhighs.py
Normal file
@ -0,0 +1,184 @@
|
||||
def is_rising_trend(price_list):
|
||||
"""
|
||||
This function determines whether prices are consistently creating higher highs and higher lows.
|
||||
|
||||
Args:
|
||||
price_list: A list of prices.
|
||||
|
||||
Returns:
|
||||
True if the prices are in a rising trend, False otherwise.
|
||||
"""
|
||||
|
||||
if len(price_list) < 2:
|
||||
return False
|
||||
#
|
||||
global last_last_low
|
||||
global last_high
|
||||
global last_low
|
||||
global last_last_high
|
||||
global last_last_low
|
||||
last_high = price_list[0]
|
||||
last_low = None
|
||||
last_last_high = price_list[0]
|
||||
last_last_low = price_list[0]
|
||||
print(price_list)
|
||||
|
||||
for i in range(1, len(price_list)):
|
||||
print("processing",price_list[i])
|
||||
|
||||
#pokud je dalsi rostouci
|
||||
if price_list[i] > price_list[i-1]:
|
||||
#je vetsi nez LH - stává se LH
|
||||
if price_list[i] > last_high:
|
||||
#last_last_high = last_high
|
||||
last_high = price_list[i]
|
||||
#print("nova last last high",last_last_high)
|
||||
print("nove last high",last_high)
|
||||
|
||||
#pokud je klesajici
|
||||
elif price_list[i] < price_list[i-1]:
|
||||
|
||||
#pokud je cena nad last last jsme ok
|
||||
if price_list[i] > last_last_low:
|
||||
if last_low is None or price_list[i] < last_low:
|
||||
if last_low is not None:
|
||||
#vytvorime nove last last low
|
||||
last_last_low = last_low
|
||||
print("nova last last low",last_last_low)
|
||||
#rovnou porovname cenu zda neklesla
|
||||
if price_list[i] < last_last_low:
|
||||
print("kleslo pod last last low")
|
||||
return False
|
||||
#mame nove last low
|
||||
last_low = price_list[i]
|
||||
print("nove last low",last_low)
|
||||
else:
|
||||
print("kleslo pod last last low, neroste")
|
||||
return False
|
||||
|
||||
print("funkce skoncila, stale roste")
|
||||
return True
|
||||
|
||||
# Example usage:
|
||||
#price_list = [1,2,3,2,2.5,3,1.8,4,5,4,4.5,4.3,4.8,4.5,6]
|
||||
|
||||
|
||||
price_list = [
|
||||
# -0.0106,
|
||||
# -0.001,
|
||||
# 0.0133,
|
||||
# 0.0116,
|
||||
# 0.0075,
|
||||
-0.015,
|
||||
-0.0142,
|
||||
-0.0071,
|
||||
-0.0077,
|
||||
-0.0083,
|
||||
0.0016,
|
||||
0.0266,
|
||||
0.0355,
|
||||
0.0455,
|
||||
0.0563,
|
||||
0.1064,
|
||||
0.1283,
|
||||
0.1271,
|
||||
0.1277,
|
||||
0.1355,
|
||||
0.152,
|
||||
0.1376,
|
||||
0.1164,
|
||||
0.1115,
|
||||
0.102,
|
||||
0.0808,
|
||||
0.0699,
|
||||
0.0625,
|
||||
0.0593,
|
||||
0.0485,
|
||||
0.0323,
|
||||
0.0382,
|
||||
0.0403,
|
||||
0.0441,
|
||||
0.0526,
|
||||
0.0728,
|
||||
0.0841,
|
||||
0.1029,
|
||||
0.1055,
|
||||
0.0964,
|
||||
0.0841,
|
||||
0.0677,
|
||||
0.0782,
|
||||
0.0877,
|
||||
0.1099,
|
||||
0.1215,
|
||||
0.1379,
|
||||
0.1234,
|
||||
0.1,
|
||||
0.0949,
|
||||
0.1133,
|
||||
0.1428,
|
||||
0.1525,
|
||||
0.166,
|
||||
0.1788,
|
||||
0.1901,
|
||||
0.1967,
|
||||
0.2099,
|
||||
0.2407,
|
||||
0.2719,
|
||||
0.2897,
|
||||
0.3101,
|
||||
0.331,
|
||||
0.328,
|
||||
0.3241,
|
||||
0.3258,
|
||||
0.3275,
|
||||
0.3188,
|
||||
0.3071,
|
||||
0.2942,
|
||||
0.2939,
|
||||
0.277,
|
||||
0.2498,
|
||||
0.2464,
|
||||
0.2413,
|
||||
0.2377,
|
||||
0.2112,
|
||||
0.2076,
|
||||
0.2018,
|
||||
0.1975,
|
||||
0.1814,
|
||||
0.1776,
|
||||
0.1761,
|
||||
0.1868,
|
||||
0.1961,
|
||||
0.2016,
|
||||
0.2313,
|
||||
0.2485,
|
||||
0.2668,
|
||||
0.2973,
|
||||
0.3278,
|
||||
0.3581,
|
||||
0.3893,
|
||||
0.3997,
|
||||
0.4176,
|
||||
0.4285,
|
||||
0.4369,
|
||||
0.4457,
|
||||
0.4524,
|
||||
0.4482,
|
||||
0.4439,
|
||||
0.4302,
|
||||
0.4205,
|
||||
0.4278,
|
||||
0.4345,
|
||||
0.4403,
|
||||
0.4504,
|
||||
0.4523,
|
||||
0.461,
|
||||
0.4649,
|
||||
0.4618,
|
||||
0.4675,
|
||||
0.4724]
|
||||
|
||||
|
||||
result = is_rising_trend(price_list)
|
||||
print(result) # This will print [(4, 60), (7, 62)] for the provided example
|
||||
|
||||
@ -9,9 +9,10 @@ from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeSt
|
||||
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, round2five, is_open_rush, is_close_rush, is_still, is_window_open, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial
|
||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||
from v2realbot.common.model import SLHistory
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from v2realbot.config import KW
|
||||
from uuid import uuid4
|
||||
import random
|
||||
import json
|
||||
#from icecream import install, ic
|
||||
#from rich import print
|
||||
@ -250,9 +251,174 @@ def next(data, state: StrategyState):
|
||||
populate_dynamic_ema_indicator(name = name)
|
||||
elif type == "NATR":
|
||||
populate_dynamic_natr_indicator(name = name)
|
||||
elif type == "custom":
|
||||
populate_dynamic_custom_indicator(name = name)
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
#WIP -
|
||||
def populate_dynamic_custom_indicator(name):
|
||||
ind_type = "custom"
|
||||
options = safe_get(state.vars.indicators, name, None)
|
||||
if options is None:
|
||||
state.ilog(e=f"No options for {name} in stratvars")
|
||||
return
|
||||
|
||||
if safe_get(options, "type", False) is False or safe_get(options, "type", False) != ind_type:
|
||||
state.ilog(e="Type error")
|
||||
return
|
||||
|
||||
#poustet kazdy tick nebo jenom na confirmed baru (on_confirmed_only = true)
|
||||
subtype = safe_get(options, 'subtype', False)
|
||||
if subtype is False:
|
||||
state.ilog(e=f"No subtype for {name} in stratvars")
|
||||
return
|
||||
|
||||
#if MA is required
|
||||
MA_length = safe_get(options, "MA_length", None)
|
||||
|
||||
def is_time_to_run():
|
||||
# on_confirmed_only = true (def. False)
|
||||
# start_at_bar_index = 2 (def. None)
|
||||
# start_at_time = "9:31" (def. None)
|
||||
# repeat_every_Nbar = N (def.None) (opakovat každý N bar, 1 - každý bar, 2 - každý 2., 0 - pouze jednou)
|
||||
# repeat_every_Nmin = N (def. None) opakovat každých N minut
|
||||
|
||||
on_confirmed_only = safe_get(options, 'on_confirmed_only', False)
|
||||
start_at_bar_index = safe_get(options, 'start_at_bar_index', None)
|
||||
start_at_time = safe_get(options, 'start_at_time', None) # "9:30"
|
||||
repeat_every_Nbar = safe_get(options, 'repeat_every_Nbar', None)
|
||||
repeat_every_Nmin = safe_get(options, 'repeat_every_Nmin', None)
|
||||
|
||||
#stavové promenne v ramci indikatoru last_run_time a last_run_index - pro repeat_every.. direktivy
|
||||
last_run_time = safe_get(options, 'last_run_time', None)
|
||||
last_run_index = safe_get(options, 'last_run_index', None)
|
||||
|
||||
#confirmed
|
||||
cond = on_confirmed_only is False or (on_confirmed_only is True and data['confirmed']==1)
|
||||
if cond is False:
|
||||
return cond, "not confirmed"
|
||||
|
||||
#start_at_time - v rámci optimalizace presunout do INIT parametru indikátorů, které se naplní v initu a celou dobu se nemění
|
||||
if start_at_time is not None:
|
||||
dt_now = datetime.fromtimestamp(data["updated"]).astimezone(zoneNY)
|
||||
# Parse the maxTime string into a datetime object with the same date as timeA
|
||||
req_start_time = datetime.strptime(start_at_time, "%H:%M").replace(
|
||||
year=dt_now.year, month=dt_now.month, day=dt_now.day)
|
||||
|
||||
# Compare the time components (hours and minutes) of timeA and maxTime
|
||||
if dt_now.time() > req_start_time.time():
|
||||
state.ilog(e=f"IND {name} {subtype} START FROM TIME - PASSED: now:{dt_now.time()} reqtime:{req_start_time.time()}")
|
||||
else:
|
||||
state.ilog(e=f"IND {name} {subtype} START FROM TIME - NOT YET: now:{dt_now.time()} reqtime:{req_start_time.time()}")
|
||||
cond = False
|
||||
|
||||
if cond is False:
|
||||
return cond, "start_at_time not yet"
|
||||
|
||||
#start_on_bar = 0
|
||||
if start_at_bar_index is not None:
|
||||
cond = start_at_bar_index < data["index"]
|
||||
if cond:
|
||||
state.ilog(e=f"IND {name} {subtype} START FROM BAR - PASSED: now:{data['index']} reqbar:{start_at_bar_index}")
|
||||
else:
|
||||
state.ilog(e=f"IND {name} {subtype} START FROM BAR - NOT YET: now:{data['index']} reqbar:{start_at_bar_index}")
|
||||
|
||||
if cond is False:
|
||||
return cond, "start_at_bar_index not yet"
|
||||
|
||||
#pokud 0 - opakujeme jednou, pokud 1 tak opakujeme vzdy, jinak dle poctu
|
||||
if repeat_every_Nbar is not None:
|
||||
#jiz bezelo - delame dalsi checky, pokud nebezelo, poustime jako true
|
||||
if last_run_index is not None:
|
||||
required_bar_to_run = last_run_index + repeat_every_Nbar
|
||||
if repeat_every_Nbar == 0:
|
||||
state.ilog(e=f"IND {name} {subtype} RUN ONCE ALREADY at:{last_run_index} at:{last_run_time}", repeat_every_Nbar=repeat_every_Nbar, last_run_index=last_run_index)
|
||||
cond = False
|
||||
elif repeat_every_Nbar == 1:
|
||||
pass
|
||||
elif data["index"] < required_bar_to_run:
|
||||
state.ilog(e=f"IND {name} {subtype} REPEAT EVERY N BAR WAITING: req:{required_bar_to_run} now:{data['index']}", repeat_every_Nbar=repeat_every_Nbar, last_run_index=last_run_index)
|
||||
cond = False
|
||||
|
||||
if cond is False:
|
||||
return cond, "repeat_every_Nbar not yet"
|
||||
|
||||
#pokud nepozadovano, pak poustime
|
||||
if repeat_every_Nmin is not None:
|
||||
#porovnavame jen pokud uz bezelo
|
||||
if last_run_time is not None:
|
||||
required_time_to_run = last_run_time + timedelta(minutes=repeat_every_Nmin)
|
||||
datetime_now = datetime.fromtimestamp(data["updated"]).astimezone(zoneNY)
|
||||
if datetime_now < required_time_to_run:
|
||||
state.ilog(e=f"IND {name} {subtype} REPEAT EVERY {repeat_every_Nmin}MINS WAITING", last_run_time=last_run_time, required_time_to_run=required_time_to_run, datetime_now=datetime_now)
|
||||
cond = False
|
||||
|
||||
if cond is False:
|
||||
return cond, "repeat_every_Nmin not yet"
|
||||
|
||||
return cond, "ok"
|
||||
|
||||
#testing custom indicator CODE
|
||||
#TODO customer params bud implicitne **params nebo jako dict
|
||||
# return 0, new_val or -2, "err msg"
|
||||
def opengap(params):
|
||||
funcName = "opengap"
|
||||
param1 = safe_get(params, "param1")
|
||||
param2 = safe_get(params, "param2")
|
||||
state.ilog(e=f"INSIDE {funcName} {param1=} {param2=}", **params)
|
||||
return 0, random.randint(10, 20)
|
||||
|
||||
should_run, msg = is_time_to_run()
|
||||
|
||||
if should_run:
|
||||
#TODO get custom params
|
||||
custom_params = safe_get(options, "cp", None)
|
||||
#vyplnime last_run_time a last_run_index
|
||||
state.vars.indicators[name]["last_run_time"] = datetime.fromtimestamp(data["updated"]).astimezone(zoneNY)
|
||||
state.vars.indicators[name]["last_run_index"] = data["index"]
|
||||
|
||||
# - 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
|
||||
#pozor jako defaultní hodnotu dává engine 0 - je to ok?
|
||||
try:
|
||||
custom_function = eval(subtype)
|
||||
res_code, new_val = custom_function(custom_params)
|
||||
if res_code == 0:
|
||||
state.indicators[name][-1]=new_val
|
||||
state.ilog(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"])
|
||||
#prepocitame MA if required
|
||||
if MA_length is not None:
|
||||
src = state.indicators[name][-MA_length:]
|
||||
MA_res = ema(src, MA_length)
|
||||
MA_value = round(MA_res[-1],4)
|
||||
state.indicators[name+"MA"][-1]=MA_value
|
||||
state.ilog(e=f"IND {name}MA {subtype} {MA_value}")
|
||||
|
||||
else:
|
||||
raise ValueError(f"IND ERROR {name} {subtype}Funkce {custom_function} vratila {res_code} {new_val}.")
|
||||
|
||||
except Exception as e:
|
||||
if len(state.indicators[name]) >= 2:
|
||||
state.indicators[name][-1]=state.indicators[name][-2]
|
||||
if MA_length is not None and len(state.indicators[name+"MA"]):
|
||||
state.indicators[name+"MA"][-1]=state.indicators[name+"MA"][-2]
|
||||
state.ilog(e=f"IND ERROR {name} {subtype} necháváme původní", message=str(e)+format_exc())
|
||||
|
||||
else:
|
||||
state.ilog(e=f"IND {name} {subtype} COND NOT READY: {msg}")
|
||||
|
||||
#not time to run
|
||||
if len(state.indicators[name]) >= 2:
|
||||
state.indicators[name][-1]=state.indicators[name][-2]
|
||||
|
||||
if MA_length is not None and len(state.indicators[name+"MA"]):
|
||||
state.indicators[name+"MA"][-1]=state.indicators[name+"MA"][-2]
|
||||
|
||||
state.ilog(e=f"IND {name} {subtype} NOT TIME TO RUN - value(and MA) still original")
|
||||
|
||||
#EMA INDICATOR
|
||||
# type = EMA, source = [close, vwap, hlcc4], length = [14], on_confirmed_only = [true, false]
|
||||
def populate_dynamic_ema_indicator(name):
|
||||
@ -502,60 +668,72 @@ def next(data, state: StrategyState):
|
||||
|
||||
if on_confirmed_only is False or (on_confirmed_only is True and data['confirmed']==1):
|
||||
try:
|
||||
#slow_slope = 99
|
||||
slope_lookback = safe_get(options, 'slope_lookback', 100)
|
||||
lookback_priceline = safe_get(options, 'lookback_priceline', None)
|
||||
lookback_offset = safe_get(options, 'lookback_offset', 25)
|
||||
minimum_slope = safe_get(options, 'minimum_slope', 25)
|
||||
maximum_slope = safe_get(options, "maximum_slope",0.9)
|
||||
lookback_offset = safe_get(options, 'lookback_offset', 25)
|
||||
|
||||
#lookback has to be even
|
||||
if lookback_offset % 2 != 0:
|
||||
lookback_offset += 1
|
||||
#jako levy body pouzivame lookback_priceline INDIKATOR vzdaleny slope_lookback barů
|
||||
if lookback_priceline is not None:
|
||||
try:
|
||||
lookbackprice = state.indicators[lookback_priceline][-slope_lookback-1]
|
||||
lookbacktime = state.bars.updated[-slope_lookback-1]
|
||||
except IndexError:
|
||||
max_delka = len(state.indicators[lookback_priceline])
|
||||
lookbackprice = state.indicators[lookback_priceline][-max_delka]
|
||||
lookbacktime = state.bars.updated[-max_delka]
|
||||
|
||||
#TBD pripdadne /2
|
||||
if len(state.bars.close) > (slope_lookback + lookback_offset):
|
||||
#test prumer nejvyssi a nejnizsi hodnoty
|
||||
# if name == "slope":
|
||||
|
||||
#levy bod bude vzdy vzdaleny o slope_lookback
|
||||
#ten bude prumerem hodnot lookback_offset a to tak ze polovina offsetu z kazde strany
|
||||
array_od = slope_lookback + int(lookback_offset/2)
|
||||
array_do = slope_lookback - int(lookback_offset/2)
|
||||
lookbackprice_array = state.bars.vwap[-array_od:-array_do]
|
||||
|
||||
#dame na porovnani jen prumer
|
||||
lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3)
|
||||
#lookbackprice = round((min(lookbackprice_array)+max(lookbackprice_array))/2,3)
|
||||
# else:
|
||||
# #puvodni lookback a od te doby dozadu offset
|
||||
# array_od = slope_lookback + lookback_offset
|
||||
# array_do = slope_lookback
|
||||
# lookbackprice_array = state.bars.vwap[-array_od:-array_do]
|
||||
# #obycejný prumer hodnot
|
||||
# lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3)
|
||||
|
||||
lookbacktime = state.bars.time[-slope_lookback]
|
||||
else:
|
||||
#kdyz neni dostatek hodnot, pouzivame jako levy bod open hodnotu close[0]
|
||||
#lookbackprice = state.bars.vwap[0]
|
||||
|
||||
#dalsi vyarianta-- lookback je pole z toho všeho co mame
|
||||
#lookbackprice = Average(state.bars.vwap)
|
||||
#NEMAME LOOKBACK PRICLINE - pouzivame stary způsob výpočtu, toto pozdeji decomissionovat
|
||||
#lookback has to be even
|
||||
if lookback_offset % 2 != 0:
|
||||
lookback_offset += 1
|
||||
|
||||
|
||||
#TBD pripdadne /2
|
||||
if len(state.bars.close) > (slope_lookback + lookback_offset):
|
||||
#test prumer nejvyssi a nejnizsi hodnoty
|
||||
# if name == "slope":
|
||||
|
||||
#pokud neni dostatek, bereme vzdy prvni petinu z dostupnych barů
|
||||
# a z ní uděláme průměr
|
||||
cnt = len(state.bars.close)
|
||||
if cnt>5:
|
||||
sliced_to = int(cnt/5)
|
||||
lookbackprice= Average(state.bars.vwap[:sliced_to])
|
||||
lookbacktime = state.bars.time[int(sliced_to/2)]
|
||||
#levy bod bude vzdy vzdaleny o slope_lookback
|
||||
#ten bude prumerem hodnot lookback_offset a to tak ze polovina offsetu z kazde strany
|
||||
array_od = slope_lookback + int(lookback_offset/2)
|
||||
array_do = slope_lookback - int(lookback_offset/2)
|
||||
lookbackprice_array = state.bars.vwap[-array_od:-array_do]
|
||||
|
||||
#dame na porovnani jen prumer
|
||||
lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3)
|
||||
#lookbackprice = round((min(lookbackprice_array)+max(lookbackprice_array))/2,3)
|
||||
# else:
|
||||
# #puvodni lookback a od te doby dozadu offset
|
||||
# array_od = slope_lookback + lookback_offset
|
||||
# array_do = slope_lookback
|
||||
# lookbackprice_array = state.bars.vwap[-array_od:-array_do]
|
||||
# #obycejný prumer hodnot
|
||||
# lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3)
|
||||
|
||||
lookbacktime = state.bars.time[-slope_lookback]
|
||||
else:
|
||||
lookbackprice = Average(state.bars.vwap)
|
||||
lookbacktime = state.bars.time[0]
|
||||
|
||||
state.ilog(e=f"IND {name} slope - not enough data bereme left bod open", slope_lookback=slope_lookback, lookbackprice=lookbackprice)
|
||||
#kdyz neni dostatek hodnot, pouzivame jako levy bod open hodnotu close[0]
|
||||
#lookbackprice = state.bars.vwap[0]
|
||||
|
||||
#dalsi vyarianta-- lookback je pole z toho všeho co mame
|
||||
#lookbackprice = Average(state.bars.vwap)
|
||||
|
||||
|
||||
|
||||
#pokud neni dostatek, bereme vzdy prvni petinu z dostupnych barů
|
||||
# a z ní uděláme průměr
|
||||
cnt = len(state.bars.close)
|
||||
if cnt>5:
|
||||
sliced_to = int(cnt/5)
|
||||
lookbackprice= Average(state.bars.vwap[:sliced_to])
|
||||
lookbacktime = state.bars.time[int(sliced_to/2)]
|
||||
else:
|
||||
lookbackprice = Average(state.bars.vwap)
|
||||
lookbacktime = state.bars.time[0]
|
||||
|
||||
state.ilog(e=f"IND {name} slope - not enough data bereme left bod open", slope_lookback=slope_lookback, lookbackprice=lookbackprice)
|
||||
|
||||
#výpočet úhlu - a jeho normalizace
|
||||
slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100
|
||||
@ -577,7 +755,9 @@ def next(data, state: StrategyState):
|
||||
state.indicators[name+"MA"][-1]=slopeMA
|
||||
last_slopesMA = state.indicators[name+"MA"][-10:]
|
||||
|
||||
state.ilog(e=f"{name=} {slope=} {slopeMA=}", msg=f"{lookbackprice=} {lookbacktime=}", slope_lookback=slope_lookback, lookbackoffset=lookback_offset, lookbacktime=lookbacktime, minimum_slope=minimum_slope, last_slopes=state.indicators[name][-10:], last_slopesMA=last_slopesMA)
|
||||
lb_priceline_string = "from "+lookback_priceline if lookback_priceline is not None else ""
|
||||
|
||||
state.ilog(e=f"IND {name} {lb_priceline_string} {slope=} {slopeMA=}", msg=f"{lookbackprice=} {lookbacktime=}", lookback_priceline=lookback_priceline, lookbackprice=lookbackprice, lookbacktime=lookbacktime, slope_lookback=slope_lookback, lookbackoffset=lookback_offset, minimum_slope=minimum_slope, last_slopes=state.indicators[name][-10:], last_slopesMA=last_slopesMA)
|
||||
#dale pracujeme s timto MAckovanym slope
|
||||
#slope = slopeMA
|
||||
|
||||
@ -764,7 +944,91 @@ def next(data, state: StrategyState):
|
||||
|
||||
return price2dec(float(state.avgp)+normalized_max_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_max_profit,3)
|
||||
|
||||
#TBD pripadne opet dat parsovani pole do INITu
|
||||
|
||||
def reverse_conditions_met(direction: TradeDirection):
|
||||
if direction == TradeDirection.LONG:
|
||||
smer = "long"
|
||||
else:
|
||||
smer = "short"
|
||||
|
||||
directive_name = "exit_cond_only_on_confirmed"
|
||||
exit_cond_only_on_confirmed = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||
|
||||
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
||||
state.ilog("REVERSAL CHECK COND ONLY ON CONFIRMED BAR")
|
||||
return False
|
||||
|
||||
#TOTO zatim u REVERSU neresime
|
||||
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
||||
# directive_name = "exit_cond_min_profit"
|
||||
# exit_cond_min_profit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||
|
||||
# #máme nastavený exit_cond_min_profit
|
||||
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
|
||||
# # , zjistíme aktuální cenu a přičteme k avgp tento profit a podle toho pustime dal
|
||||
|
||||
# if exit_cond_min_profit is not None:
|
||||
# exit_cond_min_profit_normalized = normalize_tick(float(exit_cond_min_profit))
|
||||
# exit_cond_goal_price = price2dec(float(state.avgp)+exit_cond_min_profit_normalized,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-exit_cond_min_profit_normalized,3)
|
||||
# curr_price = float(data["close"])
|
||||
# state.ilog(e=f"EXIT COND min profit {exit_cond_goal_price=} {exit_cond_min_profit=} {exit_cond_min_profit_normalized=} {curr_price=}")
|
||||
# if (int(state.positions) < 0 and curr_price<=exit_cond_goal_price) or (int(state.positions) > 0 and curr_price>=exit_cond_goal_price):
|
||||
# state.ilog(e=f"EXIT COND min profit PASS - POKRACUJEME")
|
||||
# else:
|
||||
# state.ilog(e=f"EXIT COND min profit NOT PASS")
|
||||
# return False
|
||||
|
||||
#TOTO ZATIM NEMA VYZNAM
|
||||
# options = safe_get(state.vars, 'exit_conditions', None)
|
||||
# if options is None:
|
||||
# state.ilog(e="No options for exit conditions in stratvars")
|
||||
# return False
|
||||
|
||||
# disable_exit_proteciton_when = dict(AND=dict(), OR=dict())
|
||||
|
||||
# #preconditions
|
||||
# disable_exit_proteciton_when['disabled_in_config'] = safe_get(options, 'enabled', False) is False
|
||||
# #too good to be true (maximum profit)
|
||||
# #disable_sell_proteciton_when['tgtbt_reached'] = safe_get(options, 'tgtbt', False) is False
|
||||
# disable_exit_proteciton_when['disable_if_positions_above'] = int(safe_get(options, 'disable_if_positions_above', 0)) < abs(int(state.positions))
|
||||
|
||||
# #testing preconditions
|
||||
# result, conditions_met = eval_cond_dict(disable_exit_proteciton_when)
|
||||
# if result:
|
||||
# state.ilog(e=f"EXIT_CONDITION for{smer} DISABLED by {conditions_met}", **conditions_met)
|
||||
# return False
|
||||
|
||||
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
||||
state.ilog(e=f"REVERSE CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
|
||||
|
||||
mother_signal = state.vars.activeTrade.generated_by
|
||||
|
||||
if mother_signal is not None:
|
||||
cond_dict = state.vars.conditions[KW.reverse][state.vars.activeTrade.generated_by][smer]
|
||||
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
|
||||
state.ilog(e=f"REVERSE CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
||||
if result:
|
||||
return True
|
||||
|
||||
#OR neprosly testujeme AND
|
||||
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
|
||||
state.ilog(e=f"REVERSE CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict)
|
||||
if result:
|
||||
return True
|
||||
|
||||
|
||||
#pokud nemame mother signal nebo exit nevratil nic, fallback na common
|
||||
cond_dict = state.vars.conditions[KW.reverse]["common"][smer]
|
||||
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
|
||||
state.ilog(e=f"REVERSE CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
||||
if result:
|
||||
return True
|
||||
|
||||
#OR neprosly testujeme AND
|
||||
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
|
||||
state.ilog(e=f"REVERSE CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict)
|
||||
if result:
|
||||
return True
|
||||
|
||||
def exit_conditions_met(direction: TradeDirection):
|
||||
if direction == TradeDirection.LONG:
|
||||
@ -945,8 +1209,9 @@ def next(data, state: StrategyState):
|
||||
insert_SL_history()
|
||||
state.ilog(e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
|
||||
|
||||
def close_position(direction: TradeDirection, reason: str):
|
||||
state.ilog(e=f"CLOSING TRADE {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
|
||||
def close_position(direction: TradeDirection, reason: str, reverse: bool = False):
|
||||
reversal_text = "REVERSAL" if reverse else ""
|
||||
state.ilog(e=f"CLOSING TRADE {reversal_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
|
||||
if direction == TradeDirection.SHORT:
|
||||
res = state.buy(size=abs(int(state.positions)))
|
||||
if isinstance(res, int) and res < 0:
|
||||
@ -959,12 +1224,14 @@ def next(data, state: StrategyState):
|
||||
|
||||
else:
|
||||
raise Exception(f"unknow TradeDirection in close_position")
|
||||
|
||||
|
||||
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
||||
insert_SL_history()
|
||||
state.vars.pending = state.vars.activeTrade.id
|
||||
state.vars.activeTrade = None
|
||||
state.vars.last_exit_index = data["index"]
|
||||
state.vars.last_exit_index = data["index"]
|
||||
if reverse:
|
||||
state.vars.reverse_requested = True
|
||||
|
||||
def eval_close_position():
|
||||
curr_price = float(data['close'])
|
||||
@ -987,12 +1254,24 @@ def next(data, state: StrategyState):
|
||||
|
||||
#SL - execution
|
||||
if curr_price > state.vars.activeTrade.stoploss_value:
|
||||
close_position(direction=TradeDirection.SHORT, reason="SL REACHED")
|
||||
|
||||
directive_name = 'reverse_for_SL_exit_short'
|
||||
reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||
|
||||
close_position(direction=TradeDirection.SHORT, reason="SL REACHED", reverse=reverse_for_SL_exit)
|
||||
return
|
||||
|
||||
#REVERSE BASED ON REVERSE CONDITIONS
|
||||
if reverse_conditions_met(TradeDirection.SHORT):
|
||||
close_position(direction=TradeDirection.SHORT, reason="REVERSE COND MET", reverse=True)
|
||||
return
|
||||
|
||||
#CLOSING BASED ON EXIT CONDITIONS
|
||||
if exit_conditions_met(TradeDirection.SHORT):
|
||||
close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET")
|
||||
directive_name = 'reverse_for_cond_exit_short'
|
||||
reverse_for_cond_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||
|
||||
close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET", reverse=reverse_for_cond_exit)
|
||||
return
|
||||
|
||||
#PROFIT
|
||||
@ -1011,11 +1290,24 @@ def next(data, state: StrategyState):
|
||||
|
||||
#SL - execution
|
||||
if curr_price < state.vars.activeTrade.stoploss_value:
|
||||
close_position(direction=TradeDirection.LONG, reason="SL REACHED")
|
||||
return
|
||||
directive_name = 'reverse_for_SL_exit_long'
|
||||
reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||
|
||||
close_position(direction=TradeDirection.LONG, reason="SL REACHED", reverse=reverse_for_SL_exit)
|
||||
return
|
||||
|
||||
|
||||
#REVERSE BASED ON REVERSE CONDITIONS
|
||||
if reverse_conditions_met(TradeDirection.LONG):
|
||||
close_position(direction=TradeDirection.LONG, reason="REVERSE COND MET", reverse=True)
|
||||
return
|
||||
|
||||
#EXIT CONDITIONS
|
||||
if exit_conditions_met(TradeDirection.LONG):
|
||||
close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET")
|
||||
directive_name = 'reverse_for_cond_exit_long'
|
||||
reverse_for_cond_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||
|
||||
close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET", reverse=reverse_for_cond_exit)
|
||||
return
|
||||
|
||||
#PROFIT
|
||||
@ -1059,7 +1351,11 @@ def next(data, state: StrategyState):
|
||||
if state.vars.activeTrade:
|
||||
if state.vars.activeTrade.direction == TradeDirection.LONG:
|
||||
state.ilog(e="odesilame LONG ORDER", trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)))
|
||||
res = state.buy(size=state.vars.chunk)
|
||||
if state.vars.activeTrade.size is not None:
|
||||
size = state.vars.activeTrade.size
|
||||
else:
|
||||
size = state.vars.chunk
|
||||
res = state.buy(size=size)
|
||||
if isinstance(res, int) and res < 0:
|
||||
raise Exception(f"error in required operation LONG {res}")
|
||||
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||
@ -1073,7 +1369,11 @@ def next(data, state: StrategyState):
|
||||
state.vars.pending = state.vars.activeTrade.id
|
||||
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
|
||||
state.ilog(e="odesilame SHORT ORDER",trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)))
|
||||
res = state.sell(size=state.vars.chunk)
|
||||
if state.vars.activeTrade.size is not None:
|
||||
size = state.vars.activeTrade.size
|
||||
else:
|
||||
size = state.vars.chunk
|
||||
res = state.sell(size=size)
|
||||
if isinstance(res, int) and res < 0:
|
||||
raise Exception(f"error in required operation SHORT {res}")
|
||||
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||
@ -1245,11 +1545,19 @@ def next(data, state: StrategyState):
|
||||
if common_go_preconditions_check(signalname=name, options=options) is False:
|
||||
return
|
||||
|
||||
plugin = safe_get(options, 'plugin', None)
|
||||
# signal_plugin = "reverzni"
|
||||
# signal_plugin_run_once_at_index = 3
|
||||
#pokud existuje plugin, tak pro signal search volame plugin a ignorujeme conditiony
|
||||
signal_plugin = safe_get(options, 'plugin', None)
|
||||
signal_plugin_run_once_at_index = safe_get(options, 'signal_plugin_run_once_at_index', 3)
|
||||
|
||||
#pokud je plugin True, spusti se kod
|
||||
if plugin:
|
||||
execute_signal_generator_plugin(name)
|
||||
if signal_plugin is not None and signal_plugin_run_once_at_index==data["index"]:
|
||||
try:
|
||||
custom_function = eval(signal_plugin)
|
||||
custom_function()
|
||||
except NameError:
|
||||
state.ilog(e="Custom plugin {signal_plugin} not found")
|
||||
else:
|
||||
short_enabled = safe_get(options, "short_enabled",safe_get(state.vars, "short_enabled",True))
|
||||
long_enabled = safe_get(options, "long_enabled",safe_get(state.vars, "long_enabled",True))
|
||||
@ -1431,6 +1739,7 @@ def init(state: StrategyState):
|
||||
state.vars.conditions.setdefault(KW.dont_go,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.dont_go+"_" + smer +"_if", section=section)
|
||||
state.vars.conditions.setdefault(KW.go,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.go+"_" + smer +"_if", section=section)
|
||||
state.vars.conditions.setdefault(KW.exit,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.exit+"_" + smer +"_if", section=section)
|
||||
state.vars.conditions.setdefault(KW.reverse,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
|
||||
|
||||
# state.vars.work_dict_dont_do[signalname+"_"+ smer] = get_work_dict_with_directive(starts_with=signalname+"_dont_"+ smer +"_if")
|
||||
# state.vars.work_dict_signal_if[signalname+"_"+ smer] = get_work_dict_with_directive(starts_with=signalname+"_"+smer+"_if")
|
||||
@ -1439,6 +1748,7 @@ def init(state: StrategyState):
|
||||
section = state.vars.exit["conditions"]
|
||||
for smer in TradeDirection:
|
||||
state.vars.conditions.setdefault(KW.exit,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.exit+"_" + smer +"_if", section=section)
|
||||
state.vars.conditions.setdefault(KW.reverse,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
|
||||
|
||||
#init klice v extData pro ulozeni historie SL
|
||||
state.extData["sl_history"] = []
|
||||
@ -1450,6 +1760,8 @@ def init(state: StrategyState):
|
||||
state.vars.activeTrade = None #pending/Trade
|
||||
#obsahuje pripravene Trady ve frontě
|
||||
state.vars.prescribedTrades = []
|
||||
#flag pro reversal
|
||||
state.vars.reverse_requested = False
|
||||
|
||||
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
||||
#pripadne udelat refresh kazdych x-iterací
|
||||
|
||||
Binary file not shown.
@ -23,6 +23,7 @@ class Trade(BaseModel):
|
||||
generated_by: Optional[str] = None
|
||||
direction: TradeDirection
|
||||
entry_price: Optional[float] = None
|
||||
size: Optional[int] = None
|
||||
# stoploss_type: TradeStoplossType
|
||||
stoploss_value: Optional[float] = None
|
||||
profit: Optional[float] = 0
|
||||
|
||||
@ -102,4 +102,5 @@ class KW:
|
||||
dont_go: str = "dont_go"
|
||||
go: str = "go"
|
||||
activate: str = "activate"
|
||||
exit: str = "exit"
|
||||
exit: str = "exit"
|
||||
reverse: str = "reverse"
|
||||
@ -573,7 +573,7 @@
|
||||
<input type="text" id="itemName"><br><br>
|
||||
|
||||
<label for="jsonTextarea">JSON Data:</label><br>
|
||||
<textarea id="jsonTextarea" rows="10" cols="50"></textarea><br><br>
|
||||
<textarea id="jsonTextarea" rows="15" cols="75"></textarea><br><br>
|
||||
|
||||
<button type="button" id="saveButton">Save</button>
|
||||
<button type="button" id="addButton">Add</button>
|
||||
|
||||
@ -38,6 +38,7 @@ $(document).ready(function () {
|
||||
rows = archiveRecords.rows('.selected').data();
|
||||
var record1 = new Object()
|
||||
//console.log(JSON.stringify(rows))
|
||||
|
||||
record1 = JSON.parse(rows[0].strat_json)
|
||||
//record1.json = rows[0].json
|
||||
//record1.id = rows[0].id;
|
||||
@ -48,6 +49,8 @@ $(document).ready(function () {
|
||||
// record1.script = rows[0].script;
|
||||
// record1.open_rush = rows[0].open_rush;
|
||||
// record1.close_rush = rows[0].close_rush;
|
||||
//console.log(record1.stratvars_conf)
|
||||
|
||||
record1.stratvars_conf = TOML.parse(record1.stratvars_conf);
|
||||
record1.add_data_conf = TOML.parse(record1.add_data_conf);
|
||||
// record1.note = rows[0].note;
|
||||
@ -65,6 +68,7 @@ $(document).ready(function () {
|
||||
// record2.script = rows[1].script;
|
||||
// record2.open_rush = rows[1].open_rush;
|
||||
// record2.close_rush = rows[1].close_rush;
|
||||
|
||||
record2.stratvars_conf = TOML.parse(record2.stratvars_conf);
|
||||
record2.add_data_conf = TOML.parse(record2.add_data_conf);
|
||||
// record2.note = rows[1].note;
|
||||
|
||||
@ -356,6 +356,7 @@ pre {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 54px;
|
||||
width: 323px;
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
color: #2196F3;
|
||||
|
||||
@ -4,6 +4,7 @@ from v2realbot.utils.tlog import tlog, tlog_exception
|
||||
from v2realbot.enums.enums import Mode, Order, Account, RecordType
|
||||
#from alpaca.trading.models import TradeUpdate
|
||||
from v2realbot.common.model import TradeUpdate
|
||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||
from v2realbot.indicators.indicators import ema
|
||||
import json
|
||||
@ -13,7 +14,7 @@ from random import randrange
|
||||
from alpaca.common.exceptions import APIError
|
||||
import copy
|
||||
from threading import Event
|
||||
from uuid import UUID
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class StrategyClassicSL(Strategy):
|
||||
@ -42,13 +43,30 @@ class StrategyClassicSL(Strategy):
|
||||
send_to_telegram(f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}")
|
||||
self.se.set()
|
||||
|
||||
|
||||
async def add_reversal(self, direction: TradeDirection, size: int, signal_name: str):
|
||||
trade_to_add = Trade(
|
||||
id=uuid4(),
|
||||
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
|
||||
status=TradeStatus.READY,
|
||||
size=size,
|
||||
generated_by=signal_name,
|
||||
direction=direction,
|
||||
entry_price=None,
|
||||
stoploss_value = None)
|
||||
|
||||
self.state.vars.prescribedTrades.append(trade_to_add)
|
||||
|
||||
self.state.vars.reverse_requested = None
|
||||
|
||||
self.state.ilog(e=f"REVERZAL {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
|
||||
|
||||
async def orderUpdateBuy(self, data: TradeUpdate):
|
||||
o: Order = data.order
|
||||
signal_name = None
|
||||
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
||||
self.state.ilog(e="Příchozí BUY notif", msg=o.status, trade=json.loads(json.dumps(data, default=json_serial)))
|
||||
|
||||
|
||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
||||
|
||||
#jde o uzavření short pozice - počítáme PROFIT
|
||||
@ -84,7 +102,12 @@ class StrategyClassicSL(Strategy):
|
||||
#self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial)))
|
||||
|
||||
#test na maximalni profit/loss
|
||||
await self.check_max_profit_loss()
|
||||
await self.check_max_profit_loss()
|
||||
|
||||
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||
#jen při celém FILLU
|
||||
if data.event == TradeEvent.FILL and self.state.vars.reverse_requested:
|
||||
await self.add_reversal(direction=TradeDirection.LONG, size=data.qty, signal_name=signal_name)
|
||||
|
||||
else:
|
||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||
@ -109,6 +132,7 @@ class StrategyClassicSL(Strategy):
|
||||
#davame pryc pending
|
||||
self.state.vars.pending = None
|
||||
|
||||
|
||||
async def orderUpdateSell(self, data: TradeUpdate):
|
||||
|
||||
self.state.ilog(e="Příchozí SELL notif", msg=data.order.status, trade=json.loads(json.dumps(data, default=json_serial)))
|
||||
@ -148,6 +172,10 @@ class StrategyClassicSL(Strategy):
|
||||
|
||||
await self.check_max_profit_loss()
|
||||
|
||||
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||
if data.event == TradeEvent.FILL and self.state.vars.reverse_requested:
|
||||
await self.add_reversal(direction=TradeDirection.SHORT, size=data.qty, signal_name=signal_name)
|
||||
|
||||
else:
|
||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||
for trade in self.state.vars.prescribedTrades:
|
||||
|
||||
Reference in New Issue
Block a user