gui refactor backtest chart
This commit is contained in:
152
testy/tinydbTest.py
Normal file
152
testy/tinydbTest.py
Normal file
@ -0,0 +1,152 @@
|
||||
from tinydb import TinyDB, Query, where
|
||||
from tinydb.operations import set
|
||||
from tinydb.storages import JSONStorage
|
||||
from tinydb_serialization import SerializationMiddleware, Serializer
|
||||
from tinydb_serialization.serializers import DateTimeSerializer
|
||||
from v2realbot.common.model import Trade
|
||||
from v2realbot.utils.utils import parse_toml_string, zoneNY, json_serial
|
||||
from v2realbot.config import DATA_DIR
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from enum import Enum
|
||||
from v2realbot.common.model import Order, TradeUpdate as btTradeUpdate
|
||||
from alpaca.trading.models import TradeUpdate
|
||||
from alpaca.trading.enums import TradeEvent, OrderType, OrderSide, OrderType, OrderStatus
|
||||
from rich import print
|
||||
import json
|
||||
|
||||
#storage_with_injected_serialization = JSONStorage()
|
||||
|
||||
serialization = SerializationMiddleware(JSONStorage)
|
||||
|
||||
#builtin DateTimeSerializer
|
||||
|
||||
#customer DateTime2TimestampSerializer
|
||||
class DateTime2TimestampSerializer(Serializer):
|
||||
OBJ_CLASS = datetime # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return str(obj.timestamp())
|
||||
|
||||
def decode(self, s):
|
||||
return s
|
||||
#return datetime.fromtimestamp(s)
|
||||
|
||||
class EnumSerializer(Serializer):
|
||||
OBJ_CLASS = Enum # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return str(obj)
|
||||
|
||||
def decode(self, s):
|
||||
return s
|
||||
|
||||
class UUIDSerializer(Serializer):
|
||||
OBJ_CLASS = UUID # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return str(obj)
|
||||
|
||||
def decode(self, s):
|
||||
return s
|
||||
|
||||
class TradeUpdateSerializer(Serializer):
|
||||
OBJ_CLASS = TradeUpdate # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return obj.__dict__
|
||||
|
||||
def decode(self, s):
|
||||
return str(s)
|
||||
|
||||
class btTradeUpdateSerializer(Serializer):
|
||||
OBJ_CLASS = btTradeUpdate # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return obj.__dict__
|
||||
|
||||
def decode(self, s):
|
||||
return str(s)
|
||||
|
||||
class OrderSerializer(Serializer):
|
||||
OBJ_CLASS = Order # The class this serializer handles
|
||||
|
||||
def encode(self, obj):
|
||||
return obj.__dict__
|
||||
|
||||
def decode(self, s):
|
||||
return s
|
||||
|
||||
orderList =[Order(id=uuid4(),
|
||||
submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
symbol = "BAC",
|
||||
qty = 1,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
order_type = OrderType.LIMIT,
|
||||
side = OrderSide.BUY,
|
||||
limit_price=22.4),
|
||||
Order(id=uuid4(),
|
||||
submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
symbol = "BAC",
|
||||
qty = 1,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
order_type = OrderType.LIMIT,
|
||||
side = OrderSide.BUY,
|
||||
limit_price=22.4)]
|
||||
|
||||
# serialization.register_serializer(DateTime2TimestampSerializer(), 'TinyDate')
|
||||
# serialization.register_serializer(UUIDSerializer(), 'TinyUUID')
|
||||
# serialization.register_serializer(TradeUpdateSerializer(), 'TinyTradeUpdate')
|
||||
# serialization.register_serializer(btTradeUpdateSerializer(), 'TinybtTradeUpdate')
|
||||
# serialization.register_serializer(OrderSerializer(), 'TinyOrder')
|
||||
|
||||
a = Order(id=uuid4(),
|
||||
submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
symbol = "BAC",
|
||||
qty = 1,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
order_type = OrderType.LIMIT,
|
||||
side = OrderSide.BUY,
|
||||
limit_price=22.4)
|
||||
|
||||
db_file = DATA_DIR + "/db.json"
|
||||
db = TinyDB(db_file, default=json_serial)
|
||||
db.truncate()
|
||||
insert = {'datum': datetime.now(), 'side': OrderSide.BUY, 'name': 'david','id': uuid4(), 'order': orderList}
|
||||
|
||||
#insert record
|
||||
db.insert(a.__dict__)
|
||||
|
||||
#get records by id
|
||||
#res = db.search(where('id') == "0a9da064-708c-4645-8a07-e749d93a213d")
|
||||
#or get one:
|
||||
res = db.get(where('id') == "0a9da064-708c-4645-8a07-e749d93a213d")
|
||||
|
||||
|
||||
#update one document
|
||||
#db.update(set('name', "nove jmeno1"), where('id') == "0a9da064-708c-4645-8a07-e749d93a213d")
|
||||
|
||||
|
||||
#get all documents
|
||||
res = db.all()
|
||||
print("vsechny zaznamy, res)", res)
|
||||
|
||||
#fetch one docuemnt
|
||||
# >>> db.get(User.name == 'John')
|
||||
# {'name': 'John', 'age': 22}
|
||||
# >>> db.get(User.name == 'Bobby')
|
||||
# None
|
||||
|
||||
|
||||
#delete record by id
|
||||
#res = db.remove(where('id') == "0a9da064-708c-4645-8a07-e749d93a213d")
|
||||
#print("removed", res)
|
||||
|
||||
#res = db.search(qorder.order.id == "af447235-c01a-4c88-9f85-f3c267d2e2e1")
|
||||
#res = db.search(qorder.orderList.side == "<OrderSide.BUY: 'buy'>")
|
||||
#print(res)
|
||||
|
||||
|
||||
#print(db.all())
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -25,7 +25,7 @@ from alpaca.data.enums import Exchange
|
||||
# raise HTTPException(status_code=404, detail=f"Could not find user with id: {id}")
|
||||
|
||||
|
||||
#for GUI
|
||||
#for GUI to fetch historical trades on given symbol
|
||||
class Trade(BaseModel):
|
||||
symbol: str
|
||||
timestamp: datetime
|
||||
@ -56,15 +56,21 @@ class RunRequest(BaseModel):
|
||||
id: UUID
|
||||
account: Account
|
||||
mode: Mode
|
||||
note: Optional[str] = None
|
||||
debug: bool = False
|
||||
bt_from: datetime = None
|
||||
bt_to: datetime = None
|
||||
cash: int = 100000
|
||||
|
||||
|
||||
|
||||
|
||||
class RunnerView(BaseModel):
|
||||
id: UUID
|
||||
run_started: Optional[datetime] = None
|
||||
run_mode: Mode
|
||||
run_name: Optional[str] = None
|
||||
run_note: Optional[str] = None
|
||||
run_account: Account
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
@ -75,6 +81,8 @@ class Runner(BaseModel):
|
||||
run_started: Optional[datetime] = None
|
||||
run_mode: Mode
|
||||
run_account: Account
|
||||
run_name: Optional[str] = None
|
||||
run_note: Optional[str] = None
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
run_thread: Optional[object] = None
|
||||
@ -108,6 +116,37 @@ class TradeUpdate(BaseModel):
|
||||
cash: Optional[float]
|
||||
pos_avg_price: Optional[float]
|
||||
|
||||
#Contains archive of running strategies (runner) - master
|
||||
class RunArchive(BaseModel):
|
||||
#unique id of algorun
|
||||
id: UUID
|
||||
#id of running strategy (stratin/runner)
|
||||
strat_id: UUID
|
||||
name: str
|
||||
note: Optional[str] = None
|
||||
started: datetime
|
||||
stopped: Optional[datetime] = None
|
||||
mode: Mode
|
||||
account: Account
|
||||
bt_from: Optional[datetime] = None
|
||||
bt_to: Optional[datetime] = None
|
||||
stratvars: Optional[dict] = None
|
||||
profit: float = 0
|
||||
trade_count: int = 0
|
||||
end_positions: int = 0
|
||||
end_positions_avgp: float = 0
|
||||
open_orders: int = 0
|
||||
|
||||
#Contains archive of running strategies (runner) - detail data
|
||||
class RunArchiveDetail(BaseModel):
|
||||
id: UUID
|
||||
name: str
|
||||
bars: dict
|
||||
#trades: Optional[dict]
|
||||
indicators: dict
|
||||
statinds: dict
|
||||
trades: List[TradeUpdate]
|
||||
|
||||
# class Trade(BaseModel):
|
||||
# order: Order
|
||||
# value: float
|
||||
|
||||
@ -5,13 +5,24 @@ from alpaca.data.historical import StockHistoricalDataClient
|
||||
from alpaca.data.requests import StockTradesRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
|
||||
from v2realbot.common.model import StrategyInstance, Runner, RunRequest
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string
|
||||
from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail
|
||||
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
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
#db layer to store stratins, TBD zmigrovat do TinyDB
|
||||
db = Store()
|
||||
|
||||
def get_all_threads():
|
||||
@ -259,6 +270,8 @@ def capsule(target: object, db: object):
|
||||
i.run_stop_ev = None
|
||||
#ukladame radek do historie (pozdeji refactor)
|
||||
save_history(id=i.id, st=target, runner=i, reason=reason)
|
||||
#store in archive header and archive detail
|
||||
archive_runner(runner=i, strat=target)
|
||||
#mazeme runner po skonceni instance
|
||||
db.runners.remove(i)
|
||||
|
||||
@ -342,6 +355,8 @@ def run_stratin(id: UUID, runReq: RunRequest):
|
||||
runner = Runner(id = i.id,
|
||||
run_started = datetime.now(zoneNY),
|
||||
run_pause_ev = pe,
|
||||
run_name = name,
|
||||
run_note = runReq.note,
|
||||
run_stop_ev = se,
|
||||
run_thread = vlakno,
|
||||
run_account = runReq.account,
|
||||
@ -368,4 +383,79 @@ def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float):
|
||||
#print(all_trades[symbol])
|
||||
return 0, all_trades[symbol]
|
||||
except Exception as e:
|
||||
return (-2, f"problem {e}")
|
||||
return (-2, f"problem {e}")
|
||||
|
||||
#archives runner and details
|
||||
def archive_runner(runner: Runner, strat: StrategyInstance):
|
||||
print("inside archive_runner")
|
||||
try:
|
||||
if strat.bt is not None:
|
||||
bp_from = strat.bt.bp_from
|
||||
bp_to = strat.bt.bp_to
|
||||
else:
|
||||
bp_from = None
|
||||
bp_to = None
|
||||
id = uuid4()
|
||||
runArchive: RunArchive = RunArchive(id = id,
|
||||
strat_id = runner.id,
|
||||
name=runner.run_name,
|
||||
note=runner.run_note,
|
||||
started=runner.run_started,
|
||||
stopped=runner.run_stopped,
|
||||
mode=runner.run_mode,
|
||||
account=runner.run_account,
|
||||
bt_from=bp_from,
|
||||
bt_to = bp_to,
|
||||
stratvars = strat.state.vars,
|
||||
profit=round(strat.state.profit,2),
|
||||
trade_count=len(strat.state.tradeList),
|
||||
end_positions=strat.state.positions,
|
||||
end_positions_avgp=round(strat.state.avgp,3),
|
||||
open_orders=9999
|
||||
)
|
||||
|
||||
runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = id,
|
||||
name=runner.run_name,
|
||||
bars=strat.state.bars,
|
||||
indicators=strat.state.indicators,
|
||||
statinds=strat.state.statinds,
|
||||
trades=strat.state.tradeList)
|
||||
resh = db_arch_h.insert(runArchive.__dict__)
|
||||
resd = db_arch_d.insert(runArchiveDetail.__dict__)
|
||||
print("archive runner finished")
|
||||
return 0, str(resh) + " " + str(resd)
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return -2, str(e)
|
||||
|
||||
def get_all_archived_runners():
|
||||
res = db_arch_h.all()
|
||||
return 0, res
|
||||
|
||||
#delete runner in archive and archive detail
|
||||
def delete_archived_runners_byID(id: UUID):
|
||||
try:
|
||||
resh = db_arch_h.remove(where('id') == id)
|
||||
resd = db_arch_d.remove(where('id') == id)
|
||||
if len(resh) == 0 or len(resd) == 0:
|
||||
return -1, "not found "+str(resh) + " " + str(resd)
|
||||
return 0, str(resh) + " " + str(resd)
|
||||
except Exception as e:
|
||||
return -2, str(e)
|
||||
|
||||
def get_all_archived_runners_detail():
|
||||
res = db_arch_d.all()
|
||||
return 0, res
|
||||
|
||||
def get_archived_runner_details_byID(id: UUID):
|
||||
res = db_arch_d.get(where('id') == id)
|
||||
if res==None:
|
||||
return -2, "not found"
|
||||
else:
|
||||
return 0, res
|
||||
|
||||
|
||||
# change_archived_runner
|
||||
# delete_archived_runner_details
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@ -13,7 +13,7 @@ class Order:
|
||||
self.limit_price = limit_price
|
||||
|
||||
|
||||
class FillCondition(Enum):
|
||||
class FillCondition(str, Enum):
|
||||
"""
|
||||
Execution settings:
|
||||
fast = pro vyplneni limi orderu musi byt cena stejne
|
||||
@ -22,13 +22,13 @@ class FillCondition(Enum):
|
||||
"""
|
||||
FAST = "fast"
|
||||
SLOW = "slow"
|
||||
class Account(Enum):
|
||||
class Account(str, Enum):
|
||||
"""
|
||||
Accounts - keys to config
|
||||
"""
|
||||
ACCOUNT1 = "ACCOUNT1"
|
||||
ACCOUNT2 = "ACCOUNT2"
|
||||
class RecordType(Enum):
|
||||
class RecordType(str, Enum):
|
||||
"""
|
||||
Represents output of aggregator
|
||||
"""
|
||||
@ -37,7 +37,7 @@ class RecordType(Enum):
|
||||
CBAR = "continuosbar"
|
||||
TRADE = "trade"
|
||||
|
||||
class Mode(Enum):
|
||||
class Mode(str, Enum):
|
||||
"""
|
||||
LIVE or BT
|
||||
"""
|
||||
@ -47,7 +47,7 @@ class Mode(Enum):
|
||||
BT = "backtest"
|
||||
|
||||
|
||||
class StartBarAlign(Enum):
|
||||
class StartBarAlign(str, Enum):
|
||||
"""
|
||||
Represents first bar start time alignement according to timeframe
|
||||
ROUND = bar starts at 0,5,10 (for 5s timeframe)
|
||||
|
||||
@ -12,7 +12,7 @@ from fastapi.security import APIKeyHeader
|
||||
import uvicorn
|
||||
from uuid import UUID
|
||||
import v2realbot.controller.services as cs
|
||||
from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade
|
||||
from v2realbot.common.model import StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveDetail
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query
|
||||
from fastapi.responses import HTMLResponse, FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@ -262,6 +262,44 @@ def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) ->
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f"No trades found {res}")
|
||||
|
||||
#ARCHIVE RUNNERS SECTION
|
||||
|
||||
#get all archived runners header
|
||||
@app.get("/archived_runners/", dependencies=[Depends(api_key_auth)])
|
||||
def _get_all_archived_runners() -> list[RunArchive]:
|
||||
res, set =cs.get_all_archived_runners()
|
||||
if res == 0:
|
||||
return set
|
||||
else:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"No data found")
|
||||
|
||||
#delete archive runner from header and detail
|
||||
@app.delete("/archived_runners/{runner_id}", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
|
||||
def _delete_archived_runners_byID(runner_id):
|
||||
res, id = cs.delete_archived_runners_byID(id=runner_id)
|
||||
if res == 0: return id
|
||||
elif res < 0:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
||||
|
||||
#get all archived runners detail
|
||||
@app.get("/archived_runners_detail/", dependencies=[Depends(api_key_auth)])
|
||||
def _get_all_archived_runners_detail() -> list[RunArchiveDetail]:
|
||||
res, set =cs.get_all_archived_runners_detail()
|
||||
if res == 0:
|
||||
return set
|
||||
else:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"No data found")
|
||||
|
||||
#get archived runners detail by id
|
||||
@app.get("/archived_runners_detail/{runner_id}", dependencies=[Depends(api_key_auth)])
|
||||
def _get_archived_runner_details_byID(runner_id) -> RunArchiveDetail:
|
||||
res, set = cs.get_archived_runner_details_byID(runner_id)
|
||||
if res == 0:
|
||||
return set
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f"No runner with id: {runner_id} a {set}")
|
||||
|
||||
|
||||
#join cekej na dokonceni vsech
|
||||
for i in cs.db.runners:
|
||||
i.run_thread.join()
|
||||
|
||||
@ -17,25 +17,28 @@
|
||||
<body>
|
||||
<div id="main" class="mainConteiner flex-container">
|
||||
<div id="chartContainer" class="flex-items">
|
||||
<h4>Status: <span id="status">Not connected</span></h4>
|
||||
<div id="formular">
|
||||
<form action="">
|
||||
<input type="text" id="runnerId" autocomplete="off" placeholder="StrategyID" value=""/>
|
||||
<button onclick="connect(event)" id="bt-conn" class="btn btn-success">Connect</button>
|
||||
<button onclick="disconnect(event)" id="bt-disc" style="display: None" class="btn btn-success">Disconnect</button>
|
||||
<!-- <label>Message: --> <input type="text" id="messageText" autocomplete="off" placeholder="WS out message"/>
|
||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="chart" style="display: None; float: left; "></div>
|
||||
<div class="legend" id="legend"></div>
|
||||
<div id="msgContainer">
|
||||
<div class="msgContainerInner">
|
||||
<div id="lines">
|
||||
<label data-toggle="collapse" data-target="#chartContainerInner">Realtime chart</label>
|
||||
<h5>Status: <span id="status">Not connected</span></h5>
|
||||
<div id="chartContainerInner" class="collapsed collapse in">
|
||||
<div id="formular">
|
||||
<form action="">
|
||||
<input type="text" id="runnerId" autocomplete="off" placeholder="StrategyID" value=""/>
|
||||
<button onclick="connect(event)" id="bt-conn" class="btn btn-success">Connect</button>
|
||||
<button onclick="disconnect(event)" id="bt-disc" style="display: None" class="btn btn-success">Disconnect</button>
|
||||
<!-- <label>Message: --> <input type="text" id="messageText" autocomplete="off" placeholder="WS out message"/>
|
||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="chart" style="display: None; float: left; "></div>
|
||||
<div class="legend" id="legend"></div>
|
||||
<div id="msgContainer">
|
||||
<div class="msgContainerInner">
|
||||
<div id="lines">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hist-trades" class="flex-items">
|
||||
<div id="form-trades">
|
||||
<label data-toggle="collapse" data-target="#trades-data">Trade history</label>
|
||||
@ -50,15 +53,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="runner-table" class="flex-items">
|
||||
<div id="controls">
|
||||
<label>API-KEY: <input type="password" id="api-key" autocomplete="off"/></label>
|
||||
<button onclick="store_api_key(event)" id="bt-store" class="btn btn-success">Store</button>
|
||||
<button id="button_pause" class="btn btn-success">Pause/Unpause</button>
|
||||
<button id="button_stop" class="btn btn-success">Stop</button>
|
||||
<button id="button_stopall" class="btn btn-success">Stop All</button>
|
||||
<button id="button_refresh" class="btn btn-success">Refresh</button>
|
||||
</div>
|
||||
<div id="runner-table">
|
||||
<label data-toggle="collapse" data-target="#runner-table-inner">Running</label>
|
||||
<div id="runner-table-inner" class="collapsed">
|
||||
<div id="controls">
|
||||
<label>API-KEY: <input type="password" id="api-key" autocomplete="off"/></label>
|
||||
<button onclick="store_api_key(event)" id="bt-store" class="btn btn-success">Store</button>
|
||||
<button id="button_pause" class="btn btn-success">Pause/Unpause</button>
|
||||
<button id="button_stop" class="btn btn-success">Stop</button>
|
||||
<button id="button_stopall" class="btn btn-success">Stop All</button>
|
||||
<button id="button_refresh" class="btn btn-success">Refresh</button>
|
||||
</div>
|
||||
<table id="runnerTable" class="display" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -95,36 +99,97 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="archive-table" class="flex-items">
|
||||
<label data-toggle="collapse" data-target="#archive-table-inner">Run Archive</label>
|
||||
<div id="archive-table-inner" class="collapsed">
|
||||
<div id="archive-chart">
|
||||
<div id="chartArchive" style="position: relative;">BT chart</div>
|
||||
<div class="legend" id="legendArchive"></div>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<button id="button_show_arch" class="btn btn-success">Show</button>
|
||||
<button id="button_delete_arch" class="btn btn-success">Delete</button>
|
||||
<!-- <button id="button_stopall" class="btn btn-success">Stop All</button>
|
||||
<button id="button_refresh" class="btn btn-success">Refresh</button> -->
|
||||
</div>
|
||||
<table id="archiveTable" class="display" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>Note</th>
|
||||
<th>started</th>
|
||||
<th>stopped</th>
|
||||
<th>mode</th>
|
||||
<th>account</th>
|
||||
<th>bt_from</th>
|
||||
<th>bt_to</th>
|
||||
<th>stratvars</th>
|
||||
<th>profit</th>
|
||||
<th>tradecnt</th>
|
||||
<th>end_pos</th>
|
||||
<th>end_pos_avgp</th>
|
||||
<th>open</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="delModalArchive" class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form method="post" id="delFormArchive">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title_del"><i class="fa fa-plus"></i> Delete Archive</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="delidarchive" class="control-label">Id</label>
|
||||
<input type="text" class="form-control" id="delidarchive" name="delidarchive" placeholder="id">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input type="submit" name="delete" id="deletearchive" class="btn btn-info" value="Delete" />
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="stratin-table" class="flex-items">
|
||||
<div id="stratin-table">
|
||||
<button id="button_add" class="btn btn-success">Add</button>
|
||||
<button id="button_add_json" class="btn btn-success">Add JSON</button>
|
||||
<button id="button_edit" class="btn btn-success">Edit</button>
|
||||
<button id="button_dup" class="btn btn-success">Duplicate</button>
|
||||
<button id="button_copy" class="btn btn-success">Copy JSON</button>
|
||||
<button id="button_delete" class="btn btn-success">Delete</button>
|
||||
<button id="button_run" class="btn btn-success">Run Strategy</button>
|
||||
<table id="stratinTable" class="display" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Id2</th>
|
||||
<th>Name</th>
|
||||
<th>Symbol</th>
|
||||
<th>class</th>
|
||||
<th>script</th>
|
||||
<th>OR</th>
|
||||
<th>CR</th>
|
||||
<th>Stratvars</th>
|
||||
<th>add_data</th>
|
||||
<th>note</th>
|
||||
<th>history</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<label data-toggle="collapse" data-target="#stratin-table-inner">Strategies</label>
|
||||
<div id="stratin-table-inner" class="collapsed">
|
||||
<button id="button_add" class="btn btn-success">Add</button>
|
||||
<button id="button_add_json" class="btn btn-success">Add JSON</button>
|
||||
<button id="button_edit" class="btn btn-success">Edit</button>
|
||||
<button id="button_dup" class="btn btn-success">Duplicate</button>
|
||||
<button id="button_copy" class="btn btn-success">Copy JSON</button>
|
||||
<button id="button_delete" class="btn btn-success">Delete</button>
|
||||
<button id="button_run" class="btn btn-success">Run Strategy</button>
|
||||
<table id="stratinTable" class="display" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Id2</th>
|
||||
<th>Name</th>
|
||||
<th>Symbol</th>
|
||||
<th>class</th>
|
||||
<th>script</th>
|
||||
<th>OR</th>
|
||||
<th>CR</th>
|
||||
<th>Stratvars</th>
|
||||
<th>add_data</th>
|
||||
<th>note</th>
|
||||
<th>history</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="recordModal" class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
@ -292,7 +357,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bottomContainer" class="flex-items" style="height: 800px">
|
||||
<BR>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/static/js/mywebsocket.js"></script>
|
||||
|
||||
@ -1,70 +1,80 @@
|
||||
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
||||
const chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}}
|
||||
const chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
||||
}})
|
||||
const 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
|
||||
},
|
||||
});
|
||||
|
||||
//it is called after population
|
||||
function populate_real_time_chart() {
|
||||
if (chart !== null) {
|
||||
chart.remove()
|
||||
}
|
||||
|
||||
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
||||
const chartOptions = { width: 1045, height: 600, leftPriceScale: {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
|
||||
}})
|
||||
const 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
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const 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,
|
||||
},
|
||||
});
|
||||
const 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,
|
||||
},
|
||||
});
|
||||
|
||||
const vwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
})
|
||||
const vwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
})
|
||||
|
||||
//chart.timeScale().fitContent();
|
||||
//chart.timeScale().fitContent();
|
||||
|
||||
//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);
|
||||
//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 => {
|
||||
//display timestamp in trade-timestamp input field
|
||||
$('#trade-timestamp').val(param.time)
|
||||
});
|
||||
|
||||
chart.subscribeCrosshairMove((param) => {
|
||||
if (param.time) {
|
||||
const data = param.seriesData.get(vwapSeries);
|
||||
const vwap = data.value !== undefined ? data.value : data.close;
|
||||
const bars = param.seriesData.get(candlestickSeries);
|
||||
const volumes = param.seriesData.get(volumeSeries);
|
||||
firstRow.innerText = "";
|
||||
//iterate of custom indicators dictionary to get values of custom lines
|
||||
// var customIndicator = {name: key, series: null}
|
||||
indList.forEach(function (item) {
|
||||
const ind = param.seriesData.get(item.series)
|
||||
firstRow.innerText += item.name + " " + ind.value + " ";
|
||||
});
|
||||
|
||||
firstRow.innerText += ' vwap' + ' ' + vwap.toFixed(2) + " O" + bars.open + " H" + bars.high + " L" + bars.low + " C" + bars.close + " V" + volumes.value + "";
|
||||
}
|
||||
else {
|
||||
firstRow.innerText = '-';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pad(n) {
|
||||
var s = ('0' + n);
|
||||
return s.substr(s.length - 2);
|
||||
}
|
||||
|
||||
chart.subscribeCrosshairMove((param) => {
|
||||
if (param.time) {
|
||||
const data = param.seriesData.get(vwapSeries);
|
||||
const vwap = data.value !== undefined ? data.value : data.close;
|
||||
const bars = param.seriesData.get(candlestickSeries);
|
||||
const volumes = param.seriesData.get(volumeSeries);
|
||||
firstRow.innerText = "";
|
||||
//iterate of custom indicators dictionary to get values of custom lines
|
||||
// var customIndicator = {name: key, series: null}
|
||||
indList.forEach(function (item) {
|
||||
const ind = param.seriesData.get(item.series)
|
||||
firstRow.innerText += item.name + " " + ind.value + " ";
|
||||
});
|
||||
|
||||
firstRow.innerText += ' vwap' + ' ' + vwap.toFixed(2) + " O" + bars.open + " H" + bars.high + " L" + bars.low + " C" + bars.close + " V" + volumes.value + "";
|
||||
|
||||
//display timestamp in trade-timestamp input field
|
||||
$('#trade-timestamp').val(param.time)
|
||||
|
||||
}
|
||||
else {
|
||||
firstRow.innerText = '-';
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,24 @@
|
||||
|
||||
API_KEY = localStorage.getItem("api-key")
|
||||
var chart = null
|
||||
//Date.prototype.toJSON = function(){ return Date.parse(this)/1000 }
|
||||
|
||||
// safely handles circular references https://stackoverflow.com/questions/11616630/how-can-i-print-a-circular-structure-in-a-json-like-format
|
||||
JSON.safeStringify = (obj, indent = 2) => {
|
||||
let cache = [];
|
||||
const retVal = JSON.stringify(
|
||||
obj,
|
||||
(key, value) =>
|
||||
typeof value === "object" && value !== null
|
||||
? cache.includes(value)
|
||||
? undefined // Duplicate reference found, discard key
|
||||
: cache.push(value) && value // Store value in our collection
|
||||
: value,
|
||||
indent
|
||||
);
|
||||
cache = null;
|
||||
return retVal;
|
||||
};
|
||||
|
||||
// Iterate through each element in the
|
||||
// first array and if some of them
|
||||
@ -92,9 +111,448 @@ function is_running(id) {
|
||||
// //console.log(obj);
|
||||
// //alert(JSON.stringify(obj))
|
||||
|
||||
var tradeDetails = new Map();
|
||||
//CHART ARCHIVED RUN - move to own file
|
||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
||||
//output array [{ time: 111, open: 11, high: 33, low: 333, close: 333},..]
|
||||
function transform_data(data) {
|
||||
transformed = []
|
||||
//get basic bars, volume and vvwap
|
||||
var bars = []
|
||||
var volume = []
|
||||
var vwap = []
|
||||
data.bars.time.forEach((element, index, array) => {
|
||||
sbars = {};
|
||||
svolume = {};
|
||||
svwap = {};
|
||||
|
||||
sbars["time"] = element;
|
||||
sbars["close"] = data.bars.close[index]
|
||||
sbars["open"] = data.bars.open[index]
|
||||
sbars["high"] = data.bars.high[index]
|
||||
sbars["low"] = data.bars.low[index]
|
||||
|
||||
|
||||
svwap["time"] = element
|
||||
svwap["value"] = data.bars.vwap[index]
|
||||
|
||||
svolume["time"] = element
|
||||
svolume["value"] = data.bars.volume[index]
|
||||
|
||||
bars.push(sbars)
|
||||
vwap.push(svwap)
|
||||
volume.push(svolume)
|
||||
});
|
||||
transformed["bars"] = bars
|
||||
transformed["vwap"] = vwap
|
||||
transformed["volume"] = volume
|
||||
|
||||
//get markers - avgp line for all buys
|
||||
var avgp_buy_line = []
|
||||
var avgp_markers = []
|
||||
var markers = []
|
||||
var markers_line = []
|
||||
data.trades.forEach((trade, index, array) => {
|
||||
obj = {};
|
||||
a_markers = {}
|
||||
timestamp = Date.parse(trade.order.filled_at)/1000
|
||||
if (trade.order.side == "buy") {
|
||||
//line pro avgp markers
|
||||
obj["time"] = timestamp;
|
||||
obj["value"] = trade.pos_avg_price;
|
||||
avgp_buy_line.push(obj)
|
||||
|
||||
//avgp markers pro prumernou cenu aktualnich pozic
|
||||
a_markers["time"] = timestamp
|
||||
a_markers["position"] = "aboveBar"
|
||||
a_markers["color"] = "#e8c76d"
|
||||
a_markers["shape"] = "arrowDown"
|
||||
a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
|
||||
avgp_markers.push(a_markers)
|
||||
}
|
||||
|
||||
//buy sell markery
|
||||
marker = {}
|
||||
marker["time"] = timestamp;
|
||||
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
||||
marker["position"] = (trade.order.side == "buy") ? "inBar" : "aboveBar"
|
||||
marker["color"] = (trade.order.side == "buy") ? "blue" : "red"
|
||||
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||
marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown"
|
||||
marker["text"] = trade.qty + " " + trade.price
|
||||
markers.push(marker)
|
||||
|
||||
//prevedeme iso data na timestampy
|
||||
trade.order.submitted_at = Date.parse(trade.order.submitted_at)/1000
|
||||
trade.order.filled_at = Date.parse(trade.order.filled_at)/1000
|
||||
trade.timestamp = Date.parse(trade.order.timestamp)/1000
|
||||
tradeDetails.set(timestamp, trade)
|
||||
|
||||
//line pro buy/sell markery
|
||||
mline = {}
|
||||
mline["time"] = timestamp
|
||||
mline["value"] = trade.price
|
||||
markers_line.push(mline)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
|
||||
|
||||
});
|
||||
transformed["avgp_buy_line"] = avgp_buy_line
|
||||
transformed["markers"] = markers
|
||||
transformed["markers_line"] = markers_line
|
||||
transformed["avgp_markers"] = avgp_markers
|
||||
//get additional indicators
|
||||
//TBD
|
||||
return transformed
|
||||
}
|
||||
|
||||
function chart_archived_run(data) {
|
||||
if (chart !== null) {
|
||||
chart.remove()
|
||||
}
|
||||
|
||||
//console.log("inside")
|
||||
var transformed_data = transform_data(data)
|
||||
//console.log(transformed_data)
|
||||
//tbd transform indicators
|
||||
//var markersData = transform_trades(data)
|
||||
|
||||
// time: datesForMarkers[i].time,
|
||||
// position: 'aboveBar',
|
||||
// color: '#e91e63',
|
||||
// shape: 'arrowDown',
|
||||
// text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),
|
||||
document.getElementById("chart").style.display = "block"
|
||||
//initialize chart
|
||||
var chartOptions = { width: 1300, height: 600, leftPriceScale: {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
|
||||
}})
|
||||
var archCandlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
||||
archCandlestickSeries.priceScale().applyOptions({
|
||||
scaleMargins: {
|
||||
top: 0.1, // highest point of the series will be 10% away from the top
|
||||
bottom: 0.4, // lowest point will be 40% away from the bottom
|
||||
},
|
||||
});
|
||||
|
||||
var archVwapSeries = chart.addLineSeries({
|
||||
// title: "vwap",
|
||||
color: '#2962FF',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
var archVolumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||
archVolumeSeries.priceScale().applyOptions({
|
||||
// set the positioning of the volume series
|
||||
scaleMargins: {
|
||||
top: 0.7, // highest point of the series will be 70% away from the top
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
archVwapSeries.setData(transformed_data["vwap"])
|
||||
archCandlestickSeries.setData(transformed_data["bars"])
|
||||
archVolumeSeries.setData(transformed_data["volume"])
|
||||
|
||||
|
||||
var avgBuyLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
color: '#e8c76d',
|
||||
// color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
avgBuyLine.setData(transformed_data["avgp_buy_line"]);
|
||||
|
||||
avgBuyLine.setMarkers(transformed_data["avgp_markers"])
|
||||
|
||||
var markersLine = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
// color: '#d6d1c3',
|
||||
color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
});
|
||||
|
||||
markersLine.setData(transformed_data["markers_line"]);
|
||||
|
||||
//console.log("markers")
|
||||
//console.log(transformed_data["markers"])
|
||||
|
||||
markersLine.setMarkers(transformed_data["markers"])
|
||||
|
||||
|
||||
|
||||
//TBD dynamicky
|
||||
//pokud je nazev atributu X_candles vytvorit candles
|
||||
//pokud je objekt Y_line pak vytvorit lajnu
|
||||
//pokud je objekt Z_markers pak vytvorit markers
|
||||
//pokud je Z = X nebo Y, pak markers dat na danou lajnu (priklad vvwap_line, avgp_line, avgp_markers)
|
||||
//udelat si nahodny vyber barev z listu
|
||||
|
||||
//DO BUDOUCNA MARKERS
|
||||
// chart.subscribeCrosshairMove(param => {
|
||||
// console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
|
||||
//define tooltip
|
||||
const container1 = document.getElementById('chart');
|
||||
|
||||
const toolTipWidth = 90;
|
||||
const toolTipHeight = 90;
|
||||
const toolTipMargin = 15;
|
||||
|
||||
// Create and style the tooltip html element
|
||||
const toolTip = document.createElement('div');
|
||||
//width: 90px; , height: 80px;
|
||||
toolTip.style = `position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`;
|
||||
toolTip.style.background = 'white';
|
||||
toolTip.style.color = 'black';
|
||||
toolTip.style.borderColor = '#2962FF';
|
||||
container1.appendChild(toolTip);
|
||||
|
||||
|
||||
//TODO onlick zkopirovat timestamp param.time
|
||||
// chart.subscribeClick(param => {
|
||||
// $('#trade-timestamp').val(param.time)
|
||||
// //alert(JSON.safeStringify(param))
|
||||
// //console.log(param.hoveredObjectId);
|
||||
// });
|
||||
|
||||
//chart.subscribeCrosshairMove(param => {
|
||||
|
||||
chart.subscribeClick(param => {
|
||||
$('#trade-timestamp').val(param.time)
|
||||
if (
|
||||
param.point === undefined ||
|
||||
!param.time ||
|
||||
param.point.x < 0 ||
|
||||
param.point.x > container1.clientWidth ||
|
||||
param.point.y < 0 ||
|
||||
param.point.y > container1.clientHeight
|
||||
) {
|
||||
toolTip.style.display = 'none';
|
||||
} else {
|
||||
//vyber serie s jakou chci pracovat - muzu i dynamicky
|
||||
//je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams
|
||||
|
||||
//key = series (key.seriestype vraci Line/Candlestick atp.) https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap
|
||||
|
||||
toolTip.style.display = 'none';
|
||||
toolTip.innerHTML = "";
|
||||
var data = param.seriesData.get(markersLine);
|
||||
if (data !== undefined) {
|
||||
//param.seriesData.forEach((value, key) => {
|
||||
//console.log("key",key)
|
||||
//console.log("value",value)
|
||||
|
||||
//data = value
|
||||
//DOCASNE VYPNUTO
|
||||
toolTip.style.display = 'block';
|
||||
|
||||
//console.log(JSON.safeStringify(key))
|
||||
if (toolTip.innerHTML == "") {
|
||||
toolTip.innerHTML = `<div>${param.time}</div>`
|
||||
}
|
||||
var price = data.value
|
||||
// !== undefined ? data.value : data.close;
|
||||
|
||||
|
||||
toolTip.innerHTML += `<pre>${JSON.stringify(tradeDetails.get(param.time),null,2)}</pre><div>${price.toFixed(3)}</div>`;
|
||||
|
||||
//inspirace
|
||||
// toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
|
||||
// ${Math.round(100 * price) / 100}
|
||||
// </div><div style="color: ${'black'}">
|
||||
// ${dateStr}
|
||||
// </div>`;
|
||||
|
||||
|
||||
// Position tooltip according to mouse cursor position
|
||||
toolTip.style.left = param.point.x+120 + 'px';
|
||||
toolTip.style.top = param.point.y-100 + 'px';
|
||||
}
|
||||
//});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
|
||||
//TBD other dynamically created indicators
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//ARCHIVE TABLES
|
||||
$(document).ready(function () {
|
||||
archiveRecords.ajax.reload();
|
||||
|
||||
//disable buttons (enable on row selection)
|
||||
$('#button_show_arch').attr('disabled','disabled');
|
||||
$('#button_delete_arch').attr('disabled','disabled');
|
||||
|
||||
|
||||
//selectable rows in archive table
|
||||
$('#archiveTable tbody').on('click', 'tr', function () {
|
||||
if ($(this).hasClass('selected')) {
|
||||
$(this).removeClass('selected');
|
||||
$('#button_show_arch').attr('disabled','disabled');
|
||||
$('#button_delete_arch').attr('disabled','disabled');
|
||||
} else {
|
||||
stratinRecords.$('tr.selected').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
$('#button_show_arch').attr('disabled',false);
|
||||
$('#button_delete_arch').attr('disabled',false);
|
||||
}
|
||||
});
|
||||
|
||||
//delete button
|
||||
$('#button_delete_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
window.$('#delModalArchive').modal('show');
|
||||
$('#delidarchive').val(row.id);
|
||||
});
|
||||
|
||||
|
||||
//show button
|
||||
$('#button_show_arch').click(function () {
|
||||
row = archiveRecords.row('.selected').data();
|
||||
$('#button_show_arch').attr('disabled',true);
|
||||
$.ajax({
|
||||
url:"/archived_runners_detail/"+row.id,
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
method:"GET",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success:function(data){
|
||||
$('#button_show_arch').attr('disabled',false);
|
||||
//$('#chartArchive').append(JSON.stringify(data,null,2));
|
||||
console.log(JSON.stringify(data,null,2));
|
||||
chart_archived_run(data);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
var err = eval("(" + xhr.responseText + ")");
|
||||
window.alert(JSON.stringify(xhr));
|
||||
//console.log(JSON.stringify(xhr));
|
||||
$('#button_show_arch').attr('disabled',false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
//delete modal
|
||||
$("#delModalArchive").on('submit','#delFormArchive', function(event){
|
||||
event.preventDefault();
|
||||
$('#deletearchive').attr('disabled','disabled');
|
||||
id = $('#delidarchive').val()
|
||||
//var formData = $(this).serializeJSON();
|
||||
$.ajax({
|
||||
url:"/archived_runners/"+id,
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
method:"DELETE",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success:function(data){
|
||||
$('#delFormArchive')[0].reset();
|
||||
window.$('#delModalArchive').modal('hide');
|
||||
$('#deletearchive').attr('disabled', false);
|
||||
archiveRecords.ajax.reload();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
var err = eval("(" + xhr.responseText + ")");
|
||||
window.alert(JSON.stringify(xhr));
|
||||
console.log(JSON.stringify(xhr));
|
||||
$('#deletearchive').attr('disabled', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
//https://www.w3schools.com/jsref/jsref_tolocalestring.asp
|
||||
function format_date(datum) {
|
||||
//const options = { weekday: 'long', year: 'numeric', month: 'numeric', day: 'numeric', };
|
||||
const options = {dateStyle: "short", timeStyle: "short"}
|
||||
const date = new Date(datum);
|
||||
return date.toLocaleString('cs-CZ', options);
|
||||
}
|
||||
|
||||
//stratin table
|
||||
var archiveRecords =
|
||||
$('#archiveTable').DataTable( {
|
||||
ajax: {
|
||||
url: '/archived_runners/',
|
||||
dataSrc: '',
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
error: function(xhr, status, error) {
|
||||
//var err = eval("(" + xhr.responseText + ")");
|
||||
//window.alert(JSON.stringify(xhr));
|
||||
console.log(JSON.stringify(xhr));
|
||||
}
|
||||
},
|
||||
columns: [{ data: 'id' },
|
||||
{data: 'name'},
|
||||
{data: 'note'},
|
||||
{data: 'started'},
|
||||
{data: 'stopped'},
|
||||
{data: 'mode'},
|
||||
{data: 'account', visible: true},
|
||||
{data: 'bt_from', visible: true},
|
||||
{data: 'bt_to', visible: true},
|
||||
{data: 'stratvars', visible: true},
|
||||
{data: 'profit'},
|
||||
{data: 'trade_count', visible: true},
|
||||
{data: 'end_positions', visible: true},
|
||||
{data: 'end_positions_avgp', visible: true},
|
||||
{data: 'open_orders', visible: true}
|
||||
],
|
||||
columnDefs: [{
|
||||
targets: [3,4,7,8],
|
||||
render: function ( data, type, row ) {
|
||||
return format_date(data)
|
||||
},
|
||||
}],
|
||||
order: [[4, 'desc']],
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
// createdRow: function( row, data, dataIndex){
|
||||
// if (is_running(data.id) ){
|
||||
// alert("runner");
|
||||
// $(row).addClass('highlight');
|
||||
// }
|
||||
//}
|
||||
} );
|
||||
|
||||
//STRATIN and RUNNERS TABELS
|
||||
$(document).ready(function () {
|
||||
//reaload hlavni tabulky
|
||||
|
||||
@ -239,6 +697,7 @@ $(document).ready(function () {
|
||||
$('#button_refresh').click(function () {
|
||||
runnerRecords.ajax.reload();
|
||||
stratinRecords.ajax.reload();
|
||||
archiveRecords.ajax.reload();
|
||||
})
|
||||
|
||||
//button copy
|
||||
@ -258,7 +717,7 @@ $(document).ready(function () {
|
||||
rec.add_data_conf = row.add_data_conf;
|
||||
rec.note = row.note;
|
||||
rec.history = "";
|
||||
jsonString = JSON.stringify(rec);
|
||||
jsonString = JSON.stringify(rec, null, 2);
|
||||
navigator.clipboard.writeText(jsonString);
|
||||
$('#button_copy').attr('disabled', false);
|
||||
})
|
||||
|
||||
@ -16,6 +16,7 @@ function connect(event) {
|
||||
console.log("nejaky error" + err)
|
||||
}
|
||||
ws.onopen = function(event) {
|
||||
populate_real_time_chart()
|
||||
document.getElementById("status").textContent = "Connected to" + runnerId.value
|
||||
document.getElementById("bt-disc").style.display = "initial"
|
||||
document.getElementById("bt-conn").style.display = "none"
|
||||
|
||||
Binary file not shown.
@ -17,7 +17,8 @@ from v2realbot.interfaces.backtest_interface import BacktestInterface
|
||||
from v2realbot.interfaces.live_interface import LiveInterface
|
||||
from alpaca.trading.enums import OrderSide
|
||||
from v2realbot.backtesting.backtester import Backtester
|
||||
from alpaca.trading.models import TradeUpdate
|
||||
#from alpaca.trading.models import TradeUpdate
|
||||
from v2realbot.common.model import TradeUpdate
|
||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||
from threading import Event, current_thread
|
||||
import json
|
||||
|
||||
Binary file not shown.
@ -9,7 +9,7 @@ import decimal
|
||||
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
||||
import pickle
|
||||
import os
|
||||
from v2realbot.common.model import StrategyInstance, Runner
|
||||
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail
|
||||
from typing import List
|
||||
import tomli
|
||||
from v2realbot.config import DATA_DIR, QUIET_MODE
|
||||
@ -66,6 +66,11 @@ def json_serial(obj):
|
||||
return obj.__dict__
|
||||
if type(obj) is btTradeUpdate:
|
||||
return obj.__dict__
|
||||
if type(obj) is RunArchive:
|
||||
return obj.__dict__
|
||||
if type(obj) is RunArchiveDetail:
|
||||
return obj.__dict__
|
||||
|
||||
raise TypeError (str(obj)+"Type %s not serializable" % type(obj))
|
||||
|
||||
def parse_toml_string(tomlst: str):
|
||||
|
||||
Reference in New Issue
Block a user