enables day by day bt run, bt market premium+inds

This commit is contained in:
David Brazda
2023-10-15 20:14:44 +02:00
parent 793c776d45
commit 7c1bba028b
25 changed files with 500 additions and 83 deletions

9
.gitignore vendored
View File

@ -43,3 +43,12 @@ v2realbot/__pycache__/config.cpython-310.pyc
.gitignore
v2realbot/enums/__pycache__/enums.cpython-310.pyc
v2realbot/common/__pycache__/model.cpython-310.pyc
v2realbot/__pycache__/config.cpython-310.pyc
v2realbot/backtesting/__pycache__/backtester.cpython-310.pyc
v2realbot/common/__pycache__/model.cpython-310.pyc
v2realbot/enums/__pycache__/enums.cpython-310.pyc
v2realbot/indicators/__pycache__/indicators.cpython-310.pyc
v2realbot/loader/__pycache__/aggregator.cpython-310.pyc
v2realbot/loader/__pycache__/trade_offline_streamer.cpython-310.pyc
v2realbot/loader/__pycache__/trade_ws_streamer.cpython-310.pyc
v2realbot/strategy/__pycache__/base.cpython-310.pyc

18
testy/funkce.py Normal file
View File

@ -0,0 +1,18 @@
# import os
# for filename in os.listdir("v2realbot/strategyblocks/indicators/custom"):
# print(filename)
import os
import importlib
import v2realbot.strategyblocks.indicators.custom as ci
ci.opengap.opengap()
# for filename in os.listdir("v2realbot/strategyblocks/indicators/custom"):
# if filename.endswith(".py") and filename != "__init__.py":
# # __import__(filename[:-3])
# #__import__(f"v2realbot.strategyblocks.indicators.custom.{filename[:-3]}")
# __import__(f"{filename[:-3]}")
# #importlib.import_module()

View File

