Files
v2realbot/v2realbot/static/js/archivechart.js

1225 lines
49 KiB
JavaScript

var tradeDetails = new Map();
var toolTip = null
var CHART_SHOW_TEXT = get_from_config("CHART_SHOW_TEXT", false)
// const myLibrary = _export;
// const TOML = window['j-toml']
//console.log("TOML je", window)
console.log("CHART_SHOW_TEXT archchart", CHART_SHOW_TEXT)
// var vwapSeries = null
// var volumeSeries = null
var markersLine = null
var avgBuyLine = null
var profitLine = null
var slLine = []
//TRANSFORM object returned from REST API 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) {
//console.log(data)
var SHOW_SL_DIGITS = get_from_config("SHOW_SL_DIGITS", true)
transformed = []
//get basic bars, volume and vvwap
var bars = []
var volume = []
var vwap = []
//pokud mame tak projedeme ext_data pro dane klice a s nimi pracujeme
var sl_line = []
var sl_line_markers = []
var sl_line_sada = []
var sl_line_markers_sada = []
//console.log(JSON.stringify(data.ext_data.sl_history, null, 2))
prev_id = 0
//cas of first record, nekdy jsou stejny - musim pridat setinku
prev_cas = 0
if ((data.ext_data !== null) && (data.ext_data.sl_history)) {
data.ext_data.sl_history.forEach((histRecord, index, array) => {
//console.log("plnime")
//nova sada
if (prev_id !== histRecord.id) {
if (prev_id !== 0) {
//push sadu do pole
sl_line.push(sl_line_sada)
sl_line_markers.push(sl_line_markers_sada)
}
//init nova sada
sl_line_sada = []
sl_line_markers_sada = []
}
prev_id = histRecord.id
//prevedeme iso data na timestampy
cas = histRecord.time
if (cas == prev_cas) {
cas = cas + 0.001
}
prev_cas = cas
//line pro buy/sell markery
sline = {}
sline["time"] = cas
sline["value"] = histRecord.sl_val
sl_line_sada.push(sline)
sline_markers = {}
sline_markers["time"] = cas
sline_markers["position"] = "inBar"
sline_markers["color"] = "#f5aa42"
//sline_markers["shape"] = "circle"
//console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
sl_line_markers_sada.push(sline_markers)
if (index === array.length - 1) {
//pro posledni zaznam push sadu do pole
sl_line.push(sl_line_sada)
sl_line_markers.push(sl_line_markers_sada)
}
});
}
//pomocne
var last_time = 0
var time = 0
data.bars.time.forEach((element, index, array) => {
sbars = {};
svolume = {};
svwap = {};
//tento algoritmus z duplicit dela posloupnosti a srovna i pripadne nekonzistence
//napr z .911 .911 .912 udela .911 .912 .913
//TODO - možná dat do backendu agregatoru
if (last_time>=element) {
console.log("bars", "problem v case - zarovnano",time, last_time, element)
data.bars.time[index] = data.bars.time[index-1] + 0.000001
}
last_time = data.bars.time[index]
sbars["time"] = data.bars.time[index];
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"] = data.bars.time[index];
svwap["value"] = data.bars.vwap[index]
svolume["time"] = data.bars.time[index];
svolume["value"] = data.bars.volume[index]
bars.push(sbars)
vwap.push(svwap)
volume.push(svolume)
});
transformed["bars"] = bars
//console.log(bars)
transformed["vwap"] = vwap
transformed["volume"] = volume
var bars = []
var volume = []
var vwap = []
if ((data.ext_data !== null) && (data.ext_data.dailyBars)) {
data.ext_data.dailyBars.time.forEach((element, index, array) => {
sbars = {};
svolume = {};
svwap = {};
sbars["time"] = element;
sbars["close"] = data.ext_data.dailyBars.close[index]
sbars["open"] = data.ext_data.dailyBars.open[index]
sbars["high"] = data.ext_data.dailyBars.high[index]
sbars["low"] = data.ext_data.dailyBars.low[index]
svwap["time"] = element
svwap["value"] = data.ext_data.dailyBars.vwap[index]
svolume["time"] = element
svolume["value"] = data.ext_data.dailyBars.volume[index]
bars.push(sbars)
vwap.push(svwap)
volume.push(svolume)
});
transformed["dailyBars"] = {}
transformed["dailyBars"]["bars"] = bars
transformed["dailyBars"]["vwap"] = vwap
transformed["dailyBars"]["volume"] = volume
var bars = []
var volume = []
var vwap = []
}
//get markers - avgp line for all buys
var avgp_buy_line = []
var avgp_markers = []
var sum_profit_line = []
var markers = []
var markers_line = []
var last_timestamp = 0.1
var iterator = 0.002
data.trades.forEach((trade, index, array) => {
obj = {};
a_markers = {}
//tady z predchozi verze muze byt string (pak je to date v iso) a nebo v novejsi uz mame timestamp
timestamp = (typeof trade.order.filled_at === 'string') ? Date.parse(trade.order.filled_at)/1000 : trade.order.filled_at
//light chart neumi vice zaznamu ve stejny cas
//protoze v BT se muze stat vice tradu v jeden cas, testujeme stejne hodnoty a pripadne pricteme jednu ms
//tradu s jednim casem muze byt za sebou vic, proto iterator
if (last_timestamp > timestamp) {
console.log("NEKONZISTENCE RAZENI",last_timestamp, timestamp)
console.log("aktualni trade", JSON.stringify(trade,null,2))
console.log("předchozí trade", JSON.stringify(data.trades[index-1],null,2))
}
if (last_timestamp == timestamp) {
last_timestamp = timestamp
console.log("DUPLICITA tradu aktual/predchozi/nasledujici", trade, data.trades[index-1], data.trades[index+1])
console.log("původní timestamp je ",timestamp)
timestamp = parseFloat(timestamp) + iterator
console.log("nový timestamp je ",timestamp)
iterator += 0.001
}
else {
last_timestamp = timestamp
iterator = 0.002
}
//AVG BUY LINE - zatim docasne vypiname
if (((trade.order.side == "buy") || (trade.order.side == "sell")) && 1==2) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.pos_avg_price !== null) && (trade.pos_avg_price !== 0)) {
//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"
//if (CHART_SHOW_TEXT)
//a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
//a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
avgp_markers.push(a_markers)
}
}
//PROFITLINE
if ((trade.order.side == "buy") || (trade.order.side == "sell")) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.profit_sum !== null)) {
//line pro avgp markers
obj["time"] = timestamp;
obj["value"] = trade.profit_sum.toFixed(1);
sum_profit_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.profit_sum.toFixed(1);
// //if (CHART_SHOW_TEXT)
// //a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
// avgp_markers.push(a_markers)
}
}
//buy sell markery
marker = {}
marker["time"] = timestamp;
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
//marker["text"] = trade.qty + "/" + trade.price
qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
if (CHART_SHOW_TEXT) {
//včetně qty
//marker["text"] = qt_optimized + "@" + trade.price
//bez qty
marker["text"] = trade.price
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
} else {
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
}
markers.push(marker)
//prevedeme iso data na timestampy
//open bud zde je iso string (predchozi verze) nebo rovnou float - podporime oboji
trade.order.submitted_at = (typeof trade.order.submitted_at === 'string') ? Date.parse(trade.order.submitted_at)/1000 : trade.order.submitted_at
trade.order.filled_at = (typeof trade.order.filled_at === 'string') ? Date.parse(trade.order.filled_at)/1000 : trade.order.filled_at
trade.timestamp = (typeof trade.timestamp === 'string') ? Date.parse(trade.order.timestamp)/1000 : trade.order.timestamp
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),
});
//pro jistotu jeste seradime podle casu
//v BT se muze predbehnout a lightweight to pak nezobrazi
markers.sort(sorter)
markers_line.sort(sorter)
avgp_buy_line.sort(sorter)
avgp_markers.sort(sorter)
sum_profit_line.sort(sorter)
transformed["avgp_buy_line"] = avgp_buy_line
transformed["sum_profit_line"] = sum_profit_line
transformed["avgp_markers"] = avgp_markers
transformed["markers"] = markers
transformed["markers_line"] = markers_line
transformed["sl_line"] = sl_line
transformed["sl_line_markers"] = sl_line_markers
//console.log("naplnene", sl_line, sl_line_markers)
//console_log(JSON.stringify(transformed["sl_line"],null,2))
//console_log(JSON.stringify(transformed["sl_line_markers"],null,2))
//get additional indicators
return transformed
}
//unit: Min, Hour, Day, Week, Month
//prepares data before displaying archived chart - fetch history bars if necessary
function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunnerDetail) {
req = {}
req["symbol"] = archRunner.symbol
if (archRunner.mode == "backtest") {
req["datetime_object_from"] = archRunner.bt_from
req["datetime_object_to"] = archRunner.bt_to
}
else
{
req["datetime_object_from"] = archRunner.started
req["datetime_object_to"] = archRunner.stopped
}
console.log("datum pred",req.datetime_object_from)
//pridame z obou stran 1 minutu - kvuli zobrazeni na frontendu,
req["datetime_object_from"] = subtractMinutes(new Date(req.datetime_object_from),1).toISOString()
req["datetime_object_to"] = addMinutes(new Date(req.datetime_object_to),1).toISOString()
console.log("datum po", req.datetime_object_from)
req["timeframe_amount"] = timeframe_amount
req["timeframe_unit"] = timeframe_unit
$.ajax({
url:"/history_bars/",
beforeSend: function (xhr) {
xhr.setRequestHeader('X-API-Key',
API_KEY); },
method:"GET",
contentType: "application/json",
data: req,
success:function(data){
//console.log("one minute bars before", JSON.stringify(data))
data.map((el)=>{
cas = new Date(el.timestamp)
el.time = cas.getTime()/1000;
delete el.timestamp
});
//console.log("one min bars_after_transformation", JSON.stringify(data))
oneMinuteBars = data
chart_archived_run(archRunner, archivedRunnerDetail, oneMinuteBars);
//call function to continue
//return data
//$("#statusStratvars").text(JSON.stringify(data.stratvars,null,2))
},
error: function(xhr, status, error) {
oneMinuteBars = null
chart_archived_run(archRunner, archivedRunnerDetail, oneMinuteBars);
var err = eval("(" + xhr.responseText + ")");
window.alert(JSON.stringify(xhr));
console.log(JSON.stringify(xhr));
}
})
}
//pomocna sluzba pro naplneni indListu a charting indikatoru
function chart_indicators(data, visible, offset) {
console.log(data)
//console.log("indikatory", JSON.stringify(data.indicators,null,2))
//podobne v livewebsokcets.js - dat do jedne funkce
if (data.hasOwnProperty("indicators")) {
// console.log("jsme uvnitr indikatoru")
//vraci se pole indicatoru, kazdy se svoji casovou osou (time) - nyni standard indikatory a cbar indikatory
var indicatorList = data.indicators
//ze stratvars daneho runnera si dotahneme nastaveni indikatoru - pro zobrazeni v tooltipu
//TOML parse
//console.log("PUVODNI", data.archRecord.stratvars_toml)
var stratvars_toml = TOML.parse(data.archRecord.stratvars_toml)
//console.log("ZPETNE STRINGIFIED", TOML.stringify(TOML.parse(data.archRecord.stratvars_toml), {newline: '\n'}))
//indicatory
//console.log("indicatory TOML", stratvars_toml.stratvars.indicators)
indId = 1
var multiOutsCnf = {}
indicatorList.forEach((indicators, index, array) => {
//var indicators = data.indicators
//index 0 - bar indikatory
//index 1 - tick based indikatory
//if there are indicators it means there must be at least two keys (time which is always present)
if (Object.keys(indicators).length > 1) {
for (const [key, value] of Object.entries(indicators)) {
if (key !== "time") {
//get cnf of indicator to display in the button tooltip
var cnf = null
//pokud je v nastaveni scale, pouzijeme tu
var scale = null
var instant = null
var returns = null
//console.log(key)
//zkusime zda nejde o instantni indikator z arch runneru
if ((data.ext_data !== null) && (data.ext_data.instantindicators)) {
let instantIndicator = data.ext_data.instantindicators.find(indicator => indicator.name == key);
//console.log("nalezen", key)
if (instantIndicator) {
cnf = instantIndicator.toml
scale = TOML.parse(cnf).scale
instant = 1
returns = TOML.parse(cnf).returns
}
}
//pokud nenalezeno, pak bereme standard
//pozor ted nebereme z addedInds
if (!cnf) {
if (stratvars_toml.stratvars.indicators[key]) {
cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
scale = stratvars_toml.stratvars.indicators[key].scale
returns = stratvars_toml.stratvars.indicators[key].returns
}
}
// //kontriolujeme v addedInds
// if (addedInds[key]) {
// cnf = addedInds[key]
// scale = TOML.parse(cnf).scale
// instant = 1
// }
// //a az potom bereme normos
// else
// {
// cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
// scale = stratvars_toml.stratvars.indicators[key].scale
// //cnf = TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
// //a = TOML.parse(cnf)
// //console.log("PARSED again",a)
// }
// }
//pro multioutput childs dotahneme scale z parenta
if (multiOutsCnf.hasOwnProperty(key)) {
scale = multiOutsCnf[key];
}
//initialize indicator and store reference to array
var obj = {name: key, type: index, series: null, cnf:cnf, instant: instant, returns: returns, indId:indId++}
//pokud jde o multioutput parenta ukladam scale parenta pro children
//varianty - scale je jeden, ukladam jako scale pro vsechny parenty
// - scale je list - pouzijeme pro kazdy output scale v listu na stejnem indexu jako output
if (returns) {
returns.forEach((returned, index, array) => {
//
if (Array.isArray(scale)) {
multiOutsCnf[returned] = scale[index]
}
else {
multiOutsCnf[returned] = scale
}
})
} //start
//console.log(key)
//get configuation of indicator to display
conf = get_ind_config(key, index)
//pokud neni v configuraci - zobrazujeme defaultne
//INIT INDICATOR BASED on CONFIGURATION
//DO BUDOUCNA zde udelat sorter a pripadny handling duplicit jako
//funkci do ktere muzu zavolat vse co pujde jako data do chartu
//MOVE TO UTILS ro reuse??
//if (conf && conf.display) {
if (conf && conf.embed) {
//tranform data do správného formátru
items = []
//var last = null
var last_time = 0
var time = 0
//tento algoritmus z duplicit dela posloupnosti a srovna i pripadne nekonzistence
//napr z .911 .911 .912 udela .911 .912 .913
value.forEach((element, index, array) => {
item = {}
//debug
//TOTO odstranit po identifikovani chyby
//if (indicators.time[index] !== undefined) {
//{console.log("problem",key,last)}
time = indicators.time[index]
//pokud je nastaveny offset (zobrazujeme pouze bod vzdaleny N sekund od posledniho)
//vynechavame prvni iteraci, aby se nam naplnil last_time
if (offset && last_time !==0) {
if (last_time + offset > time) {
return;
}
}
if (last_time>=time) {
console.log(key, "problem v case - zarovnano",time, last_time, element)
indicators.time[index] = indicators.time[index-1] + 0.000001
}
item["time"] = indicators.time[index]
item["value"] = element
last_time = indicators.time[index]
if ((element == null) || (indicators.time[index] == null)) {
console.log("probelem u indikatoru",key, "nekonzistence", element, indicators.time[index])
}
//console.log("objekt indicatoru",item)
items.push(item)
//debug
//last = item
// }
// else
// {
// console.log("chybejici cas", key)
// }
});
//SERADIT PRO JISTOTU
//items.sort(sorter)
//FIND DUPLICITIES
// last_time = 0
// items.forEach((element, index, array) => {
// if (last_time >= element.time) {
// console.log("je duplicita/nekonzistence v ", element.time, element.value)
// }
// last_time = element.time
// })
if (conf.embed) {
if (conf.histogram) {
obj.series = chart.addHistogramSeries({
title: (conf.titlevisible?key:""),
color: colors.shift(),
priceFormat: {type: 'volume'},
priceScaleId: (scale)?scale:conf.priceScaleId,
lastValueVisible: conf.lastValueVisible,
visible: conf.display});
obj.series.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,
},
});
}
else {
var barva = colors.shift()
obj.series = chart.addLineSeries({
color: barva,
priceScaleId: (scale)?scale:conf.priceScaleId,
title: (conf.titlevisible?key:""),
lineWidth: 1,
visible: conf.display
});
// //existuje statinds se stejnym klicem - bereme z nej minimum slope
// a = data.statinds[key]
// console.log("pro klic" + key + ":"+a, JSON.stringify(a,null,2))
// console.log(data.statinds[key].minimum_slope)
// console.log(JSON.stringify(data.statinds,null,2))
//console.log((key in data.statinds), data.statinds, key)
if (key in data.statinds) {
//natvrdo nakreslime lajnu pro min angle
//TODO predelat na configuracne
const minSlopeLineOptopns = {
price: data.statinds[key].minimum_slope,
color: barva,
lineWidth: 1,
lineStyle: 2, // LineStyle.Dotted
axisLabelVisible: true,
title: "min",
};
const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns);
const maxSlopeLineOptopns = {
price: data.statinds[key].maximum_slope,
color: barva,
lineWidth: 1,
lineStyle: 2, // LineStyle.Dotted
axisLabelVisible: true,
title: "max",
};
const maxSlopeLine = obj.series.createPriceLine(maxSlopeLineOptopns);
}
}
}
//INDICATOR on new pane
else { console.log("not implemented")}
//console.log("v chartovani",activatedButtons)
//pokud existuje v aktivnich pak zobrazujeme jako aktivni
if ((activatedButtons) && (activatedButtons.includes(obj.name))) {
//console.log("true",active?active:conf.display)
active = true
}
else {active = false}
//pro main s multioutputem nezobrazujeme
if (returns) {
active = false
}
//add options
obj.series.applyOptions({
visible: active?active:visible,
lastValueVisible: false,
priceLineVisible: false,
});
//DEBUG
// if (key == 'tick_price') {
// console.log("problem tu",JSON.stringify(items,null,2))
// }
//add data
obj.series.setData(items)
// add to indList array - pole zobrazovanych indikatoru
indList.push(obj);
}
}
}
}
})
}
//sort by type first (0-bar,1-cbar inds) and then alphabetically
// indList.sort((a, b) => {
// if (a.type !== b.type) {
// return a.type - b.type;
// } else {
// let nameA = a.name.toUpperCase();
// let nameB = b.name.toUpperCase();
// if (nameA < nameB) {
// return -1;
// } else if (nameA > nameB) {
// return 1;
// } else {
// // If uppercase names are equal, compare original names to prioritize uppercase
// return a.name < b.name ? -1 : 1;
// }
// }
// });
//SORTING tak, aby multioutputs atributy byly vzdy na konci dane skupiny (tzn. v zobrazeni jsou zpracovany svými rodiči)
// Step 1: Create a Set of all names in 'returns' arrays
const namesInReturns = new Set();
indList.forEach(item => {
if (Array.isArray(item.returns)) {
item.returns.forEach(name => namesInReturns.add(name));
}
});
// Step 2: Custom sort function
indList.sort((a, b) => {
// First, sort by 'type'
if (a.type !== b.type) {
return a.type - b.type;
}
// For items with the same 'type', apply secondary sorting
const aInReturns = namesInReturns.has(a.name);
const bInReturns = namesInReturns.has(b.name);
if (aInReturns && !bInReturns) return 1; // 'a' goes after 'b'
if (!aInReturns && bInReturns) return -1; // 'a' goes before 'b'
// If both or neither are in 'returns', sort alphabetically by 'name'
return a.name.localeCompare(b.name);
});
//puvodni funkce
// indList.sort((a, b) => {
// const nameA = a.name.toUpperCase(); // ignore upper and lowercase
// const nameB = b.name.toUpperCase(); // ignore upper and lowercase
// if (nameA < nameB) {
// return -1;
// }
// if (nameA > nameB) {
// return 1;
// }
// // names must be equal
// return 0;
// });
//vwap a volume zatim jen v detailnim zobrazeni
if (!offset) {
//display vwap and volume
initialize_vwap()
vwapSeries.setData(data.transformed_data["vwap"])
initialize_volume()
volumeSeries.setData(data.transformed_data["volume"])
console.log("volume")
}
}
//pomocna
function remove_indicators() {
//reset COLORS
colors = reset_colors.slice()
//remove CUSTOMS indicators if exists
indList.forEach((element, index, array) => {
if (element.series) {
//console.log(element.series, "tady series")
chart.removeSeries(element.series);
}
}
);
indList = [];
//remove BASIC indicators
if (vwapSeries) {
chart.removeSeries(vwapSeries)
vwapSeries = null;
}
if (volumeSeries) {
chart.removeSeries(volumeSeries)
volumeSeries = null;
}
}
//switch to interval pomocna funkce
function switch_to_interval(interval, data, extra) {
store_activated_buttons_state(extra);
if (!data) {
window.alert("no data switch to interval")
}
//prip prpenuti prepisujeme candlestick a markery
if (candlestickSeries) {
last_range = chart.timeScale().getVisibleRange()
chart.removeSeries(candlestickSeries);
candlestickSeries = null
}
else {
last_range = null
}
intitialize_candles()
candlestickSeries.setData(data.AllCandleSeriesesData.get(interval));
var containerlower = document.getElementById('lowercontainer');
remove_indicators();
btnElement = document.getElementById("indicatorsButtons")
if (btnElement) {
containerlower.removeChild(btnElement);
}
if (interval == data.native_resolution) {
//indicators are in native resolution only
chart_indicators(data, false);
var indbuttonElement = populate_indicator_buttons(false);
}
else {
//na nepuvodnim grafu zobrazit jako offset a zobrazit jako neviditelne
chart_indicators(data,false,30)
//buttonky jako vypnute
var indbuttonElement = populate_indicator_buttons(false);
}
containerlower.append(indbuttonElement);
display_buy_markers(data);
//TADY JSEM SKONCIL - toto nize predelat na hide button pro display bar markers
// btnElement = document.getElementById("pricelineButtons")
// var indbuttonElement = populate_indicator_buttons(false);
// if (btnElement) {
// container1.removeChild(btnElement);
// }
//container1.append(indbuttonElement);
if (last_range) {
chart.timeScale().setVisibleRange(last_range);
}
}
//displays (redraws) buy markers
function display_buy_markers(data) {
transformed_data = data.transformed_data
// if (profitLine) {
// console.log(profitLine)
// chart.removeSeries(profitLine)
// console.log("nd")
// }
if (avgBuyLine) {
chart.removeSeries(avgBuyLine)
}
if (markersLine) {
chart.removeSeries(markersLine)
}
if (slLine) {
slLine.forEach((series, index, array) => {
chart.removeSeries(series)
})
slLine=[]
}
// if (slLine) {
// chart.removeSeries(slLine)
// }
//console.log("avgp_buy_line",JSON.stringify(transformed_data["avgp_buy_line"],null,2))
//console.log("avgp_markers",JSON.stringify(transformed_data["avgp_markers"],null,2))
//if (transformed_data["sl_line"].length > 0) {
//console.log(JSON.stringify(transformed_data["sl_line"]), null,2)
//xx - ted bude slLine pole
transformed_data["sl_line"].forEach((slRecord, index, array) => {
//console.log("uvnitr")
slLine_temp = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e4c76d',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
slLine_temp.applyOptions({
lastValueVisible: false,
priceLineVisible: false,
});
slLine_temp.setData(slRecord);
slLine_temp.setMarkers(transformed_data["sl_line_markers"][index]);
slLine.push(slLine_temp)
//xx
})
//}
if (transformed_data["sum_profit_line"].length > 0) {
profitLine = chart.addLineSeries({
// title: "avgpbuyline", e8c76d
color: '#99912b',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
profitLine.applyOptions({
lastValueVisible: false,
priceLineVisible: false,
priceScaleId: "own",
visible: false
});
profitLine.setData(transformed_data["sum_profit_line"]);
//profitLine.setMarkers(transformed_data["sum_profit_line_markers"]);
}
if (transformed_data["avgp_buy_line"].length > 0) {
avgBuyLine = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e8c76d',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
avgBuyLine.applyOptions({
lastValueVisible: false,
priceLineVisible: false,
});
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
avgBuyLine.setMarkers(transformed_data["avgp_markers"]);
}
markersLine = chart.addLineSeries({
// title: "avgpbuyline",
// color: '#d6d1c3',
color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
//console.log("markers_line",JSON.stringify(transformed_data["markers_line"],null,2))
//console.log("markers",JSON.stringify(transformed_data["markers"],null,2))
markersLine.setData(transformed_data["markers_line"]);
markersLine.setMarkers(transformed_data["markers"])
const container1 = document.getElementById('chart');
//chart.subscribeCrosshairMove(param => {
chart.subscribeCrosshairMove(param => {
//LEGEND SECTIOIN
firstRow.style.color = 'white';
update_chart_legend(param);
//TOOLTIP SECTION
//$('#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);
var data2 = param.seriesData.get(avgBuyLine);
var profitdata = param.seriesData.get(profitLine);
if ((data !== undefined) || (data2 !== 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>`
// }
buy_price = 0
//u sell markeru nemame avgBuyLine
if (data2 !== undefined) {
buy_price = parseFloat(data2.value).toFixed(3)
}
toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).order.qty}/${data.value}</div>`;
if (profitdata !== undefined) {
toolTip.innerHTML += `<div>P:${parseFloat(profitdata.value).toFixed(1)}</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 + 'px';
}
//});
}
});
}
//render chart of archived runs
function chart_archived_run(archRecord, data, oneMinuteBars) {
cleanup_chart()
var transformed_data = transform_data(data)
data["transformed_data"] = transformed_data
data["archRecord"] = archRecord
//initialize resolutions
data["native_resolution"] = data.bars.resolution[0]+"s"
//console.log("native", native_resolution)
//available intervals zatim jen 1m
var intervals = [data.native_resolution, '1m', '1d'];
var dailyData = null
if (transformed_data["dailyBars"]) {
dailyData = transformed_data["dailyBars"]["bars"]
}
//zkusime daily data dat do minuty
//console.log("daily", dailyData)
nativeData = transformed_data["bars"]
//console.log("native")
//get one minute data
//tbd prepare volume
//console.log("oneMinuteData",oneMinuteBars)
data["AllCandleSeriesesData"] = new Map([
[data.native_resolution, nativeData ],
["1m", dailyData?dailyData.concat(oneMinuteBars):oneMinuteBars],
["1d", dailyData ],
]);
//dame si data do globalni, abychom je mohli pouzivat jinde (trochu prasarna, predelat pak)
archData = data
var switcherElement = createSimpleSwitcher(intervals, intervals[1], switch_to_interval, data);
//define tooltip
const container1 = document.getElementById('chart');
const containerlower = document.getElementById('lowercontainer');
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.backgroundColor = '#2d323e';
toolTip.style.color = '#babfcd';
toolTip.style.borderColor = "#a7a9b0";
//'#2962FF';
container1.appendChild(toolTip);
//initialize chart
document.getElementById("chart").style.display = "block"
initialize_chart()
containerlower.append(switcherElement)
candlestickSeries = null
//v pripade, ze neprojde get bars, nastavit na intervals[0]
switch_to_interval(intervals[1], data)
chart.timeScale().fitContent();
chart.subscribeClick(param => {
$('#trade-timestamp').val(param.time)
//toggle_vertical_line(param.time);
if (archRecord.ilog_save == true) {
fetch_log_data(param.time, archRecord.id);
}
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>`;
//console.log("toolTip.innerHTML",toolTip.innerHTML)
//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
$("#statusArchId").text(archRecord.id)
$("#statusRegime").text("PAST RUN: "+archRecord.id)
$("#statusName").text(archRecord.name)
$("#statusMode").text(archRecord.mode)
$("#statusAccount").text(archRecord.account)
$("#statusIlog").text("Logged:" + archRecord.ilog_save)
$("#statusStratvars").text(((archRecord.strat_json)?archRecord.strat_json:archRecord.stratvars),null,2)
$("#statusSettings").text(JSON.stringify(archRecord.settings,null,2) + JSON.stringify(data.ext_data,null,2))
//mezitim se master zmenil //test /novy test
}
function fetch_log_data(timestamp, runner_id) {
timestamp_from = timestamp - 20
timestamp_to = timestamp + 20
req = {}
req["runner_id"] = runner_id;
req["timestamp_from"] = timestamp_from;
req["timestamp_to"] = timestamp_to;
$.ajax({
url:"/archived_runners_log/"+runner_id,
beforeSend: function (xhr) {
xhr.setRequestHeader('X-API-Key',
API_KEY); },
method:"GET",
contentType: "application/json",
data: req,
success:function(data){
//console.log("archived logs", JSON.stringify(data))
display_log(data, timestamp)
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
//window.alert(JSON.stringify(xhr));
console.log("Chyb pri dotazeni logu", JSON.stringify(xhr));
}
})
}
function display_log(iterLogList, timestamp) {
//console.log("Incoming logline object")
var lines = document.getElementById('lines')
var line = document.createElement('div')
line.classList.add("line")
const newLine = document.createTextNode("---------------")
line.appendChild(newLine)
lines.appendChild(line)
iterLogList.forEach((logLine) => {
//console.log("logline item")
//console.log(JSON.stringify(logLine,null,2))
// <div class="line">
// <div data-toggle="collapse" data-target="#rec1">12233 <strong>Event</strong></div>
// <div id="rec1" class="collapse">
// Detaila mozna structured
// Lorem ipsum dolor sit amet, consectetur adipisicing elit,
// sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
// quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
// </div>
// </div>
highlighted = (parseInt(logLine.time) == parseInt(timestamp)) ? "highlighted" : ""
logcnt++;
hdr = logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message)
hdr = Prism.highlight(hdr, Prism.languages.log, 'log');
row = '<div data-bs-toggle="collapse" class="'+ highlighted + '" onclick="set_timestamp(' + logLine.time + ')" data-bs-target="#rec'+logcnt+'">'
+hdr + '</div>'
str_row = JSON.stringify(logLine.details, null, 2)
//row_detail = '<div id="rec'+logcnt+'" data-toggle="collapse" data-target="#rec'+logcnt+'"class="collapse pidi"><pre>' + str_row + '</pre></div>'
const html = Prism.highlight(str_row, Prism.languages.json, 'json');
//console.log("tady", html)
row_detail = '<div id="rec'+logcnt+'" class="collapse pidi"><pre><code class="language-log">' + html + '</code></pre></div>'
var lines = document.getElementById('lines')
var line = document.createElement('div')
line.classList.add("line")
line.dataset.timestamp = logLine.time
line.insertAdjacentHTML( 'beforeend', row );
line.insertAdjacentHTML( 'beforeend', row_detail );
//line.appendChild(newLine)
//var pre = document.createElement("span")
//pre.classList.add("pidi")
//const stLine = document.createTextNode(str_row)
//pre.appendChild(stLine)
//line.appendChild(pre)
lines.appendChild(line)
});
$('#messages').animate({
scrollTop: $('#lines')[0].scrollHeight}, 2000);
}