pridan zaklad k custom indikatorum

This commit is contained in:
David Brazda
2023-09-10 21:20:22 +02:00
parent e08ae0f537
commit f568f62687
10 changed files with 621 additions and 67 deletions

23
testy/evalscope.py Normal file
View 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
View 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

View File

@ -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.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.utils.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory from v2realbot.common.model import SLHistory
from datetime import datetime from datetime import datetime, timedelta
from v2realbot.config import KW from v2realbot.config import KW
from uuid import uuid4 from uuid import uuid4
import random
import json import json
#from icecream import install, ic #from icecream import install, ic
#from rich import print #from rich import print
@ -250,9 +251,174 @@ def next(data, state: StrategyState):
populate_dynamic_ema_indicator(name = name) populate_dynamic_ema_indicator(name = name)
elif type == "NATR": elif type == "NATR":
populate_dynamic_natr_indicator(name = name) populate_dynamic_natr_indicator(name = name)
elif type == "custom":
populate_dynamic_custom_indicator(name = name)
else: else:
return 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 #EMA INDICATOR
# type = EMA, source = [close, vwap, hlcc4], length = [14], on_confirmed_only = [true, false] # type = EMA, source = [close, vwap, hlcc4], length = [14], on_confirmed_only = [true, false]
def populate_dynamic_ema_indicator(name): 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): if on_confirmed_only is False or (on_confirmed_only is True and data['confirmed']==1):
try: try:
#slow_slope = 99
slope_lookback = safe_get(options, 'slope_lookback', 100) 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) minimum_slope = safe_get(options, 'minimum_slope', 25)
maximum_slope = safe_get(options, "maximum_slope",0.9) maximum_slope = safe_get(options, "maximum_slope",0.9)
lookback_offset = safe_get(options, 'lookback_offset', 25)
#lookback has to be even #jako levy body pouzivame lookback_priceline INDIKATOR vzdaleny slope_lookback barů
if lookback_offset % 2 != 0: if lookback_priceline is not None:
lookback_offset += 1 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: else:
#kdyz neni dostatek hodnot, pouzivame jako levy bod open hodnotu close[0] #NEMAME LOOKBACK PRICLINE - pouzivame stary způsob výpočtu, toto pozdeji decomissionovat
#lookbackprice = state.bars.vwap[0] #lookback has to be even
if lookback_offset % 2 != 0:
#dalsi vyarianta-- lookback je pole z toho všeho co mame lookback_offset += 1
#lookbackprice = Average(state.bars.vwap)
#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ů #levy bod bude vzdy vzdaleny o slope_lookback
# a z ní uděláme průměr #ten bude prumerem hodnot lookback_offset a to tak ze polovina offsetu z kazde strany
cnt = len(state.bars.close) array_od = slope_lookback + int(lookback_offset/2)
if cnt>5: array_do = slope_lookback - int(lookback_offset/2)
sliced_to = int(cnt/5) lookbackprice_array = state.bars.vwap[-array_od:-array_do]
lookbackprice= Average(state.bars.vwap[:sliced_to])
lookbacktime = state.bars.time[int(sliced_to/2)] #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: else:
lookbackprice = Average(state.bars.vwap) #kdyz neni dostatek hodnot, pouzivame jako levy bod open hodnotu close[0]
lookbacktime = state.bars.time[0] #lookbackprice = state.bars.vwap[0]
state.ilog(e=f"IND {name} slope - not enough data bereme left bod open", slope_lookback=slope_lookback, lookbackprice=lookbackprice) #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 #výpočet úhlu - a jeho normalizace
slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100 slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100
@ -577,7 +755,9 @@ def next(data, state: StrategyState):
state.indicators[name+"MA"][-1]=slopeMA state.indicators[name+"MA"][-1]=slopeMA
last_slopesMA = state.indicators[name+"MA"][-10:] 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 #dale pracujeme s timto MAckovanym slope
#slope = slopeMA #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) 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): def exit_conditions_met(direction: TradeDirection):
if direction == TradeDirection.LONG: if direction == TradeDirection.LONG:
@ -945,8 +1209,9 @@ def next(data, state: StrategyState):
insert_SL_history() 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) 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): def close_position(direction: TradeDirection, reason: str, reverse: bool = False):
state.ilog(e=f"CLOSING TRADE {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade) 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: if direction == TradeDirection.SHORT:
res = state.buy(size=abs(int(state.positions))) res = state.buy(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0: if isinstance(res, int) and res < 0:
@ -959,12 +1224,14 @@ def next(data, state: StrategyState):
else: else:
raise Exception(f"unknow TradeDirection in close_position") raise Exception(f"unknow TradeDirection in close_position")
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu #pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
insert_SL_history() insert_SL_history()
state.vars.pending = state.vars.activeTrade.id state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None 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(): def eval_close_position():
curr_price = float(data['close']) curr_price = float(data['close'])
@ -987,12 +1254,24 @@ def next(data, state: StrategyState):
#SL - execution #SL - execution
if curr_price > state.vars.activeTrade.stoploss_value: 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 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 #CLOSING BASED ON EXIT CONDITIONS
if exit_conditions_met(TradeDirection.SHORT): 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 return
#PROFIT #PROFIT
@ -1011,11 +1290,24 @@ def next(data, state: StrategyState):
#SL - execution #SL - execution
if curr_price < state.vars.activeTrade.stoploss_value: if curr_price < state.vars.activeTrade.stoploss_value:
close_position(direction=TradeDirection.LONG, reason="SL REACHED") directive_name = 'reverse_for_SL_exit_long'
return 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): 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 return
#PROFIT #PROFIT
@ -1059,7 +1351,11 @@ def next(data, state: StrategyState):
if state.vars.activeTrade: if state.vars.activeTrade:
if state.vars.activeTrade.direction == TradeDirection.LONG: if state.vars.activeTrade.direction == TradeDirection.LONG:
state.ilog(e="odesilame LONG ORDER", trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial))) 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: if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation LONG {res}") raise Exception(f"error in required operation LONG {res}")
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars #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 state.vars.pending = state.vars.activeTrade.id
elif state.vars.activeTrade.direction == TradeDirection.SHORT: elif state.vars.activeTrade.direction == TradeDirection.SHORT:
state.ilog(e="odesilame SHORT ORDER",trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial))) 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: if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation SHORT {res}") raise Exception(f"error in required operation SHORT {res}")
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars #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: if common_go_preconditions_check(signalname=name, options=options) is False:
return 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 #pokud je plugin True, spusti se kod
if plugin: if signal_plugin is not None and signal_plugin_run_once_at_index==data["index"]:
execute_signal_generator_plugin(name) try:
custom_function = eval(signal_plugin)
custom_function()
except NameError:
state.ilog(e="Custom plugin {signal_plugin} not found")
else: else:
short_enabled = safe_get(options, "short_enabled",safe_get(state.vars, "short_enabled",True)) 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)) 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.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.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.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_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") # 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"] section = state.vars.exit["conditions"]
for smer in TradeDirection: 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.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 #init klice v extData pro ulozeni historie SL
state.extData["sl_history"] = [] state.extData["sl_history"] = []
@ -1450,6 +1760,8 @@ def init(state: StrategyState):
state.vars.activeTrade = None #pending/Trade state.vars.activeTrade = None #pending/Trade
#obsahuje pripravene Trady ve frontě #obsahuje pripravene Trady ve frontě
state.vars.prescribedTrades = [] 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 #TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
#pripadne udelat refresh kazdych x-iterací #pripadne udelat refresh kazdych x-iterací

