gui js refactors + ilogs database
This commit is contained in:
97
testy/testSqlite3.py
Normal file
97
testy/testSqlite3.py
Normal file
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
#res = db_runner_log.all()
|
||||
#print(res)
|
||||
|
||||
44
testy/tinyFLUXtest.py
Normal file
44
testy/tinyFLUXtest.py
Normal file
@ -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)
|
||||
@ -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__)
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="light">
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
@ -51,12 +51,16 @@
|
||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-outline-success btn-sm">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="statusHeader" data-bs-toggle="collapse" data-bs-target="#statusStratvars">
|
||||
<div id="statusHeader" data-bs-toggle="collapse" data-bs-target="#statusDetail">
|
||||
<div id="statusRegime" class="headerItem"></div>
|
||||
<div id="statusName" class="headerItem"></div>
|
||||
<div id="statusMode" class="headerItem"></div>
|
||||
<div id="statusAccount" class="headerItem"></div>
|
||||
<pre id="statusStratvars" class="headerItem collapse"></pre>
|
||||
<div id="statusIlog" class="headerItem"></div>
|
||||
<div id="statusDetail" class="headerItem collapse">
|
||||
<pre id="statusStratvars" class="headerItem"></pre>
|
||||
<pre id="statusSettings" class="headerItem"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart" style="display: None; float: left; "></div>
|
||||
<div class="legend" id="legend"></div>
|
||||
@ -95,16 +99,18 @@
|
||||
<button id="button_pause" class="btn btn-outline-success btn-sm">Pause/Unpause</button>
|
||||
<button id="button_stop" class="btn btn-outline-success btn-sm">Stop</button>
|
||||
<button id="button_stopall" class="btn btn-outline-success btn-sm">Stop All</button>
|
||||
<button id="button_refresh" class="btn btn-outline-success btn-sm">Refresh</button>
|
||||
<button id="button_refresh" class="refresh btn btn-outline-success btn-sm">Refresh</button>
|
||||
</div>
|
||||
<table id="runnerTable" class="table-striped table-bordered dataTable" style="width:100%; border-color: #dce1dc;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>StratId</th>
|
||||
<th>Started</th>
|
||||
<th>Mode</th>
|
||||
<th>Symbol</th>
|
||||
<th>Account</th>
|
||||
<th>Account</th>
|
||||
<th>ilog</th>
|
||||
<th>Paused</th>
|
||||
<th>Profit</th>
|
||||
<th>Trades</th>
|
||||
@ -152,6 +158,7 @@
|
||||
<button id="button_edit_arch" class="btn btn-outline-success btn-sm">Edit</button>
|
||||
<button id="button_delete_arch" class="btn btn-outline-success btn-sm">Delete</button>
|
||||
<button id="button_show_arch" class="btn btn-outline-success btn-sm">Show</button>
|
||||
<button id="button_refresh" class="refresh btn btn-outline-success btn-sm">Refresh</button>
|
||||
<!-- <button id="button_stopall" class="btn btn-outline-success btn-sm">Stop All</button>
|
||||
<button id="button_refresh" class="btn btn-outline-success btn-sm">Refresh</button> -->
|
||||
</div>
|
||||
@ -159,20 +166,22 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>StratId</th>
|
||||
<th>Name</th>
|
||||
<th>Symbol</th>
|
||||
<th>Sym</th>
|
||||
<th>Note</th>
|
||||
<th>started</th>
|
||||
<th>stopped</th>
|
||||
<th>mode</th>
|
||||
<th>account</th>
|
||||
<th>bt_from</th>
|
||||
<th>bt_to</th>
|
||||
<th>bt_to</th>
|
||||
<th>ilog</th>
|
||||
<th>stratvars</th>
|
||||
<th>profit</th>
|
||||
<th>tradecnt</th>
|
||||
<th>end_pos</th>
|
||||
<th>end_pos_avgp</th>
|
||||
<th>trade</th>
|
||||
<th>pos</th>
|
||||
<th>pos_avgp</th>
|
||||
<th>open</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -216,7 +225,7 @@
|
||||
<input type="text" class="form-control" id="editidarchive" name="id" placeholder="id" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="note" class="form-label">note</label>
|
||||
<label for="editnote" class="form-label">note</label>
|
||||
<textarea class="form-control" rows="2" id="editnote" name="note"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@ -245,6 +254,7 @@
|
||||
<button id="button_copy" class="btn btn-outline-success btn-sm">Copy JSON</button>
|
||||
<button id="button_delete" class="btn btn-outline-success btn-sm">Delete</button>
|
||||
<button id="button_run" class="btn btn-outline-success btn-sm">Run Strategy</button>
|
||||
<button id="button_refresh" class="refresh btn btn-outline-success btn-sm">Refresh</button>
|
||||
<table id="stratinTable" class="table-striped table-bordered dataTable" style="width:100%; border-color: #dce1dc;">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -418,8 +428,16 @@
|
||||
<input type="number" class="form-control" id="cash" name="cash" placeholder="cash" value="100000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cash" class="form-label">Subscribe for RT</label>
|
||||
<input type="checkbox" class="form-check-input mt-0" id="subscribe" name="subscribe" aria-label="Real time subscribe">
|
||||
<label for="ilog_save" class="form-label">Enable logs</label>
|
||||
<input type="checkbox" class="form-check" id="ilog_save" name="ilog_save" aria-label="Enable logs">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subscribe" class="form-label">Subscribe for RT</label>
|
||||
<input type="checkbox" class="form-check" id="subscribe" name="subscribe" aria-label="Real time subscribe">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="note" class="form-label">note</label>
|
||||
<textarea class="form-control" rows="1" id="note" name="note"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@ -433,6 +451,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="configContainer" class="flex-items">
|
||||
<label data-bs-toggle="collapse" data-bs-target="#configInner" aria-expanded="true">
|
||||
<h4><span class="badge secondary-bg">Config</span></h4>
|
||||
</label>
|
||||
<div id="configInner" class="collapse show">
|
||||
config options
|
||||
</div>
|
||||
</div>
|
||||
<div id="bottomContainer" class="flex-items" style="height: 800px">
|
||||
<BR>
|
||||
</div>
|
||||
|
||||
@ -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))
|
||||
|
||||
// <div class="line">
|
||||
// <div data-toggle="collapse" data-target="#rec1">12233 <strong>Event</strong></div>
|
||||
// <div id="rec1" class="collapse">
|
||||
// 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.
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
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>'
|
||||
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>'
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@ -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("<pre>"+JSON.stringify(row.stratvars,null,2)+"</pre>")
|
||||
//$("#lines").html("<pre>"+JSON.stringify(row.stratvars,null,2)+"</pre>")
|
||||
|
||||
//$('#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){
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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 '<div class="legendItemName">' + val + '</>'
|
||||
}
|
||||
function val(val) {
|
||||
return '<div class="legendItemValue">' + 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()
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -4,8 +4,9 @@
|
||||
from datetime import datetime
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print
|
||||
from v2realbot.utils.tlog import tlog
|
||||
from v2realbot.utils.ilog import insert_log, insert_log_multiple
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order, Account
|
||||
from v2realbot.config import BT_DELAYS, get_key, HEARTBEAT_TIMEOUT, QUIET_MODE
|
||||
from v2realbot.config import BT_DELAYS, get_key, HEARTBEAT_TIMEOUT, QUIET_MODE, LOG_RUNNER_EVENTS
|
||||
import queue
|
||||
#from rich import print
|
||||
from v2realbot.loader.aggregator import TradeAggregator2Queue, TradeAggregator2List, TradeAggregator
|
||||
@ -22,10 +23,11 @@ from v2realbot.common.model import TradeUpdate
|
||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||
from threading import Event, current_thread
|
||||
import json
|
||||
from uuid import UUID
|
||||
|
||||
# obecna Parent strategie podporující queues
|
||||
class Strategy:
|
||||
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) -> 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)
|
||||
Binary file not shown.
92
v2realbot/utils/ilog.py
Normal file
92
v2realbot/utils/ilog.py
Normal file
@ -0,0 +1,92 @@
|
||||
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, check_same_thread=False)
|
||||
#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())]
|
||||
|
||||
#returns rowcount of inserted rows
|
||||
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
|
||||
|
||||
#returns rowcount of inserted rows
|
||||
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
|
||||
|
||||
#returns list of ilog jsons
|
||||
def get_log_window(runner_id: UUID, timestamp_from: float = 0, timestamp_to: float = 9682851459):
|
||||
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 >={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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user