From f3e72290518676207e1f1de31068c8b8f7637479 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Fri, 15 Mar 2024 13:31:28 +0100 Subject: [PATCH] hard stop / soft stop for cutoff (#177) martingale base --- v2realbot/backtesting/backtester.py | 4 +- .../js/tables/archivetable/functions.js | 6 +++ v2realbot/strategy/StrategyClassicSL.py | 48 ++++++++++++++----- v2realbot/strategy/base.py | 7 +-- v2realbot/strategyblocks/newtrade/signals.py | 4 +- v2realbot/strategyblocks/newtrade/sizing.py | 8 +++- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/v2realbot/backtesting/backtester.py b/v2realbot/backtesting/backtester.py index e05b901..47e20ec 100644 --- a/v2realbot/backtesting/backtester.py +++ b/v2realbot/backtesting/backtester.py @@ -524,7 +524,7 @@ class Backtester: if actual_minus_reserved <= 0: cena = price if price else self.get_last_price(time, self.symbol) if (self.cash - reserved_price - float(int(size)*float(cena))) < 0: - printanyway("not enough cash for shorting. cash",self.cash,"reserved",reserved,"available",self.cash-reserved,"needed",float(int(size)*float(cena))) + printanyway("ERROR: not enough cash for shorting. cash",self.cash,"reserved",reserved,"available",self.cash-reserved,"needed",float(int(size)*float(cena))) return -1 #check for available cash @@ -550,7 +550,7 @@ class Backtester: if actual_plus_reserved_qty >= 0: cena = price if price else self.get_last_price(time, self.symbol) if (self.cash - reserved_price - float(int(size)*float(cena))) < 0: - printanyway("not enough cash to buy long. cash",self.cash,"reserved_qty",reserved_qty,"reserved_price",reserved_price, "available",self.cash-reserved_price,"needed",float(int(size)*float(cena))) + printanyway("ERROR: not enough cash to buy long. cash",self.cash,"reserved_qty",reserved_qty,"reserved_price",reserved_price, "available",self.cash-reserved_price,"needed",float(int(size)*float(cena))) return -1 id = str(uuid4()) diff --git a/v2realbot/static/js/tables/archivetable/functions.js b/v2realbot/static/js/tables/archivetable/functions.js index e08cb44..91ce3fb 100644 --- a/v2realbot/static/js/tables/archivetable/functions.js +++ b/v2realbot/static/js/tables/archivetable/functions.js @@ -495,6 +495,12 @@ function refresh_logfile() { readOnly: true }); }); + // Focus at the end of the file: + const model = editorLog.getModel(); + const lastLineNumber = model.getLineCount(); + const lastLineColumn = model.getLineMaxColumn(lastLineNumber); + editorLog.setPosition({ lineNumber: lastLineNumber, column: lastLineColumn }); + editorLog.revealPosition({ lineNumber: lastLineNumber, column: lastLineColumn }); }, error: function(xhr, status, error) { var err = eval("(" + xhr.responseText + ")"); diff --git a/v2realbot/strategy/StrategyClassicSL.py b/v2realbot/strategy/StrategyClassicSL.py index 23803a1..70b584f 100644 --- a/v2realbot/strategy/StrategyClassicSL.py +++ b/v2realbot/strategy/StrategyClassicSL.py @@ -35,40 +35,62 @@ 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) + #load typ direktivy hard/soft cutoff + hard_cutoff = safe_get(self.state.vars, "hard_cutoff", False) rel_profit = round(float(np.sum(self.state.rel_profit_cum)),5) if max_sum_profit_to_quit_rel is not None: 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=} relprofits:{str(self.state.rel_profit_cum)}") + msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}" + printanyway(msg) + self.state.ilog(e=msg) self.state.vars.pending = "max_sum_profit_to_quit_rel" if self.mode not in [Mode.BT, Mode.PREP]: - 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.signal_stop = True + send_to_telegram(msg) + if hard_cutoff: + self.hard_stop = True + else: + self.soft_stop = True return True if max_sum_loss_to_quit_rel is not None: 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=} relprofits:{str(self.state.rel_profit_cum)}") + msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}" + printanyway(msg) + self.state.ilog(e=msg) self.state.vars.pending = "max_sum_loss_to_quit_rel" if self.mode not in [Mode.BT, Mode.PREP]: - 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.signal_stop = True + send_to_telegram(msg) + if hard_cutoff: + self.hard_stop = True + else: + self.soft_stop = True 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=} relprofits:{str(self.state.rel_profit_cum)}") + msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}" + printanyway(msg) + self.state.ilog(e=msg) self.state.vars.pending = "max_sum_profit_to_quit" if self.mode not in [Mode.BT, Mode.PREP]: - 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.signal_stop = True + send_to_telegram(msg) + if hard_cutoff: + self.hard_stop = True + else: + self.soft_stop = True 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=} relprofits:{str(self.state.rel_profit_cum)}") + msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}" + printanyway(msg) + self.state.ilog(e=msg) self.state.vars.pending = "max_sum_loss_to_quit" if self.mode not in [Mode.BT, Mode.PREP]: - 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.signal_stop = True + send_to_telegram(msg) + if hard_cutoff: + self.hard_stop = True + else: + self.soft_stop = True return True return False @@ -414,7 +436,7 @@ class StrategyClassicSL(Strategy): populate_all_indicators(item, self.state) #pro přípravu dat next nevoláme - if self.mode == Mode.PREP: + if self.mode == Mode.PREP or self.soft_stop: return else: self.next(item, self.state) diff --git a/v2realbot/strategy/base.py b/v2realbot/strategy/base.py index 4fc861d..6461c78 100644 --- a/v2realbot/strategy/base.py +++ b/v2realbot/strategy/base.py @@ -80,7 +80,8 @@ class Strategy: self.pe = pe self.se = se #signal stop - internal - self.signal_stop = False + self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie + self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu #prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions #zatim jen jedna q1 @@ -433,7 +434,7 @@ class Strategy: #printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize()) except queue.Empty: #check internal signals - for profit/loss optim etc - valid for runner - if self.signal_stop: + if self.hard_stop: print(current_thread().name, "Stopping signal - internal") break @@ -454,7 +455,7 @@ class Strategy: if item == "last" or self.se.is_set(): print(current_thread().name, "stopping") break - elif self.signal_stop: + elif self.hard_stop: print(current_thread().name, "Stopping signal - internal") break elif self.pe.is_set(): diff --git a/v2realbot/strategyblocks/newtrade/signals.py b/v2realbot/strategyblocks/newtrade/signals.py index 71f7320..3ca9d74 100644 --- a/v2realbot/strategyblocks/newtrade/signals.py +++ b/v2realbot/strategyblocks/newtrade/signals.py @@ -78,7 +78,7 @@ def execute_signal_generator(state, data, name): last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY), status=TradeStatus.READY, generated_by=name, - size=multiplier*state.vars.chunk, + size=int(multiplier*state.vars.chunk), size_multiplier = multiplier, direction=TradeDirection.LONG, entry_price=None, @@ -90,7 +90,7 @@ def execute_signal_generator(state, data, name): last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY), status=TradeStatus.READY, generated_by=name, - size=multiplier*state.vars.chunk, + size=int(multiplier*state.vars.chunk), size_multiplier = multiplier, direction=TradeDirection.SHORT, entry_price=None, diff --git a/v2realbot/strategyblocks/newtrade/sizing.py b/v2realbot/strategyblocks/newtrade/sizing.py index 0d42851..2d81962 100644 --- a/v2realbot/strategyblocks/newtrade/sizing.py +++ b/v2realbot/strategyblocks/newtrade/sizing.py @@ -151,13 +151,17 @@ def get_multiplier(state: StrategyState, data, signaloptions: dict, direction: T #pocet ztrátových obchodů v řadě mi udává multiplikátor (0 - 1, 1 ztráta 2x, 3 v řadě - 4x atp.) if martingale_enabled: + + #martingale base - základ umocňování - klasicky 2 + base = float(utls.safe_get(options, "martingale_base", 2)) + #pocet aktuálních konsekutivních ztrát cont_loss_series_cnt = state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] if cont_loss_series_cnt == 0: multiplier = 1 else: - multiplier = 2 ** cont_loss_series_cnt + multiplier = base ** cont_loss_series_cnt state.ilog(lvl=1,e=f"SIZER - MARTINGALE {multiplier}", options=options, time=state.time, cont_loss_series_cnt=cont_loss_series_cnt) - + if (martingale_enabled is False and multiplier > 1) or multiplier <= 0: state.ilog(lvl=1,e=f"SIZER - Mame nekde problem MULTIPLIER mimo RANGE ERROR {multiplier}", options=options, time=state.time) multiplier = 1