From 9bad9b850a0528acf6248d8e57d5818d0b79435d Mon Sep 17 00:00:00 2001 From: David Brazda Date: Wed, 30 Aug 2023 15:23:01 +0200 Subject: [PATCH] SL do grafu, pred zmenou signal podminek --- v2realbot/ENTRY_ClassicSL_v01.py | 211 ++++++++++-------- .../common/__pycache__/model.cpython-310.pyc | Bin 6971 -> 7217 bytes v2realbot/common/model.py | 7 + v2realbot/controller/services.py | 4 +- v2realbot/static/js/archivechart.js | 90 +++++++- v2realbot/static/js/livewebsocket.js | 19 ++ .../strategy/__pycache__/base.cpython-310.pyc | Bin 14405 -> 14554 bytes v2realbot/strategy/base.py | 11 +- .../utils/__pycache__/utils.cpython-310.pyc | Bin 12120 -> 12167 bytes v2realbot/utils/utils.py | 4 +- 10 files changed, 246 insertions(+), 100 deletions(-) diff --git a/v2realbot/ENTRY_ClassicSL_v01.py b/v2realbot/ENTRY_ClassicSL_v01.py index 21ca187..c9e2a9f 100644 --- a/v2realbot/ENTRY_ClassicSL_v01.py +++ b/v2realbot/ENTRY_ClassicSL_v01.py @@ -7,6 +7,7 @@ from v2realbot.indicators.indicators import ema from v2realbot.indicators.oscillators import rsi from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, round2five, is_open_rush, is_close_rush, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial +from v2realbot.common.model import SLHistory from datetime import datetime from uuid import uuid4 import json @@ -498,20 +499,25 @@ def next(data, state: StrategyState): lookbacktime = state.bars.time[-slope_lookback] else: #kdyz neni dostatek hodnot, pouzivame jako levy bod open hodnotu close[0] - lookbackprice = state.bars.vwap[0] + #lookbackprice = state.bars.vwap[0] - #pokud neni dostatek, bereme vzdy petinu ze stávajících barů - # cnt = len(state.bars.close) - # if cnt>5: - # sliced_to = int(cnt/5) - # lookbackprice= Average(state.bars.vwap[:sliced_to]) - - # else: - # lookbackprice = state.bars.vwap[0] - #update -- lookback je pole z toho co mame + #dalsi vyarianta-- lookback je pole z toho všeho co mame #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) + + + + #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 slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100 @@ -533,7 +539,7 @@ def next(data, state: StrategyState): state.indicators[name+"MA"][-1]=slopeMA last_slopesMA = state.indicators[name+"MA"][-10:] - state.ilog(e=f"{name=} {slope=} {slopeMA=}", msg=f"{lookbackprice=}", lookbackoffset=lookback_offset, minimum_slope=minimum_slope, last_slopes=state.indicators[name][-10:], last_slopesMA=last_slopesMA) + 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) #dale pracujeme s timto MAckovanym slope #slope = slopeMA @@ -752,7 +758,11 @@ def next(data, state: StrategyState): # return result - def trail_SL_if_required(direction: TradeDirection): + def insert_SL_history(): + #insert stoploss history as key sl_history into runner archive extended data + state.extData["sl_history"].append(SLHistory(id=state.vars.activeTrade.id, time=state.time, sl_val=state.vars.activeTrade.stoploss_value)) + + def trail_SL_management(): #pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset #+ varianta - skoncit breakeven @@ -768,43 +778,68 @@ def next(data, state: StrategyState): # #zda trailing zastavit na brakeeven # SL_trailing_stop_at_breakeven_short = true # SL_trailing_stop_at_breakeven_long = true - if direction == TradeDirection.LONG: - smer = "long" - else: - smer = "short" - - options = safe_get(state.vars, 'exit_conditions', None) - if options is None: - state.ilog(e="Trail SL. No options for exit conditions in stratvars.") - return - - if safe_get(options, 'SL_trailing_enabled_'+str(smer), False) is True: - stop_breakeven = safe_get(options, 'SL_trailing_stop_at_breakeven_'+str(smer), False) - def_SL = safe_get(options, 'SL_defval_'+str(smer), 0.01) - offset = safe_get(options, "SL_trailing_offset_"+str(smer), 0.01) + if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None: - #pokud je pozadovan trail jen do breakeven a uz prekroceno - if (direction == TradeDirection.LONG and stop_breakeven and state.vars.activeTrade.stoploss_value >= float(state.avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and state.vars.activeTrade.stoploss_value <= float(state.avgp)): - state.ilog(e=f"SL trail stop at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven) + if int(state.positions) < 0: + direction = TradeDirection.SHORT + smer = "short" + else: + direction = TradeDirection.LONG + smer = "long" + + options = safe_get(state.vars, 'exit_conditions', None) + if options is None: + state.ilog(e="Trail SL. No options for exit conditions in stratvars.") return - #IDEA: Nyni posouvame SL o offset, mozna ji posunout jen o direktivu step ? + if safe_get(options, 'SL_trailing_enabled_'+str(smer), False) is True: + stop_breakeven = safe_get(options, 'SL_trailing_stop_at_breakeven_'+str(smer), False) + def_SL = safe_get(options, 'SL_defval_'+str(smer), 0.01) + offset = safe_get(options, "SL_trailing_offset_"+str(smer), 0.01) - offset_normalized = normalize_tick(offset) #to ticks and from options - def_SL_normalized = normalize_tick(def_SL) - if direction == TradeDirection.LONG: - move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized - state.ilog(e=f"SL trailing EVAL {smer} SL:{state.vars.activeTrade.stoploss_value} MOVETHRESHOLD:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) - if (move_SL_threshold) < data['close']: - state.vars.activeTrade.stoploss_value += offset_normalized - state.ilog(e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) - elif direction == TradeDirection.SHORT: - move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized - state.ilog(e=f"SL trailing EVAL {smer} SL:{state.vars.activeTrade.stoploss_value} MOVETHRESHOLD:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) - if (move_SL_threshold) > data['close']: - state.vars.activeTrade.stoploss_value -= offset_normalized - state.ilog(e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) + #pokud je pozadovan trail jen do breakeven a uz prekroceno + if (direction == TradeDirection.LONG and stop_breakeven and state.vars.activeTrade.stoploss_value >= float(state.avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and state.vars.activeTrade.stoploss_value <= float(state.avgp)): + state.ilog(e=f"SL trail STOP at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven) + return + + #IDEA: Nyni posouvame SL o offset, mozna ji posunout jen o direktivu step ? + offset_normalized = normalize_tick(offset) #to ticks and from options + def_SL_normalized = normalize_tick(def_SL) + if direction == TradeDirection.LONG: + move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized + state.ilog(e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) + if (move_SL_threshold) < data['close']: + state.vars.activeTrade.stoploss_value += offset_normalized + insert_SL_history() + state.ilog(e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) + elif direction == TradeDirection.SHORT: + move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized + state.ilog(e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized) + if (move_SL_threshold) > data['close']: + state.vars.activeTrade.stoploss_value -= offset_normalized + 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) + + def close_position(direction: TradeDirection, reason: str): + state.ilog(e=f"CLOSING TRADE {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade) + if direction == TradeDirection.SHORT: + res = state.buy(size=abs(int(state.positions))) + if isinstance(res, int) and res < 0: + raise Exception(f"error in required operation {reason} {res}") + + elif direction == TradeDirection.LONG: + res = state.sell(size=state.positions) + if isinstance(res, int) and res < 0: + raise Exception(f"error in required operation STOPLOSS SELL {res}") + + else: + raise Exception(f"unknow TradeDirection in close_position") + + #pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu + insert_SL_history() + state.vars.pending = state.vars.activeTrade.id + state.vars.activeTrade = None def eval_close_position(): curr_price = float(data['close']) @@ -817,32 +852,21 @@ def next(data, state: StrategyState): state.ilog(e=f"Goal price {goal_price} max price {max_price}") #close position handling + #TBD pridat OPTIMALIZACI POZICE - EXIT 1/2 - #mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i long pozicemi) + #mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi) if int(state.positions) < 0: #EOD EXIT - TBD - - #SL TRAILING - trail_SL_if_required(direction=TradeDirection.SHORT) + #FORCED EXIT PRI KONCI DNE #SL - execution if curr_price > state.vars.activeTrade.stoploss_value: - state.ilog(e=f"STOPLOSS reached on SHORT", curr_price=curr_price, trade=state.vars.activeTrade) - res = state.buy(size=abs(int(state.positions))) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation STOPLOSS BUY {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None + close_position(direction=TradeDirection.SHORT, reason="SL REACHED") return #CLOSING BASED ON EXIT CONDITIONS if exit_conditions_met(TradeDirection.SHORT): - res = state.buy(size=abs(int(state.positions))) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation EXIT COND BUY {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None - state.ilog(e=f"EXIT COND MET. market BUY was sent {curr_price=}", positions=state.positions, avgp=state.avgp) + close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET") return #PROFIT @@ -853,37 +877,19 @@ def next(data, state: StrategyState): max_price_signal = curr_price<=max_price #OPTIMALIZACE pri stoupajícím angle if max_price_signal or sell_protection_enabled() is False: - res = state.buy(size=abs(int(state.positions))) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation PROFIT BUY {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None - state.ilog(e=f"PROFIT MET EXIT. market BUY was sent {curr_price=} {max_price_signal=}", positions=state.positions, avgp=state.avgp) + close_position(direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}") return #mame long elif int(state.positions) > 0: #EOD EXIT - TBD - #SL - trailing - trail_SL_if_required(direction=TradeDirection.LONG) - #SL - execution if curr_price < state.vars.activeTrade.stoploss_value: - state.ilog(e=f"STOPLOSS reached on LONG", curr_price=curr_price, trade=state.vars.activeTrade) - res = state.sell(size=state.positions) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation STOPLOSS SELL {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None + close_position(direction=TradeDirection.LONG, reason="SL REACHED") return if exit_conditions_met(TradeDirection.LONG): - res = state.sell(size=abs(int(state.positions))) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation EXIT COND SELL {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None - state.ilog(e=f"EXIT COND MET. market SELL was sent {curr_price=}", positions=state.positions, avgp=state.avgp) + close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET") return #PROFIT @@ -894,12 +900,7 @@ def next(data, state: StrategyState): max_price_signal = curr_price>=max_price #OPTIMALIZACE pri stoupajícím angle if max_price_signal or sell_protection_enabled() is False: - res = state.sell(size=state.positions) - if isinstance(res, int) and res < 0: - raise Exception(f"error in required operation PROFIT SELL {res}") - state.vars.pending = state.vars.activeTrade.id - state.vars.activeTrade = None - state.ilog(e=f"PROFIT MET EXIT. market SELL was sent {curr_price=} {max_price_signal=}", positions=state.positions, avgp=state.avgp, sellinprogress=state.vars.sell_in_progress) + close_position(direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}") return def execute_prescribed_trades(): @@ -939,6 +940,7 @@ def next(data, state: StrategyState): #normalizuji dle aktualni ceny sl_defvalue_normalized = normalize_tick(sl_defvalue) state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized + insert_SL_history() state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }") state.vars.pending = state.vars.activeTrade.id elif state.vars.activeTrade.direction == TradeDirection.SHORT: @@ -952,6 +954,7 @@ def next(data, state: StrategyState): #normalizuji dle aktualni ceny sl_defvalue_normalized = normalize_tick(sl_defvalue) state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized + insert_SL_history() state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }") state.vars.pending = state.vars.activeTrade.id else: @@ -966,7 +969,7 @@ def next(data, state: StrategyState): def execute_asr(): pass - #preconditions and conditions of BUY SIGNAL + #preconditions and conditions of LONG/SHORT SIGNAL def conditions_met(signalname: str, direction: TradeDirection): if direction == TradeDirection.LONG: smer = "long" @@ -989,6 +992,8 @@ def next(data, state: StrategyState): # dont_buy_when['last_buy_offset_too_soon'] = data['index'] < (int(state.vars.lastbuyindex) + int(safe_get(state.vars, "lastbuy_offset",3))) # dont_buy_when['blockbuy_active'] = (state.vars.blockbuy == 1) # dont_buy_when['jevylozeno_active'] = (state.vars.jevylozeno == 1) + + #obecne open_rush platne pro vsechny dont_do_when['open_rush'] = is_open_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), safe_get(state.vars, "open_rush",0)) dont_do_when['close_rush'] = is_close_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), safe_get(state.vars, "close_rush",0)) @@ -1065,14 +1070,27 @@ def next(data, state: StrategyState): state.ilog(e="No options for {name} in stratvars") return - validfrom = safe_get(options, "validfrom", 0) - validto = safe_get(options, "validto", 0) + #check working windows + close_rush = safe_get(options, "close_rush",0) + open_rush = safe_get(options, "open_rush",0) + + if is_open_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), open_rush) or is_close_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), close_rush): + state.ilog(e="SINGAL {name} OUT of WORKING WINDOW", msg=f"{open_rush=} {close_rush=} ") + return + + #natvrdo nebo na podminku + activated = safe_get(options, "activated", True) + recurring = safe_get(options, "reccurring", False) on_confirmed_only = safe_get(options, 'on_confirmed_only', False) plugin = safe_get(options, 'plugin', None) short_enabled = safe_get(state.vars, "short_enabled",True) long_enabled = safe_get(state.vars, "long_enabled",True) + if activated is False: + state.ilog(e="SIGNAL {name} NOT ACTIVATED") + return + #pokud je plugin True, spusti se kod if plugin: execute_signal_generator_plugin(name) @@ -1121,7 +1139,10 @@ def next(data, state: StrategyState): trade = state.vars.activeTrade if trade is None: return -1 - + + #SL - trailing + trail_SL_management() + eval_close_position() #SELL STOPLOSS #SELL PROFIT @@ -1204,8 +1225,10 @@ def init(state: StrategyState): 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") - #nove atributy na rizeni tradu + #init klice v extData pro ulozeni historie SL + state.extData["sl_history"] = [] + #nove atributy na rizeni tradu #identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace) state.vars.pending = None #obsahuje aktivni Trade a jeho nastaveni diff --git a/v2realbot/common/__pycache__/model.cpython-310.pyc b/v2realbot/common/__pycache__/model.cpython-310.pyc index 26abf1a45a81566ef8dd7d1a65da78f875ec23e3..5c72e14d440f89ff622add187150a6553f56bd7b 100644 GIT binary patch delta 461 zcmW-d&r1S96vt=QRae>7#msg!u^(D(3f-!MhoGPiBA5i>MWmU7WEiuXXum}252)r( z=oWMkkNpXqy6(SFgnvLp?=|o~@6DU}^1d@~o8xVxYZy@i%bk9+mq#N@&&S0kP3;r5 z%<}hy<$3Zh&{$=KI&oGe5A<|~`jekzh9)jR^$Hy5>b0`c;9QXme;B} z(I+cs-hxyZhz>%>hZjaren7Y;AWAft0&^6cWm18sRcB3p*;5lgSKtr*dk`a#&&l6( OoR(xPQ&dem8Ttb-wPAz+ delta 255 zcmdmJvD=I{pO=@50SNv}yiQ@9$Scd3uu(gnku60zm_buz^8&_fVaBM| zYk{~(9z=k6lM|VwHV2EnW@Id%Y%d|pQvnpa#gdYlTvAjyIYUB8pbDh28bs892);=| z5{#vjH%pkalmhu6?Tl`d6Im1{zm`;GOJN6E$uU_;%7NKWlXJ3wggkdGNEa`N$N_3q zt^=}81934Qkl { + + console.log("plnime") + + //nova sada + if (prev_id !== histRecord.id) { + if (prev_id !== 0) { + //push sadu do pole + sl_line.push(sl_line_sada) + sl_line_markers.push(sl_line_markers_sada) + } + //init nova sada + sl_line_sada = [] + sl_line_markers_sada = [] + } + + prev_id = histRecord.id + //prevedeme iso data na timestampy + cas = histRecord.time + //line pro buy/sell markery + sline = {} + sline["time"] = cas + sline["value"] = histRecord.sl_val + sl_line_sada.push(sline) + + sline_markers = {} + sline_markers["time"] = cas + sline_markers["position"] = "inBar" + sline_markers["color"] = "#f5aa42" + //sline_markers["shape"] = "circle" + sline_markers["text"] = histRecord.sl_val.toFixed(3) + sl_line_markers_sada.push(sline_markers) + + }); + data.bars.time.forEach((element, index, array) => { sbars = {}; svolume = {}; @@ -117,7 +161,7 @@ function transform_data(data) { marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : "" } else { closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c" - marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : "" + marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3) } markers.push(marker) @@ -153,7 +197,11 @@ function transform_data(data) { transformed["avgp_markers"] = avgp_markers transformed["markers"] = markers transformed["markers_line"] = markers_line - + transformed["sl_line"] = sl_line + transformed["sl_line_markers"] = sl_line_markers + console.log("naplnene", sl_line, sl_line_markers) + //console_log(JSON.stringify(transformed["sl_line"],null,2)) + //console_log(JSON.stringify(transformed["sl_line_markers"],null,2)) //get additional indicators return transformed } @@ -539,11 +587,47 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { if (markersLine) { chart.removeSeries(markersLine) - } + } + + slLine.forEach((series, index, array) => { + chart.removeSeries(series) + }) + // if (slLine) { + // chart.removeSeries(slLine) + // } //console.log("avgp_buy_line",JSON.stringify(transformed_data["avgp_buy_line"],null,2)) //console.log("avgp_markers",JSON.stringify(transformed_data["avgp_markers"],null,2)) + //if (transformed_data["sl_line"].length > 0) { + //console.log(JSON.stringify(transformed_data["sl_line"]), null,2) + //xx - ted bude slLine pole + transformed_data["sl_line"].forEach((slRecord, index, array) => { + + console.log("uvnitr") + slLine_temp = chart.addLineSeries({ + // title: "avgpbuyline", + color: '#e4c76d', + // color: 'transparent', + lineWidth: 1, + lastValueVisible: false + }); + + slLine_temp.applyOptions({ + lastValueVisible: false, + priceLineVisible: false, + }); + + slLine_temp.setData(slRecord); + //zatim nemame markery pro sl history + slLine_temp.setMarkers(transformed_data["sl_line_markers"][index]); + slLine.push(slLine_temp) + + //xx + }) + + //} + if (transformed_data["avgp_buy_line"].length > 0) { avgBuyLine = chart.addLineSeries({ // title: "avgpbuyline", diff --git a/v2realbot/static/js/livewebsocket.js b/v2realbot/static/js/livewebsocket.js index 23424fe..cb826ad 100644 --- a/v2realbot/static/js/livewebsocket.js +++ b/v2realbot/static/js/livewebsocket.js @@ -3,6 +3,7 @@ var pbiList = [] var ws = null; var logcnt = 0 var positionsPriceLine = null +var stoplossPriceLine = null var limitkaPriceLine = null var angleSeries = {} //var angleSeries_slow = 1 @@ -200,6 +201,24 @@ function connect(event) { candlestickSeries.removePriceLine(positionsPriceLine) } positionsPriceLine = candlestickSeries.createPriceLine(posLine); + + if (positions.sl_value !== null) { + if (stoplossPriceLine !== null) { + candlestickSeries.removePriceLine(stoplossPriceLine) + } + const stoplossLine = { + price: positions.sl_value, + color: '#f5aa42', + lineWidth: 1, + lineStyle: 1, // LineStyle.Dotted + axisLabelVisible: true, + title: "SL", + }; + + stoplossPriceLine = candlestickSeries.createPriceLine(stoplossLine); + } + + } if (parsed_data.hasOwnProperty("statinds")) { diff --git a/v2realbot/strategy/__pycache__/base.cpython-310.pyc b/v2realbot/strategy/__pycache__/base.cpython-310.pyc index 0b5dbbe6bf88bc4f3d605ceead277195d3f3783a..e9a96f179207ced1daada31cfe2afbc9db576802 100644 GIT binary patch delta 1197 zcmZ9LU2GFa5XW~m=d($C#vv3E1BvU9D2J(NXi(HTl}aH9T9gQggd!0I!#R*k6oTvMTi@YSn!}tyIxIBDDzQzVwMo5Wc^^buUPOgm^-{AYS;GSqJmslYck!-orEGQa8$Uga*Yx0 zeKKzcM!1NnpA$)|HxEvrfjK5Bz3ObjwmS_bt8+cT=;>z%I;WU4F)!%W6u~h@3^7sm z)a68T^MH<)mFR&=ba*~msln#t%umw`be82TXkc!n5ZKAaEX-XfqeDD*w4Cp-LZF|G z0oDK=U8}iBvo0TSs%MQncWS!W^wM%QcQN|muD9`)OA`iN%U$AFvmDnVT?}`spTVt` z=AHm6n82pPvR*di%_0;5aJe53tYH3Njs?o^DQciRa%_Hx7rJZ=3ogXX>YXTMqEqyI2d!-4rbS>Lo3_c z{F?M$IXs!^6KN-78{#XxUvZEyh44e(=-^~}bRwfpt=tI-wXEg4O};Ui7M>sRWOjTs z>v`n>KlFVj*DXX=_>uKeis3yIGNb*EYKjw7{aa90m2Vr7^Gzjj)Qh zt_gE<5kw&b{B^uaZEIZu;nM!rzoFq2<@Qw2OI8+J*Hp@mL#njP zZiboDr0gcl5zY|K63!9M6ZGW&kadA@kuXZQL>M9HjjvQJ?dldh)VlFc;l4P-Zj@TKgCdVFah)(txIwr{I83;Ohs|uzgWHYbKeeU(aO@6jgq&dJ zpxl_2Y4xmqyE#BZ4yi3`Gu-5*V{7Bk_7H>Ys`ByuL3On-|4Y)sBdx0EUwjKE)t4Pj z@Idu<*zmh5cC4?tLyLC__te#n&%yt7U2Ck5wh5saVg_ss)qgGaqhqJUJ4HqAXNYW delta 1057 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 K&qwrjdH)BuOKPV8 delta 3700 zcmZ{mdu&tJ9mjoa$8i!TnBW8_@0h=h^|< zYVJffXc=qS>AGjPc2!$~gha_UO=DdrwNQQd6a00b_=3KCuDF^w&b7RpB_?X31zL5yMyZL{Dz))C zr7rGKJW?;zyzzRaUY5;TgVF%DMVc?ZR9PBtR2t(=O4Cf6U-3hmLu-z=C@oSe)&lXM z5|rf%g{`dRQOzvJLDZ zh+wWTK}$Vtn6xSzsV_^Hf?Ge0(MH-dNt8|0Pn$v6Oj~FGo?B=TooI-*g2UELCfY`q zO`7PklV)Wbvz|05+nM8q9kl&Lq72aGbOrd7&{5fm4(PNOI%1=%XeV@JC#n#ui>`)P zyXYF|Xx7VCLw~yIT99@_H`dV}=*A!&qrEJT_Q}=_^dWHHGe_G<`#~E5k4>x(f^Mc; zpuQLCTVcMoNx$uM2Wb1|XajU7X#43dM!;)#mJUMV12C>VbO^>Z1mikL_tJeZ4h7Wx z^Z=*_Sl>yDa;U&W53)m4VQXMqhhSWX=)>UsFg*+@b7~i;!!!))VS0o{Aj&Wup;35- zsd>7)u!x|e^ z3b;BtCW})h9uTFrCrL=`^0~w%+e@TT4CM!k0_Y|Y6ld}qNykjPTinb)-Pek(jfghD zP(NRW<>iQ3j!-omQJ&OwM#D6DEYX7os}P+s-0OC=vJ6xA?x@#tWalWL1tQYT>xaSP&PRSlJ zAR?tM@lj=gh%BgW^FuJ0Qj4aiX9vR+`Mnd2M@HF?p`x1sPQnMpr3J4A+R@3P6h@Pp z#-h-V1m7+_D2qih+MGz@tTRaVOLhMhN?>a496IXq1akM-}dd0Q!7f6NRiFNUiseP=+QpYjxFHZqgaZRMxr z5q+&{ux?&_l5(J#73|%llyD*Ht8bBKg{`KUJTI2jY}@~R4F4R!C>hD%B%qb$LW zr}(-J;&0xTK^*mzh2uTQH)CSUqiPC-X%v2m*nyba3El!)4=xxU4m=tqqz*Q^)Wv^2 z6;}Q$lSyda72-m@qv@-T@n1h6)^9=EhAVPK+^F{iJ|V&M^tV8u59A4l_*;=6u#Uq2Qdh!R9G zz;EN%u>2>0VM(xK203n!af3|En-m#G6QBF*$+&PdpE)S!BA*{Y@0Ez_hz}7mrA)RF zs&H~XLKRc+zak98Fk3r(jl|I&d`1iIicF@nl?0!%Dm< z&bD}0&JAWbqsat(2&BX5WHc}bfdH>Jr@^GWz@ z<1(=&m-u67ojf3#dz!Z% zDT?dPQP}X~A$B~?@sowWBc2Itv7N-?S#dMaY{S!8%d@^Fds2>lvkj2C1MHUAX-^78I^MUa51b5CM);lQiJVL zL@k11b6M_WhSK7n!LsZPlx(R;f<|=yF; F{{tcjY?S~2 diff --git a/v2realbot/utils/utils.py b/v2realbot/utils/utils.py index 4db727a..a538644 100644 --- a/v2realbot/utils/utils.py +++ b/v2realbot/utils/utils.py @@ -9,7 +9,7 @@ import decimal from v2realbot.enums.enums import RecordType, Mode, StartBarAlign import pickle import os -from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals +from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from typing import List import tomli @@ -208,6 +208,8 @@ def json_serial(obj): return obj.__dict__ if type(obj) is Intervals: return obj.__dict__ + if type(obj) is SLHistory: + return obj.__dict__ raise TypeError (str(obj)+"Type %s not serializable" % type(obj))