refactor RSI SELL

This commit is contained in:
David Brazda
2023-05-25 20:17:56 +02:00
parent 8cf2956720
commit bf7c1773bd
16 changed files with 736 additions and 231 deletions

View File

@ -5,7 +5,7 @@ from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import Strat
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType
from v2realbot.indicators.indicators import ema from v2realbot.indicators.indicators import ema
from v2realbot.indicators.oscillators import rsi from v2realbot.indicators.oscillators import rsi
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, get_tick from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, get_tick, round2five
from datetime import datetime from datetime import datetime
#from icecream import install, ic #from icecream import install, ic
#from rich import print #from rich import print
@ -175,6 +175,10 @@ def next(data, state: StrategyState):
akt_pozic = int(state.positions) akt_pozic = int(state.positions)
max_pozic = int(state.vars.maxpozic) max_pozic = int(state.vars.maxpozic)
if akt_pozic >= max_pozic:
state.ilog(e="MAX pozic reached, cannot vyklad")
return
#mame polovinu a vic vylozeno, pouzivame defenzicni krivku #mame polovinu a vic vylozeno, pouzivame defenzicni krivku
if is_defensive_mode(): if is_defensive_mode():
state.ilog(e="DEF: Pouzivame defenzivni krivku", akt_pozic=akt_pozic, max_pozic=max_pozic, curve_def=curve_def) state.ilog(e="DEF: Pouzivame defenzivni krivku", akt_pozic=akt_pozic, max_pozic=max_pozic, curve_def=curve_def)
@ -243,31 +247,38 @@ def next(data, state: StrategyState):
#na urovni CBARU mame zajisteno, ze update prichazi pri zmene ceny #na urovni CBARU mame zajisteno, ze update prichazi pri zmene ceny
#v kazde iteraci testujeme sell #v kazde iteraci testujeme sell
#pri confirmed tesutjeme i buy #pri potvrzenem baru muzeme provest kroky per hlavni BAR
#potvrzeni neprinasi nikdy zadna nova data, ale pouze potvrzeni.
state.ilog(e="-----") state.ilog(e="-----")
eval_sell() eval_sell()
conf_bar = data['confirmed'] conf_bar = data['confirmed']
#for CBAR TICK and VOLUME change info if conf_bar == 1:
#price change vs Volume #delej veci per standardni bar
tick_price = data['close'] state.ilog(e="BAR potvrzeny")
tick_volume = data['volume'] - state.vars.last_tick_volume else:
#pouze potvrzovací BAR CBARu, mozna id confirmed = 1, pak ignorovat
if tick_volume == 0:
pass pass
#delej veci tick-based
##naplneni indikatoru vnitrniho CBAR tick price #CBAR INDICATOR pro tick price a deltu VOLUME
##pozor CBAR identifikatory jsou ukladane do historie az pro konfirmnuty bar tick_price = round2five(data['close'])
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
if conf_bar == 0:
try: try:
state.indicators.tick_price[-1] = tick_price #pokud v potvrzovacím baru nebyly zmeny, nechavam puvodni hodnoty
state.indicators.tick_volume[-1] = tick_volume # if tick_delta_volume == 0:
# state.indicators.tick_price[-1] = state.indicators.tick_price[-2]
# state.indicators.tick_volume[-1] = state.indicators.tick_volume[-2]
# else:
#docasne dame pryc volume deltu a davame absolutni cislo
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
except: except:
pass pass
state.ilog(e=f"TICK PRICE {tick_price} VOLUME {tick_volume} {conf_bar=}", last_price=state.vars.last_tick_price, last_volume=state.vars.last_tick_volume) state.ilog(e=f"TICK PRICE {tick_price} VOLUME {tick_delta_volume} {conf_bar=}", last_price=state.vars.last_tick_price, last_volume=state.vars.last_tick_volume)
state.vars.last_tick_price = tick_price state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume'] state.vars.last_tick_volume = data['volume']
@ -278,6 +289,18 @@ def next(data, state: StrategyState):
#TEST BUY SIGNALu z cbartick_price - 3klesave za sebou
buy_tp = isfalling(state.cbar_indicators.tick_price,state.vars.Trend)
state.ilog(e=f"TICK SIGNAL ISFALLING {buy_tp}", last_tp=state.cbar_indicators.tick_price[-6:], trend=state.vars.Trend)
#IVWAP - PRUBEZNY persistovany VWAP
# try:
# #naplneni cbar tick indikatoru s prubeznou vwap
# state.cbar_indicators.ivwap[-1]=data['vwap']
# except:
# pass
# if data['confirmed'] == 0: # if data['confirmed'] == 0:
# state.ilog(e="CBAR unconfirmed - returned", msg=str(data)) # state.ilog(e="CBAR unconfirmed - returned", msg=str(data))
# #TBD zde muzeme i nakupovat # #TBD zde muzeme i nakupovat
@ -287,7 +310,7 @@ def next(data, state: StrategyState):
# else: # else:
# state.ilog(e="CBAR confirmed - continue", msg=str(data)) # state.ilog(e="CBAR confirmed - continue", msg=str(data))
#EMA INDICATOR - #BAR EMA INDICATOR -
#plnime MAcko - nyni posilame jen N poslednich hodnot #plnime MAcko - nyni posilame jen N poslednich hodnot
#zaroven osetrujeme pripady, kdy je malo dat a ukladame nulu #zaroven osetrujeme pripady, kdy je malo dat a ukladame nulu
try: try:
@ -296,27 +319,31 @@ def next(data, state: StrategyState):
source = state.bars.close[-ma:] #state.bars.vwap source = state.bars.close[-ma:] #state.bars.vwap
ema_value = ema(source, ma) ema_value = ema(source, ma)
state.indicators.ema[-1]=trunc(ema_value[-1],3) state.indicators.ema[-1]=trunc(ema_value[-1],3)
state.ilog(e=f"EMA {state.indicators.ema[-1]}", ema_last=state.indicators.ema[-6:])
except Exception as e: except Exception as e:
state.ilog(e="EMA nechavame 0", message=str(e)+format_exc()) state.ilog(e="EMA nechavame 0", message=str(e)+format_exc())
#state.indicators.ema[-1]=(0) #state.indicators.ema[-1]=(0)
#RSI14 INDICATOR #CBAR RSI14 INDICATOR
try: try:
##mame v atributech nastaveni? ##mame v atributech nastaveni?
rsi_dont_buy_above = safe_get(state.vars, "rsi_dont_buy_above",50) rsi_dont_buy_above = safe_get(state.vars, "rsi_dont_buy_above",50)
rsi_buy_signal_conf = safe_get(state.vars, "rsi_buy_signal_below",40) rsi_buy_signal_conf = safe_get(state.vars, "rsi_buy_signal_below",40)
rsi_buy_signal = False rsi_buy_signal = False
rsi_dont_buy = False rsi_dont_buy = False
rsi_length = 2 rsi_length = 14
source = state.bars.close #[-rsi_length:] #state.bars.vwap
#source = state.bars.close #[-rsi_length:] #state.bars.vwap
#jako zdroj je prubezna CBAR tickprice
source = state.cbar_indicators.tick_price
rsi_res = rsi(source, rsi_length) rsi_res = rsi(source, rsi_length)
rsi_value = trunc(rsi_res[-1],3) rsi_value = trunc(rsi_res[-1],3)
state.indicators.RSI14[-1]=rsi_value state.cbar_indicators.RSI14[-1]=rsi_value
rsi_dont_buy = rsi_value > rsi_dont_buy_above rsi_dont_buy = rsi_value > rsi_dont_buy_above
rsi_buy_signal = rsi_value < rsi_buy_signal_conf rsi_buy_signal = rsi_value < rsi_buy_signal_conf
state.ilog(e=f"RSI{rsi_value} {rsi_length=} {rsi_dont_buy=} {rsi_buy_signal=}", rsi_indicator=state.indicators.RSI14[-5:]) state.ilog(e=f"CBARRSI{rsi_value} {rsi_length=} {rsi_dont_buy=} {rsi_buy_signal=}", rsi_indicator=state.cbar_indicators.RSI14[-5:])
except Exception as e: except Exception as e:
state.ilog(e=f"RSI {rsi_length=} nechavame 0", message=str(e)+format_exc()) state.ilog(e=f"CBARRSI {rsi_length=} nechavame 0", message=str(e)+format_exc())
#state.indicators.RSI14.append(0) #state.indicators.RSI14.append(0)
@ -417,9 +444,8 @@ def next(data, state: StrategyState):
#TODO: zvazit jestli nechat i pri otevrenych pozicich, zatim nechavame #TODO: zvazit jestli nechat i pri otevrenych pozicich, zatim nechavame
#TODO int(int(state.oa.poz)/int(state.variables.chunk)) > X #TODO int(int(state.oa.poz)/int(state.variables.chunk)) > X
#TODO predelat mechanismus ticků (zrelativizovat), aby byl pouzitelny na tituly s ruznou cenou
#TODO spoustet 1x za X iteraci nebo cas #TODO spoustet 1x za X iteraci nebo cas
if state.vars.jevylozeno == 1: if state.vars.jevylozeno == 1 and len(state.vars.pendingbuys)>0:
#pokud mame vylozeno a cena je vetsi nez tick2reset #pokud mame vylozeno a cena je vetsi nez tick2reset
if len(state.vars.pendingbuys)>0: if len(state.vars.pendingbuys)>0:
maxprice = max(state.vars.pendingbuys.values()) maxprice = max(state.vars.pendingbuys.values())
@ -443,34 +469,6 @@ def next(data, state: StrategyState):
state.vars.jevylozeno = 0 state.vars.jevylozeno = 0
state.ilog(e="PB se vyklepaly nastavujeme: neni vylozeno", jevylozeno=state.vars.jevylozeno) state.ilog(e="PB se vyklepaly nastavujeme: neni vylozeno", jevylozeno=state.vars.jevylozeno)
#TODO toto dodelat konzolidaci a mozna lock na limitku a pendingbuys a jevylozeno ??
#kdykoliv se muze notifikace ztratit
# - pendingbuys - vsechny open orders buy
# - limitka - open order sell
#pokud je vylozeno a mame pozice a neexistuje limitka - pak ji vytvorim
# if int(state.oe.poz)>0 and state.oe.limitka == 0:
# #pro jistotu updatujeme pozice
# state.oe.avgp, state.oe.poz = state.oe.pos()
# if int(state.oe.poz) > 0:
# cena = round(float(state.oe.avgp) + float(state.oe.stratvars["profit"]),2)
# print("BUGF: limitka neni vytvarime, a to za cenu",cena,"mnozstvi",state.oe.poz)
# print("aktuzalni ltp",ltp.price[state.oe.symbol])
# try:
# state.oe.limitka = state.oe.sell_noasync(cena, state.oe.poz)
# print("vytvorena limitka", state.oe.limitka)
# except Exception as e:
# print("Neslo vytvorit profitku. Problem,ale jedeme dal",str(e))
# pass
# ##raise Exception(e)
print(10*"*","NEXT STOP",10*"*") print(10*"*","NEXT STOP",10*"*")
def init(state: StrategyState): def init(state: StrategyState):
@ -481,12 +479,13 @@ def init(state: StrategyState):
state.vars.last_tick_price = 0 state.vars.last_tick_price = 0
state.vars.last_tick_volume = 0 state.vars.last_tick_volume = 0
state.vars.next_new = 0 state.vars.next_new = 0
state.indicators['tick_price'] = [] #state.cbar_indicators['ivwap'] = []
state.indicators['tick_volume'] = [] state.cbar_indicators['tick_price'] = []
state.cbar_indicators['tick_volume'] = []
state.indicators['ema'] = [] state.indicators['ema'] = []
state.indicators['slope'] = [] state.indicators['slope'] = []
state.indicators['slopeMA'] = [] state.indicators['slopeMA'] = []
state.indicators['RSI14'] = [] state.cbar_indicators['RSI14'] = []
#static indicators - those not series based #static indicators - those not series based
state.statinds['angle'] = dict(minimum_slope=state.vars["minimum_slope"]) state.statinds['angle'] = dict(minimum_slope=state.vars["minimum_slope"])
state.vars["ticks2reset_backup"] = state.vars.ticks2reset state.vars["ticks2reset_backup"] = state.vars.ticks2reset

