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
|
import json
|
||||||
from rich import print
|
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")
|
# res = db_arch_h.update(set('note', "ahoj"), where('id') == "74aa524e-3ed4-41fb-8166-f20946520344")
|
||||||
# print(res)
|
# print(res)
|
||||||
res = db_arch_d.all()
|
#res = db_runner_log.all()
|
||||||
print(res)
|
#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()
|
db.truncate()
|
||||||
insert = {'datum': datetime.now(), 'side': OrderSide.BUY, 'name': 'david','id': uuid4(), 'order': orderList}
|
insert = {'datum': datetime.now(), 'side': OrderSide.BUY, 'name': 'david','id': uuid4(), 'order': orderList}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#insert record
|
#insert record
|
||||||
db.insert(a.__dict__)
|
db.insert(a.__dict__)
|
||||||
|
|
||||||
|
|||||||
@ -191,6 +191,8 @@ def next(data, state: StrategyState):
|
|||||||
state.vars.blockbuy = 0
|
state.vars.blockbuy = 0
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
state.ilog(e="-----")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
## slope vyresi rychlé sesupy - jeste je treba podchytit pomalejsi sesupy
|
## 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]))
|
# state.ilog(e="Slope - MA"+str(state.indicators.slopeMA[-1]))
|
||||||
|
|
||||||
else:
|
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)
|
state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -357,7 +363,7 @@ def next(data, state: StrategyState):
|
|||||||
|
|
||||||
#HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru
|
#HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru
|
||||||
lp = state.interface.get_last_price(symbol=state.symbol)
|
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
|
#maxSlopeMA = -0.03
|
||||||
#SLOPE ANGLE PROTECTIONs
|
#SLOPE ANGLE PROTECTIONs
|
||||||
@ -461,7 +467,7 @@ def init(state: StrategyState):
|
|||||||
state.indicators['slope'] = []
|
state.indicators['slope'] = []
|
||||||
state.indicators['slopeMA'] = []
|
state.indicators['slopeMA'] = []
|
||||||
#static indicators - those not series based
|
#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
|
state.vars["ticks2reset_backup"] = state.vars.ticks2reset
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -475,7 +481,7 @@ def main():
|
|||||||
name = os.path.basename(__file__)
|
name = os.path.basename(__file__)
|
||||||
se = Event()
|
se = Event()
|
||||||
pe = 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,
|
s.set_mode(mode = Mode.BT,
|
||||||
debug = False,
|
debug = False,
|
||||||
start = datetime(2023, 4, 14, 10, 42, 0, 0, tzinfo=zoneNY),
|
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
|
mode: Mode
|
||||||
note: Optional[str] = None
|
note: Optional[str] = None
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
|
ilog_save: bool = False
|
||||||
bt_from: datetime = None
|
bt_from: datetime = None
|
||||||
bt_to: datetime = None
|
bt_to: datetime = None
|
||||||
cash: int = 100000
|
cash: int = 100000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RunnerView(BaseModel):
|
class RunnerView(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
|
strat_id: UUID
|
||||||
run_started: Optional[datetime] = None
|
run_started: Optional[datetime] = None
|
||||||
run_mode: Mode
|
run_mode: Mode
|
||||||
run_name: Optional[str] = None
|
run_name: Optional[str] = None
|
||||||
run_note: Optional[str] = None
|
run_note: Optional[str] = None
|
||||||
run_account: Account
|
run_account: Account
|
||||||
|
run_ilog_save: Optional[bool] = False
|
||||||
run_symbol: Optional[str] = None
|
run_symbol: Optional[str] = None
|
||||||
run_trade_count: Optional[int] = 0
|
run_trade_count: Optional[int] = 0
|
||||||
run_profit: Optional[float] = 0
|
run_profit: Optional[float] = 0
|
||||||
@ -83,12 +84,14 @@ class RunnerView(BaseModel):
|
|||||||
#Running instance - not persisted
|
#Running instance - not persisted
|
||||||
class Runner(BaseModel):
|
class Runner(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
|
strat_id: UUID
|
||||||
run_started: Optional[datetime] = None
|
run_started: Optional[datetime] = None
|
||||||
run_mode: Mode
|
run_mode: Mode
|
||||||
run_account: Account
|
run_account: Account
|
||||||
run_symbol: Optional[str] = None
|
run_symbol: Optional[str] = None
|
||||||
run_name: Optional[str] = None
|
run_name: Optional[str] = None
|
||||||
run_note: Optional[str] = None
|
run_note: Optional[str] = None
|
||||||
|
run_ilog_save: Optional[bool] = False
|
||||||
run_trade_count: Optional[int]
|
run_trade_count: Optional[int]
|
||||||
run_profit: Optional[float]
|
run_profit: Optional[float]
|
||||||
run_positions: Optional[int]
|
run_positions: Optional[int]
|
||||||
@ -174,6 +177,8 @@ class RunArchive(BaseModel):
|
|||||||
bt_from: Optional[datetime] = None
|
bt_from: Optional[datetime] = None
|
||||||
bt_to: Optional[datetime] = None
|
bt_to: Optional[datetime] = None
|
||||||
stratvars: Optional[dict] = None
|
stratvars: Optional[dict] = None
|
||||||
|
settings: Optional[dict] = None
|
||||||
|
ilog_save: Optional[bool] = False
|
||||||
profit: float = 0
|
profit: float = 0
|
||||||
trade_count: int = 0
|
trade_count: int = 0
|
||||||
end_positions: 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 v2realbot.enums.enums import Mode, Account, FillCondition
|
||||||
from appdirs import user_data_dir
|
from appdirs import user_data_dir
|
||||||
|
|
||||||
|
|
||||||
|
LOG_RUNNER_EVENTS = False
|
||||||
#no print in console
|
#no print in console
|
||||||
QUIET_MODE = False
|
QUIET_MODE = False
|
||||||
#how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting
|
#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.enums import DataFeed
|
||||||
from alpaca.data.timeframe import TimeFrame
|
from alpaca.data.timeframe import TimeFrame
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
|
||||||
from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange
|
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
|
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 datetime import datetime
|
||||||
from threading import Thread, current_thread, Event, enumerate
|
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
|
import importlib
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from tinydb import TinyDB, Query, where
|
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):
|
def inject_stratvars(id: UUID, stratvars_parsed_new: AttributeDict, stratvars_parsed_old: AttributeDict):
|
||||||
for i in db.runners:
|
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)
|
#inject only those changed, some of them cannot be changed (for example pendingbuys)
|
||||||
|
|
||||||
changed_keys = []
|
changed_keys = []
|
||||||
@ -180,7 +181,7 @@ def modify_stratin_running(si: StrategyInstance, id: UUID):
|
|||||||
|
|
||||||
##enable realtime chart - inject given queue for strategy instance
|
##enable realtime chart - inject given queue for strategy instance
|
||||||
##webservice listens to this queue
|
##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:
|
for i in db.runners:
|
||||||
if str(i.id) == str(id):
|
if str(i.id) == str(id):
|
||||||
i.run_instance.rtqueue = rtqueue
|
i.run_instance.rtqueue = rtqueue
|
||||||
@ -189,7 +190,7 @@ async def stratin_realtime_on(id: UUID, rtqueue: Queue):
|
|||||||
print("ERROR NOT FOUND")
|
print("ERROR NOT FOUND")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
async def stratin_realtime_off(id: UUID):
|
async def runner_realtime_off(id: UUID):
|
||||||
for i in db.runners:
|
for i in db.runners:
|
||||||
if str(i.id) == str(id):
|
if str(i.id) == str(id):
|
||||||
i.run_instance.rtqueue = None
|
i.run_instance.rtqueue = None
|
||||||
@ -199,7 +200,7 @@ async def stratin_realtime_off(id: UUID):
|
|||||||
return -2
|
return -2
|
||||||
|
|
||||||
##controller (run_stratefy, pause, stop, reload_params)
|
##controller (run_stratefy, pause, stop, reload_params)
|
||||||
def pause_stratin(id: UUID):
|
def pause_runner(id: UUID):
|
||||||
for i in db.runners:
|
for i in db.runners:
|
||||||
print(i.id)
|
print(i.id)
|
||||||
if str(i.id) == id:
|
if str(i.id) == id:
|
||||||
@ -215,7 +216,7 @@ def pause_stratin(id: UUID):
|
|||||||
print("no ID found")
|
print("no ID found")
|
||||||
return (-1, "not running instance found")
|
return (-1, "not running instance found")
|
||||||
|
|
||||||
def stop_stratin(id: UUID = None):
|
def stop_runner(id: UUID = None):
|
||||||
chng = []
|
chng = []
|
||||||
for i in db.runners:
|
for i in db.runners:
|
||||||
#print(i['id'])
|
#print(i['id'])
|
||||||
@ -236,6 +237,13 @@ def stop_stratin(id: UUID = None):
|
|||||||
return (-2, "not found" + str(id))
|
return (-2, "not found" + str(id))
|
||||||
|
|
||||||
def is_stratin_running(id: UUID):
|
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:
|
for i in db.runners:
|
||||||
if str(i.id) == str(id):
|
if str(i.id) == str(id):
|
||||||
if i.run_started is not None and i.run_stopped is None:
|
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_pause_ev = None
|
||||||
i.run_stop_ev = None
|
i.run_stop_ev = None
|
||||||
#ukladame radek do historie (pozdeji refactor)
|
#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
|
#store in archive header and archive detail
|
||||||
archive_runner(runner=i, strat=target)
|
archive_runner(runner=i, strat=target)
|
||||||
#mazeme runner po skonceni instance
|
#mazeme runner po skonceni instance
|
||||||
db.runners.remove(i)
|
db.runners.remove(i)
|
||||||
|
|
||||||
print("Runner STOPPED")
|
print("Runner STOPPED")
|
||||||
|
#stratin run
|
||||||
def run_stratin(id: UUID, runReq: RunRequest):
|
def run_stratin(id: UUID, runReq: RunRequest):
|
||||||
if runReq.mode == Mode.BT:
|
if runReq.mode == Mode.BT:
|
||||||
if runReq.bt_from is None:
|
if runReq.bt_from is None:
|
||||||
@ -336,7 +344,12 @@ def run_stratin(id: UUID, runReq: RunRequest):
|
|||||||
next=next,
|
next=next,
|
||||||
init=init,
|
init=init,
|
||||||
stratvars=stratvars,
|
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)
|
print("instance vytvorena", instance)
|
||||||
#set mode
|
#set mode
|
||||||
if runReq.mode == Mode.LIVE or runReq.mode == Mode.PAPER:
|
if runReq.mode == Mode.LIVE or runReq.mode == Mode.PAPER:
|
||||||
@ -359,7 +372,9 @@ def run_stratin(id: UUID, runReq: RunRequest):
|
|||||||
vlakno.start()
|
vlakno.start()
|
||||||
print("Spuštěna", instance.name)
|
print("Spuštěna", instance.name)
|
||||||
##storing the attributtes - pozor pri stopu je zase odstranit
|
##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_started = datetime.now(zoneNY),
|
||||||
run_pause_ev = pe,
|
run_pause_ev = pe,
|
||||||
run_name = name,
|
run_name = name,
|
||||||
@ -368,13 +383,14 @@ def run_stratin(id: UUID, runReq: RunRequest):
|
|||||||
run_stop_ev = se,
|
run_stop_ev = se,
|
||||||
run_thread = vlakno,
|
run_thread = vlakno,
|
||||||
run_account = runReq.account,
|
run_account = runReq.account,
|
||||||
|
run_ilog_save = runReq.ilog_save,
|
||||||
run_mode = runReq.mode,
|
run_mode = runReq.mode,
|
||||||
run_instance = instance)
|
run_instance = instance)
|
||||||
db.runners.append(runner)
|
db.runners.append(runner)
|
||||||
print(db.runners)
|
print(db.runners)
|
||||||
print(i)
|
print(i)
|
||||||
print(enumerate())
|
print(enumerate())
|
||||||
return (0, i.id)
|
return (0, id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return (-2, "Exception: "+str(e))
|
return (-2, "Exception: "+str(e))
|
||||||
return (-2, "not found")
|
return (-2, "not found")
|
||||||
@ -403,9 +419,17 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
|
|||||||
else:
|
else:
|
||||||
bp_from = None
|
bp_from = None
|
||||||
bp_to = None
|
bp_to = None
|
||||||
id = uuid4()
|
|
||||||
runArchive: RunArchive = RunArchive(id = id,
|
settings = dict(resolution=strat.state.timeframe,
|
||||||
strat_id = runner.id,
|
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,
|
name=runner.run_name,
|
||||||
note=runner.run_note,
|
note=runner.run_note,
|
||||||
symbol=runner.run_symbol,
|
symbol=runner.run_symbol,
|
||||||
@ -413,9 +437,11 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
|
|||||||
stopped=runner.run_stopped,
|
stopped=runner.run_stopped,
|
||||||
mode=runner.run_mode,
|
mode=runner.run_mode,
|
||||||
account=runner.run_account,
|
account=runner.run_account,
|
||||||
|
ilog_save=runner.run_ilog_save,
|
||||||
bt_from=bp_from,
|
bt_from=bp_from,
|
||||||
bt_to = bp_to,
|
bt_to = bp_to,
|
||||||
stratvars = strat.state.vars,
|
stratvars = strat.state.vars,
|
||||||
|
settings = settings,
|
||||||
profit=round(float(strat.state.profit),2),
|
profit=round(float(strat.state.profit),2),
|
||||||
trade_count=len(strat.state.tradeList),
|
trade_count=len(strat.state.tradeList),
|
||||||
end_positions=strat.state.positions,
|
end_positions=strat.state.positions,
|
||||||
@ -434,7 +460,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
|
|||||||
print("is not numpy", key, value)
|
print("is not numpy", key, value)
|
||||||
flattened_indicators[key]= value
|
flattened_indicators[key]= value
|
||||||
|
|
||||||
runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = id,
|
runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = runner.id,
|
||||||
name=runner.run_name,
|
name=runner.run_name,
|
||||||
bars=strat.state.bars,
|
bars=strat.state.bars,
|
||||||
indicators=flattened_indicators,
|
indicators=flattened_indicators,
|
||||||
@ -452,14 +478,15 @@ def get_all_archived_runners():
|
|||||||
res = db_arch_h.all()
|
res = db_arch_h.all()
|
||||||
return 0, res
|
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):
|
def delete_archived_runners_byID(id: UUID):
|
||||||
try:
|
try:
|
||||||
resh = db_arch_h.remove(where('id') == id)
|
resh = db_arch_h.remove(where('id') == id)
|
||||||
resd = db_arch_d.remove(where('id') == id)
|
resd = db_arch_d.remove(where('id') == id)
|
||||||
if len(resh) == 0 or len(resd) == 0:
|
reslogs = delete_logs(id)
|
||||||
return -1, "not found "+str(resh) + " " + str(resd)
|
if len(resh) == 0 or len(resd) == 0 or reslogs ==0:
|
||||||
return 0, str(resh) + " " + str(resd)
|
return -1, "not found "+str(resh) + " " + str(resd) + " " + str(reslogs)
|
||||||
|
return 0, str(resh) + " " + str(resd) + " " + str(reslogs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return -2, str(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_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)
|
#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)
|
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)
|
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]
|
return 0, bars.data[symbol]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return -2, str(e)
|
return -2, str(e)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ from fastapi.security import APIKeyHeader
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import v2realbot.controller.services as cs
|
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 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 import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query
|
||||||
from fastapi.responses import HTMLResponse, FileResponse
|
from fastapi.responses import HTMLResponse, FileResponse
|
||||||
@ -95,14 +96,14 @@ async def websocket_endpoint(
|
|||||||
api_key: Annotated[str, Depends(get_api_key)],
|
api_key: Annotated[str, Depends(get_api_key)],
|
||||||
):
|
):
|
||||||
await websocket.accept()
|
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")
|
#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
|
return
|
||||||
else:
|
else:
|
||||||
print("stratin exists")
|
print("stratin exists")
|
||||||
q: Queue = Queue()
|
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
|
# tx task; reads data from queue and sends to websocket
|
||||||
async def websocket_tx_task(ws, _q):
|
async def websocket_tx_task(ws, _q):
|
||||||
@ -158,7 +159,7 @@ async def websocket_endpoint(
|
|||||||
print("CLIENT DISCONNECTED for", runner_id)
|
print("CLIENT DISCONNECTED for", runner_id)
|
||||||
finally:
|
finally:
|
||||||
q.put("break")
|
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)])
|
@app.get("/threads/", dependencies=[Depends(api_key_auth)])
|
||||||
def _get_all_threads():
|
def _get_all_threads():
|
||||||
@ -227,16 +228,16 @@ def _run_stratin(stratin_id: UUID, runReq: RunRequest):
|
|||||||
elif res < 0:
|
elif res < 0:
|
||||||
raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"Error: {res}:{id}")
|
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)
|
@app.put("/runners/{runner_id}/pause", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
|
||||||
def _pause_stratin(stratin_id):
|
def _pause_runner(runner_id):
|
||||||
res, id = cs.pause_stratin(id=stratin_id)
|
res, id = cs.pause_runner(id=runner_id)
|
||||||
if res == 0: return id
|
if res == 0: return id
|
||||||
elif res < 0:
|
elif res < 0:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
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)
|
@app.put("/runners/{runner_id}/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
|
||||||
def _stop_stratin(stratin_id):
|
def _stop_runner(runner_id):
|
||||||
res, id = cs.stop_stratin(id=stratin_id)
|
res, id = cs.stop_runner(id=runner_id)
|
||||||
if res == 0: return id
|
if res == 0: return id
|
||||||
elif res < 0:
|
elif res < 0:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
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:
|
elif res < 0:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
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)
|
@app.put("/runners/stop", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
|
||||||
def stop_all_stratins():
|
def stop_all_runners():
|
||||||
res, id = cs.stop_stratin()
|
res, id = cs.stop_runner()
|
||||||
if res == 0: return id
|
if res == 0: return id
|
||||||
elif res < 0:
|
elif res < 0:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
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:
|
else:
|
||||||
raise HTTPException(status_code=404, detail=f"No runner with id: {runner_id} a {set}")
|
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
|
#get alpaca history bars
|
||||||
@app.get("/history_bars/", dependencies=[Depends(api_key_auth)])
|
@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]:
|
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:
|
if res == 0:
|
||||||
return set
|
return set
|
||||||
else:
|
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
|
#join cekej na dokonceni vsech
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-bs-theme="light">
|
<html lang="en" data-bs-theme="dark">
|
||||||
<head>
|
<head>
|
||||||
<!-- Required meta tags -->
|
<!-- Required meta tags -->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
@ -51,12 +51,16 @@
|
|||||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-outline-success btn-sm">Send</button>
|
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-outline-success btn-sm">Send</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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="statusRegime" class="headerItem"></div>
|
||||||
<div id="statusName" class="headerItem"></div>
|
<div id="statusName" class="headerItem"></div>
|
||||||
<div id="statusMode" class="headerItem"></div>
|
<div id="statusMode" class="headerItem"></div>
|
||||||
<div id="statusAccount" 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>
|
||||||
<div id="chart" style="display: None; float: left; "></div>
|
<div id="chart" style="display: None; float: left; "></div>
|
||||||
<div class="legend" id="legend"></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_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_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_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>
|
</div>
|
||||||
<table id="runnerTable" class="table-striped table-bordered dataTable" style="width:100%; border-color: #dce1dc;">
|
<table id="runnerTable" class="table-striped table-bordered dataTable" style="width:100%; border-color: #dce1dc;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Id</th>
|
<th>Id</th>
|
||||||
|
<th>StratId</th>
|
||||||
<th>Started</th>
|
<th>Started</th>
|
||||||
<th>Mode</th>
|
<th>Mode</th>
|
||||||
<th>Symbol</th>
|
<th>Symbol</th>
|
||||||
<th>Account</th>
|
<th>Account</th>
|
||||||
|
<th>ilog</th>
|
||||||
<th>Paused</th>
|
<th>Paused</th>
|
||||||
<th>Profit</th>
|
<th>Profit</th>
|
||||||
<th>Trades</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_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_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_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_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="btn btn-outline-success btn-sm">Refresh</button> -->
|
||||||
</div>
|
</div>
|
||||||
@ -159,8 +166,9 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Id</th>
|
<th>Id</th>
|
||||||
|
<th>StratId</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Symbol</th>
|
<th>Sym</th>
|
||||||
<th>Note</th>
|
<th>Note</th>
|
||||||
<th>started</th>
|
<th>started</th>
|
||||||
<th>stopped</th>
|
<th>stopped</th>
|
||||||
@ -168,11 +176,12 @@
|
|||||||
<th>account</th>
|
<th>account</th>
|
||||||
<th>bt_from</th>
|
<th>bt_from</th>
|
||||||
<th>bt_to</th>
|
<th>bt_to</th>
|
||||||
|
<th>ilog</th>
|
||||||
<th>stratvars</th>
|
<th>stratvars</th>
|
||||||
<th>profit</th>
|
<th>profit</th>
|
||||||
<th>tradecnt</th>
|
<th>trade</th>
|
||||||
<th>end_pos</th>
|
<th>pos</th>
|
||||||
<th>end_pos_avgp</th>
|
<th>pos_avgp</th>
|
||||||
<th>open</th>
|
<th>open</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -216,7 +225,7 @@
|
|||||||
<input type="text" class="form-control" id="editidarchive" name="id" placeholder="id" readonly>
|
<input type="text" class="form-control" id="editidarchive" name="id" placeholder="id" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<textarea class="form-control" rows="2" id="editnote" name="note"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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_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_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_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;">
|
<table id="stratinTable" class="table-striped table-bordered dataTable" style="width:100%; border-color: #dce1dc;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -418,8 +428,16 @@
|
|||||||
<input type="number" class="form-control" id="cash" name="cash" placeholder="cash" value="100000">
|
<input type="number" class="form-control" id="cash" name="cash" placeholder="cash" value="100000">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="cash" class="form-label">Subscribe for RT</label>
|
<label for="ilog_save" class="form-label">Enable logs</label>
|
||||||
<input type="checkbox" class="form-check-input mt-0" id="subscribe" name="subscribe" aria-label="Real time subscribe">
|
<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>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -433,6 +451,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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">
|
<div id="bottomContainer" class="flex-items" style="height: 800px">
|
||||||
<BR>
|
<BR>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
var tradeDetails = new Map();
|
var tradeDetails = new Map();
|
||||||
var toolTip = null
|
var toolTip = null
|
||||||
var CHART_SHOW_TEXT = false
|
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
|
//TRANSFORM object returned from RESTA PI get_arch_run_detail
|
||||||
//to series and markers required by lightweigth chart
|
//to series and markers required by lightweigth chart
|
||||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
//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 avgp_markers = []
|
||||||
var markers = []
|
var markers = []
|
||||||
var markers_line = []
|
var markers_line = []
|
||||||
|
var last_timestamp = 0.1
|
||||||
|
var iterator = 0.001
|
||||||
data.trades.forEach((trade, index, array) => {
|
data.trades.forEach((trade, index, array) => {
|
||||||
obj = {};
|
obj = {};
|
||||||
a_markers = {}
|
a_markers = {}
|
||||||
timestamp = Date.parse(trade.order.filled_at)/1000
|
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") {
|
if (trade.order.side == "buy") {
|
||||||
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
|
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
|
||||||
if (trade.pos_avg_price !== null) {
|
if (trade.pos_avg_price !== null) {
|
||||||
@ -103,12 +122,11 @@ function transform_data(data) {
|
|||||||
transformed["markers_line"] = markers_line
|
transformed["markers_line"] = markers_line
|
||||||
transformed["avgp_markers"] = avgp_markers
|
transformed["avgp_markers"] = avgp_markers
|
||||||
//get additional indicators
|
//get additional indicators
|
||||||
//TBD
|
|
||||||
return transformed
|
return transformed
|
||||||
}
|
}
|
||||||
|
|
||||||
//unit: Min, Hour, Day, Week, Month
|
//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) {
|
function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunnerDetail) {
|
||||||
req = {}
|
req = {}
|
||||||
req["symbol"] = archRunner.symbol
|
req["symbol"] = archRunner.symbol
|
||||||
@ -133,7 +151,7 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn
|
|||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
data: req,
|
data: req,
|
||||||
success:function(data){
|
success:function(data){
|
||||||
console.log("one minute bars", JSON.stringify(data))
|
console.log("one minute bars before", JSON.stringify(data))
|
||||||
data.map((el)=>{
|
data.map((el)=>{
|
||||||
cas = new Date(el.timestamp)
|
cas = new Date(el.timestamp)
|
||||||
el.time = cas.getTime()/1000;
|
el.time = cas.getTime()/1000;
|
||||||
@ -154,7 +172,6 @@ function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunn
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//render chart of archived runs
|
//render chart of archived runs
|
||||||
function chart_archived_run(archRecord, data, oneMinuteBars) {
|
function chart_archived_run(archRecord, data, oneMinuteBars) {
|
||||||
if (chart !== null) {
|
if (chart !== null) {
|
||||||
@ -203,102 +220,267 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
|
|||||||
//initialize chart
|
//initialize chart
|
||||||
document.getElementById("chart").style.display = "block"
|
document.getElementById("chart").style.display = "block"
|
||||||
|
|
||||||
var chartOptions = { width: 1300,
|
initialize_chart()
|
||||||
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
|
|
||||||
}})
|
|
||||||
|
|
||||||
container1.append(switcherElement)
|
container1.append(switcherElement)
|
||||||
|
|
||||||
var archCandlestickSeries = null
|
candlestickSeries = null
|
||||||
|
|
||||||
switch_to_interval(intervals[1])
|
switch_to_interval(intervals[1])
|
||||||
chart.timeScale().fitContent();
|
chart.timeScale().fitContent();
|
||||||
|
|
||||||
function switch_to_interval(interval) {
|
function switch_to_interval(interval) {
|
||||||
//prip prenuti prepisujeme candlestick a markery
|
//prip prpenuti prepisujeme candlestick a markery
|
||||||
|
if (candlestickSeries) {
|
||||||
if (archCandlestickSeries) {
|
|
||||||
last_range = chart.timeScale().getVisibleRange()
|
last_range = chart.timeScale().getVisibleRange()
|
||||||
chart.removeSeries(archCandlestickSeries);
|
chart.removeSeries(candlestickSeries);
|
||||||
archCandlestickSeries = null
|
candlestickSeries = null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
last_range = null
|
last_range = null
|
||||||
}
|
}
|
||||||
archCandlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
|
||||||
archCandlestickSeries.priceScale().applyOptions({
|
if (interval == native_resolution) {
|
||||||
scaleMargins: {
|
//indicators are in native resolution only
|
||||||
top: 0.1, // highest point of the series will be 10% away from the top
|
display_indicators(data);
|
||||||
bottom: 0.4, // lowest point will be 40% away from the bottom
|
}
|
||||||
},
|
else {
|
||||||
});
|
remove_indicators();
|
||||||
archCandlestickSeries.setData(AllCandleSeriesesData.get(interval));
|
}
|
||||||
|
|
||||||
|
intitialize_candles()
|
||||||
|
candlestickSeries.setData(AllCandleSeriesesData.get(interval));
|
||||||
|
|
||||||
if (last_range) {
|
if (last_range) {
|
||||||
chart.timeScale().setVisibleRange(last_range);
|
chart.timeScale().setVisibleRange(last_range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var archVwapSeries = chart.addLineSeries({
|
//TBD
|
||||||
// title: "vwap",
|
//pro kazdy identifikator zobrazime button na vypnuti zapnuti
|
||||||
color: '#2962FF',
|
//vybereme barvu pro kazdy identifikator
|
||||||
lineWidth: 1,
|
//zjistime typ idenitfikatoru - zatim right vs left
|
||||||
lastValueVisible: false
|
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
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("avgp_buy_line",transformed_data["avgp_buy_line"])
|
console.log("avgp_buy_line",transformed_data["avgp_buy_line"])
|
||||||
console.log("avgp_markers",transformed_data["avgp_markers"])
|
console.log("avgp_markers",transformed_data["avgp_markers"])
|
||||||
|
|
||||||
if (transformed_data["avgp_buy_line"].length > 0) {
|
if (transformed_data["avgp_buy_line"].length > 0) {
|
||||||
var avgBuyLine = chart.addLineSeries({
|
avgBuyLine = chart.addLineSeries({
|
||||||
// title: "avgpbuyline",
|
// title: "avgpbuyline",
|
||||||
color: '#e8c76d',
|
color: '#e8c76d',
|
||||||
// color: 'transparent',
|
// color: 'transparent',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lastValueVisible: false
|
lastValueVisible: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
avgBuyLine.applyOptions({
|
||||||
|
lastValueVisible: false,
|
||||||
|
priceLineVisible: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
|
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log("avgbuyline")
|
||||||
|
}
|
||||||
|
|
||||||
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
|
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
|
||||||
}
|
}
|
||||||
|
|
||||||
var markersLine = chart.addLineSeries({
|
markersLine = chart.addLineSeries({
|
||||||
// title: "avgpbuyline",
|
// title: "avgpbuyline",
|
||||||
// color: '#d6d1c3',
|
// color: '#d6d1c3',
|
||||||
color: 'transparent',
|
color: 'transparent',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lastValueVisible: false
|
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"])
|
markersLine.setMarkers(transformed_data["markers"])
|
||||||
|
|
||||||
|
|
||||||
@ -333,6 +515,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
|
|||||||
|
|
||||||
//chart.subscribeCrosshairMove(param => {
|
//chart.subscribeCrosshairMove(param => {
|
||||||
chart.subscribeCrosshairMove(param => {
|
chart.subscribeCrosshairMove(param => {
|
||||||
|
//LEGEND SECTIOIN
|
||||||
|
firstRow.style.color = 'white';
|
||||||
|
update_chart_legend(param);
|
||||||
|
|
||||||
|
//TOOLTIP SECTION
|
||||||
//$('#trade-timestamp').val(param.time)
|
//$('#trade-timestamp').val(param.time)
|
||||||
if (
|
if (
|
||||||
param.point === undefined ||
|
param.point === undefined ||
|
||||||
@ -390,15 +577,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
chart.subscribeClick(param => {
|
chart.subscribeClick(param => {
|
||||||
$('#trade-timestamp').val(param.time)
|
$('#trade-timestamp').val(param.time)
|
||||||
|
if (archRecord.ilog_save == "true") {
|
||||||
|
fetch_log_data(param.time, archRecord.id);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
param.point === undefined ||
|
param.point === undefined ||
|
||||||
!param.time ||
|
!param.time ||
|
||||||
@ -458,9 +641,90 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
|
|||||||
$("#statusName").text(archRecord.name)
|
$("#statusName").text(archRecord.name)
|
||||||
$("#statusMode").text(archRecord.mode)
|
$("#statusMode").text(archRecord.mode)
|
||||||
$("#statusAccount").text(archRecord.account)
|
$("#statusAccount").text(archRecord.account)
|
||||||
|
$("#statusIlog").text("Logged:" + archRecord.ilog_save)
|
||||||
$("#statusStratvars").text(JSON.stringify(archRecord.stratvars,null,2))
|
$("#statusStratvars").text(JSON.stringify(archRecord.stratvars,null,2))
|
||||||
|
$("#statusSettings").text(JSON.stringify(archRecord.settings,null,2))
|
||||||
|
|
||||||
//TBD other dynamically created indicators
|
//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_delete_arch').attr('disabled','disabled');
|
||||||
$('#button_edit_arch').attr('disabled','disabled');
|
$('#button_edit_arch').attr('disabled','disabled');
|
||||||
|
|
||||||
|
|
||||||
//selectable rows in archive table
|
//selectable rows in archive table
|
||||||
$('#archiveTable tbody').on('click', 'tr', function () {
|
$('#archiveTable tbody').on('click', 'tr', function () {
|
||||||
if ($(this).hasClass('selected')) {
|
if ($(this).hasClass('selected')) {
|
||||||
@ -36,6 +35,7 @@ $(document).ready(function () {
|
|||||||
row = archiveRecords.row('.selected').data();
|
row = archiveRecords.row('.selected').data();
|
||||||
window.$('#editModalArchive').modal('show');
|
window.$('#editModalArchive').modal('show');
|
||||||
$('#editidarchive').val(row.id);
|
$('#editidarchive').val(row.id);
|
||||||
|
$('#editnote').val(row.note);
|
||||||
$('#editstratvars').val(JSON.stringify(row.stratvars,null,2));
|
$('#editstratvars').val(JSON.stringify(row.stratvars,null,2));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ $(document).ready(function () {
|
|||||||
success:function(data){
|
success:function(data){
|
||||||
$('#button_show_arch').attr('disabled',false);
|
$('#button_show_arch').attr('disabled',false);
|
||||||
$('#chartContainerInner').addClass("show");
|
$('#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));
|
//$('#chartArchive').append(JSON.stringify(data,null,2));
|
||||||
console.log(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));
|
window.alert(JSON.stringify(xhr));
|
||||||
console.log(JSON.stringify(xhr));
|
console.log(JSON.stringify(xhr));
|
||||||
$('#deletearchive').attr('disabled', false);
|
$('#deletearchive').attr('disabled', false);
|
||||||
|
archiveRecords.ajax.reload();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -154,6 +155,7 @@ var archiveRecords =
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [{ data: 'id' },
|
columns: [{ data: 'id' },
|
||||||
|
{data: 'strat_id'},
|
||||||
{data: 'name'},
|
{data: 'name'},
|
||||||
{data: 'symbol'},
|
{data: 'symbol'},
|
||||||
{data: 'note'},
|
{data: 'note'},
|
||||||
@ -163,6 +165,7 @@ var archiveRecords =
|
|||||||
{data: 'account', visible: true},
|
{data: 'account', visible: true},
|
||||||
{data: 'bt_from', visible: true},
|
{data: 'bt_from', visible: true},
|
||||||
{data: 'bt_to', visible: true},
|
{data: 'bt_to', visible: true},
|
||||||
|
{data: 'ilog_save', visible: true},
|
||||||
{data: 'stratvars', visible: true},
|
{data: 'stratvars', visible: true},
|
||||||
{data: 'profit'},
|
{data: 'profit'},
|
||||||
{data: 'trade_count', visible: true},
|
{data: 'trade_count', visible: true},
|
||||||
@ -176,7 +179,7 @@ var archiveRecords =
|
|||||||
// return format_date(data)
|
// return format_date(data)
|
||||||
// },
|
// },
|
||||||
// }],
|
// }],
|
||||||
order: [[5, 'desc']],
|
order: [[6, 'desc']],
|
||||||
paging: true,
|
paging: true,
|
||||||
lengthChange: false,
|
lengthChange: false,
|
||||||
// createdRow: function( row, data, dataIndex){
|
// createdRow: function( row, data, dataIndex){
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
const momentumIndicatorNames = ["roc", "slope", "slopeMA"]
|
const momentumIndicatorNames = ["roc", "slope", "slopeMA"]
|
||||||
var indList = []
|
|
||||||
var pbiList = []
|
var pbiList = []
|
||||||
var ws = null;
|
var ws = null;
|
||||||
var logcnt = 0
|
var logcnt = 0
|
||||||
var positionsPriceLine = null
|
var positionsPriceLine = null
|
||||||
var limitkaPriceLine = null
|
var limitkaPriceLine = null
|
||||||
var angleSeries = 1
|
var angleSeries = 1
|
||||||
var candlestickSeries
|
var candlestickSeries = null
|
||||||
var volumeSeries
|
var volumeSeries = null
|
||||||
var vwapSeries
|
var vwapSeries = null
|
||||||
|
|
||||||
//get details of runner to populate chart status
|
//get details of runner to populate chart status
|
||||||
//fetch necessary - it could be initiated by manually inserting runnerId
|
//fetch necessary - it could be initiated by manually inserting runnerId
|
||||||
@ -92,12 +91,12 @@ function connect(event) {
|
|||||||
iterLogList = parsed_data.iter_log
|
iterLogList = parsed_data.iter_log
|
||||||
//console.log("Incoming logline object")
|
//console.log("Incoming logline object")
|
||||||
|
|
||||||
var lines = document.getElementById('lines')
|
// var lines = document.getElementById('lines')
|
||||||
var line = document.createElement('div')
|
// var line = document.createElement('div')
|
||||||
line.classList.add("line")
|
// line.classList.add("line")
|
||||||
const newLine = document.createTextNode("---------------")
|
// const newLine = document.createTextNode("---------------")
|
||||||
line.appendChild(newLine)
|
// line.appendChild(newLine)
|
||||||
lines.appendChild(line)
|
// lines.appendChild(line)
|
||||||
|
|
||||||
iterLogList.forEach((logLine) => {
|
iterLogList.forEach((logLine) => {
|
||||||
//console.log("logline item")
|
//console.log("logline item")
|
||||||
@ -194,7 +193,7 @@ function connect(event) {
|
|||||||
positions = parsed_data.positions
|
positions = parsed_data.positions
|
||||||
const posLine = {
|
const posLine = {
|
||||||
price: positions.avgp,
|
price: positions.avgp,
|
||||||
color: 'black',
|
color: '#918686',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lineStyle: 1, // LineStyle.Dotted
|
lineStyle: 1, // LineStyle.Dotted
|
||||||
axisLabelVisible: true,
|
axisLabelVisible: true,
|
||||||
@ -221,7 +220,7 @@ function connect(event) {
|
|||||||
if (klic === "angle") {
|
if (klic === "angle") {
|
||||||
|
|
||||||
//nejsou vsechny hodnoty
|
//nejsou vsechny hodnoty
|
||||||
if (Object.keys(hodnota).length > 0) {
|
if (Object.keys(hodnota).length > 1) {
|
||||||
// console.log("angle nalezen");
|
// console.log("angle nalezen");
|
||||||
// console.log(JSON.stringify(hodnota));
|
// console.log(JSON.stringify(hodnota));
|
||||||
if (angleSeries !== 1) {
|
if (angleSeries !== 1) {
|
||||||
@ -271,49 +270,52 @@ function connect(event) {
|
|||||||
//console.log("object new - init and add")
|
//console.log("object new - init and add")
|
||||||
var obj = {name: key, series: null}
|
var obj = {name: key, series: null}
|
||||||
|
|
||||||
//predelat configuracne
|
//get configuation of indicator to display
|
||||||
//inicializace indicatoru
|
conf = get_ind_config(key)
|
||||||
//momentum
|
|
||||||
if (momentumIndicatorNames.includes(key)) {
|
|
||||||
|
|
||||||
|
//INIT INDICATOR BASED on CONFIGURATION
|
||||||
|
|
||||||
obj.series = chart.addLineSeries({
|
//MOVE TO UTILS ro reuse??
|
||||||
priceScaleId: 'left',
|
if (conf && conf.display) {
|
||||||
title: key,
|
if (conf.embed) {
|
||||||
lineWidth: 1
|
obj.series = chart.addLineSeries({
|
||||||
});
|
color: colors.shift(),
|
||||||
|
priceScaleId: conf.priceScaleId,
|
||||||
|
lastValueVisible: conf.lastValueVisible,
|
||||||
|
title: (conf.titlevisible?conf.name:""),
|
||||||
|
lineWidth: 1
|
||||||
|
});
|
||||||
|
|
||||||
//natvrdo nakreslime lajnu pro min angle
|
//tady add data
|
||||||
//TODO predelat na configuracne
|
obj.series.update({
|
||||||
const minSlopeLineOptopns = {
|
time: indicators.time,
|
||||||
price: parsed_data.statinds.angle.minimum_slope,
|
value: value});
|
||||||
color: '#b67de8',
|
indList.push(obj);
|
||||||
lineWidth: 2,
|
|
||||||
lineStyle: 2, // LineStyle.Dotted
|
|
||||||
axisLabelVisible: true,
|
|
||||||
title: "max:",
|
|
||||||
};
|
|
||||||
|
|
||||||
const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns);
|
|
||||||
|
|
||||||
|
//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
|
//indicator exists in an array, lets update it
|
||||||
else {
|
else {
|
||||||
//console.log("object found - update")
|
//console.log("object found - update")
|
||||||
|
//tady add data
|
||||||
searchObject.series.update({
|
searchObject.series.update({
|
||||||
time: indicators.time,
|
time: indicators.time,
|
||||||
value: value
|
value: value
|
||||||
|
|||||||
@ -10,7 +10,7 @@ function get_status(id) {
|
|||||||
runnerRecords.rows().iterator('row', function ( context, index ) {
|
runnerRecords.rows().iterator('row', function ( context, index ) {
|
||||||
var data = this.row(index).data();
|
var data = this.row(index).data();
|
||||||
//window.alert(JSON.stringify(data))
|
//window.alert(JSON.stringify(data))
|
||||||
if (data.id == id) {
|
if (data.strat_id == id) {
|
||||||
//window.alert("found");
|
//window.alert("found");
|
||||||
if ((data.run_mode) == "backtest") { status_detail = data.run_mode}
|
if ((data.run_mode) == "backtest") { status_detail = data.run_mode}
|
||||||
else { status_detail = data.run_mode + " | " + data.run_account}
|
else { status_detail = data.run_mode + " | " + data.run_account}
|
||||||
@ -25,12 +25,12 @@ function get_status(id) {
|
|||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_running(id) {
|
function is_stratin_running(id) {
|
||||||
var running = false
|
var running = false
|
||||||
runnerRecords.rows().iterator('row', function ( context, index ) {
|
runnerRecords.rows().iterator('row', function ( context, index ) {
|
||||||
var data = this.row(index).data();
|
var data = this.row(index).data();
|
||||||
//window.alert(JSON.stringify(data))
|
//window.alert(JSON.stringify(data))
|
||||||
if (data.id == id) {
|
if (data.strat_id == id) {
|
||||||
running = true
|
running = true
|
||||||
}
|
}
|
||||||
//window.alert("found") }
|
//window.alert("found") }
|
||||||
@ -181,7 +181,7 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
|
|
||||||
//button refresh
|
//button refresh
|
||||||
$('#button_refresh').click(function () {
|
$('.refresh').click(function () {
|
||||||
runnerRecords.ajax.reload();
|
runnerRecords.ajax.reload();
|
||||||
stratinRecords.ajax.reload();
|
stratinRecords.ajax.reload();
|
||||||
archiveRecords.ajax.reload();
|
archiveRecords.ajax.reload();
|
||||||
@ -255,7 +255,7 @@ $(document).ready(function () {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('#button_pause').attr('disabled','disabled');
|
$('#button_pause').attr('disabled','disabled');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"/stratins/"+row.id+"/pause",
|
url:"/runners/"+row.id+"/pause",
|
||||||
beforeSend: function (xhr) {
|
beforeSend: function (xhr) {
|
||||||
xhr.setRequestHeader('X-API-Key',
|
xhr.setRequestHeader('X-API-Key',
|
||||||
API_KEY); },
|
API_KEY); },
|
||||||
@ -282,7 +282,7 @@ $(document).ready(function () {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('#button_stop').attr('disabled','disabled');
|
$('#button_stop').attr('disabled','disabled');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"/stratins/"+row.id+"/stop",
|
url:"/runners/"+row.id+"/stop",
|
||||||
beforeSend: function (xhr) {
|
beforeSend: function (xhr) {
|
||||||
xhr.setRequestHeader('X-API-Key',
|
xhr.setRequestHeader('X-API-Key',
|
||||||
API_KEY); },
|
API_KEY); },
|
||||||
@ -310,7 +310,7 @@ $(document).ready(function () {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('#buttonall_stop').attr('disabled','disabled');
|
$('#buttonall_stop').attr('disabled','disabled');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"/stratins/stop",
|
url:"/runners/stop",
|
||||||
beforeSend: function (xhr) {
|
beforeSend: function (xhr) {
|
||||||
xhr.setRequestHeader('X-API-Key',
|
xhr.setRequestHeader('X-API-Key',
|
||||||
API_KEY); },
|
API_KEY); },
|
||||||
@ -343,6 +343,7 @@ $(document).ready(function () {
|
|||||||
$('#mode').val(localStorage.getItem("mode"));
|
$('#mode').val(localStorage.getItem("mode"));
|
||||||
$('#account').val(localStorage.getItem("account"));
|
$('#account').val(localStorage.getItem("account"));
|
||||||
$('#debug').val(localStorage.getItem("debug"));
|
$('#debug').val(localStorage.getItem("debug"));
|
||||||
|
$('#ilog_save').val(localStorage.getItem("ilog_save"));
|
||||||
$('#runid').val(row.id);
|
$('#runid').val(row.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -465,10 +466,12 @@ var runnerRecords =
|
|||||||
// },
|
// },
|
||||||
},
|
},
|
||||||
columns: [{ data: 'id' },
|
columns: [{ data: 'id' },
|
||||||
|
{data: 'strat_id'},
|
||||||
{data: 'run_started'},
|
{data: 'run_started'},
|
||||||
{data: 'run_mode'},
|
{data: 'run_mode'},
|
||||||
{data: 'run_symbol'},
|
{data: 'run_symbol'},
|
||||||
{data: 'run_account'},
|
{data: 'run_account'},
|
||||||
|
{data: 'run_ilog_save'},
|
||||||
{data: 'run_paused'},
|
{data: 'run_paused'},
|
||||||
{data: 'run_profit'},
|
{data: 'run_profit'},
|
||||||
{data: 'run_trade_count'},
|
{data: 'run_trade_count'},
|
||||||
@ -489,6 +492,7 @@ $("#runModal").on('submit','#runForm', function(event){
|
|||||||
localStorage.setItem("mode", $('#mode').val());
|
localStorage.setItem("mode", $('#mode').val());
|
||||||
localStorage.setItem("account", $('#account').val());
|
localStorage.setItem("account", $('#account').val());
|
||||||
localStorage.setItem("debug", $('#debug').val());
|
localStorage.setItem("debug", $('#debug').val());
|
||||||
|
localStorage.setItem("ilog_save", $('#ilog_save').val());
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('#run').attr('disabled','disabled');
|
$('#run').attr('disabled','disabled');
|
||||||
|
|
||||||
@ -496,9 +500,19 @@ $("#runModal").on('submit','#runForm', function(event){
|
|||||||
//rename runid to id
|
//rename runid to id
|
||||||
Object.defineProperty(formData, "id", Object.getOwnPropertyDescriptor(formData, "runid"));
|
Object.defineProperty(formData, "id", Object.getOwnPropertyDescriptor(formData, "runid"));
|
||||||
delete 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_from == "") {delete formData["bt_from"];}
|
||||||
if (formData.bt_to == "") {delete formData["bt_to"];}
|
if (formData.bt_to == "") {delete formData["bt_to"];}
|
||||||
jsonString = JSON.stringify(formData);
|
jsonString = JSON.stringify(formData);
|
||||||
|
//console.log(jsonString)
|
||||||
//window.alert(jsonString);
|
//window.alert(jsonString);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"/stratins/"+formData.id+"/run",
|
url:"/stratins/"+formData.id+"/run",
|
||||||
@ -512,7 +526,10 @@ $("#runModal").on('submit','#runForm', function(event){
|
|||||||
//pokud mame subscribnuto na RT
|
//pokud mame subscribnuto na RT
|
||||||
if ($('#subscribe').prop('checked')) {
|
if ($('#subscribe').prop('checked')) {
|
||||||
//subscribe input value gets id of current runner
|
//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" );
|
$( "#bt-conn" ).trigger( "click" );
|
||||||
}
|
}
|
||||||
$('#runForm')[0].reset();
|
$('#runForm')[0].reset();
|
||||||
|
|||||||
@ -1,76 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
function populate_real_time_chart() {
|
function populate_real_time_chart() {
|
||||||
if (chart !== null) {
|
if (chart !== null) {
|
||||||
chart.remove();
|
chart.remove();
|
||||||
clear_status_header();
|
clear_status_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#chartContainerInner').addClass("show");
|
initialize_chart()
|
||||||
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
intitialize_candles()
|
||||||
var chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}}
|
initialize_vwap()
|
||||||
// var chartOptions = { width: 1045,
|
initialize_volume()
|
||||||
// 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);
|
|
||||||
|
|
||||||
|
|
||||||
chart.subscribeClick(param => {
|
chart.subscribeClick(param => {
|
||||||
//display timestamp in trade-timestamp input field
|
//display timestamp in trade-timestamp input field
|
||||||
@ -78,24 +16,8 @@ function populate_real_time_chart() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
chart.subscribeCrosshairMove((param) => {
|
chart.subscribeCrosshairMove((param) => {
|
||||||
if (param.time) {
|
firstRow.style.color = 'white';
|
||||||
const data = param.seriesData.get(vwapSeries);
|
update_chart_legend(param);
|
||||||
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 = '-';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,144 @@
|
|||||||
|
|
||||||
API_KEY = localStorage.getItem("api-key")
|
API_KEY = localStorage.getItem("api-key")
|
||||||
var chart = null
|
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/
|
//range switch pro chart https://jsfiddle.net/TradingView/qrb9a850/
|
||||||
function createSimpleSwitcher(items, activeItem, activeItemChangedCallback) {
|
function createSimpleSwitcher(items, activeItem, activeItemChangedCallback) {
|
||||||
@ -67,6 +205,7 @@ function clear_status_header() {
|
|||||||
$("#statusName").text("")
|
$("#statusName").text("")
|
||||||
$("#statusMode").text("")
|
$("#statusMode").text("")
|
||||||
$("#statusAccount").text("")
|
$("#statusAccount").text("")
|
||||||
|
$("#statusIlog").text("")
|
||||||
$("#statusStratvars").text("")
|
$("#statusStratvars").text("")
|
||||||
//clear previous logs from rt
|
//clear previous logs from rt
|
||||||
$("#lines").empty()
|
$("#lines").empty()
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
:root {
|
/* :root {
|
||||||
--dt-row-selected: 18, 143, 175;
|
--dt-row-selected: 18, 143, 175;
|
||||||
|
} */
|
||||||
|
:root {
|
||||||
|
--dt-row-selected: 37, 120, 114;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody, td, tfoot, th, thead, tr {
|
tbody, td, tfoot, th, thead, tr {
|
||||||
border-color: inherit;
|
border-color: #7d7d8a;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
@ -11,10 +14,29 @@ tbody, td, tfoot, th, thead, tr {
|
|||||||
|
|
||||||
.secondary-bg {
|
.secondary-bg {
|
||||||
--bs-bg-opacity: 1;
|
--bs-bg-opacity: 1;
|
||||||
background-color: #128faf;
|
background-color: #414749;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #128faf; */
|
||||||
|
|
||||||
.btn-outline-success {
|
.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-color: #316164;
|
||||||
--bs-btn-border-color: #247e85;
|
--bs-btn-border-color: #247e85;
|
||||||
--bs-btn-hover-color: #fff;
|
--bs-btn-hover-color: #fff;
|
||||||
@ -29,6 +51,25 @@ tbody, td, tfoot, th, thead, tr {
|
|||||||
--bs-btn-disabled-bg: transparent;
|
--bs-btn-disabled-bg: transparent;
|
||||||
--bs-btn-disabled-border-color: #839498;
|
--bs-btn-disabled-border-color: #839498;
|
||||||
--bs-gradient: none;
|
--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 {
|
html {
|
||||||
@ -63,13 +104,23 @@ html {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
color: #050505;
|
color: #050505;
|
||||||
left: 115px;
|
left: 115px;
|
||||||
top: 99px;
|
top: 115px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
font-size: 16px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.legendItemName {
|
||||||
|
margin-left: 5px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legendItemValue {
|
||||||
|
margin-left: 5px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
#msgContainer {
|
#msgContainer {
|
||||||
/* display: inline-block; */
|
/* display: inline-block; */
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -83,16 +134,17 @@ html {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 568px;
|
height: 568px;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
margin-left: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width : 1681px) {
|
@media (min-width: 1681px) {}
|
||||||
.msgContainerInner {
|
.msgContainerInner {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 568px;
|
height: 568px;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
margin-left: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +156,7 @@ pre {
|
|||||||
/* margin: 0 0 10px; */
|
/* margin: 0 0 10px; */
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
/* line-height: 2.428571; */
|
/* line-height: 2.428571; */
|
||||||
color: #333;
|
/* color: #333; */
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
/* background-color: #f5f5f5; */
|
/* background-color: #f5f5f5; */
|
||||||
@ -126,11 +178,12 @@ pre {
|
|||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
font-size: normal;
|
font-size: normal;
|
||||||
/* font-weight: bold; */
|
/* font-weight: bold; */
|
||||||
display: -webkit-inline-box;
|
display: flex;
|
||||||
background-color: #dfe4e6;
|
background-color: #dfe4e6;
|
||||||
/* color: white; */
|
/* color: white; */
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerItem {
|
.headerItem {
|
||||||
|
|||||||
@ -11,12 +11,12 @@ from random import randrange
|
|||||||
from alpaca.common.exceptions import APIError
|
from alpaca.common.exceptions import APIError
|
||||||
import copy
|
import copy
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
class StrategyOrderLimitVykladaci(Strategy):
|
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:
|
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)
|
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):
|
async def orderUpdateBuy(self, data: TradeUpdate):
|
||||||
o: Order = data.order
|
o: Order = data.order
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -4,8 +4,9 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print
|
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.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.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
|
import queue
|
||||||
#from rich import print
|
#from rich import print
|
||||||
from v2realbot.loader.aggregator import TradeAggregator2Queue, TradeAggregator2List, TradeAggregator
|
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 alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from threading import Event, current_thread
|
from threading import Event, current_thread
|
||||||
import json
|
import json
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
# obecna Parent strategie podporující queues
|
# obecna Parent strategie podporující queues
|
||||||
class Strategy:
|
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)
|
#variable to store methods overriden by strategytypes (ie pre plugins)
|
||||||
self.overrides = None
|
self.overrides = None
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
@ -51,6 +53,8 @@ class Strategy:
|
|||||||
self.account = account
|
self.account = account
|
||||||
self.key = get_key(mode=self.mode, account=self.account)
|
self.key = get_key(mode=self.mode, account=self.account)
|
||||||
self.rtqueue = None
|
self.rtqueue = None
|
||||||
|
self.runner_id = runner_id
|
||||||
|
self.ilog_save = ilog_save
|
||||||
|
|
||||||
|
|
||||||
#TODO predelat na dynamické queues
|
#TODO predelat na dynamické queues
|
||||||
@ -106,13 +110,13 @@ class Strategy:
|
|||||||
self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name)
|
self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name)
|
||||||
#propojujeme notifice s interfacem (pro callback)
|
#propojujeme notifice s interfacem (pro callback)
|
||||||
self.order_notifs.connect_callback(self)
|
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:
|
elif mode == Mode.BT:
|
||||||
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
|
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.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.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
|
self.order_notifs = None
|
||||||
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
||||||
#zatim takto - pak pripadne do fajlu nebo jinak OPTIMALIZOVAT
|
#zatim takto - pak pripadne do fajlu nebo jinak OPTIMALIZOVAT
|
||||||
@ -195,7 +199,7 @@ class Strategy:
|
|||||||
#ic(self.state.time)
|
#ic(self.state.time)
|
||||||
|
|
||||||
if self.mode == Mode.BT:
|
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
|
#pozor backtester muze volat order_updates na minuly cas - nastavi si bt.time
|
||||||
self.bt.execute_orders_and_callbacks(self.state.time)
|
self.bt.execute_orders_and_callbacks(self.state.time)
|
||||||
#ic(self.bt.time)
|
#ic(self.bt.time)
|
||||||
@ -207,7 +211,8 @@ class Strategy:
|
|||||||
#ic(self.state.time)
|
#ic(self.state.time)
|
||||||
|
|
||||||
if self.mode == Mode.BT:
|
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(self.bt.time)
|
||||||
#ic(len(self.btdata))
|
#ic(len(self.btdata))
|
||||||
#ic(self.bt.cash)
|
#ic(self.bt.cash)
|
||||||
@ -282,9 +287,9 @@ class Strategy:
|
|||||||
print("REQUEST COUNT:", self.interface.mincnt)
|
print("REQUEST COUNT:", self.interface.mincnt)
|
||||||
|
|
||||||
self.bt.backtest_end = datetime.now()
|
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
|
#-> 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
|
#this is(WILL BE) called when strategy is stopped
|
||||||
# LIVE - pause or stop signal received
|
# LIVE - pause or stop signal received
|
||||||
@ -301,6 +306,10 @@ class Strategy:
|
|||||||
self.dataloader.remove_stream(i)
|
self.dataloader.remove_stream(i)
|
||||||
#pamatujeme si streamy, ktere ma strategie a tady je removneme
|
#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
|
#zavolame na loaderu remove streamer - mohou byt dalsi bezici strategie, ktery loader vyuzivaji
|
||||||
#pripadne udelat shared loader a nebo dedicated loader
|
#pripadne udelat shared loader a nebo dedicated loader
|
||||||
#pokud je shared tak volat remove
|
#pokud je shared tak volat remove
|
||||||
@ -319,6 +328,7 @@ class Strategy:
|
|||||||
else:
|
else:
|
||||||
now = self.bt.time
|
now = self.bt.time
|
||||||
|
|
||||||
|
self.state.ilog(e="NOTIF ARRIVED AT"+str(now))
|
||||||
print("NOTIFICATION ARRIVED AT:", now)
|
print("NOTIFICATION ARRIVED AT:", now)
|
||||||
self.update_live_timenow()
|
self.update_live_timenow()
|
||||||
|
|
||||||
@ -413,10 +423,10 @@ class Strategy:
|
|||||||
|
|
||||||
#cleaning iterlog lsit
|
#cleaning iterlog lsit
|
||||||
#TODO pridat cistku i mimo RT blok
|
#TODO pridat cistku i mimo RT blok
|
||||||
self.state.iter_log_list = []
|
|
||||||
else:
|
if self.ilog_save: insert_log_multiple(self.state.runner_id, self.state.iter_log_list)
|
||||||
#mazeme logy pokud neni na ws pozadovano
|
#smazeme logy
|
||||||
self.state.iter_log_list = []
|
self.state.iter_log_list = []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def append_bar(history_reference, new_bar: dict):
|
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)
|
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
|
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.vars = stratvars
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.positions = 0
|
self.positions = 0
|
||||||
@ -483,6 +493,9 @@ class StrategyState:
|
|||||||
#time of last trade processed
|
#time of last trade processed
|
||||||
self.last_trade_time = 0
|
self.last_trade_time = 0
|
||||||
self.timeframe = None
|
self.timeframe = None
|
||||||
|
self.runner_id = runner_id
|
||||||
|
self.bt = bt
|
||||||
|
self.ilog_save = ilog_save
|
||||||
|
|
||||||
bars = {'high': [],
|
bars = {'high': [],
|
||||||
'low': [],
|
'low': [],
|
||||||
@ -525,17 +538,24 @@ class StrategyState:
|
|||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
self.time = datetime.now().timestamp()
|
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 e is None:
|
||||||
if msg is None:
|
if msg is None:
|
||||||
row = dict(time=self.time, details=kwargs)
|
row = dict(time=time, details=kwargs)
|
||||||
else:
|
else:
|
||||||
row = dict(time=self.time, message=msg, details=kwargs)
|
row = dict(time=time, message=msg, details=kwargs)
|
||||||
else:
|
else:
|
||||||
if msg is None:
|
if msg is None:
|
||||||
row = dict(time=self.time, event=e, details=kwargs)
|
row = dict(time=time, event=e, details=kwargs)
|
||||||
else:
|
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)
|
self.iter_log_list.append(row)
|
||||||
row["name"] = self.name
|
row["name"] = self.name
|
||||||
print(row)
|
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