refaktor gui jdeme na live

This commit is contained in:
David Brazda
2023-05-01 19:11:17 +02:00
parent 902eee1d67
commit 3a1b1550fd
13 changed files with 229 additions and 73 deletions

32
testy/tinyDBselect.py Normal file
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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():
@ -455,12 +455,22 @@ def delete_archived_runners_byID(id: UUID):
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:

View File

@ -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]:

View File

@ -149,8 +149,9 @@
<div class="legend" id="legendArchive"></div>
</div>
<div id="controls">
<button id="button_show_arch" class="btn btn-outline-success btn-sm">Show</button>
<button id="button_edit_arch" class="btn btn-outline-success btn-sm">Edit</button>
<button id="button_delete_arch" class="btn btn-outline-success btn-sm">Delete</button>
<button id="button_show_arch" class="btn btn-outline-success btn-sm">Show</button>
<!-- <button id="button_stopall" class="btn btn-outline-success btn-sm">Stop All</button>
<button id="button_refresh" class="btn btn-outline-success btn-sm">Refresh</button> -->
</div>
@ -194,7 +195,38 @@
</div>
<div class="modal-footer">
<input type="submit" name="delete" id="deletearchive" class="btn btn-primary" value="Delete" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div id="editModalArchive" class="modal fade">
<div class="modal-dialog">
<form method="post" id="editFormArchive">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"><i class="fa fa-plus"></i> Edit Archive</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="editidarchive" class="form-label">Id</label>
<input type="text" class="form-control" id="editidarchive" name="id" placeholder="id" readonly>
</div>
<div class="form-group">
<label for="note" class="form-label">note</label>
<textarea class="form-control" rows="2" id="editnote" name="note"></textarea>
</div>
<div class="form-group">
<label for="stratvars" class="form-label">Stratvars</label>
<textarea class="form-control" rows="4" id="editstratvars" name="stratvars"></textarea>
</div>
</div>
<div class="modal-footer">
<input type="submit" name="delete" id="editarchive" class="btn btn-primary" value="Edit" />
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
@ -297,7 +329,7 @@
<!--<input type="hidden" name="id" id="id" />-->
<!--<input type="hidden" name="action" id="action" value="" />-->
<input type="submit" name="save" id="save" class="btn btn-primary" value="Save" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
@ -319,7 +351,7 @@
</div>
<div class="modal-footer">
<input type="submit" name="delete" id="delete" class="btn btn-primary" value="Delete" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
@ -341,7 +373,7 @@
</div>
<div class="modal-footer">
<input type="submit" name="json_add" id="json_add" class="btn btn-primary" value="Add" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
@ -375,11 +407,11 @@
</div>
<div class="form-group">
<label for="bt_from" class="form-label">bt_from</label>
<input type="datetime-local" class="form-control" id="bt_from" name="bt_from" placeholder="2023-04-06T09:00:00Z">
<input type="datetime-local" class="form-control" id="bt_from" name="bt_from" placeholder="2023-04-06T09:00:00Z" step="1">
</div>
<div class="form-group">
<label for="bt_to" class="form-label">bt_to</label>
<input type="datetime-local" class="form-control" id="bt_to" name="bt_to" placeholder="2023-04-06T09:00:00Z">
<input type="datetime-local" class="form-control" id="bt_to" name="bt_to" placeholder="2023-04-06T09:00:00Z" step="1">
</div>
<div class="form-group">
<label for="cash" class="form-label">cash</label>
@ -394,7 +426,7 @@
<!--<input type="hidden" name="id" id="id" />-->
<!--<input type="hidden" name="action" id="action" value="" />-->
<input type="submit" name="run" id="run" class="btn btn-primary" value="Run" />
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>

View File

@ -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,6 +47,8 @@ function transform_data(data) {
a_markers = {}
timestamp = Date.parse(trade.order.filled_at)/1000
if (trade.order.side == "buy") {
//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;
@ -56,21 +59,23 @@ function transform_data(data) {
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
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
marker = {}
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
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,7 +276,10 @@ 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"])
if (transformed_data["avgp_buy_line"].length > 0) {
var avgBuyLine = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e8c76d',
@ -272,6 +289,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
});
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
}
var markersLine = chart.addLineSeries({
// title: "avgpbuyline",

View File

@ -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("<pre>"+JSON.stringify(row.stratvars,null,2)+"</pre>")
//$('#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,

View File

@ -115,7 +115,7 @@ function connect(event) {
logcnt++;
row = '<div data-toggle="collapse" onclick="set_timestamp(' + logLine.time + ')" data-target="#rec'+logcnt+'">'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'</div>'
row = '<div data-bs-toggle="collapse" onclick="set_timestamp(' + logLine.time + ')" data-bs-target="#rec'+logcnt+'">'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'</div>'
str_row = JSON.stringify(logLine.details, null, 2)
//row_detail = '<div id="rec'+logcnt+'" data-toggle="collapse" data-target="#rec'+logcnt+'"class="collapse pidi"><pre>' + str_row + '</pre></div>'

View File

@ -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);

View File

@ -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;