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 @@
-->
- - - - + + + + - - - - - + + + + + - +
@@ -853,10 +853,26 @@ - + + + + + + + - + + + + + + + + + + + diff --git a/v2realbot/static/js/archivetables.js b/v2realbot/static/js/archivetables.js deleted file mode 100644 index 2668ec7..0000000 --- a/v2realbot/static/js/archivetables.js +++ /dev/null @@ -1,1405 +0,0 @@ -//ARCHIVE TABLES -let editor_diff_arch1 -let editor_diff_arch2 -var archData = null -var batchHeaders = [] - -function refresh_arch_and_callback(row, callback) { - //console.log("entering refresh") - var request = $.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)); - } - }); - - // Handling the responses of both requests - $.when(request).then(function(response) { - // Both requests have completed successfully - //console.log("Result from request:", response); - //console.log("Response received. calling callback") - //call callback function - callback(response) - - }, function(error) { - // Handle errors from either request here - // Example: - console.error("Error from first request:", error); - console.log("requesting id error") - }); -} - -//type ("text/csv","application/xml"), filetype (csv), filename -function downloadFile(type, filetype, filename, content) { - var blob = new Blob([content], { type: type }); - var url = window.URL.createObjectURL(blob); - var link = document.createElement("a"); - link.href = url; - link.download = filename +"."+filetype; - link.click(); -} - -// Function to convert a JavaScript object to XML -function convertToXml(data) { - var xml = '\n\n'; - data.forEach(function (item) { - xml += ' \n'; - Object.keys(item).forEach(function (key) { - xml += ' <' + key + '>' + item[key] + '\n'; - }); - xml += ' \n'; - }); - xml += ''; - return xml; -} - -// Function to convert a JavaScript object to CSV -function convertToCsv(data) { - var csv = ''; - // Get the headers - var headers = Object.keys(data[0]); - csv += headers.join(',') + '\n'; - - // Iterate over the data - data.forEach(function (item) { - var row = headers.map(function (header) { - return item[header]; - }); - csv += row.join(',') + '\n'; - }); - - return csv; -} - -function prepare_export() { - rows = archiveRecords.rows('.selected'); - 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 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 - }; -} - -$(document).ready(function () { - archiveRecords.ajax.reload(); - - // 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) - } - }); - - // 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 element - var parentTr = $(this).closest('tr'); - // Retrieve the 'data-name' attribute from the parent - var batch_id = parentTr.data('name'); - display_batch_report(batch_id) - }); - - // Event listener for click to delete batch - $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_delete_button', function (event) { - event.stopPropagation(); - // Get the parent element - var parentTr = $(this).closest('tr'); - // Retrieve the 'data-name' attribute from the parent - var batch_id = parentTr.data('name'); - $('#batch_id_del').val(batch_id); - $('#listofids').html(""); - window.$('#delModalBatch').modal('show'); - }); - - 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) - } - - // $('#archiveTable tbody').on('mouseleave', 'td:nth-child(2)', function () { - // $('#imagePreview').hide(); - // }); - - // Hide image on click anywhere in the document - // $(document).on('click', function() { - // $('#imagePreview').hide(); - // }); - - // function hideImage() { - // $('#imagePreview').hide(); - // } - // $('#archiveTable tbody').on('mousemove', 'td:nth-child(2)', function(e) { - // $('#imagePreview').css({'top': e.pageY + 10, 'left': e.pageX + 10}); - // }); - - - //button export - $('#button_export_xml').click(function () { - xmled = convertToXml(prepare_export()) - console.log(xmled) - //csved = convertToCsv(trdList) - //console.log(csved) - //type ("text/csv","application/xml"), filetype (csv), filename, content - //downloadFile("text/csv","csv", "trades", csved) - downloadFile("application/xml","xml", "trades", xmled) - //console.log(jsonobj) - //downloadCsv(jsonobj); - }); - - //button export - $('#button_export_csv').click(function () { - csved = convertToCsv(prepare_export()) - console.log(csved) - //type ("text/csv","application/xml"), filetype (csv), filename, content - downloadFile("text/csv","csv", "trades", csved) - //console.log(jsonobj) - //downloadCsv(jsonobj); -}); - - //button select page - $('#button_selpage').click(function () { - if ($('#button_selpage').hasClass('active')) { - $('#button_selpage').removeClass('active'); - archiveRecords.rows().deselect(); - } - else { - $('#button_selpage').addClass('active'); - archiveRecords.rows( { page: 'current' } ).select(); - } - }); - - //button clear log - $('#button_clearlog').click(function () { - $('#lines').empty(); - }); - - //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'); - - //selectable rows in archive table - $('#archiveTable tbody').on('click', 'tr', function () { - if ($(this).hasClass('selected')) { - //$(this).removeClass('selected'); - $('#button_show_arch').attr('disabled','disabled'); - $('#button_analyze').attr('disabled','disabled'); - $('#button_runagain_arch').attr('disabled','disabled'); - $('#button_delete_arch').attr('disabled','disabled'); - $('#button_delete_batch').attr('disabled','disabled'); - $('#button_edit_arch').attr('disabled','disabled'); - $('#button_compare_arch').attr('disabled','disabled'); - } else { - //archiveRecords.$('tr.selected').removeClass('selected'); - $(this).addClass('selected'); - $('#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 compare arch - $('#button_compare_arch').click(function () { - if (editor_diff_arch1) {editor_diff_arch1.dispose()} - if (editor_diff_stratin1) {editor_diff_stratin1.dispose()} - if (editor_diff_arch2) {editor_diff_arch2.dispose()} - if (editor_diff_stratin2) {editor_diff_stratin2.dispose()} - window.$('#diffModal').modal('show'); - rows = archiveRecords.rows('.selected').data(); - - id1 = rows[0].id - id2 = rows[1].id - - var request1 = $.ajax({ - url: "/archived_runners/"+id1, - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"GET", - contentType: "application/json", - dataType: "json", - success:function(data){ - //console.log("first request 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)); - console.log("first request error") - } - }); - var request2 = $.ajax({ - url: "/archived_runners/"+id2, - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"GET", - contentType: "application/json", - dataType: "json", - success:function(data){ - //console.log("first request 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)); - console.log("first request error") - } - }); - - // 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") - perform_compare(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); - }); - - //sem vstupuji dva nove natahnute objekty - function perform_compare(data1, data2) { - - var record1 = new Object() - //console.log(JSON.stringify(rows)) - - record1 = JSON.parse(data1.strat_json) - //record1.json = rows[0].json - //record1.id = rows[0].id; - // record1.id2 = parseInt(rows[0].id2); - //record1.name = rows[0].name; - // record1.symbol = rows[0].symbol; - // record1.class_name = rows[0].class_name; - // record1.script = rows[0].script; - // record1.open_rush = rows[0].open_rush; - // record1.close_rush = rows[0].close_rush; - //console.log(record1.stratvars_conf) - - //ELEMENTS TO COMPARE - - //profit sekce - //console.log(data1.metrics) - - try { - record1["profit"] = JSON.parse(data1.metrics.profit) - } - catch (e) { - console.log(e.message) - } - - //record1.stratvars_conf = TOML.parse(record1.stratvars_conf); - //record1.add_data_conf = TOML.parse(record1.add_data_conf); - // record1.note = rows[0].note; - // record1.history = ""; - //jsonString1 = JSON.stringify(record1, null, 2); - - var record2 = new Object() - record2 = JSON.parse(data2.strat_json) - - // record2.id = rows[1].id; - // record2.id2 = parseInt(rows[1].id2); - //record2.name = rows[1].name; - // record2.symbol = rows[1].symbol; - // record2.class_name = rows[1].class_name; - // record2.script = rows[1].script; - // record2.open_rush = rows[1].open_rush; - // record2.close_rush = rows[1].close_rush; - - //ELEMENTS TO COMPARE - //console.log(data2.metrics) - - try { - record2["profit"] = JSON.parse(data2.metrics.profit) - } - catch (e) { - console.log(e.message) - } - //record2.stratvars_conf = TOML.parse(record2.stratvars_conf); - //record2.add_data_conf = TOML.parse(record2.add_data_conf); - // record2.note = rows[1].note; - // record2.history = ""; - //jsonString2 = JSON.stringify(record2, null, 2); - - $('#diff_first').text(record1.name); - $('#diff_second').text(record2.name); - $('#diff_first_id').text(data1.id); - $('#diff_second_id').text(data2.id); - - //monaco - require(["vs/editor/editor.main"], () => { - editor_diff_arch1 = monaco.editor.createDiffEditor(document.getElementById('diff_content1'), - { - language: 'toml', - theme: 'tomlTheme-dark', - originalEditable: false, - automaticLayout: true - } - ); - console.log(record1.stratvars_conf) - console.log(record2.stratvars_conf) - editor_diff_arch1.setModel({ - original: monaco.editor.createModel(record1.stratvars_conf, 'toml'), - modified: monaco.editor.createModel(record2.stratvars_conf, 'toml'), - }); - editor_diff_arch2 = monaco.editor.createDiffEditor(document.getElementById('diff_content2'), - { - language: 'toml', - theme: 'tomlTheme-dark', - originalEditable: false, - automaticLayout: true - } - ); - editor_diff_arch2.setModel({ - original: monaco.editor.createModel(record1.add_data_conf, 'toml'), - modified: monaco.editor.createModel(record2.add_data_conf, 'toml'), - }); - }); - - // var delta = compareObjects(record1, record2) - // const htmlMarkup2 = `
{\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 + "
" - }); - - $('#listofids').html(ids_to_del); - window.$('#delModalArchive').modal('show'); - //$('#delidarchive').val(row.id); - } - }); - - //edit button - $('#button_edit_arch').click(function () { - row = archiveRecords.row('.selected').data(); - if (row == undefined) { - return - } - - refresh_arch_and_callback(row, display_edit_modal) - - function display_edit_modal(row) { - window.$('#editModalArchive').modal('show'); - $('#editidarchive').val(row.id); - $('#editnote').val(row.note); - - - try { - metrics = JSON.parse(row.metrics) - } - catch (e) { - metrics = row.metrics - } - $('#metrics').val(JSON.stringify(metrics,null,2)); - //$('#metrics').val(TOML.parse(row.metrics)); - if (row.stratvars_toml) { - $('#editstratvars').val(row.stratvars_toml); - } - else{ - $('#editstratvars').val(JSON.stringify(row.stratvars,null,2)); - } - - - $('#editstratjson').val(row.strat_json); - } - }); - - //show button - $('#button_show_arch').click(function () { - - row = archiveRecords.row('.selected').data(); - if (row == undefined) { - return - } - - refresh_arch_and_callback(row, get_detail_and_show) - - function get_detail_and_show(row) { - $.ajax({ - url:"/archived_runners_detail/"+row.id, - beforeSend: function (xhr) { - xhr.setRequestHeader('X-API-Key', - API_KEY); }, - method:"GET", - contentType: "application/json", - dataType: "json", - 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() - //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 '
'+data+'
'; - } - return data; - }, - }, - { - targets: [0,17], - render: function ( data, type, row ) { - if (!data) return data - return '
'+data+'' - }, - }, - { - targets: [5], - render: function ( data, type, row ) { - now = new Date(data) - if (type == "sort") { - return new Date(data).getTime(); - } - var date = new Date(data); - tit = date.toLocaleString('cs-CZ', { - timeZone: 'America/New_York', - }) - - if (isToday(now)) { - //return local time only - return '
'+ 'dnes ' + format_date(data,false,true)+'
' - } - else - { - //return local datetime - return '
'+ format_date(data,false,false)+'
' - } - }, - }, - { - targets: [6], - render: function ( data, type, row ) { - now = new Date(data) - if (type == "sort") { - return new Date(data).getTime(); - } - var date = new Date(data); - tit = date.toLocaleString('cs-CZ', { - timeZone: 'America/New_York', - }) - - if (isToday(now)) { - //return local time only - return '
'+ 'dnes ' + format_date(data,false,true)+'
' - } - else - { - //return local datetime - return '
'+ format_date(data,false,false)+'
' - } - }, - }, - { - targets: [9,10], - render: function ( data, type, row ) { - if (type == "sort") { - return new Date(data).getTime(); - } - //console.log(data) - //market datetime - return data ? format_date(data, true) : data - }, - }, - { - targets: [2], - render: function ( data, type, row ) { - return '
'+data+'
' - }, - }, - // { - // targets: [4], - // render: function ( data, type, row ) { - // return '
'+data+'
' - // }, - // }, - { - targets: [16], - render: function ( data, type, row ) { - //console.log("metrics", data) - try { - data = JSON.parse(data) - } - catch (error) { - //console.log(error) - } - var res = JSON.stringify(data) - var unquoted = res.replace(/"([^"]+)":/g, '$1:') - - //zobrazujeme jen kratkou summary pokud mame, jinak davame vse, do titlu davame vzdy vse - //console.log(data) - short = null - if ((data) && (data.profit) && (data.profit.sum)) { - short = data.profit.sum - } - else { - short = unquoted - } - return '
'+short+'
' - }, - }, - { - targets: [4], - render: function ( data, type, row ) { - return '
'+data+'
' - }, - }, - { - targets: [13,14,15], - 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']], - select: { - info: true, - style: 'multi', - //selector: 'tbody > tr:not(.group-header)' - selector: 'tbody > tr:not(.group-header)' - }, - paging: true, - // lengthChange: false, - // select: true, - // createdRow: function( row, data, dataIndex){ - // if (is_running(data.id) ){ - // alert("runner"); - // $(row).addClass('highlight'); - // } - //} - // Add row grouping based on 'batch_id' - //TODO projit a zrevidovat - pripadne optimalizovat - //NOTE zde jse skoncil - rowGroup: { - dataSrc: 'batch_id', - //toto je volano pri renderovani headeru grupy - startRender: function (rows, group) { - var firstRowData = rows.data()[0]; - //pro no-batch-id je idcko prvni id - var groupId = group ? group : 'no-batch-id-' + firstRowData.id; - var stateKey = 'dt-group-state-' + groupId; - var state = localStorage.getItem(stateKey); - - // Iterate over each row in the group to set the data attribute - // zaroven pro kazdy node nastavime viditelnost podle nastaveni - rows.every(function (rowIdx, tableLoop, rowLoop) { - var rowNode = $(this.node()); - rowNode.attr('data-group-name', groupId); - if (state == 'collapsed') { - rowNode.hide(); - } else { - rowNode.show(); - } - }); - - // Initialize variables for the group - var itemCount = 0; - var period = ''; - var profit = ''; - var started = null; - var stratinId = null; - - // // Process each item only once - // archiveRecords.rows({ search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop) { - // var data = this.data(); - - // if ((group && data.batch_id == group)) { - // itemCount++; - // if (itemCount === 1 ) { - // firstNote = data.note ? data.note.substring(0, 14) : ''; - - // if (data.note) { - // better_counter = extractNumbersFromString(data.note); - // } - // try { - // profit = data.metrics.profit.batch_sum_profit; - // } catch (e) { - // profit = 'N/A'; - // } - // } - // } - // }); - - - //pokud mame batch_id podivame se zda jeho nastaveni uz nema a pokud ano pouzijeme to - //pokud nemame tak si ho loadneme - if (group) { - const existingBatch = batchHeaders.find(batch => batch.batch_id == group); - //jeste neni v poli batchu - udelame hlavicku - if (!existingBatch) { - itemCount = extractNumbersFromString(firstRowData.note); - profit = firstRowData.metrics.profit.batch_sum_profit; - period = firstRowData.note ? firstRowData.note.substring(0, 14) : ''; - started = firstRowData.started - stratinId = firstRowData.strat_id - var newBatchHeader = {batch_id:group, profit:profit, itemCount:itemCount, period:period, started:started, stratinId:stratinId} - batchHeaders.push(newBatchHeader) - } - //uz je v poli, ale mame novejsi (pribyl v ramci backtestu napr.) - updatujeme - else if (new Date(existingBatch.started) < new Date(firstRowData.started)) { - itemCount = extractNumbersFromString(firstRowData.note); - profit = firstRowData.metrics.profit.batch_sum_profit; - period = firstRowData.note ? firstRowData.note.substring(0, 14) : ''; - started = firstRowData.started - stratinId = firstRowData.id - existingBatch.itemCount = itemCount; - existingBatch.profit = profit; - existingBatch.period = period; - existingBatch.started = started; - } - //uz je v poli batchu vytahneme - else { - profit = existingBatch.profit - itemCount = existingBatch.itemCount - period = existingBatch.period - started = existingBatch.started - stratinId = existingBatch.stratinId - } - } - - //zaroven nastavime u vsech childu - - // Construct the GROUP HEADER - sem pripadna tlačítka atp. - //var groupHeaderContent = '' + (group ? 'Batch ID: ' + group : 'No Batch') + ''; - var tools = '' - var icon = '' - icon_color = '' - profit_icon_color = '' - exp_coll_icon_name = '' - exp_coll_icon_name = (state == 'collapsed') ? 'expand_more' : 'expand_less' - if (group) { - tools += 'lab_profile' - tools += 'delete' - icon_color = getColorForId(stratinId) - profit_icon_color = (profit>0) ? "#4f8966" : "#bb2f5e" //"#d42962" - } - else { - //def color for no batch - semi transparent - icon_color = "#ced4da17" - } - icon = ''+exp_coll_icon_name+'' - - //console.log(group, groupId, stratinId) - //var groupHeaderContent = ''+(group ? 'Batch ID: ' + group: 'No Batch')+''; - var groupHeaderContent = ''+ icon + (group ? 'Batch ID: ' + group: 'No Batch')+''; - groupHeaderContent += (group ? ' (' + itemCount + ')' + ' ' + period + ' Profit: ' + profit + '' : ''); - groupHeaderContent += group ? tools : "" - return $('') - .append('' + groupHeaderContent + '') - .attr('data-name', groupId) - .addClass('group-header') - .addClass(state); - } - }, - // drawCallback: function (settings) { - // var api = this.api(); - // var rows = api.rows({ page: 'current' }).nodes(); - - // api.column(17, { page: 'current' }).data().each(function (group, i) { - // console.log("drawCallabck i",i) - // console.log("rows", $(rows).eq(i)) - // var groupName = group ? group : $(rows).eq(i).attr('data-name'); - // console.log("groupName", groupName) - // var stateKey = 'dt-group-state-' + groupName; - // var state = localStorage.getItem(stateKey); - - // if (state === 'collapsed') { - // $(rows).eq(i).hide(); - // } else { - // $(rows).eq(i).show(); - // } - - // // Set the unique identifier as a data attribute on each row - // //$(rows).eq(i).attr('data-group-name', groupName); - - // // // Add or remove the 'collapsed' class based on the state - // // if (groupName.startsWith('no-batch-id-')) { - // // $('tr[data-name="' + groupName + '"]').toggleClass('collapsed', state === 'collapsed'); - // // } - // }); - // } -}); - -//workaround pro spatne oznacovani selectu i pro group-headery -$('#archiveTable tbody').on('click', 'tr.group-header', function(event) { - var $row = $(this); - - // Schedule the class removal/addition for the next event loop - setTimeout(function() { - if ($row.hasClass("selected")) { - console.log("Header selected, removing selection"); - $row.removeClass("selected"); - } else { - console.log("Header not selected, adding selection"); - $row.addClass("selected"); - } - }, 0); -}); - -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; -} - -// Expand/Collapse functionality -$('#archiveTable tbody').on('click', 'tr.group-header', function (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'); - } -}); - - - - - -//WIP buttons to hide datatable columns - // document.querySelectorAll('a.toggle-vis').forEach((el) => { - // el.addEventListener('click', function (e) { - // e.preventDefault(); - - // let columnIdx = e.target.getAttribute('data-column'); - // let column = archiveRecords.column(columnIdx); - - // // Toggle the visibility - // column.visible(!column.visible()); - // }); - // }); - - \ No newline at end of file diff --git a/v2realbot/static/js/mytables.js b/v2realbot/static/js/mytables.js index 50642e0..e652400 100644 --- a/v2realbot/static/js/mytables.js +++ b/v2realbot/static/js/mytables.js @@ -302,6 +302,7 @@ $(document).ready(function () { runnerRecords.ajax.reload(); stratinRecords.ajax.reload(); archiveRecords.ajax.reload(); + disable_arch_buttons(); }) //button copy @@ -927,11 +928,23 @@ var runnerRecords = ], paging: false, processing: false, - columnDefs: [ { - targets: [0,1], + columnDefs: [ + { + targets: [0], render: function ( data, type, row ) { return '
'+data+'' + }, }, + { + targets: 1, + render: function ( data, type, row ) { + if (type === 'display') { + //console.log("arch") + var color = getColorForId(data); + return '
'+data+'
'; + } + return data; + }, }, { targets: [2], diff --git a/v2realbot/static/js/tables/archivetable/functions.js b/v2realbot/static/js/tables/archivetable/functions.js new file mode 100644 index 0000000..17b2928 --- /dev/null +++ b/v2realbot/static/js/tables/archivetable/functions.js @@ -0,0 +1,535 @@ +//funkce a promenne specificke pro archiveTable +//usually work with archiveRecords + +//ARCHIVE TABLES +let editor_diff_arch1 +let editor_diff_arch2 +var archData = null +var batchHeaders = [] + +function refresh_arch_and_callback(row, callback) { + //console.log("entering refresh") + var request = $.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)); + } + }); + + // Handling the responses of both requests + $.when(request).then(function(response) { + // Both requests have completed successfully + //console.log("Result from request:", response); + //console.log("Response received. calling callback") + //call callback function + callback(response) + + }, function(error) { + // Handle errors from either request here + // Example: + console.error("Error from first request:", error); + console.log("requesting id error") + }); +} + +//triggers charting +function get_detail_and_chart(row) { + $.ajax({ + url:"/archived_runners_detail/"+row.id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + 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() + //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 element + var parentTr = $(this).closest('tr'); + // Retrieve the 'data-name' attribute from the parent + var batch_id = parentTr.data('name'); + display_batch_report(batch_id) + }); + + // Event listener for click to delete batch + $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_delete_button', function (event) { + event.stopPropagation(); + // Get the parent element + var parentTr = $(this).closest('tr'); + // Retrieve the 'data-name' attribute from the parent + var batch_id = parentTr.data('name'); + $('#batch_id_del').val(batch_id); + $('#listofids').html(""); + window.$('#delModalBatch').modal('show'); + }); + + // Event listener for click to xml export batch + $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_exportxml_button', function (event) { + event.stopPropagation(); + // Get the parent element + var parentTr = $(this).closest('tr'); + // Retrieve the 'data-name' attribute from the parent + var batch_id = parentTr.data('name'); + download_exported_data("xml", batch_id); + }); + + // Event listener for click to csv export batch + $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_exportcsv_button', function (event) { + event.stopPropagation(); + // Get the parent element + var parentTr = $(this).closest('tr'); + // Retrieve the 'data-name' attribute from the parent + var batch_id = parentTr.data('name'); + console.log(batch_id) + download_exported_data("csv", batch_id); + }); + + // Event listener for optimal batch cutoff + $('#archiveTable tbody').on('click', 'tr.group-header #batchtool_cutoff_button', function (event) { + event.stopPropagation(); + // Get the parent element + var parentTr = $(this).closest('tr'); + // Retrieve the 'data-name' attribute from the parent + var batch_id = parentTr.data('name'); + console.log(batch_id) + analyze_optimal_cutoff(batch_id) + }); + + //TOOL BUTTONs above the TABLE - for selected days + //button export + $('#button_export_xml').click(function(event) { + download_exported_data("xml"); + }); + + + //button export + $('#button_export_csv').click(function(event) { + download_exported_data("csv"); + }); + + //button select page + $('#button_selpage').click(function () { + if ($('#button_selpage').hasClass('active')) { + $('#button_selpage').removeClass('active'); + archiveRecords.rows().deselect(); + disable_arch_buttons(); + } + else { + $('#button_selpage').addClass('active'); + archiveRecords.rows( { page: 'current' } ).select(); + enable_arch_buttons(); + } + }); + + //button clear log + $('#button_clearlog').click(function () { + $('#lines').empty(); + }); + + //button compare arch + $('#button_compare_arch').click(function () { + if (editor_diff_arch1) {editor_diff_arch1.dispose()} + if (editor_diff_stratin1) {editor_diff_stratin1.dispose()} + if (editor_diff_arch2) {editor_diff_arch2.dispose()} + if (editor_diff_stratin2) {editor_diff_stratin2.dispose()} + window.$('#diffModal').modal('show'); + rows = archiveRecords.rows('.selected').data(); + + id1 = rows[0].id + id2 = rows[1].id + + var request1 = $.ajax({ + url: "/archived_runners/"+id1, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + success:function(data){ + //console.log("first request 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)); + console.log("first request error") + } + }); + var request2 = $.ajax({ + url: "/archived_runners/"+id2, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + dataType: "json", + success:function(data){ + //console.log("first request 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)); + console.log("first request error") + } + }); + + // 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") + perform_compare(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); + }); + + //sem vstupuji dva nove natahnute objekty + function perform_compare(data1, data2) { + + var record1 = new Object() + //console.log(JSON.stringify(rows)) + + record1 = JSON.parse(data1.strat_json) + //record1.json = rows[0].json + //record1.id = rows[0].id; + // record1.id2 = parseInt(rows[0].id2); + //record1.name = rows[0].name; + // record1.symbol = rows[0].symbol; + // record1.class_name = rows[0].class_name; + // record1.script = rows[0].script; + // record1.open_rush = rows[0].open_rush; + // record1.close_rush = rows[0].close_rush; + //console.log(record1.stratvars_conf) + + //ELEMENTS TO COMPARE + + //profit sekce + //console.log(data1.metrics) + + try { + record1["profit"] = JSON.parse(data1.metrics.profit) + } + catch (e) { + console.log(e.message) + } + + //record1.stratvars_conf = TOML.parse(record1.stratvars_conf); + //record1.add_data_conf = TOML.parse(record1.add_data_conf); + // record1.note = rows[0].note; + // record1.history = ""; + //jsonString1 = JSON.stringify(record1, null, 2); + + var record2 = new Object() + record2 = JSON.parse(data2.strat_json) + + // record2.id = rows[1].id; + // record2.id2 = parseInt(rows[1].id2); + //record2.name = rows[1].name; + // record2.symbol = rows[1].symbol; + // record2.class_name = rows[1].class_name; + // record2.script = rows[1].script; + // record2.open_rush = rows[1].open_rush; + // record2.close_rush = rows[1].close_rush; + + //ELEMENTS TO COMPARE + //console.log(data2.metrics) + + try { + record2["profit"] = JSON.parse(data2.metrics.profit) + } + catch (e) { + console.log(e.message) + } + //record2.stratvars_conf = TOML.parse(record2.stratvars_conf); + //record2.add_data_conf = TOML.parse(record2.add_data_conf); + // record2.note = rows[1].note; + // record2.history = ""; + //jsonString2 = JSON.stringify(record2, null, 2); + + $('#diff_first').text(record1.name); + $('#diff_second').text(record2.name); + $('#diff_first_id').text(data1.id); + $('#diff_second_id').text(data2.id); + + //monaco + require(["vs/editor/editor.main"], () => { + editor_diff_arch1 = monaco.editor.createDiffEditor(document.getElementById('diff_content1'), + { + language: 'toml', + theme: 'tomlTheme-dark', + originalEditable: false, + automaticLayout: true + } + ); + console.log(record1.stratvars_conf) + console.log(record2.stratvars_conf) + editor_diff_arch1.setModel({ + original: monaco.editor.createModel(record1.stratvars_conf, 'toml'), + modified: monaco.editor.createModel(record2.stratvars_conf, 'toml'), + }); + editor_diff_arch2 = monaco.editor.createDiffEditor(document.getElementById('diff_content2'), + { + language: 'toml', + theme: 'tomlTheme-dark', + originalEditable: false, + automaticLayout: true + } + ); + editor_diff_arch2.setModel({ + original: monaco.editor.createModel(record1.add_data_conf, 'toml'), + modified: monaco.editor.createModel(record2.add_data_conf, 'toml'), + }); + }); + + // var delta = compareObjects(record1, record2) + // const htmlMarkup2 = `
{\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 + "
" + }); + + $('#listofids').html(ids_to_del); + window.$('#delModalArchive').modal('show'); + //$('#delidarchive').val(row.id); + } + }); + + //edit button + $('#button_edit_arch').click(function () { + row = archiveRecords.row('.selected').data(); + if (row == undefined) { + return + } + + refresh_arch_and_callback(row, display_edit_modal) + + function display_edit_modal(row) { + window.$('#editModalArchive').modal('show'); + $('#editidarchive').val(row.id); + $('#editnote').val(row.note); + + + try { + metrics = JSON.parse(row.metrics) + } + catch (e) { + metrics = row.metrics + } + $('#metrics').val(JSON.stringify(metrics,null,2)); + //$('#metrics').val(TOML.parse(row.metrics)); + if (row.stratvars_toml) { + $('#editstratvars').val(row.stratvars_toml); + } + else{ + $('#editstratvars').val(JSON.stringify(row.stratvars,null,2)); + } + + + $('#editstratjson').val(row.strat_json); + } + }); + + //show button + $('#button_show_arch').click(function () { + row = archiveRecords.row('.selected').data(); + if (row == undefined) { + return + } + refresh_arch_and_callback(row, get_detail_and_chart) + }); + + //run again button + $('#button_runagain_arch').click(run_day_again) + + //workaround pro spatne oznacovani selectu i pro group-headery + // $('#archiveTable tbody').on('click', 'tr.group-header', function(event) { + // var $row = $(this); + + // // Schedule the class removal/addition for the next event loop + // setTimeout(function() { + // if ($row.hasClass("selected")) { + // console.log("Header selected, removing selection"); + // $row.removeClass("selected"); + // } + // }, 0); + // }); + + // Expand/Collapse functionality + $('#archiveTable tbody').on('click', 'tr.group-header', expand_collapse_rows); + +}) + \ No newline at end of file diff --git a/v2realbot/static/js/tables/archivetable/init.js b/v2realbot/static/js/tables/archivetable/init.js new file mode 100644 index 0000000..b290719 --- /dev/null +++ b/v2realbot/static/js/tables/archivetable/init.js @@ -0,0 +1,381 @@ +//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 '
'+data+'
'; + } + return data; + }, + }, + { + targets: [0,17], + render: function ( data, type, row ) { + if (!data) return data + return '
'+data+'' + }, + }, + { + targets: [5], + render: function ( data, type, row ) { + now = new Date(data) + if (type == "sort") { + return new Date(data).getTime(); + } + var date = new Date(data); + tit = date.toLocaleString('cs-CZ', { + timeZone: 'America/New_York', + }) + + if (isToday(now)) { + //return local time only + return '
'+ 'dnes ' + format_date(data,false,true)+'
' + } + else + { + //return local datetime + return '
'+ format_date(data,false,false)+'
' + } + }, + }, + { + targets: [6], + render: function ( data, type, row ) { + now = new Date(data) + if (type == "sort") { + return new Date(data).getTime(); + } + var date = new Date(data); + tit = date.toLocaleString('cs-CZ', { + timeZone: 'America/New_York', + }) + + if (isToday(now)) { + //return local time only + return '
'+ 'dnes ' + format_date(data,false,true)+'
' + } + else + { + //return local datetime + return '
'+ format_date(data,false,false)+'
' + } + }, + }, + { + targets: [9,10], + render: function ( data, type, row ) { + if (type == "sort") { + return new Date(data).getTime(); + } + //console.log(data) + //market datetime + return data ? format_date(data, true) : data + }, + }, + { + targets: [2], + render: function ( data, type, row ) { + return '
'+data+'
' + }, + }, + // { + // targets: [4], + // render: function ( data, type, row ) { + // return '
'+data+'
' + // }, + // }, + { + targets: [16], + render: function ( data, type, row ) { + //console.log("metrics", data) + try { + data = JSON.parse(data) + } + catch (error) { + //console.log(error) + } + var res = JSON.stringify(data) + var unquoted = res.replace(/"([^"]+)":/g, '$1:') + + //zobrazujeme jen kratkou summary pokud mame, jinak davame vse, do titlu davame vzdy vse + //console.log(data) + short = null + if ((data) && (data.profit) && (data.profit.sum)) { + short = data.profit.sum + } + else { + short = unquoted + } + return '
'+short+'
' + }, + }, + { + targets: [4], + render: function ( data, type, row ) { + return '
'+data+'
' + }, + }, + { + targets: [13,14,15], + 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']], + select: { + info: true, + style: 'multi', + //selector: 'tbody > tr:not(.group-header)' + selector: 'tbody > tr:not(.group-header)' + }, + paging: true, + // lengthChange: false, + // select: true, + // createdRow: function( row, data, dataIndex){ + // if (is_running(data.id) ){ + // alert("runner"); + // $(row).addClass('highlight'); + // } + //} + // Add row grouping based on 'batch_id' + //TODO projit a zrevidovat - pripadne optimalizovat + //NOTE zde jse skoncil + rowGroup: { + dataSrc: 'batch_id', + //toto je volano pri renderovani headeru grupy + startRender: function (rows, group) { + var firstRowData = rows.data()[0]; + //pro no-batch-id je idcko prvni id + var groupId = group ? group : 'no-batch-id-' + firstRowData.id; + var stateKey = 'dt-group-state-' + groupId; + var state = localStorage.getItem(stateKey); + + // Iterate over each row in the group to set the data attribute + // zaroven pro kazdy node nastavime viditelnost podle nastaveni + rows.every(function (rowIdx, tableLoop, rowLoop) { + var rowNode = $(this.node()); + rowNode.attr('data-group-name', groupId); + if (state == 'collapsed') { + rowNode.hide(); + } else { + rowNode.show(); + } + }); + + // Initialize variables for the group + var itemCount = 0; + var period = ''; + var profit = ''; + var started = null; + var stratinId = null; + + // // Process each item only once + // archiveRecords.rows({ search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop) { + // var data = this.data(); + + // if ((group && data.batch_id == group)) { + // itemCount++; + // if (itemCount === 1 ) { + // firstNote = data.note ? data.note.substring(0, 14) : ''; + + // if (data.note) { + // better_counter = extractNumbersFromString(data.note); + // } + // try { + // profit = data.metrics.profit.batch_sum_profit; + // } catch (e) { + // profit = 'N/A'; + // } + // } + // } + // }); + + + //pokud mame batch_id podivame se zda jeho nastaveni uz nema a pokud ano pouzijeme to + //pokud nemame tak si ho loadneme + if (group) { + const existingBatch = batchHeaders.find(batch => batch.batch_id == group); + //jeste neni v poli batchu - udelame hlavicku + if (!existingBatch) { + itemCount = extractNumbersFromString(firstRowData.note); + profit = firstRowData.metrics.profit.batch_sum_profit; + period = firstRowData.note ? firstRowData.note.substring(0, 14) : ''; + started = firstRowData.started + stratinId = firstRowData.strat_id + var newBatchHeader = {batch_id:group, profit:profit, itemCount:itemCount, period:period, started:started, stratinId:stratinId} + batchHeaders.push(newBatchHeader) + } + //uz je v poli, ale mame novejsi (pribyl v ramci backtestu napr.) - updatujeme + else if (new Date(existingBatch.started) < new Date(firstRowData.started)) { + itemCount = extractNumbersFromString(firstRowData.note); + profit = firstRowData.metrics.profit.batch_sum_profit; + period = firstRowData.note ? firstRowData.note.substring(0, 14) : ''; + started = firstRowData.started + stratinId = firstRowData.id + existingBatch.itemCount = itemCount; + existingBatch.profit = profit; + existingBatch.period = period; + existingBatch.started = started; + } + //uz je v poli batchu vytahneme + else { + profit = existingBatch.profit + itemCount = existingBatch.itemCount + period = existingBatch.period + started = existingBatch.started + stratinId = existingBatch.stratinId + } + } + + //zaroven nastavime u vsech childu + + // Construct the GROUP HEADER - sem pripadna tlačítka atp. + //var groupHeaderContent = '' + (group ? 'Batch ID: ' + group : 'No Batch') + ''; + var tools = '' + var icon = '' + icon_color = '' + profit_icon_color = '' + exp_coll_icon_name = '' + exp_coll_icon_name = (state == 'collapsed') ? 'expand_more' : 'expand_less' + if (group) { + tools = '' + tools += 'lab_profile' + tools += 'delete' + tools += 'csv' + tools += 'insert_drive_file' + tools += 'cut' + //final closure + tools += '' + icon_color = getColorForId(stratinId) + profit_icon_color = (profit>0) ? "#4f8966" : "#bb2f5e" //"#d42962" + } + else { + //def color for no batch - semi transparent + icon_color = "#ced4da17" + } + icon = ''+exp_coll_icon_name+'' + + //console.log(group, groupId, stratinId) + //var groupHeaderContent = ''+(group ? 'Batch ID: ' + group: 'No Batch')+''; + var groupHeaderContent = ''+ icon + (group ? 'Batch ID: ' + group: 'No Batch')+''; + groupHeaderContent += (group ? ' (' + itemCount + ')' + ' ' + period + ' Profit: ' + profit + '' : ''); + groupHeaderContent += group ? tools : "" + return $('') + .append('' + groupHeaderContent + '') + .attr('data-name', groupId) + .addClass('group-header') + .addClass(state); + } + }, + // drawCallback: function (settings) { + // var api = this.api(); + // var rows = api.rows({ page: 'current' }).nodes(); + + // api.column(17, { page: 'current' }).data().each(function (group, i) { + // console.log("drawCallabck i",i) + // console.log("rows", $(rows).eq(i)) + // var groupName = group ? group : $(rows).eq(i).attr('data-name'); + // console.log("groupName", groupName) + // var stateKey = 'dt-group-state-' + groupName; + // var state = localStorage.getItem(stateKey); + + // if (state === 'collapsed') { + // $(rows).eq(i).hide(); + // } else { + // $(rows).eq(i).show(); + // } + + // // Set the unique identifier as a data attribute on each row + // //$(rows).eq(i).attr('data-group-name', groupName); + + // // // Add or remove the 'collapsed' class based on the state + // // if (groupName.startsWith('no-batch-id-')) { + // // $('tr[data-name="' + groupName + '"]').toggleClass('collapsed', state === 'collapsed'); + // // } + // }); + // } +}); + diff --git a/v2realbot/static/js/tables/archivetable/modals.js b/v2realbot/static/js/tables/archivetable/modals.js new file mode 100644 index 0000000..c3f812b --- /dev/null +++ b/v2realbot/static/js/tables/archivetable/modals.js @@ -0,0 +1,55 @@ +//mozna dat do dokument ready a rozdelit na handlers a funkce + + +//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(); + disable_arch_buttons(); + }, + 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(); + $('#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) + } +}); \ No newline at end of file diff --git a/v2realbot/static/js/utils/colors.js b/v2realbot/static/js/utils/colors.js new file mode 100644 index 0000000..e69de29 diff --git a/v2realbot/static/js/utils/exports.js b/v2realbot/static/js/utils/exports.js new file mode 100644 index 0000000..cc31982 --- /dev/null +++ b/v2realbot/static/js/utils/exports.js @@ -0,0 +1,41 @@ +// Function to convert a JavaScript object to CSV +function convertToCsv(data) { + var csv = ''; + // Get the headers + var headers = Object.keys(data[0]); + csv += headers.join(',') + '\n'; + + // Iterate over the data + data.forEach(function (item) { + var row = headers.map(function (header) { + return item[header]; + }); + csv += row.join(',') + '\n'; + }); + + return csv; +} + +//type ("text/csv","application/xml"), filetype (csv), filename +function downloadFile(type, filetype, filename, content) { + var blob = new Blob([content], { type: type }); + var url = window.URL.createObjectURL(blob); + var link = document.createElement("a"); + link.href = url; + link.download = filename +"."+filetype; + link.click(); +} + +// Function to convert a JavaScript object to XML +function convertToXml(data) { + var xml = '\n\n'; + data.forEach(function (item) { + xml += ' \n'; + Object.keys(item).forEach(function (key) { + xml += ' <' + key + '>' + item[key] + '\n'; + }); + xml += ' \n'; + }); + xml += ''; + return xml; +} \ No newline at end of file diff --git a/v2realbot/static/js/utils.js b/v2realbot/static/js/utils/utils.js similarity index 99% rename from v2realbot/static/js/utils.js rename to v2realbot/static/js/utils/utils.js index 4ba77be..7c6118e 100644 --- a/v2realbot/static/js/utils.js +++ b/v2realbot/static/js/utils/utils.js @@ -1,3 +1,5 @@ +//general util functions all accross + API_KEY = localStorage.getItem("api-key") var chart = null diff --git a/v2realbot/static/main.css b/v2realbot/static/main.css index 101ffc7..8878409 100644 --- a/v2realbot/static/main.css +++ b/v2realbot/static/main.css @@ -280,6 +280,16 @@ table.dataTable thead>tr>th.sorting_asc:before, table.dataTable thead>tr>th.sort /* font-weight: bold; */ } +/* Hide all .batchtool_ elements by default */ +.batchtool { + display: none; +} + +/* Show .batchtool elements when hovering over a .group-header row */ +.group-header:hover .batchtool { + display: inline-block; /* or whatever display type suits your layout */ +} + .group-header .batchheader-profit-info { color: #3e999e; /* Highlight profit info */ /* font-weight: bold; */