optimalizace BT runu - offline loader skrz queue

This commit is contained in:
David Brazda
2023-09-20 16:23:56 +02:00
parent d0d2e91468
commit a0cbeb2bc6
12 changed files with 200 additions and 72 deletions

View File

@ -6,15 +6,15 @@ from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Orde
from v2realbot.indicators.indicators import ema, natr, roc from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.indicators.oscillators import rsi from v2realbot.indicators.oscillators import rsi
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, round2five, is_open_rush, is_close_rush, is_still, is_window_open, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, round2five, is_open_rush, is_close_rush, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff
from v2realbot.utils.directive_utils import get_conditions_from_configuration from v2realbot.utils.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory from v2realbot.common.model import SLHistory
from datetime import datetime, timedelta from datetime import datetime, timedelta
from v2realbot.config import KW from v2realbot.config import KW
from uuid import uuid4 from uuid import uuid4
import random #import random
import json import json
from numpy import inf import numpy as np
#from icecream import install, ic #from icecream import install, ic
#from rich import print #from rich import print
from threading import Event from threading import Event
@ -57,7 +57,6 @@ Hlavní loop:
- if not exit - eval optimalizations - if not exit - eval optimalizations
""" """
def next(data, state: StrategyState): def next(data, state: StrategyState):
print(10*"*","NEXT START",10*"*") print(10*"*","NEXT START",10*"*")
# important vars state.avgp, state.positions, state.vars, data # important vars state.avgp, state.positions, state.vars, data
@ -808,10 +807,14 @@ def next(data, state: StrategyState):
#ten bude prumerem hodnot lookback_offset a to tak ze polovina offsetu z kazde strany #ten bude prumerem hodnot lookback_offset a to tak ze polovina offsetu z kazde strany
array_od = slope_lookback + int(lookback_offset/2) array_od = slope_lookback + int(lookback_offset/2)
array_do = slope_lookback - int(lookback_offset/2) array_do = slope_lookback - int(lookback_offset/2)
lookbackprice_array = state.bars.vwap[-array_od:-array_do]
#dame na porovnani jen prumer #lookbackprice_array = state.bars.vwap[-array_od:-array_do]
lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3) #lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3)
#jako optimalizace pouzijeme NUMPY
lookbackprice = np.mean(state.bars.vwap[-array_od:-array_do])
# Round the lookback price to 3 decimal places
lookbackprice = round(lookbackprice, 3)
#lookbackprice = round((min(lookbackprice_array)+max(lookbackprice_array))/2,3) #lookbackprice = round((min(lookbackprice_array)+max(lookbackprice_array))/2,3)
# else: # else:
# #puvodni lookback a od te doby dozadu offset # #puvodni lookback a od te doby dozadu offset
@ -836,10 +839,13 @@ def next(data, state: StrategyState):
cnt = len(state.bars.close) cnt = len(state.bars.close)
if cnt>5: if cnt>5:
sliced_to = int(cnt/5) sliced_to = int(cnt/5)
lookbackprice= Average(state.bars.vwap[:sliced_to])
lookbackprice = np.mean(state.bars.vwap[:sliced_to])
#lookbackprice= Average(state.bars.vwap[:sliced_to])
lookbacktime = state.bars.time[int(sliced_to/2)] lookbacktime = state.bars.time[int(sliced_to/2)]
else: else:
lookbackprice = Average(state.bars.vwap) lookbackprice = np.mean(state.bars.vwap)
#lookbackprice = Average(state.bars.vwap)
lookbacktime = state.bars.time[0] lookbacktime = state.bars.time[0]
state.ilog(lvl=1,e=f"IND {name} slope - not enough data bereme left bod open", slope_lookback=slope_lookback, lookbackprice=lookbackprice) state.ilog(lvl=1,e=f"IND {name} slope - not enough data bereme left bod open", slope_lookback=slope_lookback, lookbackprice=lookbackprice)
@ -882,7 +888,7 @@ def next(data, state: StrategyState):
if len(state.vars.last_50_deltas) >=50: if len(state.vars.last_50_deltas) >=50:
state.vars.last_50_deltas.pop(0) state.vars.last_50_deltas.pop(0)
state.vars.last_50_deltas.append(last_update_delta) state.vars.last_50_deltas.append(last_update_delta)
avg_delta = Average(state.vars.last_50_deltas) avg_delta = np.mean(state.vars.last_50_deltas)
state.ilog(lvl=1,e=f"-----{data['index']}-{conf_bar}--delta:{last_update_delta}---AVGdelta:{avg_delta}", data=data) state.ilog(lvl=1,e=f"-----{data['index']}-{conf_bar}--delta:{last_update_delta}---AVGdelta:{avg_delta}", data=data)
@ -1740,6 +1746,11 @@ def next(data, state: StrategyState):
state.ilog(lvl=1,e=f"SIGNAL {signalname} - WINDOW CLOSED", msg=f"{window_open=} {window_close=} ") state.ilog(lvl=1,e=f"SIGNAL {signalname} - WINDOW CLOSED", msg=f"{window_open=} {window_close=} ")
return False return False
min_bar_index = safe_get(options, "min_bar_index",safe_get(state.vars, "min_bar_index",0))
if int(data["index"]) < int(min_bar_index):
state.ilog(lvl=1,e=f"MIN BAR INDEX {min_bar_index} waiting - TOO SOON", currindex=data["index"])
return False
next_signal_offset = safe_get(options, "next_signal_offset_from_last_exit",safe_get(state.vars, "next_signal_offset_from_last_exit",0)) next_signal_offset = safe_get(options, "next_signal_offset_from_last_exit",safe_get(state.vars, "next_signal_offset_from_last_exit",0))
if state.vars.last_exit_index is not None: if state.vars.last_exit_index is not None:

View File

@ -14,6 +14,7 @@ from msgpack import packb, unpackb
import asyncio import asyncio
import os import os
from traceback import format_exc from traceback import format_exc
#from codetiming import Timer
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
"""" """"
@ -90,6 +91,7 @@ minsize=100
exthours=false exthours=false
""" """
#@Timer(name="nextfce-timers")
def next(data, state: StrategyState): def next(data, state: StrategyState):
print(10*"*","NEXT START",10*"*") print(10*"*","NEXT START",10*"*")
#ic(state.avgp, state.positions) #ic(state.avgp, state.positions)

View File

@ -25,6 +25,7 @@ from traceback import format_exc
from datetime import timedelta, time from datetime import timedelta, time
from threading import Lock from threading import Lock
from v2realbot.common.db import pool from v2realbot.common.db import pool
#from pyinstrument import Profiler
#adding lock to ensure thread safety of TinyDB (in future will be migrated to proper db) #adding lock to ensure thread safety of TinyDB (in future will be migrated to proper db)
lock = Lock() lock = Lock()
@ -284,7 +285,11 @@ def capsule(target: object, db: object, inter_batch_params: dict = None):
#TODO zde odchytit pripadnou exceptionu a zapsat do history #TODO zde odchytit pripadnou exceptionu a zapsat do history
#cil aby padnuti jedne nezpusobilo pad enginu #cil aby padnuti jedne nezpusobilo pad enginu
try: try:
# profiler = Profiler()
# profiler.start()
target.start() target.start()
print("Strategy instance stopped. Update runners") print("Strategy instance stopped. Update runners")
reason = "SHUTDOWN OK" reason = "SHUTDOWN OK"
except Exception as e: except Exception as e:
@ -294,6 +299,12 @@ def capsule(target: object, db: object, inter_batch_params: dict = None):
print(reason) print(reason)
send_to_telegram(reason) send_to_telegram(reason)
finally: finally:
# profiler.stop()
# now = datetime.now()
# results_file = "profiler"+now.strftime("%Y-%m-%d_%H-%M-%S")+".html"
# with open(results_file, "w", encoding="utf-8") as f_html:
# f_html.write(profiler.output_html())
# remove runners after thread is stopped and save results to stratin history # remove runners after thread is stopped and save results to stratin history
for i in db.runners: for i in db.runners:
if i.run_instance == target: if i.run_instance == target:
@ -407,7 +418,7 @@ def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_b
if runReq.bt_to is None: if runReq.bt_to is None:
runReq.bt_to = datetime.now().astimezone(zoneNY) runReq.bt_to = datetime.now().astimezone(zoneNY)
print("hodnota ID pred",id) #print("hodnota ID pred",id)
#volani funkce instantiate_strategy #volani funkce instantiate_strategy
for i in db.stratins: for i in db.stratins:
if str(i.id) == str(id): if str(i.id) == str(id):
@ -493,9 +504,9 @@ def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_b
run_instance = instance, run_instance = instance,
run_stratvars_toml=i.stratvars_conf) run_stratvars_toml=i.stratvars_conf)
db.runners.append(runner) db.runners.append(runner)
print(db.runners) #print(db.runners)
print(i) #print(i)
print(enumerate()) #print(enumerate())
#pokud spoustime v batch módu, tak čekáme na výsledek a pak pouštíme další run #pokud spoustime v batch módu, tak čekáme na výsledek a pak pouštíme další run
if synchronous: if synchronous:
@ -584,38 +595,40 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
max_profit_time = None max_profit_time = None
long_cnt = 0 long_cnt = 0
short_cnt = 0 short_cnt = 0
for trade in strat.state.vars.prescribedTrades:
if trade.profit_sum > max_profit: if "prescribedTrades" in strat.state.vars:
max_profit = trade.profit_sum for trade in strat.state.vars.prescribedTrades:
max_profit_time = trade.last_update if trade.profit_sum > max_profit:
if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.LONG: max_profit = trade.profit_sum
long_cnt += 1 max_profit_time = trade.last_update
if trade.profit is not None: if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.LONG:
long_profit += trade.profit long_cnt += 1
if trade.profit < 0: if trade.profit is not None:
long_losses += trade.profit long_profit += trade.profit
if trade.profit > 0: if trade.profit < 0:
long_wins += trade.profit long_losses += trade.profit
if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.SHORT: if trade.profit > 0:
short_cnt +=1 long_wins += trade.profit
if trade.profit is not None: if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.SHORT:
short_profit += trade.profit short_cnt +=1
if trade.profit < 0: if trade.profit is not None:
short_losses += trade.profit short_profit += trade.profit
if trade.profit > 0: if trade.profit < 0:
short_wins += trade.profit short_losses += trade.profit
res["profit"]["long_cnt"] = long_cnt if trade.profit > 0:
res["profit"]["short_cnt"] = short_cnt short_wins += trade.profit
res["profit"]["long_profit"] = round(long_profit,2) res["profit"]["long_cnt"] = long_cnt
res["profit"]["short_profit"] = round(short_profit,2) res["profit"]["short_cnt"] = short_cnt
res["profit"]["long_losses"] = round(long_losses,2) res["profit"]["long_profit"] = round(long_profit,2)
res["profit"]["short_losses"] = round(short_losses,2) res["profit"]["short_profit"] = round(short_profit,2)
res["profit"]["long_wins"] = round(long_wins,2) res["profit"]["long_losses"] = round(long_losses,2)
res["profit"]["short_wins"] = round(short_wins,2) res["profit"]["short_losses"] = round(short_losses,2)
res["profit"]["max_profit"] = round(max_profit,2) res["profit"]["long_wins"] = round(long_wins,2)
res["profit"]["max_profit_time"] = str(max_profit_time) res["profit"]["short_wins"] = round(short_wins,2)
#vlozeni celeho listu res["profit"]["max_profit"] = round(max_profit,2)
res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial)) res["profit"]["max_profit_time"] = str(max_profit_time)
#vlozeni celeho listu
res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial))
except NameError: except NameError:
pass pass

