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}")
|
# 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):
|
class Trade(BaseModel):
|
||||||
symbol: str
|
symbol: str
|
||||||
timestamp: datetime
|
timestamp: datetime
|
||||||
@ -56,15 +56,21 @@ class RunRequest(BaseModel):
|
|||||||
id: UUID
|
id: UUID
|
||||||
account: Account
|
account: Account
|
||||||
mode: Mode
|
mode: Mode
|
||||||
|
note: Optional[str] = None
|
||||||
debug: bool = False
|
debug: 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
|
||||||
run_started: Optional[datetime] = None
|
run_started: Optional[datetime] = None
|
||||||
run_mode: Mode
|
run_mode: Mode
|
||||||
|
run_name: Optional[str] = None
|
||||||
|
run_note: Optional[str] = None
|
||||||
run_account: Account
|
run_account: Account
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
@ -75,6 +81,8 @@ class Runner(BaseModel):
|
|||||||
run_started: Optional[datetime] = None
|
run_started: Optional[datetime] = None
|
||||||
run_mode: Mode
|
run_mode: Mode
|
||||||
run_account: Account
|
run_account: Account
|
||||||
|
run_name: Optional[str] = None
|
||||||
|
run_note: Optional[str] = None
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
run_thread: Optional[object] = None
|
run_thread: Optional[object] = None
|
||||||
@ -108,6 +116,37 @@ class TradeUpdate(BaseModel):
|
|||||||
cash: Optional[float]
|
cash: Optional[float]
|
||||||
pos_avg_price: 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):
|
# class Trade(BaseModel):
|
||||||
# order: Order
|
# order: Order
|
||||||
# value: float
|
# value: float
|
||||||
|
|||||||
@ -5,13 +5,24 @@ from alpaca.data.historical import StockHistoricalDataClient
|
|||||||
from alpaca.data.requests import StockTradesRequest
|
from alpaca.data.requests import StockTradesRequest
|
||||||
from alpaca.data.enums import DataFeed
|
from alpaca.data.enums import DataFeed
|
||||||
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
|
from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail
|
||||||
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string
|
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial
|
||||||
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
|
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR
|
||||||
import importlib
|
import importlib
|
||||||
from queue import Queue
|
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()
|
db = Store()
|
||||||
|
|
||||||
def get_all_threads():
|
def get_all_threads():
|
||||||
@ -259,6 +270,8 @@ def capsule(target: object, db: object):
|
|||||||
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.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
|
#mazeme runner po skonceni instance
|
||||||
db.runners.remove(i)
|
db.runners.remove(i)
|
||||||
|
|
||||||
@ -342,6 +355,8 @@ def run_stratin(id: UUID, runReq: RunRequest):
|
|||||||
runner = Runner(id = i.id,
|
runner = Runner(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_note = runReq.note,
|
||||||
run_stop_ev = se,
|
run_stop_ev = se,
|
||||||
run_thread = vlakno,
|
run_thread = vlakno,
|
||||||
run_account = runReq.account,
|
run_account = runReq.account,
|
||||||
@ -368,4 +383,79 @@ def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float):
|
|||||||
#print(all_trades[symbol])
|
#print(all_trades[symbol])
|
||||||
return 0, all_trades[symbol]
|
return 0, all_trades[symbol]
|
||||||
except Exception as e:
|
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
|
self.limit_price = limit_price
|
||||||
|
|
||||||
|
|
||||||
class FillCondition(Enum):
|
class FillCondition(str, Enum):
|
||||||
"""
|
"""
|
||||||
Execution settings:
|
Execution settings:
|
||||||
fast = pro vyplneni limi orderu musi byt cena stejne
|
fast = pro vyplneni limi orderu musi byt cena stejne
|
||||||
@ -22,13 +22,13 @@ class FillCondition(Enum):
|
|||||||
"""
|
"""
|
||||||
FAST = "fast"
|
FAST = "fast"
|
||||||
SLOW = "slow"
|
SLOW = "slow"
|
||||||
class Account(Enum):
|
class Account(str, Enum):
|
||||||
"""
|
"""
|
||||||
Accounts - keys to config
|
Accounts - keys to config
|
||||||
"""
|
"""
|
||||||
ACCOUNT1 = "ACCOUNT1"
|
ACCOUNT1 = "ACCOUNT1"
|
||||||
ACCOUNT2 = "ACCOUNT2"
|
ACCOUNT2 = "ACCOUNT2"
|
||||||
class RecordType(Enum):
|
class RecordType(str, Enum):
|
||||||
"""
|
"""
|
||||||
Represents output of aggregator
|
Represents output of aggregator
|
||||||
"""
|
"""
|
||||||
@ -37,7 +37,7 @@ class RecordType(Enum):
|
|||||||
CBAR = "continuosbar"
|
CBAR = "continuosbar"
|
||||||
TRADE = "trade"
|
TRADE = "trade"
|
||||||
|
|
||||||
class Mode(Enum):
|
class Mode(str, Enum):
|
||||||
"""
|
"""
|
||||||
LIVE or BT
|
LIVE or BT
|
||||||
"""
|
"""
|
||||||
@ -47,7 +47,7 @@ class Mode(Enum):
|
|||||||
BT = "backtest"
|
BT = "backtest"
|
||||||
|
|
||||||
|
|
||||||
class StartBarAlign(Enum):
|
class StartBarAlign(str, Enum):
|
||||||
"""
|
"""
|
||||||
Represents first bar start time alignement according to timeframe
|
Represents first bar start time alignement according to timeframe
|
||||||
ROUND = bar starts at 0,5,10 (for 5s timeframe)
|
ROUND = bar starts at 0,5,10 (for 5s timeframe)
|
||||||
|
|||||||
@ -12,7 +12,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.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 import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query
|
||||||
from fastapi.responses import HTMLResponse, FileResponse
|
from fastapi.responses import HTMLResponse, FileResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
@ -262,6 +262,44 @@ def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) ->
|
|||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=404, detail=f"No trades found {res}")
|
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
|
#join cekej na dokonceni vsech
|
||||||
for i in cs.db.runners:
|
for i in cs.db.runners:
|
||||||
i.run_thread.join()
|
i.run_thread.join()
|
||||||
|
|||||||
@ -17,25 +17,28 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="main" class="mainConteiner flex-container">
|
<div id="main" class="mainConteiner flex-container">
|
||||||
<div id="chartContainer" class="flex-items">
|
<div id="chartContainer" class="flex-items">
|
||||||
<h4>Status: <span id="status">Not connected</span></h4>
|
<label data-toggle="collapse" data-target="#chartContainerInner">Realtime chart</label>
|
||||||
<div id="formular">
|
<h5>Status: <span id="status">Not connected</span></h5>
|
||||||
<form action="">
|
<div id="chartContainerInner" class="collapsed collapse in">
|
||||||
<input type="text" id="runnerId" autocomplete="off" placeholder="StrategyID" value=""/>
|
<div id="formular">
|
||||||
<button onclick="connect(event)" id="bt-conn" class="btn btn-success">Connect</button>
|
<form action="">
|
||||||
<button onclick="disconnect(event)" id="bt-disc" style="display: None" class="btn btn-success">Disconnect</button>
|
<input type="text" id="runnerId" autocomplete="off" placeholder="StrategyID" value=""/>
|
||||||
<!-- <label>Message: --> <input type="text" id="messageText" autocomplete="off" placeholder="WS out message"/>
|
<button onclick="connect(event)" id="bt-conn" class="btn btn-success">Connect</button>
|
||||||
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
<button onclick="disconnect(event)" id="bt-disc" style="display: None" class="btn btn-success">Disconnect</button>
|
||||||
</form>
|
<!-- <label>Message: --> <input type="text" id="messageText" autocomplete="off" placeholder="WS out message"/>
|
||||||
</div>
|
<button onclick="sendMessage(event)" id="bt.send" class="btn btn-success">Send</button>
|
||||||
<div id="chart" style="display: None; float: left; "></div>
|
</form>
|
||||||
<div class="legend" id="legend"></div>
|
</div>
|
||||||
<div id="msgContainer">
|
<div id="chart" style="display: None; float: left; "></div>
|
||||||
<div class="msgContainerInner">
|
<div class="legend" id="legend"></div>
|
||||||
<div id="lines">
|
<div id="msgContainer">
|
||||||
|
<div class="msgContainerInner">
|
||||||
|
<div id="lines">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div id="hist-trades" class="flex-items">
|
<div id="hist-trades" class="flex-items">
|
||||||
<div id="form-trades">
|
<div id="form-trades">
|
||||||
<label data-toggle="collapse" data-target="#trades-data">Trade history</label>
|
<label data-toggle="collapse" data-target="#trades-data">Trade history</label>
|
||||||
@ -50,15 +53,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="runner-table" class="flex-items">
|
<div id="runner-table" class="flex-items">
|
||||||
<div id="controls">
|
<label data-toggle="collapse" data-target="#runner-table-inner">Running</label>
|
||||||
<label>API-KEY: <input type="password" id="api-key" autocomplete="off"/></label>
|
<div id="runner-table-inner" class="collapsed">
|
||||||
<button onclick="store_api_key(event)" id="bt-store" class="btn btn-success">Store</button>
|
<div id="controls">
|
||||||
<button id="button_pause" class="btn btn-success">Pause/Unpause</button>
|
<label>API-KEY: <input type="password" id="api-key" autocomplete="off"/></label>
|
||||||
<button id="button_stop" class="btn btn-success">Stop</button>
|
<button onclick="store_api_key(event)" id="bt-store" class="btn btn-success">Store</button>
|
||||||
<button id="button_stopall" class="btn btn-success">Stop All</button>
|
<button id="button_pause" class="btn btn-success">Pause/Unpause</button>
|
||||||
<button id="button_refresh" class="btn btn-success">Refresh</button>
|
<button id="button_stop" class="btn btn-success">Stop</button>
|
||||||
</div>
|
<button id="button_stopall" class="btn btn-success">Stop All</button>
|
||||||
<div id="runner-table">
|
<button id="button_refresh" class="btn btn-success">Refresh</button>
|
||||||
|
</div>
|
||||||
<table id="runnerTable" class="display" style="width:100%">
|
<table id="runnerTable" class="display" style="width:100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -95,36 +99,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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" class="flex-items">
|
||||||
<div id="stratin-table">
|
<label data-toggle="collapse" data-target="#stratin-table-inner">Strategies</label>
|
||||||
<button id="button_add" class="btn btn-success">Add</button>
|
<div id="stratin-table-inner" class="collapsed">
|
||||||
<button id="button_add_json" class="btn btn-success">Add JSON</button>
|
<button id="button_add" class="btn btn-success">Add</button>
|
||||||
<button id="button_edit" class="btn btn-success">Edit</button>
|
<button id="button_add_json" class="btn btn-success">Add JSON</button>
|
||||||
<button id="button_dup" class="btn btn-success">Duplicate</button>
|
<button id="button_edit" class="btn btn-success">Edit</button>
|
||||||
<button id="button_copy" class="btn btn-success">Copy JSON</button>
|
<button id="button_dup" class="btn btn-success">Duplicate</button>
|
||||||
<button id="button_delete" class="btn btn-success">Delete</button>
|
<button id="button_copy" class="btn btn-success">Copy JSON</button>
|
||||||
<button id="button_run" class="btn btn-success">Run Strategy</button>
|
<button id="button_delete" class="btn btn-success">Delete</button>
|
||||||
<table id="stratinTable" class="display" style="width:100%">
|
<button id="button_run" class="btn btn-success">Run Strategy</button>
|
||||||
<thead>
|
<table id="stratinTable" class="display" style="width:100%">
|
||||||
<tr>
|
<thead>
|
||||||
<th>Id</th>
|
<tr>
|
||||||
<th>Id2</th>
|
<th>Id</th>
|
||||||
<th>Name</th>
|
<th>Id2</th>
|
||||||
<th>Symbol</th>
|
<th>Name</th>
|
||||||
<th>class</th>
|
<th>Symbol</th>
|
||||||
<th>script</th>
|
<th>class</th>
|
||||||
<th>OR</th>
|
<th>script</th>
|
||||||
<th>CR</th>
|
<th>OR</th>
|
||||||
<th>Stratvars</th>
|
<th>CR</th>
|
||||||
<th>add_data</th>
|
<th>Stratvars</th>
|
||||||
<th>note</th>
|
<th>add_data</th>
|
||||||
<th>history</th>
|
<th>note</th>
|
||||||
<th></th>
|
<th>history</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
<th></th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody></tbody>
|
</thead>
|
||||||
</table>
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div id="recordModal" class="modal fade">
|
<div id="recordModal" class="modal fade">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
@ -292,7 +357,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="bottomContainer" class="flex-items" style="height: 800px">
|
||||||
|
<BR>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
||||||
<script src="/static/js/mywebsocket.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}}
|
//it is called after population
|
||||||
const chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
function populate_real_time_chart() {
|
||||||
chart.applyOptions({ timeScale: { visible: true, timeVisible: true, secondsVisible: true }, crosshair: {
|
if (chart !== null) {
|
||||||
mode: LightweightCharts.CrosshairMode.Normal, labelVisible: true
|
chart.remove()
|
||||||
}})
|
}
|
||||||
const candlestickSeries = chart.addCandlestickSeries({ lastValueVisible: true, priceLineWidth:2, priceLineColor: "red", priceFormat: { type: 'price', precision: 2, minMove: 0.01 }});
|
|
||||||
candlestickSeries.priceScale().applyOptions({
|
//const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
|
||||||
scaleMargins: {
|
const chartOptions = { width: 1045, height: 600, leftPriceScale: {visible: true}}
|
||||||
top: 0.1, // highest point of the series will be 10% away from the top
|
chart = LightweightCharts.createChart(document.getElementById('chart'), chartOptions);
|
||||||
bottom: 0.4, // lowest point will be 40% away from the bottom
|
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: ''});
|
const volumeSeries = chart.addHistogramSeries({title: "Volume", color: '#26a69a', priceFormat: {type: 'volume'}, priceScaleId: ''});
|
||||||
volumeSeries.priceScale().applyOptions({
|
volumeSeries.priceScale().applyOptions({
|
||||||
// set the positioning of the volume series
|
// set the positioning of the volume series
|
||||||
scaleMargins: {
|
scaleMargins: {
|
||||||
top: 0.7, // highest point of the series will be 70% away from the top
|
top: 0.7, // highest point of the series will be 70% away from the top
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const vwapSeries = chart.addLineSeries({
|
const vwapSeries = chart.addLineSeries({
|
||||||
// title: "vwap",
|
// title: "vwap",
|
||||||
color: '#2962FF',
|
color: '#2962FF',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lastValueVisible: false
|
lastValueVisible: false
|
||||||
})
|
})
|
||||||
|
|
||||||
//chart.timeScale().fitContent();
|
//chart.timeScale().fitContent();
|
||||||
|
|
||||||
//TBD dynamicky zobrazovat vsechny indikatory
|
//TBD dynamicky zobrazovat vsechny indikatory
|
||||||
//document.getElementById('chart').style.display = 'inline-block';
|
//document.getElementById('chart').style.display = 'inline-block';
|
||||||
var legendlist = document.getElementById('legend');
|
var legendlist = document.getElementById('legend');
|
||||||
var firstRow = document.createElement('div');
|
var firstRow = document.createElement('div');
|
||||||
firstRow.innerText = '-';
|
firstRow.innerText = '-';
|
||||||
// firstRow.style.color = 'white';
|
// firstRow.style.color = 'white';
|
||||||
legendlist.appendChild(firstRow);
|
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) {
|
function pad(n) {
|
||||||
var s = ('0' + n);
|
var s = ('0' + n);
|
||||||
return s.substr(s.length - 2);
|
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")
|
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
|
// Iterate through each element in the
|
||||||
// first array and if some of them
|
// first array and if some of them
|
||||||
@ -92,9 +111,448 @@ function is_running(id) {
|
|||||||
// //console.log(obj);
|
// //console.log(obj);
|
||||||
// //alert(JSON.stringify(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 () {
|
$(document).ready(function () {
|
||||||
//reaload hlavni tabulky
|
//reaload hlavni tabulky
|
||||||
|
|
||||||
@ -239,6 +697,7 @@ $(document).ready(function () {
|
|||||||
$('#button_refresh').click(function () {
|
$('#button_refresh').click(function () {
|
||||||
runnerRecords.ajax.reload();
|
runnerRecords.ajax.reload();
|
||||||
stratinRecords.ajax.reload();
|
stratinRecords.ajax.reload();
|
||||||
|
archiveRecords.ajax.reload();
|
||||||
})
|
})
|
||||||
|
|
||||||
//button copy
|
//button copy
|
||||||
@ -258,7 +717,7 @@ $(document).ready(function () {
|
|||||||
rec.add_data_conf = row.add_data_conf;
|
rec.add_data_conf = row.add_data_conf;
|
||||||
rec.note = row.note;
|
rec.note = row.note;
|
||||||
rec.history = "";
|
rec.history = "";
|
||||||
jsonString = JSON.stringify(rec);
|
jsonString = JSON.stringify(rec, null, 2);
|
||||||
navigator.clipboard.writeText(jsonString);
|
navigator.clipboard.writeText(jsonString);
|
||||||
$('#button_copy').attr('disabled', false);
|
$('#button_copy').attr('disabled', false);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -16,6 +16,7 @@ function connect(event) {
|
|||||||
console.log("nejaky error" + err)
|
console.log("nejaky error" + err)
|
||||||
}
|
}
|
||||||
ws.onopen = function(event) {
|
ws.onopen = function(event) {
|
||||||
|
populate_real_time_chart()
|
||||||
document.getElementById("status").textContent = "Connected to" + runnerId.value
|
document.getElementById("status").textContent = "Connected to" + runnerId.value
|
||||||
document.getElementById("bt-disc").style.display = "initial"
|
document.getElementById("bt-disc").style.display = "initial"
|
||||||
document.getElementById("bt-conn").style.display = "none"
|
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 v2realbot.interfaces.live_interface import LiveInterface
|
||||||
from alpaca.trading.enums import OrderSide
|
from alpaca.trading.enums import OrderSide
|
||||||
from v2realbot.backtesting.backtester import Backtester
|
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 alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from threading import Event, current_thread
|
from threading import Event, current_thread
|
||||||
import json
|
import json
|
||||||
|
|||||||
Binary file not shown.
@ -9,7 +9,7 @@ import decimal
|
|||||||
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
from v2realbot.common.model import StrategyInstance, Runner
|
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail
|
||||||
from typing import List
|
from typing import List
|
||||||
import tomli
|
import tomli
|
||||||
from v2realbot.config import DATA_DIR, QUIET_MODE
|
from v2realbot.config import DATA_DIR, QUIET_MODE
|
||||||
@ -66,6 +66,11 @@ def json_serial(obj):
|
|||||||
return obj.__dict__
|
return obj.__dict__
|
||||||
if type(obj) is btTradeUpdate:
|
if type(obj) is btTradeUpdate:
|
||||||
return obj.__dict__
|
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))
|
raise TypeError (str(obj)+"Type %s not serializable" % type(obj))
|
||||||
|
|
||||||
def parse_toml_string(tomlst: str):
|
def parse_toml_string(tomlst: str):
|
||||||
|
|||||||
Reference in New Issue
Block a user