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 26abf1a..5c72e14 100644 Binary files a/v2realbot/common/__pycache__/model.cpython-310.pyc and b/v2realbot/common/__pycache__/model.cpython-310.pyc differ diff --git a/v2realbot/common/model.py b/v2realbot/common/model.py index 8d80166..c02b130 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -199,6 +199,12 @@ class RunArchive(BaseModel): end_positions_avgp: float = 0 open_orders: Union[dict, int] = None +#trida pro ukladani historie stoplossy do ext_data +class SLHistory(BaseModel): + id: Optional[UUID] + time: datetime + sl_val: float + #Contains archive of running strategies (runner) - detail data class RunArchiveDetail(BaseModel): id: UUID @@ -208,6 +214,7 @@ class RunArchiveDetail(BaseModel): indicators: List[dict] statinds: dict trades: List[TradeUpdate] + ext_data: Optional[dict] # class Trade(BaseModel): # order: Order diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index f88898e..fbc923b 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -595,12 +595,14 @@ def archive_runner(runner: Runner, strat: StrategyInstance): # flattened_indicators[key]= value # flattened_indicators_list.append(flattened_indicators) + runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = runner.id, name=runner.run_name, bars=strat.state.bars, indicators=flattened_indicators_list, statinds=strat.state.statinds, - trades=strat.state.tradeList) + trades=strat.state.tradeList, + ext_data=strat.state.extData) with lock: resh = db_arch_h.insert(runArchive.__dict__) resd = insert_archive_detail(runArchiveDetail) diff --git a/v2realbot/static/js/archivechart.js b/v2realbot/static/js/archivechart.js index a42f9f4..4475689 100644 --- a/v2realbot/static/js/archivechart.js +++ b/v2realbot/static/js/archivechart.js @@ -5,6 +5,7 @@ var CHART_SHOW_TEXT = false // var volumeSeries = null var markersLine = null var avgBuyLine = null +var slLine = [] //TRANSFORM object returned from REST API get_arch_run_detail //to series and markers required by lightweigth chart //input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...} @@ -15,6 +16,49 @@ function transform_data(data) { var bars = [] var volume = [] var vwap = [] + + //pokud mame tak projedeme ext_data pro dane klice a s nimi pracujeme + var sl_line = [] + var sl_line_markers = [] + var sl_line_sada = [] + var sl_line_markers_sada = [] + //console.log(JSON.stringify(data.ext_data.sl_history, null, 2)) + prev_id = 0 + data.ext_data.sl_history.forEach((histRecord, index, array) => { + + 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 0b5dbbe..e9a96f1 100644 Binary files a/v2realbot/strategy/__pycache__/base.cpython-310.pyc and b/v2realbot/strategy/__pycache__/base.cpython-310.pyc differ diff --git a/v2realbot/strategy/base.py b/v2realbot/strategy/base.py index 422af7a..a77d16d 100644 --- a/v2realbot/strategy/base.py +++ b/v2realbot/strategy/base.py @@ -514,7 +514,14 @@ class Strategy: #vkladame average price and positions, pokud existuji #self.state.avgp , self.state.positions - rt_out["positions"] = dict(time=self.state.time, positions=self.state.positions, avgp=self.state.avgp) + + #pro typ strategie Classic, posilame i vysi stoploss + try: + sl_value = self.state.vars["activeTrade"].stoploss_value + except (KeyError, AttributeError): + sl_value = None + + rt_out["positions"] = dict(time=self.state.time, positions=self.state.positions, avgp=self.state.avgp, sl_value=sl_value) #vkladame limitku a pendingbuys try: @@ -652,6 +659,8 @@ class StrategyState: self.iter_log_list = [] self.profit = 0 self.tradeList = [] + #nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history + self.extData = {} self.mode = None def ilog(self, e: str = None, msg: str = None, **kwargs): diff --git a/v2realbot/utils/__pycache__/utils.cpython-310.pyc b/v2realbot/utils/__pycache__/utils.cpython-310.pyc index a183cb6..4aa9604 100644 Binary files a/v2realbot/utils/__pycache__/utils.cpython-310.pyc and b/v2realbot/utils/__pycache__/utils.cpython-310.pyc differ 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))