From cc64026ade5c9a265acc3a3ddf41afa2a80f3c45 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Thu, 16 Nov 2023 17:09:27 +0100 Subject: [PATCH] oprava rel profit + exit protection --- v2realbot/controller/services.py | 6 +- v2realbot/static/js/libs/prism/prism-log.js | 2 +- v2realbot/strategy/StrategyClassicSL.py | 23 ++++---- v2realbot/strategy/base.py | 1 + .../activetrade/close/close_position.py | 1 + .../activetrade/close/conditions.py | 59 ++++++++++++------- .../activetrade/close/evaluate_close.py | 16 ++++- .../indicators/indicators_hub.py | 2 +- 8 files changed, 72 insertions(+), 38 deletions(-) diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index ac8e605..ac966aa 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -669,7 +669,7 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param res["profit"]["batch_sum_rel_profit"] = inter_batch_params["batch_rel_profit"] #rel_profit zprumerovane - res["profit"]["daily_rel_profit_avg"] = float(np.mean(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0 + res["profit"]["daily_rel_profit_avg"] = float(np.sum(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0 #rel_profit rozepsane zisky res["profit"]["daily_rel_profit_list"] = strat.state.rel_profit_cum @@ -728,7 +728,7 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param mpt_string = "PT"+str(max_profit_time.hour)+":"+str(max_profit_time.minute) if max_profit_time is not None else "" mlt_string ="LT"+str(max_loss_time.hour)+":"+str(max_loss_time.minute) if max_loss_time is not None else "" - rp_string = "RP" + str(float(np.mean(strat.state.rel_profit_cum))) if len(strat.state.rel_profit_cum) >0 else "noRP" + rp_string = "RP" + str(float(np.sum(strat.state.rel_profit_cum))) if len(strat.state.rel_profit_cum) >0 else "noRP" ##summary pro rychle zobrazeni P333L-222 PT9:30 PL10:30 res["profit"]["sum"]="P"+str(int(max_profit))+"L"+str(int(max_loss))+" "+ mpt_string+" " + mlt_string + rp_string + " "+str(strat.state.rel_profit_cum) @@ -768,7 +768,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params: #add profit of this batch iteration to batch_sum_profit if inter_batch_params is not None: inter_batch_params["batch_profit"] += round(float(strat.state.profit),2) - inter_batch_params["batch_rel_profit"] += float(np.mean(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0 + inter_batch_params["batch_rel_profit"] += float(np.sum(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0 #WIP diff --git a/v2realbot/static/js/libs/prism/prism-log.js b/v2realbot/static/js/libs/prism/prism-log.js index 69133e8..4275d7f 100644 --- a/v2realbot/static/js/libs/prism/prism-log.js +++ b/v2realbot/static/js/libs/prism/prism-log.js @@ -36,7 +36,7 @@ Prism.languages.log = { alias: ['info', 'keyword'] }, { - pattern: /\b(?:DBG|DEBUG|FINE|REVERSE|ADD|EXITADD|SIGNAL|PRECOND)\b/, + pattern: /\b(?:DBG|DEBUG|FINE|REVERSE|ADD|EXITADD|DONT_EXIT|SIGNAL|PRECOND)\b/, alias: ['debug', 'keyword'] }, { diff --git a/v2realbot/strategy/StrategyClassicSL.py b/v2realbot/strategy/StrategyClassicSL.py index 854e5ed..04b0c3c 100644 --- a/v2realbot/strategy/StrategyClassicSL.py +++ b/v2realbot/strategy/StrategyClassicSL.py @@ -36,35 +36,34 @@ class StrategyClassicSL(Strategy): max_sum_profit_to_quit_rel = safe_get(self.state.vars, "max_sum_profit_to_quit_rel", None) max_sum_loss_to_quit_rel = safe_get(self.state.vars, "max_sum_loss_to_quit_rel", None) + rel_profit = round(float(np.sum(self.state.rel_profit_cum)),5) if max_sum_profit_to_quit_rel is not None: - rel_profit = round(float(np.mean(self.state.rel_profit_cum)),5) if rel_profit >= float(max_sum_profit_to_quit_rel): - self.state.ilog(e=f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=}") + self.state.ilog(e=f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.state.vars.pending = "max_sum_profit_to_quit_rel" - send_to_telegram(f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=}") + send_to_telegram(f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.se.set() return True if max_sum_loss_to_quit_rel is not None: - rel_profit = round(float(np.mean(self.state.rel_profit_cum)),5) if rel_profit < 0 and rel_profit <= float(max_sum_loss_to_quit_rel): - self.state.ilog(e=f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=}") + self.state.ilog(e=f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.state.vars.pending = "max_sum_loss_to_quit_rel" - send_to_telegram(f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=}") + send_to_telegram(f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.se.set() return True if max_sum_profit_to_quit is not None: if float(self.state.profit) >= float(max_sum_profit_to_quit): - self.state.ilog(e=f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=}") + self.state.ilog(e=f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.state.vars.pending = "max_sum_profit_to_quit" - send_to_telegram(f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=}") + send_to_telegram(f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.se.set() return True 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): - self.state.ilog(e=f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=}") + self.state.ilog(e=f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.state.vars.pending = "max_sum_loss_to_quit" - send_to_telegram(f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=}") + send_to_telegram(f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}") self.se.set() return True @@ -135,7 +134,7 @@ class StrategyClassicSL(Strategy): #TODO pokud mame partial exit, tak se spravne vypocita relativni profit, ale # je jen na mensi mnozszvi take z nej delat cum_calculate je blbost - OPRAVIT self.state.rel_profit_cum.append(rel_profit) - rel_profit_cum_calculated = round(np.mean(self.state.rel_profit_cum),5) + rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5) self.state.ilog(e=f"BUY notif - SHORT PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id)) @@ -264,7 +263,7 @@ class StrategyClassicSL(Strategy): #pokud jde o finalni FILL - pridame do pole relativnich profit (ze ktereho se pocita kumulativni relativni profit) if data.event == TradeEvent.FILL: self.state.rel_profit_cum.append(rel_profit) - rel_profit_cum_calculated = round(np.mean(self.state.rel_profit_cum),5) + rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5) self.state.ilog(e=f"SELL notif - PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum = str(self.state.rel_profit_cum), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id)) diff --git a/v2realbot/strategy/base.py b/v2realbot/strategy/base.py index ca9f317..c1b721e 100644 --- a/v2realbot/strategy/base.py +++ b/v2realbot/strategy/base.py @@ -706,6 +706,7 @@ class StrategyState: self.resolution = None self.runner_id = runner_id self.bt = bt + self.dont_exit_already_activated = False self.ilog_save = ilog_save self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT) self.sl_optimizer_long = optimsl.SLOptimizer(ptm.TradeDirection.LONG) diff --git a/v2realbot/strategyblocks/activetrade/close/close_position.py b/v2realbot/strategyblocks/activetrade/close/close_position.py index 3a89ade..c79445a 100644 --- a/v2realbot/strategyblocks/activetrade/close/close_position.py +++ b/v2realbot/strategyblocks/activetrade/close/close_position.py @@ -38,6 +38,7 @@ def close_position(state, data, direction: TradeDirection, reason: str, followup #pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu insert_SL_history(state) + state.dont_exit_already_activated = False state.vars.pending = state.vars.activeTrade.id state.vars.activeTrade = None state.vars.last_exit_index = data["index"] diff --git a/v2realbot/strategyblocks/activetrade/close/conditions.py b/v2realbot/strategyblocks/activetrade/close/conditions.py index 1240a85..6469294 100644 --- a/v2realbot/strategyblocks/activetrade/close/conditions.py +++ b/v2realbot/strategyblocks/activetrade/close/conditions.py @@ -21,34 +21,53 @@ def dontexit_protection_met(state, data, direction: TradeDirection): else: smer = "short" - mother_signal = state.vars.activeTrade.generated_by + #zapracovana optimalizace, kdy po aktivovanem DONTEXITU to opet klesne pod profit a neprodá se + #vyreseno pri kazde aktivaci se vyplni flag already_activated + #pri naslednem false podminky se v pripade, ze je aktivovany flag posle True - + #take se vyrusi v closu + def process_result(result): + if result: + state.dont_exit_already_activated = True + return True + else: + return False - if mother_signal is not None: - #TESTUJEME DONT_EXIT_ - cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer] + def evaluate_result(): + mother_signal = state.vars.activeTrade.generated_by + + if mother_signal is not None: + #TESTUJEME DONT_EXIT_ + cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer] + #OR + result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR") + state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated)) + if result: + return True + + #OR neprosly testujeme AND + result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND") + state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated)) + if result: + return True + + cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer] #OR result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR") - state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict) + state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated)) if result: return True #OR neprosly testujeme AND result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND") - state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict) - if result: - return True - - cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer] - #OR - result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR") - state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict) - if result: - return True - - #OR neprosly testujeme AND - result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND") - state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict) - return result + state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated)) + return result + + #nejprve evaluujeme vsechny podminky + result = evaluate_result() + + #pak evaluujeme vysledek a vracíme + return process_result(result) + def exit_conditions_met(state, data, direction: TradeDirection): if direction == TradeDirection.LONG: diff --git a/v2realbot/strategyblocks/activetrade/close/evaluate_close.py b/v2realbot/strategyblocks/activetrade/close/evaluate_close.py index 40a77db..b61fd6f 100644 --- a/v2realbot/strategyblocks/activetrade/close/evaluate_close.py +++ b/v2realbot/strategyblocks/activetrade/close/evaluate_close.py @@ -96,6 +96,13 @@ def eval_close_position(state: StrategyState, data): if max_price_signal or dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False: close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}") return + #pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také + elif state.dont_exit_already_activated == True: + #TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE + #if dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False: + close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}") + state.dont_exit_already_activated = False + return #FORCED EXIT PRI KONCI DNE if eod_exit_activated(state, data, TradeDirection.SHORT): @@ -177,7 +184,14 @@ def eval_close_position(state: StrategyState, data): if max_price_signal or dontexit_protection_met(state, data, direction=TradeDirection.LONG) is False: close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}") return - + #pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také + elif state.dont_exit_already_activated == True: + #TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE + # if dontexit_protection_met(state=state, data=data,direction=TradeDirection.LONG) is False: + close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}") + state.dont_exit_already_activated = False + return + #FORCED EXIT PRI KONCI DNE if eod_exit_activated(state, data, TradeDirection.LONG): close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED") diff --git a/v2realbot/strategyblocks/indicators/indicators_hub.py b/v2realbot/strategyblocks/indicators/indicators_hub.py index 6d45b59..223da13 100644 --- a/v2realbot/strategyblocks/indicators/indicators_hub.py +++ b/v2realbot/strategyblocks/indicators/indicators_hub.py @@ -55,7 +55,7 @@ def populate_all_indicators(data, state: StrategyState): #TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie #TODO na toto se podivam, nejak moc zajasonovani a zpatky - #PERF PROBLEM - state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} GP:{state.vars.activeTrade.goal_price if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.mean(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending)) + state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} GP:{state.vars.activeTrade.goal_price if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.sum(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending)) #kroky pro CONFIRMED BAR only if conf_bar == 1: