diff --git a/v2realbot/common/model.py b/v2realbot/common/model.py index 67a60a2..5cc289c 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -52,6 +52,14 @@ class DataTablesRequest(BaseModel): # return user.id # raise HTTPException(status_code=404, detail=f"Could not find user with id: {id}") + +#obecny vstup pro analyzera (vstupem muze byt bud batch_id nebo seznam runneru) +class AnalyzerInputs(BaseModel): + batch_id: Optional[str] = None + runner_ids: Optional[List[UUID]] = None + #additional parameter + params: Optional[dict] = {} + class RunDay(BaseModel): """ Helper object for batch run - carries list of days in format required by run batch manager diff --git a/v2realbot/main.py b/v2realbot/main.py index be7f850..7c8035a 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, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest +from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query from fastapi.responses import FileResponse, StreamingResponse from fastapi.staticfiles import StaticFiles @@ -590,10 +590,14 @@ def _generate_report_image(runner_ids: list[UUID]): #TODO toto bude zaklad pro obecnou funkci, ktera bude volat ruzne analyzy #vstupem bude obecny objekt, ktery ponese nazev analyzy + atributy -@app.post("/batches/optimizecutoff/{batch_id}", dependencies=[Depends(api_key_auth)], responses={200: {"content": {"image/png": {}}}}) -def _generate_analysis(batch_id: str): +@app.post("/batches/optimizecutoff", dependencies=[Depends(api_key_auth)], responses={200: {"content": {"image/png": {}}}}) +def _generate_analysis(analyzerInputs: AnalyzerInputs): try: - res, stream = find_optimal_cutoff(batch_id=batch_id, steps=50, stream=True) + if len(analyzerInputs.runner_ids) == 0 and analyzerInputs.batch_id is None: + raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error: batch_id or runner_ids required") + + #bude predelano na obecny analyzator s obecnym rozhrannim + res, stream = find_optimal_cutoff(runner_ids=analyzerInputs.runner_ids, batch_id=analyzerInputs.batch_id, stream=True, **analyzerInputs.params) if res == 0: return StreamingResponse(stream, media_type="image/png",headers={"Content-Disposition": "attachment; filename=optimizedcutoff.png"}) elif res < 0: raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error: {res}:{id}") diff --git a/v2realbot/reporting/optimizecutoffs.py b/v2realbot/reporting/optimizecutoffs.py index 42740bf..ed997d1 100644 --- a/v2realbot/reporting/optimizecutoffs.py +++ b/v2realbot/reporting/optimizecutoffs.py @@ -10,6 +10,7 @@ from enum import Enum import numpy as np import v2realbot.controller.services as cs from rich import print +from v2realbot.common.model import AnalyzerInputs from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print from pathlib import Path @@ -211,7 +212,7 @@ def find_optimal_cutoff(runner_ids: list = None, batch_id: str = None, stream: b plt.yticks(rotation=0) # Keep y-axis labels horizontal plt.gca().invert_yaxis() plt.gca().invert_xaxis() - plt.suptitle("Total Profit for Combinations of Profit and Loss Cutoffs", fontsize=16) + plt.suptitle(f"Total Profit for Combinations of Profit/Loss Cutoffs ({cnt_max})", fontsize=16) plt.title(f"Optimal Profit Cutoff: {optimal_profit_cutoff:.2f}, Optimal Loss Cutoff: {optimal_loss_cutoff:.2f}, Max Profit: {max_profit:.2f}", fontsize=10) plt.xlabel("Loss Cutoff") plt.ylabel("Profit Cutoff") @@ -235,6 +236,7 @@ if __name__ == '__main__': # id_list = ["e8938b2e-8462-441a-8a82-d823c6a025cb"] # generate_trading_report_image(runner_ids=id_list) batch_id = "c76b4414" + vstup = AnalyzerInputs(**params) res, val = find_optimal_cutoff(batch_id=batch_id, file="optimal_cutoff_vectorized.png",steps=20) #res, val = find_optimal_cutoff(batch_id=batch_id, rem_outliers=True, file="optimal_cutoff_vectorized_nooutliers.png") diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index 5918bda..d9446f8 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -309,18 +309,18 @@
-->{\n${generateHTML(record2, delta)}}\n`;
- // document.getElementById('second').innerHTML = htmlMarkup2;
-
- // const htmlMarkup1 = `{\n${generateHTML(record1, delta)}}\n`;
- // document.getElementById('first').innerHTML = htmlMarkup1;
-
- event.preventDefault();
- //$('#button_compare').attr('disabled','disabled');
- }
- });
-
- //generate batch optimization cutoff (predelat na button pro obecne analyzy batche)
- $('#button_analyze').click(function () {
- row = archiveRecords.row('.selected').data();
- if (row == undefined || row.batch_id == undefined) {
- return
- }
- console.log(row)
- $('#button_analyze').attr('disabled','disabled');
- $.ajax({
- url:"/batches/optimizecutoff/"+row.batch_id,
- beforeSend: function (xhr) {
- xhr.setRequestHeader('X-API-Key',
- API_KEY); },
- method:"POST",
- xhrFields: {
- responseType: 'blob'
- },
- contentType: "application/json",
- processData: false,
- data: JSON.stringify(row.batch_id),
- success:function(blob){
- var url = window.URL || window.webkitURL;
- console.log("vraceny obraz", blob)
- console.log("url",url.createObjectURL(blob))
- display_image(url.createObjectURL(blob))
- $('#button_analyze').attr('disabled',false);
- },
- error: function(xhr, status, error) {
- console.log("proc to skace do erroru?")
- //window.alert(JSON.stringify(xhr));
- console.log(JSON.stringify(xhr));
- $('#button_analyze').attr('disabled',false);
- }
- })
- });
-
-
- //generate report button
- $('#button_report').click(function () {
- rows = archiveRecords.rows('.selected');
- if (rows == undefined) {
- return
- }
- $('#button_report').attr('disabled','disabled');
- runnerIds = []
- if(rows.data().length > 0 ) {
- // Loop through the selected rows and display an alert with each row's ID
- rows.every(function (rowIdx, tableLoop, rowLoop ) {
- var data = this.data()
- runnerIds.push(data.id);
- });
- }
- $.ajax({
- url:"/archived_runners/generatereportimage",
- beforeSend: function (xhr) {
- xhr.setRequestHeader('X-API-Key',
- API_KEY); },
- method:"POST",
- xhrFields: {
- responseType: 'blob'
- },
- contentType: "application/json",
- processData: false,
- data: JSON.stringify(runnerIds),
- success:function(blob){
- var url = window.URL || window.webkitURL;
- console.log("vraceny obraz", blob)
- console.log("url",url.createObjectURL(blob))
- display_image(url.createObjectURL(blob))
- $('#button_report').attr('disabled',false);
- },
- error: function(xhr, status, error) {
- console.log("proc to skace do erroru?")
- //window.alert(JSON.stringify(xhr));
- console.log(JSON.stringify(xhr));
- $('#button_report').attr('disabled',false);
- }
- })
- });
-
-
- function refresh_logfile() {
- $.ajax({
- url:"/log?lines=30",
- beforeSend: function (xhr) {
- xhr.setRequestHeader('X-API-Key',
- API_KEY); },
- method:"GET",
- contentType: "application/json",
- dataType: "json",
- success:function(response){
- if (response.lines.length == 0) {
- $('#log-content').html("no records");
- }
- else {
- $('#log-content').html(response.lines.join('\n'));
- }
- },
- error: function(xhr, status, error) {
- var err = eval("(" + xhr.responseText + ")");
- window.alert(JSON.stringify(xhr));
- }
- })
- }
-
- //button to query log
- $('#logRefreshButton').click(function () {
- refresh_logfile()
- });
-
- //button to open log modal
- $('#button_show_log').click(function () {
- window.$('#logModal').modal('show');
- refresh_logfile()
- });
-
- //delete batch button
- $('#button_delete_batch').click(function () {
- row = archiveRecords.row('.selected').data();
- if (row == undefined || row.batch_id == undefined) {
- return
- }
- $('#batch_id_del').val(row.batch_id);
-
- rows = archiveRecords.rows('.selected');
- if (rows == undefined) {
- return
- }
- $('#listofids').html("");
- window.$('#delModalBatch').modal('show');
- });
-
- //delete batch modal
- $("#delModalBatch").on('submit','#delFormBatch', function(event){
- event.preventDefault();
- batch_id = $('#batch_id_del').val();
- $('#deletebatch').attr('disabled', 'disabled');
- $.ajax({
- url:"/archived_runners/batch/"+batch_id,
- beforeSend: function (xhr) {
- xhr.setRequestHeader('X-API-Key',
- API_KEY); },
- method:"DELETE",
- contentType: "application/json",
- dataType: "json",
- data: JSON.stringify(batch_id),
- success:function(data){
- $('#delFormBatch')[0].reset();
- window.$('#delModalBatch').modal('hide');
- $('#deletebatch').attr('disabled', false);
- $('#button_delete_batch').attr('disabled', false);
- //console.log(data)
- archiveRecords.ajax.reload();
- },
- error: function(xhr, status, error) {
- var err = eval("(" + xhr.responseText + ")");
- window.alert(JSON.stringify(xhr));
- console.log(JSON.stringify(xhr));
- $('#deletebatch').attr('disabled', false);
- $('#button_delete_batch').attr('disabled', false);
- archiveRecords.ajax.reload();
- }
- })
- });
-
- //delete arch button
- $('#button_delete_arch').click(function () {
- rows = archiveRecords.rows('.selected');
- if (rows == undefined) {
- return
- }
- $('#listofids').html("");
-
- if(rows.data().length > 0 ) {
- ids_to_del = ""
- // Loop through the selected rows and display an alert with each row's ID
- rows.every(function (rowIdx, tableLoop, rowLoop ) {
- var data = this.data()
- ids_to_del = ids_to_del + data.id + ""+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() - //get other base resolutions - prepare_data(row, 1, "Min", data) - }, - error: function(xhr, status, error) { - var err = eval("(" + xhr.responseText + ")"); - window.alert(JSON.stringify(xhr)); - //console.log(JSON.stringify(xhr)); - $('#button_show_arch').attr('disabled',false); - } - }) - } - }); -}) - - //run again button - $('#button_runagain_arch').click(function () { - row = archiveRecords.row('.selected').data(); - $('#button_runagain_arch').attr('disabled',true); - - var record1 = new Object() - //console.log(JSON.stringify(rows)) - - //record1 = JSON.parse(rows[0].strat_json) - //record1.json = rows[0].json - - //TBD mozna zkopirovat jen urcite? - - //getting required data (detail of the archived runner + stratin to be run) - var request1 = $.ajax({ - url: "/archived_runners/"+row.id, - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"GET", - contentType: "application/json", - dataType: "json", - success:function(data){ - //console.log("fetched data ok") - //console.log(JSON.stringify(data,null,2)); - }, - error: function(xhr, status, error) { - var err = eval("(" + xhr.responseText + ")"); - window.alert(JSON.stringify(xhr)); - console.log(JSON.stringify(xhr)); - } - }); - - //nalaodovat data pro strategii - var request2 = $.ajax({ - url: "/stratins/"+row.strat_id, - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"GET", - contentType: "application/json", - dataType: "json", - success:function(data){ - //console.log("fetched data ok") - //console.log(JSON.stringify(data,null,2)); - }, - error: function(xhr, status, error) { - var err = eval("(" + xhr.responseText + ")"); - window.alert(JSON.stringify(xhr)); - console.log(JSON.stringify(xhr)); - } - }); - - - // Handling the responses of both requests - $.when(request1, request2).then(function(response1, response2) { - // Both requests have completed successfully - var result1 = response1[0]; - var result2 = response2[0]; - - //console.log("Result from first request:", result1); - //console.log("Result from second request:", result2); - - //console.log("calling compare") - rerun_strategy(result1, result2) - // Perform your action with the results from both requests - // Example: - - }, function(error1, error2) { - // Handle errors from either request here - // Example: - console.error("Error from first request:", error1); - console.error("Error from second request:", error2); - }); - - - function rerun_strategy(archRunner, stratData) { - record1 = archRunner - //console.log(record1) - - //smazeneme nepotrebne a pridame potrebne - //do budoucna predelat na vytvoreni noveho objektu - //nebudeme muset odstanovat pri kazdem pridani noveho atributu v budoucnu - delete record1["end_positions"]; - delete record1["end_positions_avgp"]; - delete record1["profit"]; - delete record1["trade_count"]; - delete record1["stratvars_toml"]; - delete record1["started"]; - delete record1["stopped"]; - delete record1["metrics"]; - delete record1["settings"]; - delete record1["stratvars"]; - - record1.note = "RERUN " + record1.note - - if (record1.bt_from == "") {delete record1["bt_from"];} - if (record1.bt_to == "") {delete record1["bt_to"];} - - //mazeme, pouze rerunujeme single - delete record1["test_batch_id"]; - delete record1["batch_id"]; - - const rec = new Object() - rec.id2 = parseInt(stratData.id2); - rec.name = stratData.name; - rec.symbol = stratData.symbol; - rec.class_name = stratData.class_name; - rec.script = stratData.script; - rec.open_rush = stratData.open_rush; - rec.close_rush = stratData.close_rush; - rec.stratvars_conf = stratData.stratvars_conf; - rec.add_data_conf = stratData.add_data_conf; - rec.note = stratData.note; - rec.history = ""; - strat_json = JSON.stringify(rec, null, 2); - record1.strat_json = strat_json - - //zkopirujeme strat_id do id a smazeme strat_id - record1.id = record1.strat_id - delete record1["strat_id"]; - - //console.log("record1 pred odeslanim", record1) - jsonString = JSON.stringify(record1); - - $.ajax({ - url:"/stratins/"+record1.id+"/run", - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"PUT", - contentType: "application/json", - data: jsonString, - success:function(data){ - $('#button_runagain_arch').attr('disabled',false); - setTimeout(function () { - runnerRecords.ajax.reload(); - stratinRecords.ajax.reload(); - }, 1500); - }, - 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); - } - }) - } - -}) - -//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); - } - }) -}); - -function delete_arch_rows(ids) { - $.ajax({ - url:"/archived_runners/", - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"DELETE", - contentType: "application/json", - dataType: "json", - data: JSON.stringify(ids), - success:function(data){ - $('#delFormArchive')[0].reset(); - window.$('#delModalArchive').modal('hide'); - $('#deletearchive').attr('disabled', false); - //console.log(data) - archiveRecords.ajax.reload(); - }, - error: function(xhr, status, error) { - var err = eval("(" + xhr.responseText + ")"); - window.alert(JSON.stringify(xhr)); - console.log(JSON.stringify(xhr)); - $('#deletearchive').attr('disabled', false); - archiveRecords.ajax.reload(); - } - }) -} - -//delete modal -$("#delModalArchive").on('submit','#delFormArchive', function(event){ - event.preventDefault(); - $('#deletearchive').attr('disabled','disabled'); - //rows = archiveRecords.rows('.selected'); - if(rows.data().length > 0 ) { - runnerIds = [] - // Loop through the selected rows and display an alert with each row's ID - rows.every(function (rowIdx, tableLoop, rowLoop ) { - var data = this.data() - runnerIds.push(data.id); - }); - delete_arch_rows(runnerIds) - } -}); - -//archive table -var archiveRecords = - $('#archiveTable').DataTable( { - ajax: { - url: '/archived_runners_p/', - dataSrc: 'data', - method:"POST", - contentType: "application/json", - // dataType: "json", - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - data: function (d) { - return JSON.stringify(d); - }, - error: function(xhr, status, error) { - //var err = eval("(" + xhr.responseText + ")"); - //window.alert(JSON.stringify(xhr)); - console.log(JSON.stringify(xhr)); - } - }, - columns: [{ data: 'id' }, - {data: 'strat_id'}, - {data: 'name'}, - {data: 'symbol'}, - {data: 'note'}, - {data: 'started'}, - {data: 'stopped'}, - {data: 'mode'}, - {data: 'account', visible: true}, - {data: 'bt_from', visible: true}, - {data: 'bt_to', visible: true}, - {data: 'ilog_save', visible: true}, - {data: 'profit'}, - {data: 'trade_count', visible: true}, - {data: 'end_positions', visible: true}, - {data: 'end_positions_avgp', visible: true}, - {data: 'metrics', visible: true}, - {data: 'batch_id', visible: true}, - ], - paging: true, - processing: true, - serverSide: true, - columnDefs: [ - { - targets: 1, - render: function ( data, type, row ) { - if (type === 'display') { - //console.log("arch") - var color = getColorForId(data); - return '
"+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() + //get other base resolutions + prepare_data(row, 1, "Min", data) + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + //console.log(JSON.stringify(xhr)); + $('#button_show_arch').attr('disabled',false); + } + }) +} + +//rerun stratin +function run_day_again() { + row = archiveRecords.row('.selected').data(); + $('#button_runagain_arch').attr('disabled',true); + + var record1 = new Object() + //console.log(JSON.stringify(rows)) + + //record1 = JSON.parse(rows[0].strat_json) + //record1.json = rows[0].json + + //TBD mozna zkopirovat jen urcite? + + //getting required data (detail of the archived runner + stratin to be run) + var request1 = $.ajax({ + url: "/archived_runners/"+row.id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + success:function(data){ + //console.log("fetched data ok") + //console.log(JSON.stringify(data,null,2)); + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + } + }); + + //nalaodovat data pro strategii + var request2 = $.ajax({ + url: "/stratins/"+row.strat_id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + success:function(data){ + //console.log("fetched data ok") + //console.log(JSON.stringify(data,null,2)); + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + } + }); + + + // Handling the responses of both requests + $.when(request1, request2).then(function(response1, response2) { + // Both requests have completed successfully + var result1 = response1[0]; + var result2 = response2[0]; + + //console.log("Result from first request:", result1); + //console.log("Result from second request:", result2); + + //console.log("calling compare") + rerun_strategy(result1, result2) + // Perform your action with the results from both requests + // Example: + + }, function(error1, error2) { + // Handle errors from either request here + // Example: + console.error("Error from first request:", error1); + console.error("Error from second request:", error2); + }); + + + function rerun_strategy(archRunner, stratData) { + record1 = archRunner + //console.log(record1) + + //smazeneme nepotrebne a pridame potrebne + //do budoucna predelat na vytvoreni noveho objektu + //nebudeme muset odstanovat pri kazdem pridani noveho atributu v budoucnu + delete record1["end_positions"]; + delete record1["end_positions_avgp"]; + delete record1["profit"]; + delete record1["trade_count"]; + delete record1["stratvars_toml"]; + delete record1["started"]; + delete record1["stopped"]; + delete record1["metrics"]; + delete record1["settings"]; + delete record1["stratvars"]; + + record1.note = "RERUN " + record1.note + + if (record1.bt_from == "") {delete record1["bt_from"];} + if (record1.bt_to == "") {delete record1["bt_to"];} + + //mazeme, pouze rerunujeme single + delete record1["test_batch_id"]; + delete record1["batch_id"]; + + const rec = new Object() + rec.id2 = parseInt(stratData.id2); + rec.name = stratData.name; + rec.symbol = stratData.symbol; + rec.class_name = stratData.class_name; + rec.script = stratData.script; + rec.open_rush = stratData.open_rush; + rec.close_rush = stratData.close_rush; + rec.stratvars_conf = stratData.stratvars_conf; + rec.add_data_conf = stratData.add_data_conf; + rec.note = stratData.note; + rec.history = ""; + strat_json = JSON.stringify(rec, null, 2); + record1.strat_json = strat_json + + //zkopirujeme strat_id do id a smazeme strat_id + record1.id = record1.strat_id + delete record1["strat_id"]; + + //console.log("record1 pred odeslanim", record1) + jsonString = JSON.stringify(record1); + + $.ajax({ + url:"/stratins/"+record1.id+"/run", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"PUT", + contentType: "application/json", + data: jsonString, + success:function(data){ + $('#button_runagain_arch').attr('disabled',false); + setTimeout(function () { + runnerRecords.ajax.reload(); + stratinRecords.ajax.reload(); + }, 1500); + }, + 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); + } + }) + } + +} + + +function expand_collapse_rows(event) { + event.stopPropagation() + var headerRow = $(this); + var name = headerRow.data('name'); + var collapsed = headerRow.hasClass('collapsed'); + + // Toggle the expand icon name + var expandIcon = headerRow.find('.expand-icon'); + if (collapsed) { + expandIcon.text('expand_less'); + } else { + expandIcon.text('expand_more'); + } + + headerRow.toggleClass('collapsed'); + + archiveRecords.rows().every(function () { + var row = $(this.node()); + var rowGroup = row.attr('data-group-name'); + if (rowGroup == name) { + row.toggle(); + } + }); + + // Save the state + if (collapsed) { + localStorage.setItem('dt-group-state-' + name, 'expanded'); + } else { + localStorage.setItem('dt-group-state-' + name, 'collapsed'); + } + } + +function delete_batch(event){ + event.preventDefault(); + batch_id = $('#batch_id_del').val(); + $('#deletebatch').attr('disabled', 'disabled'); + $.ajax({ + url:"/archived_runners/batch/"+batch_id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"DELETE", + contentType: "application/json", + dataType: "json", + data: JSON.stringify(batch_id), + success:function(data){ + $('#delFormBatch')[0].reset(); + window.$('#delModalBatch').modal('hide'); + $('#deletebatch').attr('disabled', false); + $('#button_delete_batch').attr('disabled', false); + //console.log(data) + archiveRecords.ajax.reload(); + disable_arch_buttons(); + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + $('#deletebatch').attr('disabled', false); + $('#button_delete_batch').attr('disabled', false); + archiveRecords.ajax.reload(); + disable_arch_buttons(); + } + }) +} + + +function analyze_optimal_cutoff(batch_id = null) { + //definice parametru + param_obj = { rem_outliers:false, steps:50} + obj = {runner_ids:[], batch_id:null, params:param_obj} + //bereme bud selected runners + if (!batch_id) { + rows = archiveRecords.rows('.selected').data(); + if (rows == undefined) { + return + } + $('#button_analyze').attr('disabled','disabled'); + // Extract IDs from each row's data and store them in an array + obj.runner_ids = []; + for (var i = 0; i < rows.length; i++) { + obj.runner_ids.push(rows[i].id); // Assuming 'id' is the property that contains the row ID + } + } + //nebo batch + else { + obj.batch_id = batch_id + + } + console.log("analyze cutoff objekt", obj) + // batch_id: Optional[str] = None + // runner_ids: Optional[List[UUID]] = None + // #additional parameter + // params: Optional[dict] = None + + $.ajax({ + url:"/batches/optimizecutoff/", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"POST", + xhrFields: { + responseType: 'blob' + }, + contentType: "application/json", + processData: false, + data: JSON.stringify(obj), + success:function(blob){ + var url = window.URL || window.webkitURL; + console.log("vraceny obraz", blob) + console.log("url",url.createObjectURL(blob)) + display_image(url.createObjectURL(blob)) + if (!batch_id) { + $('#button_analyze').attr('disabled',false); + } + }, + error: function(xhr, status, error) { + console.log("proc to skace do erroru?") + //window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + $('#button_analyze').attr('disabled',false); + if (!batch_id) { + $('#button_analyze').attr('disabled',false); + } + } + }) +} + +//pomocna funkce, ktera vraci filtrovane radky tabulky (bud oznacene nebo batchove) +function get_selected_or_batch(batch_id = null) { + if (!batch_id) { + rows = archiveRecords.rows('.selected'); + } else { + rows = archiveRecords.rows( function ( idx, data, node ) { + return data.batch_id == batch_id; + }); + //console.log("batch rows",batch_id, rows) + } +} + +//prepares export data, either for selected rows or based on batch_id +function prepare_export(batch_id = null) { + rows = get_selected_or_batch(batch_id) + var trdList = [] + if(rows.data().length > 0 ) { + //console.log(rows.data()) + // Loop through the selected rows and display an alert with each row's ID + rows.every(function (rowIdx, tableLoop, rowLoop ) { + var data = this.data() + data.metrics.prescr_trades.forEach((trade) => { + new_obj = {} + new_obj["entry_time"] = (trade.entry_time) ? new Date(trade.entry_time * 1000) : null + new_obj["entry_time"] = (new_obj["entry_time"]) ? new_obj["entry_time"].toLocaleString('cs-CZ', { + timeZone: 'America/New_York', + }) : null + new_obj["exit_time"] = (trade.exit_time) ? new Date(trade.exit_time * 1000):null + new_obj["exit_time"] = (new_obj["exit_time"]) ? new_obj["exit_time"].toLocaleString('cs-CZ', { + timeZone: 'America/New_York', + }) : null + new_obj["direction"] = trade.direction + new_obj["profit"] = trade.profit + new_obj["rel_profit"] = trade.rel_profit + trdList.push(new_obj) + }) + }); + } + return trdList +} + +function download_exported_data(type, batch_id = null) { + filename = batch_id ? "batch"+batch_id+"-trades" : "trades" + if (type == "xml") { + response_type = "application/xml" + output = convertToXml(prepare_export(batch_id)) + } + else { + response_type = "text/csv" + output = convertToCsv(prepare_export(batch_id)) + } + console.log(output) + downloadFile(response_type,type, filename, output) +} + +function display_image(imageUrl) { + // Attempt to load the image + var img = new Image(); + img.src = imageUrl; + img.onload = function() { + // If the image loads successfully, display it + $('#previewImg').attr('src', imageUrl); + //$('#imagePreview').show(); + window.$('#imageModal').modal('show'); + }; + img.onerror = function() { + console.log("no image available") + // If the image fails to load, do nothing + }; +} + +function display_batch_report(batch_id) { + //var imageUrl = '/media/report_'+data.id+".png"; // Replace with your logic to get image URL + var imageUrl = '/media/basic/'+batch_id+'.png'; // Replace with your logic to get image URL + //console.log(imageUrl) + display_image(imageUrl) +} + +function refresh_logfile() { + $.ajax({ + url:"/log?lines=30", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + success:function(response){ + if (response.lines.length == 0) { + $('#log-content').html("no records"); + } + else { + $('#log-content').html(response.lines.join('\n')); + } + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + } + }) +} + +function delete_arch_rows(ids) { + $.ajax({ + url:"/archived_runners/", + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"DELETE", + contentType: "application/json", + dataType: "json", + data: JSON.stringify(ids), + success:function(data){ + $('#delFormArchive')[0].reset(); + window.$('#delModalArchive').modal('hide'); + $('#deletearchive').attr('disabled', false); + //console.log(data) + archiveRecords.ajax.reload(); + disable_arch_buttons() + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + $('#deletearchive').attr('disabled', false); + //archiveRecords.ajax.reload(); + } + }) +} + +function extractNumbersFromString(str) { + // Regular expression to match the pattern #number1/number2 + const pattern = /#(\d+)\/(\d+)/; + const match = str.match(pattern); + + if (match) { + // Extract number1 and number2 from the match + const number1 = parseInt(match[1], 10); + const number2 = parseInt(match[2], 10); + + //return { number1, number2 }; + return number2; + } else { + return null; + } +} + +// Function to generate a unique key for localStorage based on batch_id +function generateStorageKey(batchId) { + return 'dt-group-state-' + batchId; +} + + +function disable_arch_buttons() { + //disable buttons (enable on row selection) + $('#button_runagain_arch').attr('disabled','disabled'); + $('#button_show_arch').attr('disabled','disabled'); + $('#button_delete_arch').attr('disabled','disabled'); + $('#button_delete_batch').attr('disabled','disabled'); + $('#button_analyze').attr('disabled','disabled'); + $('#button_edit_arch').attr('disabled','disabled'); + $('#button_compare_arch').attr('disabled','disabled'); + $('#button_report').attr('disabled','disabled'); + $('#button_export_xml').attr('disabled','disabled'); + $('#button_export_csv').attr('disabled','disabled'); +} + +function enable_arch_buttons() { + $('#button_analyze').attr('disabled',false); + $('#button_show_arch').attr('disabled',false); + $('#button_runagain_arch').attr('disabled',false); + $('#button_delete_arch').attr('disabled',false); + $('#button_delete_batch').attr('disabled',false); + $('#button_edit_arch').attr('disabled',false); + $('#button_compare_arch').attr('disabled',false); + $('#button_report').attr('disabled',false); + $('#button_export_xml').attr('disabled',false); + $('#button_export_csv').attr('disabled',false); +} \ No newline at end of file diff --git a/v2realbot/static/js/tables/archivetable/handlers.js b/v2realbot/static/js/tables/archivetable/handlers.js new file mode 100644 index 0000000..0320cad --- /dev/null +++ b/v2realbot/static/js/tables/archivetable/handlers.js @@ -0,0 +1,477 @@ +//event handlers for archiveTables + + + +$(document).ready(function () { + archiveRecords.ajax.reload(); + disable_arch_buttons(); + + + // Use 'td:nth-child(2)' to target the second column + $('#archiveTable tbody').on('click', 'td:nth-child(2)', function () { + var data = archiveRecords.row(this).data(); + //var imageUrl = '/media/report_'+data.id+".png"; // Replace with your logic to get image URL + var imageUrl = '/media/basic/'+data.id+'.png'; // Replace with your logic to get image URL + //console.log(imageUrl) + display_image(imageUrl) + }); + + // Use 'td:nth-child(2)' to target the second column + $('#archiveTable tbody').on('click', 'td:nth-child(18)', function () { + var data = archiveRecords.row(this).data(); + if (data.batch_id) { + display_batch_report(data.batch_id) + } + }); + + //selectable rows in archive table + $('#archiveTable tbody').on('click', 'tr[data-group-name]', function () { + if ($(this).hasClass('selected')) { + //$(this).removeClass('selected'); + //aadd here condition that disable is called only when there is no other selected class on tr[data-group-name] + // Check if there are no other selected rows before disabling buttons + if ($('#archiveTable tr[data-group-name].selected').length === 1) { + disable_arch_buttons(); + } + //disable_arch_buttons() + } else { + //archiveRecords.$('tr.selected').removeClass('selected'); + $(this).addClass('selected'); + enable_arch_buttons() + } + }); + + + //TOOL BUTTONs on BATCH HEADER + + // Event listener for click to display batch report + $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_report_button', function (event) { + event.stopPropagation(); + // Get the parent
{\n${generateHTML(record2, delta)}}\n`;
+ // document.getElementById('second').innerHTML = htmlMarkup2;
+
+ // const htmlMarkup1 = `{\n${generateHTML(record1, delta)}}\n`;
+ // document.getElementById('first').innerHTML = htmlMarkup1;
+
+ event.preventDefault();
+ //$('#button_compare').attr('disabled','disabled');
+ }
+ });
+
+ //generate batch optimization cutoff (predelat na button pro obecne analyzy batche)
+ $('#button_analyze').click(function () {
+ analyze_optimal_cutoff();
+ });
+
+ //generate report button
+ $('#button_report').click(function () {
+ rows = archiveRecords.rows('.selected');
+ if (rows == undefined) {
+ return
+ }
+ $('#button_report').attr('disabled','disabled');
+ runnerIds = []
+ if(rows.data().length > 0 ) {
+ // Loop through the selected rows and display an alert with each row's ID
+ rows.every(function (rowIdx, tableLoop, rowLoop ) {
+ var data = this.data()
+ runnerIds.push(data.id);
+ });
+ }
+ $.ajax({
+ url:"/archived_runners/generatereportimage",
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader('X-API-Key',
+ API_KEY); },
+ method:"POST",
+ xhrFields: {
+ responseType: 'blob'
+ },
+ contentType: "application/json",
+ processData: false,
+ data: JSON.stringify(runnerIds),
+ success:function(blob){
+ var url = window.URL || window.webkitURL;
+ console.log("vraceny obraz", blob)
+ console.log("url",url.createObjectURL(blob))
+ display_image(url.createObjectURL(blob))
+ $('#button_report').attr('disabled',false);
+ },
+ error: function(xhr, status, error) {
+ console.log("proc to skace do erroru?")
+ //window.alert(JSON.stringify(xhr));
+ console.log(JSON.stringify(xhr));
+ $('#button_report').attr('disabled',false);
+ }
+ })
+ });
+
+ //button to query log
+ $('#logRefreshButton').click(function () {
+ refresh_logfile()
+ });
+
+ //button to open log modal
+ $('#button_show_log').click(function () {
+ window.$('#logModal').modal('show');
+ refresh_logfile()
+ });
+
+ //delete batch button - open modal - DECOMISS - dostupne jen na batche
+ // $('#button_delete_batch').click(function () {
+ // row = archiveRecords.row('.selected').data();
+ // if (row == undefined || row.batch_id == undefined) {
+ // return
+ // }
+ // $('#batch_id_del').val(row.batch_id);
+
+ // rows = archiveRecords.rows('.selected');
+ // if (rows == undefined) {
+ // return
+ // }
+ // $('#listofids').html("");
+ // window.$('#delModalBatch').modal('show');
+ // });
+
+
+ //delete batch submit modal
+ $("#delModalBatch").on('submit','#delFormBatch', delete_batch);
+
+ //delete arch button - open modal
+ $('#button_delete_arch').click(function () {
+ rows = archiveRecords.rows('.selected');
+ if (rows == undefined) {
+ return
+ }
+ $('#listofids').html("");
+
+ if(rows.data().length > 0 ) {
+ ids_to_del = ""
+ // Loop through the selected rows and display an alert with each row's ID
+ rows.every(function (rowIdx, tableLoop, rowLoop ) {
+ var data = this.data()
+ ids_to_del = ids_to_del + data.id + "