View File

@ -107,9 +107,9 @@ class TradeAggregator:
except KeyError: except KeyError:
ltp.price[symbol]=data['p'] ltp.price[symbol]=data['p']
#DOCASNE VYPNUTO - VYMYSLET JINAK
if float(data['p']) > float(ltp.price[symbol]) + (float(data['p'])/100*pct_off) or float(data['p']) < float(ltp.price[symbol])-(float(data['p'])/100*pct_off): #if float(data['p']) > float(ltp.price[symbol]) + (float(data['p'])/100*pct_off) or float(data['p']) < float(ltp.price[symbol])-(float(data['p'])/100*pct_off):
print("ZLO", data,ltp.price[symbol]) #print("ZLO", data,ltp.price[symbol])
#nechavame zlo zatim projit #nechavame zlo zatim projit
##return [] ##return []
# with open("cache/wrongtrades.txt", 'a') as fp: # with open("cache/wrongtrades.txt", 'a') as fp:

View File

@ -17,6 +17,8 @@ from msgpack import packb
from pandas import to_datetime from pandas import to_datetime
import pickle import pickle
import os import os
from rich import print
import queue
""" """
Trade offline data streamer, based on Alpaca historical data. Trade offline data streamer, based on Alpaca historical data.
@ -55,6 +57,7 @@ class Trade_Offline_Streamer(Thread):
# Override the run() function of Thread class # Override the run() function of Thread class
#odebrano async #odebrano async
def main(self): def main(self):
trade_queue = queue.Queue()
print(10*"*","Trade OFFLINE streamer STARTED", current_thread().name,10*"*") print(10*"*","Trade OFFLINE streamer STARTED", current_thread().name,10*"*")
if not self.streams: if not self.streams:
@ -91,7 +94,10 @@ class Trade_Offline_Streamer(Thread):
##PREPSAT jednoduse tak, aby podporovalo jen jeden symbol ##PREPSAT jednoduse tak, aby podporovalo jen jeden symbol
#agregator2list bude mit vstup list #agregator2list bude mit vstup list
#datetime.fromtimestamp(data['updated']).astimezone(zoneNY))
#REFACTOR STARTS HERE #REFACTOR STARTS HERE
#print(f"{self.time_from=} {self.time_to=}")
calendar_request = GetCalendarRequest(start=self.time_from,end=self.time_to) calendar_request = GetCalendarRequest(start=self.time_from,end=self.time_to)
cal_dates = self.clientTrading.get_calendar(calendar_request) cal_dates = self.clientTrading.get_calendar(calendar_request)
#ic(cal_dates) #ic(cal_dates)
@ -101,15 +107,20 @@ class Trade_Offline_Streamer(Thread):
#minimalni jednotka pro CACHE je 1 den - a to jen marketopen to marketclose (extended hours not supported yet) #minimalni jednotka pro CACHE je 1 den - a to jen marketopen to marketclose (extended hours not supported yet)
for day in cal_dates: for day in cal_dates:
print("Processing DAY", day.date) print("Processing DAY", day.date)
print(day.date) #print(day.date)
print(day.open) print(day.open)
print(day.close) print(day.close)
#make it offset aware #make it offset aware
day.open = day.open.replace(tzinfo=zoneNY) day.open = zoneNY.localize(day.open)
#day.open.replace(tzinfo=zoneNY)
#add 20 minutes of premarket #add 20 minutes of premarket
#day.open = day.open - timedelta(minutes=20) #day.open = day.open - timedelta(minutes=20)
day.close = day.close.replace(tzinfo=zoneNY) day.close = zoneNY.localize(day.close)
#day.close = day.close.replace(tzinfo=zoneNY)
#print(day.open)
#print(day.close)
#print("dayopentimestamp", day.open.timestamp())
#print("dayclosetimestamp", day.close.timestamp())
##pokud datum do je mensi day.open, tak tento den neresime ##pokud datum do je mensi day.open, tak tento den neresime
if self.time_to < day.open: if self.time_to < day.open:
print("time_to je pred zacatkem marketu. Vynechavame tento den.") print("time_to je pred zacatkem marketu. Vynechavame tento den.")
@ -184,7 +195,14 @@ class Trade_Offline_Streamer(Thread):
#poustime i 20 minut premarketu pro presnejsi populaci slopu v prvnich minutech #poustime i 20 minut premarketu pro presnejsi populaci slopu v prvnich minutech
# - timedelta(minutes=20) # - timedelta(minutes=20)
if self.time_from < to_datetime(t['t']) < self.time_to: #homogenizace timestampu s online streamem
#tmp = to_datetime(t['t'], utc=True).timestamp()
datum = to_datetime(t['t'], utc=True)
if self.time_from < datum < self.time_to:
#poustime dal, jinak ne #poustime dal, jinak ne
if wait_for_q: if wait_for_q:
#cekame na Q nebo na O (nekterym dnum chybelo Q) #cekame na Q nebo na O (nekterym dnum chybelo Q)
@ -194,7 +212,10 @@ class Trade_Offline_Streamer(Thread):
wait_for_q = False wait_for_q = False
#homogenizace timestampu s online streamem #homogenizace timestampu s online streamem
t['t'] = Timestamp.from_unix(to_datetime(t['t']).timestamp()) t['t'] = Timestamp.from_unix(datum.timestamp())
#print(f"{t['t']}")
#t['t'] = Timestamp.from_unix(to_datetime(t['t']).timestamp())
#print(to_datetime(t['t']).timestamp())
#print("PROGRESS ",cnt,"/",celkem) #print("PROGRESS ",cnt,"/",celkem)
#print(t) #print(t)
@ -204,23 +225,38 @@ class Trade_Offline_Streamer(Thread):
#print("zaznam",t) #print("zaznam",t)
#print("Ingest", s, "zaznam", t) #print("Ingest", s, "zaznam", t)
#await s.ingest_trade(packb(t)) #await s.ingest_trade(packb(t))
asyncio.run(s.ingest_trade(packb(t))) trade_queue.put((s,t))
##asyncio.run(s.ingest_trade(packb(t)))
cnt += 1 cnt += 1
#protoze jsou serazene, tak prvni ktery je vetsi muze prerusit #protoze jsou serazene, tak prvni ktery je vetsi muze prerusit
elif to_datetime(t['t']) > self.time_to: elif datum > self.time_to:
print("prerusujeme") #print(f"{datum=}")
#print(to_datetime(t['t']))
#print(f"{self.time_to=}")
#print("prerusujeme")
break break
#vsem streamum posleme last TODO: (tuto celou cast prepsat a zjednodusit) #vsem streamum posleme last TODO: (tuto celou cast prepsat a zjednodusit)
#po loadovani vsech dnu #po loadovani vsech dnu
print("naloadovane vse posilame last") print("naloadovane vse posilame last")
for s in self.to_run[symbpole[0]]: for s in self.to_run[symbpole[0]]:
#zde bylo await #zde bylo await
asyncio.run(s.ingest_trade(packb("last"))) trade_queue.put((s,"last"))
##asyncio.run(s.ingest_trade(packb("last")))
print("poslano last") print("poslano last")
#loop = asyncio.get_running_loop() async def process_trade_queue(trade_queue):
print("stoping loop") while not trade_queue.empty():
#loop.stop() #print("send trade")
s, trade = trade_queue.get()
await s.ingest_trade(packb(trade))
#spusteni asyncio run - tentokrat jednou, ktera spusti proces jez to z queue odesle
#nevyhoda reseni - kdyz je to pres vice dnu, tak se naloaduji vsechny dny do queue
#ale to mi u Classic zatim nevadi - poustim per days
#uvidim jak to ovlivn rychlost
asyncio.run(process_trade_queue(trade_queue))
print("skoncilo zpracovani ASYNCIO RUN TRADE QUEUE - zpracovany vsechny trady v agreagtorech")
print(10*"*","Trade OFFLINE streamer STOPPED", current_thread().name,10*"*") print(10*"*","Trade OFFLINE streamer STOPPED", current_thread().name,10*"*")