View File

@ -23,6 +23,7 @@ class Trade(BaseModel):
generated_by: Optional[str] = None generated_by: Optional[str] = None
direction: TradeDirection direction: TradeDirection
entry_price: Optional[float] = None entry_price: Optional[float] = None
size: Optional[int] = None
# stoploss_type: TradeStoplossType # stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None stoploss_value: Optional[float] = None
profit: Optional[float] = 0 profit: Optional[float] = 0

View File

@ -102,4 +102,5 @@ class KW:
dont_go: str = "dont_go" dont_go: str = "dont_go"
go: str = "go" go: str = "go"
activate: str = "activate" activate: str = "activate"
exit: str = "exit" exit: str = "exit"
reverse: str = "reverse"

View File

@ -573,7 +573,7 @@
<input type="text" id="itemName"><br><br> <input type="text" id="itemName"><br><br>
<label for="jsonTextarea">JSON Data:</label><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="saveButton">Save</button>
<button type="button" id="addButton">Add</button> <button type="button" id="addButton">Add</button>

View File

@ -38,6 +38,7 @@ $(document).ready(function () {
rows = archiveRecords.rows('.selected').data(); rows = archiveRecords.rows('.selected').data();
var record1 = new Object() var record1 = new Object()
//console.log(JSON.stringify(rows)) //console.log(JSON.stringify(rows))
record1 = JSON.parse(rows[0].strat_json) record1 = JSON.parse(rows[0].strat_json)
//record1.json = rows[0].json //record1.json = rows[0].json
//record1.id = rows[0].id; //record1.id = rows[0].id;
@ -48,6 +49,8 @@ $(document).ready(function () {
// record1.script = rows[0].script; // record1.script = rows[0].script;
// record1.open_rush = rows[0].open_rush; // record1.open_rush = rows[0].open_rush;
// record1.close_rush = rows[0].close_rush; // record1.close_rush = rows[0].close_rush;
//console.log(record1.stratvars_conf)
record1.stratvars_conf = TOML.parse(record1.stratvars_conf); record1.stratvars_conf = TOML.parse(record1.stratvars_conf);
record1.add_data_conf = TOML.parse(record1.add_data_conf); record1.add_data_conf = TOML.parse(record1.add_data_conf);
// record1.note = rows[0].note; // record1.note = rows[0].note;
@ -65,6 +68,7 @@ $(document).ready(function () {
// record2.script = rows[1].script; // record2.script = rows[1].script;
// record2.open_rush = rows[1].open_rush; // record2.open_rush = rows[1].open_rush;
// record2.close_rush = rows[1].close_rush; // record2.close_rush = rows[1].close_rush;
record2.stratvars_conf = TOML.parse(record2.stratvars_conf); record2.stratvars_conf = TOML.parse(record2.stratvars_conf);
record2.add_data_conf = TOML.parse(record2.add_data_conf); record2.add_data_conf = TOML.parse(record2.add_data_conf);
// record2.note = rows[1].note; // record2.note = rows[1].note;

View File

@ -356,6 +356,7 @@ pre {
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: 54px; margin-left: 54px;
width: 323px;
height: 30px; height: 30px;
margin-top: 8px; margin-top: 8px;
color: #2196F3; color: #2196F3;

View File

@ -4,6 +4,7 @@ from v2realbot.utils.tlog import tlog, tlog_exception
from v2realbot.enums.enums import Mode, Order, Account, RecordType from v2realbot.enums.enums import Mode, Order, Account, RecordType
#from alpaca.trading.models import TradeUpdate #from alpaca.trading.models import TradeUpdate
from v2realbot.common.model import TradeUpdate from v2realbot.common.model import TradeUpdate
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from alpaca.trading.enums import TradeEvent, OrderStatus from alpaca.trading.enums import TradeEvent, OrderStatus
from v2realbot.indicators.indicators import ema from v2realbot.indicators.indicators import ema
import json import json
@ -13,7 +14,7 @@ from random import randrange
from alpaca.common.exceptions import APIError from alpaca.common.exceptions import APIError
import copy import copy
from threading import Event from threading import Event
from uuid import UUID from uuid import UUID, uuid4
class StrategyClassicSL(Strategy): 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=}") send_to_telegram(f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}")
self.se.set() 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): async def orderUpdateBuy(self, data: TradeUpdate):
o: Order = data.order o: Order = data.order
signal_name = None signal_name = None
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se ##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))) 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: if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
#jde o uzavření short pozice - počítáme PROFIT #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))) #self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial)))
#test na maximalni profit/loss #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: else:
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano #zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
@ -109,6 +132,7 @@ class StrategyClassicSL(Strategy):
#davame pryc pending #davame pryc pending
self.state.vars.pending = None self.state.vars.pending = None
async def orderUpdateSell(self, data: TradeUpdate): 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))) 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() 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: else:
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano #zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
for trade in self.state.vars.prescribedTrades: for trade in self.state.vars.prescribedTrades: