statusheader on gui

This commit is contained in:
David Brazda
2023-06-06 16:04:47 +02:00
parent 80e264eeaa
commit b32edf7d2b
9 changed files with 222 additions and 15 deletions

View File

@ -297,7 +297,7 @@ def next(data, state: StrategyState):
state.indicators.slope[-1]=slope
#angle je ze slope
state.statinds.angle = dict(time=state.bars.time[-1], price=state.bars.close[-1], lookbacktime=state.bars.time[-slope_lookback], lookbackprice=lookbackprice, minimum_slope=minimum_slope)
state.statinds.angle = dict(time=state.bars.time[-1], price=state.bars.close[-1], lookbacktime=state.bars.time[-slope_lookback], lookbackprice=lookbackprice, minimum_slope=minimum_slope, maximum_slope=safe_get(state.vars, "bigwave_slope_above",0.20))
#slope MA vyrovna vykyvy ve slope, dále pracujeme se slopeMA
slope_MA_length = 5
@ -333,6 +333,19 @@ def next(data, state: StrategyState):
state.ilog(e=f"RSI {rsi_length=} necháváme 0", message=str(e)+format_exc())
#state.indicators.RSI14[-1]=0
def populate_cbar_rsi_indicator():
#CBAR RSI indicator
try:
crsi_length = int(safe_get(state.vars, "crsi_length",14))
source = state.cbar_indicators.tick_price #[-rsi_length:] #state.bars.vwap
crsi_res = rsi(source, crsi_length)
crsi_value = trunc(crsi_res[-1],3)
state.cbar_indicators.CRSI[-1]=crsi_value
#state.ilog(e=f"RSI {rsi_length=} {rsi_value=} {rsi_dont_buy=} {rsi_buy_signal=}", rsi_indicator=state.indicators.RSI14[-5:])
except Exception as e:
state.ilog(e=f"CRSI {crsi_length=} necháváme 0", message=str(e)+format_exc())
#state.indicators.RSI14[-1]=0
def slope_too_low():
return state.indicators.slopeMA[-1] < float(state.vars.minimum_slope)
@ -368,7 +381,7 @@ def next(data, state: StrategyState):
state.vars.jevylozeno = 0
state.ilog(e="PB prazdne nastavujeme: neni vylozeno", jevylozeno=state.vars.jevylozeno)
##kdy nenakupovat - tzn. neprojdou nakupy a kdyz uz jsou tak se zrusi
##kdy nesmí být žádné nákupní objednávky - zruší se
def buy_protection_enabled():
dont_buy_when = dict(AND=dict(), OR=dict())
##add conditions here
@ -402,12 +415,16 @@ def next(data, state: StrategyState):
def buy_conditions_met():
#preconditions
dont_buy_when = dict(AND=dict(), OR=dict())
dont_buy_when['bar_not_confirmed'] = (data['confirmed'] == 0)
if safe_get(state.vars, "buy_only_on_confirmed",True):
dont_buy_when['bar_not_confirmed'] = (data['confirmed'] == 0)
#od posledniho vylozeni musi ubehnout N baru
dont_buy_when['last_buy_offset_too_soon'] = data['index'] < (state.vars.last_buysignal_index + safe_get(state.vars, "lastbuy_offset",3))
dont_buy_when['blockbuy_active'] = (state.vars.blockbuy == 1)
dont_buy_when['jevylozeno_active'] = (state.vars.jevylozeno == 1)
#dont_buy_when['buy_protection_enabled'] = buy_protection_enabled()
dont_buy_when['rsi_too_high'] = state.indicators.RSI14[-1] > safe_get(state.vars, "rsi_dont_buy_above",50)
#dont_buy_when['slope_too_low'] = slope_too_low()
dont_buy_when['open_rush'] = is_open_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), safe_get(state.vars, "open_rush",0))
dont_buy_when['close_rush'] = is_close_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), safe_get(state.vars, "close_rush",0))
dont_buy_when['rsi_is_zero'] = (state.indicators.RSI14[-1] == 0)
@ -434,12 +451,23 @@ def next(data, state: StrategyState):
#buy_cond['AND']['rsi_is_rising'] = isrising(state.indicators.RSI14,2)
#buy_cond["AND"]["rsi_buy_signal_below"] = state.indicators.RSI14[-1] < safe_get(state.vars, "rsi_buy_signal_below",40)
#puvodni buy conditiony
#puvodni buy conditiony RSI pod + EMA klesajici
#buy_cond["AND"]["rsi_buy_signal_below"] = state.indicators.RSI14[-1] < safe_get(state.vars, "rsi_buy_signal_below",40)
#buy_cond["AND"]["ema_trend_is_falling"] = isfalling(state.indicators.ema,state.vars.Trend)
#pouze RSI pod 35 a zadny jiny
#pouze RSI nizke a RSI klesa
buy_cond["AND"]["rsi_buy_signal_below"] = state.indicators.RSI14[-1] < safe_get(state.vars, "rsi_buy_signal_below",40)
buy_cond["AND"]["rsi_is_falling"] = isfalling(state.indicators.RSI14,state.vars.Trend)
#buy_cond['crsi_below_crsi_buy_limit'] = state.cbar_indicators.CRSI[-1] < safe_get(state.vars, "crsi_buy_signal_below",30)
#slopME klesa a RSI začalo stoupat
# buy_cond["AND"]["rsi_is_rising2"] = isrising(state.indicators.RSI14,2)
# buy_cond['AND']['slopeMA_falling_Trend'] = isfalling(state.indicators.slopeMA,state.vars.Trend)
# buy_cond["AND"]["rsi_buy_signal_below"] = state.indicators.RSI14[-1] < safe_get(state.vars, "rsi_buy_signal_below",40)
#zkusit jako doplnkovy BUY SIGNAL 3 klesavy cbar RSI pripadne TICK PRICE
result, conditions_met = eval_cond_dict(buy_cond)
if result:
@ -484,8 +512,10 @@ def next(data, state: StrategyState):
last_ind_vals[key] = state.cbar_indicators[key][-5:]
return last_ind_vals
conf_bar = data['confirmed']
state.ilog(e=f"---{data['index']}-{conf_bar}--")
conf_bar = data['confirmed']
last_update_delta = round((float(data['updated']) - state.vars.last_update_time),6) if state.vars.last_update_time != 0 else 0
state.vars.last_update_time = float(data['updated'])
state.ilog(e=f"---{data['index']}-{conf_bar}--delta:{last_update_delta}")
#kroky pro CONFIRMED BAR only
if conf_bar == 1:
@ -501,8 +531,11 @@ def next(data, state: StrategyState):
else:
#CBAR INDICATOR pro tick price a deltu VOLUME
populate_cbar_tick_price_indicator()
populate_cbar_rsi_indicator()
#SPOLECNA LOGIKA - bar indikatory muzeme populovat kazdy tick (dobre pro RT GUI), ale uklada se stejne az pri confirmu
populate_ema_indicator()
populate_slope_indicator()
populate_rsi_indicator()
@ -526,9 +559,11 @@ def init(state: StrategyState):
state.vars.last_tick_volume = 0
state.vars.next_new = 0
state.vars.last_buysignal_index = 0
state.vars.last_update_time = 0
#state.cbar_indicators['ivwap'] = []
state.cbar_indicators['tick_price'] = []
state.cbar_indicators['tick_volume'] = []
state.cbar_indicators['CRSI'] = []
state.indicators['ema'] = []
state.indicators['slope'] = []
state.indicators['slopeMA'] = []

