diff --git a/testy/testDict.py b/testy/testDict.py new file mode 100644 index 0000000..b0eb27e --- /dev/null +++ b/testy/testDict.py @@ -0,0 +1,31 @@ +from v2realbot.utils.utils import AttributeDict +indicators = AttributeDict(time=[]) + +for key,value in indicators.items(): + if key != "time": + indicators[key].append(0) + + +#as indicators are stored vector based, time is populated for each iteration +def populate_indicator_time(self, item): + if self.rectype == RecordType.BAR: + #jako cas indikatorů pridavame cas baru, jejich hodnoty se naplni v nextu + self.state.indicators['time'].append(item['time']) + elif self.rectype == RecordType.TRADE: + pass + elif self.rectype == RecordType.CBAR: + #novy vzdy pridame + if self.nextnew: + self.state.indicators['time'].append(item['time']) + self.append_bar(self.state.bars,item) + self.nextnew = 0 + #nasledujici updatneme, po potvrzeni, nasleduje novy bar + else: + if item['confirmed'] == 0: + self.state.indicators['time'][-1]=item['time'] + self.replace_prev_bar(self.state.bars,item) + #confirmed + else: + self.state.indicators['time'][-1]=item['time'] + self.replace_prev_bar(self.state.bars,item) + self.nextnew = 1 \ No newline at end of file diff --git a/v2realbot/ENTRY_backtest_strategyVykladaci.py b/v2realbot/ENTRY_backtest_strategyVykladaci.py index 9dcaa02..e6cdefa 100644 --- a/v2realbot/ENTRY_backtest_strategyVykladaci.py +++ b/v2realbot/ENTRY_backtest_strategyVykladaci.py @@ -251,7 +251,7 @@ def next(data, state: StrategyState): #kvuli spravnemu zobrazovani na gui state.indicators.slope.append(0) state.indicators.slopeMA.append(0) - state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback) + state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback, slope=state.indicators.slope, slopeMA=state.indicators.slopeMA) except Exception as e: print("Exception in NEXT Indicator section", str(e)) diff --git a/v2realbot/__pycache__/config.cpython-310.pyc b/v2realbot/__pycache__/config.cpython-310.pyc index 86d14e6..a587fb8 100644 Binary files a/v2realbot/__pycache__/config.cpython-310.pyc and b/v2realbot/__pycache__/config.cpython-310.pyc differ diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index ed6d025..c046f14 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -543,8 +543,8 @@ def get_alpaca_history_bars(symbol: str, datetime_object_from: datetime, datetim #ohlcvList = {} #bars = {} - - return 0, bars.data[symbol] + #bars.data[symbol] + return 0, result except Exception as e: return -2, str(e) diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index fb6a51a..a45de82 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -12,6 +12,8 @@ + + @@ -38,7 +40,7 @@
Status: Not connected
@@ -75,7 +77,7 @@
@@ -90,7 +92,7 @@
@@ -147,7 +149,7 @@
@@ -456,7 +458,7 @@
config options diff --git a/v2realbot/static/js/archivechart.js b/v2realbot/static/js/archivechart.js index e5d2b5a..9fbc196 100644 --- a/v2realbot/static/js/archivechart.js +++ b/v2realbot/static/js/archivechart.js @@ -90,7 +90,7 @@ function transform_data(data) { marker["time"] = timestamp; // marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar" marker["position"] = (trade.order.side == "buy") ? "inBar" : "aboveBar" - marker["color"] = (trade.order.side == "buy") ? "#cfcbc2" : "red" + marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red" //marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown" marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown" //marker["text"] = trade.qty + "/" + trade.price @@ -177,6 +177,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { if (chart !== null) { chart.remove() clear_status_header() + indList = []; if (toolTip !== null) { toolTip.style.display = 'none'; } @@ -252,6 +253,8 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { intitialize_candles() candlestickSeries.setData(AllCandleSeriesesData.get(interval)); + display_buy_markers(); + if (last_range) { chart.timeScale().setVisibleRange(last_range); } @@ -433,150 +436,119 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { } } - //gets indicators from archived data and displays them on the chart + //displays (redraws) buy markers + function display_buy_markers() { + if (avgBuyLine !== null) { + chart.removeSeries(avgBuyLine) + } + + if (markersLine !== null) { + chart.removeSeries(markersLine) + } + + console.log("avgp_buy_line",transformed_data["avgp_buy_line"]) + console.log("avgp_markers",transformed_data["avgp_markers"]) + + if (transformed_data["avgp_buy_line"].length > 0) { + avgBuyLine = chart.addLineSeries({ + // title: "avgpbuyline", + color: '#e8c76d', + // color: 'transparent', + lineWidth: 1, + lastValueVisible: false + }); + + avgBuyLine.applyOptions({ + lastValueVisible: false, + priceLineVisible: false, + }); - console.log("avgp_buy_line",transformed_data["avgp_buy_line"]) - console.log("avgp_markers",transformed_data["avgp_markers"]) + try { + avgBuyLine.setData(transformed_data["avgp_buy_line"]); + } + catch (error) { + console.log("avgbuyline") + } + + avgBuyLine.setMarkers(transformed_data["avgp_markers"]) + } - if (transformed_data["avgp_buy_line"].length > 0) { - avgBuyLine = chart.addLineSeries({ - // title: "avgpbuyline", - color: '#e8c76d', - // color: 'transparent', + markersLine = chart.addLineSeries({ + // title: "avgpbuyline", + // color: '#d6d1c3', + color: 'transparent', lineWidth: 1, lastValueVisible: false }); - avgBuyLine.applyOptions({ - lastValueVisible: false, - priceLineVisible: false, - }); - - - try { - avgBuyLine.setData(transformed_data["avgp_buy_line"]); - } - catch (error) { - console.log("avgbuyline") - } - - avgBuyLine.setMarkers(transformed_data["avgp_markers"]) - } - - markersLine = chart.addLineSeries({ - // title: "avgpbuyline", - // color: '#d6d1c3', - color: 'transparent', - lineWidth: 1, - lastValueVisible: false - }); - - - - try { markersLine.setData(transformed_data["markers_line"]); - } - catch (error) { - console.log("markersLine") - } + markersLine.setMarkers(transformed_data["markers"]) + //chart.subscribeCrosshairMove(param => { + chart.subscribeCrosshairMove(param => { + //LEGEND SECTIOIN + firstRow.style.color = 'white'; + update_chart_legend(param); - markersLine.setMarkers(transformed_data["markers"]) - - - - //TBD dynamicky - //pokud je nazev atributu X_candles vytvorit candles - //pokud je objekt Y_line pak vytvorit lajnu - //pokud je objekt Z_markers pak vytvorit markers - //pokud je Z = X nebo Y, pak markers dat na danou lajnu (priklad vvwap_line, avgp_line, avgp_markers) - //udelat si nahodny vyber barev z listu - - //DO BUDOUCNA MARKERS - // chart.subscribeCrosshairMove(param => { - // console.log(param.hoveredObjectId); - // }); - - - - - //TODO onlick zkopirovat timestamp param.time - // chart.subscribeClick(param => { - // $('#trade-timestamp').val(param.time) - // //alert(JSON.safeStringify(param)) - // //console.log(param.hoveredObjectId); - // }); - - //TODO - // - legend - // - identifikatory - // - volume - - - //chart.subscribeCrosshairMove(param => { - chart.subscribeCrosshairMove(param => { - //LEGEND SECTIOIN - firstRow.style.color = 'white'; - update_chart_legend(param); - - //TOOLTIP SECTION - //$('#trade-timestamp').val(param.time) - if ( - param.point === undefined || - !param.time || - param.point.x < 0 || - param.point.x > container1.clientWidth || - param.point.y < 0 || - param.point.y > container1.clientHeight - ) { - toolTip.style.display = 'none'; - } else { - //vyber serie s jakou chci pracovat - muzu i dynamicky - //je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams - - //key = series (key.seriestype vraci Line/Candlestick atp.) https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap - - toolTip.style.display = 'none'; - toolTip.innerHTML = ""; - var data = param.seriesData.get(markersLine); - var data2 = param.seriesData.get(avgBuyLine); - if ((data !== undefined) || (data2 !== undefined)) { - //param.seriesData.forEach((value, key) => { - //console.log("key",key) - //console.log("value",value) - - //data = value - //DOCASNE VYPNUTO - toolTip.style.display = 'block'; + //TOOLTIP SECTION + //$('#trade-timestamp').val(param.time) + if ( + param.point === undefined || + !param.time || + param.point.x < 0 || + param.point.x > container1.clientWidth || + param.point.y < 0 || + param.point.y > container1.clientHeight + ) { + toolTip.style.display = 'none'; + } else { + //vyber serie s jakou chci pracovat - muzu i dynamicky + //je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams + + //key = series (key.seriestype vraci Line/Candlestick atp.) https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap + + toolTip.style.display = 'none'; + toolTip.innerHTML = ""; + var data = param.seriesData.get(markersLine); + var data2 = param.seriesData.get(avgBuyLine); + if ((data !== undefined) || (data2 !== undefined)) { + //param.seriesData.forEach((value, key) => { + //console.log("key",key) + //console.log("value",value) - //console.log(JSON.safeStringify(key)) - // if (toolTip.innerHTML == "") { - // toolTip.innerHTML = `
${param.time}
` - // } - buy_price = 0 - //u sell markeru nemame avgBuyLine - if (data2 !== undefined) { - buy_price = parseFloat(data2.value).toFixed(3) - } + //data = value + //DOCASNE VYPNUTO + toolTip.style.display = 'block'; + + //console.log(JSON.safeStringify(key)) + // if (toolTip.innerHTML == "") { + // toolTip.innerHTML = `
${param.time}
` + // } + buy_price = 0 + //u sell markeru nemame avgBuyLine + if (data2 !== undefined) { + buy_price = parseFloat(data2.value).toFixed(3) + } - toolTip.innerHTML += `
POS:${tradeDetails.get(param.time).position_qty}/${buy_price}
T:${tradeDetails.get(param.time).qty}/${data.value}
`; - - //inspirace - // toolTip.innerHTML = `
Apple Inc.
- // ${Math.round(100 * price) / 100} - //
- // ${dateStr} - //
`; - - - // Position tooltip according to mouse cursor position - toolTip.style.left = param.point.x+120 + 'px'; - toolTip.style.top = param.point.y + 'px'; + toolTip.innerHTML += `
POS:${tradeDetails.get(param.time).position_qty}/${buy_price}
T:${tradeDetails.get(param.time).qty}/${data.value}
`; + + //inspirace + // toolTip.innerHTML = `
Apple Inc.
+ // ${Math.round(100 * price) / 100} + //
+ // ${dateStr} + //
`; + + + // Position tooltip according to mouse cursor position + toolTip.style.left = param.point.x+120 + 'px'; + toolTip.style.top = param.point.y + 'px'; + } + //}); } - //}); - } - }); + }); + } chart.subscribeClick(param => { $('#trade-timestamp').val(param.time) @@ -638,7 +610,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { //add status - $("#statusRegime").text("ARCHIVED RUN") + $("#statusRegime").text("PAST RUN: "+archRecord.id) $("#statusName").text(archRecord.name) $("#statusMode").text(archRecord.mode) $("#statusAccount").text(archRecord.account) diff --git a/v2realbot/static/js/archivetables.js b/v2realbot/static/js/archivetables.js index 5ac88d4..9d797e9 100644 --- a/v2realbot/static/js/archivetables.js +++ b/v2realbot/static/js/archivetables.js @@ -175,7 +175,7 @@ var archiveRecords = ], paging: false, processing: false, - columnDefs: [ { + columnDefs: [{ targets: [0,1], render: function ( data, type, row ) { return '
'+data+'' @@ -198,13 +198,62 @@ var archiveRecords = }, }, - { + { targets: [9,10], render: function ( data, type, row ) { //market datetime return format_date(data, true) }, }, + { + targets: [2], + render: function ( data, type, row ) { + return '
'+data+'' + }, + }, + { + targets: [4], + render: function ( data, type, row ) { + return '
'+data+'' + }, + }, + { + targets: [11], + render: function ( data, type, row ) { + //if ilog_save true + if (data) { + return 'done_outline' + } + else { + return null + } + }, + }, + { + targets: [8], + render: function ( data, type, row ) { + //if ilog_save true + if (data == "ACCOUNT1") { + res="ACC1" + } + else if (data == "ACCOUNT2") { + res="ACC2" + } + else { res=data} + return res + }, + }, + { + targets: [7], + render: function ( data, type, row ) { + //if ilog_save true + if (data == "backtest") { + res="bt" + } + else { res=data} + return res + }, + } ], order: [[6, 'desc']], // paging: true, @@ -216,4 +265,7 @@ var archiveRecords = // $(row).addClass('highlight'); // } //} - } ); \ No newline at end of file + } ); + + + \ No newline at end of file diff --git a/v2realbot/static/js/mytables.js b/v2realbot/static/js/mytables.js index 45f009d..6d24d63 100644 --- a/v2realbot/static/js/mytables.js +++ b/v2realbot/static/js/mytables.js @@ -6,19 +6,19 @@ function store_api_key(event) { } function get_status(id) { - var status = "stopped" + var status = "" runnerRecords.rows().iterator('row', function ( context, index ) { var data = this.row(index).data(); //window.alert(JSON.stringify(data)) if (data.strat_id == id) { //window.alert("found"); - if ((data.run_mode) == "backtest") { status_detail = data.run_mode} + if ((data.run_mode) == "backtest") { status_detail = ''+data.run_mode+''} else { status_detail = data.run_mode + " | " + data.run_account} if (data.run_paused == null) { - status = "running | "+ status_detail + status = 'play_circle'+status_detail } else { - status = "paused | "+ status_detail + status = 'pause_circle'+ status_detail }} //window.alert("found") } }); @@ -423,7 +423,7 @@ var stratinRecords = targets: 12, render: function ( data, type, row ) { var status = get_status(data) - return ''+status+'' + return status }, }, { diff --git a/v2realbot/static/js/utils.js b/v2realbot/static/js/utils.js index 90c5ae2..4133e86 100644 --- a/v2realbot/static/js/utils.js +++ b/v2realbot/static/js/utils.js @@ -32,11 +32,13 @@ legendlist.appendChild(firstRow); function update_chart_legend(param) { - function name(val) { - return '
' + val + '' + function name(val, color = null) { + color = (color)?' style="color: '+ color + ';"' : ""; + return '
' + val + '' } - function val(val) { - return '
' + val + '' + function val(val, color = null) { + color = (color)?' style="color: '+ color + ';"' : ""; + return '
' + val + '' } if (param.time) { @@ -44,24 +46,29 @@ function update_chart_legend(param) { //BASIC INDICATORS const bars = param.seriesData.get(candlestickSeries); if (bars !== undefined) { - firstRow.innerHTML += name("O") + val(bars.open) + name("H") + val(bars.high) + name("L") + val(bars.low) + name("C") + val(bars.close) + //console.log(JSON.stringify(candlestickSeries.options())) + var color = candlestickSeries.options().upColor; + firstRow.innerHTML += name("O", color) + val(bars.open) + name("H", color) + val(bars.high) + name("L", color) + val(bars.low) + name("C") + val(bars.close) } const volumes = param.seriesData.get(volumeSeries); if (volumes !== undefined) { - firstRow.innerHTML += name("Vol") +val(volumes.value) + var color = volumeSeries.options().color; + firstRow.innerHTML += name("Vol", color) +val(volumes.value) } const data = param.seriesData.get(vwapSeries); if (data !== undefined) { + var color = vwapSeries.options().color; const vwap = data.value !== undefined ? data.value : data.close; - firstRow.innerHTML += name('vwap') + val(vwap.toFixed(2)) + firstRow.innerHTML += name('vwap', color) + val(vwap.toFixed(2)) } //ADDITIONAL CUSTOM INDICATORS //iterate of custom indicators dictionary to get values of custom lines // var customIndicator = {name: key, series: null} indList.forEach(function (item) { var ind = param.seriesData.get(item.series) - if (ind !== undefined) { firstRow.innerHTML += name(item.name) + val(ind.value.toFixed(3))} + var color = item.series.options().color; + if (ind !== undefined) { firstRow.innerHTML += name(item.name, color) + val(ind.value.toFixed(3), color)} }); } else { diff --git a/v2realbot/static/main.css b/v2realbot/static/main.css index 29e703a..09e28d7 100644 --- a/v2realbot/static/main.css +++ b/v2realbot/static/main.css @@ -1,17 +1,19 @@ /* :root { --dt-row-selected: 18, 143, 175; + --dt-row-selected: 140,142,141; } */ :root { - --dt-row-selected: 140,142,141; + --dt-row-selected: 56,95,126; + --dt-row-selected-text: 173,181,189; } [data-bs-theme=dark] { color-scheme: dark; - --bs-body-color: #adb5bd; + --bs-body-color: #787b86; --bs-body-color-rgb: 173,181,189; --bs-body-bg: #2a2e39; --bs-body-bg-rgb: 33,37,41; - --bs-emphasis-color: #fff; + --bs-emphasis-color: #adb5bd; --bs-emphasis-color-rgb: 255,255,255; --bs-secondary-color: rgba(173, 181, 189, 0.75); --bs-secondary-color-rgb: 173,181,189; @@ -58,6 +60,23 @@ --bs-form-invalid-border-color: #ea868f; } +.form-label { + margin-top: 0.5em; + color: var(--bs-emphasis-color); +} +.form-check { + display: block; + min-height: 2.5rem; + padding-left: 1.5em; + margin-left: 0.25rem; +} + +.h4, h4 { + font-size: 1.4rem; + color: var(--bs-emphasis-color); + font-weight: 200; +} + tbody, td, tfoot, th, thead, tr { border-color: #7d7d8a; border-style: solid; @@ -77,6 +96,32 @@ table.dataTable thead th, table.dataTable thead td, table.dataTable tfoot th, ta color: #787b86; } +table.dataTable thead>tr>th.sorting, table.dataTable thead>tr>th.sorting_asc, table.dataTable thead>tr>th.sorting_desc, table.dataTable thead>tr>th.sorting_asc_disabled, table.dataTable thead>tr>th.sorting_desc_disabled, table.dataTable thead>tr>td.sorting, table.dataTable thead>tr>td.sorting_asc, table.dataTable thead>tr>td.sorting_desc, table.dataTable thead>tr>td.sorting_asc_disabled, table.dataTable thead>tr>td.sorting_desc_disabled { + cursor: pointer; + position: relative; + padding-right: 10px; + font-size: xx-small; +} + +table.dataTable thead>tr>th.sorting:before, table.dataTable thead>tr>th.sorting:after, table.dataTable thead>tr>th.sorting_asc:before, table.dataTable thead>tr>th.sorting_asc:after, table.dataTable thead>tr>th.sorting_desc:before, table.dataTable thead>tr>th.sorting_desc:after, table.dataTable thead>tr>th.sorting_asc_disabled:before, table.dataTable thead>tr>th.sorting_asc_disabled:after, table.dataTable thead>tr>th.sorting_desc_disabled:before, table.dataTable thead>tr>th.sorting_desc_disabled:after, table.dataTable thead>tr>td.sorting:before, table.dataTable thead>tr>td.sorting:after, table.dataTable thead>tr>td.sorting_asc:before, table.dataTable thead>tr>td.sorting_asc:after, table.dataTable thead>tr>td.sorting_desc:before, table.dataTable thead>tr>td.sorting_desc:after, table.dataTable thead>tr>td.sorting_asc_disabled:before, table.dataTable thead>tr>td.sorting_asc_disabled:after, table.dataTable thead>tr>td.sorting_desc_disabled:before, table.dataTable thead>tr>td.sorting_desc_disabled:after { + position: absolute; + display: block; + opacity: 0.525; + right: 4px; + line-height: 9px; + font-size: 0.8em; +} +table.dataTable thead>tr>th.sorting_asc:before, table.dataTable thead>tr>th.sorting_desc:after, table.dataTable thead>tr>td.sorting_asc:before, table.dataTable thead>tr>td.sorting_desc:after { + opacity: 1; +} +.tdnote { + width: 188px; +} + +.tdname { + width: 140px; +} + .secondary-bg { --bs-bg-opacity: 1; background-color: #414749; @@ -101,6 +146,8 @@ table.dataTable thead th, table.dataTable thead td, table.dataTable tfoot th, ta --bs-gradient: none; } + + /* .btn-outline-success { --bs-btn-color: #316164; --bs-btn-border-color: #247e85; @@ -177,7 +224,7 @@ html { z-index: 1; font-size: 12px; line-height: 18px; - font-weight: 300; + font-weight: 400; } .legendItemName { @@ -260,7 +307,7 @@ pre { } .highlighted { - font-weight: bold; + color: var(--bs-emphasis-color); } .switcher { @@ -279,7 +326,7 @@ pre { padding: 6px 8px; font-size: 14px; color: #262b3e; - background-color: transparent; + background-color: #818581; margin-right: 8px; border: none; border-radius: 4px; @@ -299,4 +346,12 @@ pre { .switcher-active-item, .switcher-active-item:hover { background-color: #e1eff9; +} + +.material-symbols-outlined { + font-variation-settings: + 'FILL' 0, + 'wght' 300, + 'GRAD' 0, + 'opsz' 24 } \ No newline at end of file diff --git a/v2realbot/strategy/__pycache__/base.cpython-310.pyc b/v2realbot/strategy/__pycache__/base.cpython-310.pyc index afd922c..498edd6 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 bbd7bc6..fefe928 100644 --- a/v2realbot/strategy/base.py +++ b/v2realbot/strategy/base.py @@ -224,7 +224,15 @@ class Strategy: self.before_iteration() ted = datetime.fromtimestamp(self.state.time).astimezone(zoneNY) if is_open_rush(ted, self.open_rush) or is_close_rush(ted, self.close_rush): - print("Rush hour - skipping") + #self.state.ilog(e="Rush hour - skipping") + #identifikatory jsou ulozeny vektorove, tzn. kdyz nejdeme dovnitr iterace(tak nepotrebujeme prazdny cas pro tuto iteraci) + #hodnoty time a identifikatoru musi byt stejne + #TBD pripdane predelat a dodelat pro CBARy az je budu pouzivat + if self.rectype == RecordType.BAR: + self.state.indicators['time'].pop() + elif self.rectype == RecordType.CBAR: + print("RUSH skipping NOT IMPLEMENTED for CBARs yet") + else: self.next(item, self.state) self.after_iteration(item)