4 Commits

10 changed files with 138 additions and 38 deletions

View File

@ -524,7 +524,7 @@ class Backtester:
if actual_minus_reserved <= 0:
cena = price if price else self.get_last_price(time, self.symbol)
if (self.cash - reserved_price - float(int(size)*float(cena))) < 0:
printanyway("not enough cash for shorting. cash",self.cash,"reserved",reserved,"available",self.cash-reserved,"needed",float(int(size)*float(cena)))
printanyway("ERROR: not enough cash for shorting. cash",self.cash,"reserved",reserved,"available",self.cash-reserved,"needed",float(int(size)*float(cena)))
return -1
#check for available cash
@ -550,7 +550,7 @@ class Backtester:
if actual_plus_reserved_qty >= 0:
cena = price if price else self.get_last_price(time, self.symbol)
if (self.cash - reserved_price - float(int(size)*float(cena))) < 0:
printanyway("not enough cash to buy long. cash",self.cash,"reserved_qty",reserved_qty,"reserved_price",reserved_price, "available",self.cash-reserved_price,"needed",float(int(size)*float(cena)))
printanyway("ERROR: not enough cash to buy long. cash",self.cash,"reserved_qty",reserved_qty,"reserved_price",reserved_price, "available",self.cash-reserved_price,"needed",float(int(size)*float(cena)))
return -1
id = str(uuid4())

View File

