From de5382d04ab2651bacf9c921ad257d2bb4609b96 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Tue, 24 Oct 2023 18:49:39 +0200 Subject: [PATCH] finalni instant indikatory + save do arch --- testy/previewindicator.py | 31 ++- v2realbot/LSTMevalrunner.py | 2 +- v2realbot/common/db.py | 7 +- v2realbot/common/model.py | 8 +- v2realbot/controller/services.py | 167 +++++++++++++--- v2realbot/main.py | 18 +- v2realbot/static/index.html | 1 + v2realbot/static/js/archivechart.js | 57 ++++-- v2realbot/static/js/archivetables.js | 124 ------------ v2realbot/static/js/instantindicators.js | 184 ++++++++++++++++++ v2realbot/static/js/utils.js | 38 ++-- v2realbot/strategyblocks/indicators/RSI.py | 8 +- .../indicators/custom/conditional.py | 1 + .../indicators/custom/expression.py | 7 +- .../strategyblocks/indicators/custom/ma.py | 8 +- .../strategyblocks/inits/init_indicators.py | 1 + v2realbot/utils/utils.py | 27 +-- 17 files changed, 455 insertions(+), 234 deletions(-) create mode 100644 v2realbot/static/js/instantindicators.js diff --git a/testy/previewindicator.py b/testy/previewindicator.py index 9efa6d7..7716647 100644 --- a/testy/previewindicator.py +++ b/testy/previewindicator.py @@ -1,19 +1,32 @@ import v2realbot.controller.services as cs - +from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator #[stratvars.indicators.vwma] -runner_id = "b44d6d8f-b44d-45b1-ad7a-7ee8b0facead" +runner_id = "1ac42f29-b902-44df-9bd6-e2a430989705" toml = """ - type = "custom" - subtype = "vwma" - on_confirmed_only = true - cp.source = "vwap" - cp.ref_source = "volume" - cp.lookback = 50 +#[stratvars.indicators.cross] +type = 'custom' +subtype = 'conditional' +on_confirmed_only = true +[cp.conditions.crossdown] +vwap.change_val_if_crossed_down = 'emaSlow' +true_val = -1 +[cp.conditions.crossup] +vwap.change_val_if_crossed_up = 'emaSlow' +true_val = 1 """ -res, vals = cs.preview_indicator_byTOML(id=runner_id, toml=toml) +toml = """ +#[stratvars.indicators.rsi14] +type = 'RSI' +source = 'vwap' +length = 14 +on_confirmed_only = true +""" +indicator = InstantIndicator(name="rsi14alt", toml=toml) + +res, vals = cs.preview_indicator_byTOML(id=runner_id, indicator=indicator) print(res) print(vals) diff --git a/v2realbot/LSTMevalrunner.py b/v2realbot/LSTMevalrunner.py index b5f6ac2..399b164 100644 --- a/v2realbot/LSTMevalrunner.py +++ b/v2realbot/LSTMevalrunner.py @@ -86,7 +86,7 @@ if save_new_ind: sada["indicators"][0]["pred_added"] = ind_pred - req = update_archive_detail(runner_id, sada) + req, res = update_archive_detail(runner_id, sada) print(f"indicator pred_added was ADDED to {runner_id}") diff --git a/v2realbot/common/db.py b/v2realbot/common/db.py index a16e073..afb85e7 100644 --- a/v2realbot/common/db.py +++ b/v2realbot/common/db.py @@ -31,7 +31,7 @@ class ConnectionPool: return connection -def execute_with_retry(cursor: sqlite3.Cursor, statement: str, retry_interval: int = 1) -> sqlite3.Cursor: +def execute_with_retry(cursor: sqlite3.Cursor, statement: str, params = None, retry_interval: int = 1) -> sqlite3.Cursor: """get connection from pool and execute SQL statement with retry logic if required. Args: @@ -44,7 +44,10 @@ def execute_with_retry(cursor: sqlite3.Cursor, statement: str, retry_interval: i """ while True: try: - return cursor.execute(statement) + if params is None: + return cursor.execute(statement) + else: + return cursor.execute(statement, params) except sqlite3.OperationalError as e: if str(e) == "database is locked": print("database retry in 1s." + str(e)) diff --git a/v2realbot/common/model.py b/v2realbot/common/model.py index e4a2a79..4274acb 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -266,10 +266,10 @@ class RunArchiveDetail(BaseModel): statinds: dict trades: List[TradeUpdate] ext_data: Optional[dict] + -class TomlInput(BaseModel): +class InstantIndicator(BaseModel): + name: str toml: str -# class Trade(BaseModel): -# order: Order -# value: float + diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index f983a7c..3272a8a 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -7,7 +7,7 @@ from alpaca.data.enums import DataFeed from alpaca.data.timeframe import TimeFrame from v2realbot.strategy.base import StrategyState from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide -from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem +from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram from v2realbot.utils.ilog import delete_logs from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType @@ -32,6 +32,8 @@ from threading import Lock from v2realbot.common.db import pool, execute_with_retry, row_to_runarchive, row_to_runarchiveview from sqlite3 import OperationalError, Row import v2realbot.strategyblocks.indicators.custom as ci +from v2realbot.strategyblocks.inits.init_indicators import initialize_dynamic_indicators +from v2realbot.strategyblocks.indicators.indicators_hub import populate_dynamic_indicators from v2realbot.interfaces.backtest_interface import BacktestInterface #from pyinstrument import Profiler @@ -1071,12 +1073,15 @@ def update_archive_detail(id: UUID, archdetail: RunArchiveDetail): try: c = conn.cursor() json_string = json.dumps(archdetail, default=json_serial) - statement = f"UPDATE runner_detail SET data = '{json_string}' WHERE runner_id='{str(id)}'" - res = execute_with_retry(c,statement) + statement = "UPDATE runner_detail SET data = ? WHERE runner_id = ?" + params = (json_string, str(id)) + ##statement = f"UPDATE runner_detail SET data = '{json_string}' WHERE runner_id='{str(id)}'" + ##print(statement) + res = execute_with_retry(cursor=c,statement=statement,params=params) conn.commit() finally: pool.release_connection(conn) - return res.rowcount + return 0, res.rowcount def insert_archive_detail(archdetail: RunArchiveDetail): conn = pool.get_connection() @@ -1111,15 +1116,40 @@ def get_testlists(): # endregion -#WIP - possibility to add TOML indicator -# open issues: hodnoty dalsich indikatoru -def preview_indicator_byTOML(id: UUID, toml: str): +#WIP - instant indicators +def preview_indicator_byTOML(id: UUID, indicator: InstantIndicator): try: - res, toml_parsed = parse_toml_string(toml) + if indicator.name is None: + return (-2, "name is required") + + #print("na zacatku", indicator.toml) + + tomlino = indicator.toml + jmeno = indicator.name + #print("tomlino",tomlino) + #print("jmeno",jmeno) + + # indicator = AttributeDict(**indicator.__dict__) + + + # print(indicator) + # def format_toml_string(toml_string): + # return f"'''{toml_string.strip()}'''" + # print("before",indicator['toml']) + # indicator['toml'] = format_toml_string(indicator['toml']) + # print(indicator['toml']) + + # print("jmeno", str(indicator.name)) + # row = f"[stratvars]\n[stratvars.indicators.{str(indicator.name)}]\n" + # print(row, end='') + # row = "[stratvars]\n[stratvars.indicators.{indicator.name}]\n" + # print(row.format(indicator=indicator)) + # print(row) + res, toml_parsed = parse_toml_string(tomlino) if res < 0: return (-2, "toml invalid") - print("parsed toml", toml_parsed) + #print("parsed toml", toml_parsed) subtype = safe_get(toml_parsed, 'subtype', False) if subtype is None: @@ -1133,16 +1163,6 @@ def preview_indicator_byTOML(id: UUID, toml: str): if res < 0: return (-2, "no archived runner {id}") - # class RunArchiveDetail(BaseModel): - # id: UUID - # name: str - # bars: dict - # #trades: Optional[dict] - # indicators: List[dict] - # statinds: dict - # trades: List[TradeUpdate] - # ext_data: Optional[dict] - #TODO - conditional udelat podminku # if value == "conditional": # conditions = state.vars.indicators[indname]["cp"]["conditions"] @@ -1156,57 +1176,140 @@ def preview_indicator_byTOML(id: UUID, toml: str): detail = RunArchiveDetail(**val) #print("toto jsme si dotahnuli", detail.bars) + #pokud tento indikator jiz je v detailu, tak ho odmazeme + if indicator.name in detail.indicators[0]: + del detail.indicators[0][indicator.name] + + #new dicts new_bars = {key: [] for key in detail.bars.keys()} new_bars = AttributeDict(**new_bars) + new_data = {key: None for key in detail.bars.keys()} + new_data= AttributeDict(**new_data) new_inds = {key: [] for key in detail.indicators[0].keys()} new_inds = AttributeDict(**new_inds) interface = BacktestInterface(symbol="X", bt=None) - state = StrategyState(name="XX", symbol = "X", stratvars = toml, interface=interface) + ##dame nastaveni indikatoru do tvaru, ktery stratvars ocekava (pro dynmaicke inicializace) + stratvars = AttributeDict(indicators=AttributeDict(**{jmeno:toml_parsed})) + print("stratvars", stratvars) + + state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface) + + #inicializujeme novy indikator v cilovem dict a stavovem inds. + new_inds[indicator.name] = [] + new_inds[indicator.name] = [] state.bars = new_bars state.indicators = new_inds - new_inds["new"] = [] print("delka",len(detail.bars["close"])) #intitialize indicator mapping - in order to use eval in expression local_dict_inds = {key: state.indicators[key] for key in state.indicators.keys() if key != "time"} local_dict_bars = {key: state.bars[key] for key in state.bars.keys() if key != "time"} - state.ind_mapping = {**local_dict_inds, **local_dict_bars} print("IND MAPPING DONE:", state.ind_mapping) + ##intialize dynamic indicators + initialize_dynamic_indicators(state) - print("subtype") - function = "ci."+subtype+"."+subtype - print("funkce", function) - custom_function = eval(function) + + # print("subtype") + # function = "ci."+subtype+"."+subtype + # print("funkce", function) + # custom_function = eval(function) + #iterujeme nad bary a on the fly pridavame novou hodnotu do vsech indikatoru a nakonec nad tim spustime indikator #tak muzeme v toml pouzit i hodnoty ostatnich indikatoru for i in range(len(detail.bars["close"])): for key in detail.bars: state.bars[key].append(detail.bars[key][i]) + #naplnime i data aktualne + new_data[key] = state.bars[key][-1] for key in detail.indicators[0]: state.indicators[key].append(detail.indicators[0][key][i]) - new_inds["new"].append(0) + new_inds[indicator.name].append(0) + try: - res_code, new_val = custom_function(state, custom_params) - if res_code == 0: - new_inds["new"][-1]=new_val + populate_dynamic_indicators(new_data, state) + # res_code, new_val = custom_function(state, custom_params) + # if res_code == 0: + # new_inds[indicator.name][-1]=new_val except Exception as e: print(str(e) + format_exc()) - print("Done", f"delka {len(new_inds['new'])}", new_inds["new"]) - return 0, new_inds["new"] + print("Done - static", f"delka {len(state.indicators[indicator.name])}", state.indicators[indicator.name]) + #print("Done", f"delka {len(new_inds[indicator.name])}", new_inds[indicator.name]) + + new_inds[indicator.name] = state.indicators[indicator.name] + + #ukládáme do ArchRunneru + detail.indicators[0][indicator.name] = new_inds[indicator.name] + + #do ext dat ukladame jmeno indikatoru (podle toho oznacuje jako zmenene) + + #inicializace ext_data a instantindicator pokud neexistuje + if hasattr(detail, "ext_data"): + if "instantindicators" not in detail.ext_data: + detail.ext_data["instantindicators"] = [] + else: + setattr(detail, "ext_data", dict(instantindicators=[])) + + #pokud tam je tak odebereme + for ind in detail.ext_data["instantindicators"]: + if ind["name"] == indicator.name: + detail.ext_data["instantindicators"].remove(ind) + print("removed old from EXT_DATA") + + #a pridame aktualni + detail.ext_data["instantindicators"].append(indicator) + print("added to EXT_DATA") + #updatneme ArchRunner + res, val = update_archive_detail(id, detail) + if res == 0: + print(f"arch runner {id} updated") + + return 0, new_inds[indicator.name] except Exception as e: print(str(e) + format_exc()) return -2, str(e) +def delete_indicator_byName(id: UUID, indicator: InstantIndicator): + try: + #dotahne runner details + res, val = get_archived_runner_details_byID(id) + if res < 0: + return (-2, "no archived runner {id}") + + detail = RunArchiveDetail(**val) + #print("toto jsme si dotahnuli", detail.bars) + + #pokud tento indikator je v detailu + if indicator.name in detail.indicators[0]: + del detail.indicators[0][indicator.name] + print("values removed from indicators") + + #do ext dat ukladame jmeno indikatoru (podle toho oznacuje jako zmenene) + if hasattr(detail, "ext_data") and "instantindicators" in detail.ext_data: + for ind in detail.ext_data["instantindicators"]: + if ind["name"] == indicator.name: + detail.ext_data["instantindicators"].remove(ind) + print("removed from EXT_DATA") + + #updatneme ArchRunner + res, val = update_archive_detail(id, detail) + if res == 0: + print("Archive udpated") + + return 0, val + + except Exception as e: + print(str(e) + format_exc()) + return -2, str(e) # region CONFIG db services #TODO vytvorit modul pro dotahovani z pythonu (get_from_config(var_name, def_value) {)- stejne jako v js diff --git a/v2realbot/main.py b/v2realbot/main.py index f73b4ed..ff94e9f 100644 --- a/v2realbot/main.py +++ b/v2realbot/main.py @@ -11,7 +11,7 @@ import uvicorn from uuid import UUID import v2realbot.controller.services as cs from v2realbot.utils.ilog import get_log_window -from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, TomlInput +from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles @@ -432,15 +432,27 @@ def _delete_archived_runners_byIDs(runner_ids: list[UUID]): #WIP - TOM indicator preview from frontend #return indicator value for archived runner @app.put("/archived_runners/{runner_id}/previewindicator", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) -def _preview_indicator_byTOML(runner_id: UUID, toml: TomlInput) -> list[float]: +def _preview_indicator_byTOML(runner_id: UUID, indicator: InstantIndicator) -> list[float]: #mozna pak pridat name - res, vals = cs.preview_indicator_byTOML(id=runner_id, toml=toml.toml) + res, vals = cs.preview_indicator_byTOML(id=runner_id, indicator=indicator) if res == 0: return vals elif res == -1: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{vals}") else: raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error not changed: {res}:{runner_id}:{vals}") +#delete instant indicator from detail +@app.delete("/archived_runners/{runner_id}/previewindicator", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) +def _delete_indicator_byName(runner_id: UUID, indicator: InstantIndicator): + #mozna pak pridat name + res, vals = cs.delete_indicator_byName(id=runner_id, indicator=indicator) + if res == 0: return vals + elif res == -1: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{vals}") + else: + raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error not changed: {res}:{runner_id}:{vals}") + + #edit archived runner ("note",..) @app.patch("/archived_runners/{runner_id}", dependencies=[Depends(api_key_auth)]) diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index 98618ab..289c95f 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -747,6 +747,7 @@ + diff --git a/v2realbot/static/js/archivechart.js b/v2realbot/static/js/archivechart.js index c668e9f..ae0e331 100644 --- a/v2realbot/static/js/archivechart.js +++ b/v2realbot/static/js/archivechart.js @@ -340,26 +340,45 @@ function chart_indicators(data, visible, offset) { var cnf = null //pokud je v nastaveni scale, pouzijeme tu var scale = null - try { - if (addedInds[key]) { - cnf = addedInds[key] - scale = TOML.parse(cnf).scale - } - else - { - cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'}) - scale = stratvars_toml.stratvars.indicators[key].scale - //cnf = TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'}) - //a = TOML.parse(cnf) - //console.log("PARSED again",a) + var instant = null + //console.log(key) + //zkusime zda nejde o instantni indikator z arch runneru + if ((data.ext_data !== null) && (data.ext_data.instantindicators)) { + let instantIndicator = data.ext_data.instantindicators.find(indicator => indicator.name == key); + //console.log("nalezen", key) + if (instantIndicator) { + cnf = instantIndicator.toml + scale = TOML.parse(cnf).scale + instant = 1 } } - catch (e) { - //nic - } + //pokud nenalezeno, pak bereme standard + //pozor ted nebereme z addedInds + if (!cnf) { + if (stratvars_toml.stratvars.indicators[key]) { + cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'}) + scale = stratvars_toml.stratvars.indicators[key].scale + } + } + // //kontriolujeme v addedInds + // if (addedInds[key]) { + // cnf = addedInds[key] + // scale = TOML.parse(cnf).scale + // instant = 1 + // } + // //a az potom bereme normos + // else + // { + // cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'}) + // scale = stratvars_toml.stratvars.indicators[key].scale + // //cnf = TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'}) + // //a = TOML.parse(cnf) + // //console.log("PARSED again",a) + // } + // } //initialize indicator and store reference to array - var obj = {name: key, series: null, cnf:cnf, added: ((addedInds[key])?1:null)} + var obj = {name: key, series: null, cnf:cnf, instant: instant} //start //console.log(key) @@ -580,7 +599,9 @@ function remove_indicators() { } //switch to interval pomocna funkce -function switch_to_interval(interval, data) { +function switch_to_interval(interval, data, extra) { + store_activated_buttons_state(extra); + if (!data) { window.alert("no data switch to interval") } @@ -671,7 +692,7 @@ function display_buy_markers(data) { //xx - ted bude slLine pole transformed_data["sl_line"].forEach((slRecord, index, array) => { - console.log("uvnitr") + //console.log("uvnitr") slLine_temp = chart.addLineSeries({ // title: "avgpbuyline", color: '#e4c76d', diff --git a/v2realbot/static/js/archivetables.js b/v2realbot/static/js/archivetables.js index 74e90ff..bfa6ec5 100644 --- a/v2realbot/static/js/archivetables.js +++ b/v2realbot/static/js/archivetables.js @@ -2,7 +2,6 @@ let editor_diff_arch1 let editor_diff_arch2 var archData = null -var addedInds = {} function refresh_arch_and_callback(row, callback) { //console.log("entering refresh") @@ -314,129 +313,6 @@ $(document).ready(function () { } }); -//Kod pro add indicator -dat do arch chart souboru - - //modal - delete indicator button - $('#deleteIndicatorButton').click(function () { - window.$('#indicatorModal').modal('hide'); - indname = $('#indicatorName').val() - //updatneme globalni promennou obsahujici vsechny arch data - //TBD nebude fungovat az budu mit vic chartů otevřených - předělat - if (archData.indicators[0][indname]) { - delete archData.indicators[0][indname] - delete addedInds[indname] - //get active resolution - const element = document.querySelector('.switcher-active-item'); - resolution = element.textContent - //console.log("aktivni rozliseni", resolution) - switch_to_interval(resolution, archData) - } - }); - - - var myModalEl = document.getElementById('indicatorModal') - myModalEl.addEventListener('hidden.bs.modal', function (event) { - close_addind_modal() - }) - - function close_addind_modal() { - index = $('#indicatorId').val() - const elem = document.getElementById("IND"+index); - if (elem) { - elem.classList.replace('switcher-item-highlighted', 'switcher-item'); - } - //vracime pripadny schovany del button - $('#deleteIndicatorButton').show(); - window.$('#indicatorModal').modal('hide'); - } - - //HLAVNI SAVE akce INDICATOR MODAL - ulozi nebo vytvori novy - $('#saveIndicatorButton').click(function () { - indName = $('#indicatorName').val() - if (!indName) { - alert("name musi byt vyplneno") - return - } - - index = $('#indicatorId').val() - var elem = document.getElementById("IND"+index); - if (elem) { - //pokud existuje - pak jde bud o edit nebo duplicate - podle jmena - - //jmeno je updatnute, jde o duplicate - vytvarime novy index - if (elem.textContent !== $('#indicatorName').val()) { - //alert("duplikujeme") - index_ind++ - index = index_ind - } - } - //pokud neexistuje, pak jde o novy index - pouzijeme tento - - runner_id = $("#statusArchId").text() - if (!runner_id) { - alert("no arch runner selected") - return - } - // row = archiveRecords.row('.selected').data(); - // if (row == undefined) { - - // } - - store_activated_buttons_state() - //pridame jeste tu aktualni, aby se zobrazila jako aktivni - activatedButtons.push(indName); - - - //console.log(activatedButtons) - - obj = new Object() - obj.runner_id = runner_id - obj.toml = ind_editor.getValue() - jsonString = JSON.stringify(obj); - //console.log("pred odeslanim",jsonString) - //cal rest api - $.ajax({ - url:"/archived_runners/"+runner_id+"/previewindicator", - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"PUT", - contentType: "application/json", - data: jsonString, - success:function(data){ - //kod pro update/vytvoreni je zde stejny - updatujeme jen zdrojove dictionary - window.$('#indicatorModal').modal('hide'); - //console.log(data) - //indName = $('#indicatorName').val() - //updatneme/vytvorime klic v globalni promennou obsahujici vsechny arch data - //TBD nebude fungovat az budu mit vic chartů otevřených - předělat - archData.indicators[0][indName] = data - - //glob promenna obsahujici aktualne pridane indikatory a jejich konfigurace - addedInds[indName] = obj.toml - //get active resolution - const element = document.querySelector('.switcher-active-item'); - resolution = element.textContent - //console.log("aktivni rozliseni", resolution) - switch_to_interval(resolution, archData) - }, - error: function(xhr, status, error) { - var err = eval("(" + xhr.responseText + ")"); - window.alert(JSON.stringify(xhr)); - //console.log(JSON.stringify(xhr)); - //$('#button_runagain_arch').attr('disabled',false); - } - }) - // #indicatorId - // #indicatorName - // #indicatorTOML - - - //$('#editidarchive').val(row.id); - //$('#editnote').val(row.note); -}); - - //show button $('#button_show_arch').click(function () { diff --git a/v2realbot/static/js/instantindicators.js b/v2realbot/static/js/instantindicators.js new file mode 100644 index 0000000..755c7be --- /dev/null +++ b/v2realbot/static/js/instantindicators.js @@ -0,0 +1,184 @@ +//docasne ulozeni aktivovanych buttonu pred reloadem +var activatedButtons = [] +//seznam upravenych instant indikatoru a jejich konfigurace +// var addedInds = {} + +function store_activated_buttons_state(extra) { + activatedButtons = [] + if (extra) { + activatedButtons.push(extra) + } + //ulozime si stav aktivovaných buttonků před změnou - mozna do sluzby + $('#indicatorsButtons .switcher-active-item').each(function() { + activatedButtons.push($(this).text()); + }); +} + +//JQUERY SECTION +$(document).ready(function () { + + //modal - delete indicator button + $('#deleteIndicatorButton').click(function () { + indname = $('#indicatorName').val() + runner_id = $("#statusArchId").text() + if (!runner_id) { + alert("no arch runner selected") + return + } + + obj = new Object() + obj.runner_id = runner_id + obj.toml = TOML.parse(ind_editor.getValue()) + obj.name = indname + jsonString = JSON.stringify(obj); + //console.log("pred odeslanim",jsonString) + //cal rest api + $.ajax({ + url:"/archived_runners/"+runner_id+"/previewindicator", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"DELETE", + contentType: "application/json", + data: jsonString, + success:function(data){ + window.$('#indicatorModal').modal('hide'); + //updatneme globalni promennou obsahujici vsechny arch data + //TBD nebude fungovat az budu mit vic chartů otevřených - předělat + //smazeme zaznam o indiaktoru v lokalni kopii ext_data (z nich se pak prekresli indikatory) + if ((archData.ext_data !== null) && (archData.ext_data.instantindicators)) { + let index = archData.ext_data.instantindicators.findIndex(indicator => indicator.name === indname); + if (index !== -1) { + archData.ext_data.instantindicators.splice(index, 1); + } + } + + if (archData.indicators[0][indname]) { + delete archData.indicators[0][indname] + //delete addedInds[indname] + //get active resolution + const element = document.querySelector('.switcher-active-item'); + resolution = element.textContent + //console.log("aktivni rozliseni", resolution) + switch_to_interval(resolution, archData) + } + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + //console.log(JSON.stringify(xhr)); + //$('#button_runagain_arch').attr('disabled',false); + } + }) + }); + + + var myModalEl = document.getElementById('indicatorModal') + myModalEl.addEventListener('hidden.bs.modal', function (event) { + close_indicator_modal() + }) + + function close_indicator_modal() { + index = $('#indicatorId').val() + const elem = document.getElementById("IND"+index); + if (elem) { + elem.classList.replace('switcher-item-highlighted', 'switcher-item'); + } + //vracime pripadny schovany del button + $('#deleteIndicatorButton').show(); + $('#saveIndicatorButton').show(); + window.$('#indicatorModal').modal('hide'); + } + + //HLAVNI SAVE akce INDICATOR MODAL - ulozi nebo vytvori novy + $('#saveIndicatorButton').click(function () { + indName = $('#indicatorName').val() + if (!indName) { + alert("name musi byt vyplneno") + return + } + + index = $('#indicatorId').val() + var elem = document.getElementById("IND"+index); + if (elem) { + //pokud existuje - pak jde bud o edit nebo duplicate - podle jmena + + //jmeno je updatnute, jde o duplicate - vytvarime novy index + if (elem.textContent !== $('#indicatorName').val()) { + //alert("duplikujeme") + index_ind++ + index = index_ind + } + } + //pokud neexistuje, pak jde o novy index - pouzijeme tento + + runner_id = $("#statusArchId").text() + if (!runner_id) { + alert("no arch runner selected") + return + } + + // store_activated_buttons_state() + // //pridame jeste tu aktualni, aby se zobrazila jako aktivni + // activatedButtons.push(indName); + console.log(activatedButtons) + + obj = new Object() + //obj.runner_id = runner_id + obj.name = indName + obj.toml = ind_editor.getValue() + jsonString = JSON.stringify(obj); + //console.log("pred odeslanim",jsonString) + //cal rest api + $.ajax({ + url:"/archived_runners/"+runner_id+"/previewindicator", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"PUT", + contentType: "application/json", + data: jsonString, + success:function(data){ + //kod pro update/vytvoreni je zde stejny - updatujeme jen zdrojove dictionary + window.$('#indicatorModal').modal('hide'); + //console.log(data) + //indName = $('#indicatorName').val() + //updatneme/vytvorime klic v globalni promennou obsahujici vsechny arch data + //TBD nebude fungovat az budu mit vic chartů otevřených - předělat + archData.indicators[0][indName] = data + + //pridame pripadne upatneme v ext_data + + //smazeme zaznam o indiaktoru v lokalni kopii ext_data (z nich se pak prekresli indikatory) + if ((archData.ext_data !== null) && (archData.ext_data.instantindicators)) { + let index = archData.ext_data.instantindicators.findIndex(indicator => indicator.name === indName); + if (index !== -1) { + archData.ext_data.instantindicators.splice(index, 1); + } + archData.ext_data.instantindicators.push(obj) + } + //neexistuje instantindicators - vytvorime jej a vlozime prvni objekt + else if ((archData.ext_data !== null) && (!archData.ext_data.instantindicators)) { + //a pridame tamtez novy zaznam + archData.ext_data["instantindicators"] =[obj] + } + + //glob promenna obsahujici aktualne pridane indikatory a jejich konfigurace + //addedInds[indName] = obj.toml + //get active resolution + const element = document.querySelector('.switcher-active-item'); + resolution = element.textContent + //console.log("aktivni rozliseni", resolution) + //vykreslime a pridavame jeste nazev indikatoru, ktery se ma vykreslit do aktivnich + switch_to_interval(resolution, archData, indName) + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + //console.log(JSON.stringify(xhr)); + //$('#button_runagain_arch').attr('disabled',false); + } + }) + }); + +}); \ No newline at end of file diff --git a/v2realbot/static/js/utils.js b/v2realbot/static/js/utils.js index 18af133..606fea7 100644 --- a/v2realbot/static/js/utils.js +++ b/v2realbot/static/js/utils.js @@ -13,7 +13,6 @@ var candlestickSeries = null var volumeSeries = null var vwapSeries = null var statusBarConfig = JSON.parse(localStorage.getItem("statusBarConfig")); -var activatedButtons = [] if (statusBarConfig == null) { statusBarConfig = {} } @@ -58,14 +57,6 @@ var indConfig_default = [ {name: "ema", titlevisible: false, embed: true, displa {name: "sec_price", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},] //console.log(JSON.stringify(indConfig_default, null,null, 2)) -function store_activated_buttons_state() { - activatedButtons = [] - //ulozime si stav aktivovaných buttonků před změnou - mozna do sluzby - $('#indicatorsButtons .switcher-active-item').each(function() { - activatedButtons.push($(this).text()); - }); -} - function initialize_statusheader() { var rows = 2; @@ -351,7 +342,7 @@ function create_indicator_button(item, index, def) { itemEl.title = item.cnf itemEl.style.color = item.series.options().color; //pokud jde o pridanou on the fly - vybarvime jinak - if (item.added) { + if (item.instant) { itemEl.style.outline = "solid 1px" } itemEl.classList.add('switcher-item'); @@ -411,11 +402,35 @@ function onResetClicked() { visible: vis }); } }) + store_activated_buttons_state(); } function generateIndicators(e) { - alert("stratvars generated to clipboard from selected indicators") + store_activated_buttons_state(); + + ind_tom = "" + indList.forEach(function (item, index) { + if (activatedButtons.includes(item.name)) { + console.log(item) + ind_tom += "\n[stratvars.indicators."+item.name+"]\n" + item.cnf + } + }); + + if (ind_editor) { + ind_editor.dispose() + } + require(["vs/editor/editor.main"], () => { + ind_editor = monaco.editor.create(document.getElementById('indicatorTOML_editor'), { + value: ind_tom, + language: 'toml', + theme: 'tomlTheme-dark', + automaticLayout: true + }); + }); + $('#deleteIndicatorButton').hide(); + $('#saveIndicatorButton').hide(); + window.$('#indicatorModal').modal('show'); } //editace indikatoru, vcetne vytvoreni noveho @@ -509,6 +524,7 @@ function populate_indicator_buttons(def) { //console.log("activatedButtons", activatedButtons) //console.log("obsahuje item.name", activatedButtons.includes(item.name), item.name) //pokud existuje v aktivnich pak + //console.log("vytvarime button",item.name,activatedButtons) if ((activatedButtons) && (activatedButtons.includes(item.name))) { active = true } diff --git a/v2realbot/strategyblocks/indicators/RSI.py b/v2realbot/strategyblocks/indicators/RSI.py index 7b37efc..df992df 100644 --- a/v2realbot/strategyblocks/indicators/RSI.py +++ b/v2realbot/strategyblocks/indicators/RSI.py @@ -24,13 +24,19 @@ def populate_dynamic_RSI_indicator(data, state: StrategyState, name): req_source = safe_get(options, 'source', 'vwap') rsi_length = int(safe_get(options, "length",14)) rsi_MA_length = safe_get(options, "MA_length", None) + start = safe_get(options, "start","linear") #linear/sharp + if on_confirmed_only is False or (on_confirmed_only is True and data['confirmed']==1): try: #source = state.bars[req_source] source = get_source_series(state, req_source) #cekame na dostatek dat - if len(source) > rsi_length: + delka = len(source) + if delka > rsi_length or start == "linear": + if delka <= rsi_length and start == "linear": + rsi_length = delka + rsi_res = rsi(source, rsi_length) rsi_value = round(rsi_res[-1],4) state.indicators[name][-1]=rsi_value diff --git a/v2realbot/strategyblocks/indicators/custom/conditional.py b/v2realbot/strategyblocks/indicators/custom/conditional.py index 734d57e..4c022b0 100644 --- a/v2realbot/strategyblocks/indicators/custom/conditional.py +++ b/v2realbot/strategyblocks/indicators/custom/conditional.py @@ -54,5 +54,6 @@ def conditional(state, params): return 0, 0 except Exception as e: + printanyway(str(e)+format_exc()) return -2, str(e)+format_exc() diff --git a/v2realbot/strategyblocks/indicators/custom/expression.py b/v2realbot/strategyblocks/indicators/custom/expression.py index 52a2fd9..d1ec064 100644 --- a/v2realbot/strategyblocks/indicators/custom/expression.py +++ b/v2realbot/strategyblocks/indicators/custom/expression.py @@ -17,17 +17,16 @@ def expression(state: StrategyState, params): if operation is None : return -2, "required param missing" - state.ilog(lvl=1,e=f"BEFORE {funcName} {operation=}", **params) + state.ilog(lvl=0,e=f"BEFORE {funcName} {operation=}", **params) #pro zacatek eval - val = eval(operation, {'state': state}, state.ind_mapping) - + val = eval(operation, {'state': state, 'np': np}, state.ind_mapping) if not np.isfinite(val): val = 0 #val = ne.evaluate(operation, state.ind_mapping) - state.ilog(lvl=1,e=f"AFTER {funcName} {operation=} res:{val}", **params) + state.ilog(lvl=1,e=f"IND {funcName} {operation=} res:{val}", **params) return 0, val diff --git a/v2realbot/strategyblocks/indicators/custom/ma.py b/v2realbot/strategyblocks/indicators/custom/ma.py index 1489d13..faca166 100644 --- a/v2realbot/strategyblocks/indicators/custom/ma.py +++ b/v2realbot/strategyblocks/indicators/custom/ma.py @@ -16,6 +16,7 @@ def ma(state, params): type = safe_get(params, "type", "ema") source = safe_get(params, "source", None) lookback = safe_get(params, "lookback",14) + start = safe_get(params, "start","linear") #linear/sharp #lookback muze byt odkaz na indikator, pak berem jeho hodnotu lookback = int(value_or_indicator(state, lookback)) @@ -23,8 +24,11 @@ def ma(state, params): source_series = get_source_series(state, source) #pokud je mene elementu, pracujeme s tim co je - if len(source_series) > lookback: - source_series = source_series[-lookback:] + akt_pocet = len(source_series) + if akt_pocet < lookback and start == "linear": + lookback = akt_pocet + + #source_series = source_series[-lookback:] type = "mi."+type ma_function = eval(type) diff --git a/v2realbot/strategyblocks/inits/init_indicators.py b/v2realbot/strategyblocks/inits/init_indicators.py index 36e8f22..697b7ae 100644 --- a/v2realbot/strategyblocks/inits/init_indicators.py +++ b/v2realbot/strategyblocks/inits/init_indicators.py @@ -20,6 +20,7 @@ from traceback import format_exc def initialize_dynamic_indicators(state): #pro vsechny indikatory, ktere maji ve svych stratvars TYPE inicializujeme + ##ßprintanyway(state.vars, state) dict_copy = state.vars.indicators.copy() for indname, indsettings in dict_copy.items(): for option,value in list(indsettings.items()): diff --git a/v2realbot/utils/utils.py b/v2realbot/utils/utils.py index 875e272..a0a2dbd 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, SLHistory +from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from typing import List import tomli @@ -337,6 +337,7 @@ def json_serial(obj): RunArchiveDetail: lambda obj: obj.__dict__, Intervals: lambda obj: obj.__dict__, SLHistory: lambda obj: obj.__dict__, + InstantIndicator: lambda obj: obj.__dict__, } serializer = type_map.get(type(obj)) @@ -354,29 +355,9 @@ def json_serial_old(obj): if isinstance(obj, (datetime, date)): return obj.timestamp() - if isinstance(obj, UUID): + if isinstance(obj, UUID) or isinstance(obj, Enum) or isinstance(obj, np.int64): return str(obj) - if isinstance(obj, Enum): - return str(obj) - if isinstance(obj, np.int64): - return int(obj) - if type(obj) is Order: - return obj.__dict__ - if type(obj) is TradeUpdate: - return obj.__dict__ - if type(obj) is btOrder: - return obj.__dict__ - if type(obj) is btTradeUpdate: - return obj.__dict__ - if type(obj) is RunArchive: - return obj.__dict__ - if type(obj) is Trade: - return obj.__dict__ - if type(obj) is RunArchiveDetail: - return obj.__dict__ - if type(obj) is Intervals: - return obj.__dict__ - if type(obj) is SLHistory: + if type(obj) in [Order, TradeUpdate, btOrder, btTradeUpdate, RunArchive, Trade, RunArchiveDetail, Intervals, SLHistory, InstantIndicator]: return obj.__dict__ raise TypeError (str(obj)+"Type %s not serializable" % type(obj))