View File

@ -194,7 +194,7 @@ class RunArchiveDetail(BaseModel):
name: str name: str
bars: dict bars: dict
#trades: Optional[dict] #trades: Optional[dict]
indicators: dict indicators: List[dict]
statinds: dict statinds: dict
trades: List[TradeUpdate] trades: List[TradeUpdate]

View File

@ -6,7 +6,7 @@ from appdirs import user_data_dir
NORMALIZED_TICK_BASE_PRICE = 30.00 NORMALIZED_TICK_BASE_PRICE = 30.00
LOG_RUNNER_EVENTS = False LOG_RUNNER_EVENTS = False
#no print in console #no print in console
QUIET_MODE = True QUIET_MODE = False
#how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting #how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting
#0 - optimistic, every knot high will fill the order #0 - optimistic, every knot high will fill the order
#N - N consecutive trades required #N - N consecutive trades required

View File

@ -458,6 +458,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
#flatten indicators from numpy array #flatten indicators from numpy array
flattened_indicators = {} flattened_indicators = {}
#pole indicatoru, kazdy ma svoji casovou osu time
flattened_indicators_list = []
for key, value in strat.state.indicators.items(): for key, value in strat.state.indicators.items():
if isinstance(value, ndarray): if isinstance(value, ndarray):
#print("is numpy", key,value) #print("is numpy", key,value)
@ -466,11 +468,22 @@ def archive_runner(runner: Runner, strat: StrategyInstance):
else: else:
#print("is not numpy", key, value) #print("is not numpy", key, value)
flattened_indicators[key]= value flattened_indicators[key]= value
flattened_indicators_list.append(flattened_indicators)
flattened_indicators = {}
for key, value in strat.state.cbar_indicators.items():
if isinstance(value, ndarray):
#print("is numpy", key,value)
flattened_indicators[key]= value.tolist()
#print("changed numpy:",value.tolist())
else:
#print("is not numpy", key, value)
flattened_indicators[key]= value
flattened_indicators_list.append(flattened_indicators)
runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = runner.id, runArchiveDetail: RunArchiveDetail = RunArchiveDetail(id = runner.id,
name=runner.run_name, name=runner.run_name,
bars=strat.state.bars, bars=strat.state.bars,
indicators=flattened_indicators, indicators=flattened_indicators_list,
statinds=strat.state.statinds, statinds=strat.state.statinds,
trades=strat.state.tradeList) trades=strat.state.tradeList)
resh = db_arch_h.insert(runArchive.__dict__) resh = db_arch_h.insert(runArchive.__dict__)

View File

@ -23,6 +23,8 @@ class TradeAggregator:
mintick: int = 0, mintick: int = 0,
exthours: bool = False): exthours: bool = False):
""" """
UPDATED VERSION - vrací více záznamů
Create trade agregator. Instance accepts trades one by one and process them and returns output type Create trade agregator. Instance accepts trades one by one and process them and returns output type
Trade - return trade one by one (no change) Trade - return trade one by one (no change)
Bar - return finished bar in given timeframe Bar - return finished bar in given timeframe
@ -62,6 +64,8 @@ class TradeAggregator:
#instance variable to hold last trade price #instance variable to hold last trade price
self.last_price = 0 self.last_price = 0
self.barindex = 1 self.barindex = 1
self.diff_price = True
self.preconfBar = {}
async def ingest_trade(self, indata, symbol): async def ingest_trade(self, indata, symbol):
""" """
@ -72,7 +76,7 @@ class TradeAggregator:
data = unpackb(indata) data = unpackb(indata)
#last item signal #last item signal
if data == "last": return data if data == "last": return [data]
#print(data) #print(data)
##implementing fitlers - zatim natvrdo a jen tyto: size: 1, cond in [O,C,4] opening,closed a derivately priced, ##implementing fitlers - zatim natvrdo a jen tyto: size: 1, cond in [O,C,4] opening,closed a derivately priced,
@ -82,15 +86,15 @@ class TradeAggregator:
## přidán W - average price trade, U - Extended hours - sold out of sequence ## přidán W - average price trade, U - Extended hours - sold out of sequence
try: try:
for i in data['c']: for i in data['c']:
if i in ('C','O','4','B','7','V','P','W','U'): return 0 if i in ('C','O','4','B','7','V','P','W','U'): return []
except KeyError: except KeyError:
pass pass
#EXPERIMENT zkusime vyhodit vsechny pod 50 #puv if int(data['s']) == 1: return 0 #EXPERIMENT zkusime vyhodit vsechny pod 50 #puv if int(data['s']) == 1: return []
#zatim nechavame - výsledek je naprosto stejný jako v tradingview #zatim nechavame - výsledek je naprosto stejný jako v tradingview
if int(data['s']) < self.minsize: return 0 if int(data['s']) < self.minsize: return []
#{'t': 1678982075.242897, 'x': 'D', 'p': 29.1333, 's': 18000, 'c': [' ', '7', 'V'], 'i': 79372107591749, 'z': 'A', 'u': 'incorrect'} #{'t': 1678982075.242897, 'x': 'D', 'p': 29.1333, 's': 18000, 'c': [' ', '7', 'V'], 'i': 79372107591749, 'z': 'A', 'u': 'incorrect'}
if 'u' in data: return 0 if 'u' in data: return []
#pokud projde TRADE s cenou 0.33% rozdilna oproti predchozi, pak vyhazujeme v ramci cisteni dat (cca 10ticku na 30USD) #pokud projde TRADE s cenou 0.33% rozdilna oproti predchozi, pak vyhazujeme v ramci cisteni dat (cca 10ticku na 30USD)
pct_off = 0.33 pct_off = 0.33
@ -106,7 +110,7 @@ class TradeAggregator:
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 0 ##return []
# with open("cache/wrongtrades.txt", 'a') as fp: # with open("cache/wrongtrades.txt", 'a') as fp:
# fp.write(str(data) + 'predchozi:'+str(ltp.price[symbol])+'\n') # fp.write(str(data) + 'predchozi:'+str(ltp.price[symbol])+'\n')
@ -128,7 +132,7 @@ class TradeAggregator:
if not is_open_hours(datetime.fromtimestamp(data['t'])) and self.exthours is False: if not is_open_hours(datetime.fromtimestamp(data['t'])) and self.exthours is False:
#print("AGG: trade not in open hours skipping", datetime.fromtimestamp(data['t']).astimezone(zoneNY)) #print("AGG: trade not in open hours skipping", datetime.fromtimestamp(data['t']).astimezone(zoneNY))
return 0 return []
#tady bude vzdycky posledni cena a posledni cas #tady bude vzdycky posledni cena a posledni cas
if self.update_ltp: if self.update_ltp:
@ -137,7 +141,7 @@ class TradeAggregator:
#if data['p'] < self.last_price - 0.02: print("zlo:",data) #if data['p'] < self.last_price - 0.02: print("zlo:",data)
if self.rectype == RecordType.TRADE: return data if self.rectype == RecordType.TRADE: return [data]
#print("agr přišel trade", datetime.fromtimestamp(data['t']),data) #print("agr přišel trade", datetime.fromtimestamp(data['t']),data)
@ -167,9 +171,45 @@ class TradeAggregator:
else: else:
self.newBar['confirmed'] = 1 self.newBar['confirmed'] = 1
self.newBar['vwap'] = self.vwaphelper / self.newBar['volume'] self.newBar['vwap'] = self.vwaphelper / self.newBar['volume']
#updatujeme čas - obsahuje datum tradu, který confirm triggeroval
self.newBar['updated'] = data['t']
#HACK pro update casu, který confirm triggeroval
#u CBARu v confirmnutem muze byt
# 1) no trades (pak potvrzujeme predchozi)
# 2) trades with same price , ktere zaroven timto flushujeme (v tomto pripade je cas updatu cas predchoziho tradu)
# variantu vyse pozname podle nastavene self.diff_price = True (mame trady a i ulozeny cas)
if self.rectype == RecordType.CBAR:
#UPDATE ať confirmace nenese zadna data, vsechny zmenena data jsou vyflusnute predtim
#pokud byly nejake trady
if self.diff_price is False:
#self.newBar['updated'] = self.lasttimestamp
#TODO tady bychom nejdriv vyflushnuly nekonfirmovany bar s trady
#a nasladne poslali prazdny confirmacni bar
self.preconfBar = deepcopy(self.newBar)
self.preconfBar['updated'] = self.lasttimestamp
self.preconfBar['confirmed'] = 0
#pridat do promenne
#else:
#NASTY HACK pro GUI
#zkousime potvrzeni baru dat o chlup mensi cas nez cas noveho baru, ktery jde hned za nim
#gui neumi zobrazit duplicity a v RT grafu nejde upravovat zpetne
#zarovname na cas baru podle timeframu(např. 5, 10, 15 ...) (ROUND)
if self.align:
t = datetime.fromtimestamp(data['t'])
t = t - timedelta(seconds=t.second % self.timeframe,microseconds=t.microsecond)
#nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
else:
#ulozime si jeho timestamp (odtum pocitame timeframe)
t = datetime.fromtimestamp(int(data['t']))
#self.newBar['updated'] = float(data['t']) - 0.001
self.newBar['updated'] = datetime.timestamp(t) - 0.000001
#PRO standardní BAR nechavame puvodni
else:
self.newBar['updated'] = data['t']
#ulozime datum akt.tradu pro mintick #ulozime datum akt.tradu pro mintick
self.lastBarConfirmed = True self.lastBarConfirmed = True
#ukládám si předchozí (confirmed)bar k vrácení #ukládám si předchozí (confirmed)bar k vrácení
@ -199,9 +239,9 @@ class TradeAggregator:
#je cena stejna od predchoziho tradu? pro nepotvrzeny cbar vracime jen pri zmene ceny #je cena stejna od predchoziho tradu? pro nepotvrzeny cbar vracime jen pri zmene ceny
if self.last_price == data['p']: if self.last_price == data['p']:
diff_price = False self.diff_price = False
else: else:
diff_price = True self.diff_price = True
self.last_price = data['p'] self.last_price = data['p']
#spočteme vwap - potřebujeme předchozí hodnoty #spočteme vwap - potřebujeme předchozí hodnoty
@ -216,6 +256,7 @@ class TradeAggregator:
self.newBar['hlcc4'] = round((self.newBar['high']+self.newBar['low']+self.newBar['close']+self.newBar['close'])/4,3) self.newBar['hlcc4'] = round((self.newBar['high']+self.newBar['low']+self.newBar['close']+self.newBar['close'])/4,3)
#predchozi bar byl v jine vterine, tzn. ukladame do noveho (aktualniho) pocatecni hodnoty #predchozi bar byl v jine vterine, tzn. ukladame do noveho (aktualniho) pocatecni hodnoty
#NEW BAR POPULATION
if (issamebar == False): if (issamebar == False):
#zaciname novy bar #zaciname novy bar
@ -249,14 +290,30 @@ class TradeAggregator:
#je tu maly bug pro CBAR - kdy prvni trade, který potvrzuje predchozi bar #je tu maly bug pro CBAR - kdy prvni trade, který potvrzuje predchozi bar
#odesle potvrzeni predchoziho baru a nikoliv open stávajícího, ten posle až druhý trade #odesle potvrzeni predchoziho baru a nikoliv open stávajícího, ten posle až druhý trade
#což asi nevadí #což asi nevadí
#OPRAVENO
#pokud je pripraveny, vracíme předchozí confirmed bar #pokud je pripraveny, vracíme předchozí confirmed bar PLUS NOVY, který ho triggeroval. pokud bylo
# pred confirmem nejake trady beze zmeny ceny flushujeme je take (preconfBar)
#predchozi bar muze obsahovat zmenena data
if len(self.returnBar) > 0: if len(self.returnBar) > 0:
self.tmp = self.returnBar return_set = []
self.returnBar = {} #pridame preconfirm bar pokud je
#print(self.tmp) if len(self.preconfBar)>0:
return self.tmp return_set.append(self.preconfBar)
self.preconfBar = {}
#pridame confirmation bar
return_set.append(self.returnBar)
#self.tmp = self.returnBar
self.returnBar = []
#doplnime prubezny vwap
self.newBar['vwap'] = self.vwaphelper / self.newBar['volume']
return_set.append(self.newBar)
#TODO pridat sem podporu pro mintick jako nize, tzn. pokud je v ochrannem okne, tak novy bar nevracet
#zatim je novy bar odesilan nehlede na mintick
#return_set = [self.tmp, self.newBar]
return return_set
#pro cont bar posilame ihned (TBD vwap a min bar tick value) #pro cont bar posilame ihned (TBD vwap a min bar tick value)
if self.rectype == RecordType.CBAR: if self.rectype == RecordType.CBAR:
@ -267,7 +324,7 @@ class TradeAggregator:
#pocatek noveho baru + Xs musi byt vetsi nez aktualni trade #pocatek noveho baru + Xs musi byt vetsi nez aktualni trade
if (self.newBar['time'] + timedelta(seconds=self.mintick)) > datetime.fromtimestamp(data['t']): if (self.newBar['time'] + timedelta(seconds=self.mintick)) > datetime.fromtimestamp(data['t']):
#print("waiting for mintick") #print("waiting for mintick")
return 0 return []
else: else:
self.lastBarConfirmed = False self.lastBarConfirmed = False
@ -276,12 +333,12 @@ class TradeAggregator:
#print(self.newBar) #print(self.newBar)
#pro (nepotvrzeny) cbar vracime jen pri zmene ceny #pro (nepotvrzeny) cbar vracime jen pri zmene ceny
if diff_price is True: if self.diff_price is True:
return self.newBar return [self.newBar]
else: else:
return 0 return []
else: else:
return 0 return []
class TradeAggregator2Queue(TradeAggregator): class TradeAggregator2Queue(TradeAggregator):
@ -297,15 +354,17 @@ class TradeAggregator2Queue(TradeAggregator):
async def ingest_trade(self, data): async def ingest_trade(self, data):
#print("ingest ve threadu:",current_thread().name) #print("ingest ve threadu:",current_thread().name)
res = await super().ingest_trade(data, self.symbol) res = await super().ingest_trade(data, self.symbol)
if res != 0:
#if len(res) > 0:
for obj in res:
#print(res) #print(res)
#pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií #pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií
if isinstance(res, dict): if isinstance(obj, dict):
copy = res.copy() copy = obj.copy()
else: else:
copy = res copy = obj
self.queue.put(copy) self.queue.put(copy)
res = {} res = []
#print("po insertu",res) #print("po insertu",res)
class TradeAggregator2List(TradeAggregator): class TradeAggregator2List(TradeAggregator):
@ -324,17 +383,17 @@ class TradeAggregator2List(TradeAggregator):
#print("ted vstoupil do tradeagg2list ingestu") #print("ted vstoupil do tradeagg2list ingestu")
res1 = await super().ingest_trade(data, self.symbol) res1 = await super().ingest_trade(data, self.symbol)
#print("ted je po zpracovani", res1) #print("ted je po zpracovani", res1)
if res1 != 0: for obj in res1:
#pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií #pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií
if isinstance(res1, dict): if isinstance(obj, dict):
copy = res1.copy() copy = obj.copy()
else: else:
copy = res1 copy = obj
if res1 == 'last': return 0 if obj == 'last': return []
self.btdata.append((copy['t'],copy['p'])) self.btdata.append((copy['t'],copy['p']))
# with open(self.debugfile, "a") as output: # with open(self.debugfile, "a") as output:
# output.write(str(copy['t']) + ' ' + str(datetime.fromtimestamp(copy['t']).astimezone(zoneNY)) + ' ' + str(copy['p']) + '\n') # output.write(str(copy['t']) + ' ' + str(datetime.fromtimestamp(copy['t']).astimezone(zoneNY)) + ' ' + str(copy['p']) + '\n')
res1 = {} res1 = []
#print("po insertu",res) #print("po insertu",res)