View File

@ -228,7 +228,7 @@ def _get_stratin(stratin_id) -> StrategyInstance:
@app.put("/stratins/{stratin_id}/run", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK) @app.put("/stratins/{stratin_id}/run", dependencies=[Depends(api_key_auth)], status_code=status.HTTP_200_OK)
def _run_stratin(stratin_id: UUID, runReq: RunRequest): def _run_stratin(stratin_id: UUID, runReq: RunRequest):
print(runReq) #print(runReq)
if runReq.test_batch_id is not None: if runReq.test_batch_id is not None:
res, id = cs.run_batch_stratin(id=stratin_id, runReq=runReq) res, id = cs.run_batch_stratin(id=stratin_id, runReq=runReq)
else: else:

View File

@ -24,7 +24,10 @@ from alpaca.trading.enums import TradeEvent, OrderStatus
from threading import Event, current_thread from threading import Event, current_thread
import json import json
from uuid import UUID from uuid import UUID
from rich import print as printnow
#from pyinstrument import Profiler
#profiler = Profiler()
# obecna Parent strategie podporující queues # obecna Parent strategie podporující queues
class Strategy: class Strategy:
def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: str = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None: def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: str = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None:
@ -324,7 +327,10 @@ class Strategy:
pass pass
#self.state.ilog(e="Rush hour - skipping") #self.state.ilog(e="Rush hour - skipping")
else: else:
# Profile the function
#profiler.start()
self.next(item, self.state) self.next(item, self.state)
#profiler.stop()
self.after_iteration(item) self.after_iteration(item)
##run strategy live ##run strategy live
@ -353,6 +359,7 @@ class Strategy:
try: try:
#block 5s, after that check signals #block 5s, after that check signals
item = self.q1.get(timeout=HEARTBEAT_TIMEOUT) item = self.q1.get(timeout=HEARTBEAT_TIMEOUT)
#printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize())
except queue.Empty: except queue.Empty:
#check signals #check signals
if self.se.is_set(): if self.se.is_set():
@ -386,6 +393,11 @@ class Strategy:
tlog(f"FINISHED") tlog(f"FINISHED")
print(40*"*",self.mode, "STRATEGY ", self.name,"STOPPING",40*"*") print(40*"*",self.mode, "STRATEGY ", self.name,"STOPPING",40*"*")
# now = datetime.now()
# results_file = "profiler"+now.strftime("%Y-%m-%d_%H-%M-%S")+".html"
# with open(results_file, "w", encoding="utf-8") as f_html:
# f_html.write(profiler.output_html())
self.stop() self.stop()
if self.mode == Mode.BT: if self.mode == Mode.BT:

