hard stop / soft stop for cutoff (#177) martingale base

This commit is contained in:
David Brazda
2024-03-15 13:31:28 +01:00
parent a6343abe88
commit f3e7229051
6 changed files with 55 additions and 22 deletions

View File

@ -524,7 +524,7 @@ class Backtester:
if actual_minus_reserved <= 0: if actual_minus_reserved <= 0:
cena = price if price else self.get_last_price(time, self.symbol) cena = price if price else self.get_last_price(time, self.symbol)
if (self.cash - reserved_price - float(int(size)*float(cena))) < 0: 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 return -1
#check for available cash #check for available cash
@ -550,7 +550,7 @@ class Backtester:
if actual_plus_reserved_qty >= 0: if actual_plus_reserved_qty >= 0:
cena = price if price else self.get_last_price(time, self.symbol) cena = price if price else self.get_last_price(time, self.symbol)
if (self.cash - reserved_price - float(int(size)*float(cena))) < 0: 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 return -1
id = str(uuid4()) id = str(uuid4())

View File

@ -495,6 +495,12 @@ function refresh_logfile() {
readOnly: true 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) { error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")"); var err = eval("(" + xhr.responseText + ")");

View File

@ -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_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) 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) rel_profit = round(float(np.sum(self.state.rel_profit_cum)),5)
if max_sum_profit_to_quit_rel is not None: if max_sum_profit_to_quit_rel is not None:
if rel_profit >= float(max_sum_profit_to_quit_rel): 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" self.state.vars.pending = "max_sum_profit_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]: 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)}") send_to_telegram(msg)
self.signal_stop = True if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True return True
if max_sum_loss_to_quit_rel is not None: if max_sum_loss_to_quit_rel is not None:
if rel_profit < 0 and rel_profit <= float(max_sum_loss_to_quit_rel): 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" self.state.vars.pending = "max_sum_loss_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]: 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)}") send_to_telegram(msg)
self.signal_stop = True if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True return True
if max_sum_profit_to_quit is not None: if max_sum_profit_to_quit is not None:
if float(self.state.profit) >= float(max_sum_profit_to_quit): 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" self.state.vars.pending = "max_sum_profit_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]: 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)}") send_to_telegram(msg)
self.signal_stop = True if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True 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 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" self.state.vars.pending = "max_sum_loss_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]: 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)}") send_to_telegram(msg)
self.signal_stop = True if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True return True
return False return False
@ -414,7 +436,7 @@ class StrategyClassicSL(Strategy):
populate_all_indicators(item, self.state) populate_all_indicators(item, self.state)
#pro přípravu dat next nevoláme #pro přípravu dat next nevoláme
if self.mode == Mode.PREP: if self.mode == Mode.PREP or self.soft_stop:
return return
else: else:
self.next(item, self.state) self.next(item, self.state)

View File

@ -80,7 +80,8 @@ class Strategy:
self.pe = pe self.pe = pe
self.se = se self.se = se
#signal stop - internal #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 #prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
#zatim jen jedna q1 #zatim jen jedna q1
@ -433,7 +434,7 @@ class Strategy:
#printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize()) #printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize())
except queue.Empty: except queue.Empty:
#check internal signals - for profit/loss optim etc - valid for runner #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") print(current_thread().name, "Stopping signal - internal")
break break
@ -454,7 +455,7 @@ class Strategy:
if item == "last" or self.se.is_set(): if item == "last" or self.se.is_set():
print(current_thread().name, "stopping") print(current_thread().name, "stopping")
break break
elif self.signal_stop: elif self.hard_stop:
print(current_thread().name, "Stopping signal - internal") print(current_thread().name, "Stopping signal - internal")
break break
elif self.pe.is_set(): elif self.pe.is_set():

View File

@ -78,7 +78,7 @@ def execute_signal_generator(state, data, name):
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY), last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY, status=TradeStatus.READY,
generated_by=name, generated_by=name,
size=multiplier*state.vars.chunk, size=int(multiplier*state.vars.chunk),
size_multiplier = multiplier, size_multiplier = multiplier,
direction=TradeDirection.LONG, direction=TradeDirection.LONG,
entry_price=None, entry_price=None,
@ -90,7 +90,7 @@ def execute_signal_generator(state, data, name):
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY), last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY, status=TradeStatus.READY,
generated_by=name, generated_by=name,
size=multiplier*state.vars.chunk, size=int(multiplier*state.vars.chunk),
size_multiplier = multiplier, size_multiplier = multiplier,
direction=TradeDirection.SHORT, direction=TradeDirection.SHORT,
entry_price=None, entry_price=None,

View File

@ -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.) #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: 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"] cont_loss_series_cnt = state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]
if cont_loss_series_cnt == 0: if cont_loss_series_cnt == 0:
multiplier = 1 multiplier = 1
else: 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) 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: 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) state.ilog(lvl=1,e=f"SIZER - Mame nekde problem MULTIPLIER mimo RANGE ERROR {multiplier}", options=options, time=state.time)
multiplier = 1 multiplier = 1