@ -43,7 +43,7 @@ from v2realbot.common.model import TradeUpdate, Order
#from rich import print
import threading
import asyncio
from v2realbot.config import BT_DELAYS, DATA_DIR, BT_FILL_CONDITION_BUY_LIMIT, BT_FILL_CONDITION_SELL_LIMIT, BT_FILL_LOG_SURROUNDING_TRADES, BT_FILL_CONS_TRADES_REQUIRED
from v2realbot.config import BT_DELAYS, DATA_DIR, BT_FILL_CONDITION_BUY_LIMIT, BT_FILL_CONDITION_SELL_LIMIT, BT_FILL_LOG_SURROUNDING_TRADES, BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_PRICE_MARKET_ORDER_PREMIUM
from v2realbot.utils.utils import AttributeDict, ltp, zoneNY, trunc, count_decimals, print
from v2realbot.utils.tlog import tlog
from v2realbot.enums.enums import FillCondition
@ -311,6 +311,12 @@ class Backtester:
#ic(i)
fill_time = i[0]
fill_price = i[1]
#přičteme MARKET PREMIUM z konfigurace (do budoucna mozna rozdilne pro BUY/SELL a nebo mozna z konfigurace pro dany itutl)
if o.side == OrderSide.BUY:
fill_price = fill_price + BT_FILL_PRICE_MARKET_ORDER_PREMIUM
elif o.side == OrderSide.SELL:
fill_price = fill_price - BT_FILL_PRICE_MARKET_ORDER_PREMIUM
print("FILL ",o.side,"MARKET at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "cena", i[1])
break
else:

View File

@ -2,7 +2,7 @@ from v2realbot.config import DATA_DIR
import sqlite3
import queue
import threading
from datetime import time
import time
sqlite_db_file = DATA_DIR + "/v2trading.db"
# Define the connection pool
@ -44,6 +44,7 @@ def execute_with_retry(cursor: sqlite3.Cursor, statement: str, retry_interval: i
return cursor.execute(statement)
except sqlite3.OperationalError as e:
if str(e) == "database is locked":
print("database retry in 1s." + str(e))
time.sleep(retry_interval)
continue
else:

View File

@ -24,6 +24,15 @@ from alpaca.data.enums import Exchange
# return user.id
# raise HTTPException(status_code=404, detail=f"Could not find user with id: {id}")
class RunDay(BaseModel):
"""
Helper object for batch run - carries list of days in format required by run batch manager
"""
start: datetime
end: datetime
name: Optional[str] = None
note: Optional[str] = None
id: Optional[str] = None
# Define a Pydantic model for input data
class ConfigItem(BaseModel):

View File

@ -41,6 +41,23 @@ DATA_DIR = user_data_dir("v2realbot")
PROFILING_NEXT_ENABLED = False
PROFILING_OUTPUT_DIR = DATA_DIR
#FILL CONFIGURATION CLASS FOR BACKTESTING
#WIP
class BT_FILL_CONF:
""""
Trida pro konfiguraci backtesting fillu pro dany symbol, pokud neexistuje tak fallback na obecny viz vyse-
MOžná udělat i separátní profil PAPER/LIVE. Nějak vymyslet profily a jejich správa.
"""
def __init__(self, symbol, BT_FILL_CONS_TRADES_REQUIRED, BT_FILL_CONDITION_BUY_LIMIT, BT_FILL_CONDITION_SELL_LIMIT,BT_FILL_PRICE_MARKET_ORDER_PREMIUM):
self.symbol = symbol
self.BT_FILL_CONS_TRADES_REQUIRED = BT_FILL_CONS_TRADES_REQUIRED
self.BT_FILL_CONDITION_BUY_LIMIT=BT_FILL_CONDITION_BUY_LIMIT
self.BT_FILL_CONDITION_SELL_LIMIT=BT_FILL_CONDITION_SELL_LIMIT
self.BT_FILL_PRICE_MARKET_ORDER_PREMIUM=BT_FILL_PRICE_MARKET_ORDER_PREMIUM
""""
LATENCY DELAYS for LIVE eastcoast
.000 trigger - last_trade_time (.4246266)

View File

@ -6,20 +6,23 @@ from alpaca.data.requests import StockTradesRequest, StockBarsRequest
from alpaca.data.enums import DataFeed
from alpaca.data.timeframe import TimeFrame
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem
from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram
from v2realbot.utils.ilog import delete_logs
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
from threading import Thread, current_thread, Event, enumerate
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR,BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_LOG_SURROUNDING_TRADES,BT_FILL_CONDITION_BUY_LIMIT,BT_FILL_CONDITION_SELL_LIMIT, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR,BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_LOG_SURROUNDING_TRADES,BT_FILL_CONDITION_BUY_LIMIT,BT_FILL_CONDITION_SELL_LIMIT, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN
import importlib
from alpaca.trading.requests import GetCalendarRequest
from alpaca.trading.client import TradingClient
#from alpaca.trading.models import Calendar
from queue import Queue
from tinydb import TinyDB, Query, where
from tinydb.operations import set
import json
from numpy import ndarray
#from rich import print
from rich import print
import pandas as pd
from traceback import format_exc
from datetime import timedelta, time
@ -339,38 +342,93 @@ def get_testlist_byID(record_id: str):
return 0, TestList(id=row[0], name=row[1], dates=json.loads(row[2]))
##TADY JSEM SKONCIL PROJIT - dodelat nastavni timezone
#nejspis vse v RunDays by melo byt jiz lokalizovano na zoneNY
#zaroven nejak vymyslet, aby bt_from/to uz bylo lokalizovano
#ted jsem dal natvrdo v main rest lokalizaci
#ale odtrasovat,ze vse funguje (nefunguje)
#volano pro batchove spousteni (BT,)
def run_batch_stratin(id: UUID, runReq: RunRequest):
#pozor toto je test interval id (batch id se pak generuje pro kazdy davkovy run tohoto intervalu)
if runReq.test_batch_id is None:
return (-1, "batch_id required for batch run")
#pozor test_batch_id je test interval id (batch id se pak generuje pro kazdy davkovy run tohoto intervalu)
if runReq.test_batch_id is None and (runReq.bt_from is None or runReq.bt_from.date() == runReq.bt_to.date()):
return (-1, "test interval or different days required for batch run")
if runReq.mode != Mode.BT:
return (-1, "batch run only for backtest")
print("request values:", runReq)
#print("request values:", runReq)
print("getting intervals")
testlist: TestList
#getting days to run into RunDays format
if runReq.test_batch_id is not None:
print("getting intervals days")
testlist: TestList
res, testlist = get_testlist_byID(record_id=runReq.test_batch_id)
res, testlist = get_testlist_byID(record_id=runReq.test_batch_id)
if res < 0:
return (-1, f"not existing ID of testlists with {runReq.test_batch_id}")
if res < 0:
return (-1, f"not existing ID of testlists with {runReq.test_batch_id}")
print("test interval:", testlist)
cal_list = []
#interval dame do formatu list(RunDays)
#TODO do budoucna predelat Interval na RunDays a na zone aware datetime
for intrvl in testlist.dates:
start_time = zoneNY.localize(datetime.fromisoformat(intrvl.start))
end_time = zoneNY.localize(datetime.fromisoformat(intrvl.end))
cal_list.append(RunDay(start = start_time, end = end_time, note=intrvl.note, id=testlist.id))
print(f"Getting intervals - RESULT: {cal_list}")
#sem getting dates
else:
#getting dates from calendat
clientTrading = TradingClient(ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, raw_data=False)
if runReq.bt_to is None:
runReq.bt_to = datetime.now().astimezone(zoneNY)
calendar_request = GetCalendarRequest(start=runReq.bt_from,end=runReq.bt_to)
cal_dates = clientTrading.get_calendar(calendar_request)
#list(Calendar)
# Calendar
# date: date
# open: datetime
# close: datetime
cal_list = []
for day in cal_dates:
start_time = zoneNY.localize(day.open)
end_time = zoneNY.localize(day.close)
#u prvni polozky
if day == cal_dates[0]:
#pokud je cas od od vetsi nez open marketu prvniho dne, pouzijeme tento pozdejis cas
if runReq.bt_from > start_time:
start_time = runReq.bt_from
#u posledni polozky
if day == cal_dates[-1]:
#cas do, je pred openenem market, nedavame tento den
if runReq.bt_to < start_time:
continue
#pokud koncovy cas neni do konce marketu, pouzijeme tento drivejsi namisto konce posledniho dne
if runReq.bt_to < end_time:
end_time = runReq.bt_to
cal_list.append(RunDay(start = start_time, end = end_time))
print(f"Getting interval dates from - to - RESULT: {cal_list}")
#spousti se vlakno s paralelnim behem a vracime ok
ridici_vlakno = Thread(target=batch_run_manager, args=(id, runReq, testlist), name=f"Batch run controll thread started.")
ridici_vlakno = Thread(target=batch_run_manager, args=(id, runReq, cal_list), name=f"Batch run control thread started.")
ridici_vlakno.start()
print(enumerate())
return 0, f"Batch run started"
#thread, ktery bude ridit paralelni spousteni
# bud ceka na dokonceni v runners nebo to bude ridit jinak a bude mit jednoho runnera?
# nejak vymyslet.
# logovani zatim jen do print
def batch_run_manager(id: UUID, runReq: RunRequest, testlist: TestList):
def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
#zde muzu iterovat nad intervaly
#cekat az dobehne jeden interval a pak spustit druhy
#pripadne naplanovat beh - to uvidim
@ -379,30 +437,21 @@ def batch_run_manager(id: UUID, runReq: RunRequest, testlist: TestList):
#mohl podporovat i BATCH RUNy.
batch_id = str(uuid4())[:8]
runReq.batch_id = batch_id
print("Entering BATCH RUN MANAGER")
print("generated batch_ID", batch_id)
print("test batch", testlist)
print("test date", testlist.dates)
interval: Intervals
cnt_max = len(testlist.dates)
cnt_max = len(rundays)
cnt = 0
#promenna pro sdileni mezi runy jednotlivych batchů (např. daily profit)
inter_batch_params = dict(batch_profit=0)
note_from_run_request = runReq.note
for intrvl in testlist.dates:
for day in rundays:
cnt += 1
interval = intrvl
if interval.note is not None:
print("mame zde note")
print("Datum od", interval.start)
print("Datum do", interval.end)
print("starting")
#předání atributů datetime.fromisoformat
runReq.bt_from = datetime.fromisoformat(interval.start)
runReq.bt_to = datetime.fromisoformat(interval.end)
runReq.note = f"Batch {batch_id} #{cnt}/{cnt_max} {testlist.name} N:{interval.note} {note_from_run_request}"
print("Datum od", day.start)
print("Datum do", day.end)
runReq.bt_from = day.start
runReq.bt_to = day.end
runReq.note = f"Batch {batch_id} #{cnt}/{cnt_max} {day.name} N:{day.note} {note_from_run_request}"
#protoze jsme v ridicim vlaknu, poustime za sebou jednotlive stratiny v synchronnim modu
res, id_val = run_stratin(id=id, runReq=runReq, synchronous=True, inter_batch_params=inter_batch_params)
@ -435,8 +484,8 @@ def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_b
res, adp = parse_toml_string(i.add_data_conf)
if res < 0:
return (-1, "add data conf invalid")
print("jsme uvnitr")
id = uuid4()
print(f"RUN {id} INITIATED")
name = i.name
symbol = i.symbol
open_rush = i.open_rush
@ -1114,6 +1163,7 @@ def get_alpaca_history_bars(symbol: str, datetime_object_from: datetime, datetim
#bars.data[symbol]
return 0, result
except Exception as e:
print(str(e) + format_exc())
return -2, str(e)
# change_archived_runner

View File

@ -24,7 +24,7 @@ from queue import Queue, Empty
from threading import Thread
import asyncio
from v2realbot.common.db import insert_queue, insert_conn, pool
from v2realbot.utils.utils import json_serial, send_to_telegram
from v2realbot.utils.utils import json_serial, send_to_telegram, zoneNY, zonePRG
from uuid import uuid4
from sqlite3 import OperationalError
from time import sleep
@ -226,7 +226,14 @@ def _get_stratin(stratin_id) -> StrategyInstance:
@app.put("/stratins/{stratin_id}/run", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
def _run_stratin(stratin_id: UUID, runReq: RunRequest):
#print(runReq)
if runReq.test_batch_id is not None:
if runReq.bt_from is not None:
runReq.bt_from = zonePRG.localize(runReq.bt_from)
if runReq.bt_to is not None:
runReq.bt_to = zonePRG.localize(runReq.bt_to)
#pokud jedeme nad test intervaly anebo je požadováno více dní - pouštíme jako batch day by day
#do budoucna dát na FE jako flag
if runReq.test_batch_id is not None or (runReq.bt_from.date() != runReq.bt_to.date()):
res, id = cs.run_batch_stratin(id=stratin_id, runReq=runReq)
else:
res, id = cs.run_stratin(id=stratin_id, runReq=runReq)

View File

@ -82,6 +82,19 @@
<link rel="stylesheet" data-name="vs/editor/editor.main" href="/static/js/libs/monaco/vs/editor/editor.main.css">
<script src="/static/js/libs/monaco/vs/loader.js"></script>
<!-- <link rel="stylesheet" href="/static/js/libs/atom-dark.css">
<link rel="stylesheet" href="/static/js/libs/speedhighlight/dist/themes/atom-dark.css">
<link rel="stylesheet" href="https://unpkg.com/@speed-highlight/core@1.2.4/dist/themes/default.css">
-->
<!--
<script type="module">
import { highlightAll, highlightText } from '/static/js/libs/speedhighlight/dist/index.js';
// import { detectLanguage } from 'https://unpkg.com/@speed-highlight/core/dist/detect.js';
// import e from 'https://unpkg.com/@speed-highlight/core@1.2.4/dist/languages/log.js';
// loadLanguage('log', e);
highlightAll();
</script> -->
<!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.41.0/min/vs/editor/editor.main.js"></script> -->
<!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.41.0/min/vs/loader.min.js"></script> -->

View File

@ -298,6 +298,8 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn
//$("#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));
@ -357,6 +359,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
candlestickSeries = null
//v pripade, ze neprojde get bars, nastavit na intervals[0]
switch_to_interval(intervals[1])
chart.timeScale().fitContent();
@ -963,11 +966,11 @@ function display_log(iterLogList, timestamp) {
highlighted = (parseInt(logLine.time) == parseInt(timestamp)) ? "highlighted" : ""
logcnt++;
row = '<div data-bs-toggle="collapse" class="'+ highlighted + '" onclick="set_timestamp(' + logLine.time + ')" data-bs-target="#rec'+logcnt+'">'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'</div>'
row = '<div data-bs-toggle="collapse" class="'+ highlighted + ' shj-lang-log" onclick="set_timestamp(' + logLine.time + ')" data-bs-target="#rec'+logcnt+'">'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'</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>'
row_detail = '<div id="rec'+logcnt+'" class="collapse pidi"><pre>' + str_row + '</pre></div>'
row_detail = '<div id="rec'+logcnt+'" class="collapse pidi shj-lang-log"><pre>' + str_row + '</pre></div>'
var lines = document.getElementById('lines')
var line = document.createElement('div')

View File

@ -0,0 +1,157 @@
[class*=shj-lang-] {
white-space: pre;
/* margin: 10px 0; */
/* border-radius: 10px; */
/* padding: 30px 20px; */
background: white;
color: #112;
box-shadow: 0 0 5px #0001;
text-shadow: none;
font: 11px Consolas,Courier New,Monaco,Andale Mono,Ubuntu Mono,monospace;
line-height: 15px;
box-sizing: border-box;
/* max-width: min(100%,100vw); */
word-wrap: normal;
}
.shj-inline {
margin: 0;
padding: 2px 5px;
display: inline-block;
border-radius: 5px
}
[class*=shj-lang-]::selection,[class*=shj-lang-] ::selection {
background: #bdf5
}
[class*=shj-lang-]>div {
display: flex;
overflow: auto
}
[class*=shj-lang-]>div :last-child {
flex: 1;
outline: none
}
.shj-numbers {
padding-left: 5px;
counter-reset: line
}
.shj-numbers div {
padding-right: 5px
}
.shj-numbers div:before {
color: #999;
display: block;
content: counter(line);
opacity: .5;
text-align: right;
margin-right: 5px;
counter-increment: line
}
.shj-syn-cmnt {
font-style: italic
}
.shj-syn-err,.shj-syn-kwd {
color: #e16
}
.shj-syn-num,.shj-syn-class {
color: #f60
}
.shj-numbers,.shj-syn-cmnt {
color: #999
}
.shj-syn-insert,.shj-syn-str {
color: #7d8
}
.shj-syn-bool {
color: #3bf
}
.shj-syn-type,.shj-syn-oper {
color: #5af
}
.shj-syn-section,.shj-syn-func {
color: #84f
}
.shj-syn-deleted,.shj-syn-var {
color: #f44
}
.shj-oneline {
padding: 12px 10px
}
.shj-lang-http.shj-oneline .shj-syn-kwd {
background: #25f;
color: #fff;
padding: 5px 7px;
border-radius: 5px
}
.shj-multiline.shj-mode-header {
padding: 20px
}
.shj-multiline.shj-mode-header:before {
content: attr(data-lang);
color: #58f;
display: block;
padding: 10px 20px;
background: #58f3;
border-radius: 5px;
margin-bottom: 20px
}
[class*=shj-lang-] {
color: #abb2bf;
background: #161b22
}
[class*=shj-lang-]:before {
color: #6f9aff
}
.shj-syn-deleted,.shj-syn-err,.shj-syn-var {
color: #e06c75
}
.shj-syn-section,.shj-syn-oper,.shj-syn-kwd {
color: #c678dd
}
.shj-syn-class {
color: #e5c07b
}
.shj-numbers,.shj-syn-cmnt {
color: #76839a
}
.shj-syn-insert {
color: #98c379
}
.shj-syn-type {
color: #56b6c2
}
.shj-syn-num,.shj-syn-bool {
color: #d19a66
}
.shj-syn-str,.shj-syn-func {
color: #61afef
}

View File

@ -278,7 +278,7 @@ html {
height: 568px;
flex-direction: column-reverse;
margin-left: 28px;
width: 100%;
width: 130%;
}
#lowercontainer {

View File

@ -63,27 +63,30 @@ def trail_SL_management(state: StrategyState, data):
def_SL = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
directive_name = "SL_trailing_offset_"+str(smer)
offset = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
directive_name = "SL_trailing_step_"+str(smer)
step = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, offset))
#pokud je pozadovan trail jen do breakeven a uz prekroceno
if (direction == TradeDirection.LONG and stop_breakeven and state.vars.activeTrade.stoploss_value >= float(state.avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and state.vars.activeTrade.stoploss_value <= float(state.avgp)):
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
return
#IDEA: Nyni posouvame SL o offset, mozna ji posunout jen o direktivu step ?
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
step_normalized = normalize_tick(state, data, step)
def_SL_normalized = normalize_tick(state, data, def_SL)
if direction == TradeDirection.LONG:
move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
if (move_SL_threshold) < data['close']:
state.vars.activeTrade.stoploss_value += offset_normalized
state.vars.activeTrade.stoploss_value += step_normalized
insert_SL_history(state)
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
elif direction == TradeDirection.SHORT:
move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
if (move_SL_threshold) > data['close']:
state.vars.activeTrade.stoploss_value -= offset_normalized
state.vars.activeTrade.stoploss_value -= step_normalized
insert_SL_history(state)
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)

View File

@ -1 +1,10 @@
from . import *
import os
# import importlib
# from v2realbot.strategyblocks.indicators.custom.opengap import opengap
for filename in os.listdir("v2realbot/strategyblocks/indicators/custom"):
if filename.endswith(".py") and filename != "__init__.py":
# __import__(filename[:-3])
__import__(f"v2realbot.strategyblocks.indicators.custom.{filename[:-3]}")
#importlib.import_module()

View File

@ -35,6 +35,32 @@ def basestats(state, params):
val = np.amax(source_array)
elif func == "mean":
val = np.mean(source_array)
elif func == "var":
data = np.array(source_array)
mean_value = np.mean(data)
# Calculate the variance of the data
val = np.mean((data - mean_value) ** 2)
elif func == "angle":
delka_pole = len(source_array)
if delka_pole < 2:
return 0,0
x = np.arange(delka_pole)
y = np.array(source_array)
# Fit a linear polynomial to the data
coeffs = np.polyfit(x, y, 1)
# Calculate the angle in radians angle_rad
val = np.arctan(coeffs[0]) * 1000
# Convert the angle to degrees angle_deg
#angle_deg = np.degrees(angle_rad)
# Normalize the degrees between -1 and 1
#val = 2 * (angle_deg / 180) - 1
elif func =="stdev":
val = np.std(source_array)
else:
return -2, "wrong function"

View File

@ -0,0 +1,26 @@
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
from v2realbot.indicators.indicators import ema as ext_ema
from v2realbot.strategyblocks.indicators.helpers import get_source_series
from rich import print as printanyway
from traceback import format_exc
from v2realbot.ml.ml import ModelML
import numpy as np
from collections import defaultdict
from v2realbot.strategyblocks.indicators.helpers import value_or_indicator
#strength, absolute change of parameter between current value and lookback value (n-past)
#used for example to measure unusual peaks
def ema(state, params):
funcName = "ema"
source = safe_get(params, "source", None)
lookback = safe_get(params, "lookback",14)
#lookback muze byt odkaz na indikator, pak berem jeho hodnotu
lookback = int(value_or_indicator(state, lookback))
source_series = get_source_series(state, source)[-lookback:]
ema_value = ext_ema(source_series, lookback)
val = round(ema_value[-1],4)
state.ilog(lvl=1,e=f"INSIDE {funcName} {val} {source=} {lookback=}", **params)
return 0, val

View File

@ -15,13 +15,16 @@ def mathop(state, params):
if source1 is None or source2 is None or operator is None:
return -2, "required source1 source2 operator"
druhy = float(value_or_indicator(state, source2))
if operator == "+":
val = round(float(source1_series[-1] + value_or_indicator(state, source2)),4)
val = round(float(source1_series[-1] + druhy),4)
elif operator == "-":
val = round(float(source1_series[-1] - value_or_indicator(state, source2)),4)
val = round(float(source1_series[-1] - druhy),4)
elif operator == "*":
val = round(float(source1_series[-1] * druhy),4)
else:
return -2, "unknow operator"
#state.ilog(lvl=0,e=f"INSIDE {funcName} {source1=} {source2=} {val}", **params)
state.ilog(lvl=1,e=f"INSIDE {funcName} {source1=} {source2=} {val} {druhy=}", **params)
return 0, val

View File

@ -0,0 +1,52 @@
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.strategyblocks.indicators.helpers import get_source_series
from rich import print as printanyway
from traceback import format_exc
from v2realbot.ml.ml import ModelML
import numpy as np
from collections import defaultdict
import bisect
#strength, absolute change of parameter between current value and lookback value (n-past)
#used for example to measure unusual peaks
def sameprice(state, params):
funcName = "sameprice"
typ = safe_get(params, "type", None)
def find_first_bigger_than_lastitem_backwards(list1):
last_item = list1[-1]
for i in range(len(list1) - 2, -1, -1):
if list1[i] > last_item:
return i
return -1
def find_first_smaller_than_lastitem_backwards(list1):
last_item = list1[-1]
for i in range(len(list1) - 2, -1, -1):
if list1[i] < last_item:
return i
return -1
if typ == "up":
pozice_prvniho_vetsiho = find_first_bigger_than_lastitem_backwards(state.bars["vwap"])
elif typ == "down":
pozice_prvniho_vetsiho = find_first_smaller_than_lastitem_backwards(state.bars["vwap"])
else:
return -2, "unknow type"
celkova_delka = len(state.bars["vwap"])
#jde o daily high
if pozice_prvniho_vetsiho == -1:
state.ilog(lvl=1,e=f"INSIDE {funcName} {typ} {pozice_prvniho_vetsiho=} vracime 1")
return 0, celkova_delka
delka_k_predchozmu = celkova_delka - pozice_prvniho_vetsiho
normalizovano = delka_k_predchozmu/celkova_delka
state.ilog(lvl=1,e=f"INSIDE {funcName} {typ} {pozice_prvniho_vetsiho=} {celkova_delka=} {delka_k_predchozmu=} {normalizovano=}", pozice_prvniho_vetsiho=pozice_prvniho_vetsiho, celkova_delka=celkova_delka, delka_k_predchozmu=delka_k_predchozmu, **params)
return 0, delka_k_predchozmu

View File

@ -8,15 +8,16 @@ import importlib
#TODO TENTO IMPORT VYMYSLET, abych naloadoval package custom a nemusel nic pridat (vymyslet dynamicke volani z cele package ci)
#from v2realbot.strategyblocks.indicators.custom._upscaled_rsi_wip import upscaledrsi
from v2realbot.strategyblocks.indicators.custom.barparams import barparams
from v2realbot.strategyblocks.indicators.custom.basestats import basestats
from v2realbot.strategyblocks.indicators.custom.delta import delta
from v2realbot.strategyblocks.indicators.custom.divergence import divergence
from v2realbot.strategyblocks.indicators.custom.model import model
from v2realbot.strategyblocks.indicators.custom.opengap import opengap
from v2realbot.strategyblocks.indicators.custom.slope import slope
from v2realbot.strategyblocks.indicators.custom.conditional import conditional
from v2realbot.strategyblocks.indicators.custom.mathop import mathop
import v2realbot.strategyblocks.indicators.custom as ci
# from v2realbot.strategyblocks.indicators.custom.barparams import barparams
# from v2realbot.strategyblocks.indicators.custom.basestats import basestats
# from v2realbot.strategyblocks.indicators.custom.delta import delta
# from v2realbot.strategyblocks.indicators.custom.divergence import divergence
# from v2realbot.strategyblocks.indicators.custom.model import model
# from v2realbot.strategyblocks.indicators.custom.opengap import opengap
# from v2realbot.strategyblocks.indicators.custom.slope import slope
# from v2realbot.strategyblocks.indicators.custom.conditional import conditional
# from v2realbot.strategyblocks.indicators.custom.mathop import mathop
# import v2realbot.strategyblocks.indicators.custom as ci
@ -143,7 +144,7 @@ def populate_dynamic_custom_indicator(data, state: StrategyState, name):
#pozor jako defaultní hodnotu dává engine 0 - je to ok?
try:
#subtype = "ci."+subtype
subtype = "ci."+subtype+"."+subtype
custom_function = eval(subtype)
res_code, new_val = custom_function(state, custom_params)
if res_code == 0:

View File

@ -1,4 +1,4 @@
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.utils.utils import isrising, isfalling,isfallingc, isrisingc, zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
from traceback import format_exc
@ -9,13 +9,16 @@ from traceback import format_exc
def value_or_indicator(state,value):
#preklad direktivy podle typu, pokud je int anebo float - je to primo hodnota
#pokud je str, jde o indikator a dotahujeme posledni hodnotu z nej
if isinstance(value, (int, float)):
if isinstance(value, (float, int)):
return value
elif isinstance(value, str):
try:
#pokud existuje v indikatoru MA bereme MA jinak indikator, pokud neexistuje bereme bar
ret = get_source_or_MA(state, indicator=value)[-1]
state.ilog(lvl=0,e=f"Pro porovnani bereme posledni hodnotu {ret} z indikatoru {value}")
lvl = 0
if ret == 0:
lvl = 1
state.ilog(lvl=lvl,e=f"Pro porovnani bereme posledni hodnotu {ret} z indikatoru {value}")
except Exception as e :
ret = 0
state.ilog(lvl=1,e=f"Neexistuje indikator s nazvem {value} vracime 0" + str(e) + format_exc())
@ -38,6 +41,8 @@ def evaluate_directive_conditions(state, work_dict, cond_type):
"above": lambda ind, val: get_source_or_MA(state, ind)[-1] > value_or_indicator(state,val),
"equals": lambda ind, val: get_source_or_MA(state, ind)[-1] == value_or_indicator(state,val),
"below": lambda ind, val: get_source_or_MA(state, ind)[-1] < value_or_indicator(state,val),
"fallingc": lambda ind, val: isfallingc(get_source_or_MA(state, ind), val),
"risingc": lambda ind, val: isrisingc(get_source_or_MA(state, ind), val),
"falling": lambda ind, val: isfalling(get_source_or_MA(state, ind), val),
"rising": lambda ind, val: isrising(get_source_or_MA(state, ind), val),
"crossed_down": lambda ind, val: buy_if_crossed_down(state, ind, value_or_indicator(state,val)),

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategyblocks.indicators.cbar_price import populate_cbar_tick_price_indicator
from v2realbot.strategyblocks.indicators.custom.custom_hub import populate_dynamic_custom_indicator
from v2realbot.strategyblocks.indicators.custom_hub import populate_dynamic_custom_indicator
from v2realbot.strategyblocks.indicators.slope import populate_dynamic_slope_indicator
from v2realbot.strategyblocks.indicators.slopeLP import populate_dynamic_slopeLP_indicator
from v2realbot.strategyblocks.indicators.ema import populate_dynamic_ema_indicator

View File

@ -33,18 +33,18 @@ def go_conditions_met(state, data, signalname: str, direction: TradeDirection):
#dont_buy_above = value nebo hazev indikatoru
#TESTUJEME SPECIFICKY DONT_GO -
#u techto ma smysl pouze OR
#jak OR tak i AND
cond_dict = state.vars.conditions[KW.dont_go][signalname][smer]
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
state.ilog(lvl=1,e=f"SPECIFIC PRECOND {smer} {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"SPECIFIC PRECOND =OR= {smer} {result}", **conditions_met, cond_dict=cond_dict)
if result:
return False
# #OR neprosly testujeme AND
# result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
# state.ilog(lvl=0,e=f"EXIT CONDITIONS of activeTrade {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict)
# if result:
# return True
#OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
state.ilog(lvl=1,e=f"SPECIFIC PRECOND =AND={smer} {result}", **conditions_met, cond_dict=cond_dict)
if result:
return False
#tyto timto nahrazeny - dat do konfigurace (dont_short_when, dont_long_when)
#dont_buy_when['rsi_too_high'] = state.indicators.RSI14[-1] > safe_get(state.vars, "rsi_dont_buy_above",50)
@ -110,7 +110,7 @@ def common_go_preconditions_check(state, data, signalname: str, options: dict):
if state.vars.last_exit_index is not None:
index_to_compare = int(state.vars.last_exit_index)+int(next_signal_offset)
if index_to_compare > int(data["index"]):
state.ilog(lvl=1,e=f"NEXT SIGNAL OFFSET from EXIT {next_signal_offset} waiting - TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_exit_index=state.vars.last_exit_index)
state.ilog(lvl=1,e=f"NEXT SIGNAL OFFSET from EXIT {next_signal_offset} waiting - TOO SOON {signalname}", currindex=data["index"], index_to_compare=index_to_compare, last_exit_index=state.vars.last_exit_index)
return False
# if is_open_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), open_rush) or is_close_rush(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), close_rush):

View File

@ -45,7 +45,7 @@ def execute_prescribed_trades(state: StrategyState, data):
res = state.buy(size=size)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation LONG {res}")
#nastaveni SL az do notifikace, kdy je známá
#TODO nastaveni SL az do notifikace, kdy je známá
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)

View File

@ -38,7 +38,7 @@ def signal_search(state: StrategyState, data):
# to vse za predpokladu, ze neni aktivni trade
def execute_signal_generator(state, data, name):
state.ilog(lvl=0,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
options = safe_get(state.vars.signals, name, None)
if options is None:
@ -60,7 +60,7 @@ def execute_signal_generator(state, data, name):
custom_function = eval(signal_plugin)
custom_function()
except NameError:
state.ilog(lvl=1,e="Custom plugin {signal_plugin} not found")
state.ilog(lvl=1,e=f"Custom plugin {signal_plugin} not found")
else:
short_enabled = safe_get(options, "short_enabled",safe_get(state.vars, "short_enabled",True))
long_enabled = safe_get(options, "long_enabled",safe_get(state.vars, "long_enabled",True))

View File

@ -408,6 +408,8 @@ qu = Queue()
#zoneNY = tz.gettz('America/New_York')
zoneNY = pytz.timezone('US/Eastern')
zonePRG = pytz.timezone('Europe/Amsterdam')
def print(*args, **kwargs):
if QUIET_MODE:
pass
@ -567,8 +569,8 @@ def is_open_hours(dt, business_hours: dict = None):
and dt.date() not in holidays \
and business_hours["from"] <= dt.time() < business_hours["to"]
#vraci zda dane pole je klesajici (bud cele a nebo jen pocet poslednich)
def isfalling_old(pole: list, pocet: int = None):
#vraci zda dane pole je klesajici (bud cele a nebo jen pocet poslednich) - no same values
def isfallingc(pole: list, pocet: int = None):
if pocet is None: pocet = len(pole)
if len(pole)<pocet: return False
pole = pole[-pocet:]
@ -599,8 +601,8 @@ def isfalling(pole: list, pocet: int = None):
res = all(i > j for i, j in zip(new_pole, new_pole[1:]))
return res
#vraci zda dane pole je roustouci (bud cele a nebo jen pocet poslednich)
def isrising_old(pole: list, pocet: int = None):
#vraci zda dane pole je roustouci (bud cele a nebo jen pocet poslednich) - no same values
def isrisingc(pole: list, pocet: int = None):
if pocet is None: pocet = len(pole)
if len(pole)<pocet: return False
pole = pole[-pocet:]