From d9abc19c1f55f406b1810ead0b9de4937b93fc74 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Fri, 5 May 2023 18:20:52 +0200 Subject: [PATCH] gui js refactors + ilogs database --- test.db | Bin 0 -> 12288 bytes testy/testSqlite3.py | 97 +++++ testy/tinyDBselect.py | 58 ++- testy/tinyFLUXtest.py | 44 ++ testy/tinydbTest.py | 4 + v2realbot/ENTRY_backtest_strategyVykladaci.py | 12 +- v2realbot/__pycache__/config.cpython-310.pyc | Bin 2779 -> 2804 bytes .../common/__pycache__/model.cpython-310.pyc | Bin 6187 -> 6392 bytes v2realbot/common/model.py | 9 +- v2realbot/config.py | 2 + v2realbot/controller/services.py | 92 +++- v2realbot/main.py | 38 +- v2realbot/static/index.html | 52 ++- v2realbot/static/js/archivechart.js | 404 +++++++++++++++--- v2realbot/static/js/archivetables.js | 9 +- v2realbot/static/js/livewebsocket.js | 100 ++--- v2realbot/static/js/mytables.js | 33 +- v2realbot/static/js/realtimechart.js | 90 +--- v2realbot/static/js/utils.js | 139 ++++++ v2realbot/static/main.css | 73 +++- .../strategy/StrategyOrderLimitVykladaci.py | 6 +- ...trategyOrderLimitVykladaci.cpython-310.pyc | Bin 8094 -> 8180 bytes .../strategy/__pycache__/base.cpython-310.pyc | Bin 13520 -> 13896 bytes v2realbot/strategy/base.py | 56 ++- .../utils/__pycache__/utils.cpython-310.pyc | Bin 8151 -> 8151 bytes v2realbot/utils/ilog.py | 92 ++++ 26 files changed, 1105 insertions(+), 305 deletions(-) create mode 100644 test.db create mode 100644 testy/testSqlite3.py create mode 100644 testy/tinyFLUXtest.py create mode 100644 v2realbot/utils/ilog.py diff --git a/test.db b/test.db new file mode 100644 index 0000000000000000000000000000000000000000..33e5e0a4da3241f050ef0058c6cac6af775f10a7 GIT binary patch literal 12288 zcmeI1%WGU!7{F&HuQtidv?3I&$YpA!g`Rlck0RKp5rmMUHsC@@&ohm-sh!5!B3TRw zTClDp8&RY#>dHUBRTm=N72MUGyVOFZ|3LA()24)!!8y2Sg*zuRxvy{T@BGep9=Un< z?fI3pSfsPvGj^?DwLP_Zy>_f9YPDMV9)b7Ps{tFMTRZT(zHNKZW>K42dcFx~8|`+j zeF;9QS7o3wP#LHUR0b*om4V7YWuP)p8K?|Y2A;fuQ;mszFCVD)rdC!%yx9L(>VC4i z8oNuU&n}f+GDot1E;SX((05BEmv6Z;Op zt&Xo*cRFsncH3s-;Z07=zBWHM;QNXTw(C#X?jc4G7YoNv6bp;<^AB!PbfZ1J?O3pD zw)pV;+0{qK4wrwMk4@jHwO#Ai)=SMVr|(SdEz7QNAC$2-AC+TIzIiHw%0OkHGVoLx z7;iSGCdUpgPV}sGURW70lEM?jeTv9QERd02GZ758PR!nV_N%{t*md`_PB{;Bjuu2} zMzB^!&j^eK7e|WD`IS(f>VyzFu-lD(?b65nBhGfqBP*diEttpA2ZOjI7I6_Na+*sd z42zN{8CA5M^{L6xFMw_-gYMQqy5ZrlFw#b*5EWJ-jw3;aCx=+HNn2utO{Rn!`72Z1 z^+9lq4^L+(cUVe|Y_J^EnS``d7BR`Batw>8ai4BJlkVyuI;w`J3*HgqK^A5~f;$78 znnom~V=Rg&CF{Fu{Jc2Zv(#Zvn4pM}P}YScWP8BMe#*mzATrVK$UVpb4(6n?Ot zQAB8|ALFH4XNKoZB3dFTLD&X{IQ0S&F=*Ho$|@Y9l_l>__4~u~HZM0WfcprKF%wH80n`&!js!h)%QN>AZHvg#?W%Eew`k zN~BW^WuKxnVw$3joptzE)u%wWq>|B6T92Sk6mgX}vWyx;0)DrXeL zt%Lz4DuIH+P6TE-qrOkqPrKpiHh0c~W=kE(=(0`{?+GwT7=pkZ18vDWdG< z(O9HmC`6nqf*jKU5dmF-#+VGIo%P=S(Gt#@lsvYcp`bOGhMYyla*YCE&L(FhN#zRS z%I_CX?cWKfu$a*rCW4HXwTeOB1&UzWgJnd5JKMxK(#neDp`*ZLQqqm&(v1(I<7W6S zYdnXhg{HI-=0Z-RK|tionL2n(w9{Udbd5~9<$D9^GTbSzG!@#Q6d;>8EN%eIctn+_ zGBE;8-FH{drMo$Zj%7?0TyoALENd|Ku!JZD8ion-3^Qz^W>kW^NmxpcwI^!riT2I* zckPeb=0Rzldstp=Jt|Mts=I=z3{(ax1C@cwKxLpZP#LHUR0jSh2FB~pw9eIkt7kls zzPlG9zR8KWwFe?xPQ>*#MBto=tF6|#+Wq=}djQ*P{aE|u-@c=5YB%Z`j~lN|L&Qc- z#Ksgvgq(=pBt+1hh?Bb^LvPfFe?oNo;4X;R$cdPqfQXP0v3Yl#j4n>pa~=$h^&@hg Y){N#xjE`hSEZ=KjsK@T%sg`g417OfpCIA2c literal 0 HcmV?d00001 diff --git a/testy/testSqlite3.py b/testy/testSqlite3.py new file mode 100644 index 0000000..0e81d66 --- /dev/null +++ b/testy/testSqlite3.py @@ -0,0 +1,97 @@ +import sqlite3 +from v2realbot.config import DATA_DIR +from v2realbot.utils.utils import json_serial +from uuid import UUID, uuid4 +import json +from datetime import datetime +from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account + +sqlite_db_file = DATA_DIR + "/v2trading.db" +conn = sqlite3.connect(sqlite_db_file) +#standardne vraci pole tuplů, kde clen tuplu jsou sloupce +#conn.row_factory = lambda c, r: json.loads(r[0]) +#conn.row_factory = lambda c, r: r[0] +#conn.row_factory = sqlite3.Row + +#CREATE TABLE +# c = conn.cursor() +# createTable= "CREATE TABLE runner_logs (runner_id varchar(32) NOT NULL, time real NOT NULL, data json NOT NULL);" +# print(c.execute(createTable)) +# sql = ("CREATE INDEX index_runner_logs ON runner_logs (runner_id, time);") +# print(c.execute(sql)) + +#testovaci objekty +insert = dict(time=datetime.now(), side="ddd", rectype=RecordType.BAR, id=uuid4()) +insert_list = [dict(time=datetime.now().timestamp(), side="ddd", rectype=RecordType.BAR, id=uuid4()),dict(time=datetime.now().timestamp(), side="ddd", rectype=RecordType.BAR, id=uuid4()),dict(time=datetime.now().timestamp(), side="ddd", rectype=RecordType.BAR, id=uuid4()),dict(time=datetime.now().timestamp(), side="ddd", rectype=RecordType.BAR, id=uuid4())] + +def insert_log(runner_id: UUID, time: float, logdict: dict): + c = conn.cursor() + json_string = json.dumps(logdict, default=json_serial) + res = c.execute("INSERT INTO runner_logs VALUES (?,?,?)",[str(runner_id), time, json_string]) + conn.commit() + return res.rowcount + +def insert_log_multiple(runner_id: UUID, loglist: list): + c = conn.cursor() + insert_data = [] + for i in loglist: + row = (str(runner_id), i["time"], json.dumps(i, default=json_serial)) + insert_data.append(row) + c.executemany("INSERT INTO runner_logs VALUES (?,?,?)", insert_data) + conn.commit() + return c.rowcount + + # c = conn.cursor() + # json_string = json.dumps(logdict, default=json_serial) + # res = c.execute("INSERT INTO runner_logs VALUES (?,?,?)",[str(runner_id), time, json_string]) + # print(res) + # conn.commit() + # return res + +#returns list of ilog jsons +def read_log_window(runner_id: UUID, timestamp_from: float, timestamp_to: float): + conn.row_factory = lambda c, r: json.loads(r[0]) + c = conn.cursor() + res = c.execute(f"SELECT data FROM runner_logs WHERE runner_id='{str(runner_id)}' AND time >={ts_from} AND time <={ts_to}") + return res.fetchall() + +#returns number of deleted elements +def delete_logs(runner_id: UUID): + c = conn.cursor() + res = c.execute(f"DELETE from runner_logs WHERE runner_id='{str(runner_id)}';") + print(res.rowcount) + conn.commit() + return res.rowcount + +print(insert_log(str(uuid4()), datetime.now().timestamp(), insert)) +c = conn.cursor() +ts_from = 1683108821.08872 +ts_to = 1683108821.08874 +# res = c.execute(f"SELECT runner_id, time, data FROM runner_logs where time > {ts_from} and time <{ts_to}") +# result = res.fetchall() + +# res= delete_logs("7f9866ac-c742-47f4-a329-1d2b6721e781") +# print(res) + +# res = read_log_window(runner_id="33", timestamp_from=11 , timestamp_to=22) +# print(res) + +res = insert_log_multiple(uuid4(), insert_list) +print(res) + +# res = read_log_window("3340e257-d19a-4179-baf3-3b39190acde3", ts_from, ts_to) + +# print(res) + +# for r in res.fetchall(): +# print(dict(r)) + + +#print(res.description) +#print(result) + + + + + + diff --git a/testy/tinyDBselect.py b/testy/tinyDBselect.py index 5a7aae1..ff247ce 100644 --- a/testy/tinyDBselect.py +++ b/testy/tinyDBselect.py @@ -19,14 +19,58 @@ from tinydb.operations import set import json from rich import print -arch_header_file = DATA_DIR + "/arch_header.json" -arch_detail_file = DATA_DIR + "/arch_detail.json" -#db layer to store runner archive -db_arch_h = TinyDB(arch_header_file, default=json_serial) -db_arch_d = TinyDB(arch_detail_file, default=json_serial) +#vyzkouset https://github.com/MrPigss/BetterJSONStorage + + +insert = {'datum': datetime.now(), 'side': "dd", 'name': 'david','id': uuid4(), 'order': "neco"} +class RunnerLogger: + def __init__(self, runner_id: UUID) -> None: + self.runner_id = runner_id + runner_log_file = DATA_DIR + "/runner_log.json" + db_runner_log = TinyDB(runner_log_file, default=json_serial) + +def insert_log_multiple(runner_id: UUID, logList: list): + runner_table = db_runner_log.table(str(runner_id)) + res = runner_table.insert_multiple(logList) + return res + +def insert_log(runner_id: UUID, logdict: dict): + runner_table = db_runner_log.table(str(runner_id)) + res = runner_table.insert(logdict) + return res + + +def read_log_window(runner_id: UUID, timestamp_from: float, timestamp_to: float): + runner_table = db_runner_log.table(str(runner_id)) + res = runner_table.search((where('datum') >= timestamp_from) & (where('datum') <= timestamp_to)) + if len(res) == 0: + return -1, "not found" + return 0, res + +def delete_log(runner_id: UUID): + res = db_runner_log.drop_table(str(runner_id)) + if res is None: + return -1, "not found" + return 0, runner_id + +# runner_id = uuid4() +# for i in range(0,10): +# print(insert_log(runner_id, insert)) + +print(delete_log(runner_id="2459a6ff-a350-44dc-9c14-11cfae07f7e9")) + +print(read_log_window("ae9cdf8f-5cd0-4a49-8cfe-c486e21cb4fa",1,99999999999999)) + + +#2459a6ff-a350-44dc-9c14-11cfae07f7e9 +#ae9cdf8f-5cd0-4a49-8cfe-c486e21cb4fa + + +#db_runner_log.drop_tables() +print(db_runner_log.tables()) # res = db_arch_h.update(set('note', "ahoj"), where('id') == "74aa524e-3ed4-41fb-8166-f20946520344") # print(res) -res = db_arch_d.all() -print(res) \ No newline at end of file +#res = db_runner_log.all() +#print(res) diff --git a/testy/tinyFLUXtest.py b/testy/tinyFLUXtest.py new file mode 100644 index 0000000..fe911e0 --- /dev/null +++ b/testy/tinyFLUXtest.py @@ -0,0 +1,44 @@ + +from typing import Any, List +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.timeframe import TimeFrame +from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account +from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange +from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial +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 +import importlib +from queue import Queue +#from tinydb import TinyDB, Query, where +#from tinydb.operations import set +import json +from rich import print +from tinyflux import Point, TinyFlux + + +runner_log_file = DATA_DIR + "/runner_flux__log.json" +#db layer to store runner archive +db_runner_log = TinyFlux(runner_log_file) + +insert_dict = {'datum': datetime.now(), 'side': "dd", 'name': 'david','id': uuid4(), 'order': "neco"} +#json.dumps(insert_dict, default=json_serial) +p1 = Point(time=datetime.now(), tags=insert_dict) + +db_runner_log.insert(p1) + +res=db_runner_log.all() +print(res) + + +# #db_runner_log.drop_table('hash') +# res = runner_table.get(where('side') == "dd") +# print(res) +# # res = db_arch_h.update(set('note', "ahoj"), where('id') == "74aa524e-3ed4-41fb-8166-f20946520344") +# # print(res) +# res = runner_table.all() +# print(res) diff --git a/testy/tinydbTest.py b/testy/tinydbTest.py index 62a62c4..fb3ef5e 100644 --- a/testy/tinydbTest.py +++ b/testy/tinydbTest.py @@ -114,6 +114,10 @@ db = TinyDB(db_file, default=json_serial) db.truncate() insert = {'datum': datetime.now(), 'side': OrderSide.BUY, 'name': 'david','id': uuid4(), 'order': orderList} + + + + #insert record db.insert(a.__dict__) diff --git a/v2realbot/ENTRY_backtest_strategyVykladaci.py b/v2realbot/ENTRY_backtest_strategyVykladaci.py index ce8098d..9dcaa02 100644 --- a/v2realbot/ENTRY_backtest_strategyVykladaci.py +++ b/v2realbot/ENTRY_backtest_strategyVykladaci.py @@ -191,6 +191,8 @@ def next(data, state: StrategyState): state.vars.blockbuy = 0 return 0 + state.ilog(e="-----") + try: ## slope vyresi rychlé sesupy - jeste je treba podchytit pomalejsi sesupy @@ -245,6 +247,10 @@ def next(data, state: StrategyState): # state.ilog(e="Slope - MA"+str(state.indicators.slopeMA[-1])) else: + #pokud plnime historii musime ji plnit od zacatku, vsehcny idenitifkatory maji spolecny time + #kvuli spravnemu zobrazovani na gui + state.indicators.slope.append(0) + state.indicators.slopeMA.append(0) state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback) except Exception as e: @@ -357,7 +363,7 @@ def next(data, state: StrategyState): #HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru lp = state.interface.get_last_price(symbol=state.symbol) - 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) + 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, data=data, stratvars=state.vars) #maxSlopeMA = -0.03 #SLOPE ANGLE PROTECTIONs @@ -461,7 +467,7 @@ def init(state: StrategyState): state.indicators['slope'] = [] state.indicators['slopeMA'] = [] #static indicators - those not series based - state.statinds['angle'] = {} + state.statinds['angle'] = dict(minimum_slope=state.vars["minimum_slope"]) state.vars["ticks2reset_backup"] = state.vars.ticks2reset def main(): @@ -475,7 +481,7 @@ def main(): name = os.path.basename(__file__) se = Event() pe = Event() - s = StrategyOrderLimitVykladaci(name = name, symbol = "BAC", account=Account.ACCOUNT1, next=next, init=init, stratvars=stratvars, open_rush=10, close_rush=0, pe=pe, se=se) + s = StrategyOrderLimitVykladaci(name = name, symbol = "BAC", account=Account.ACCOUNT1, next=next, init=init, stratvars=stratvars, open_rush=10, close_rush=0, pe=pe, se=se, ilog_save=True) s.set_mode(mode = Mode.BT, debug = False, start = datetime(2023, 4, 14, 10, 42, 0, 0, tzinfo=zoneNY), diff --git a/v2realbot/__pycache__/config.cpython-310.pyc b/v2realbot/__pycache__/config.cpython-310.pyc index e3b2ecba7b7a31c9417dea6143037e9683b80e33..d49f68a33dc8ef8e509c375f1ebf3063f40a414a 100644 GIT binary patch delta 661 zcmZ9IJ#Q015QcB{~ z(@-QeB3wfa6)hZA&7-s>N#w-RV zW+rACuy_#SoIa+IfaE!4SxB)Qq*)#^YzDGy7ILfrc~<14S7K$DVHKFIu`0@Qc-{O& zz5)fd07Z5c)kQQfp;}|huSk1Q#-8#4bO)WolkrgMhkftp7(;3F__J}}JLLmUUXDqX zkc9s_P%bC&ts&aUX`-Owg8wDZD#bCZ;U1a@Lo>UU8>T*E!22wjU4QM!Cd-xD(DZ-v+Q z)v?s?^RX?2Csre383nPTR0`drk*KJ=sxYT8ukc@okq#fZ$9#O-{}f&_ZldFR2*Lax z;m>5n{}|cJ%GBP$j@xWE8g|pQAJ`42C4F80urBrOx>I+zcbh`ZWzw!tC1mQJU2i&@ zcHMEE-F^F@?O;KDM5O;H`V0BcDBk$j$RwK1>+TZ|PWpUEdT%j delta 665 zcmYk2J#Q015QcB~HIbWll^}SLl1=wb3~; zm)Q5COV?ioAdErK-M}yc#)2lQ5bY}%de0!{hEO|T5DzQs3WiZXR8T+gL4vXf8k8B7 zCDC;K7>{|nX!m? z=PQ*A8kCz7QHdDggTgHtTjHDVVOc&!mDW@`b;V$(f859Aj(E)q!ttNvPb)~_8G@%d z&R{&4IZjPnRV$4gX%rQl z-1f8w?%d{l!q-B4g#<3U^i&+|&Ujwt1&N|WN#g9DMTeojJ9GBj`3>4(I9yWZ*Ewd-A{+uF3-y{;$x;ThFE z5C3U_zhysyRK3w?_qMv%obCFy-Er>OcJseh{*CNux1LOJf8^d~boBHN!BEol*s5QA H(r^9+b|a5M diff --git a/v2realbot/common/__pycache__/model.cpython-310.pyc b/v2realbot/common/__pycache__/model.cpython-310.pyc index 9978461870e3b98eb65eb37b9421296dc66f7eab..cd3cb2d68cb96cc1143c607a50432f8597b175f6 100644 GIT binary patch delta 1666 zcmaKs&u<$=6vsWWcfDSFe|y)CX$7UJL^x1>m(UcNmZl{@sag>!E1}5gPK%Rbm+=pd z;ex~!O|O-T1P9;%hzl1a{sM%=Z4aEdfrQ!<;>7#DjW?B$@M^z)@6Fq{JMS|yUqAi# zGw#T7>l-mVTY` zT9k+BG-x`ebU6>xsHR?-*CfA1(=uhi;|}{F`4Ri>>JAdxWrD{Eahc$A!G!$c0|WLu zr60nlUPZZmbvhhMwK2Uk7nM+n*bkR(4Mt^2S z7hgsHjzP{sBFH(&%aHSs*C0!f3y|ZGb;w1?CCKXlaPl;ncw+n*NkPG94s-R1NpZD> zIt?NVuF&5a@&6)`4QhFT!ln*1xrKy+THY#sAgMBvVjQc?PF))JIG>P^?XyiQjqDQ5 zL43cKxepBhNi_wt0zrW!D*XZp5XM}Tk)%LY_z^!3WQE^mzgzk-d_tJyW`S^n`KeXv z-DP!#RJ{UOgD?k1b(Y-qf)I0IB>$mt$6gVOhm_xDtXzaqmz5qu&48J0K@_ijgCsXc zJG=7wM7@c~Tae43-P<7Z9lFO)kQ~yceWypwBVq%1zyGrNSJk*e348jk_o%qS!Q9&s z#uf!X)@#9Wv4x<{y-?o?esJEUcy7%4x2969=&kTPjo%F)Kao*f(>+c|>58LAuECglvPzc6f8Hsq60zX7UiO!UWWEMStEt@f@FKILPt_ z@7q~ljcE(Z$z(9RIbK&UB3EGCysbtz1{1ZZ|7<@mdIa~WtM6kpgfQT;m>S}VS~ehT zBMfsH-qE+=GUiQKO#K5`3Ni-SQ+s57LHC^Og0)bbr#Mp!Yrc=??WgANar6I)ziLiU z%7J94cXUeTkErL8%ui?3q^G9(_vF_loE!=ZN@1-itPKU=3LF)_hZ65w9sQv5q}TF8 OKk<9M>pQ;D?*0pW!$S}N delta 1434 zcmaJ>O;6NN6rIAfbUJP4&2%~-A2AA;;73qnl#fA06j6g1F)SLR4orbj1bj2#%3xf% zguKL^iE-gVSHulFH%2!m8r{t=aA#a-Tp91Vm9j8#nx47$y!+m3&w01Mj=VbTE;){s zpx=$R6XnPLru$6SX4Qt*p+4x{J;}fhoiJH8i-~eFa5+v?LyO82fIjun*w>Py&`OvZE}_|c2~8eV8+wQKj<)pI$_;@Y8e!9} z1g+gzOoS%=Cuxs{AVs{zyv}(#&JC)wsji86C+6|YDSBoj%_;`t!e{ zhA9#)64Ro0O0y^=CW&pw`Hra3IEhMQF4xjQpg0yc=1JI?w~K375UNKaMxtipJQ5Wy zc)Tc!8bI_Y7qRG3p5?p{kLUiH;MI-D1UBHZ9E5B|f?jzF;b{O{!0NN+EcZW3`{{nI zdB?YNJ4Ug2J8Q6I>{jFvb{He58%xu-!&s4wD?b>-5 z_PWph)~Juu;Ji90c5CBIR>ZWXPZ9Y@ead!fQy^7E#L=Om5|!(Yi@+b z4|;pMOT@3!T`dq;3G^HKF6ufUbzX^ATK87{IVyUJYJ;m$C+9G#eWoKshdBT6x=SWz z>BPVwm)gdo80NTS7GfCV20quo6gMa^?@_E%m*lxihULtBWi@1z>SnbjOSPMovKsXd zbsnd^`Fl}MF~3@pRjCnKHu(w4biRM|N_l1|~dnk7RCCurN6mDUTOR)V} yGaJy8QmW17FJ0(X3CkrMm2gnP774W__N2lqdb4`dvOnVpA@ZVCxWW;7zVHXgniH7- diff --git a/v2realbot/common/model.py b/v2realbot/common/model.py index e6c038b..c62b13f 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -58,20 +58,21 @@ class RunRequest(BaseModel): mode: Mode note: Optional[str] = None debug: bool = False + ilog_save: bool = False bt_from: datetime = None bt_to: datetime = None cash: int = 100000 - - class RunnerView(BaseModel): id: UUID + strat_id: UUID run_started: Optional[datetime] = None run_mode: Mode run_name: Optional[str] = None run_note: Optional[str] = None run_account: Account + run_ilog_save: Optional[bool] = False run_symbol: Optional[str] = None run_trade_count: Optional[int] = 0 run_profit: Optional[float] = 0 @@ -83,12 +84,14 @@ class RunnerView(BaseModel): #Running instance - not persisted class Runner(BaseModel): id: UUID + strat_id: UUID run_started: Optional[datetime] = None run_mode: Mode run_account: Account run_symbol: Optional[str] = None run_name: Optional[str] = None run_note: Optional[str] = None + run_ilog_save: Optional[bool] = False run_trade_count: Optional[int] run_profit: Optional[float] run_positions: Optional[int] @@ -174,6 +177,8 @@ class RunArchive(BaseModel): bt_from: Optional[datetime] = None bt_to: Optional[datetime] = None stratvars: Optional[dict] = None + settings: Optional[dict] = None + ilog_save: Optional[bool] = False profit: float = 0 trade_count: int = 0 end_positions: int = 0 diff --git a/v2realbot/config.py b/v2realbot/config.py index a272a83..bdec1cb 100644 --- a/v2realbot/config.py +++ b/v2realbot/config.py @@ -2,6 +2,8 @@ from alpaca.data.enums import DataFeed from v2realbot.enums.enums import Mode, Account, FillCondition from appdirs import user_data_dir + +LOG_RUNNER_EVENTS = False #no print in console QUIET_MODE = False #how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index 92eee53..70a259d 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -6,11 +6,12 @@ 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 -from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange -from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial +from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange, Bar +from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours +from v2realbot.utils.ilog import delete_logs 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 +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 import importlib from queue import Queue from tinydb import TinyDB, Query, where @@ -117,7 +118,7 @@ def delete_stratin(id: UUID): def inject_stratvars(id: UUID, stratvars_parsed_new: AttributeDict, stratvars_parsed_old: AttributeDict): for i in db.runners: - if str(i.id) == str(id): + if str(i.strat_id) == str(id): #inject only those changed, some of them cannot be changed (for example pendingbuys) changed_keys = [] @@ -180,7 +181,7 @@ def modify_stratin_running(si: StrategyInstance, id: UUID): ##enable realtime chart - inject given queue for strategy instance ##webservice listens to this queue -async def stratin_realtime_on(id: UUID, rtqueue: Queue): +async def runner_realtime_on(id: UUID, rtqueue: Queue): for i in db.runners: if str(i.id) == str(id): i.run_instance.rtqueue = rtqueue @@ -189,7 +190,7 @@ async def stratin_realtime_on(id: UUID, rtqueue: Queue): print("ERROR NOT FOUND") return -2 -async def stratin_realtime_off(id: UUID): +async def runner_realtime_off(id: UUID): for i in db.runners: if str(i.id) == str(id): i.run_instance.rtqueue = None @@ -199,7 +200,7 @@ async def stratin_realtime_off(id: UUID): return -2 ##controller (run_stratefy, pause, stop, reload_params) -def pause_stratin(id: UUID): +def pause_runner(id: UUID): for i in db.runners: print(i.id) if str(i.id) == id: @@ -215,7 +216,7 @@ def pause_stratin(id: UUID): print("no ID found") return (-1, "not running instance found") -def stop_stratin(id: UUID = None): +def stop_runner(id: UUID = None): chng = [] for i in db.runners: #print(i['id']) @@ -236,6 +237,13 @@ def stop_stratin(id: UUID = None): return (-2, "not found" + str(id)) def is_stratin_running(id: UUID): + for i in db.runners: + if str(i.strat_id) == str(id): + if i.run_started is not None and i.run_stopped is None: + return True + return False + +def is_runner_running(id: UUID): for i in db.runners: if str(i.id) == str(id): if i.run_started is not None and i.run_stopped is None: @@ -280,14 +288,14 @@ def capsule(target: object, db: object): i.run_pause_ev = None i.run_stop_ev = None #ukladame radek do historie (pozdeji refactor) - save_history(id=i.id, st=target, runner=i, reason=reason) + save_history(id=i.strat_id, st=target, runner=i, reason=reason) #store in archive header and archive detail archive_runner(runner=i, strat=target) #mazeme runner po skonceni instance db.runners.remove(i) print("Runner STOPPED") - +#stratin run def run_stratin(id: UUID, runReq: RunRequest): if runReq.mode == Mode.BT: if runReq.bt_from is None: @@ -336,7 +344,12 @@ def run_stratin(id: UUID, runReq: RunRequest): next=next, init=init, stratvars=stratvars, - open_rush=open_rush, close_rush=close_rush, pe=pe, se=se) + open_rush=open_rush, + close_rush=close_rush, + pe=pe, + se=se, + runner_id=id, + ilog_save=runReq.ilog_save) print("instance vytvorena", instance) #set mode if runReq.mode == Mode.LIVE or runReq.mode == Mode.PAPER: @@ -359,7 +372,9 @@ def run_stratin(id: UUID, runReq: RunRequest): vlakno.start() print("Spuštěna", instance.name) ##storing the attributtes - pozor pri stopu je zase odstranit - runner = Runner(id = i.id, + #id runneru je nove id, stratin se dava dalsiho parametru + runner = Runner(id = id, + strat_id = i.id, run_started = datetime.now(zoneNY), run_pause_ev = pe, run_name = name, @@ -368,13 +383,14 @@ def run_stratin(id: UUID, runReq: RunRequest): run_stop_ev = se, run_thread = vlakno, run_account = runReq.account, + run_ilog_save = runReq.ilog_save, run_mode = runReq.mode, run_instance = instance) db.runners.append(runner) print(db.runners) print(i) print(enumerate()) - return (0, i.id) + return (0, id) except Exception as e: return (-2, "Exception: "+str(e)) return (-2, "not found") @@ -403,9 +419,17 @@ def archive_runner(runner: Runner, strat: StrategyInstance): else: bp_from = None bp_to = None - id = uuid4() - runArchive: RunArchive = RunArchive(id = id, - strat_id = runner.id, + + settings = dict(resolution=strat.state.timeframe, + rectype=strat.state.rectype, + configs=dict( + BT_FILL_CONS_TRADES_REQUIRED=BT_FILL_CONS_TRADES_REQUIRED, + BT_FILL_LOG_SURROUNDING_TRADES=BT_FILL_LOG_SURROUNDING_TRADES, + BT_FILL_CONDITION_BUY_LIMIT=BT_FILL_CONDITION_BUY_LIMIT, + BT_FILL_CONDITION_SELL_LIMIT=BT_FILL_CONDITION_SELL_LIMIT)) + + runArchive: RunArchive = RunArchive(id = runner.id, + strat_id = runner.strat_id, name=runner.run_name, note=runner.run_note, symbol=runner.run_symbol, @@ -413,9 +437,11 @@ def archive_runner(runner: Runner, strat: StrategyInstance): stopped=runner.run_stopped, mode=runner.run_mode, account=runner.run_account, + ilog_save=runner.run_ilog_save, bt_from=bp_from, bt_to = bp_to, stratvars = strat.state.vars, + settings = settings, profit=round(float(strat.state.profit),2), trade_count=len(strat.state.tradeList), end_positions=strat.state.positions, @@ -434,7 +460,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance): print("is not numpy", key, value) flattened_indicators[key]= value - runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = id, + runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = runner.id, name=runner.run_name, bars=strat.state.bars, indicators=flattened_indicators, @@ -452,14 +478,15 @@ def get_all_archived_runners(): res = db_arch_h.all() return 0, res -#delete runner in archive and archive detail +#delete runner in archive and archive detail and runner logs def delete_archived_runners_byID(id: UUID): try: resh = db_arch_h.remove(where('id') == id) resd = db_arch_d.remove(where('id') == id) - if len(resh) == 0 or len(resd) == 0: - return -1, "not found "+str(resh) + " " + str(resd) - return 0, str(resh) + " " + str(resd) + reslogs = delete_logs(id) + if len(resh) == 0 or len(resd) == 0 or reslogs ==0: + return -1, "not found "+str(resh) + " " + str(resd) + " " + str(reslogs) + return 0, str(resh) + " " + str(resd) + " " + str(reslogs) except Exception as e: return -2, str(e) @@ -493,7 +520,30 @@ def get_alpaca_history_bars(symbol: str, datetime_object_from: datetime, datetim #datetime_object_from = datetime(2023, 2, 27, 18, 51, 38, tzinfo=datetime.timezone.utc) #datetime_object_to = datetime(2023, 2, 27, 21, 51, 39, tzinfo=datetime.timezone.utc) bar_request = StockBarsRequest(symbol_or_symbols=symbol,timeframe=timeframe, start=datetime_object_from, end=datetime_object_to, feed=DataFeed.SIP) + #print("before df") bars = client.get_stock_bars(bar_request) + result = [] + for row in bars.data[symbol]: + if is_open_hours(row.timestamp): + result.append(row) + + # print("df", bars) + # print(bars.info()) + # bars = bars.droplevel(0) + # print("after drop", bars) + # print(bars.info()) + # print("before tz", bars) + # bars = bars.tz_convert('America/New_York') + # print("before time", bars) + # bars = bars.between_time("9:30","16:00") + # print("after time", bars) + # bars = bars.reset_index() + # bars = bars.to_dict(orient="records") + #print(ohlcvList) + #ohlcvList = {} + + #bars = {} + return 0, bars.data[symbol] except Exception as e: return -2, str(e) diff --git a/v2realbot/main.py b/v2realbot/main.py index 33e2221..4710279 100644 --- a/v2realbot/main.py +++ b/v2realbot/main.py @@ -13,6 +13,7 @@ from fastapi.security import APIKeyHeader import uvicorn from uuid import UUID import v2realbot.controller.services as cs +from v2realbot.utils.ilog import get_log_window from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveDetail, Bar, RunArchiveChange from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query from fastapi.responses import HTMLResponse, FileResponse @@ -95,14 +96,14 @@ async def websocket_endpoint( api_key: Annotated[str, Depends(get_api_key)], ): await websocket.accept() - if not cs.is_stratin_running(runner_id): + if not cs.is_runner_running(runner_id): #await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA, reason="Strat not running") - raise WebSocketException(code=status.WS_1003_UNSUPPORTED_DATA, reason="Stratin not running.") + raise WebSocketException(code=status.WS_1003_UNSUPPORTED_DATA, reason="Runner not running.") return else: print("stratin exists") q: Queue = Queue() - await cs.stratin_realtime_on(id=runner_id, rtqueue=q) + await cs.runner_realtime_on(id=runner_id, rtqueue=q) # tx task; reads data from queue and sends to websocket async def websocket_tx_task(ws, _q): @@ -158,7 +159,7 @@ async def websocket_endpoint( print("CLIENT DISCONNECTED for", runner_id) finally: q.put("break") - await cs.stratin_realtime_off(runner_id) + await cs.runner_realtime_off(runner_id) @app.get("/threads/", dependencies=[Depends(api_key_auth)]) def _get_all_threads(): @@ -227,16 +228,16 @@ def _run_stratin(stratin_id: UUID, runReq: RunRequest): elif res < 0: raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error: {res}:{id}") -@app.put("/stratins/{stratin_id}/pause", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) -def _pause_stratin(stratin_id): - res, id = cs.pause_stratin(id=stratin_id) +@app.put("/runners/{runner_id}/pause", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) +def _pause_runner(runner_id): + res, id = cs.pause_runner(id=runner_id) if res == 0: return id elif res < 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}") -@app.put("/stratins/{stratin_id}/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) -def _stop_stratin(stratin_id): - res, id = cs.stop_stratin(id=stratin_id) +@app.put("/runners/{runner_id}/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) +def _stop_runner(runner_id): + res, id = cs.stop_runner(id=runner_id) if res == 0: return id elif res < 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}") @@ -248,9 +249,9 @@ def _delete_stratin(stratin_id): elif res < 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}") -@app.put("/stratins/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) -def stop_all_stratins(): - res, id = cs.stop_stratin() +@app.put("/runners/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) +def stop_all_runners(): + res, id = cs.stop_runner() if res == 0: return id elif res < 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}") @@ -310,6 +311,15 @@ def _get_archived_runner_details_byID(runner_id) -> RunArchiveDetail: else: raise HTTPException(status_code=404, detail=f"No runner with id: {runner_id} a {set}") +#get archived runners detail by id +@app.get("/archived_runners_log/{runner_id}", dependencies=[Depends(api_key_auth)]) +def _get_archived_runner_log_byID(runner_id: UUID, timestamp_from: float, timestamp_to: float) -> list[dict]: + res = get_log_window(runner_id,timestamp_from, timestamp_to) + if len(res) > 0: + return res + else: + raise HTTPException(status_code=404, detail=f"No logs found with id: {runner_id} and between {timestamp_from} and {timestamp_to}") + #get alpaca history bars @app.get("/history_bars/", dependencies=[Depends(api_key_auth)]) def _get_alpaca_history_bars(symbol: str, datetime_object_from: datetime, datetime_object_to: datetime, timeframe_amount: int, timeframe_unit: TimeFrameUnit) -> list[Bar]: @@ -317,7 +327,7 @@ def _get_alpaca_history_bars(symbol: str, datetime_object_from: datetime, dateti if res == 0: return set else: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"No data found") + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"No data found {res} {set}") #join cekej na dokonceni vsech diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index 7fbdd62..53de61e 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -1,5 +1,5 @@ - + @@ -51,12 +51,16 @@ -
+
-

+                        
+
+

+                            

+                        
@@ -95,16 +99,18 @@ - +
+ - + + @@ -152,6 +158,7 @@ + @@ -159,20 +166,22 @@ + - + - + + - - - + + + @@ -216,7 +225,7 @@
- +
@@ -245,6 +254,7 @@ +
IdStratId Started Mode SymbolAccountAccountilog Paused Profit Trades
IdStratId NameSymbolSym Note started stopped mode account bt_frombt_tobt_toilog stratvars profittradecntend_posend_pos_avgptradepospos_avgp open
@@ -418,8 +428,16 @@
- - + + +
+
+ + +
+
+ +
+
+ +
+ config options +
+

diff --git a/v2realbot/static/js/archivechart.js b/v2realbot/static/js/archivechart.js index 95c8152..640e72c 100644 --- a/v2realbot/static/js/archivechart.js +++ b/v2realbot/static/js/archivechart.js @@ -1,6 +1,10 @@ var tradeDetails = new Map(); var toolTip = null var CHART_SHOW_TEXT = false +// var vwapSeries = null +// var volumeSeries = null +var markersLine = null +var avgBuyLine = 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]...} @@ -42,10 +46,25 @@ function transform_data(data) { var avgp_markers = [] var markers = [] var markers_line = [] + var last_timestamp = 0.1 + var iterator = 0.001 data.trades.forEach((trade, index, array) => { obj = {}; a_markers = {} timestamp = Date.parse(trade.order.filled_at)/1000 + //light chart neumi vice zaznamu ve stejny cas + //protoze v BT se muze stat vice tradu v jeden cas, testujeme stejne hodnoty a pripadne pricteme jednu ms + //tradu s jednim casem muze byt za sebou vic, proto iterator + if (last_timestamp == timestamp) { + last_timestamp = timestamp + timestamp = timestamp + iterator + iterator += 0.001 + } + else { + last_timestamp = timestamp + iterator = 0.001 + } + if (trade.order.side == "buy") { //avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena if (trade.pos_avg_price !== null) { @@ -103,12 +122,11 @@ function transform_data(data) { transformed["markers_line"] = markers_line transformed["avgp_markers"] = avgp_markers //get additional indicators - //TBD return transformed } //unit: Min, Hour, Day, Week, Month -//prepare data before displaying archived chart - fetch history bars if necessary +//prepares data before displaying archived chart - fetch history bars if necessary function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunnerDetail) { req = {} req["symbol"] = archRunner.symbol @@ -133,7 +151,7 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn contentType: "application/json", data: req, success:function(data){ - console.log("one minute bars", JSON.stringify(data)) + console.log("one minute bars before", JSON.stringify(data)) data.map((el)=>{ cas = new Date(el.timestamp) el.time = cas.getTime()/1000; @@ -154,7 +172,6 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn }) } - //render chart of archived runs function chart_archived_run(archRecord, data, oneMinuteBars) { if (chart !== null) { @@ -203,105 +220,270 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { //initialize chart document.getElementById("chart").style.display = "block" - var chartOptions = { width: 1300, - height: 600, - leftPriceScale: {visible: true}, - layout: { - background: { - type: 'solid', - color: '#000000', - }, - textColor: '#d1d4dc', - }, - grid: { - vertLines: { - visible: true, - color: "#434d46" - }, - horzLines: { - color: "#667069", - visible:true - }, - }, - } - chart = LightweightCharts.createChart(container1, chartOptions); - chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: { - mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true - }}) + initialize_chart() container1.append(switcherElement) - var archCandlestickSeries = null + candlestickSeries = null switch_to_interval(intervals[1]) chart.timeScale().fitContent(); function switch_to_interval(interval) { - //prip prenuti prepisujeme candlestick a markery - - if (archCandlestickSeries) { + //prip prpenuti prepisujeme candlestick a markery + if (candlestickSeries) { last_range = chart.timeScale().getVisibleRange() - chart.removeSeries(archCandlestickSeries); - archCandlestickSeries = null + chart.removeSeries(candlestickSeries); + candlestickSeries = null } else { last_range = null } - 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 - }, - }); - archCandlestickSeries.setData(AllCandleSeriesesData.get(interval)); + + if (interval == native_resolution) { + //indicators are in native resolution only + display_indicators(data); + } + else { + remove_indicators(); + } + + intitialize_candles() + candlestickSeries.setData(AllCandleSeriesesData.get(interval)); + if (last_range) { chart.timeScale().setVisibleRange(last_range); } } - 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, - }, - }); + //TBD + //pro kazdy identifikator zobrazime button na vypnuti zapnuti + //vybereme barvu pro kazdy identifikator + //zjistime typ idenitfikatoru - zatim right vs left + function display_indicators(data) { + console.log("indikatory", JSON.stringify(data.indicators,null,2)) + //podobne v livewebsokcets.js - dat do jedne funkce + if (data.hasOwnProperty("indicators")) { + // console.log("jsme uvnitr indikatoru") + var indicators = data.indicators + //if there are indicators it means there must be at least two keys (time which is always present) + if (Object.keys(indicators).length > 1) { + for (const [key, value] of Object.entries(indicators)) { + if (key !== "time") { + //initialize indicator and store reference to array + var obj = {name: key, series: null} + //start + //console.log(key) + //get configuation of indicator to display + conf = get_ind_config(key) + + //INIT INDICATOR BASED on CONFIGURATION + + //MOVE TO UTILS ro reuse?? + if (conf && conf.display) { + + //tranform data do správného formátru + items = [] + value.forEach((element, index, array) => { + item = {} + + item["time"] = indicators.time[index] + item["value"] = element + //console.log("objekt indicatoru",item) + items.push(item) + }); + + if (conf.embed) { + obj.series = chart.addLineSeries({ + color: colors.shift(), + priceScaleId: conf.priceScaleId, + title: (conf.titlevisible?conf.name:""), + lineWidth: 1 + }); + + //toto nejak vymyslet konfiguracne, additional threshold lines + if (key == "slopeMA") { + //natvrdo nakreslime lajnu pro min angle + //TODO predelat na configuracne + const minSlopeLineOptopns = { + price: data.statinds.angle.minimum_slope, + color: '#b67de8', + lineWidth: 1, + lineStyle: 2, // LineStyle.Dotted + axisLabelVisible: true, + title: "max:", + }; + + const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns); + } + } + //INDICATOR on new pane + else { console.log("not implemented")} + + //add options + obj.series.applyOptions({ + lastValueVisible: false, + priceLineVisible: false, + }); + //add data + obj.series.setData(items) + + // add to indList array - pole zobrazovanych indikatoru + indList.push(obj); + + + } + + + + + + + + + + + + + + + + + // if (momentumIndicatorNames.includes(key)) { + // obj.series = chart.addLineSeries({ + // priceScaleId: 'left', + // color: colors.shift(), + // title: key, + // lineWidth: 1, + // lastValueVisible: false + // }); + + // if (key == "slopeMA") { + // const minSlopeLineOptopns = { + // //vzit odnekud jinud? + // price: data.statinds.angle.minimum_slope, + // color: colors.shift(), + // lineWidth: 1, + // lineStyle: 2, // LineStyle.Dotted + // axisLabelVisible: true, + // title: "max:", + // }; + + // const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns); + // } + // } + // //ostatni + // else { + // obj.series = chart.addLineSeries({ + // title: key, + // color: colors.shift(), + // lineWidth: 1, + // lastValueVisible: false + // }); + // } + + // obj.series.applyOptions({ + // lastValueVisible: false, + // priceLineVisible: false, + // }); + + // try { + // obj.series.setData(items) + // } + // catch (error) { + // console.log("obj.series.setData(items)", items) + // console.error(error) + // } + + // //add to indList array - pole zobrazovanych indikatoru + // indList.push(obj); + + } + } + } + } + + //display vwap and volume + initialize_vwap() + vwapSeries.setData(transformed_data["vwap"]) + + initialize_volume() + volumeSeries.setData(transformed_data["volume"]) + console.log("volume") + } + + function remove_indicators() { + //reset COLORS + colors = reset_colors + + //remove CUSTOMS indicators if exists + indList.forEach((element, index, array) => { + chart.removeSeries(element.series); + } + ); + indList = []; + //remove BASIC indicators + if (vwapSeries !== null) { + chart.removeSeries(vwapSeries) + } + if (volumeSeries !== null) { + chart.removeSeries(volumeSeries) + } + } + + //gets indicators from archived data and displays them on the chart + + console.log("avgp_buy_line",transformed_data["avgp_buy_line"]) console.log("avgp_markers",transformed_data["avgp_markers"]) if (transformed_data["avgp_buy_line"].length > 0) { - var avgBuyLine = chart.addLineSeries({ + avgBuyLine = chart.addLineSeries({ // title: "avgpbuyline", color: '#e8c76d', // color: 'transparent', lineWidth: 1, lastValueVisible: false }); + + avgBuyLine.applyOptions({ + lastValueVisible: false, + priceLineVisible: false, + }); + + + try { avgBuyLine.setData(transformed_data["avgp_buy_line"]); + } + catch (error) { + console.log("avgbuyline") + } + avgBuyLine.setMarkers(transformed_data["avgp_markers"]) } - var markersLine = chart.addLineSeries({ + markersLine = chart.addLineSeries({ // title: "avgpbuyline", // color: '#d6d1c3', color: 'transparent', lineWidth: 1, lastValueVisible: false }); - markersLine.setData(transformed_data["markers_line"]); + + + + try { + markersLine.setData(transformed_data["markers_line"]); + } + catch (error) { + console.log("markersLine") + } + + markersLine.setMarkers(transformed_data["markers"]) - + //TBD dynamicky //pokud je nazev atributu X_candles vytvorit candles @@ -333,6 +515,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { //chart.subscribeCrosshairMove(param => { chart.subscribeCrosshairMove(param => { + //LEGEND SECTIOIN + firstRow.style.color = 'white'; + update_chart_legend(param); + + //TOOLTIP SECTION //$('#trade-timestamp').val(param.time) if ( param.point === undefined || @@ -390,15 +577,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { } }); - - - - - - - chart.subscribeClick(param => { $('#trade-timestamp').val(param.time) + if (archRecord.ilog_save == "true") { + fetch_log_data(param.time, archRecord.id); + } if ( param.point === undefined || !param.time || @@ -458,9 +641,90 @@ function chart_archived_run(archRecord, data, oneMinuteBars) { $("#statusName").text(archRecord.name) $("#statusMode").text(archRecord.mode) $("#statusAccount").text(archRecord.account) + $("#statusIlog").text("Logged:" + archRecord.ilog_save) $("#statusStratvars").text(JSON.stringify(archRecord.stratvars,null,2)) - + $("#statusSettings").text(JSON.stringify(archRecord.settings,null,2)) //TBD other dynamically created indicators +} + + +function fetch_log_data(timestamp, runner_id) { + timestamp_from = timestamp - 20 + timestamp_to = timestamp + 20 + req = {} + req["runner_id"] = runner_id; + req["timestamp_from"] = timestamp_from; + req["timestamp_to"] = timestamp_to; + $.ajax({ + url:"/archived_runners_log/"+runner_id, + beforeSend: function (xhr) { + xhr.setRequestHeader('X-API-Key', + API_KEY); }, + method:"GET", + contentType: "application/json", + data: req, + success:function(data){ + console.log("archived logs", JSON.stringify(data)) + display_log(data, timestamp) + }, + error: function(xhr, status, error) { + var err = eval("(" + xhr.responseText + ")"); + window.alert(JSON.stringify(xhr)); + console.log(JSON.stringify(xhr)); + } + }) +} + +function display_log(iterLogList, timestamp) { + //console.log("Incoming logline object") + + var lines = document.getElementById('lines') + var line = document.createElement('div') + line.classList.add("line") + const newLine = document.createTextNode("---------------") + line.appendChild(newLine) + lines.appendChild(line) + + iterLogList.forEach((logLine) => { + //console.log("logline item") + //console.log(JSON.stringify(logLine,null,2)) + + //
+ //
12233 Event
+ //
+ // Detaila mozna structured + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, + // sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + // quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + //
+ //
+ + highlighted = (parseInt(logLine.time) == parseInt(timestamp)) ? "highlighted" : "" + logcnt++; + row = '
'+logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message) +'
' + str_row = JSON.stringify(logLine.details, null, 2) + //row_detail = '
' + str_row + '
' + + row_detail = '
' + str_row + '
' + + var lines = document.getElementById('lines') + var line = document.createElement('div') + line.classList.add("line") + line.dataset.timestamp = logLine.time + + line.insertAdjacentHTML( 'beforeend', row ); + line.insertAdjacentHTML( 'beforeend', row_detail ); + //line.appendChild(newLine) + //var pre = document.createElement("span") + //pre.classList.add("pidi") + //const stLine = document.createTextNode(str_row) + //pre.appendChild(stLine) + //line.appendChild(pre) + lines.appendChild(line) + }); + $('#messages').animate({ + scrollTop: $('#lines')[0].scrollHeight}, 2000); + } \ No newline at end of file diff --git a/v2realbot/static/js/archivetables.js b/v2realbot/static/js/archivetables.js index 308d8f5..5b70b9e 100644 --- a/v2realbot/static/js/archivetables.js +++ b/v2realbot/static/js/archivetables.js @@ -7,7 +7,6 @@ $(document).ready(function () { $('#button_delete_arch').attr('disabled','disabled'); $('#button_edit_arch').attr('disabled','disabled'); - //selectable rows in archive table $('#archiveTable tbody').on('click', 'tr', function () { if ($(this).hasClass('selected')) { @@ -36,6 +35,7 @@ $(document).ready(function () { row = archiveRecords.row('.selected').data(); window.$('#editModalArchive').modal('show'); $('#editidarchive').val(row.id); + $('#editnote').val(row.note); $('#editstratvars').val(JSON.stringify(row.stratvars,null,2)); }); @@ -54,7 +54,7 @@ $(document).ready(function () { success:function(data){ $('#button_show_arch').attr('disabled',false); $('#chartContainerInner').addClass("show"); - $("#lines").html("
"+JSON.stringify(row.stratvars,null,2)+"
") + //$("#lines").html("
"+JSON.stringify(row.stratvars,null,2)+"
") //$('#chartArchive').append(JSON.stringify(data,null,2)); console.log(JSON.stringify(data,null,2)); @@ -134,6 +134,7 @@ $("#delModalArchive").on('submit','#delFormArchive', function(event){ window.alert(JSON.stringify(xhr)); console.log(JSON.stringify(xhr)); $('#deletearchive').attr('disabled', false); + archiveRecords.ajax.reload(); } }) }); @@ -154,6 +155,7 @@ var archiveRecords = } }, columns: [{ data: 'id' }, + {data: 'strat_id'}, {data: 'name'}, {data: 'symbol'}, {data: 'note'}, @@ -163,6 +165,7 @@ var archiveRecords = {data: 'account', visible: true}, {data: 'bt_from', visible: true}, {data: 'bt_to', visible: true}, + {data: 'ilog_save', visible: true}, {data: 'stratvars', visible: true}, {data: 'profit'}, {data: 'trade_count', visible: true}, @@ -176,7 +179,7 @@ var archiveRecords = // return format_date(data) // }, // }], - order: [[5, 'desc']], + order: [[6, 'desc']], paging: true, lengthChange: false, // createdRow: function( row, data, dataIndex){ diff --git a/v2realbot/static/js/livewebsocket.js b/v2realbot/static/js/livewebsocket.js index d51f793..76505a6 100644 --- a/v2realbot/static/js/livewebsocket.js +++ b/v2realbot/static/js/livewebsocket.js @@ -1,14 +1,13 @@ const momentumIndicatorNames = ["roc", "slope", "slopeMA"] -var indList = [] var pbiList = [] var ws = null; var logcnt = 0 var positionsPriceLine = null var limitkaPriceLine = null var angleSeries = 1 -var candlestickSeries -var volumeSeries -var vwapSeries +var candlestickSeries = null +var volumeSeries = null +var vwapSeries = null //get details of runner to populate chart status //fetch necessary - it could be initiated by manually inserting runnerId @@ -92,12 +91,12 @@ function connect(event) { iterLogList = parsed_data.iter_log //console.log("Incoming logline object") - var lines = document.getElementById('lines') - var line = document.createElement('div') - line.classList.add("line") - const newLine = document.createTextNode("---------------") - line.appendChild(newLine) - lines.appendChild(line) + // var lines = document.getElementById('lines') + // var line = document.createElement('div') + // line.classList.add("line") + // const newLine = document.createTextNode("---------------") + // line.appendChild(newLine) + // lines.appendChild(line) iterLogList.forEach((logLine) => { //console.log("logline item") @@ -194,7 +193,7 @@ function connect(event) { positions = parsed_data.positions const posLine = { price: positions.avgp, - color: 'black', + color: '#918686', lineWidth: 1, lineStyle: 1, // LineStyle.Dotted axisLabelVisible: true, @@ -221,7 +220,7 @@ function connect(event) { if (klic === "angle") { //nejsou vsechny hodnoty - if (Object.keys(hodnota).length > 0) { + if (Object.keys(hodnota).length > 1) { // console.log("angle nalezen"); // console.log(JSON.stringify(hodnota)); if (angleSeries !== 1) { @@ -271,49 +270,52 @@ function connect(event) { //console.log("object new - init and add") var obj = {name: key, series: null} - //predelat configuracne - //inicializace indicatoru - //momentum - if (momentumIndicatorNames.includes(key)) { - - - obj.series = chart.addLineSeries({ - priceScaleId: 'left', - title: key, - lineWidth: 1 - }); - - //natvrdo nakreslime lajnu pro min angle - //TODO predelat na configuracne - const minSlopeLineOptopns = { - price: parsed_data.statinds.angle.minimum_slope, - color: '#b67de8', - lineWidth: 2, - lineStyle: 2, // LineStyle.Dotted - axisLabelVisible: true, - title: "max:", - }; - - const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns); + //get configuation of indicator to display + conf = get_ind_config(key) - + //INIT INDICATOR BASED on CONFIGURATION + + //MOVE TO UTILS ro reuse?? + if (conf && conf.display) { + if (conf.embed) { + obj.series = chart.addLineSeries({ + color: colors.shift(), + priceScaleId: conf.priceScaleId, + lastValueVisible: conf.lastValueVisible, + title: (conf.titlevisible?conf.name:""), + lineWidth: 1 + }); + + //tady add data + obj.series.update({ + time: indicators.time, + value: value}); + indList.push(obj); + + //toto nejak vymyslet konfiguracne, additional threshold lines + if (key == "slopeMA") { + //natvrdo nakreslime lajnu pro min angle + //TODO predelat na configuracne + const minSlopeLineOptopns = { + price: parsed_data.statinds.angle.minimum_slope, + color: '#b67de8', + lineWidth: 1, + lineStyle: 2, // LineStyle.Dotted + axisLabelVisible: true, + title: "max:", + }; + + const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns); + } + } + //INDICATOR on new pane + else { console.log("not implemented")} } - //ostatni - else { - obj.series = chart.addLineSeries({ - //title: key, - lineWidth: 1, - lastValueVisible: false - }); - } - obj.series.update({ - time: indicators.time, - value: value}); - indList.push(obj); } //indicator exists in an array, lets update it else { //console.log("object found - update") + //tady add data searchObject.series.update({ time: indicators.time, value: value diff --git a/v2realbot/static/js/mytables.js b/v2realbot/static/js/mytables.js index 3bd903a..ba27867 100644 --- a/v2realbot/static/js/mytables.js +++ b/v2realbot/static/js/mytables.js @@ -10,7 +10,7 @@ function get_status(id) { runnerRecords.rows().iterator('row', function ( context, index ) { var data = this.row(index).data(); //window.alert(JSON.stringify(data)) - if (data.id == id) { + if (data.strat_id == id) { //window.alert("found"); if ((data.run_mode) == "backtest") { status_detail = data.run_mode} else { status_detail = data.run_mode + " | " + data.run_account} @@ -25,12 +25,12 @@ function get_status(id) { return status } -function is_running(id) { +function is_stratin_running(id) { var running = false runnerRecords.rows().iterator('row', function ( context, index ) { var data = this.row(index).data(); //window.alert(JSON.stringify(data)) - if (data.id == id) { + if (data.strat_id == id) { running = true } //window.alert("found") } @@ -181,7 +181,7 @@ $(document).ready(function () { //button refresh - $('#button_refresh').click(function () { + $('.refresh').click(function () { runnerRecords.ajax.reload(); stratinRecords.ajax.reload(); archiveRecords.ajax.reload(); @@ -255,7 +255,7 @@ $(document).ready(function () { event.preventDefault(); $('#button_pause').attr('disabled','disabled'); $.ajax({ - url:"/stratins/"+row.id+"/pause", + url:"/runners/"+row.id+"/pause", beforeSend: function (xhr) { xhr.setRequestHeader('X-API-Key', API_KEY); }, @@ -282,7 +282,7 @@ $(document).ready(function () { event.preventDefault(); $('#button_stop').attr('disabled','disabled'); $.ajax({ - url:"/stratins/"+row.id+"/stop", + url:"/runners/"+row.id+"/stop", beforeSend: function (xhr) { xhr.setRequestHeader('X-API-Key', API_KEY); }, @@ -310,7 +310,7 @@ $(document).ready(function () { event.preventDefault(); $('#buttonall_stop').attr('disabled','disabled'); $.ajax({ - url:"/stratins/stop", + url:"/runners/stop", beforeSend: function (xhr) { xhr.setRequestHeader('X-API-Key', API_KEY); }, @@ -343,6 +343,7 @@ $(document).ready(function () { $('#mode').val(localStorage.getItem("mode")); $('#account').val(localStorage.getItem("account")); $('#debug').val(localStorage.getItem("debug")); + $('#ilog_save').val(localStorage.getItem("ilog_save")); $('#runid').val(row.id); }); @@ -465,10 +466,12 @@ var runnerRecords = // }, }, columns: [{ data: 'id' }, + {data: 'strat_id'}, {data: 'run_started'}, {data: 'run_mode'}, {data: 'run_symbol'}, {data: 'run_account'}, + {data: 'run_ilog_save'}, {data: 'run_paused'}, {data: 'run_profit'}, {data: 'run_trade_count'}, @@ -489,6 +492,7 @@ $("#runModal").on('submit','#runForm', function(event){ localStorage.setItem("mode", $('#mode').val()); localStorage.setItem("account", $('#account').val()); localStorage.setItem("debug", $('#debug').val()); + localStorage.setItem("ilog_save", $('#ilog_save').val()); event.preventDefault(); $('#run').attr('disabled','disabled'); @@ -496,9 +500,19 @@ $("#runModal").on('submit','#runForm', function(event){ //rename runid to id Object.defineProperty(formData, "id", Object.getOwnPropertyDescriptor(formData, "runid")); delete formData["runid"]; + //console.log(formData) + if ($('#ilog_save').prop('checked')) { + formData.ilog_save = true; + } + else + { + formData.ilog_save = false; + } + // $('#subscribe').prop('checked') if (formData.bt_from == "") {delete formData["bt_from"];} if (formData.bt_to == "") {delete formData["bt_to"];} jsonString = JSON.stringify(formData); + //console.log(jsonString) //window.alert(jsonString); $.ajax({ url:"/stratins/"+formData.id+"/run", @@ -512,7 +526,10 @@ $("#runModal").on('submit','#runForm', function(event){ //pokud mame subscribnuto na RT if ($('#subscribe').prop('checked')) { //subscribe input value gets id of current runner - $('#runnerId').val($('#runid').val()); + //$('#runid').val() + //data obsuje ID runneru - na ten se subscribneme + console.log("vysledek z run:", data) + $('#runnerId').val(data); $( "#bt-conn" ).trigger( "click" ); } $('#runForm')[0].reset(); diff --git a/v2realbot/static/js/realtimechart.js b/v2realbot/static/js/realtimechart.js index 1b342e5..85b006d 100644 --- a/v2realbot/static/js/realtimechart.js +++ b/v2realbot/static/js/realtimechart.js @@ -1,76 +1,14 @@ - function populate_real_time_chart() { if (chart !== null) { chart.remove(); clear_status_header(); } - $('#chartContainerInner').addClass("show"); - //const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } }; - var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}} - // var chartOptions = { width: 1045, - // height: 600, - // leftPriceScale: {visible: true}, - // layout: { - // background: { - // type: 'solid', - // color: '#000000', - // }, - // textColor: '#d1d4dc', - // }, - // grid: { - // vertLines: { - // visible: false, - // }, - // horzLines: { - // color: 'rgba(42, 46, 57, 0.5)', - // }, - // }, - // } - - - chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions); - chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: { - mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true - }}) - candlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }}); - candlestickSeries.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 - }, - }); - - - volumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''}); - volumeSeries.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, - }, - }); - - vwapSeries = chart.addLineSeries({ - // title: "vwap", - color: '#2962FF', - lineWidth: 1, - lastValueVisible: false - }) - - //chart.timeScale().fitContent(); - - - //TBD unifikovat legendu - //TBD dynamicky zobrazovat vsechny indikatory - //document.getElementById('chart').style.display = 'inline-block'; - var legendlist = document.getElementById('legend'); - var firstRow = document.createElement('div'); - firstRow.innerText = '-'; - // firstRow.style.color = 'white'; - legendlist.appendChild(firstRow); - + initialize_chart() + intitialize_candles() + initialize_vwap() + initialize_volume() chart.subscribeClick(param => { //display timestamp in trade-timestamp input field @@ -78,24 +16,8 @@ function populate_real_time_chart() { }); chart.subscribeCrosshairMove((param) => { - if (param.time) { - const data = param.seriesData.get(vwapSeries); - const vwap = data.value !== undefined ? data.value : data.close; - const bars = param.seriesData.get(candlestickSeries); - const volumes = param.seriesData.get(volumeSeries); - firstRow.innerText = ""; - //iterate of custom indicators dictionary to get values of custom lines - // var customIndicator = {name: key, series: null} - indList.forEach(function (item) { - const ind = param.seriesData.get(item.series) - firstRow.innerText += item.name + " " + ind.value + " "; - }); - - firstRow.innerText += ' vwap' + ' ' + vwap.toFixed(2) + " O" + bars.open + " H" + bars.high + " L" + bars.low + " C" + bars.close + " V" + volumes.value + ""; - } - else { - firstRow.innerText = '-'; - } + firstRow.style.color = 'white'; + update_chart_legend(param); }); } diff --git a/v2realbot/static/js/utils.js b/v2realbot/static/js/utils.js index 193bcdb..00ae9a2 100644 --- a/v2realbot/static/js/utils.js +++ b/v2realbot/static/js/utils.js @@ -1,6 +1,144 @@ API_KEY = localStorage.getItem("api-key") var chart = null +var colors = ["#8B1874","#B71375","#B46060","#61c740","#BE6DB7","#898121","#4389d9","#00425A","#B5D5C5","#e61957"] +var reset_colors = colors +var indList = [] + +indConfig = {} +settings = {} +settings +//ostatni indicatory nez vwap, volume a bary +indConfig = [ {name: "ema", titlevisible: false, embed: true, display: true, priceScaleId: "right", lastValueVisible: false}, + {name: "slope", titlevisible: true, embed: true, display: false, priceScaleId: "left", lastValueVisible: false}, + {name: "slopeMA", titlevisible: true, embed: true, display: true, priceScaleId: "left", lastValueVisible: false},] + +function get_ind_config(indName) { + const i = indConfig.findIndex(e => e.name === indName); + if (i>-1) + { + return indConfig[i] + } + return null +} + + +//LEGEND INIT +var legendlist = document.getElementById('legend'); +var firstRow = document.createElement('div'); +firstRow.innerHTML = '-'; +// firstRow.style.color = 'white'; +legendlist.appendChild(firstRow); + +function update_chart_legend(param) { + + function name(val) { + return '
' + val + '' + } + function val(val) { + return '
' + val + '' + } + + if (param.time) { + firstRow.innerHTML = ""; + //BASIC INDICATORS + const bars = param.seriesData.get(candlestickSeries); + if (bars !== undefined) { + firstRow.innerHTML += name("O") + val(bars.open) + name("H") + val(bars.high) + name("L") + val(bars.low) + name("C") + val(bars.close) + } + + const volumes = param.seriesData.get(volumeSeries); + if (volumes !== undefined) { + firstRow.innerHTML += name("Vol") +val(volumes.value) + } + const data = param.seriesData.get(vwapSeries); + if (data !== undefined) { + const vwap = data.value !== undefined ? data.value : data.close; + firstRow.innerHTML += name('vwap') + val(vwap.toFixed(2)) + } + //ADDITIONAL CUSTOM INDICATORS + //iterate of custom indicators dictionary to get values of custom lines + // var customIndicator = {name: key, series: null} + indList.forEach(function (item) { + var ind = param.seriesData.get(item.series) + if (ind !== undefined) { firstRow.innerHTML += name(item.name) + val(ind.value.toFixed(3))} + }); + } + else { + firstRow.innerHTML = ''; + } +} + +function initialize_chart() { + $('#chartContainerInner').addClass("show"); + //PUVODNI BILY MOD + //var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}} + + //TMAVY MOD + var chartOptions = { width: 1080, + height: 600, + leftPriceScale: {visible: true}, + layout: { + background: { + type: 'solid', + color: '#000000', + }, + textColor: '#d1d4dc', + }, + grid: { + vertLines: { + visible: true, + color: "#434d46" + }, + horzLines: { + color: "#667069", + 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 + }}) +} + +//mozna atributy last value visible +function intitialize_candles() { + candlestickSeries = chart.addCandlestickSeries({ lastValueVisible: false, priceLineWidth:1, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }}); + candlestickSeries.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 + }, + }); + candlestickSeries.applyOptions({ + lastValueVisible: true, + priceLineVisible: true, + }); + +} + +function initialize_volume() { + volumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''}); + volumeSeries.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, + }, + }); +} + +function initialize_vwap() { + vwapSeries = chart.addLineSeries({ + // title: "vwap", + color: '#2962FF', + lineWidth: 1, + lastValueVisible: false, + priceLineVisible: false + }) +} //range switch pro chart https://jsfiddle.net/TradingView/qrb9a850/ function createSimpleSwitcher(items, activeItem, activeItemChangedCallback) { @@ -67,6 +205,7 @@ function clear_status_header() { $("#statusName").text("") $("#statusMode").text("") $("#statusAccount").text("") + $("#statusIlog").text("") $("#statusStratvars").text("") //clear previous logs from rt $("#lines").empty() diff --git a/v2realbot/static/main.css b/v2realbot/static/main.css index a0f7644..29ce6a8 100644 --- a/v2realbot/static/main.css +++ b/v2realbot/static/main.css @@ -1,9 +1,12 @@ -:root { +/* :root { --dt-row-selected: 18, 143, 175; +} */ +:root { + --dt-row-selected: 37, 120, 114; } tbody, td, tfoot, th, thead, tr { - border-color: inherit; + border-color: #7d7d8a; border-style: solid; border-width: 0; padding: 4px; @@ -11,10 +14,29 @@ tbody, td, tfoot, th, thead, tr { .secondary-bg { --bs-bg-opacity: 1; - background-color: #128faf; + background-color: #414749; } +/* #128faf; */ + .btn-outline-success { + --bs-btn-color: #75a9ac; + --bs-btn-border-color: #8b8b8b; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #3996a4; + --bs-btn-hover-border-color: #198754; + --bs-btn-focus-shadow-rgb: 25,135,84; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #40929d; + --bs-btn-active-border-color: #446379; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #4e7c85; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #839498; + --bs-gradient: none; +} + +/* .btn-outline-success { --bs-btn-color: #316164; --bs-btn-border-color: #247e85; --bs-btn-hover-color: #fff; @@ -29,6 +51,25 @@ tbody, td, tfoot, th, thead, tr { --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #839498; --bs-gradient: none; +} */ + +.badge { + --bs-badge-padding-x: 0.65em; + --bs-badge-padding-y: 0.35em; + --bs-badge-font-size: 0.75em; + --bs-badge-font-weight: 700; + --bs-badge-color: #fff; + --bs-badge-border-radius: var(--bs-border-radius); + display: inline-block; + padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); + font-size: var(--bs-badge-font-size); + font-weight: var(--bs-badge-font-weight); + line-height: 1; + color: var(--bs-body-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: var(--bs-badge-border-radius); } html { @@ -63,13 +104,23 @@ html { position: absolute; color: #050505; left: 115px; - top: 99px; + top: 115px; z-index: 1; - font-size: 16px; + font-size: 12px; line-height: 18px; font-weight: 300; } +.legendItemName { + margin-left: 5px; + display: flex; +} + +.legendItemValue { + margin-left: 5px; + display: flex; +} + #msgContainer { /* display: inline-block; */ overflow: auto; @@ -83,16 +134,17 @@ html { overflow: auto; height: 568px; flex-direction: column-reverse; + margin-left: 28px; } } -@media (min-width : 1681px) { - .msgContainerInner { +@media (min-width: 1681px) {} + .msgContainerInner { display: flex; overflow: auto; height: 568px; flex-direction: column-reverse; - } + margin-left: 28px; } @@ -104,7 +156,7 @@ pre { /* margin: 0 0 10px; */ font-size: 10px; /* line-height: 2.428571; */ - color: #333; + /* color: #333; */ word-break: break-all; word-wrap: break-word; /* background-color: #f5f5f5; */ @@ -126,11 +178,12 @@ pre { margin-left: 0px; font-size: normal; /* font-weight: bold; */ - display: -webkit-inline-box; + display: flex; background-color: #dfe4e6; /* color: white; */ padding: 1px; padding-left: 5px; + width: max-content; } .headerItem { diff --git a/v2realbot/strategy/StrategyOrderLimitVykladaci.py b/v2realbot/strategy/StrategyOrderLimitVykladaci.py index add4877..6c9a6c9 100644 --- a/v2realbot/strategy/StrategyOrderLimitVykladaci.py +++ b/v2realbot/strategy/StrategyOrderLimitVykladaci.py @@ -11,12 +11,12 @@ from random import randrange from alpaca.common.exceptions import APIError import copy from threading import Event - +from uuid import UUID class StrategyOrderLimitVykladaci(Strategy): - def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: Mode = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None) -> None: - super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se) + def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: Mode = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None: + super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se, runner_id, ilog_save) async def orderUpdateBuy(self, data: TradeUpdate): o: Order = data.order diff --git a/v2realbot/strategy/__pycache__/StrategyOrderLimitVykladaci.cpython-310.pyc b/v2realbot/strategy/__pycache__/StrategyOrderLimitVykladaci.cpython-310.pyc index ad85ab9631a72c11d2c29ac62c25732761758d6a..6d1b12ea3dd53a1067f2f7a193def3d587314fe4 100644 GIT binary patch delta 2000 zcmZ{l%WoS+7{EQ=wY`qLem72DpRf%T)1%H;7cWt5Lf;Is=0CEA)#J4AVDSOn@t<50$cmrZ)U!4=6j9( zaPH4J(>BdEkNkYIwFFP@n;G)>{>Zoe;DNB!zdDc`Af7WGFd?$-fe0lZYPmr$mOR<$ z{eS-6$`YY3FP}SI#0^?MnVo_2qZFtoN5GTwQtfRW2zcd}sIOKsK&L+N$;VIq5Ri{S z0}!Mdv{3^>Gzek&n6yov-3Cz_f*1`$97w7Ao~N`$AOXp3GiO4IMoM1T1MS;-F1j;H zLx&v2pcD3PdvbBu4_!1YwYnj*?aL+L0QAtLn$w{d_Q|;v^g+MW-2($KDBtZc1jF*3 zzUR?Bxegh2R&*`ZS)oqL>x+!16Y;=>-+E+>^ZYOG<%u9`RI8NbDS}*gM@%c0HYhyg-&f?MpVv;{GGNEZDB4&6xc&+zE zC0sdq;XfAvu%P!Q}G=JPS zO-}JWq0zBXG#60Vgca$(tyKxO*vQl-vr;(P_sl0W`5 z_QRp9oXqM%yU|!z!3m#~@)oP%JaM;fwfQ4H-yBc$5I4=s{AzN9%<}DI-}G+6bk}wU zUokfZ6_-k$3KIhn#%keq&EBXK**yO_*>$NZCz;9zMl2#_TF+Yrr@qre-MGvovbyUQ z?#~($IiFM6psY1ErOOvm^JKdDAXOwGTtu#iBxWaENte?;Yv-}^$b>k1ArAorxmYsuz6t`a7Bh1t*7vZT~Ftf^U7}vmp?+NRJu0^H}$%C`uGKN9A4$3`x5EoqnRks>`>F_2@!IJr+N>cy(4u+uzr)#0#vGH ohVBZz(WroOT3NK5rV4|n31fQIhK5CFi5xlI{Da;uJxo&CzXHIaxc~qF delta 2004 zcmZ`)&2Jk;6!&=7_BviWj^ieA^Vu{Fm?RDPY?G!*3B`rdhNNkbSc@ra6ZZHv}pYe}NAikU-oJ;=+ltC&UdQAr9OCiT7rm#)^uq{q39gG4H+Kys`gU z`gAF5hr=x%_WC?Ah(OTFD(Ke=My);JKYo=<18_PI7Q&Wuy?Vz1CkI_mm&;*Nu+gaLO^BHY) znD(%k)Ty+W_JOXQ_R|6Ijni{<5Z-6#c{&8|jzf>?F*>2gXPj=)uXp=be(Qs7L_)bc z=;y3b<;9HN)HKRrcocZbL*fgi-}@*ezE^s6-BHWT%JQ>d5q~I?VqD#CdlnWkboBxZ zk-Z_v(Nq6f-B$dQpy5+uL;Ltx{8O90>}t_8gD?x=1gu;kSIL`>4g$Q>C7#Q$q{8Qp zUHH7X7rfX1lwf`peHIbs#ZSTMp%Wk6TbiA4BZ0dyUlQXjx5piX0WLB)T4j(J_TRTH{(fJL8S@BuxhXaOh7@qv`Kcmegq-MTquGL|B#`cABmxLn>5pR|vVE8@HMb4?7YZuC69qHY(mGs&iG z6kBn$g1J+&_i`D@u;a_zuNL!;wrSa!Je1+6nirqBm|>QC7#8Mr8F!RQq2wrew#(l|=TjLA^?wrU1g_+2_tlN3 z%z@i}2qPs2?>FrnH1lxKI3rFFro|OGdNgMdzjpt-B#W1I=g|BRK{EN|_9eY8O^2t# zak(M-de=yoDD_SpAlHtTF)hp7v>1<~-G?BH??efIdU+dyiBLp%001*<+m@V1F7wJG z!`tK*w1Gb1bUFhQTm~+!bh@dL=g@CV($@td?q7}rXr2`kQ6e+iXfzm)YVBG)s&n*6 zp0BUYZZ% diff --git a/v2realbot/strategy/__pycache__/base.cpython-310.pyc b/v2realbot/strategy/__pycache__/base.cpython-310.pyc index 9dde1bb8efcc79596860b597d8be23e85d33a9b4..afd922cd186a9f64ddf0f567c1d378dfcaa9a399 100644 GIT binary patch delta 6503 zcmai2eQX@Zb>G?D`~D<(Jo0!)@^qAF9W7E4DcO=GE0j%2p-i1+l9KC<)AR9WN#1ey z?mW9kS<0!GK&*ivARKp`Zy=!ZQC9^D{h^>K(l~LGrghr7G4fB^g;O;D6!1Uo|3!=T zy;+h+u>yLB`R$wc-poU8wZnH=05i*~VFnhILGpQxbKCRE}*ZR$rmJ%|*F`x~=`vD_uX0(hs zs12G!+K`Y1jbSsZWd$8F4w*SEC+M&-Vjk8G3p!%7kC>y{sHjAZC(NVTQ9;Lyyg8TbMYCzxm4>17ev5U^dQcCq#OV+nhPS%|61@T+RA^T3p@*R4K^zf#K7u4hgDCFtbrlj@J zC+RVG_S11X0nZdYPEWvdfS%OTD>9v=Q=2OPvO0O4PScAZ7|{>Y=ky~WdY;Z~s&|Pt z3Y=NNxkN7u&J%k&pQdwybCeQTr57CkqRmRSzIvxsB_8!Goe8jJt){bLg*t%> ztX$D5ZRn1d>2{OV%2CkU{UNPFc^-=OG2irjqumO(q+)+70+_M~w$t|{pfY>Z;2t&9 zlB&ZkFZFB@BDI2`m2HIp4^aua<}G{ziz;1Wi1%eS0S`6~kSBu9=bS)&Luaf)b&HLm z^ht#HF+`z#9H~6t_R`ZQkURc3yfFJiA z>6*ma6hJ=esFrT5@LRr-9NTM;4CGqVt#WJLeEyLRdstLb#4_17QImA8?gEg$$vYL~0IUhL;1Qg%^+%O?Fzt zHy_ue<8>#4&7*>Vt3Wya5~W32vP)O^Zvv-=F9NUZ1=#%|jqXq^dyS6;uaM91X7JS| zSkhi7NgCMl!BY0s7FxuXs$^Ye@7nz>xut+KMT1-LEQag>sDYtW)5vY9D5s$g>kU3Jk;~DJB_m_3xI5KY2hkJWriGA z#U~!Fo-j9DYGi|X#)5!P#KW(6ru*baOeh6C=-?$xW z*4AtFn_06?_529K-RQ{AEipf8;HnAxb}bz{?Wpr}H)b8rwTmOyW*1l*HE^ix_Ylxp z-7knyr1*pIR9lQnSkm5rm@vKp)D~UI0_8;NjDpK(Ho$CPLr$X<{WE(7p${Pp;CPl6 zF3!xlTiDBlv5L+5Dr$Tdp@<+l4+hz_&n(#$kQDh^BojJHf^s*>kZv;e@U6%RLZ0F8 zM-$_}kFP!rkPo|yAU>%)cX2`V8KV)cluLGY6EjMdT?FS%^`c!db%rBx z{T`YDTSn+Y&A81ftE;d!b{&0R$#xg#8C1ZK$|(P@u2k+|C-Bv^>^U5hQmNUFr&4P)Z6|o6WHj|z#_EjSK@DMw zo%x&Q?ed9W_ojkzCFl(+G6{P5JH5}6cOL$_*Ct0kunFyfNg1pT z-Gynf1KU9ivei&=7$-(dPJ(~2?>FRiUh04KTt4gWZ{Zch2*sWkJ5G$%wF3Ao(-CBZ z&mm0nU@E(C3`sEwb3i%Ds&2C$al3#3O-Dgxu1NCi+&wd{Js zHOmXUl}Zi&AG1eQ?9?YvtOEZqbybba>VNaxz!+KM{~90Qs{>PQ94M8VgiRu~9i}qc z+}^;I!tL0CZq_$o@w0Apb3Drs@z?W_earjkkhLCKvC4HwddhaOTr!O1QhA-tV28w{ ziTOewsYMD58`pQT2zCjWu)mu&gazRzNaeuNvbR7b4_S;A2clmIlb{kMQ4&<+MExIr zCOsO&4C?1ltiRxEX)(e;HAsmaYRPv=AH*)qknAv3pziL22=1yDsYdNsH4ee`EX+lq zio49+fu!1VM`iut6Yz{2oXGP!&XR2=ov-2A)~ z5WZ}_0-~3Z>k$D3%mxxXHq7s5CeMp#!Le0go!>%gpWDRhD!?pGHU=~K^ZhW&4|N(i@Y=UQTjO6H{M7h8Y~?!$-$nQy!U5%N;C&Xp!M@MsA>&0 zU@rC?$$jGbgaZwK4O%Mk_l90RBHAPj8SIY{4rqP>cq{zu@WM$~(}`JnjTYgcwHtsR zh_$?cpV~tvn(+JPnM3Ifqs6_+q?``C%RjXceJhW*xEat;E@Jzr$2$%{%KKBCB=+`wb%zV9R zi_FmRY+zJj@1XR+y1ffL3+B{9-x5^3{97Z>s{Nj@{P5!ujVz1a{t1YjAk(e7(Zm1( zoxWLWunMvvZo&B2O`ySsaG%_lovt(Vb-7+QSOM!{1d*3TP~us5@p@r~eFnKQ0w$Qu4f05d zl;BOIM65(d-e!lej&6p18(|wk?9(qGwTbX01YBw-EbP2&>h@ZlvIoe=Wndp7;L!FB zR}37FU+f8hK`7P=fM2>n)Crm3UnTrO{;cn`e>$4+_wxV9r`o8C4(E|yFVv=?SeO*< zUW`zpDHW*#;Y3%f9&tf}cNi*Mhj^fP;Y!2@#l0Hw!_|nob2So#tC7%3f<|c!?lQt~ z3lazYF5SDL(pWbY*CTko%J;IBhnr)wa^cd!BMtfjhK4N?;(Z6CY2@L?D&xl5{UMDD z{X7)wKe=yww!4K#zECRw7#5cW5q@nv$zK~+`O0{@9c{tQ2gqV}wB-?0+>WkFRNX>u zmmR$e-l>R5D1=6!Fn=P|1W-_bg2QDI516Ey1PXi@U>|z4Vn4+eQi2{3{Iu|Jo`P^{ zkX3-)w|5NxedzE$>lb_Dfb|Q*7h3>#vDgB6AHyxlK0x>-!rvkMJpyh`_74DXjKr28 zp?!#~3kZ8&^Qz-tHtOZ|<>sw@Q-2P1#OH4z^%4TQE;lF(cP2JWXLZ(5dY`{HG1dNa zARTXm)mJJu`zG=y0P-Q%>*bL#hHxCA(}L)i`0#-*3G?pQIPS;ap(k7h!b=YP6|YZr zS-f<-bQoQ!qnOre8T-5Y^8$h81t`{!AVEyGZFSpoPulikK3s$wh^l0hsvnXZyle_o zeRtFv-ax>!1($_QiJfW5;(5Xj!u^9n$(DS_Q_F}N-bTn<3e*!jS~G%AfUgL?f2WR` z>Xv$^?SaN2CvAD$a}eA}z`f&n$u&whONE;)aIv^5j*segsbcH~LC0t6mQ{kI+HXO+ z8yUY2w98Q4wG@nh+2xAm&d1#J!}X5n4w!I}!l``@t0ET@rhxP98h~&|xXdDuxOU@* z>TYDiS!W*uINtS}CAMn0y;fNL=03M2Jl0o0D<#7=5P277U123iys}^R109EF91v1p z5+=917x5GdcaDpY1n3i(5$5GWAs=P`h+q5_!bkX7U$IE*n%0Csw}TPXiXy}iFd#Ycjy{|(Rw}FFOzvLGh%;0IReKRG1MnYA z4uz6f>qF@0znvT>DIS^{_YMF<%klVBU*sm7vcx4OTz{td^3*Ys;crcilR^I8R6a8V zicX?rG)m>t1V(B&3GH-qn9HX|NtWkNW!fT+kyCPVxr0Vc5X=RZ6Z3x`Qm0i%I` H_l5rj1e%oS delta 6160 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 Vv None: + def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: str = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None: #variable to store methods overriden by strategytypes (ie pre plugins) self.overrides = None self.symbol = symbol @@ -51,6 +53,8 @@ class Strategy: self.account = account self.key = get_key(mode=self.mode, account=self.account) self.rtqueue = None + self.runner_id = runner_id + self.ilog_save = ilog_save #TODO predelat na dynamické queues @@ -106,13 +110,13 @@ class Strategy: self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name) #propojujeme notifice s interfacem (pro callback) self.order_notifs.connect_callback(self) - self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype) + self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, ilog_save=self.ilog_save) elif mode == Mode.BT: self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata) self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end) self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt) - self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype) + self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save) self.order_notifs = None ##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade #zatim takto - pak pripadne do fajlu nebo jinak OPTIMALIZOVAT @@ -195,7 +199,7 @@ class Strategy: #ic(self.state.time) if self.mode == Mode.BT: - self.state.ilog(e="----- BT exec START", msg=f"{self.bt.time=}") + #self.state.ilog(e="----- BT exec START", msg=f"{self.bt.time=}") #pozor backtester muze volat order_updates na minuly cas - nastavi si bt.time self.bt.execute_orders_and_callbacks(self.state.time) #ic(self.bt.time) @@ -207,7 +211,8 @@ class Strategy: #ic(self.state.time) if self.mode == Mode.BT: - self.state.ilog(e="----- BT exec FINISH", msg=f"{self.bt.time=}") + pass + #self.state.ilog(e="----- BT exec FINISH", msg=f"{self.bt.time=}") #ic(self.bt.time) #ic(len(self.btdata)) #ic(self.bt.cash) @@ -282,9 +287,9 @@ class Strategy: print("REQUEST COUNT:", self.interface.mincnt) self.bt.backtest_end = datetime.now() - print(40*"*",self.mode, "BACKTEST RESULTS",40*"*") + #print(40*"*",self.mode, "BACKTEST RESULTS",40*"*") #-> account, cash,trades,open_orders - self.bt.display_backtest_result(self.state) + #self.bt.display_backtest_result(self.state) #this is(WILL BE) called when strategy is stopped # LIVE - pause or stop signal received @@ -301,6 +306,10 @@ class Strategy: self.dataloader.remove_stream(i) #pamatujeme si streamy, ktere ma strategie a tady je removneme + #posilame break na RT queue na frontend + if self.rtqueue is not None: + self.rtqueue.put("break") + #zavolame na loaderu remove streamer - mohou byt dalsi bezici strategie, ktery loader vyuzivaji #pripadne udelat shared loader a nebo dedicated loader #pokud je shared tak volat remove @@ -319,6 +328,7 @@ class Strategy: else: now = self.bt.time + self.state.ilog(e="NOTIF ARRIVED AT"+str(now)) print("NOTIFICATION ARRIVED AT:", now) self.update_live_timenow() @@ -413,10 +423,10 @@ class Strategy: #cleaning iterlog lsit #TODO pridat cistku i mimo RT blok - self.state.iter_log_list = [] - else: - #mazeme logy pokud neni na ws pozadovano - self.state.iter_log_list = [] + + if self.ilog_save: insert_log_multiple(self.state.runner_id, self.state.iter_log_list) + #smazeme logy + self.state.iter_log_list = [] @staticmethod def append_bar(history_reference, new_bar: dict): @@ -468,7 +478,7 @@ class StrategyState: triggerují callback, který následně vyvolá např. buy (ten se musí ale udít v čase fillu, tzn. callback si nastaví čas interfacu na filltime) po dokončení bt kroků před zahájením iterace "NEXT" se časy znovu updatnout na původni state.time """ - def __init__(self, name: str, symbol: str, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR): + def __init__(self, name: str, symbol: str, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR, runner_id: UUID = None, bt: Backtester = None, ilog_save: bool = False): self.vars = stratvars self.interface = interface self.positions = 0 @@ -483,6 +493,9 @@ class StrategyState: #time of last trade processed self.last_trade_time = 0 self.timeframe = None + self.runner_id = runner_id + self.bt = bt + self.ilog_save = ilog_save bars = {'high': [], 'low': [], @@ -525,17 +538,24 @@ class StrategyState: if self.mode == Mode.LIVE or self.mode == Mode.PAPER: self.time = datetime.now().timestamp() + #pri backtestingu logujeme BT casem (muze byt jiny nez self.time - napr. pri notifikacich a naslednych akcích) + if self.mode == Mode.BT: + time = self.bt.time + else: + time = self.time + if e is None: if msg is None: - row = dict(time=self.time, details=kwargs) + row = dict(time=time, details=kwargs) else: - row = dict(time=self.time, message=msg, details=kwargs) + row = dict(time=time, message=msg, details=kwargs) else: if msg is None: - row = dict(time=self.time, event=e, details=kwargs) + row = dict(time=time, event=e, details=kwargs) else: - row = dict(time=self.time, event=e, message=msg, details=kwargs) + row = dict(time=time, event=e, message=msg, details=kwargs) self.iter_log_list.append(row) row["name"] = self.name print(row) - #TBD mozna odsud to posilat do nejakeho struct logger jako napr. structlog nebo loguru, separatni file podle name + #zatim obecny parametr -predelat per RUN? + #if LOG_RUNNER_EVENTS: insert_log(self.runner_id, time=self.time, logdict=row) \ No newline at end of file diff --git a/v2realbot/utils/__pycache__/utils.cpython-310.pyc b/v2realbot/utils/__pycache__/utils.cpython-310.pyc index ceec23a02c937753b5a0bdc607ed501a59cd5e25..46a00c72d0748d1a764ed4aff732bd2b3e58f7e4 100644 GIT binary patch delta 127 zcmca^f8CxtpO=@50SHb`4c^FoTAK0J=IhdKjK=GMLPZ-u#5xdR2_kfX#4WbOf`Zh% z6iwEmtst?LAYvnk*fcpnR)TTEanpdd9b zMU%B?3rK7wh}ZxkHck$Zm0(;yIa5}VasB3Q*?EkN>n3Z-%L;%ESOii6Hf8={timestamp_from} AND time <={timestamp_to} ORDER BY time") + return res.fetchall() + +#returns number of deleted elements +def delete_logs(runner_id: UUID): + c = conn.cursor() + res = c.execute(f"DELETE from runner_logs WHERE runner_id='{str(runner_id)}';") + print(res.rowcount) + conn.commit() + return res.rowcount + +# print(insert_log(str(uuid4()), datetime.now().timestamp(), insert)) +# c = conn.cursor() +# ts_from = 1683108821.08872 +# ts_to = 1683108821.08874 +# res = c.execute(f"SELECT runner_id, time, data FROM runner_logs where time > {ts_from} and time <{ts_to}") +# result = res.fetchall() + +# res= delete_logs("7f9866ac-c742-47f4-a329-1d2b6721e781") +# print(res) + +# res = read_log_window(runner_id="33", timestamp_from=11 , timestamp_to=22) +# print(res) + +# res = insert_log_multiple(uuid4(), insert_list) +# print(res) + +# res = read_log_window("3340e257-d19a-4179-baf3-3b39190acde3", ts_from, ts_to) + +# print(res) + +# for r in res.fetchall(): +# print(dict(r)) + + +#print(res.description) +#print(result) + + + + + +