@ -3,7 +3,7 @@ from uuid import UUID, uuid4
import pickle
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockTradesRequest, StockBarsRequest
from alpaca.data.enums import DataFeed
from alpaca.data.enums import DataFeed
from alpaca.data.timeframe import TimeFrame
from v2realbot.strategy.base import StrategyState
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
@ -1114,7 +1114,7 @@ def get_all_archived_runners_p(request: DataTablesRequest) -> Tuple[int, RunArch
# Total count query
total_count_query = """
SELECT COUNT(*) FROM runner_header
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value)
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value OR symbol like :search_value OR name like :search_value)
"""
c.execute(total_count_query, {'search_value': f'%{search_value}%'})
total_count = c.fetchone()[0]
@ -1129,7 +1129,7 @@ def get_all_archived_runners_p(request: DataTablesRequest) -> Tuple[int, RunArch
SUM(profit) OVER (PARTITION BY batch_id) AS batch_profit,
COUNT(*) OVER (PARTITION BY batch_id) AS batch_count
FROM runner_header
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value)
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value OR symbol like :search_value OR name like :search_value)
),
InterleavedGroups AS (
SELECT *,
@ -1156,7 +1156,7 @@ def get_all_archived_runners_p(request: DataTablesRequest) -> Tuple[int, RunArch
# Filtered count query
filtered_count_query = """
SELECT COUNT(*) FROM runner_header
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value)
WHERE (:search_value = '' OR strat_id LIKE :search_value OR batch_id LIKE :search_value OR symbol like :search_value OR name like :search_value)
"""
c.execute(filtered_count_query, {'search_value': f'%{search_value}%'})
filtered_count = c.fetchone()[0]

View File

@ -667,14 +667,14 @@
</div>
<div class="form-group mt-3">
<label for="logHere" class="form-label">Log</label>
<div id="log-container">
<pre id="log-content"></pre>
<div id="log-container"style="height:700px;border:1px solid black;">
<!-- <pre id="log-content"></pre> -->
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="logRefreshButton" value="Refresh">Refresh</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" id="closeLogModal" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
@ -1166,9 +1166,9 @@
<!-- <script src="/static/js/archivetables.js?v=1.05"></script> -->
<!-- archiveTables split into separate files -->
<script src="/static/js/tables/archivetable/init.js?v=1.12"></script>
<script src="/static/js/tables/archivetable/functions.js?v=1.10"></script>
<script src="/static/js/tables/archivetable/functions.js?v=1.11"></script>
<script src="/static/js/tables/archivetable/modals.js?v=1.07"></script>
<script src="/static/js/tables/archivetable/handlers.js?v=1.09"></script>
<script src="/static/js/tables/archivetable/handlers.js?v=1.11"></script>
<!-- Runmanager functionality -->
<script src="/static/js/tables/runmanager/init.js?v=1.1"></script>
@ -1178,7 +1178,7 @@
<script src="/static/js/livewebsocket.js?v=1.02"></script>
<script src="/static/js/realtimechart.js?v=1.02"></script>
<script src="/static/js/mytables.js?v=1.02"></script>
<script src="/static/js/mytables.js?v=1.03"></script>
<script src="/static/js/testlist.js?v=1.01"></script>
<script src="/static/js/ml.js?v=1.02"></script>
<script src="/static/js/common.js?v=1.01"></script>

View File

@ -90,9 +90,55 @@ $(document).ready(function () {
monaco.languages.register({ id: 'python' });
monaco.languages.register({ id: 'json' });
//Register mylogs language
monaco.languages.register({ id: 'mylogs' });
// Register the TOML language
monaco.languages.setLanguageConfiguration('mylogs', {
comments: {
lineComment: '//', // Adjust if your logs use a different comment symbol
},
brackets: [['[', ']'], ['{', '}']], // Array and object brackets
autoClosingPairs: [
{ open: '{', close: '}', notIn: ['string'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: "'", close: "'", notIn: ['string', 'comment'] },
],
});
monaco.languages.setMonarchTokensProvider('mylogs', {
tokenizer: {
root: [
[/#.*/, 'comment'], // Comments (if applicable)
// Timestamps
[/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+/, 'timestamp'],
// Log Levels
[/\b(INFO|DEBUG|WARNING|ERROR|CRITICAL)\b/, 'log-level'],
// Strings
[/".*"/, 'string'],
[/'.*'/, 'string'],
// Key-Value Pairs
[/[A-Za-z_]+\s*:/, 'key'],
[/-?\d+\.\d+/, 'number.float'], // Floating-point
[/-?\d+/, 'number.integer'], // Integers
[/\btrue\b/, 'boolean.true'],
[/\bfalse\b/, 'boolean.false'],
// Other Words and Symbols
[/[A-Za-z_]+/, 'identifier'],
[/[ \t\r\n]+/, 'white'],
[/[\[\]{}(),]/, 'delimiter'], // Expand if more delimiters exist
]
}
});
monaco.languages.register({ id: 'toml' });
// Define the TOML language configuration
monaco.languages.setLanguageConfiguration('toml', {
comments: {

View File

@ -6,6 +6,7 @@ let editor_diff_arch1
let editor_diff_arch2
var archData = null
var batchHeaders = []
var editorLog = null
function refresh_arch_and_callback(row, callback) {
//console.log("entering refresh")
@ -472,13 +473,34 @@ function refresh_logfile() {
contentType: "application/json",
dataType: "json",
success:function(response){
if (editorLog) {
editorLog.dispose();
}
if (response.lines.length == 0) {
$('#log-content').html("no records");
value = "no records";
// $('#log-content').html("no records");
}
else {
var escapedLines = response.lines.map(line => escapeHtml(line));
$('#log-content').html(escapedLines.join('\n'));
}
//console.log(response.lines)
//var escapedLines = response.lines.map(line => escapeHtml(line));
value = response.lines.join('\n')
// $('#log-content').html(escapedLines.join('\n'));
}
require(["vs/editor/editor.main"], () => {
editorLog = monaco.editor.create(document.getElementById('log-container'), {
value: value,
language: 'mylogs',
theme: 'tomlTheme-dark',
automaticLayout: true,
readOnly: true
});
});
// Focus at the end of the file:
const model = editorLog.getModel();
const lastLineNumber = model.getLineCount();
const lastLineColumn = model.getLineMaxColumn(lastLineNumber);
editorLog.setPosition({ lineNumber: lastLineNumber, column: lastLineColumn });
editorLog.revealPosition({ lineNumber: lastLineNumber, column: lastLineColumn });
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");

View File

@ -265,8 +265,8 @@ $(document).ready(function () {
$('#diff_first').text(record1.name);
$('#diff_second').text(record2.name);
$('#diff_first_id').text(data1.id);
$('#diff_second_id').text(data2.id);
$('#diff_first_id').text(data1.id + ' Batch: ' + data1.batch_id);
$('#diff_second_id').text(data2.id + ' Batch: ' + data2.batch_id);
//monaco
require(["vs/editor/editor.main"], () => {
@ -358,8 +358,13 @@ $(document).ready(function () {
})
});
$('#closeLogModal').click(function () {
editorLog.dispose()
});
//button to query log
$('#logRefreshButton').click(function () {
editorLog.dispose()
refresh_logfile()
});

View File

@ -35,40 +35,62 @@ class StrategyClassicSL(Strategy):
max_sum_profit_to_quit_rel = safe_get(self.state.vars, "max_sum_profit_to_quit_rel", None)
max_sum_loss_to_quit_rel = safe_get(self.state.vars, "max_sum_loss_to_quit_rel", None)
#load typ direktivy hard/soft cutoff
hard_cutoff = safe_get(self.state.vars, "hard_cutoff", False)
rel_profit = round(float(np.sum(self.state.rel_profit_cum)),5)
if max_sum_profit_to_quit_rel is not None:
if rel_profit >= float(max_sum_profit_to_quit_rel):
self.state.ilog(e=f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_profit_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(f"QUITTING MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
self.signal_stop = True
send_to_telegram(msg)
if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True
if max_sum_loss_to_quit_rel is not None:
if rel_profit < 0 and rel_profit <= float(max_sum_loss_to_quit_rel):
self.state.ilog(e=f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_loss_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(f"QUITTING MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
self.signal_stop = True
send_to_telegram(msg)
if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True
if max_sum_profit_to_quit is not None:
if float(self.state.profit) >= float(max_sum_profit_to_quit):
self.state.ilog(e=f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_profit_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(f"QUITTING MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
self.signal_stop = True
send_to_telegram(msg)
if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True
if max_sum_loss_to_quit is not None:
if float(self.state.profit) < 0 and float(self.state.profit) <= float(max_sum_loss_to_quit):
self.state.ilog(e=f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_loss_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(f"QUITTING MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}")
self.signal_stop = True
send_to_telegram(msg)
if hard_cutoff:
self.hard_stop = True
else:
self.soft_stop = True
return True
return False
@ -414,7 +436,7 @@ class StrategyClassicSL(Strategy):
populate_all_indicators(item, self.state)
#pro přípravu dat next nevoláme
if self.mode == Mode.PREP:
if self.mode == Mode.PREP or self.soft_stop:
return
else:
self.next(item, self.state)

View File

@ -80,7 +80,8 @@ class Strategy:
self.pe = pe
self.se = se
#signal stop - internal
self.signal_stop = False
self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie
self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu
#prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
#zatim jen jedna q1
@ -433,7 +434,7 @@ class Strategy:
#printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize())
except queue.Empty:
#check internal signals - for profit/loss optim etc - valid for runner
if self.signal_stop:
if self.hard_stop:
print(current_thread().name, "Stopping signal - internal")
break
@ -454,7 +455,7 @@ class Strategy:
if item == "last" or self.se.is_set():
print(current_thread().name, "stopping")
break
elif self.signal_stop:
elif self.hard_stop:
print(current_thread().name, "Stopping signal - internal")
break
elif self.pe.is_set():

View File

@ -78,7 +78,7 @@ def execute_signal_generator(state, data, name):
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
generated_by=name,
size=multiplier*state.vars.chunk,
size=int(multiplier*state.vars.chunk),
size_multiplier = multiplier,
direction=TradeDirection.LONG,
entry_price=None,
@ -90,7 +90,7 @@ def execute_signal_generator(state, data, name):
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
generated_by=name,
size=multiplier*state.vars.chunk,
size=int(multiplier*state.vars.chunk),
size_multiplier = multiplier,
direction=TradeDirection.SHORT,
entry_price=None,

View File

@ -151,13 +151,17 @@ def get_multiplier(state: StrategyState, data, signaloptions: dict, direction: T
#pocet ztrátových obchodů v řadě mi udává multiplikátor (0 - 1, 1 ztráta 2x, 3 v řadě - 4x atp.)
if martingale_enabled:
#martingale base - základ umocňování - klasicky 2
base = float(utls.safe_get(options, "martingale_base", 2))
#pocet aktuálních konsekutivních ztrát
cont_loss_series_cnt = state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]
if cont_loss_series_cnt == 0:
multiplier = 1
else:
multiplier = 2 ** cont_loss_series_cnt
multiplier = base ** cont_loss_series_cnt
state.ilog(lvl=1,e=f"SIZER - MARTINGALE {multiplier}", options=options, time=state.time, cont_loss_series_cnt=cont_loss_series_cnt)
if (martingale_enabled is False and multiplier > 1) or multiplier <= 0:
state.ilog(lvl=1,e=f"SIZER - Mame nekde problem MULTIPLIER mimo RANGE ERROR {multiplier}", options=options, time=state.time)
multiplier = 1