diff --git a/testy/tinyDBselect.py b/testy/tinyDBselect.py new file mode 100644 index 0000000..5a7aae1 --- /dev/null +++ b/testy/tinyDBselect.py @@ -0,0 +1,32 @@ + +from typing import Any, List +from uuid import UUID, uuid4 +import pickle +from alpaca.data.historical import StockHistoricalDataClient +from alpaca.data.requests import StockTradesRequest, StockBarsRequest +from alpaca.data.enums import DataFeed +from alpaca.data.timeframe import TimeFrame +from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account +from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange +from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial +from datetime import datetime +from threading import Thread, current_thread, Event, enumerate +from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR +import importlib +from queue import Queue +from tinydb import TinyDB, Query, where +from tinydb.operations import set +import json +from rich import print + +arch_header_file = DATA_DIR + "/arch_header.json" +arch_detail_file = DATA_DIR + "/arch_detail.json" +#db layer to store runner archive +db_arch_h = TinyDB(arch_header_file, default=json_serial) +db_arch_d = TinyDB(arch_detail_file, default=json_serial) + + +# res = db_arch_h.update(set('note', "ahoj"), where('id') == "74aa524e-3ed4-41fb-8166-f20946520344") +# print(res) +res = db_arch_d.all() +print(res) \ No newline at end of file diff --git a/v2realbot/__pycache__/config.cpython-310.pyc b/v2realbot/__pycache__/config.cpython-310.pyc index 63e3332..e3b2ecb 100644 Binary files a/v2realbot/__pycache__/config.cpython-310.pyc and b/v2realbot/__pycache__/config.cpython-310.pyc differ diff --git a/v2realbot/common/__pycache__/model.cpython-310.pyc b/v2realbot/common/__pycache__/model.cpython-310.pyc index 3759e12..9978461 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 21cbaaa..e6c038b 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -153,6 +153,11 @@ class TradeUpdate(BaseModel): cash: Optional[float] pos_avg_price: Optional[float] + +class RunArchiveChange(BaseModel): + id: UUID + note: str + #Contains archive of running strategies (runner) - master class RunArchive(BaseModel): #unique id of algorun diff --git a/v2realbot/config.py b/v2realbot/config.py index 376d9fa..a272a83 100644 --- a/v2realbot/config.py +++ b/v2realbot/config.py @@ -3,7 +3,7 @@ from v2realbot.enums.enums import Mode, Account, FillCondition from appdirs import user_data_dir #no print in console -QUIET_MODE = True +QUIET_MODE = False #how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting #0 - optimistic, every knot high will fill the order #N - N consecutive trades required diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index 59e1cd6..c00668b 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -6,7 +6,7 @@ from alpaca.data.requests import StockTradesRequest, StockBarsRequest from alpaca.data.enums import DataFeed from alpaca.data.timeframe import TimeFrame from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account -from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail +from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial from datetime import datetime from threading import Thread, current_thread, Event, enumerate @@ -37,10 +37,10 @@ def get_all_runners(): if len(db.runners) > 0: #print(db.runners) for i in db.runners: - i.run_profit = round(i.run_instance.state.profit,2) + i.run_profit = round(float(i.run_instance.state.profit),2) i.run_trade_count = len(i.run_instance.state.tradeList) i.run_positions = i.run_instance.state.positions - i.run_avgp = round(i.run_instance.state.avgp,3) + i.run_avgp = round(float(i.run_instance.state.avgp),3) return (0, db.runners) else: return (0, []) @@ -419,10 +419,10 @@ def archive_runner(runner: Runner, strat: StrategyInstance): bt_from=bp_from, bt_to = bp_to, stratvars = strat.state.vars, - profit=round(strat.state.profit,2), + profit=round(float(strat.state.profit),2), trade_count=len(strat.state.tradeList), end_positions=strat.state.positions, - end_positions_avgp=round(strat.state.avgp,3), + end_positions_avgp=round(float(strat.state.avgp),3), open_orders=9999 ) @@ -437,7 +437,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance): print("archive runner finished") return 0, str(resh) + " " + str(resd) except Exception as e: - print(str(e)) + print("Exception in archive_runner: " + str(e)) return -2, str(e) def get_all_archived_runners(): @@ -454,13 +454,23 @@ def delete_archived_runners_byID(id: UUID): return 0, str(resh) + " " + str(resd) except Exception as e: return -2, str(e) + +#edit archived runner note +def edit_archived_runners(runner_id: UUID, archChange: RunArchiveChange): + try: + res = db_arch_h.update(set('note', archChange.note), where('id') == str(runner_id)) + if len(res) == 0: + return -1, "not found "+str(runner_id) + return 0, runner_id + except Exception as e: + return -2, str(e) def get_all_archived_runners_detail(): res = db_arch_d.all() return 0, res def get_archived_runner_details_byID(id: UUID): - res = db_arch_d.get(where('id') == id) + res = db_arch_d.get(where('id') == str(id)) if res==None: return -2, "not found" else: diff --git a/v2realbot/main.py b/v2realbot/main.py index 12391e7..33e2221 100644 --- a/v2realbot/main.py +++ b/v2realbot/main.py @@ -13,7 +13,7 @@ from fastapi.security import APIKeyHeader import uvicorn from uuid import UUID import v2realbot.controller.services as cs -from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveDetail, Bar +from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveDetail, Bar, RunArchiveChange from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query from fastapi.responses import HTMLResponse, FileResponse from fastapi.staticfiles import StaticFiles @@ -282,6 +282,16 @@ def _delete_archived_runners_byID(runner_id): elif res < 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}") +#edit archived runner ("note",..) +@app.patch("/archived_runners/{runner_id}", dependencies=[Depends(api_key_auth)]) +def _edit_archived_runners(archChange: RunArchiveChange, runner_id: UUID): + res, id = cs.edit_archived_runners(runner_id=runner_id, archChange=archChange) + if res == 0: return runner_id + elif res == -1: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error not found: {res}:{runner_id}") + else: + raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error not changed: {res}:{runner_id}") + #get all archived runners detail @app.get("/archived_runners_detail/", dependencies=[Depends(api_key_auth)]) def _get_all_archived_runners_detail() -> list[RunArchiveDetail]: diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index 0fbd44d..7fbdd62 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -149,8 +149,9 @@
- + +
@@ -194,13 +195,44 @@ + +
@@ -319,7 +351,7 @@ @@ -341,7 +373,7 @@ @@ -375,11 +407,11 @@
- +
- +
@@ -394,7 +426,7 @@ - +
diff --git a/v2realbot/static/js/archivechart.js b/v2realbot/static/js/archivechart.js index 17150a5..95c8152 100644 --- a/v2realbot/static/js/archivechart.js +++ b/v2realbot/static/js/archivechart.js @@ -1,5 +1,6 @@ var tradeDetails = new Map(); var toolTip = null +var CHART_SHOW_TEXT = false //TRANSFORM object returned from RESTA PI 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]...} @@ -46,19 +47,23 @@ function transform_data(data) { a_markers = {} timestamp = Date.parse(trade.order.filled_at)/1000 if (trade.order.side == "buy") { - //line pro avgp markers - obj["time"] = timestamp; - obj["value"] = trade.pos_avg_price; - avgp_buy_line.push(obj) + //avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena + if (trade.pos_avg_price !== null) { + //line pro avgp markers + obj["time"] = timestamp; + obj["value"] = trade.pos_avg_price; + avgp_buy_line.push(obj) - //avgp markers pro prumernou cenu aktualnich pozic - a_markers["time"] = timestamp - a_markers["position"] = "aboveBar" - a_markers["color"] = "#e8c76d" - a_markers["shape"] = "arrowDown" -// a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3) - a_markers["text"] = trade.position_qty - avgp_markers.push(a_markers) + //avgp markers pro prumernou cenu aktualnich pozic + a_markers["time"] = timestamp + a_markers["position"] = "aboveBar" + a_markers["color"] = "#e8c76d" + a_markers["shape"] = "arrowDown" + if (CHART_SHOW_TEXT) + // a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3) + a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty + avgp_markers.push(a_markers) + } } //buy sell markery @@ -66,11 +71,11 @@ 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") ? "blue" : "red" + marker["color"] = (trade.order.side == "buy") ? "#cfcbc2" : "red" //marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown" marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown" - //marker["text"] = trade.qty + " " + trade.price - marker["text"] = trade.qty + //marker["text"] = trade.qty + "/" + trade.price + marker["text"] = CHART_SHOW_TEXT ? trade.qty + "/" + trade.price : trade.qty markers.push(marker) //prevedeme iso data na timestampy @@ -107,8 +112,16 @@ function transform_data(data) { function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunnerDetail) { req = {} req["symbol"] = archRunner.symbol - req["datetime_object_from"] = archRunner.bt_from - req["datetime_object_to"] = archRunner.bt_to + + if (archRunner.mode == "backtest") { + req["datetime_object_from"] = archRunner.bt_from + req["datetime_object_to"] = archRunner.bt_to + } + else + { + req["datetime_object_from"] = archRunner.started + req["datetime_object_to"] = archRunner.stopped + } req["timeframe_amount"] = timeframe_amount req["timeframe_unit"] = timeframe_unit $.ajax({ @@ -120,13 +133,13 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn contentType: "application/json", data: req, success:function(data){ - //console.log("bars", JSON.stringify(data)) + console.log("one minute bars", JSON.stringify(data)) data.map((el)=>{ cas = new Date(el.timestamp) el.time = cas.getTime()/1000; delete el.timestamp }); - //console.log("bars_after_transformation", JSON.stringify(data)) + console.log("one min bars_after_transformation", JSON.stringify(data)) oneMinuteBars = data chart_archived_run(archRunner, archivedRunnerDetail, oneMinuteBars); //call function to continue @@ -151,6 +164,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { toolTip.style.display = 'none'; } } + var transformed_data = transform_data(data) //initialize resolutions @@ -262,16 +276,20 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { }, }); + console.log("avgp_buy_line",transformed_data["avgp_buy_line"]) + console.log("avgp_markers",transformed_data["avgp_markers"]) - var avgBuyLine = chart.addLineSeries({ - // title: "avgpbuyline", - color: '#e8c76d', - // color: 'transparent', - lineWidth: 1, - lastValueVisible: false - }); - avgBuyLine.setData(transformed_data["avgp_buy_line"]); - avgBuyLine.setMarkers(transformed_data["avgp_markers"]) + if (transformed_data["avgp_buy_line"].length > 0) { + var avgBuyLine = chart.addLineSeries({ + // title: "avgpbuyline", + color: '#e8c76d', + // color: 'transparent', + lineWidth: 1, + lastValueVisible: false + }); + avgBuyLine.setData(transformed_data["avgp_buy_line"]); + avgBuyLine.setMarkers(transformed_data["avgp_markers"]) + } var markersLine = chart.addLineSeries({ // title: "avgpbuyline", diff --git a/v2realbot/static/js/archivetables.js b/v2realbot/static/js/archivetables.js index 0ca7900..308d8f5 100644 --- a/v2realbot/static/js/archivetables.js +++ b/v2realbot/static/js/archivetables.js @@ -5,6 +5,7 @@ $(document).ready(function () { //disable buttons (enable on row selection) $('#button_show_arch').attr('disabled','disabled'); $('#button_delete_arch').attr('disabled','disabled'); + $('#button_edit_arch').attr('disabled','disabled'); //selectable rows in archive table @@ -13,11 +14,13 @@ $(document).ready(function () { $(this).removeClass('selected'); $('#button_show_arch').attr('disabled','disabled'); $('#button_delete_arch').attr('disabled','disabled'); + $('#button_edit_arch').attr('disabled','disabled'); } else { stratinRecords.$('tr.selected').removeClass('selected'); $(this).addClass('selected'); $('#button_show_arch').attr('disabled',false); $('#button_delete_arch').attr('disabled',false); + $('#button_edit_arch').attr('disabled',false); } }); @@ -28,6 +31,13 @@ $(document).ready(function () { $('#delidarchive').val(row.id); }); + //edit button + $('#button_edit_arch').click(function () { + row = archiveRecords.row('.selected').data(); + window.$('#editModalArchive').modal('show'); + $('#editidarchive').val(row.id); + $('#editstratvars').val(JSON.stringify(row.stratvars,null,2)); + }); //show button $('#button_show_arch').click(function () { @@ -44,6 +54,8 @@ $(document).ready(function () { success:function(data){ $('#button_show_arch').attr('disabled',false); $('#chartContainerInner').addClass("show"); + $("#lines").html("
"+JSON.stringify(row.stratvars,null,2)+"
") + //$('#chartArchive').append(JSON.stringify(data,null,2)); console.log(JSON.stringify(data,null,2)); //if lower res is required call prepare_data otherwise call chart_archived_run() @@ -60,6 +72,43 @@ $(document).ready(function () { }); }) +//edit modal +$("#editModalArchive").on('submit','#editFormArchive', function(event){ + event.preventDefault(); + $('#editarchive').attr('disabled','disabled'); + trow = archiveRecords.row('.selected').data(); + note = $('#editnote').val() + var formData = $(this).serializeJSON(); + row = {} + row["id"] = trow.id + row["note"] = note + jsonString = JSON.stringify(row); + console.log("pred odeslanim json string", jsonString) + $.ajax({ + url:"/archived_runners/"+trow.id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"PATCH", + contentType: "application/json", + // dataType: "json", + data: jsonString, + success:function(data){ + $('#editFormArchive')[0].reset(); + window.$('#editModalArchive').modal('hide'); + $('#editarchive').attr('disabled', false); + archiveRecords.ajax.reload(); + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + $('#editarchive').attr('disabled', false); + } + }) +}); + + //delete modal $("#delModalArchive").on('submit','#delFormArchive', function(event){ event.preventDefault(); @@ -121,12 +170,12 @@ var archiveRecords = {data: 'end_positions_avgp', visible: true}, {data: 'open_orders', visible: true} ], - columnDefs: [{ - targets: [4,5,8,9], - render: function ( data, type, row ) { - return format_date(data) - }, - }], + // columnDefs: [{ + // targets: [4,5,8,9], + // render: function ( data, type, row ) { + // return format_date(data) + // }, + // }], order: [[5, 'desc']], paging: true, lengthChange: false, diff --git a/v2realbot/static/js/livewebsocket.js b/v2realbot/static/js/livewebsocket.js index 37d0999..d51f793 100644 --- a/v2realbot/static/js/livewebsocket.js +++ b/v2realbot/static/js/livewebsocket.js @@ -115,7 +115,7 @@ function connect(event) { logcnt++; - row = '
'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'
' + row = '
'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'
' str_row = JSON.stringify(logLine.details, null, 2) //row_detail = '
' + str_row + '
' diff --git a/v2realbot/static/js/realtimechart.js b/v2realbot/static/js/realtimechart.js index a23437f..1b342e5 100644 --- a/v2realbot/static/js/realtimechart.js +++ b/v2realbot/static/js/realtimechart.js @@ -8,26 +8,26 @@ function populate_real_time_chart() { $('#chartContainerInner').addClass("show"); //const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } }; - //var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}} - var chartOptions = { width: 1045, - height: 600, - leftPriceScale: {visible: true}, - layout: { - background: { - type: 'solid', - color: '#000000', - }, - textColor: '#d1d4dc', - }, - grid: { - vertLines: { - visible: false, - }, - horzLines: { - color: 'rgba(42, 46, 57, 0.5)', - }, - }, - } + var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}} + // var chartOptions = { width: 1045, + // height: 600, + // leftPriceScale: {visible: true}, + // layout: { + // background: { + // type: 'solid', + // color: '#000000', + // }, + // textColor: '#d1d4dc', + // }, + // grid: { + // vertLines: { + // visible: false, + // }, + // horzLines: { + // color: 'rgba(42, 46, 57, 0.5)', + // }, + // }, + // } chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions); diff --git a/v2realbot/static/main.css b/v2realbot/static/main.css index ed74c29..a0f7644 100644 --- a/v2realbot/static/main.css +++ b/v2realbot/static/main.css @@ -127,7 +127,7 @@ pre { font-size: normal; /* font-weight: bold; */ display: -webkit-inline-box; - background-color: #128faf; + background-color: #dfe4e6; /* color: white; */ padding: 1px; padding-left: 5px;