exitadd direktiva + bugfixy na partialfill profit

This commit is contained in:
David Brazda
2023-09-13 09:56:51 +02:00
parent abf48e14df
commit e8f5805faf
8 changed files with 296 additions and 91 deletions

View File

@ -2,7 +2,7 @@ import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from v2realbot.strategy.base import StrategyState from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType, Followup
from v2realbot.indicators.indicators import ema, natr, roc from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.indicators.oscillators import rsi from v2realbot.indicators.oscillators import rsi
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
@ -1011,8 +1011,9 @@ 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)
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
def reverse_conditions_met(direction: TradeDirection): def keyword_conditions_met(direction: TradeDirection, keyword: KW):
action = str(keyword).upper()
if direction == TradeDirection.LONG: if direction == TradeDirection.LONG:
smer = "long" smer = "long"
else: else:
@ -1022,7 +1023,7 @@ def next(data, state: StrategyState):
exit_cond_only_on_confirmed = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False)) 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: if exit_cond_only_on_confirmed and data['confirmed'] == 0:
state.ilog(lvl=0,e="REVERSAL CHECK COND ONLY ON CONFIRMED BAR") state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
return False return False
#TOTO zatim u REVERSU neresime #TOTO zatim u REVERSU neresime
@ -1066,37 +1067,123 @@ def next(data, state: StrategyState):
# return False # return False
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general #bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
state.ilog(lvl=0,e=f"REVERSE CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse]) state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
mother_signal = state.vars.activeTrade.generated_by mother_signal = state.vars.activeTrade.generated_by
if mother_signal is not None: if mother_signal is not None:
cond_dict = state.vars.conditions[KW.reverse][state.vars.activeTrade.generated_by][smer] cond_dict = state.vars.conditions[keyword][state.vars.activeTrade.generated_by][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR") result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=1,e=f"{action} CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
#OR neprosly testujeme AND #OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND") result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=1,e=f"{action} CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
#pokud nemame mother signal nebo exit nevratil nic, fallback na common #pokud nemame mother signal nebo exit nevratil nic, fallback na common
cond_dict = state.vars.conditions[KW.reverse]["common"][smer] cond_dict = state.vars.conditions[keyword]["common"][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR") result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=1,e=f"{action} CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
#OR neprosly testujeme AND #OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND") result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=0,e=f"REVERSE CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=0,e=f"{action} CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
#DECOMISSIONOVANI - nahrazeno obacnou keyword_conditions_met
# 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(lvl=0,e="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(lvl=0,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(lvl=0,e=f"EXIT COND min profit PASS - POKRACUJEME")
# # else:
# # state.ilog(lvl=0,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(lvl=0,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(lvl=0,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(lvl=0,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(lvl=1,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(lvl=1,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(lvl=1,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(lvl=0,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:
smer = "long" smer = "long"
@ -1276,9 +1363,9 @@ def next(data, state: StrategyState):
insert_SL_history() insert_SL_history()
state.ilog(lvl=1,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(lvl=1,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, reverse: bool = False): def close_position(direction: TradeDirection, reason: str, followup: Followup = None):
reversal_text = "REVERSAL" if reverse else "" followup_text = str(followup) if followup is not None else ""
state.ilog(lvl=1,e=f"CLOSING TRADE {reversal_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade) state.ilog(lvl=1,e=f"CLOSING TRADE {followup_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:
@ -1297,8 +1384,8 @@ def next(data, state: StrategyState):
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: if followup is not None:
state.vars.reverse_requested = True state.vars.requested_followup = followup
def eval_close_position(): def eval_close_position():
curr_price = float(data['close']) curr_price = float(data['close'])
@ -1324,21 +1411,33 @@ def next(data, state: StrategyState):
directive_name = 'reverse_for_SL_exit_short' 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)) reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
followup_action = Followup.REVERSE if reverse_for_SL_exit else None
close_position(direction=TradeDirection.SHORT, reason="SL REACHED", reverse=reverse_for_SL_exit) close_position(direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
return return
#REVERSE BASED ON REVERSE CONDITIONS #REVERSE BASED ON REVERSE CONDITIONS
if reverse_conditions_met(TradeDirection.SHORT): if keyword_conditions_met(direction=TradeDirection.SHORT, keyword=KW.reverse):
close_position(direction=TradeDirection.SHORT, reason="REVERSE COND MET", reverse=True) close_position(direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(direction=TradeDirection.SHORT, keyword=KW.exitadd):
close_position(direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
return return
#CLOSING BASED ON EXIT CONDITIONS #CLOSING BASED ON EXIT CONDITIONS
if exit_conditions_met(TradeDirection.SHORT): if exit_conditions_met(TradeDirection.SHORT):
directive_name = 'reverse_for_cond_exit_short' 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)) reverse_for_cond_exit_short = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
directive_name = 'add_for_cond_exit_short'
close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET", reverse=reverse_for_cond_exit) add_for_cond_exit_short = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
if reverse_for_cond_exit_short:
followup_action = Followup.REVERSE
elif add_for_cond_exit_short:
followup_action = Followup.ADD
else:
followup_action = None
close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
return return
#PROFIT #PROFIT
@ -1359,22 +1458,34 @@ def next(data, state: StrategyState):
if curr_price < state.vars.activeTrade.stoploss_value: if curr_price < state.vars.activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_long' 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)) reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
followup_action = Followup.REVERSE if reverse_for_SL_exit else None
close_position(direction=TradeDirection.LONG, reason="SL REACHED", reverse=reverse_for_SL_exit) close_position(direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
return return
#REVERSE BASED ON REVERSE CONDITIONS #REVERSE BASED ON REVERSE CONDITIONS
if reverse_conditions_met(TradeDirection.LONG): if keyword_conditions_met(TradeDirection.LONG, KW.reverse):
close_position(direction=TradeDirection.LONG, reason="REVERSE COND MET", reverse=True) close_position(direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(TradeDirection.LONG, KW.exitadd):
close_position(direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
return return
#EXIT CONDITIONS #EXIT CONDITIONS
if exit_conditions_met(TradeDirection.LONG): if exit_conditions_met(TradeDirection.LONG):
directive_name = 'reverse_for_cond_exit_long' 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)) reverse_for_cond_exit_long = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
directive_name = 'add_for_cond_exit_long'
close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET", reverse=reverse_for_cond_exit) add_for_cond_exit_long = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
if reverse_for_cond_exit_long:
followup_action = Followup.REVERSE
elif add_for_cond_exit_long:
followup_action = Followup.ADD
else:
followup_action = None
close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
return return
#PROFIT #PROFIT
@ -1520,13 +1631,13 @@ def next(data, state: StrategyState):
#TESTUJEME GO SIGNAL #TESTUJEME GO SIGNAL
cond_dict = state.vars.conditions[KW.go][signalname][smer] cond_dict = state.vars.conditions[KW.go][signalname][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR") result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=0,e=f"EVAL GO SIGNAL {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=1,e=f"EVAL GO SIGNAL {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
#OR neprosly testujeme AND #OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND") result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=0,e=f"EVAL GO SIGNAL {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict) state.ilog(lvl=1,e=f"EVAL GO SIGNAL {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result: if result:
return True return True
@ -1598,11 +1709,11 @@ def next(data, state: StrategyState):
state.ilog(lvl=1,e=f"PRECOND GENERAL not met {cond_met}", message=cond_met, precond_check=precond_check) state.ilog(lvl=1,e=f"PRECOND GENERAL not met {cond_met}", message=cond_met, precond_check=precond_check)
return False return False
state.ilog(lvl=0,e=f"{signalname} ALL PRECOND MET") state.ilog(lvl=1,e=f"{signalname} ALL PRECOND MET")
return True return True
def execute_signal_generator(name): def execute_signal_generator(name):
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] ) state.ilog(lvl=0,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
options = safe_get(state.vars.signals, name, None) options = safe_get(state.vars.signals, name, None)
if options is None: if options is None:
@ -1814,7 +1925,7 @@ def init(state: StrategyState):
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.conditions.setdefault(KW.reverse,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.exitadd,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.exitadd+"_" + 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")
@ -1823,7 +1934,7 @@ def init(state: StrategyState):
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) state.vars.conditions.setdefault(KW.reverse,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.exitadd,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.exitadd+"_" + 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"] = []
@ -1835,7 +1946,7 @@ def init(state: StrategyState):
#obsahuje pripravene Trady ve frontě #obsahuje pripravene Trady ve frontě
state.vars.prescribedTrades = [] state.vars.prescribedTrades = []
#flag pro reversal #flag pro reversal
state.vars.reverse_requested = False state.vars.requested_followup = None
#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

@ -104,8 +104,13 @@ ACCOUNT2_PAPER_PAPER = True
ACCOUNT2_PAPER_FEED = DataFeed.IEX ACCOUNT2_PAPER_FEED = DataFeed.IEX
class KW: class KW:
activate: str = "activate"
dont_go: str = "dont_go" dont_go: str = "dont_go"
go: str = "go" go: str = "go"
activate: str = "activate" # wip addsize: str = "addsize"
exit: str = "exit" exit: str = "exit"
#wip exitsize: str = "exitsize"
exitadd: str = "exitadd"
reverse: str = "reverse" reverse: str = "reverse"
#exitaddsize: str = "exitaddsize"

View File

@ -12,6 +12,10 @@ class Order:
self.filled_time = filled_time self.filled_time = filled_time
self.limit_price = limit_price self.limit_price = limit_price
class Followup(str, Enum):
REVERSE = "reverse"
ADD = "add"
class FillCondition(str, Enum): class FillCondition(str, Enum):
""" """
Execution settings: Execution settings:

View File

@ -27,8 +27,10 @@ from queue import Queue, Empty
from threading import Thread from threading import Thread
import asyncio import asyncio
from v2realbot.common.db import insert_queue, insert_conn, pool from v2realbot.common.db import insert_queue, insert_conn, pool
from v2realbot.utils.utils import json_serial from v2realbot.utils.utils import json_serial, send_to_telegram
from uuid import uuid4 from uuid import uuid4
from sqlite3 import OperationalError
from time import sleep
#from async io import Queue, QueueEmpty #from async io import Queue, QueueEmpty
# install() # install()
@ -519,9 +521,9 @@ def insert_queue2db():
# Retrieve data from the queue # Retrieve data from the queue
data = insert_queue.get() data = insert_queue.get()
try:
# Unpack the data # Unpack the data
runner_id, loglist = data runner_id, loglist = data
c = insert_conn.cursor() c = insert_conn.cursor()
insert_data = [] insert_data = []
for i in loglist: for i in loglist:
@ -530,6 +532,14 @@ def insert_queue2db():
c.executemany("INSERT INTO runner_logs VALUES (?,?,?)", insert_data) c.executemany("INSERT INTO runner_logs VALUES (?,?,?)", insert_data)
insert_conn.commit() insert_conn.commit()
# Mark the task as done in the queue # Mark the task as done in the queue
except OperationalError as e:
send_to_telegram("insert logs daemon returned" + str(e) + "RETRYING")
if "database is locked" in str(e):
# Database is locked, wait for a while and retry
insert_queue.put(data) # Put the data back into the queue for retry
sleep(1) # You can adjust the sleep duration
else:
raise # If it's another error, raise it
#join cekej na dokonceni vsech #join cekej na dokonceni vsech
for i in cs.db.runners: for i in cs.db.runners:

View File

@ -8,6 +8,7 @@ console.log("CHART_SHOW_TEXT archchart", CHART_SHOW_TEXT)
// var volumeSeries = null // var volumeSeries = null
var markersLine = null var markersLine = null
var avgBuyLine = null var avgBuyLine = null
var profitLine = null
var slLine = [] var slLine = []
//TRANSFORM object returned from REST API get_arch_run_detail //TRANSFORM object returned from REST API get_arch_run_detail
//to series and markers required by lightweigth chart //to series and markers required by lightweigth chart
@ -111,6 +112,7 @@ function transform_data(data) {
//get markers - avgp line for all buys //get markers - avgp line for all buys
var avgp_buy_line = [] var avgp_buy_line = []
var avgp_markers = [] var avgp_markers = []
var sum_profit_line = []
var markers = [] var markers = []
var markers_line = [] var markers_line = []
var last_timestamp = 0.1 var last_timestamp = 0.1
@ -139,9 +141,9 @@ function transform_data(data) {
last_timestamp = timestamp last_timestamp = timestamp
iterator = 0.002 iterator = 0.002
} }
//puvodne bylo pro buy
//pro sell muzeme teoreticky taky mit buyline (pri shortu) //AVG BUY LINE - zatim docasne vypiname
if ((trade.order.side == "buy") || (trade.order.side == "sell")) { if (((trade.order.side == "buy") || (trade.order.side == "sell")) && 1==2) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena //avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.pos_avg_price !== null) && (trade.pos_avg_price !== 0)) { if ((trade.pos_avg_price !== null) && (trade.pos_avg_price !== 0)) {
//line pro avgp markers //line pro avgp markers
@ -161,6 +163,26 @@ function transform_data(data) {
} }
} }
if ((trade.order.side == "buy") || (trade.order.side == "sell")) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.profit_sum !== null)) {
//line pro avgp markers
obj["time"] = timestamp;
obj["value"] = trade.profit_sum.toFixed(1);
sum_profit_line.push(obj)
//avgp markers pro prumernou cenu aktualnich pozic
// a_markers["time"] = timestamp
// a_markers["position"] = "aboveBar"
// a_markers["color"] = "#e8c76d"
// a_markers["shape"] = "arrowDown"
// a_markers["text"] = trade.profit_sum.toFixed(1);
// //if (CHART_SHOW_TEXT)
// //a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
// avgp_markers.push(a_markers)
}
}
//buy sell markery //buy sell markery
@ -172,7 +194,7 @@ function transform_data(data) {
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown" //marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown" marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
//marker["text"] = trade.qty + "/" + trade.price //marker["text"] = trade.qty + "/" + trade.price
qt_optimized = (trade.qty % 1000 === 0) ? (trade.qty / 1000).toFixed(1) + 'K' : trade.qty qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
if (CHART_SHOW_TEXT) { if (CHART_SHOW_TEXT) {
//včetně qty //včetně qty
@ -215,8 +237,10 @@ function transform_data(data) {
markers_line.sort(sorter) markers_line.sort(sorter)
avgp_buy_line.sort(sorter) avgp_buy_line.sort(sorter)
avgp_markers.sort(sorter) avgp_markers.sort(sorter)
sum_profit_line.sort(sorter)
transformed["avgp_buy_line"] = avgp_buy_line transformed["avgp_buy_line"] = avgp_buy_line
transformed["sum_profit_line"] = sum_profit_line
transformed["avgp_markers"] = avgp_markers transformed["avgp_markers"] = avgp_markers
transformed["markers"] = markers transformed["markers"] = markers
transformed["markers_line"] = markers_line transformed["markers_line"] = markers_line
@ -366,7 +390,15 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
} }
container1.append(indbuttonElement); container1.append(indbuttonElement);
display_buy_markers(); display_buy_markers();
//TADY JSEM SKONCIL - toto nize predelat na hide button pro display bar markers
// btnElement = document.getElementById("pricelineButtons")
// var indbuttonElement = populate_indicator_buttons(false);
// if (btnElement) {
// container1.removeChild(btnElement);
// }
//container1.append(indbuttonElement);
if (last_range) { if (last_range) {
chart.timeScale().setVisibleRange(last_range); chart.timeScale().setVisibleRange(last_range);
@ -604,6 +636,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
//displays (redraws) buy markers //displays (redraws) buy markers
function display_buy_markers() { function display_buy_markers() {
if (profitLine) {
chart.removeSeries(profitLine)
}
if (avgBuyLine) { if (avgBuyLine) {
chart.removeSeries(avgBuyLine) chart.removeSeries(avgBuyLine)
} }
@ -654,6 +691,27 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
//} //}
if (transformed_data["sum_profit_line"].length > 0) {
profitLine = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e8c76d',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
profitLine.applyOptions({
lastValueVisible: false,
priceLineVisible: false,
priceScaleId: "own"
});
profitLine.setData(transformed_data["sum_profit_line"]);
//profitLine.setMarkers(transformed_data["sum_profit_line_markers"]);
}
if (transformed_data["avgp_buy_line"].length > 0) { if (transformed_data["avgp_buy_line"].length > 0) {
avgBuyLine = chart.addLineSeries({ avgBuyLine = chart.addLineSeries({
// title: "avgpbuyline", // title: "avgpbuyline",
@ -714,6 +772,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
toolTip.innerHTML = ""; toolTip.innerHTML = "";
var data = param.seriesData.get(markersLine); var data = param.seriesData.get(markersLine);
var data2 = param.seriesData.get(avgBuyLine); var data2 = param.seriesData.get(avgBuyLine);
var profitdata = param.seriesData.get(profitLine);
if ((data !== undefined) || (data2 !== undefined)) { if ((data !== undefined) || (data2 !== undefined)) {
//param.seriesData.forEach((value, key) => { //param.seriesData.forEach((value, key) => {
//console.log("key",key) //console.log("key",key)
@ -733,8 +792,10 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
buy_price = parseFloat(data2.value).toFixed(3) buy_price = parseFloat(data2.value).toFixed(3)
} }
toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).qty}/${data.value}</div>`; toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).order.qty}/${data.value}</div>`;
if (profitdata !== undefined) {
toolTip.innerHTML += `<div>P:${parseFloat(profitdata.value).toFixed(1)}</div>`
}
//inspirace //inspirace
// toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}"> // toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
// ${Math.round(100 * price) / 100} // ${Math.round(100 * price) / 100}

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import Strategy from v2realbot.strategy.base import Strategy
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print, json_serial, safe_get, get_tick, send_to_telegram from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print, json_serial, safe_get, get_tick, send_to_telegram
from v2realbot.utils.tlog import tlog, tlog_exception 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, Followup
#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 v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
@ -25,7 +25,7 @@ class StrategyClassicSL(Strategy):
super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se, runner_id, ilog_save) super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se, runner_id, ilog_save)
#zkontroluje zda aktualni profit/loss - nedosahnul limit a pokud ano tak vypne strategii #zkontroluje zda aktualni profit/loss - nedosahnul limit a pokud ano tak vypne strategii
async def check_max_profit_loss(self): async def stop_when_max_profit_loss(self):
self.state.ilog(e="CHECK MAX PROFIT") self.state.ilog(e="CHECK MAX PROFIT")
max_sum_profit_to_quit = safe_get(self.state.vars, "max_sum_profit_to_quit", None) max_sum_profit_to_quit = safe_get(self.state.vars, "max_sum_profit_to_quit", None)
max_sum_loss_to_quit = safe_get(self.state.vars, "max_sum_loss_to_quit", None) max_sum_loss_to_quit = safe_get(self.state.vars, "max_sum_loss_to_quit", None)
@ -36,15 +36,19 @@ class StrategyClassicSL(Strategy):
self.state.vars.pending = "max_sum_profit_to_quit" self.state.vars.pending = "max_sum_profit_to_quit"
send_to_telegram(f"QUITTING MAX SUM PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=}") send_to_telegram(f"QUITTING MAX SUM PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=}")
self.se.set() self.se.set()
return True
if max_sum_loss_to_quit is not None: if max_sum_loss_to_quit is not None:
if float(self.state.profit) < 0 and float(self.state.profit) <= float(max_sum_loss_to_quit): if float(self.state.profit) < 0 and float(self.state.profit) <= float(max_sum_loss_to_quit):
self.state.ilog(e=f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}") self.state.ilog(e=f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}")
self.state.vars.pending = "max_sum_loss_to_quit" self.state.vars.pending = "max_sum_loss_to_quit"
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()
return True
return False
async def add_reversal(self, direction: TradeDirection, size: int, signal_name: str): async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
trade_to_add = Trade( trade_to_add = Trade(
id=uuid4(), id=uuid4(),
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY), last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
@ -57,9 +61,9 @@ class StrategyClassicSL(Strategy):
self.state.vars.prescribedTrades.append(trade_to_add) self.state.vars.prescribedTrades.append(trade_to_add)
self.state.vars.reverse_requested = None self.state.vars.requested_followup = None
self.state.ilog(e=f"REVERZAL {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add) self.state.ilog(e=f"FOLLOWUP {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
@ -92,6 +96,7 @@ class StrategyClassicSL(Strategy):
trade.profit_sum = self.state.profit trade.profit_sum = self.state.profit
signal_name = trade.generated_by signal_name = trade.generated_by
if data.event == TradeEvent.FILL:
#zapsat update profitu do tradeList #zapsat update profitu do tradeList
for tradeData in self.state.tradeList: for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id: if tradeData.execution_id == data.execution_id:
@ -101,14 +106,17 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "signal_name", signal_name) setattr(tradeData, "signal_name", signal_name)
#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, pokud vypiname pak uz nedelame pripdany reverzal
await self.check_max_profit_loss() if await self.stop_when_max_profit_loss() is False:
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name #pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
#jen při celém FILLU #jen při celém FILLU
if data.event == TradeEvent.FILL and self.state.vars.reverse_requested: if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
await self.add_reversal(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name) if self.state.vars.requested_followup == Followup.REVERSE:
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name)
elif self.state.vars.requested_followup == Followup.ADD:
#zatim stejna SIZE
await self.add_followup(direction=TradeDirection.SHORT, size=o.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:
@ -161,6 +169,7 @@ class StrategyClassicSL(Strategy):
trade.profit_sum = self.state.profit trade.profit_sum = self.state.profit
signal_name = trade.generated_by signal_name = trade.generated_by
if data.event == TradeEvent.FILL:
#zapsat update profitu do tradeList #zapsat update profitu do tradeList
for tradeData in self.state.tradeList: for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id: if tradeData.execution_id == data.execution_id:
@ -170,11 +179,15 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "signal_name", signal_name) setattr(tradeData, "signal_name", signal_name)
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}") #self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
await self.check_max_profit_loss() if await self.stop_when_max_profit_loss() is False:
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name #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: if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
await self.add_reversal(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name) if self.state.vars.requested_followup == Followup.REVERSE:
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
elif self.state.vars.requested_followup == Followup.ADD:
#zatim stejna SIZE
await self.add_followup(direction=TradeDirection.LONG, size=data.order.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
@ -206,6 +219,7 @@ class StrategyClassicSL(Strategy):
#pri chybe api nechavame puvodni hodnoty #pri chybe api nechavame puvodni hodnoty
if a != -1: if a != -1:
self.state.avgp, self.state.positions = a,p self.state.avgp, self.state.positions = a,p
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
#ic(self.state.avgp, self.state.positions) #ic(self.state.avgp, self.state.positions)
#this parent method is called by strategy just once before waiting for first data #this parent method is called by strategy just once before waiting for first data