View File

@ -0,0 +1,341 @@
"""
Aggregator mdoule containing main aggregator logic for TRADES, BARS and CBAR
"""
from v2realbot.enums.enums import RecordType, StartBarAlign
from datetime import datetime, timedelta
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, Queue,is_open_hours,zoneNY
from queue import Queue
from rich import print
from v2realbot.enums.enums import Mode
import threading
from copy import deepcopy
from msgpack import unpackb
import os
from config import DATA_DIR
class TradeAggregator:
def __init__(self,
rectype: RecordType = RecordType.BAR,
timeframe: int = 5,
minsize: int = 100,
update_ltp: bool = False,
align: StartBarAlign = StartBarAlign.ROUND,
mintick: int = 0,
exthours: bool = False):
"""
Create trade agregator. Instance accepts trades one by one and process them and returns output type
Trade - return trade one by one (no change)
Bar - return finished bar in given timeframe
CBar - returns continuous bar, finished bar is marked by confirmed status
Args:
timeframe (number): Resolution of bar in seconds
update_ltp (bool): Whether to update global variable with price (usually only one instance does that)
align: Defines alignement of first bar. ROUND - according to timeframe( 5,10,15 - for 5s timeframe), RANDOM - according to timestamp of first trade
mintick: Applies for CBAR. Minimální mezera po potvrzeni baru a aktualizaci dalsiho nepotvrzeneho (např. pro 15s, muzeme chtit prvni tick po 5s). po teto dobe realtime.
"""
self.rectype: RecordType = rectype
self.timeframe = timeframe
self.minsize = minsize
self.update_ltp = update_ltp
self.exthours = exthours
if mintick >= timeframe:
print("Mintick musi byt mensi nez timeframe")
raise Exception
self.mintick = mintick
#class variables = starters
self.iterace = 1
self.lasttimestamp = 0
#inicalizace pro prvni agregaci
self.newBar = dict(high=0, low=999999, volume = 0, trades = 0, confirmed = 0, vwap = 0, close=0, index = 1, updated = 0)
self.bar_start = 0
self.align = align
self.tm: datetime = None
self.firstpass = True
self.vwaphelper = 0
self.returnBar = {}
self.lastBarConfirmed = False
#min trade size
self.minsize = minsize
#instance variable to hold last trade price
self.last_price = 0
self.barindex = 1
async def ingest_trade(self, indata, symbol):
"""
Aggregator logic for trade record
Args:
indata (dict): online or offline record
"""
data = unpackb(indata)
#last item signal
if data == "last": return data
#print(data)
##implementing fitlers - zatim natvrdo a jen tyto: size: 1, cond in [O,C,4] opening,closed a derivately priced,
## 22.3. - dal jsem pryc i contingency trades [' ', '7', 'V'] - nasel jsem obchod o 30c mimo
## dán pryč P - prior reference time + 25centu mimo, {'t': '2023-04-12T19:45:08.63257344Z', 'x': 'D', 'p': 28.68, 's': 1000, 'c': [' ', 'P'], 'i': 71693108525109, 'z': 'A'},
## Q - jsou v pohode, oteviraci trady, ale O jsou jejich duplikaty
## přidán W - average price trade, U - Extended hours - sold out of sequence
try:
for i in data['c']:
if i in ('C','O','4','B','7','V','P','W','U'): return 0
except KeyError:
pass
#EXPERIMENT zkusime vyhodit vsechny pod 50 #puv if int(data['s']) == 1: return 0
#zatim nechavame - výsledek je naprosto stejný jako v tradingview
if int(data['s']) < self.minsize: return 0
#{'t': 1678982075.242897, 'x': 'D', 'p': 29.1333, 's': 18000, 'c': [' ', '7', 'V'], 'i': 79372107591749, 'z': 'A', 'u': 'incorrect'}
if 'u' in data: return 0
#pokud projde TRADE s cenou 0.33% rozdilna oproti predchozi, pak vyhazujeme v ramci cisteni dat (cca 10ticku na 30USD)
pct_off = 0.33
##ic(ltp.price)
##ic(ltp.price[symbol])
try:
ltp.price[symbol]
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])
#nechavame zlo zatim projit
##return 0
# with open("cache/wrongtrades.txt", 'a') as fp:
# fp.write(str(data) + 'predchozi:'+str(ltp.price[symbol])+'\n')
#timestampy jsou v UTC
#TIMESTAMP format is different for online and offline trade streams
#offline trade
#{'t': '2023-02-17T14:30:00.16111744Z', 'x': 'J', 'p': 35.14, 's': 20, 'c': [' ', 'F', 'I'], 'i': 52983525027938, 'z': 'A'}
#websocket trade
#{'T': 't', 'S': 'MSFT', 'i': 372, 'x': 'V', 'p': 264.58, 's': 25, 'c': ['@', 'I'], 'z': 'C', 't': Timestamp(seconds=1678973696, nanoseconds=67312449), 'r': Timestamp(seconds=1678973696, nanoseconds=72865209)}
#parse alpaca timestamp
# tzn. na offline mohu pouzit >>> datetime.fromisoformat(d).timestamp() 1676644200.161117
#orizne sice nanosekundy ale to nevadi
#print("tady", self.mode, data['t'])
# if self.mode == Mode.BT:
# data['t'] = datetime.fromisoformat(str(data['t'])).timestamp()
# else:
data['t'] = parse_alpaca_timestamp(data['t'])
if not is_open_hours(datetime.fromtimestamp(data['t'])) and self.exthours is False:
#print("AGG: trade not in open hours skipping", datetime.fromtimestamp(data['t']).astimezone(zoneNY))
return 0
#tady bude vzdycky posledni cena a posledni cas
if self.update_ltp:
ltp.price[symbol] = data['p']
ltp.time[symbol] = data['t']
#if data['p'] < self.last_price - 0.02: print("zlo:",data)
if self.rectype == RecordType.TRADE: return data
#print("agr přišel trade", datetime.fromtimestamp(data['t']),data)
#OPIC pokud bude vadit, ze prvni bar neni kompletni - pak zapnout tuto opicarnu
#kddyz jde o prvni iteraci a pozadujeme align, cekame na kulaty cas (pro 5s 0,5,10..)
# if self.lasttimestamp ==0 and self.align:
# if self.firstpass:
# self.tm = datetime.fromtimestamp(data['t'])
# self.tm += timedelta(seconds=self.timeframe)
# self.tm = self.tm - timedelta(seconds=self.tm.second % self.timeframe,microseconds=self.tm.microsecond)
# self.firstpass = False
# print("trade: ",datetime.fromtimestamp(data['t']))
# print("required",self.tm)
# if self.tm > datetime.fromtimestamp(data['t']):
# return
# else: pass
#print("barstart",datetime.fromtimestamp(self.bar_start))
#print("oriznute data z tradu", datetime.fromtimestamp(int(data['t'])))
#print("timeframe",self.timeframe)
if int(data['t']) - self.bar_start < self.timeframe:
issamebar = True
else:
issamebar = False
##flush předchozí bar a incializace (krom prvni iterace)
if self.lasttimestamp ==0: pass
else:
self.newBar['confirmed'] = 1
self.newBar['vwap'] = self.vwaphelper / self.newBar['volume']
#updatujeme čas - obsahuje datum tradu, který confirm triggeroval
self.newBar['updated'] = data['t']
#ulozime datum akt.tradu pro mintick
self.lastBarConfirmed = True
#ukládám si předchozí (confirmed)bar k vrácení
self.returnBar = self.newBar
#print(self.returnBar)
#inicializuji pro nový bar
self.vwaphelper = 0
# return self.newBar
##flush CONFIRMED bar to queue
#self.q.put(self.newBar)
##TODO pridat prubezne odesilani pokud je pozadovano
self.barindex +=1
self.newBar = {
"close": 0,
"high": 0,
"low": 99999999,
"volume": 0,
"trades": 0,
"hlcc4": 0,
"confirmed": 0,
"updated": 0,
"vwap": 0,
"index": self.barindex
}
#je cena stejna od predchoziho tradu? pro nepotvrzeny cbar vracime jen pri zmene ceny
if self.last_price == data['p']:
diff_price = False
else:
diff_price = True
self.last_price = data['p']
#spočteme vwap - potřebujeme předchozí hodnoty
self.vwaphelper += (data['p'] * data['s'])
self.newBar['updated'] = data['t']
self.newBar['close'] = data['p']
self.newBar['high'] = max(self.newBar['high'],data['p'])
self.newBar['low'] = min(self.newBar['low'],data['p'])
self.newBar['volume'] = self.newBar['volume'] + data['s']
self.newBar['trades'] = self.newBar['trades'] + 1
#pohrat si s timto round
self.newBar['hlcc4'] = round((self.newBar['high']+self.newBar['low']+self.newBar['close']+self.newBar['close'])/4,3)
#predchozi bar byl v jine vterine, tzn. ukladame do noveho (aktualniho) pocatecni hodnoty
if (issamebar == False):
#zaciname novy bar
self.newBar['open'] = data['p']
#zarovname time prvniho baru podle timeframu kam patří (např. 5, 10, 15 ...) (ROUND)
if self.align:
t = datetime.fromtimestamp(data['t'])
t = t - timedelta(seconds=t.second % self.timeframe,microseconds=t.microsecond)
self.bar_start = datetime.timestamp(t)
#nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
else:
#ulozime si jeho timestamp (odtum pocitame timeframe)
t = datetime.fromtimestamp(int(data['t']))
#timestamp
self.bar_start = int(data['t'])
self.newBar['time'] = t
self.newBar['resolution'] = self.timeframe
self.newBar['confirmed'] = 0
#uložíme do předchozí hodnoty (poznáme tak open a close)
self.lasttimestamp = data['t']
self.iterace += 1
# print(self.iterace, data)
#je tu maly bug pro CBAR - kdy prvni trade, který potvrzuje predchozi bar
#odesle potvrzeni predchoziho baru a nikoliv open stávajícího, ten posle až druhý trade
#což asi nevadí
#pokud je pripraveny, vracíme předchozí confirmed bar
if len(self.returnBar) > 0:
self.tmp = self.returnBar
self.returnBar = {}
#print(self.tmp)
return self.tmp
#pro cont bar posilame ihned (TBD vwap a min bar tick value)
if self.rectype == RecordType.CBAR:
#pokud je mintick nastavený a předchozí bar byl potvrzený
if self.mintick != 0 and self.lastBarConfirmed:
#d zacatku noveho baru musi ubehnout x sekund nez posilame updazte
#pocatek noveho baru + Xs musi byt vetsi nez aktualni trade
if (self.newBar['time'] + timedelta(seconds=self.mintick)) > datetime.fromtimestamp(data['t']):
#print("waiting for mintick")
return 0
else:
self.lastBarConfirmed = False
#doplnime prubezny vwap
self.newBar['vwap'] = self.vwaphelper / self.newBar['volume']
#print(self.newBar)
#pro (nepotvrzeny) cbar vracime jen pri zmene ceny
if diff_price is True:
return self.newBar
else:
return 0
else:
return 0
class TradeAggregator2Queue(TradeAggregator):
"""
Child of TradeAggregator - sends items to given queue
In the future others will be added - TradeAggToTxT etc.
"""
def __init__(self, symbol: str, queue: Queue, rectype: RecordType = RecordType.BAR, timeframe: int = 5, minsize: int = 100, update_ltp: bool = False, align: StartBarAlign = StartBarAlign.ROUND, mintick: int = 0, exthours: bool = False):
super().__init__(rectype=rectype, timeframe=timeframe, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
self.queue = queue
self.symbol = symbol
async def ingest_trade(self, data):
#print("ingest ve threadu:",current_thread().name)
res = await super().ingest_trade(data, self.symbol)
if res != 0:
#print(res)
#pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií
if isinstance(res, dict):
copy = res.copy()
else:
copy = res
self.queue.put(copy)
res = {}
#print("po insertu",res)
class TradeAggregator2List(TradeAggregator):
""""
stores records to the list
"""
def __init__(self, symbol: str, btdata: list, rectype: RecordType = RecordType.BAR, timeframe: int = 5, minsize: int = 100, update_ltp: bool = False, align: StartBarAlign = StartBarAlign.ROUND, mintick: int = 0, exthours: bool = False):
super().__init__(rectype=rectype, timeframe=timeframe, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
self.btdata = btdata
self.symbol = symbol
# self.debugfile = DATA_DIR + "/BACprices.txt"
# if os.path.exists(self.debugfile):
# os.remove(self.debugfile)
async def ingest_trade(self, data):
#print("ted vstoupil do tradeagg2list ingestu")
res1 = await super().ingest_trade(data, self.symbol)
#print("ted je po zpracovani", res1)
if res1 != 0:
#pri rychlem plneni vetsiho dictionary se prepisovali - vyreseno kopií
if isinstance(res1, dict):
copy = res1.copy()
else:
copy = res1
if res1 == 'last': return 0
self.btdata.append((copy['t'],copy['p']))
# with open(self.debugfile, "a") as output:
# output.write(str(copy['t']) + ' ' + str(datetime.fromtimestamp(copy['t']).astimezone(zoneNY)) + ' ' + str(copy['p']) + '\n')
res1 = {}
#print("po insertu",res)

View File

@ -127,8 +127,6 @@ function transform_data(data) {
//pro jistotu jeste seradime podle casu //pro jistotu jeste seradime podle casu
//v BT se muze predbehnout a lightweight to pak nezobrazi //v BT se muze predbehnout a lightweight to pak nezobrazi
const sorter = (a, b) => a.time > b.time ? 1 : -1;
markers.sort(sorter) markers.sort(sorter)
markers_line.sort(sorter) markers_line.sort(sorter)
avgp_buy_line.sort(sorter) avgp_buy_line.sort(sorter)
@ -287,11 +285,17 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
//vybereme barvu pro kazdy identifikator //vybereme barvu pro kazdy identifikator
//zjistime typ idenitfikatoru - zatim right vs left //zjistime typ idenitfikatoru - zatim right vs left
function display_indicators(data) { function display_indicators(data) {
console.log("indikatory", JSON.stringify(data.indicators,null,2)) //console.log("indikatory", JSON.stringify(data.indicators,null,2))
//podobne v livewebsokcets.js - dat do jedne funkce //podobne v livewebsokcets.js - dat do jedne funkce
if (data.hasOwnProperty("indicators")) { if (data.hasOwnProperty("indicators")) {
// console.log("jsme uvnitr indikatoru") // console.log("jsme uvnitr indikatoru")
var indicators = data.indicators
//vraci se pole indicatoru, kazdy se svoji casovou osou (time) - nyni standard indikatory a cbar indikatory
var indicatorList = data.indicators
indicatorList.forEach((indicators, index, array) => {
//var indicators = data.indicators
//if there are indicators it means there must be at least two keys (time which is always present) //if there are indicators it means there must be at least two keys (time which is always present)
if (Object.keys(indicators).length > 1) { if (Object.keys(indicators).length > 1) {
for (const [key, value] of Object.entries(indicators)) { for (const [key, value] of Object.entries(indicators)) {
@ -306,20 +310,37 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
//INIT INDICATOR BASED on CONFIGURATION //INIT INDICATOR BASED on CONFIGURATION
//DO BUDOUCNA zde udelat sorter a pripadny handling duplicit jako
//funkci do ktere muzu zavolat vse co pujde jako data do chartu
//MOVE TO UTILS ro reuse?? //MOVE TO UTILS ro reuse??
if (conf && conf.display) { if (conf && conf.display) {
//tranform data do správného formátru //tranform data do správného formátru
items = [] items = []
//var last = null //var last = null
var last_time = 0
var time = 0
value.forEach((element, index, array) => { value.forEach((element, index, array) => {
item = {} item = {}
//debug //debug
//TOTO odstranit po identifikovani chyby //TOTO odstranit po identifikovani chyby
//if (indicators.time[index] !== undefined) { //if (indicators.time[index] !== undefined) {
//{console.log("problem",key,last)} //{console.log("problem",key,last)}
item["time"] = indicators.time[index] time = indicators.time[index]
if (time==last_time) {
//console.log(key, "problem v case - pousunuto o 0.001",time, last_time, element)
time += 0.000001
}
item["time"] = time
item["value"] = element item["value"] = element
last_time = time
if ((element == null) || (indicators.time[index] == null)) {
console.log("probelem u indikatoru",key, "nekonzistence", element, indicators.time[index])
}
//console.log("objekt indicatoru",item) //console.log("objekt indicatoru",item)
items.push(item) items.push(item)
//debug //debug
@ -389,7 +410,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
priceLineVisible: false, priceLineVisible: false,
}); });
//console.log("problem tu",items) //console.log("problem tu",JSON.stringify(items))
//add data //add data
obj.series.setData(items) obj.series.setData(items)
@ -399,6 +420,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
} }
} }
} }
})
} }
//display vwap and volume //display vwap and volume

View File

@ -5,6 +5,7 @@ var logcnt = 0
var positionsPriceLine = null var positionsPriceLine = null
var limitkaPriceLine = null var limitkaPriceLine = null
var angleSeries = 1 var angleSeries = 1
var cbar = false
//get details of runner to populate chart status //get details of runner to populate chart status
//fetch necessary - it could be initiated by manually inserting runnerId //fetch necessary - it could be initiated by manually inserting runnerId
@ -53,35 +54,21 @@ function connect(event) {
ws.onmessage = function(event) { ws.onmessage = function(event) {
var parsed_data = JSON.parse(event.data) var parsed_data = JSON.parse(event.data)
//console.log(JSON.stringify(parsed_data)) console.log(JSON.stringify(parsed_data))
//check received data and display lines // //check received data and display lines
if (parsed_data.hasOwnProperty("bars")) { // if (parsed_data.hasOwnProperty("bars")) {
var bar = parsed_data.bars // var bar = parsed_data.bars
candlestickSeries.update(bar); // candlestickSeries.update(bar);
volumeSeries.update({ // volumeSeries.update({
time: bar.time, // time: bar.time,
value: bar.volume // value: bar.volume
}); // });
vwapSeries.update({ // vwapSeries.update({
time: bar.time, // time: bar.time,
value: bar.vwap // value: bar.vwap
}); // });
} // }
if (parsed_data.hasOwnProperty("bars")) {
// console.log("mame bary")
var bar = parsed_data.bars
candlestickSeries.update(bar);
volumeSeries.update({
time: bar.time,
value: bar.volume
});
vwapSeries.update({
time: bar.time,
value: bar.vwap
});
}
//loglist //loglist
if (parsed_data.hasOwnProperty("iter_log")) { if (parsed_data.hasOwnProperty("iter_log")) {
@ -344,6 +331,52 @@ function connect(event) {
} }
} }
} }
if (parsed_data.hasOwnProperty("bars")) {
var bar = parsed_data.bars
//pokud jde o cbary, tak jako time bereme cas posledniho update
//aby se nam na grafu nepredbihaly cbar indikatory
//workaround pro identifikaci CBARU
//pokud se vyskytne unconfirmed bar = jde o CBARY - nastavena globalni promena
//standardni bar je vzdy potvrzeny
// if (bar.confirmed == 0) {
// cbar = true }
// //pozor CBARY zobrazujeme na konci platnosti baru, nikoliv dle TIME, ale UPDATED
// //kvuli navazovani prubeznych indikatoru na gui
// if (cbar) {
// // CBAR kreslime az po potvrzeni
// if (bar.confirmed == 1) {
// bar.time = bar.updated
// candlestickSeries.update(bar);
// volumeSeries.update({
// time: bar.time,
// value: bar.volume
// });
// vwapSeries.update({
// time: bar.time,
// value: bar.vwap
// });
// }
// }
// else {
// //time = bar.time
candlestickSeries.update(bar);
volumeSeries.update({
time: bar.time,
value: bar.volume
});
vwapSeries.update({
time: bar.time,
value: bar.vwap
});
//}
}
} }
ws.onclose = function(event) { ws.onclose = function(event) {
document.getElementById("status").textContent = "Disconnected from" + runnerId.value document.getElementById("status").textContent = "Disconnected from" + runnerId.value

View File

@ -9,6 +9,8 @@ var candlestickSeries = null
var volumeSeries = null var volumeSeries = null
var vwapSeries = null var vwapSeries = null
const sorter = (a, b) => a.time > b.time ? 1 : -1;
indConfig = {} indConfig = {}
settings = {} settings = {}
settings settings
@ -16,6 +18,7 @@ settings
indConfig = [ {name: "ema", titlevisible: false, embed: true, display: true, priceScaleId: "right", lastValueVisible: false}, indConfig = [ {name: "ema", titlevisible: false, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},
{name: "tick_volume", histogram: true, titlevisible: true, embed: true, display: true, priceScaleId: '', lastValueVisible: false}, {name: "tick_volume", histogram: true, titlevisible: true, embed: true, display: true, priceScaleId: '', lastValueVisible: false},
{name: "tick_price", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false}, {name: "tick_price", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},
{name: "ivwap", titlevisible: true, embed: true, display: false, priceScaleId: "right", lastValueVisible: false},
{name: "slope", titlevisible: true, embed: true, display: false, priceScaleId: "middle", lastValueVisible: false}, {name: "slope", titlevisible: true, embed: true, display: false, priceScaleId: "middle", lastValueVisible: false},
{name: "slopeMA", titlevisible: true, embed: true, display: true, priceScaleId: "middle", lastValueVisible: false}, {name: "slopeMA", titlevisible: true, embed: true, display: true, priceScaleId: "middle", lastValueVisible: false},
{name: "emaSlow", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false}, {name: "emaSlow", titlevisible: true, embed: true, display: true, priceScaleId: "right", lastValueVisible: false},

View File

@ -138,10 +138,11 @@ class Strategy:
def save_item_history(self,item): def save_item_history(self,item):
if self.rectype == RecordType.BAR: if self.rectype == RecordType.BAR:
#jako cas indikatorů pridavame cas baru a inicialni hodnoty vsech indikatoru #jako cas indikatorů pridavame cas baru a inicialni hodnoty vsech indikatoru
self.state.indicators['time'].append(item['time'])
for key in self.state.indicators: for key in self.state.indicators:
if key == 'time': if key == 'time':
continue self.state.indicators['time'].append(item['updated'])
else:
self.state.indicators[key].append(0) self.state.indicators[key].append(0)
self.append_bar(self.state.bars,item) self.append_bar(self.state.bars,item)
elif self.rectype == RecordType.TRADE: elif self.rectype == RecordType.TRADE:
@ -150,25 +151,42 @@ class Strategy:
#self.state.indicators['time'].append(datetime.fromtimestamp(self.state.last_trade_time)) #self.state.indicators['time'].append(datetime.fromtimestamp(self.state.last_trade_time))
#self.append_trade(self.state.trades,item) #self.append_trade(self.state.trades,item)
elif self.rectype == RecordType.CBAR: elif self.rectype == RecordType.CBAR:
#novy vzdy pridame
if self.nextnew: if self.nextnew:
self.state.indicators['time'].append(item['updated']) #standardni identifikatory - populace hist zaznamu pouze v novem baru (dale se deji jen udpaty)
for key in self.state.indicators: for key in self.state.indicators:
if key == 'time': if key == 'time':
continue self.state.indicators['time'].append(item['time'])
else:
self.state.indicators[key].append(0) self.state.indicators[key].append(0)
#cbar indikatory populace v kazde iteraci
for key in self.state.cbar_indicators:
if key == 'time':
self.state.cbar_indicators['time'].append(item['updated'])
else:
self.state.cbar_indicators[key].append(0)
#populujeme i novy bar v historii
self.append_bar(self.state.bars,item) self.append_bar(self.state.bars,item)
self.nextnew = 0 self.nextnew = 0
#nasledujici updatneme, po potvrzeni, nasleduje novy bar #nasledujici updatneme, po potvrzeni, nasleduje novy bar
#nasledujici identifikatory v ramci cbaru take pridavame
# (udrzujeme historii prubehu identifikatoru v ramci cbaru)
else: else:
#bary updatujeme, pridavame jen prvni
self.replace_prev_bar(self.state.bars,item)
#u cbar indikatoru, pridavame kazdou zmenu ceny, krome potvrzeneho baru
if item['confirmed'] == 0: if item['confirmed'] == 0:
self.state.indicators['time'][-1]=item['updated'] #v naslednych updatech baru inicializujeme vzdy jen cbar indikatory
self.replace_prev_bar(self.state.bars,item) for key in self.state.cbar_indicators:
#confirmed if key == 'time':
self.state.cbar_indicators['time'].append(item['updated'])
else: else:
self.state.indicators['time'][-1]=item['updated'] self.state.cbar_indicators[key].append(0)
self.replace_prev_bar(self.state.bars,item) else:
#pokud je potvrzeny, pak nenese nikdy zmenu ceny, nepridavame zaznam nic
self.nextnew = 1 self.nextnew = 1
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time""" """"refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
@ -400,6 +418,7 @@ class Strategy:
rt_out["bars"] = item rt_out["bars"] = item
else: else:
rt_out["trades"] = item rt_out["trades"] = item
#get only last values from indicators, if there are any indicators present #get only last values from indicators, if there are any indicators present
if len(self.state.indicators) > 0: if len(self.state.indicators) > 0:
rt_out["indicators"] = dict() rt_out["indicators"] = dict()
@ -410,6 +429,13 @@ class Strategy:
#zatim takto odchycene identifikatory, ktere nemaji list, ale dict - do budoucna predelat na samostatny typ "indicators_static" #zatim takto odchycene identifikatory, ktere nemaji list, ale dict - do budoucna predelat na samostatny typ "indicators_static"
except IndexError: except IndexError:
pass pass
#populate cbar indicators
if len(self.state.cbar_indicators) > 0:
for key, value in self.state.cbar_indicators.items():
try:
rt_out["indicators"][key]= value[-1]
except IndexError:
pass
#same for static indicators #same for static indicators
if len(self.state.statinds) > 0: if len(self.state.statinds) > 0:
@ -544,6 +570,7 @@ class StrategyState:
self.bars = AttributeDict(bars) self.bars = AttributeDict(bars)
self.trades = AttributeDict(trades) self.trades = AttributeDict(trades)
self.indicators = AttributeDict(time=[]) self.indicators = AttributeDict(time=[])
self.cbar_indicators = AttributeDict(time=[])
self.statinds = AttributeDict() self.statinds = AttributeDict()
#these methods can be overrided by StrategyType (to add or alter its functionality) #these methods can be overrided by StrategyType (to add or alter its functionality)
self.buy = self.interface.buy self.buy = self.interface.buy

View File

@ -131,6 +131,14 @@ def price2dec(price: float, decimals: int = 2) -> float:
""" """
return round(price,decimals) if count_decimals(price) > decimals else price return round(price,decimals) if count_decimals(price) > decimals else price
def round2five(price: float):
"""
zatim jen na 3 mista -pripadne predelat na dynamicky
z 23.342 - 23.340
z 23.346 - 23.345
"""
return (round(price*100*2)/2)/100
def count_decimals(number: float) -> int: def count_decimals(number: float) -> int:
""" """
Count the number of decimals in a given float: 1.4335 -> 4 or 3 -> 0 Count the number of decimals in a given float: 1.4335 -> 4 or 3 -> 0