View File

@ -3,7 +3,7 @@ import math
from queue import Queue from queue import Queue
from datetime import datetime, timezone, time, timedelta, date from datetime import datetime, timezone, time, timedelta, date
import pytz import pytz
from dateutil import tz #from dateutil import tz
from rich import print as richprint from rich import print as richprint
import decimal import decimal
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
@ -291,7 +291,8 @@ class Store:
qu = Queue() qu = Queue()
zoneNY = tz.gettz('America/New_York') #zoneNY = tz.gettz('America/New_York')
zoneNY = pytz.timezone('US/Eastern')
def print(*args, **kwargs): def print(*args, **kwargs):
if QUIET_MODE: if QUIET_MODE:
@ -300,13 +301,45 @@ def print(*args, **kwargs):
####ic(*args, **kwargs) ####ic(*args, **kwargs)
richprint(*args, **kwargs) richprint(*args, **kwargs)
#optimized by BARD
def price2dec(price: float, decimals: int = 2) -> float: def price2dec(price: float, decimals: int = 2) -> float:
"""Rounds a price to a specified number of decimal places, but only if the
price has more than that number of decimals.
Args:
price: The price to round.
decimals: The number of decimals to round to.
Returns:
A rounded price, or the original price if the price has less than or equal
to the specified number of decimals.
"""
if price.is_integer():
return price
# Calculate the number of decimal places in the price.
num_decimals = int(math.floor(math.log10(abs(price - math.floor(price)))))
# If the price has more than the specified number of decimals, round it.
if num_decimals > decimals:
return round(price, decimals)
else:
return price
def price2dec_old(price: float, decimals: int = 2) -> float:
""" """
pousti maximalne 2 decimals pousti maximalne 2 decimals
pokud je trojmistne a vic pak zakrouhli na 2, jinak necha pokud je trojmistne a vic pak zakrouhli na 2, jinak necha
""" """
return round(price,decimals) if count_decimals(price) > decimals else price return round(price,decimals) if count_decimals(price) > decimals else price
def count_decimals(number: float) -> int:
"""
Count the number of decimals in a given float: 1.4335 -> 4 or 3 -> 0
"""
return abs(decimal.Decimal(str(number)).as_tuple().exponent)
def round2five(price: float): def round2five(price: float):
""" """
zatim jen na 3 mista -pripadne predelat na dynamicky zatim jen na 3 mista -pripadne predelat na dynamicky
@ -315,12 +348,6 @@ def round2five(price: float):
""" """
return (round(price*100*2)/2)/100 return (round(price*100*2)/2)/100
def count_decimals(number: float) -> int:
"""
Count the number of decimals in a given float: 1.4335 -> 4 or 3 -> 0
"""
return abs(decimal.Decimal(str(number)).as_tuple().exponent)
def p(var, n = None): def p(var, n = None):
if n: print(n, f'{var = }') if n: print(n, f'{var = }')
else: print(f'{var = }') else: print(f'{var = }')
@ -337,8 +364,35 @@ def is_open_rush(dt: datetime, mins: int = 30):
rushtime = (datetime.combine(date.today(), business_hours["from"]) + timedelta(minutes=mins)).time() rushtime = (datetime.combine(date.today(), business_hours["from"]) + timedelta(minutes=mins)).time()
return business_hours["from"] <= dt.time() < rushtime return business_hours["from"] <= dt.time() < rushtime
#TODO market time pro dany den si dotahnout z Alpaca #optimalized by BARD
def is_window_open(dt: datetime, start: int = 0, end: int = 390): def is_window_open(dt: datetime, start: int = 0, end: int = 390):
""""
Returns true if time (start in minutes and end in minutes) is in working window
"""
# Check if start and end are within bounds early to avoid unnecessary computations.
if start < 0 or start > 389 or end < 0 or end > 389:
return False
# Convert the datetime object to the New York time zone.
dt = dt.astimezone(zoneNY)
# Get the business hours start and end times.
business_hours_start = time(hour=9, minute=30)
business_hours_end = time(hour=16, minute=0)
# Check if the datetime is within business hours.
if not business_hours_start <= dt.time() <= business_hours_end:
return False
# Calculate the start and end times of the working window.
working_window_start = (datetime.combine(date.today(), business_hours_start) + timedelta(minutes=start)).time()
working_window_end = (datetime.combine(date.today(), business_hours_start) + timedelta(minutes=end)).time()
# Check if the datetime is within the working window.
return working_window_start <= dt.time() <= working_window_end
#puvodni neoptimalizovana verze
#TODO market time pro dany den si dotahnout z Alpaca
def is_window_open_old(dt: datetime, start: int = 0, end: int = 390):
"""" """"
Returns true if time (start in minutes and end in minutes) is in working window Returns true if time (start in minutes and end in minutes) is in working window
""" """