From a0cbeb2bc69f62ef1fb77439768d34dbe0352fca Mon Sep 17 00:00:00 2001 From: David Brazda Date: Wed, 20 Sep 2023 16:23:56 +0200 Subject: [PATCH] optimalizace BT runu - offline loader skrz queue --- v2realbot/ENTRY_ClassicSL_v01.py | 31 ++++--- v2realbot/ENTRY_Vykladaci_RSI_MYSELL_type.py | 2 + v2realbot/controller/services.py | 85 ++++++++++-------- .../__pycache__/aggregator.cpython-310.pyc | Bin 7032 -> 6899 bytes .../trade_offline_streamer.cpython-310.pyc | Bin 4942 -> 5376 bytes v2realbot/loader/aggregator.py | 6 +- v2realbot/loader/trade_offline_streamer.py | 62 ++++++++++--- v2realbot/main.py | 2 +- .../strategy/__pycache__/base.cpython-310.pyc | Bin 14735 -> 14770 bytes v2realbot/strategy/base.py | 12 +++ .../utils/__pycache__/utils.cpython-310.pyc | Bin 14041 -> 15073 bytes v2realbot/utils/utils.py | 72 +++++++++++++-- 12 files changed, 200 insertions(+), 72 deletions(-) diff --git a/v2realbot/ENTRY_ClassicSL_v01.py b/v2realbot/ENTRY_ClassicSL_v01.py index 53361dc..3709915 100644 --- a/v2realbot/ENTRY_ClassicSL_v01.py +++ b/v2realbot/ENTRY_ClassicSL_v01.py @@ -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.oscillators import rsi 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.common.model import SLHistory from datetime import datetime, timedelta from v2realbot.config import KW from uuid import uuid4 -import random +#import random import json -from numpy import inf +import numpy as np #from icecream import install, ic #from rich import print from threading import Event @@ -57,7 +57,6 @@ Hlavní loop: - if not exit - eval optimalizations """ - def next(data, state: StrategyState): print(10*"*","NEXT START",10*"*") # 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 array_od = 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 = round(sum(lookbackprice_array)/lookback_offset,3) + #lookbackprice_array = state.bars.vwap[-array_od:-array_do] + #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) # else: # #puvodni lookback a od te doby dozadu offset @@ -836,10 +839,13 @@ def next(data, state: StrategyState): cnt = len(state.bars.close) if 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)] else: - lookbackprice = Average(state.bars.vwap) + lookbackprice = np.mean(state.bars.vwap) + #lookbackprice = Average(state.bars.vwap) 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) @@ -882,7 +888,7 @@ def next(data, state: StrategyState): if len(state.vars.last_50_deltas) >=50: state.vars.last_50_deltas.pop(0) 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) @@ -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=} ") 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)) if state.vars.last_exit_index is not None: diff --git a/v2realbot/ENTRY_Vykladaci_RSI_MYSELL_type.py b/v2realbot/ENTRY_Vykladaci_RSI_MYSELL_type.py index c06181d..4b42b51 100644 --- a/v2realbot/ENTRY_Vykladaci_RSI_MYSELL_type.py +++ b/v2realbot/ENTRY_Vykladaci_RSI_MYSELL_type.py @@ -14,6 +14,7 @@ from msgpack import packb, unpackb import asyncio import os from traceback import format_exc +#from codetiming import Timer print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) """" @@ -90,6 +91,7 @@ minsize=100 exthours=false """ +#@Timer(name="nextfce-timers") def next(data, state: StrategyState): print(10*"*","NEXT START",10*"*") #ic(state.avgp, state.positions) diff --git a/v2realbot/controller/services.py b/v2realbot/controller/services.py index af0e68d..ce8b087 100644 --- a/v2realbot/controller/services.py +++ b/v2realbot/controller/services.py @@ -25,6 +25,7 @@ from traceback import format_exc from datetime import timedelta, time from threading import Lock 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) 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 #cil aby padnuti jedne nezpusobilo pad enginu try: + + # profiler = Profiler() + # profiler.start() target.start() + print("Strategy instance stopped. Update runners") reason = "SHUTDOWN OK" except Exception as e: @@ -294,6 +299,12 @@ def capsule(target: object, db: object, inter_batch_params: dict = None): print(reason) send_to_telegram(reason) 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 for i in db.runners: 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: runReq.bt_to = datetime.now().astimezone(zoneNY) - print("hodnota ID pred",id) + #print("hodnota ID pred",id) #volani funkce instantiate_strategy for i in db.stratins: 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_stratvars_toml=i.stratvars_conf) db.runners.append(runner) - print(db.runners) - print(i) - print(enumerate()) + #print(db.runners) + #print(i) + #print(enumerate()) #pokud spoustime v batch módu, tak čekáme na výsledek a pak pouštíme další run if synchronous: @@ -584,38 +595,40 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param max_profit_time = None long_cnt = 0 short_cnt = 0 - for trade in strat.state.vars.prescribedTrades: - if trade.profit_sum > max_profit: - max_profit = trade.profit_sum - max_profit_time = trade.last_update - if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.LONG: - long_cnt += 1 - if trade.profit is not None: - long_profit += trade.profit - if trade.profit < 0: - long_losses += trade.profit - if trade.profit > 0: - long_wins += trade.profit - if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.SHORT: - short_cnt +=1 - if trade.profit is not None: - short_profit += trade.profit - if trade.profit < 0: - short_losses += trade.profit - if trade.profit > 0: - short_wins += trade.profit - res["profit"]["long_cnt"] = long_cnt - res["profit"]["short_cnt"] = short_cnt - res["profit"]["long_profit"] = round(long_profit,2) - res["profit"]["short_profit"] = round(short_profit,2) - res["profit"]["long_losses"] = round(long_losses,2) - res["profit"]["short_losses"] = round(short_losses,2) - res["profit"]["long_wins"] = round(long_wins,2) - res["profit"]["short_wins"] = round(short_wins,2) - res["profit"]["max_profit"] = round(max_profit,2) - 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)) + + if "prescribedTrades" in strat.state.vars: + for trade in strat.state.vars.prescribedTrades: + if trade.profit_sum > max_profit: + max_profit = trade.profit_sum + max_profit_time = trade.last_update + if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.LONG: + long_cnt += 1 + if trade.profit is not None: + long_profit += trade.profit + if trade.profit < 0: + long_losses += trade.profit + if trade.profit > 0: + long_wins += trade.profit + if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.SHORT: + short_cnt +=1 + if trade.profit is not None: + short_profit += trade.profit + if trade.profit < 0: + short_losses += trade.profit + if trade.profit > 0: + short_wins += trade.profit + res["profit"]["long_cnt"] = long_cnt + res["profit"]["short_cnt"] = short_cnt + res["profit"]["long_profit"] = round(long_profit,2) + res["profit"]["short_profit"] = round(short_profit,2) + res["profit"]["long_losses"] = round(long_losses,2) + res["profit"]["short_losses"] = round(short_losses,2) + res["profit"]["long_wins"] = round(long_wins,2) + res["profit"]["short_wins"] = round(short_wins,2) + res["profit"]["max_profit"] = round(max_profit,2) + 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: pass diff --git a/v2realbot/loader/__pycache__/aggregator.cpython-310.pyc b/v2realbot/loader/__pycache__/aggregator.cpython-310.pyc index a5706fac4b7914d626231874863a70b7b5766844..395ed484e0bef6b38bf08729f7b21b67a2c7dab9 100644 GIT binary patch delta 1137 zcmZ8gJ8aWX6ur+*<2YYC&d1KD;n$Xy(hXQLBC)V_1QDS=8sVT+3TAU;q=>OnC09%x znhhZ#gqWZUBVvIWR(1vk7&-UZ3V~RT?>+Z_-+8~U|GZW@D3$Vr->>KS2Rm;{FHP-3 zCVpqWigF&!M5xPLp8_Muoti9rG#4}RcodSKm*x|ijE1yJ&L?>b7TpL z$*(E7>`|LFM^+Td1?uqr0<58$`xrFY&Lsveq$*xlT78lQC2=-26nil~HWqrM0GUUq z$7Xz695z7l4Eb$Yyg?cb+OmMhR{t#Ffp=S0PdE}$eg`SBmyvcPCb~3%HpaTj+ej=x zIzcxnJQI-6=uAW|1*;=MP**wk5X8YfRlb&n>N4X|jZ`a);efbVo+b5Q&A}0#Q+N$_ z)y_;ybXd+Sss5iOs;i|s5^F{X$~2SI_(*vHv28K*{Q1nZ&UZ@|ND5TwhpeW3RpC5g4$QGGHsXg22{HpuNnI zHd!6gG^a3kfoWmxbQ@moTnKuIJ%Wx2(qd%DNDKBQV)Oh7vlB;$9mAZuY$?-G>>j+X zqOcD67Gy8|IOKTHR6h3z*$>jZhOrA~ET5GFd0t))_N!sq=Dv8sPa zy^U#?!~VzEJI|=nNH`HEWQAQVkX>CWR7UIMhKp38erWx%6WPcA&o-$~_7lgDF;_o` zsta6rU?2%ud6O9hl=DR>}v^o|;!!?{)*RamQ>?f^rz$-D&jj9{Q_lMV8vtPpJB68@E zy-g@}2>e@;ttw$KY;JFf3LPDqDs#x!RqYTq`ozR!wbjkh>=zu7uPXcSJ2(AkBi^2T zZuENgYH!7VmVO<#3;4fU)|VG=`U{y);*$R>Gn4!ozW;D-#jz8x`N{02?`OY>ul~K< XY~)TTFS14dY0lKsvE=TX++yr6l0zj` diff --git a/v2realbot/loader/__pycache__/trade_offline_streamer.cpython-310.pyc b/v2realbot/loader/__pycache__/trade_offline_streamer.cpython-310.pyc index 13eafa6956aaf9440ce35de2e7b52f3bf02d1d00..e7ce8c24c4a3e13690d6d345658ca09ab201c88d 100644 GIT binary patch delta 1905 zcmZuyO>7fK6rP#gwb%A~{U77}Zuob8ETvG23PD07Z9yU-q!A)2xRz%Lar~3)PN?F} z$`o@&O}kPr2)aIS;RakW^W9%j6HR}n59894$uORxh^Fh4jgi$6j6m1xQ>#-E^e&YlkSnw9b9rBhi=ABK!o)Mi1v{C zYb@^`52rdJD+22rEc>~H5JMjBAqO);F(pjuqMA%`SH_5=nqm)tUFu@sB`*h-Idm=J z5b%Y(Fef#&2fnQWRcPq0@Q3D57jsmO(C}Uhiy}Mn!RTbn|(VO6D@f2YqpvY-4#t}gYN=Kax$KE8*@YIc9 zj3LY8Njmrza*_gp3o3di==EzjKq8E=F2N8HY6P7MxL}+DNc|Ng9q&Uz+MwG z^x$3OWZ9HA&9fYaO}rrxaVVH5@iHIY`!Z7mm&cI>cP91}Uv!_xb|)6$2Czp>#wPsyM8+9LPza@*xV7&Ukg#rVK z&`+@Jw?TXYmw|_R$R2`UxM)5g4u-cLdvMioc)mS+9;Wy`nAd)sr3j{CS>upilzqi) zY_c6+S~HoitvA<9o0XYWp{6w`0+u8{7HrE~+vc|EW7}8y?g?;PftUo%&um()s%hK( z4HB>e08)n0os!e9i35!B2Y`bACh`>ui{QJDDV_*~GVSfQa#cnJ) zoZS~q5)DT~ut*f^M3=R&dn$Rdl++CpA_kGAw4_JUvLk|$!Os2dXEiu*@$4iCC#QU)$PE2pkw+;G25&kR(B_d^iAY^_&sT1#R- g?2f1MNnbtPXwmJuc}9$&U2?}WMODKpCL}BU4M7%Q6rMM`UjMDv>x~oJiJPA`gxX0fg#e|rLP7*ps!&_hL)55AlzO%$PP$HZ zr&L(8a)rw=!e~Go0BJ1*2jGHoEZ3fpKs|vhae&^qBE$*An+;7Zb~Uqa-h1)ezzjijxlQS^3B)%bUhJEXaq(TkyO*oqVq2U{Eo=h9me{BZLzM zjB@CbCS}mofe6yn0tfwC7l1JRYV27iYv~X$rbh_@O{0&oy-}86#(fpnmq{0+8%dTN z_VCn3nyYwHVF}V{Y6z5#fszWQLZXa-80_pV64FDcwPdIXiV-c2T;y5wn>cbTwNFDG zEi1|ky$@kRB$i1J)~<&JN=^%-X?H1LS+Rwru~ks?XmyOH6Du*Ah2{t^vdlgRW21n{ z&d}OXROl;g6aOQG&8j_nH~6TaOs`<2%q$T;%EwerRO+Lr3nen-b%wmT5($$?jy>q0 ziZZiGxIkx$SDNF|gh=(lf6NOa#qwSHz7wW-H998JSX~CmZIMzHtq?|Zyt^6@FSSU5 zU+$tPbzj5bUFMUA;*=y+4lC?yq3YXIG%d3KVRf7*VFjaXJ`)}9ljsC5_CY5a*~Vu2 zc?A$#V;)j_36wFSdf-)KnMIUo#WH6_28I4#UV0Hxf>Xl+QHUvw4z^TBg7a)NG(F13 zLKC4&!u*7OM&lQg!Siu9%<*RsIHy`2T9{E5mg)hkg!w~1X59#F1n^F z0a`jS4Bp=IZnorO^RKZ1yg7iz04}d) zcYW8t6WnRKw_W*rZpmt!0dMW9Z9A>jt`zwhI3>TyzmiQTrb#tT$v^W;)*$J-yE~f= z*Ds<|p0Rtg$<5#$ud&tgUqH{mL=ZRN8{FSyu0M{hn&a2xZ}z({D%Gn-!`<0-Zx6JF z$NksU4hZ}@J`bmPWi8=!7Dv#-Nq$wR7VWeN83RXr+tk)fO%4h#)la4kI!$$LT(gX_ zZbFf!KnMLF{T}@(b=twT2`O!iwkPDHL`kkVFK4eS3PVko68|=e*r0zWd#5L diff --git a/v2realbot/loader/aggregator.py b/v2realbot/loader/aggregator.py index 19e91aa..3446294 100644 --- a/v2realbot/loader/aggregator.py +++ b/v2realbot/loader/aggregator.py @@ -107,9 +107,9 @@ class TradeAggregator: except KeyError: ltp.price[symbol]=data['p'] - - 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]) + #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): + #print("ZLO", data,ltp.price[symbol]) #nechavame zlo zatim projit ##return [] # with open("cache/wrongtrades.txt", 'a') as fp: diff --git a/v2realbot/loader/trade_offline_streamer.py b/v2realbot/loader/trade_offline_streamer.py index 9bf8508..8a9a0e3 100644 --- a/v2realbot/loader/trade_offline_streamer.py +++ b/v2realbot/loader/trade_offline_streamer.py @@ -17,6 +17,8 @@ from msgpack import packb from pandas import to_datetime import pickle import os +from rich import print +import queue """ 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 #odebrano async def main(self): + trade_queue = queue.Queue() print(10*"*","Trade OFFLINE streamer STARTED", current_thread().name,10*"*") if not self.streams: @@ -91,7 +94,10 @@ class Trade_Offline_Streamer(Thread): ##PREPSAT jednoduse tak, aby podporovalo jen jeden symbol #agregator2list bude mit vstup list + #datetime.fromtimestamp(data['updated']).astimezone(zoneNY)) #REFACTOR STARTS HERE + #print(f"{self.time_from=} {self.time_to=}") + calendar_request = GetCalendarRequest(start=self.time_from,end=self.time_to) cal_dates = self.clientTrading.get_calendar(calendar_request) #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) for day in cal_dates: print("Processing DAY", day.date) - print(day.date) + #print(day.date) print(day.open) print(day.close) #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 #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 if self.time_to < day.open: 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 # - 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 if wait_for_q: #cekame na Q nebo na O (nekterym dnum chybelo Q) @@ -194,7 +212,10 @@ class Trade_Offline_Streamer(Thread): wait_for_q = False #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(t) @@ -204,23 +225,38 @@ class Trade_Offline_Streamer(Thread): #print("zaznam",t) #print("Ingest", s, "zaznam", 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 #protoze jsou serazene, tak prvni ktery je vetsi muze prerusit - elif to_datetime(t['t']) > self.time_to: - print("prerusujeme") + elif datum > self.time_to: + #print(f"{datum=}") + #print(to_datetime(t['t'])) + #print(f"{self.time_to=}") + #print("prerusujeme") break #vsem streamum posleme last TODO: (tuto celou cast prepsat a zjednodusit) #po loadovani vsech dnu print("naloadovane vse posilame last") for s in self.to_run[symbpole[0]]: #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") - #loop = asyncio.get_running_loop() - print("stoping loop") - #loop.stop() + async def process_trade_queue(trade_queue): + while not trade_queue.empty(): + #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*"*") diff --git a/v2realbot/main.py b/v2realbot/main.py index 6b67a55..fa8108e 100644 --- a/v2realbot/main.py +++ b/v2realbot/main.py @@ -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) def _run_stratin(stratin_id: UUID, runReq: RunRequest): - print(runReq) + #print(runReq) if runReq.test_batch_id is not None: res, id = cs.run_batch_stratin(id=stratin_id, runReq=runReq) else: diff --git a/v2realbot/strategy/__pycache__/base.cpython-310.pyc b/v2realbot/strategy/__pycache__/base.cpython-310.pyc index 74636b501b00da747005d9c0c57e99ad4afeb73e..563966a8c64592068ece2da27d19cadc821425f8 100644 GIT binary patch delta 3615 zcmb7GYiwLs5#F=A_O4$WJ8|s9k2rB;$BW}6b(+{t{f?c~iJOPh#;ucfbG`T4-gsY` zd+j_#Yb9ybRwxB}Dg=}kfrMJ41k^4PDDb1d;0Gj7CPk~2CP?k%A%mMr_|guWS$t8KcnB&GWFb0yy&lPHd0 zQ6Jz5z$D;Fz*B%Jz-fkPiDxtffmqgp8V4NGi>0k)$3cqAK7F&aVTY`Bi@o^i1hfJt znR3I1X~s;$F^=hvOCMGTb4_J8)zSf0T?tK|>KGrH=-|Y)GavPPP7gy@!n#wBPdvt2 zKSAP|gyhGwlQa%OC5Q6T!z{ZSz)!K5O>M}KX;Ew@kxa(oMlv~V#OM7qpvNm3hU5^# zFb@9@$1sxoMg!jN*n;}>a>ces4uV9%ywV|BH#k0adekjCF?3>df*PR15$PY_+@lN@C28A!5U55oa{HnOQq_TK{jX}uKL*dv`*Oi~JIs&mj8fH**6T3ZS_#)^3E zPNdPl)XStBTF7!?ERz&gOhZ%>&!lG(lwx5I^U(?~x8=|UhKD93F1Iw1HWP8f$&gKF zpei-8Su1VErVVm~3^pYp4j#mkevs?pERNk@rxR7Rorv8HCLAl}6?kp5Zk25;S}~|& za|xUGy8KG+-KuKUjzB35kPuhe6(LdvFv#ErvKgC(^e5{#95+Ez*rZuoR0B}<&>hI}f#ec;#2$GW6pJ28h0(~3EXlqIM4?s^19 zGx}oW5CIze5U$s0!mL_lhI_?DE{-t0$G}(T8ff%cQiUSTdqtu4&l# zJV@yQZ!@sN`gToor8JT3coD*9_1|g+Ps_+D29LQTv&mg&%s6~UIN4<2>}%2$h^L*Y z0Q#h$LZy7;4JVe&WU{sTTJ7FIQN*A7QSDFtYEduM#k!^0F(|$0B0Gj~# z0apRTx~6{XaP&WW2Fb;C} zWN8NcpuSOGxA{8AwTGc|EIX;+tKY8t`lI@m(4WHFYg9O-D;s{YF`wL3LF*42HXl6% zPjbZ1uJ=?0StSt^j$!K@mnnWKrd~*a2$60LD>VNf~xx=lhCe#l2YRk{Bf1g3w*xG zs^9B@ZIdnHQxODo6Hs9J63hOmf4pr%E$c{g?E|a*1-AK6_ctHjE+-KW5AyXY+x<27 zcJmE?Gn&dRmP>9^mKkqsra@s4(X^gyX{?hvTmgTX!42EV*rJi-&-WGmO3P&QQE0{h zGVt?L3)936B(1cUiL8#a)+$H0w)T{*a;eX^b}YRC(+XTxzI(H=Bu_{BDrCX)b|&o> znG30`O(AT87Xg(4@Og}e@>6iCn4DNh( zDQmMSL?velS^|@N~*(nHW<@Miz*=t0A2vJ0Cq6&M=z5}(ggT? zNh_c!nmGQ%u@QKp*0K(4k)xSfQ1N4QU3zoWMa%}eM^y2ap@ z+YS%yc*=6-GN#vCNH_fsfYYtYWB%AtR&3Q>9mfL)!jI{fI~r9~zt!<2bw1a<>#%=U zJJ#(4JOn`VlO%W$RQ|Ct47MwGYtLyVj|$I7svrfBQb+;ruq8Jj>QBE0pabYPfDZuZ z0{Sfjzu!>L=oY#S(I8;eg_=%g;`7rBmsdU5B%Iz(7%lD&7b@SbW AkpKVy delta 3598 zcmb7G>u(%a75Cj;@57I+T|2u@Y{!WsJD#|XQ>S$t?8a-yu4Cn4<6H#2wwt7Nq|zyhp7ZbK>q~77ar$#$MM>$ zAcQUb_S|#MJ#+4P-TNc^Wjkus)>c*UU+WEF<}X}|wg}_h?g`O8QDMZ4_zM+A{CVHp zXL!|QY~a05gQte;a`d(E_yBqY}rzuDkOf%(%b;C#+x}zVI z|EzjMj28Q<-xkYzS#&-$d$i}!*h~+{4c==$&*6y&Do?7zJKAEg-8BvEAfpYkN7={w{L(}hl z9iN~f`F`!zLBv?)%gY?RbpwYcpB#59r$?s8XQ%}VI3gVfv;owohhGOalKn;Uid2D86Y}_lnAgR=-8cRas1YjPZd|aOzI?RF-#ZRKG zzU4tC-B2OVi82im*r9_JNm;ptG-a7Nz-z76i?#Z)e{^I<`RP`rb4EI)I~LhAhiz4Q zK5yoXA8&^5ZXoTLSuf#hyp`AMW8!PYoAr%i z8Gca~pgdZsR(M1?04l=u=PldwEuf!S%)1d@r=7Hwvx!))1-wWVpeuA&Tq|MS!NY#Km4eCWe1j+&1vGQIbhUL}f zO&h-q(fY#>96KmKY~Ch>yxZIn`Xt;Q6Jej+*7E*xDU|D-o@+4DHg`(SOgXDFhQ?uC z9ecI)=W5B!^4+j9>S+RQ`SS&bha3X!igdX@uQU5qZb-YXqWE;{m>7n?TFaWWmV7D2 z?n|sh?SKP-Qh_DppEo})=44mfSeHueHz0ow@Ft)v8mE|dUB1?4bzKwcEZ00oNo^}r z{w^!ul-iczuJ6OtTYzhT9{|dfzsI~^$yc_Vo`N1uiUzDF_UD8&IZsk$E4;+goSETe7V^e&0&?D(ieCr`jjBsgo!R_p^10^*%2C zwEZPtI||MXa!=#tw|L==($#52x;)v@x=A(OIf&0x>Iq?&;iHy~d=x(Al$m1*)|?Oo{t)F_}qokA5Azz$x3x zxs^sCo3|+htGLyu8h|TQ3zbIWT#%fkRdCcib}Ky}--YI7Kv_)RVcs86seD5fw{QQk z5QFkhUCoU+y<5qn-aJbgra5_YGOBHEEZd)FS)WX3pH6-OJH%PKpnh#vr05Mro~K{) zFE`6?`G-CLtT*D;tlT^*2ct!6N0$(~e51Q{L`CR2w%h{T2B^f`0R_iATfbSQl<8?K z9fm{l@7>n&B9rxVHA*r01dg90!Jq?j6 z^24CKSqw@|?iWF+L5U%t+SwpU#FaB)TlR`Z$JS7$PEHqW{Rmkt~l1mO5r{4jPbNUeQdjRrIA2IN&4jI%o(|cg4 z53XsU`HYoXnlGGN^Ndr?GpMezFJj9v07~2&hpOyV)=I~)AhqSqzVQ=RnRG+uS!ayj zu4*`G)m~@(e?fpn72i^jQ4oeawfkZaFBrNk@9w_4@&YqVM^9&Leq`dXZisJqTj#gJ z-*~mcq;F%BVg$S^Trobq$5j!~>L>QdQ;wdbU!~*9_Zf|%wg*lInduaj6irj1En<^ zOR-9`;5c`P4Hv={0WlVCd5pl-hU9DgAB)Lie$Ow(PD14>{{76j;i0T$6f)*Mo=+9_ SfGq6ot%>?8mc9Q6NB;wcu?#B! diff --git a/v2realbot/strategy/base.py b/v2realbot/strategy/base.py index 6381b72..3106e0d 100644 --- a/v2realbot/strategy/base.py +++ b/v2realbot/strategy/base.py @@ -24,7 +24,10 @@ from alpaca.trading.enums import TradeEvent, OrderStatus from threading import Event, current_thread import json from uuid import UUID +from rich import print as printnow +#from pyinstrument import Profiler +#profiler = Profiler() # obecna Parent strategie podporující queues class Strategy: def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: str = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None: @@ -324,7 +327,10 @@ class Strategy: pass #self.state.ilog(e="Rush hour - skipping") else: + # Profile the function + #profiler.start() self.next(item, self.state) + #profiler.stop() self.after_iteration(item) ##run strategy live @@ -353,6 +359,7 @@ class Strategy: try: #block 5s, after that check signals item = self.q1.get(timeout=HEARTBEAT_TIMEOUT) + #printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize()) except queue.Empty: #check signals if self.se.is_set(): @@ -386,6 +393,11 @@ class Strategy: tlog(f"FINISHED") 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() if self.mode == Mode.BT: diff --git a/v2realbot/utils/__pycache__/utils.cpython-310.pyc b/v2realbot/utils/__pycache__/utils.cpython-310.pyc index 8b244b573a6c2f61a85f329ef35ba7dd35dca8d9..a97e1708a23f68b8d53a353e348b8b994abaadca 100644 GIT binary patch delta 5361 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 P<#qV-eC6W&qO$)3kSQm~ delta 4459 zcmZuzYiwM_72esm*XzyVtl#VS5j*zA_U_uok0efrN#eY~3D`;CLfBmI%-S2@ySvQ2 zYsU#<0|{+K)T%I)wuJId+enl?j9TbJK~*JQeLy9kT!BzqREdY03RKY_1@)X+hj`tr z05g5hxZt~_b&NgtqjO929+UruF{6}4ax@S z%hO7>krq}1gxDrp#YR$IT1{(CyOhnemTjSRz}B-(DK~AP;nPm#8ahYU0N(_BGusM$ z3vC7KQM#74owh00(ssHIp4(^#?S$ucI#1VwdKcT6a?)VY&gH*U^pWL^sh9u)lt%jc%q}PTS~~ zQ+8#XIZxS?ea!#ZetOO0M7e=(rK8{zpbIhxoMH?Dano&dI|OngvY=JiLB~LBg6@P! zV=!AM-9>i;GYMhrp?e{W1N1n(p1J6_?A=HAgZn{xLUK2P?1aqGBs~E9p%UAJ^bl}2 zfznNE3UPRO zW{MirglCf4Cv1^B#50cF?g+8$=JCbP9IuezTG%+e4WS)jop{~VNV>#D*8=HTOuO&3 zw|4=5p701h?3O(mx14E{=@G)?=z-n5Thx}FSSOo(@D2l1@@zgs8BJ)aVJ6VQxMcc# z*+fqiy{S}Y2>_W+H z0Fz^UH@5d8EH}MS(M9&%Ul9^VTFQhsaAas7T8v-G7C6h3@|2X>;OT+r^P3spLXv-Cx_xoW;Kl^A;b)yk{*<$)VyYvMEs|!9x}W5c~vuUmxB@4 z#M+wCl8CZ3{lr{+vc_$1Yeb8~2x}0wA~XS5-b8|?lV&2po5dgM!ttxHrwXAGVKqVs zAq@a^=*UhTEeWHp{yK6(r0O3eyTs@9y#q299LcJb!kpzaHOUfls+MO^hj*Zufv_ws zBH0ipcZmlY?j$#f)#1lTy?8d7I z4S_Yp+tLTC7$iR8cT4_wa8zeddunW)%_nZo^4TO9$)6Bw;$8+#NJfKrbxl*`vbfYO zhrUkK#iE9iCY27393L%ij>miP>;P|B(DImBp)d*kF;u|ns(0~8S(amK&us3HItaZ0 zP@qzy)ZOyQv!*YVo@0ZTRBGM)VKLJBU~MP(SsvI?XU#ODdXg4aTcv@+hUluh~09`soWx&6>b*EZAT|37}b(W!k-ckwEc~o6Z7rs$ur`K z_PvL`kAwXHzzR&|jdX??Mq(zL=Z50td1J;*!wQtfV)@fXA`AOEfd;!k|5HEs`I&%awu*K#=q449(8VSPu}J_-CYxToCo^Ur$^q z1c+@Z4jjE02S&hufv_S46`-EfvIdhm$bm2aC7Qi}@Z92_u5wbi7Tco;tpFe38eDPi zIB>*adFL6MrRsuF%mW6I{)Yz~F}WYqqPG_USffNx_G zhQ-=wtDHI5%loi++#gTpfOrXQUq--vydtN33^)NIpJGYlIS?Stux}-w#65WF=YR$z z#zqloD@3;&L}cv9*zOYDENoi`T*f1!eNA40bpxh#5NAgKSKBz zVdbrj97?=KME78rd? zG81Mtk%4=B;T@a~?x+y)!=D#-)Ms#$MGJBaM{$f#Fp5tR{*ACg)SsMT$=QSfcQG^G zM?u_F z1^L_Jy`fmMm#QYqn`w=g0cknm17N0jb$wK()I%==J-({{uNR9LN9w diff --git a/v2realbot/utils/utils.py b/v2realbot/utils/utils.py index 4e5a75a..1989a01 100644 --- a/v2realbot/utils/utils.py +++ b/v2realbot/utils/utils.py @@ -3,7 +3,7 @@ import math from queue import Queue from datetime import datetime, timezone, time, timedelta, date import pytz -from dateutil import tz +#from dateutil import tz from rich import print as richprint import decimal from v2realbot.enums.enums import RecordType, Mode, StartBarAlign @@ -291,7 +291,8 @@ class Store: qu = Queue() -zoneNY = tz.gettz('America/New_York') +#zoneNY = tz.gettz('America/New_York') +zoneNY = pytz.timezone('US/Eastern') def print(*args, **kwargs): if QUIET_MODE: @@ -300,13 +301,45 @@ def print(*args, **kwargs): ####ic(*args, **kwargs) richprint(*args, **kwargs) +#optimized by BARD 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 pokud je trojmistne a vic pak zakrouhli na 2, jinak necha """ 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): """ zatim jen na 3 mista -pripadne predelat na dynamicky @@ -315,12 +348,6 @@ def round2five(price: float): """ 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): if n: print(n, 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() 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): + """" + 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 """