View File

@ -18,6 +18,7 @@ from tinydb import TinyDB, Query, where
from tinydb.operations import set
import json
from numpy import ndarray
import pandas as pd
from traceback import format_exc
from datetime import timedelta, time
@ -415,8 +416,50 @@ def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float):
except Exception as e:
return (-2, f"problem {e}")
def populate_metrics_output_directory(strat: StrategyInstance):
"""
WIP
Spocte zakladni metriky pred ulozenim do archivu
"""
#open_orders to dataset
oo_dict = AttributeDict(orderid=[],submitted_at=[],symbol=[],side=[],order_type=[],qty=[],limit_price=[],status=[])
for t in strat.open_orders:
oo_dict.orderid.append(str(t.id))
oo_dict.submitted_at.append(t.submitted_at)
oo_dict.symbol.append(t.symbol)
oo_dict.side.append(t.side)
oo_dict.qty.append(t.qty)
oo_dict.order_type.append(t.order_type)
oo_dict.limit_price.append(t.limit_price)
oo_dict.status.append(t.status)
open_orders_df = pd.DataFrame(oo_dict)
open_orders_df = open_orders_df.set_index('submitted_at', drop=False)
#trades to dataset
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[],value=[],cash=[],pos_avg_price=[])
for t in strat.trades:
trade_dict.orderid.append(str(t.order.id))
trade_dict.timestamp.append(t.timestamp)
trade_dict.symbol.append(t.order.symbol)
trade_dict.side.append(t.order.side)
trade_dict.qty.append(t.qty)
trade_dict.price.append(t.price)
trade_dict.position_qty.append(t.position_qty)
trade_dict.value.append(t.value)
trade_dict.cash.append(t.cash)
trade_dict.order_type.append(t.order.order_type)
trade_dict.pos_avg_price.append(t.pos_avg_price)
trade_df = pd.DataFrame(trade_dict)
trade_df = trade_df.set_index('timestamp',drop=False)
#archives runner and details
def archive_runner(runner: Runner, strat: StrategyInstance):
#results_metrics = dict()
print("inside archive_runner")
try:
if strat.bt is not None:
@ -434,6 +477,13 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
BT_FILL_CONDITION_BUY_LIMIT=BT_FILL_CONDITION_BUY_LIMIT,
BT_FILL_CONDITION_SELL_LIMIT=BT_FILL_CONDITION_SELL_LIMIT))
#WIP
#populate result metrics dictionary (max drawdown etc.)
#list of maximum positions (2000 2x, 1800 x 1, 900 x 1, 100 x 20)
#list of most profitable trades (pos,avgp + cena)
#nejspis prevedeni na dataset a report nad datasetem
#results_metrics = populate_metrics(strat)
runArchive: RunArchive = RunArchive(id = runner.id,
strat_id = runner.strat_id,
name=runner.run_name,

View File

@ -67,6 +67,7 @@
<pre id="statusSettings" class="headerItem"></pre>
</div>
</div>
<div id="statusHeaderTool"></div>
<div id="chart" style="display: None; float: left; "></div>
<div class="legend" id="legend"></div>
<div id="msgContainer">
@ -236,15 +237,15 @@
</div>
<div class="form-group">
<label for="editnote" class="form-label">note</label>
<textarea class="form-control" rows="2" id="editnote" name="note"></textarea>
<textarea class="form-control" rows="3" id="editnote" name="note"></textarea>
</div>
<div class="form-group">
<label for="stratvars" class="form-label">Stratvars</label>
<textarea class="form-control" rows="4" id="editstratvars" name="stratvars"></textarea>
<textarea class="form-control" rows="8" id="editstratvars" name="stratvars"></textarea>
</div>
<div class="form-group">
<label for="strat_json" class="form-label">Strat JSON</label>
<textarea class="form-control" rows="4" id="editstratjson" name="stratjson"></textarea>
<textarea class="form-control" rows="6" id="editstratjson" name="stratjson"></textarea>
</div>
</div>
<div class="modal-footer">

View File

@ -639,7 +639,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
toolTip.innerHTML += `<pre>${JSON.stringify(tradeDetails.get(param.time),null,2)}</pre><div>${price.toFixed(3)}</div>`;
console.log("toolTip.innerHTML",toolTip.innerHTML)
//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'}">

View File

@ -251,6 +251,10 @@ var archiveRecords =
targets: [5,6],
render: function ( data, type, row ) {
now = new Date(data)
if (type == "sort") {
return new Date(data).getTime();
}
if (isToday(now)) {
//return local time only
return 'dnes ' + format_date(data,false,true)
@ -267,8 +271,12 @@ var archiveRecords =
{
targets: [9,10],
render: function ( data, type, row ) {
if (type == "sort") {
return new Date(data).getTime();
}
//console.log(data)
//market datetime
return format_date(data, true)
return data ? format_date(data, true) : data
},
},
{

View File

@ -118,6 +118,16 @@ function connect(event) {
//pre.appendChild(stLine)
//line.appendChild(pre)
lines.appendChild(line)
//addline to statusheader if set
for (const [key, value] of Object.entries(statusBarConfig)) {
if (logLine.event.includes(value)) {
console.log("found", value, key, logLine.event)
document.getElementById("status"+key).textContent = logLine.event + (logLine.message ? " - " + logLine.message : "")
document.getElementById("status"+key).title = JSON.stringify(logLine,null,2);
}
}
});
$('#messages').animate({
scrollTop: $('#lines')[0].scrollHeight}, 2000);

View File

@ -6,6 +6,7 @@ function populate_real_time_chart() {
intitialize_candles()
initialize_vwap()
initialize_volume()
initialize_statusheader()
chart.subscribeClick(param => {
//display timestamp in trade-timestamp input field

View File

@ -8,6 +8,12 @@ var verticalSeries=null
var candlestickSeries = null
var volumeSeries = null
var vwapSeries = null
var statusBarConfig = JSON.parse(localStorage.getItem("statusBarConfig"));
if (statusBarConfig == null) {
statusBarConfig = {}
}
const sorter = (a, b) => a.time > b.time ? 1 : -1;
@ -24,13 +30,89 @@ indConfig = [ {name: "ema", titlevisible: false, embed: true, display: true, pri
{name: "emaSlow", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},
{name: "emaFast", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},
{name: "RSI14", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "RSI5", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "CRSI", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "aroon", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "apo", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "ppo", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "stoch2", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},
{name: "stoch1", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},]
function initialize_statusheader() {
var rows = 2;
var columns = 4;
console.log(JSON.stringify(statusBarConfig))
// Create the grid table
var gridTable = document.getElementById('statusHeaderTool');
var cntid = 0
for (var i = 0; i < rows; i++) {
var row = document.createElement('tr');
for (var j = 0; j < columns; j++) {
cntid++
var cell = document.createElement('td');
cell.className = "statustd";
var div = document.createElement('div');
var cellid = "status" + cntid
div.id = cellid;
var input = document.createElement('input');
input.id = cntid;
input.type = 'text';
input.style.display = "none";
if (statusBarConfig !== null && statusBarConfig[cntid]) {
//div.style.backgroundColor = 'red';
div.textContent = "set";
input.value = statusBarConfig[cntid];
}
cell.addEventListener('click', function() {
var inputValue = this.querySelector('input').value;
//this.querySelector('div').textContent = inputValue;
this.querySelector('div').style.display = 'none';
this.querySelector('input').style.display = 'block';
this.querySelector('input').focus();
});
input.addEventListener('blur', function() {
this.style.display = 'none';
//this.previousElementSibling.textContent = inputValue;
this.previousElementSibling.style.display = 'block';
if (this.value !== "") {
statusBarConfig[this.id] = this.value;
}
else {
delete statusBarConfig[this.id]
}
if (statusBarConfig[this.id]) {
this.previousElementSibling.textContent = "set"
//this.previousElementSibling.style.backgroundColor = 'red';
}
else {
this.previousElementSibling.style.backgroundColor = 'transparent';
}
console.log("potom", JSON.stringify(statusBarConfig))
localStorage.setItem("statusBarConfig", JSON.stringify(statusBarConfig));
});
cell.appendChild(div);
cell.appendChild(input);
row.appendChild(cell);
}
gridTable.appendChild(row);
}
}
function get_ind_config(indName) {
const i = indConfig.findIndex(e => e.name === indName);
if (i>-1)

View File

@ -224,7 +224,7 @@ html {
position: absolute;
color: #050505;
left: 115px;
top: 115px;
top: 195px;
z-index: 1;
font-size: 12px;
line-height: 18px;
@ -315,6 +315,26 @@ pre {
width: max-content;
}
.statustd {
width: 95px;
height: 25px;
border: 1px solid black;
text-align: center;
}
#statusHeaderTool {
margin-left: 0px;
font-size: normal;
/* font-weight: bold; */
display: flex;
/* background-color: #dfe4e6; */
/* color: white; */
padding: 1px;
padding-left: 5px;
width: max-content;
height: 95px;
}
.headerItem {
padding-right: 30px;
}