From 2bfd51845feaea50f508091f5ae2ebe2b319e530 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Thu, 21 Sep 2023 22:06:05 +0200 Subject: [PATCH] new ind basestats + indname cond s bar dictionary --- v2realbot/ENTRY_ClassicSL_v01.py | 143 +++++++++++++++--- .../trade_offline_streamer.cpython-310.pyc | Bin 5376 -> 5317 bytes .../utils/__pycache__/utils.cpython-310.pyc | Bin 15073 -> 16262 bytes v2realbot/utils/utils.py | 54 +++++++ 4 files changed, 176 insertions(+), 21 deletions(-) diff --git a/v2realbot/ENTRY_ClassicSL_v01.py b/v2realbot/ENTRY_ClassicSL_v01.py index 3709915..064fa13 100644 --- a/v2realbot/ENTRY_ClassicSL_v01.py +++ b/v2realbot/ENTRY_ClassicSL_v01.py @@ -6,7 +6,7 @@ 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, 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, create_new_bars from v2realbot.utils.directive_utils import get_conditions_from_configuration from v2realbot.common.model import SLHistory from datetime import datetime, timedelta @@ -22,6 +22,7 @@ from msgpack import packb, unpackb import asyncio import os from traceback import format_exc +from collections import defaultdict print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) """" @@ -89,7 +90,7 @@ def next(data, state: StrategyState): return value elif isinstance(value, str): try: - #pokud existuje MA bereme MA jinak standard + #pokud existuje v indikatoru MA bereme MA jinak indikator, pokud neexistuje bereme bar ret = get_source_or_MA(indicator=value)[-1] state.ilog(lvl=0,e=f"Pro porovnani bereme posledni hodnotu {ret} z indikatoru {value}") except Exception as e : @@ -108,6 +109,8 @@ def next(data, state: StrategyState): #OBECNE DIREKTIVY - REUSOVATELNE if directive.endswith("above"): cond[cond_type][directive+"_"+indname+"_"+str(value)] = get_source_or_MA(indname)[-1] > value_or_indicator(value) + elif directive.endswith("equals"): + cond[cond_type][directive+"_"+indname+"_"+str(value)] = get_source_or_MA(indname)[-1] == value_or_indicator(value) elif directive.endswith("below"): cond[cond_type][directive+"_"+indname+"_"+str(value)] = get_source_or_MA(indname)[-1] < value_or_indicator(value) elif directive.endswith("falling"): @@ -170,10 +173,14 @@ def next(data, state: StrategyState): def get_source_or_MA(indicator): #pokud ma, pouzije MAcko, pokud ne tak standardni indikator + #pokud to jmeno neexistuje, tak pripadne bere z barů (close,open,hlcc4, vwap atp.) try: return state.indicators[indicator+"MA"] except KeyError: - return state.indicators[indicator] + try: + return state.indicators[indicator] + except KeyError: + return state.bars[indicator] # #vrati true pokud dany indikator krosnul obema smery # def buy_if_crossed(indicator, value): # res = crossed(threshold=value, list=get_source_or_MA(indicator)) @@ -256,7 +263,6 @@ def next(data, state: StrategyState): else: return - #WIP - def populate_dynamic_custom_indicator(name): ind_type = "custom" @@ -396,9 +402,53 @@ def next(data, state: StrategyState): # return 0, Average(source_series[-lookback:]) - #abs/rel divergence of two indicators - def divergence(params): - funcName = "indicatorDivergence" + #indicator to run on bar multiples + #např. umožní RSI na 5min + #params: resolution (bar multiples) + def upscaledrsi(params): + funcName = "upscaledrsi" + #new res in seconds + new_resolution = safe_get(params, "resolution", None) + old_resolution = state.bars["resolution"][-1] + + #pokud potrebuju vsechny bary, tak si je dotahnu + new_bars = {} + new_bars = create_new_bars(state.bars, new_resolution) + #val = rsi(bars.) + + #pokud potrebuju jen close nebo open muzu pouzit toto + # vezme to N-th element z pole + def resample_close_prices(bars, new_resolution): + # Check that the new resolution is a multiple of the old resolution. + if new_resolution % bars['resolution'][-1] != 0: + raise ValueError('New resolution must be a multiple of the old resolution.') + + # Calculate the step size for selecting every Nth element. + step = new_resolution // bars['resolution'][-1] + + # Extract close prices at the new resolution. + new_close_prices = bars['close'][::step] + #optimizied - but works only for numpy arrays, prevedeni z listu na numpy is costly - bars_array = np.array(bars) + #new_close_prices = np.take(bars['close'], np.arange(0, len(bars['close']), step), axis=0) + + return new_close_prices + + + #pokud je vstup jedna hodnota - muzu brat close,open v danem rozliseni tzn. jen N-th hodnotu zde + # Check that the new resolution is a multiple of the old resolution. + if new_resolution % state.bars["resolution"][-1] != 0: + raise ValueError('The new resolution must be a multiple of the old resolution.') + + #get the number of bars in the new resolution. + n = new_resolution // old_resolution + # Calculate the new resolution values. + new_resolution_values = old_resolution_values.reshape((-1, new_resolution // len(old_resolution_values))) + + # Select the N-th values from the new resolution values. + new_resolution_values[:, n] + + + source1 = safe_get(params, "source1", None) if source1 in ["open","high","low","close","vwap","hlcc4"]: source1_series = state.bars[source1] @@ -411,6 +461,16 @@ def next(data, state: StrategyState): source2_series = state.indicators[source2] mode = safe_get(params, "type") state.ilog(lvl=0,e=f"INSIDE {funcName} {source1=} {source2=} {mode=}", **params) + + #abs/rel divergence of two indicators + def divergence(params): + funcName = "indicatorDivergence" + source1 = safe_get(params, "source1", None) + source1_series = get_source_series(source1) + source2 = safe_get(params, "source2", None) + source2_series = get_source_series(source2) + mode = safe_get(params, "type") + state.ilog(lvl=0,e=f"INSIDE {funcName} {source1=} {source2=} {mode=}", **params) val = 0 if mode == "abs": val = round(abs(float(source1_series[-1]) - float(source2_series[-1])),4) @@ -426,16 +486,65 @@ def next(data, state: StrategyState): val = pct_diff(num1=float(source1_series[-1]),num2=float(source2_series[-1])) return 0, val + #indicator allowing to be based on any bar parameter (index, high,open,close,trades,volume, etc.) + def barparams(params): + funcName = "barparams" + if params is None: + return -2, "params required" + source = safe_get(params, "source", None) + if source is None: + return -2, "source required" + try: + return 0, state.bars[source][-1] + except Exception as e: + return -2, str(e)+format_exc() + + #vstupem je bud indicator nebo bar parametr + #na tomto vstupu dokaze provest zakladni statisticke funkce pro subpole X hodnot zpatky + #podporovane functions: min, max, mean + def basestats(params): + funcName = "basestats" + #name of indicator or + source = safe_get(params, "source", None) + lookback = safe_get(params, "lookback", None) + func = safe_get(params, "function", None) + + source_dict = defaultdict(list) + source_dict[source] = get_source_series(source) + + if lookback is None: + source_array = source_dict[source] + else: + try: + source_array = source_dict[source][-lookback-1:] + except IndexError: + source_array = source_dict[source] + + if func == "min": + val = np.amin(source_array) + elif func == "max": + val = np.amax(source_array) + elif func == "mean": + val = np.mean(source_array) + else: + return -2, "wrong function" + + return 0, val + + def get_source_series(source): + try: + return state.bars[source] + except KeyError: + return state.indicators[source] + + #strength, absolute change of parameter between current value and lookback value (n-past) #used for example to measure unusual peaks def delta(params): funcName = "delta" source = safe_get(params, "source", None) lookback = safe_get(params, "lookback",1) - if source in ["open","high","low","close","vwap","hlcc4"]: - source_series = state.bars[source] - else: - source_series = state.indicators[source] + source_series = get_source_series(source) lookbackval = source_series[-lookback-1] currval = source_series[-1] @@ -448,19 +557,11 @@ def next(data, state: StrategyState): def slope(params): funcName = "slope" source = safe_get(params, "source", None) - if source in ["open","high","low","close","vwap","hlcc4"]: - source_series = state.bars[source] - else: - source_series = state.indicators[source] + source_series = get_source_series(source) lookback = safe_get(params, "lookback", 5) lookback_priceline = safe_get(params, "lookback_priceline", None) - if lookback_priceline is None: - lookback_series = source_series - elif lookback_priceline in ["open","high","low","close","vwap","hlcc4"]: - lookback_series = state.bars[lookback_priceline] - else: - lookback_series = state.indicators[lookback_priceline] + lookback_series = get_source_series(lookback_priceline) try: lookbackprice = lookback_series[-lookback-1] diff --git a/v2realbot/loader/__pycache__/trade_offline_streamer.cpython-310.pyc b/v2realbot/loader/__pycache__/trade_offline_streamer.cpython-310.pyc index e7ce8c24c4a3e13690d6d345658ca09ab201c88d..6963c18deb6f27c9a5cbe9748b4114dddbdf4920 100644 GIT binary patch delta 608 zcmYjPJ8u&~5T4z;$oKU5{NQ|vA6$qfY@k5mA(RR!4Fwg2MS-9gQaI9CF)PyS3Z#qZ zA|f_F;9R&wfGkSNGzf{FioZZX9cE7{*wvhFXXl&mo0)w(IUk4fF!X@d#fA5heF%+=4G-Mf*j%~}Vr_#ilX1!HiyFA`~?okGFTPe3H1YhucD zNuGiwfWH3ebf!T9$`M>T1|>EzOh7v1bH&!ISN5g5WQ*UTC0(o+{C!h-Vo(mHw`9sE zu1LBCXD|f>n|5I_$er8p>X+Vihn>cWr2^8LmD@77M8taTj{xBl2vt^Ds4hNz_to=jvW;e@`|>+>`fvgqXoOFA$?g-VGo{7 zRV=Q{7}FH6gPSK@HLE;L8!V{!=cbp{->On_M~0OD9c~e-m%?jCr(^8@e>!D{Q^POD z@_CEJq5bs2H38WKl_JHjepW+N+XDjA-@c(k7SkJ7+L-XJ)FhB Xe1_}4(I>{v^eWDHBUpcr?^*u<(+iZY delta 695 zcmYjPO^ee|6us{y!z5|bHf_f={Z7ZK9X~*jaUp{nMO=u2i{e5Qye-(7Ntw<=VSHhP zxU@_$;AR*}e}N8N2`+WvPjD}8^f!q2g#jmzm*kz?d(XM&=3DRenmx8{3+Vd#*BZdF z{dAfAjDzJ*s~impBm0n}NzdFNKhh({K;#vw5VYc+(O{YQj&Y-vlgp#hCQLpEj#(AA zGZC)TQ1>8WXvcG7W#4u|xS~Rl<;0s49x)d%=R#-*E{(K$74t4ggD_LnM)jm2%{iO= z5hZD2A>s$c&=Sq0C9OFNGsNXcGvW;TfS}WgGzNij6?T8UBn@gkq8;29ZK-vX7$s8) zz~WR3v($#4bY%9VSSjwCT|X7L2{Sfn1ynj%BAT)i7t~W3EAf%p?X90CNG(wY=#+IE z&xCfs(G6|Nti-=ff8UdNB@?S*L1tvmgIn;pA3EZKbg%p`u$a~<>Yzc&|4P}M zz8$AZu1TBfzrzM)H9dI2=rqID>C=quJX8Flm#oR6vU$#=!?6}mE&pO73s}7lVL^pS zMVCt@f2QWR!e~FB$#HDQKdi=Gf5_)*<{Uf90Q&kJSMlr{2+QJ9a#@xo&G00mo6e6T z!*fE5V5B39bZQwMLhtG2%e0%U@I2mtm?B&}35i#Cayv=sCOq2e` bNlV?97GLro8G9dZIlf-83Vd diff --git a/v2realbot/utils/__pycache__/utils.cpython-310.pyc b/v2realbot/utils/__pycache__/utils.cpython-310.pyc index a97e1708a23f68b8d53a353e348b8b994abaadca..fd83525c5782591253b0816b9c1b121d244b8609 100644 GIT binary patch delta 5930 zcma)A3ve7qnVz1R-5u>qvMtM!Y}so$ery!WiXBU`EI;Ij?8J%VckFeN@o2kOT4{D? z>6w*ev&x3pNeXgK9quGtswlv4aOE5Ua=I!CxFba_ca>a0a^^UWo1-}3D8Pgw6rm`J zN5cR2>a}Zgb*tL1yZ`=Q{df2O_v#15C(3ar77Huz`PThWHhk}g<15J1FLm^jvO-mR zb7~8%rwx~sR4=_im#}TL5xz@VA8lgWGX`Bon=b`Y{j`Pcpshe%&iXPzx`MV{(o;L> z`{^p6CxE`1?E-o`T?5>^>1}i^XdIyH=z3`P&fF6~+$LMj8 zK1m;yw385{Q!;iB(9=LaMIWs&&rh78DVTVmO37LJAka>OiHF#JF!?YwVSEP0=OFNw zG&DrhKucB8C}lu9OEZjs)G!?ZM<0avWa%ixXB6V|5Is9aZ3xK2K+e%Tkn?Q+MJ;94 zDb!&mEwEj%Y7^q~KFUG-9Cg8Dg}MW%9xVdZqT_S|v<%TndLG&|RgWvl4@`ef%aU?h z#bY<~i9l<>cQ1V1gG5nEq@+;N0;jM=X#yP6c-&K{c9A?rJmRS^tG6f@$wI#6%5tDg zsh;);y`)|x|7TlXz)&K?ga$qun$kcdKvfV{OQcbm(o1@YEK{Zeo;Dh+m@MfuNJE!3 zYBVcTL7<0#Ue}_Of;9X%I7wgFxBg6P@93A~C>+-9Bg&Qm8L60CIC;oE3nb<-Kfv zIN^-g>2b-`Dfz*WIj;;Q(eykekuB#FE=xOk>Pl0`m{;Wa8EgL^JAcmYM8Xfx+u?AO zlncFwtBvQ1u2)VBv4oY#6>TqDuvx;%$S62AUEpRX4}yF$LR|Bsf;K1o6iLbj^%5I>2-VSgWN!Dc6W9d!`DGC z8TP}cEW60|bMA0o%jP{W$@0FMFZcnLD|qMqpvA5HF!Q4>vv_*Mb-2eUD9%}vh}pca zxy4*E;_JB2sd)89q+&RR88cmWig_<(%#7XClGz#8v$$t^*&K6Ii)M)|PiIKWAwp6^ zHV=n^xn?$R&MBEwkKuAt(OGebo13bi9mxPTfqLnk^aexeUQ>9Fcsp?VNPSwK*Ezi_ zAvo@4fX1jn5}KhIWcE)d!QVC18KhZj)mlj_X(qF?4Zx$Td>e>~Wq}tOwnK;aBkVxf zDXs@vTX_%mCG9k{@`JzxyT6!2$SI?yZMmL_3fu*j>-vXTo=q0`&b)Z7uEXM; zsQL~7U$bmGsq=kE*pGl#Q{fslNwwn5y~t5nOx87BFKgRpf#hP9k3#(7>SEHQ@gw4F z*jIT{^hA>6sIVf8)C3=7a13;dfLRN z8e6*gR!|@h3LHgwJZj`a1LKTa!%T-X~lfJ`NLU;F$YhKLB4@{0$;rS+=9BloT&CYFt$LdJoFtMRHv6!q|F|qQbjM z%4l?s5(CP%DoPwEd#Wh)Ksi)JX#mQJD#{|DJXE3VnK8B)NSP|8B|veiD2+gwoS`iB z0_Y?qURZp#*(Xin$(Hpi99SH0L)Zl12OzO`-^Di~;e_~7%iX&!Ko>HE<_wM220k+C zI(gG&JZstQ7}sv#jRy=8hWv)#C^5u9>uMtkcc)q9mqn)aInp8i*xGHOU4Jp294N*G z+e$MN&Qp;=s(2cWUqV>0dC0z~0Cj_zo|Cg7d2lH1`_O6w%&D-4xQ}t%$9EevF2pm- zA0eY+$%>bE$_nNO3vdx^sEW2@QT{9lLBiBsW@mggQ>e9Nm{2{ko@pKti`xF;F06t4 zuMj>8;On%QD@YyuV@RxVRCld$c1m1tYXcA7ZtEb=isdU;4Lpae^$1rH{<>OH>cHg4 zp8>cJSB7~p3>TvTq8SbHXTED9YX zrjvH4dtuB=nM!KAx_tmf%z?=mca zn={#Qc6m-qkbe=^(|+5rL=8JBJd0d9C~PK+3IDQq=C&V_uZW4Yx06@JC)XZ&;2+T7 zKLYsip`x43GuJgooFcq?3|@3c@C{S51V0L;3SVp{GCU9Z-@*8X^{^Ty!FSvB1mSN^ z?^~B59bZM|nA7>o2(KXg3&Lvv$q<*uzlnr@7q72BeN*ZNLlf{uawep`&xqX{R+Ddk zYG?x~e+z}cw8H-bV36O+(m&z!s|eEOKSS?C#d2_=Wn_2*AZ5sB8!XrlL6y1H$`{W% z6sdTGXX0z(=gE!aJEHaW>*lSzkWNZh(1H2o^Kam=X1`)!K)#7(Y`+V#{NIr6zYyM- zzNaHX%1P|YbJ7RB8@RW(Ts|63YQ8bS*ci3WyOmI&6WJe!KoB?WMl(uFW&e#m87*Xi zYWg}7{uKf5D#xnqhbCB`)CQMOZXO2CH%1&gD^1*DIr5~)6ZKOO-?)~1U);6v<*o@- ziQLyTemCkMS)bG7VX>yOU9KGX@hvb@_b+4%K==U?euz*+sCI&%0or>I zjCU~iV2+BD1SB3wulfP4!8`;;)=>Vx&`Aak4=$*(;?>Ts4Ren39|1?LEp#|iad^e1 z)f?qj{RH`L+=BTu&_c-kGn_mvMmLT2zI8L7-&83lmR+#YR^=yzXXOfU5S7WOk?^-g zv}^aCPowZ>5dH>%BFMd)kBIEa0D5dKR{BcJTr-2Eboehwh1`Xs}@ zgu{6yDDMY6Ud*SL&N|Ub_&d|#JO4lqVT6*pA8?EKKKnI_UPbsI(l!2Z?7x8Uw*c3O zUuVLRw;=796%*1slxSDQ(JkvvRF+@Y(_ZEYRQk75x^q|F$BCh$sqq7_J0rHYw)woCzC!I z^T~Kkprqas@0p-#q$=-FCNu80F$pE@IB7UJlT^nw#Ouv|fqo=FGtFbnrG zh*UJoRqSa9mvC6UK~hD(=;&@!2@&^npIIl3)GaWQK&edtzLDWh4t`E?{tNM1cYAlW zD!&1q&+rO{H@ag&!7ZFWi0i@o)Cg}Ucs=2LiV>0M*;tlVw2Bo2eXB!=Ak=7$rbk$M zOuoyQdt~+?!J9u0%utjuy$wPgJPIu|X=u3APGrUc5|$QV1a^gA$39IhiR9d2xF}IRSk-%b5T8c;d2xU5W8^QzH+$ETviNQ9s>p|syb9qd(Y~#{ zEF%*vSb1u>9GBup=AZg5Par4$H0HlR*a!gsF>oB4@5M15CVm7#Ze9v|=MXG}A%qbG z++?^+Mfe8_A4kF@LP=4|ihSnt%QzMY_})sX?<-caat28+A>f;We-Yu!2yY^M8{s<$ zvUYqQdzcCNX@s{CFaz*+5acD52Th*47`Vja>k;R-O!n|X`9^=vp+%eR1O~-@M9{uF lR~o9JuGriZX>K-}jJVO(7;0T&bcZ#g-iXHA#pnAr{|OQC%V7Wj delta 4637 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 diff --git a/v2realbot/utils/utils.py b/v2realbot/utils/utils.py index 1989a01..3454367 100644 --- a/v2realbot/utils/utils.py +++ b/v2realbot/utils/utils.py @@ -25,6 +25,58 @@ import numpy as np import pandas as pd from collections import deque +import numpy as np + +def create_new_bars(bars, new_resolution): + """Creates new bars dictionary in the new resolution. + + Args: + bars: A dictionary representing ohlcv bars. + new_resolution: A new resolution in seconds. + + Returns: + A dictionary representing ohlcv bars in the new resolution. + """ + + # Check that the new resolution is a multiple of the old resolution. + if new_resolution % bars['resolution'][0] != 0: + raise ValueError('New resolution must be a multiple of the old resolution.') + + # Calculate the number of bars in the new resolution. + new_bar_count = int(len(bars['time']) / (new_resolution / bars['resolution'][0])) + + # Create a new dictionary to store the new bars. + new_bars = {'high': np.empty(new_bar_count), + 'low': np.empty(new_bar_count), + 'volume': np.empty(new_bar_count), + 'close': np.empty(new_bar_count), + 'open': np.empty(new_bar_count), + 'time': np.empty(new_bar_count), + 'resolution': [new_resolution]} + + # Calculate the start and end time of each new bar. + new_bar_start_times = np.arange(0, new_bar_count) * new_resolution + new_bar_end_times = new_bar_start_times + new_resolution + + # Find all the old bars that are within each new bar. + old_bar_indices_in_new_bars = np.searchsorted(bars['time'], new_bar_start_times, side='right') - 1 + + # Calculate the high, low, volume, and close of each new bar. + new_bar_highs = np.amax(bars['high'][old_bar_indices_in_new_bars:], axis=1) + new_bar_lows = np.amin(bars['low'][old_bar_indices_in_new_bars:], axis=1) + new_bar_volumes = np.sum(bars['volume'][old_bar_indices_in_new_bars:], axis=1) + new_bar_closes = bars['close'][old_bar_indices_in_new_bars[:,-1]] + + # Add the new bars to the new dictionary. + new_bars['high'] = new_bar_highs + new_bars['low'] = new_bar_lows + new_bars['volume'] = new_bar_volumes + new_bars['close'] = new_bar_closes + new_bars['open'] = new_bar_closes[:-1] + new_bars['time'] = new_bar_start_times + + return new_bars + def pct_diff(num1: float, num2: float, decimals: int = 3, absolute: bool = False): if num1 == 0: return 0 @@ -246,6 +298,8 @@ def json_serial(obj): return str(obj) if isinstance(obj, Enum): return str(obj) + if isinstance(obj, np.int64): + return int(obj) if type(obj) is Order: return obj.__dict__ if type(obj) is TradeUpdate: