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
|
#HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru
|
||||||
lp = state.interface.get_last_price(symbol=state.symbol)
|
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
|
#maxSlopeMA = -0.03
|
||||||
#SLOPE ANGLE PROTECTIONs
|
#SLOPE ANGLE PROTECTIONs
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -72,6 +72,10 @@ class RunnerView(BaseModel):
|
|||||||
run_name: Optional[str] = None
|
run_name: Optional[str] = None
|
||||||
run_note: Optional[str] = None
|
run_note: Optional[str] = None
|
||||||
run_account: Account
|
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_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
|
|
||||||
@ -83,6 +87,10 @@ class Runner(BaseModel):
|
|||||||
run_account: Account
|
run_account: Account
|
||||||
run_name: Optional[str] = None
|
run_name: Optional[str] = None
|
||||||
run_note: 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_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
run_thread: Optional[object] = None
|
run_thread: Optional[object] = None
|
||||||
|
|||||||
@ -34,7 +34,12 @@ def get_all_threads():
|
|||||||
|
|
||||||
def get_all_runners():
|
def get_all_runners():
|
||||||
if len(db.runners) > 0:
|
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)
|
return (0, db.runners)
|
||||||
else:
|
else:
|
||||||
return (0, [])
|
return (0, [])
|
||||||
@ -54,6 +59,10 @@ def get_stratin(id: UUID):
|
|||||||
def get_runner(id: UUID):
|
def get_runner(id: UUID):
|
||||||
for i in db.runners:
|
for i in db.runners:
|
||||||
if str(i.id) == str(id):
|
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 (0, i)
|
||||||
return (-2, "not found")
|
return (-2, "not found")
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,13 @@
|
|||||||
<link rel="manifest" href="/static/site.webmanifest">
|
<link rel="manifest" href="/static/site.webmanifest">
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
<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://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>
|
<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">
|
<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>
|
<script src="/static/js/jquery.dataTables.min.js"></script>
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<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://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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main" class="mainConteiner flex-container">
|
<div id="main" class="mainConteiner flex-container">
|
||||||
@ -29,6 +31,13 @@
|
|||||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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 id="chart" style="display: None; float: left; "></div>
|
||||||
<div class="legend" id="legend"></div>
|
<div class="legend" id="legend"></div>
|
||||||
<div id="msgContainer">
|
<div id="msgContainer">
|
||||||
@ -70,7 +79,11 @@
|
|||||||
<th>Started</th>
|
<th>Started</th>
|
||||||
<th>Mode</th>
|
<th>Mode</th>
|
||||||
<th>Account</th>
|
<th>Account</th>
|
||||||
<th>Paused</th>
|
<th>Paused</th>
|
||||||
|
<th>Profit</th>
|
||||||
|
<th>Trades</th>
|
||||||
|
<th>Pos</th>
|
||||||
|
<th>AVGP</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
@ -362,9 +375,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
<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/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>
|
</body>
|
||||||
</html>
|
</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 positionsPriceLine = null
|
||||||
var limitkaPriceLine = null
|
var limitkaPriceLine = null
|
||||||
var angleSeries = 1
|
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) {
|
function connect(event) {
|
||||||
var runnerId = document.getElementById("runnerId")
|
var runnerId = document.getElementById("runnerId")
|
||||||
@ -21,6 +52,7 @@ function connect(event) {
|
|||||||
document.getElementById("bt-disc").style.display = "initial"
|
document.getElementById("bt-disc").style.display = "initial"
|
||||||
document.getElementById("bt-conn").style.display = "none"
|
document.getElementById("bt-conn").style.display = "none"
|
||||||
document.getElementById("chart").style.display = "block"
|
document.getElementById("chart").style.display = "block"
|
||||||
|
populate_rt_status_header(runnerId.value)
|
||||||
}
|
}
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
var parsed_data = JSON.parse(event.data)
|
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
|
//on button
|
||||||
function store_api_key(event) {
|
function store_api_key(event) {
|
||||||
key = document.getElementById("api-key").value;
|
key = document.getElementById("api-key").value;
|
||||||
@ -101,456 +37,7 @@ function is_running(id) {
|
|||||||
});
|
});
|
||||||
return running
|
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
|
//STRATIN and RUNNERS TABELS
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
@ -903,6 +390,7 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
//stratin table
|
//stratin table
|
||||||
var stratinRecords =
|
var stratinRecords =
|
||||||
$('#stratinTable').DataTable( {
|
$('#stratinTable').DataTable( {
|
||||||
@ -930,7 +418,7 @@ var stratinRecords =
|
|||||||
{data: 'add_data_conf', visible: false},
|
{data: 'add_data_conf', visible: false},
|
||||||
{data: 'note'},
|
{data: 'note'},
|
||||||
{data: 'history', visible: false},
|
{data: 'history', visible: false},
|
||||||
{data: 'id', visible: true}
|
{data: 'id', visible: true},
|
||||||
],
|
],
|
||||||
columnDefs: [{
|
columnDefs: [{
|
||||||
targets: 12,
|
targets: 12,
|
||||||
@ -941,6 +429,10 @@ var stratinRecords =
|
|||||||
}],
|
}],
|
||||||
order: [[1, 'asc']],
|
order: [[1, 'asc']],
|
||||||
paging: false,
|
paging: false,
|
||||||
|
// select: {
|
||||||
|
// style: 'multi'
|
||||||
|
// },
|
||||||
|
processing: false
|
||||||
// createdRow: function( row, data, dataIndex){
|
// createdRow: function( row, data, dataIndex){
|
||||||
// if (is_running(data.id) ){
|
// if (is_running(data.id) ){
|
||||||
// alert("runner");
|
// alert("runner");
|
||||||
@ -976,10 +468,17 @@ var runnerRecords =
|
|||||||
{data: 'run_started'},
|
{data: 'run_started'},
|
||||||
{data: 'run_mode'},
|
{data: 'run_mode'},
|
||||||
{data: 'run_account'},
|
{data: 'run_account'},
|
||||||
{data: 'run_paused'}
|
{data: 'run_paused'},
|
||||||
|
{data: 'run_profit'},
|
||||||
|
{data: 'run_trade_count'},
|
||||||
|
{data: 'run_positions'},
|
||||||
|
{data: 'run_avgp'},
|
||||||
],
|
],
|
||||||
paging: false,
|
paging: false,
|
||||||
processing: false
|
processing: false,
|
||||||
|
// select: {
|
||||||
|
// style: 'multi'
|
||||||
|
// },
|
||||||
} );
|
} );
|
||||||
|
|
||||||
//modal na run
|
//modal na run
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
|
|
||||||
//it is called after population
|
//it is called after population
|
||||||
|
|
||||||
function populate_real_time_chart() {
|
function populate_real_time_chart() {
|
||||||
if (chart !== null) {
|
if (chart !== null) {
|
||||||
chart.remove()
|
chart.remove();
|
||||||
|
clear_status_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
//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 = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
||||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
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({
|
candlestickSeries.priceScale().applyOptions({
|
||||||
scaleMargins: {
|
scaleMargins: {
|
||||||
top: 0.1, // highest point of the series will be 10% away from the top
|
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({
|
volumeSeries.priceScale().applyOptions({
|
||||||
// set the positioning of the volume series
|
// set the positioning of the volume series
|
||||||
scaleMargins: {
|
scaleMargins: {
|
||||||
@ -29,7 +31,7 @@ function populate_real_time_chart() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const vwapSeries = chart.addLineSeries({
|
vwapSeries = chart.addLineSeries({
|
||||||
// title: "vwap",
|
// title: "vwap",
|
||||||
color: '#2962FF',
|
color: '#2962FF',
|
||||||
lineWidth: 1,
|
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;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#statusHeader {
|
||||||
|
margin-left: 55px;
|
||||||
|
font-size: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerItem {
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.highlighted {
|
.highlighted {
|
||||||
font-weight: bold;
|
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