bugfixy ,rozdeleni js do souboru,rozsireni runneru
This commit is contained in:
@ -357,7 +357,7 @@ def next(data, state: StrategyState):
|
||||
|
||||
#HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru
|
||||
lp = state.interface.get_last_price(symbol=state.symbol)
|
||||
state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),2)} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)} DEF:{str(is_defensive_mode())}", last_price=lp, stratvars=state.vars)
|
||||
state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)} DEF:{str(is_defensive_mode())}", last_price=lp, stratvars=state.vars)
|
||||
|
||||
#maxSlopeMA = -0.03
|
||||
#SLOPE ANGLE PROTECTIONs
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -72,6 +72,10 @@ class RunnerView(BaseModel):
|
||||
run_name: Optional[str] = None
|
||||
run_note: Optional[str] = None
|
||||
run_account: Account
|
||||
run_trade_count: Optional[int] = 0
|
||||
run_profit: Optional[float] = 0
|
||||
run_positions: Optional[int] = 0
|
||||
run_avgp: Optional[float] = 0
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
|
||||
@ -83,6 +87,10 @@ class Runner(BaseModel):
|
||||
run_account: Account
|
||||
run_name: Optional[str] = None
|
||||
run_note: Optional[str] = None
|
||||
run_trade_count: Optional[int]
|
||||
run_profit: Optional[float]
|
||||
run_positions: Optional[int]
|
||||
run_avgp: Optional[float]
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
run_thread: Optional[object] = None
|
||||
|
||||
@ -34,7 +34,12 @@ def get_all_threads():
|
||||
|
||||
def get_all_runners():
|
||||
if len(db.runners) > 0:
|
||||
print(db.runners)
|
||||
#print(db.runners)
|
||||
for i in db.runners:
|
||||
i.run_profit = round(i.run_instance.state.profit,2)
|
||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||
i.run_positions = i.run_instance.state.positions
|
||||
i.run_avgp = round(i.run_instance.state.avgp,3)
|
||||
return (0, db.runners)
|
||||
else:
|
||||
return (0, [])
|
||||
@ -54,6 +59,10 @@ def get_stratin(id: UUID):
|
||||
def get_runner(id: UUID):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
i.run_profit = round(i.run_instance.state.profit,2)
|
||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||
i.run_positions = i.run_instance.state.positions
|
||||
i.run_avgp = round(i.run_instance.state.avgp,3)
|
||||
return (0, i)
|
||||
return (-2, "not found")
|
||||
|
||||
|
||||
@ -8,11 +8,13 @@
|
||||
<link rel="manifest" href="/static/site.webmanifest">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="/static/js/jquery.dataTables.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/main.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mousetrap/1.4.6/mousetrap.min.js"></script>
|
||||
<!-- <script src="https://cdn.datatables.net/select/1.6.2/js/dataTables.select.min.js"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" class="mainConteiner flex-container">
|
||||
@ -29,6 +31,13 @@
|
||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="statusHeader" data-toggle="collapse" data-target="#statusStratvars">
|
||||
<div id="statusRegime" class="headerItem"></div>
|
||||
<div id="statusName" class="headerItem"></div>
|
||||
<div id="statusMode" class="headerItem"></div>
|
||||
<div id="statusAccount" class="headerItem"></div>
|
||||
<pre id="statusStratvars" class="headerItem collapse"></pre>
|
||||
</div>
|
||||
<div id="chart" style="display: None; float: left; "></div>
|
||||
<div class="legend" id="legend"></div>
|
||||
<div id="msgContainer">
|
||||
@ -70,7 +79,11 @@
|
||||
<th>Started</th>
|
||||
<th>Mode</th>
|
||||
<th>Account</th>
|
||||
<th>Paused</th>
|
||||
<th>Paused</th>
|
||||
<th>Profit</th>
|
||||
<th>Trades</th>
|
||||
<th>Pos</th>
|
||||
<th>AVGP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
@ -362,9 +375,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/static/js/mywebsocket.js"></script>
|
||||
<script src="/static/js/mychart.js"></script>
|
||||
<script src="/static/js/mytables.js"></script>
|
||||
<script src="/static/js/jquery.serializejson.js"></script>
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/archivechart.js"></script>
|
||||
<script src="/static/js/archivetables.js"></script>
|
||||
<script src="/static/js/livewebsocket.js"></script>
|
||||
<script src="/static/js/realtimechart.js"></script>
|
||||
<script src="/static/js/mytables.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
296
v2realbot/static/js/archivechart.js
Normal file
296
v2realbot/static/js/archivechart.js
Normal file
@ -0,0 +1,296 @@
|
||||
var tradeDetails = new Map();
|
||||
var toolTip = null
|
||||
//TRANSFORM object returned from RESTA PI get_arch_run_detail
|
||||
//to series and markers required by lightweigth chart
|
||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
||||
//output array [{ time: 111, open: 11, high: 33, low: 333, close: 333},..]
|
||||
function transform_data(data) {
|
||||
transformed = []
|
||||
//get basic bars, volume and vvwap
|
||||
var bars = []
|
||||
var volume = []
|
||||
var vwap = []
|
||||
data.bars.time.forEach((element, index, array) => {
|
||||
sbars = {};
|
||||
svolume = {};
|
||||
svwap = {};
|
||||
|
||||
sbars["time"] = element;
|
||||
sbars["close"] = data.bars.close[index]
|
||||
sbars["open"] = data.bars.open[index]
|
||||
sbars["high"] = data.bars.high[index]
|
||||
sbars["low"] = data.bars.low[index]
|
||||
|
||||
|
||||
svwap["time"] = element
|
||||
svwap["value"] = data.bars.vwap[index]
|
||||
|
||||
svolume["time"] = element
|
||||
svolume["value"] = data.bars.volume[index]
|
||||
|
||||
bars.push(sbars)
|
||||
vwap.push(svwap)
|
||||
volume.push(svolume)
|
||||
});
|
||||
transformed["bars"] = bars
|
||||
transformed["vwap"] = vwap
|
||||
transformed["volume"] = volume
|
||||
|
||||
//get markers - avgp line for all buys
|
||||
var avgp_buy_line = []
|
||||
var avgp_markers = []
|
||||
var markers = []
|
||||
var markers_line = []
|
||||
data.trades.forEach((trade, index, array) => {
|
||||
obj = {};
|
||||
a_markers = {}
|
||||
timestamp = Date.parse(trade.order.filled_at)/1000
|
||||
if (trade.order.side == "buy") {
|
||||
//line pro avgp markers
|
||||
obj["time"] = timestamp;
|
||||
obj["value"] = trade.pos_avg_price;
|
||||
avgp_buy_line.push(obj)
|
||||
|
||||
//avgp markers pro prumernou cenu aktualnich pozic
|
||||
a_markers["time"] = timestamp
|
||||
a_markers["position"] = "aboveBar"
|
||||
a_markers["color"] = "#e8c76d"
|
||||
a_markers["shape"] = "arrowDown"
|
||||
a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
|
||||
avgp_markers.push(a_markers)
|
||||
}
|
||||
|
||||
//buy sell markery
|
||||
marker = {}
|
||||
marker["time"] = timestamp;
|
||||
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
||||
marker["position"] = (trade.order.side == "buy") ? "inBar" : "aboveBar"
|
||||
marker["color"] = (trade.order.side == "buy") ? "blue" : "red"
|
||||
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||
marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown"
|
||||
marker["text"] = trade.qty + " " + trade.price
|
||||
markers.push(marker)
|
||||
|
||||
//prevedeme iso data na timestampy
|
||||
trade.order.submitted_at = Date.parse(trade.order.submitted_at)/1000
|
||||
trade.order.filled_at = Date.parse(trade.order.filled_at)/1000
|
||||
trade.timestamp = Date.parse(trade.order.timestamp)/1000
|
||||
tradeDetails.set(timestamp, trade)
|
||||
|
||||
//line pro buy/sell markery
|
||||
mline = {}
|
||||
mline["time"] = timestamp
|
||||
mline["value"] = trade.price
|
||||
markers_line.push(mline)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
|
||||
|
||||
});
|
||||
transformed["avgp_buy_line"] = avgp_buy_line
|
||||
transformed["markers"] = markers
|
||||
transformed["markers_line"] = markers_line
|
||||
transformed["avgp_markers"] = avgp_markers
|
||||
//get additional indicators
|
||||
//TBD
|
||||
return transformed
|
||||
}
|
||||
|
||||
//render chart of archived runs
|
||||
function chart_archived_run(archRecord, data) {
|
||||
if (chart !== null) {
|
||||
chart.remove()
|
||||
clear_status_header()
|
||||
if (toolTip !== null) {
|
||||
toolTip.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("inside")
|
||||
var transformed_data = transform_data(data)
|
||||
//console.log(transformed_data)
|
||||
//tbd transform indicators
|
||||
//var markersData = transform_trades(data)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
document.getElementById("chart").style.display = "block"
|
||||
//initialize chart
|
||||
var chartOptions = { width: 1300, height: 600, leftPriceScale: {visible: true}}
|
||||
chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
||||
}})
|
||||
var archCandlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
||||
archCandlestickSeries.priceScale().applyOptions({
|
||||
scaleMargins: {
|
||||
top: 0.1, // highest point of the series will be 10% away from the top
|
||||
bottom: 0.4, // lowest point will be 40% away from the bottom
|
||||
},
|
||||
});
|
||||
|
||||
var archVwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
var archVolumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||
archVolumeSeries.priceScale().applyOptions({
|
||||
// set the positioning of the volume series
|
||||
scaleMargins: {
|
||||
top: 0.7, // highest point of the series will be 70% away from the top
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
archVwapSeries.setData(transformed_data["vwap"])
|
||||
archCandlestickSeries.setData(transformed_data["bars"])
|
||||
archVolumeSeries.setData(transformed_data["volume"])
|
||||
|
||||
|
||||
var avgBuyLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
color: '#e8c76d',
|
||||
// color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
|
||||
|
||||
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
|
||||
|
||||
var markersLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
// color: '#d6d1c3',
|
||||
color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
markersLine.setData(transformed_data["markers_line"]);
|
||||
|
||||
//console.log("markers")
|
||||
//console.log(transformed_data["markers"])
|
||||
|
||||
markersLine.setMarkers(transformed_data["markers"])
|
||||
|
||||
|
||||
|
||||
//TBD dynamicky
|
||||
//pokud je nazev atributu X_candles vytvorit candles
|
||||
//pokud je objekt Y_line pak vytvorit lajnu
|
||||
//pokud je objekt Z_markers pak vytvorit markers
|
||||
//pokud je Z = X nebo Y, pak markers dat na danou lajnu (priklad vvwap_line, avgp_line, avgp_markers)
|
||||
//udelat si nahodny vyber barev z listu
|
||||
|
||||
//DO BUDOUCNA MARKERS
|
||||
// chart.subscribeCrosshairMove(param => {
|
||||
// console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
|
||||
//define tooltip
|
||||
const container1 = document.getElementById('chart');
|
||||
|
||||
const toolTipWidth = 90;
|
||||
const toolTipHeight = 90;
|
||||
const toolTipMargin = 15;
|
||||
|
||||
// Create and style the tooltip html element
|
||||
toolTip = document.createElement('div');
|
||||
//width: 90px; , height: 80px;
|
||||
toolTip.style = `position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`;
|
||||
toolTip.style.background = 'white';
|
||||
toolTip.style.color = 'black';
|
||||
toolTip.style.borderColor = '#2962FF';
|
||||
container1.appendChild(toolTip);
|
||||
|
||||
|
||||
//TODO onlick zkopirovat timestamp param.time
|
||||
// chart.subscribeClick(param => {
|
||||
// $('#trade-timestamp').val(param.time)
|
||||
// //alert(JSON.safeStringify(param))
|
||||
// //console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
//chart.subscribeCrosshairMove(param => {
|
||||
|
||||
chart.subscribeClick(param => {
|
||||
$('#trade-timestamp').val(param.time)
|
||||
if (
|
||||
param.point === undefined ||
|
||||
!param.time ||
|
||||
param.point.x < 0 ||
|
||||
param.point.x > container1.clientWidth ||
|
||||
param.point.y < 0 ||
|
||||
param.point.y > container1.clientHeight
|
||||
) {
|
||||
toolTip.style.display = 'none';
|
||||
} else {
|
||||
//vyber serie s jakou chci pracovat - muzu i dynamicky
|
||||
//je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams
|
||||
|
||||
//key = series (key.seriestype vraci Line/Candlestick atp.) https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap
|
||||
|
||||
toolTip.style.display = 'none';
|
||||
toolTip.innerHTML = "";
|
||||
var data = param.seriesData.get(markersLine);
|
||||
if (data !== undefined) {
|
||||
//param.seriesData.forEach((value, key) => {
|
||||
//console.log("key",key)
|
||||
//console.log("value",value)
|
||||
|
||||
//data = value
|
||||
//DOCASNE VYPNUTO
|
||||
toolTip.style.display = 'block';
|
||||
|
||||
//console.log(JSON.safeStringify(key))
|
||||
if (toolTip.innerHTML == "") {
|
||||
toolTip.innerHTML = `<div>${param.time}</div>`
|
||||
}
|
||||
var price = data.value
|
||||
// !== undefined ? data.value : data.close;
|
||||
|
||||
|
||||
toolTip.innerHTML += `<pre>${JSON.stringify(tradeDetails.get(param.time),null,2)}</pre><div>${price.toFixed(3)}</div>`;
|
||||
|
||||
//inspirace
|
||||
// toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
|
||||
// ${Math.round(100 * price) / 100}
|
||||
// </div><div style="color: ${'black'}">
|
||||
// ${dateStr}
|
||||
// </div>`;
|
||||
|
||||
|
||||
// Position tooltip according to mouse cursor position
|
||||
toolTip.style.left = param.point.x+120 + 'px';
|
||||
toolTip.style.top = param.point.y-100 + 'px';
|
||||
}
|
||||
//});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//add status
|
||||
$("#statusRegime").text("ARCHIVED RUN")
|
||||
$("#statusName").text(archRecord.name)
|
||||
$("#statusMode").text(archRecord.mode)
|
||||
$("#statusAccount").text(archRecord.account)
|
||||
$("#statusStratvars").text(JSON.stringify(archRecord.stratvars,null,2))
|
||||
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
|
||||
//TBD other dynamically created indicators
|
||||
|
||||
}
|
||||
135
v2realbot/static/js/archivetables.js
Normal file
135
v2realbot/static/js/archivetables.js
Normal file
@ -0,0 +1,135 @@
|
||||
//ARCHIVE TABLES
|
||||
$(document).ready(function () {
|
||||
archiveRecords.ajax.reload();
|
||||
|
||||
//disable buttons (enable on row selection)
|
||||
$('#button_show_arch').attr('disabled','disabled');
|
||||
$('#button_delete_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_delete_arch').attr('disabled','disabled');
|
||||
} else {
|
||||
stratinRecords.$('tr.selected').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
$('#button_show_arch').attr('disabled',false);
|
||||
$('#button_delete_arch').attr('disabled',false);
|
||||
}
|
||||
});
|
||||
|
||||
//delete button
|
||||
$('#button_delete_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
window.$('#delModalArchive').modal('show');
|
||||
$('#delidarchive').val(row.id);
|
||||
});
|
||||
|
||||
|
||||
//show button
|
||||
$('#button_show_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
$('#button_show_arch').attr('disabled',true);
|
||||
$.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);
|
||||
//$('#chartArchive').append(JSON.stringify(data,null,2));
|
||||
console.log(JSON.stringify(data,null,2));
|
||||
chart_archived_run(row, 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);
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
//delete modal
|
||||
$("#delModalArchive").on('submit','#delFormArchive', function(event){
|
||||
event.preventDefault();
|
||||
$('#deletearchive').attr('disabled','disabled');
|
||||
id = $('#delidarchive').val()
|
||||
//var formData = $(this).serializeJSON();
|
||||
$.ajax({
|
||||
url:"/archived_runners/"+id,
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
method:"DELETE",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success:function(data){
|
||||
$('#delFormArchive')[0].reset();
|
||||
window.$('#delModalArchive').modal('hide');
|
||||
$('#deletearchive').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));
|
||||
$('#deletearchive').attr('disabled', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
//archive table
|
||||
var archiveRecords =
|
||||
$('#archiveTable').DataTable( {
|
||||
ajax: {
|
||||
url: '/archived_runners/',
|
||||
dataSrc: '',
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
error: function(xhr, status, error) {
|
||||
//var err = eval("(" + xhr.responseText + ")");
|
||||
//window.alert(JSON.stringify(xhr));
|
||||
console.log(JSON.stringify(xhr));
|
||||
}
|
||||
},
|
||||
columns: [{ data: 'id' },
|
||||
{data: 'name'},
|
||||
{data: 'note'},
|
||||
{data: 'started'},
|
||||
{data: 'stopped'},
|
||||
{data: 'mode'},
|
||||
{data: 'account', visible: true},
|
||||
{data: 'bt_from', visible: true},
|
||||
{data: 'bt_to', visible: true},
|
||||
{data: 'stratvars', visible: true},
|
||||
{data: 'profit'},
|
||||
{data: 'trade_count', visible: true},
|
||||
{data: 'end_positions', visible: true},
|
||||
{data: 'end_positions_avgp', visible: true},
|
||||
{data: 'open_orders', visible: true}
|
||||
],
|
||||
columnDefs: [{
|
||||
targets: [3,4,7,8],
|
||||
render: function ( data, type, row ) {
|
||||
return format_date(data)
|
||||
},
|
||||
}],
|
||||
order: [[4, 'desc']],
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
// createdRow: function( row, data, dataIndex){
|
||||
// if (is_running(data.id) ){
|
||||
// alert("runner");
|
||||
// $(row).addClass('highlight');
|
||||
// }
|
||||
//}
|
||||
} );
|
||||
@ -6,6 +6,37 @@ var logcnt = 0
|
||||
var positionsPriceLine = null
|
||||
var limitkaPriceLine = null
|
||||
var angleSeries = 1
|
||||
var candlestickSeries
|
||||
var volumeSeries
|
||||
var vwapSeries
|
||||
|
||||
//get details of runner to populate chart status
|
||||
//fetch necessary - it could be initiated by manually inserting runnerId
|
||||
function populate_rt_status_header(runnerId) {
|
||||
$.ajax({
|
||||
url:"/runners/"+runnerId,
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
method:"GET",
|
||||
contentType: "application/json",
|
||||
success:function(data){
|
||||
console.log(JSON.stringify(data))
|
||||
//add status on chart
|
||||
$("#statusRegime").text("REALTIME")
|
||||
$("#statusName").text(data.run_name)
|
||||
$("#statusMode").text(data.run_mode)
|
||||
$("#statusAccount").text(data.run_account)
|
||||
//$("#statusStratvars").text(JSON.stringify(data.stratvars,null,2))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
var err = eval("(" + xhr.responseText + ")");
|
||||
window.alert(JSON.stringify(xhr));
|
||||
console.log(JSON.stringify(xhr));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function connect(event) {
|
||||
var runnerId = document.getElementById("runnerId")
|
||||
@ -21,6 +52,7 @@ function connect(event) {
|
||||
document.getElementById("bt-disc").style.display = "initial"
|
||||
document.getElementById("bt-conn").style.display = "none"
|
||||
document.getElementById("chart").style.display = "block"
|
||||
populate_rt_status_header(runnerId.value)
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var parsed_data = JSON.parse(event.data)
|
||||
@ -1,67 +1,3 @@
|
||||
|
||||
API_KEY = localStorage.getItem("api-key")
|
||||
var chart = null
|
||||
//Date.prototype.toJSON = function(){ return Date.parse(this)/1000 }
|
||||
|
||||
// safely handles circular references https://stackoverflow.com/questions/11616630/how-can-i-print-a-circular-structure-in-a-json-like-format
|
||||
JSON.safeStringify = (obj, indent = 2) => {
|
||||
let cache = [];
|
||||
const retVal = JSON.stringify(
|
||||
obj,
|
||||
(key, value) =>
|
||||
typeof value === "object" && value !== null
|
||||
? cache.includes(value)
|
||||
? undefined // Duplicate reference found, discard key
|
||||
: cache.push(value) && value // Store value in our collection
|
||||
: value,
|
||||
indent
|
||||
);
|
||||
cache = null;
|
||||
return retVal;
|
||||
};
|
||||
|
||||
// Iterate through each element in the
|
||||
// first array and if some of them
|
||||
// include the elements in the second
|
||||
// array then return true.
|
||||
function findCommonElements3(arr1, arr2) {
|
||||
return arr1.some(item => arr2.includes(item))
|
||||
}
|
||||
|
||||
function set_timestamp(timestamp) {
|
||||
//console.log(timestamp);
|
||||
$('#trade-timestamp').val(timestamp);
|
||||
}
|
||||
|
||||
//KEY shortcuts
|
||||
Mousetrap.bind('e', function() {
|
||||
$( "#button_edit" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('a', function() {
|
||||
$( "#button_add" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('d', function() {
|
||||
$( "#button_dup" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('c', function() {
|
||||
$( "#button_copy" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('r', function() {
|
||||
$( "#button_run" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('p', function() {
|
||||
$( "#button_pause" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('s', function() {
|
||||
$( "#button_stop" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('j', function() {
|
||||
$( "#button_add_json" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('x', function() {
|
||||
$( "#button_delete" ).trigger( "click" );
|
||||
});
|
||||
|
||||
//on button
|
||||
function store_api_key(event) {
|
||||
key = document.getElementById("api-key").value;
|
||||
@ -101,456 +37,7 @@ function is_running(id) {
|
||||
});
|
||||
return running
|
||||
}
|
||||
// alert(JSON.stringify(stratinRecords.data()))
|
||||
// arr = stratinRecords.data()
|
||||
// foreach(row in arr.rows) {
|
||||
// alert(row.id)
|
||||
// }
|
||||
|
||||
// //let obj = arr.find(o => o.id2 === '2');
|
||||
// //console.log(obj);
|
||||
// //alert(JSON.stringify(obj))
|
||||
|
||||
var tradeDetails = new Map();
|
||||
//CHART ARCHIVED RUN - move to own file
|
||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
||||
//output array [{ time: 111, open: 11, high: 33, low: 333, close: 333},..]
|
||||
function transform_data(data) {
|
||||
transformed = []
|
||||
//get basic bars, volume and vvwap
|
||||
var bars = []
|
||||
var volume = []
|
||||
var vwap = []
|
||||
data.bars.time.forEach((element, index, array) => {
|
||||
sbars = {};
|
||||
svolume = {};
|
||||
svwap = {};
|
||||
|
||||
sbars["time"] = element;
|
||||
sbars["close"] = data.bars.close[index]
|
||||
sbars["open"] = data.bars.open[index]
|
||||
sbars["high"] = data.bars.high[index]
|
||||
sbars["low"] = data.bars.low[index]
|
||||
|
||||
|
||||
svwap["time"] = element
|
||||
svwap["value"] = data.bars.vwap[index]
|
||||
|
||||
svolume["time"] = element
|
||||
svolume["value"] = data.bars.volume[index]
|
||||
|
||||
bars.push(sbars)
|
||||
vwap.push(svwap)
|
||||
volume.push(svolume)
|
||||
});
|
||||
transformed["bars"] = bars
|
||||
transformed["vwap"] = vwap
|
||||
transformed["volume"] = volume
|
||||
|
||||
//get markers - avgp line for all buys
|
||||
var avgp_buy_line = []
|
||||
var avgp_markers = []
|
||||
var markers = []
|
||||
var markers_line = []
|
||||
data.trades.forEach((trade, index, array) => {
|
||||
obj = {};
|
||||
a_markers = {}
|
||||
timestamp = Date.parse(trade.order.filled_at)/1000
|
||||
if (trade.order.side == "buy") {
|
||||
//line pro avgp markers
|
||||
obj["time"] = timestamp;
|
||||
obj["value"] = trade.pos_avg_price;
|
||||
avgp_buy_line.push(obj)
|
||||
|
||||
//avgp markers pro prumernou cenu aktualnich pozic
|
||||
a_markers["time"] = timestamp
|
||||
a_markers["position"] = "aboveBar"
|
||||
a_markers["color"] = "#e8c76d"
|
||||
a_markers["shape"] = "arrowDown"
|
||||
a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
|
||||
avgp_markers.push(a_markers)
|
||||
}
|
||||
|
||||
//buy sell markery
|
||||
marker = {}
|
||||
marker["time"] = timestamp;
|
||||
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
||||
marker["position"] = (trade.order.side == "buy") ? "inBar" : "aboveBar"
|
||||
marker["color"] = (trade.order.side == "buy") ? "blue" : "red"
|
||||
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||
marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown"
|
||||
marker["text"] = trade.qty + " " + trade.price
|
||||
markers.push(marker)
|
||||
|
||||
//prevedeme iso data na timestampy
|
||||
trade.order.submitted_at = Date.parse(trade.order.submitted_at)/1000
|
||||
trade.order.filled_at = Date.parse(trade.order.filled_at)/1000
|
||||
trade.timestamp = Date.parse(trade.order.timestamp)/1000
|
||||
tradeDetails.set(timestamp, trade)
|
||||
|
||||
//line pro buy/sell markery
|
||||
mline = {}
|
||||
mline["time"] = timestamp
|
||||
mline["value"] = trade.price
|
||||
markers_line.push(mline)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
|
||||
|
||||
});
|
||||
transformed["avgp_buy_line"] = avgp_buy_line
|
||||
transformed["markers"] = markers
|
||||
transformed["markers_line"] = markers_line
|
||||
transformed["avgp_markers"] = avgp_markers
|
||||
//get additional indicators
|
||||
//TBD
|
||||
return transformed
|
||||
}
|
||||
|
||||
function chart_archived_run(data) {
|
||||
if (chart !== null) {
|
||||
chart.remove()
|
||||
}
|
||||
|
||||
//console.log("inside")
|
||||
var transformed_data = transform_data(data)
|
||||
//console.log(transformed_data)
|
||||
//tbd transform indicators
|
||||
//var markersData = transform_trades(data)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
document.getElementById("chart").style.display = "block"
|
||||
//initialize chart
|
||||
var chartOptions = { width: 1300, height: 600, leftPriceScale: {visible: true}}
|
||||
chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
||||
}})
|
||||
var archCandlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
||||
archCandlestickSeries.priceScale().applyOptions({
|
||||
scaleMargins: {
|
||||
top: 0.1, // highest point of the series will be 10% away from the top
|
||||
bottom: 0.4, // lowest point will be 40% away from the bottom
|
||||
},
|
||||
});
|
||||
|
||||
var archVwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
var archVolumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||
archVolumeSeries.priceScale().applyOptions({
|
||||
// set the positioning of the volume series
|
||||
scaleMargins: {
|
||||
top: 0.7, // highest point of the series will be 70% away from the top
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
archVwapSeries.setData(transformed_data["vwap"])
|
||||
archCandlestickSeries.setData(transformed_data["bars"])
|
||||
archVolumeSeries.setData(transformed_data["volume"])
|
||||
|
||||
|
||||
var avgBuyLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
color: '#e8c76d',
|
||||
// color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
|
||||
|
||||
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
|
||||
|
||||
var markersLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
// color: '#d6d1c3',
|
||||
color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
markersLine.setData(transformed_data["markers_line"]);
|
||||
|
||||
//console.log("markers")
|
||||
//console.log(transformed_data["markers"])
|
||||
|
||||
markersLine.setMarkers(transformed_data["markers"])
|
||||
|
||||
|
||||
|
||||
//TBD dynamicky
|
||||
//pokud je nazev atributu X_candles vytvorit candles
|
||||
//pokud je objekt Y_line pak vytvorit lajnu
|
||||
//pokud je objekt Z_markers pak vytvorit markers
|
||||
//pokud je Z = X nebo Y, pak markers dat na danou lajnu (priklad vvwap_line, avgp_line, avgp_markers)
|
||||
//udelat si nahodny vyber barev z listu
|
||||
|
||||
//DO BUDOUCNA MARKERS
|
||||
// chart.subscribeCrosshairMove(param => {
|
||||
// console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
|
||||
//define tooltip
|
||||
const container1 = document.getElementById('chart');
|
||||
|
||||
const toolTipWidth = 90;
|
||||
const toolTipHeight = 90;
|
||||
const toolTipMargin = 15;
|
||||
|
||||
// Create and style the tooltip html element
|
||||
const toolTip = document.createElement('div');
|
||||
//width: 90px; , height: 80px;
|
||||
toolTip.style = `position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`;
|
||||
toolTip.style.background = 'white';
|
||||
toolTip.style.color = 'black';
|
||||
toolTip.style.borderColor = '#2962FF';
|
||||
container1.appendChild(toolTip);
|
||||
|
||||
|
||||
//TODO onlick zkopirovat timestamp param.time
|
||||
// chart.subscribeClick(param => {
|
||||
// $('#trade-timestamp').val(param.time)
|
||||
// //alert(JSON.safeStringify(param))
|
||||
// //console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
//chart.subscribeCrosshairMove(param => {
|
||||
|
||||
chart.subscribeClick(param => {
|
||||
$('#trade-timestamp').val(param.time)
|
||||
if (
|
||||
param.point === undefined ||
|
||||
!param.time ||
|
||||
param.point.x < 0 ||
|
||||
param.point.x > container1.clientWidth ||
|
||||
param.point.y < 0 ||
|
||||
param.point.y > container1.clientHeight
|
||||
) {
|
||||
toolTip.style.display = 'none';
|
||||
} else {
|
||||
//vyber serie s jakou chci pracovat - muzu i dynamicky
|
||||
//je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams
|
||||
|
||||
//key = series (key.seriestype vraci Line/Candlestick atp.) https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap
|
||||
|
||||
toolTip.style.display = 'none';
|
||||
toolTip.innerHTML = "";
|
||||
var data = param.seriesData.get(markersLine);
|
||||
if (data !== undefined) {
|
||||
//param.seriesData.forEach((value, key) => {
|
||||
//console.log("key",key)
|
||||
//console.log("value",value)
|
||||
|
||||
//data = value
|
||||
//DOCASNE VYPNUTO
|
||||
toolTip.style.display = 'block';
|
||||
|
||||
//console.log(JSON.safeStringify(key))
|
||||
if (toolTip.innerHTML == "") {
|
||||
toolTip.innerHTML = `<div>${param.time}</div>`
|
||||
}
|
||||
var price = data.value
|
||||
// !== undefined ? data.value : data.close;
|
||||
|
||||
|
||||
toolTip.innerHTML += `<pre>${JSON.stringify(tradeDetails.get(param.time),null,2)}</pre><div>${price.toFixed(3)}</div>`;
|
||||
|
||||
//inspirace
|
||||
// toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
|
||||
// ${Math.round(100 * price) / 100}
|
||||
// </div><div style="color: ${'black'}">
|
||||
// ${dateStr}
|
||||
// </div>`;
|
||||
|
||||
|
||||
// Position tooltip according to mouse cursor position
|
||||
toolTip.style.left = param.point.x+120 + 'px';
|
||||
toolTip.style.top = param.point.y-100 + 'px';
|
||||
}
|
||||
//});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
|
||||
//TBD other dynamically created indicators
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//ARCHIVE TABLES
|
||||
$(document).ready(function () {
|
||||
archiveRecords.ajax.reload();
|
||||
|
||||
//disable buttons (enable on row selection)
|
||||
$('#button_show_arch').attr('disabled','disabled');
|
||||
$('#button_delete_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_delete_arch').attr('disabled','disabled');
|
||||
} else {
|
||||
stratinRecords.$('tr.selected').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
$('#button_show_arch').attr('disabled',false);
|
||||
$('#button_delete_arch').attr('disabled',false);
|
||||
}
|
||||
});
|
||||
|
||||
//delete button
|
||||
$('#button_delete_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
window.$('#delModalArchive').modal('show');
|
||||
$('#delidarchive').val(row.id);
|
||||
});
|
||||
|
||||
|
||||
//show button
|
||||
$('#button_show_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
$('#button_show_arch').attr('disabled',true);
|
||||
$.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);
|
||||
//$('#chartArchive').append(JSON.stringify(data,null,2));
|
||||
console.log(JSON.stringify(data,null,2));
|
||||
chart_archived_run(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);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
//delete modal
|
||||
$("#delModalArchive").on('submit','#delFormArchive', function(event){
|
||||
event.preventDefault();
|
||||
$('#deletearchive').attr('disabled','disabled');
|
||||
id = $('#delidarchive').val()
|
||||
//var formData = $(this).serializeJSON();
|
||||
$.ajax({
|
||||
url:"/archived_runners/"+id,
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
method:"DELETE",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success:function(data){
|
||||
$('#delFormArchive')[0].reset();
|
||||
window.$('#delModalArchive').modal('hide');
|
||||
$('#deletearchive').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));
|
||||
$('#deletearchive').attr('disabled', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
//https://www.w3schools.com/jsref/jsref_tolocalestring.asp
|
||||
function format_date(datum) {
|
||||
//const options = { weekday: 'long', year: 'numeric', month: 'numeric', day: 'numeric', };
|
||||
const options = {dateStyle: "short", timeStyle: "short"}
|
||||
const date = new Date(datum);
|
||||
return date.toLocaleString('cs-CZ', options);
|
||||
}
|
||||
|
||||
//stratin table
|
||||
var archiveRecords =
|
||||
$('#archiveTable').DataTable( {
|
||||
ajax: {
|
||||
url: '/archived_runners/',
|
||||
dataSrc: '',
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
error: function(xhr, status, error) {
|
||||
//var err = eval("(" + xhr.responseText + ")");
|
||||
//window.alert(JSON.stringify(xhr));
|
||||
console.log(JSON.stringify(xhr));
|
||||
}
|
||||
},
|
||||
columns: [{ data: 'id' },
|
||||
{data: 'name'},
|
||||
{data: 'note'},
|
||||
{data: 'started'},
|
||||
{data: 'stopped'},
|
||||
{data: 'mode'},
|
||||
{data: 'account', visible: true},
|
||||
{data: 'bt_from', visible: true},
|
||||
{data: 'bt_to', visible: true},
|
||||
{data: 'stratvars', visible: true},
|
||||
{data: 'profit'},
|
||||
{data: 'trade_count', visible: true},
|
||||
{data: 'end_positions', visible: true},
|
||||
{data: 'end_positions_avgp', visible: true},
|
||||
{data: 'open_orders', visible: true}
|
||||
],
|
||||
columnDefs: [{
|
||||
targets: [3,4,7,8],
|
||||
render: function ( data, type, row ) {
|
||||
return format_date(data)
|
||||
},
|
||||
}],
|
||||
order: [[4, 'desc']],
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
// createdRow: function( row, data, dataIndex){
|
||||
// if (is_running(data.id) ){
|
||||
// alert("runner");
|
||||
// $(row).addClass('highlight');
|
||||
// }
|
||||
//}
|
||||
} );
|
||||
|
||||
//STRATIN and RUNNERS TABELS
|
||||
$(document).ready(function () {
|
||||
@ -903,6 +390,7 @@ $(document).ready(function () {
|
||||
});
|
||||
} );
|
||||
|
||||
|
||||
//stratin table
|
||||
var stratinRecords =
|
||||
$('#stratinTable').DataTable( {
|
||||
@ -930,7 +418,7 @@ var stratinRecords =
|
||||
{data: 'add_data_conf', visible: false},
|
||||
{data: 'note'},
|
||||
{data: 'history', visible: false},
|
||||
{data: 'id', visible: true}
|
||||
{data: 'id', visible: true},
|
||||
],
|
||||
columnDefs: [{
|
||||
targets: 12,
|
||||
@ -941,6 +429,10 @@ var stratinRecords =
|
||||
}],
|
||||
order: [[1, 'asc']],
|
||||
paging: false,
|
||||
// select: {
|
||||
// style: 'multi'
|
||||
// },
|
||||
processing: false
|
||||
// createdRow: function( row, data, dataIndex){
|
||||
// if (is_running(data.id) ){
|
||||
// alert("runner");
|
||||
@ -976,10 +468,17 @@ var runnerRecords =
|
||||
{data: 'run_started'},
|
||||
{data: 'run_mode'},
|
||||
{data: 'run_account'},
|
||||
{data: 'run_paused'}
|
||||
{data: 'run_paused'},
|
||||
{data: 'run_profit'},
|
||||
{data: 'run_trade_count'},
|
||||
{data: 'run_positions'},
|
||||
{data: 'run_avgp'},
|
||||
],
|
||||
paging: false,
|
||||
processing: false
|
||||
processing: false,
|
||||
// select: {
|
||||
// style: 'multi'
|
||||
// },
|
||||
} );
|
||||
|
||||
//modal na run
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
|
||||
//it is called after population
|
||||
|
||||
function populate_real_time_chart() {
|
||||
if (chart !== null) {
|
||||
chart.remove()
|
||||
chart.remove();
|
||||
clear_status_header();
|
||||
}
|
||||
|
||||
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
||||
const chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}}
|
||||
var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}}
|
||||
chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
||||
}})
|
||||
const candlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
||||
candlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
||||
candlestickSeries.priceScale().applyOptions({
|
||||
scaleMargins: {
|
||||
top: 0.1, // highest point of the series will be 10% away from the top
|
||||
@ -20,7 +22,7 @@ function populate_real_time_chart() {
|
||||
});
|
||||
|
||||
|
||||
const volumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||
volumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||
volumeSeries.priceScale().applyOptions({
|
||||
// set the positioning of the volume series
|
||||
scaleMargins: {
|
||||
@ -29,7 +31,7 @@ function populate_real_time_chart() {
|
||||
},
|
||||
});
|
||||
|
||||
const vwapSeries = chart.addLineSeries({
|
||||
vwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
81
v2realbot/static/js/utils.js
Normal file
81
v2realbot/static/js/utils.js
Normal file
@ -0,0 +1,81 @@
|
||||
|
||||
API_KEY = localStorage.getItem("api-key")
|
||||
var chart = null
|
||||
|
||||
// safely handles circular references https://stackoverflow.com/questions/11616630/how-can-i-print-a-circular-structure-in-a-json-like-format
|
||||
JSON.safeStringify = (obj, indent = 2) => {
|
||||
let cache = [];
|
||||
const retVal = JSON.stringify(
|
||||
obj,
|
||||
(key, value) =>
|
||||
typeof value === "object" && value !== null
|
||||
? cache.includes(value)
|
||||
? undefined // Duplicate reference found, discard key
|
||||
: cache.push(value) && value // Store value in our collection
|
||||
: value,
|
||||
indent
|
||||
);
|
||||
cache = null;
|
||||
return retVal;
|
||||
};
|
||||
|
||||
//https://www.w3schools.com/jsref/jsref_tolocalestring.asp
|
||||
function format_date(datum) {
|
||||
//const options = { weekday: 'long', year: 'numeric', month: 'numeric', day: 'numeric', };
|
||||
const options = {dateStyle: "short", timeStyle: "short"}
|
||||
const date = new Date(datum);
|
||||
return date.toLocaleString('cs-CZ', options);
|
||||
}
|
||||
|
||||
|
||||
function clear_status_header() {
|
||||
$("#statusRegime").text("")
|
||||
$("#statusName").text("")
|
||||
$("#statusMode").text("")
|
||||
$("#statusAccount").text("")
|
||||
$("#statusStratvars").text("")
|
||||
//clear previous logs from rt
|
||||
$("#lines").empty()
|
||||
}
|
||||
|
||||
// Iterate through each element in the
|
||||
// first array and if some of them
|
||||
// include the elements in the second
|
||||
// array then return true.
|
||||
function findCommonElements3(arr1, arr2) {
|
||||
return arr1.some(item => arr2.includes(item))
|
||||
}
|
||||
|
||||
function set_timestamp(timestamp) {
|
||||
//console.log(timestamp);
|
||||
$('#trade-timestamp').val(timestamp);
|
||||
}
|
||||
|
||||
//KEY shortcuts
|
||||
Mousetrap.bind('e', function() {
|
||||
$( "#button_edit" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('a', function() {
|
||||
$( "#button_add" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('d', function() {
|
||||
$( "#button_dup" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('c', function() {
|
||||
$( "#button_copy" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('r', function() {
|
||||
$( "#button_run" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('p', function() {
|
||||
$( "#button_pause" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('s', function() {
|
||||
$( "#button_stop" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('j', function() {
|
||||
$( "#button_add_json" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('x', function() {
|
||||
$( "#button_delete" ).trigger( "click" );
|
||||
});
|
||||
@ -85,6 +85,54 @@ pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#statusHeader {
|
||||
margin-left: 55px;
|
||||
font-size: normal;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.headerItem {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
.switcher-item {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
color: #262b3e;
|
||||
background-color: transparent;
|
||||
margin-right: 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.switcher-item:hover {
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.switcher-active-item {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
color: #262b3e;
|
||||
}
|
||||
|
||||
.switcher-active-item,
|
||||
.switcher-active-item:hover {
|
||||
background-color: #e1eff9;
|
||||
}
|
||||
Reference in New Issue
Block a user