From 6e775a33d9accf9d165d02820228e3b836da9cb4 Mon Sep 17 00:00:00 2001 From: David Brazda Date: Thu, 11 May 2023 17:30:41 +0200 Subject: [PATCH] bugfixes a pridana moznost z archivu mit json --- v2realbot/ENTRY_VykladaciTest.py | 487 ++++++++++++++++ v2realbot/ENTRY_Vykladaci_RSI.py | 521 ++++++++++++++++++ .../__pycache__/backtester.cpython-310.pyc | Bin 19082 -> 19082 bytes v2realbot/backtesting/backtester.py | 2 +- .../common/__pycache__/model.cpython-310.pyc | Bin 6392 -> 6494 bytes v2realbot/common/model.py | 3 + v2realbot/controller/services.py | 11 +- .../__pycache__/indicators.cpython-310.pyc | Bin 1289 -> 1010 bytes v2realbot/indicators/indicators.py | 12 +- v2realbot/indicators/moving_averages.py | 13 +- v2realbot/indicators/oscillators.py | 102 ++++ .../live_interface.cpython-310.pyc | Bin 5358 -> 5358 bytes v2realbot/interfaces/live_interface.py | 1 - v2realbot/static/index.html | 7 +- v2realbot/static/js/archivechart.js | 20 +- v2realbot/static/js/archivetables.js | 2 + v2realbot/static/js/mytables.js | 20 +- v2realbot/static/js/utils.js | 46 +- v2realbot/static/main.css | 2 +- .../utils/__pycache__/utils.cpython-310.pyc | Bin 8241 -> 8685 bytes v2realbot/utils/utils.py | 13 + 21 files changed, 1223 insertions(+), 39 deletions(-) create mode 100644 v2realbot/ENTRY_VykladaciTest.py create mode 100644 v2realbot/ENTRY_Vykladaci_RSI.py create mode 100644 v2realbot/indicators/oscillators.py diff --git a/v2realbot/ENTRY_VykladaciTest.py b/v2realbot/ENTRY_VykladaciTest.py new file mode 100644 index 0000000..4e97c81 --- /dev/null +++ b/v2realbot/ENTRY_VykladaciTest.py @@ -0,0 +1,487 @@ +import os,sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from v2realbot.strategy.base import StrategyState +from v2realbot.strategy.StrategyOrderLimitVykladaci import StrategyOrderLimitVykladaci +from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType +from v2realbot.indicators.indicators import ema +from v2realbot.indicators.oscillators import rsi, stochastic_oscillator, stochastic_rsi, absolute_price_oscillator, percentage_price_oscillator +from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get +from datetime import datetime +#from icecream import install, ic +#from rich import print +from threading import Event +from msgpack import packb, unpackb +import asyncio +import os +from traceback import format_exc + +def next(data, state: StrategyState): + print(10*"*","NEXT START",10*"*") + #ic(state.avgp, state.positions) + #ic(state.vars) + #ic(data) + + # + def is_defensive_mode(): + akt_pozic = int(state.positions) + max_pozic = int(state.vars.maxpozic) + def_mode_from = safe_get(state.vars, "def_mode_from") + if def_mode_from == None: def_mode_from = max_pozic/2 + if akt_pozic >= int(def_mode_from): + state.ilog(e=f"DEFENSIVE mode ACTIVE {state.vars.def_mode_from=}", msg=state.positions) + return True + else: + state.ilog(e=f"STANDARD mode ACTIVE {state.vars.def_mode_from=}", msg=state.positions) + return False + + def get_limitka_price(): + def_profit = safe_get(state.vars, "def_profit") + if def_profit == None: def_profit = state.vars.profit + if is_defensive_mode(): + return price2dec(float(state.avgp)+float(def_profit)) + else: + return price2dec(float(state.avgp)+float(state.vars.profit)) + + def consolidation(): + ##CONSOLIDATION PART - moved here, musí být před nákupem, jinak to dělalo nepořádek v pendingbuys + #docasne zkusime konzolidovat i kdyz neni vylozeno (aby se srovnala limitka ve vsech situacich) + if state.vars.jevylozeno == 1 or 1==1: + ##CONSOLIDATION PART kazdy Nty bar dle nastaveni + if int(data["index"])%int(state.vars.consolidation_bar_count) == 0: + print("***CONSOLIDATION ENTRY***") + state.ilog(e="CONSOLIDATION ENTRY ***") + + orderlist = state.interface.get_open_orders(symbol=state.symbol, side=None) + #pro jistotu jeste dotahneme aktualni pozice + state.avgp, state.positions = state.interface.pos() + + #print(orderlist) + pendingbuys_new = {} + limitka_old = state.vars.limitka + #print("Puvodni LIMITKA", limitka_old) + #zaciname s cistym stitem + state.vars.limitka = None + state.vars.limitka_price = None + limitka_found = False + limitka_qty = 0 + limitka_filled_qty = 0 + for o in orderlist: + if o.side == OrderSide.SELL: + + if limitka_found: + state.ilog(e="nalezeno vicero sell objednavek, bereme prvni, ostatni - rusime") + result=state.interface.cancel(o.id) + state.ilog(e="zrusena objednavka"+str(o.id), message=result) + continue + + #print("Nalezena LIMITKA") + limitka_found = True + state.vars.limitka = o.id + state.vars.limitka_price = o.limit_price + limitka_qty = int(o.qty) + limitka_filled_qty = int(o.filled_qty) + + #aktualni mnozstvi = puvodni minus filled + if limitka_filled_qty is not None: + print("prepocitavam filledmnozstvi od limitka_qty a filled_qty", limitka_qty, limitka_filled_qty) + limitka_qty = int(limitka_qty) - int(limitka_filled_qty) + ##TODO sem pridat upravu ceny + if o.side == OrderSide.BUY and o.order_type == OrderType.LIMIT: + pendingbuys_new[str(o.id)]=float(o.limit_price) + + state.ilog(e="Konzolidace limitky", msg=f"stejna:{(str(limitka_old)==str(state.vars.limitka))}", limitka_old=str(limitka_old), limitka_new=str(state.vars.limitka), limitka_new_price=state.vars.limitka_price, limitka_qty=limitka_qty, limitka_filled_qty=limitka_filled_qty) + + #pokud mame + + #neni limitka, ale mela by byt - vytváříme ji + if int(state.positions) > 0 and state.vars.limitka is None: + state.ilog(e="Limitka neni, ale mela by být.", msg=f"{state.positions=}") + price=get_limitka_price() + state.vars.limitka = asyncio.run(state.interface.sell_l(price=price, size=int(state.positions))) + state.vars.limitka_price = price + if state.vars.limitka == -1: + state.ilog(e="Vytvoreni limitky neprobehlo, vracime None", msg=f"{state.vars.limitka=}") + state.vars.limitka = None + state.vars.limitka_price = None + else: + state.ilog(e="Vytvořena nová limitka", limitka=str(state.vars.limitka), limtka_price=state.vars.limitka_price, qty=state.positions) + + #existuje a nesedi mnozstvi nebo cena + elif state.vars.limitka is not None and int(state.positions) > 0 and ((int(state.positions) != int(limitka_qty)) or float(state.vars.limitka_price) != float(get_limitka_price())): + #limitka existuje, ale spatne mnostvi - updatujeme + state.ilog(e=f"Limitka existuje, ale spatne mnozstvi nebo CENA - updatujeme", msg=f"{state.positions=} {limitka_qty=} {state.vars.limitka_price=}", nastavenacena=state.vars.limitka_price, spravna_cena=get_limitka_price(), pos=state.positions, limitka_qty=limitka_qty) + #snad to nespadne, kdyztak pridat exception handling + puvodni = state.vars.limitka + #TBD zde odchytit nejak result + state.vars.limitka = asyncio.run(state.interface.repl(price=get_limitka_price(), orderid=state.vars.limitka, size=int(state.positions))) + + if state.vars.limitka == -1: + state.ilog(e="Replace limitky neprobehl, vracime puvodni", msg=f"{state.vars.limitka=}", puvodni=puvodni) + state.vars.limitka = puvodni + else: + limitka_qty = int(state.positions) + state.ilog(e="Změněna limitka", limitka=str(state.vars.limitka), limitka_price=state.vars.limitka_price, limitka_qty=limitka_qty) + + #tbd pokud se bude vyskytovat pak pridat ještě konzolidaci ceny limitky + + if pendingbuys_new != state.vars.pendingbuys: + state.ilog(e="Rozdilna PB prepsana", pb_new=pendingbuys_new, pb_old = state.vars.pendingbuys) + print("ROZDILNA PENDINGBUYS přepsána") + print("OLD",state.vars.pendingbuys) + state.vars.pendingbuys = unpackb(packb(pendingbuys_new)) + print("NEW", state.vars.pendingbuys) + else: + print("PENDINGBUYS sedí - necháváme", state.vars.pendingbuys) + state.ilog(e="PB sedi nechavame", pb_new=pendingbuys_new, pb_old = state.vars.pendingbuys) + print("OLD jevylozeno", state.vars.jevylozeno) + if len(state.vars.pendingbuys) > 0: + state.vars.jevylozeno = 1 + else: + state.vars.jevylozeno = 0 + print("NEW jevylozeno", state.vars.jevylozeno) + state.ilog(e="Nove jevylozeno", msg=state.vars.jevylozeno) + + #print(limitka) + #print(pendingbuys_new) + #print(pendingbuys) + #print(len(pendingbuys)) + #print(len(pendingbuys_new)) + #print(jevylozeno) + print("***CONSOLIDATION EXIT***") + state.ilog(e="CONSOLIDATION EXIT ***") + else: + state.ilog(e="No time for consolidation", msg=data["index"]) + print("no time for consolidation", data["index"]) + + #mozna presunout o level vys + def vyloz(): + ##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci + #curve = [0.01, 0.01, 0, 0, 0.01, 0, 0, 0, 0.02, 0, 0, 0, 0.03, 0,0,0,0,0, 0.02, 0,0,0,0,0,0, 0.02] + curve = state.vars.curve + ##defenzivni krivka pro + curve_def = state.vars.curve_def + #vykladani po 5ti kusech, když zbývají 2 a méně, tak děláme nový výklad + vykladka = state.vars.vykladka + #kolik muzu max vylozit + kolikmuzu = int((int(state.vars.maxpozic) - int(state.positions))/int(state.vars.chunk)) + akt_pozic = int(state.positions) + max_pozic = int(state.vars.maxpozic) + + #mame polovinu a vic vylozeno, pouzivame defenzicni krivku + if is_defensive_mode(): + state.ilog(e="DEF: Pouzivame defenzivni krivku", akt_pozic=akt_pozic, max_pozic=max_pozic, curve_def=curve_def) + curve = curve_def + #zaroven docasne menime ticks2reset na defenzivni 0.06 + state.vars.ticks2reset = 0.06 + state.ilog(e="DEF: Menime tick2reset na 0.06", ticks2reset=state.vars.ticks2reset, ticks2reset_backup=state.vars.ticks2reset_backup) + else: + #vracime zpet, pokud bylo zmeneno + if state.vars.ticks2reset != state.vars.ticks2reset_backup: + state.vars.ticks2reset = state.vars.ticks2reset_backup + state.ilog(e="DEF: Menime tick2reset zpet na"+str(state.vars.ticks2reset), ticks2reset=state.vars.ticks2reset, ticks2reset_backup=state.vars.ticks2reset_backup) + + if kolikmuzu < vykladka: vykladka = kolikmuzu + + if len(curve) < vykladka: + vykladka = len(curve) + qty = int(state.vars.chunk) + last_price = price2dec(state.interface.get_last_price(state.symbol)) + #profit = float(state.vars.profit) + price = last_price + state.ilog(e="BUY Vykladame", msg=f"first price {price=} {vykladka=}", curve=curve, ema=state.indicators.ema[-1], trend=state.vars.Trend, price=price, vykladka=vykladka) + ##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci + + ##VAR - na zaklade conf. muzeme jako prvni posilat MARKET order + if safe_get(state.vars, "first_buy_market") == True: + #pri defenzivnim rezimu pouzivame vzdy LIMIT order + if is_defensive_mode(): + state.ilog(e="DEF mode on, odesilame jako prvni limitku") + state.buy_l(price=price, size=qty) + else: + state.ilog(e="Posilame jako prvni MARKET order") + state.buy(size=qty) + else: + state.buy_l(price=price, size=qty) + print("prvni limitka na aktuální cenu. Další podle křivky", price, qty) + for i in range(0,vykladka-1): + price = price2dec(float(price - curve[i])) + if price == last_price: + qty = qty + int(state.vars.chunk) + else: + state.buy_l(price=price, size=qty) + #print(i,"BUY limitka - delta",curve[i]," cena:", price, "mnozstvi:", qty) + qty = int(state.vars.chunk) + last_price = price + state.vars.blockbuy = 1 + state.vars.jevylozeno = 1 + + #CBAR protection, only 1x order per CBAR - then wait until another confirmed bar + if state.vars.blockbuy == 1 and state.rectype == RecordType.CBAR: + if state.bars.confirmed[-1] == 0: + print("OCHR: multibuy protection. waiting for next bar") + return 0 + # pop potvrzeni jeste jednou vratime (aby se nekoupilo znova, je stale ten stejny bar) + # a pak dalsi vejde az po minticku + else: + # pro vykladaci + state.vars.blockbuy = 0 + return 0 + + state.ilog(e="-----") + + #EMA INDICATOR - + #plnime MAcko - nyni posilame jen N poslednich hodnot + #zaroven osetrujeme pripady, kdy je malo dat a ukladame nulu + try: + ma = int(state.vars.MA) + #poslednich ma hodnot + source = state.bars.close[-ma:] #state.bars.vwap + ema_value = ema(source, ma) + state.indicators.ema.append(trunc(ema_value[-1],3)) + except Exception as e: + state.ilog(e="EMA ukladame 0", message=str(e)+format_exc()) + state.indicators.ema.append(0) + + #EMA SLOW + try: + ma = 100 + #poslednich ma hodnot + source = state.bars.hlcc4[-ma:] #state.bars.vwap + emaSlow_value = ema(source, ma) + emaSlow = trunc(emaSlow_value[-1],3) + state.indicators.emaSlow.append(emaSlow) + state.ilog(e=f"emaSlow {emaSlow=}", emaSlow=state.indicators.emaSlow[-5:]) + except Exception as e: + state.ilog(e=f"emaSlow {ma=} ukladame 0", message=str(e)+format_exc()) + state.indicators.emaSlow.append(0) + + + #EMA FAST + try: + ma = 20 + #poslednich ma hodnot + source = state.bars.hlcc4[-ma:] #state.bars.vwap + emaFast_value = ema(source, ma) + emaFast = trunc(emaFast_value[-1],3) + state.indicators.emaFast.append(emaFast) + state.ilog(e=f"emaFast {emaFast=}", emaFast=state.indicators.emaFast[-5:]) + except Exception as e: + state.ilog(e=f"emaFast {ma=} ukladame 0", message=str(e)+format_exc()) + state.indicators.emaFast.append(0) + + + #RSI14 + try: + rsi_length = 14 + #poslednich ma hodnot + source = state.bars.close #[-rsi_length:] #state.bars.vwap + rsi_res = rsi(source, rsi_length) + rsi_value = trunc(rsi_res[-1],3) + state.indicators.RSI14.append(rsi_value) + state.ilog(e=f"RSI {rsi_length=} {rsi_value=}", rsi_indicator=state.indicators.RSI14[-5:]) + except Exception as e: + state.ilog(e=f"RSI {rsi_length=} ukladame 0", message=str(e)+format_exc()) + state.indicators.RSI14.append(0) + + #stoch + try: + stoch_length = 14 + #poslednich ma hodnot + source_high = state.bars.high #[-rsi_length:] #state.bars.vwap + source_low = state.bars.low + source_close=state.bars.close + stoch_res1, stoch_res2 = stochastic_oscillator(source_high, source_low, source_close) + stoch_value1 = trunc(stoch_res1[-1],3) + stoch_value2 = trunc(stoch_res2[-1],3) + state.indicators.stoch1.append(stoch_value1) + state.indicators.stoch2.append(stoch_value2) + state.ilog(e=f"stoch {stoch_value1=} {stoch_value2=}", stoch1=state.indicators.stoch1[-5:], stoch2=state.indicators.stoch2[-5:]) + except Exception as e: + state.ilog(e=f"stoch ukladame 0", message=str(e)+format_exc()) + state.indicators.stoch1.append(0) + state.indicators.stoch2.append(0) + + + #absolute price oscillator + #meri mezeru mezi dvemi MACky (ato bud absolut price nebo percentage) + try: + short = 1 + long = 20 + #poslednich ma hodnot + source = state.bars.close #[-rsi_length:] #state.bars.vwap + apo_res = absolute_price_oscillator(source, short,long) + apo_value = trunc(apo_res[-1],3) + state.indicators.apo.append(apo_value) + state.ilog(e=f"apo {apo_value=}", apo_indicator=state.indicators.apo[-5:]) + except Exception as e: + state.ilog(e=f"apo ukladame 0", message=str(e)+format_exc()) + state.indicators.apo.append(0) + + #percentage price oscillator + try: + short = 1 + long = 20 + #poslednich ma hodnot + source = state.bars.close #[-rsi_length:] #state.bars.vwap + ppo_res = percentage_price_oscillator(source, short,long) + ppo_value = trunc(ppo_res[-1],3) + state.indicators.ppo.append(ppo_value) + state.ilog(e=f"poo {ppo_value=}", ppo_indicator=state.indicators.ppo[-5:]) + except Exception as e: + state.ilog(e=f"ppo ukladame 0", message=str(e)+format_exc()) + state.indicators.ppo.append(0) + + + #SLOPE INDICATOR + #úhel stoupání a klesání vyjádřený mezi -1 až 1 + #pravý bod přímky je aktuální cena, levý je průměr X(lookback offset) starších hodnot od slope_lookback. + #obsahuje statický indikátor (angle) pro vizualizaci + try: + slope = 99 + slope_lookback = int(state.vars.slope_lookback) + minimum_slope = float(state.vars.minimum_slope) + lookback_offset = int(state.vars.lookback_offset) + + if len(state.bars.close) > (slope_lookback + lookback_offset): + array_od = slope_lookback + lookback_offset + array_do = slope_lookback + lookbackprice_array = state.bars.vwap[-array_od:-array_do] + #obycejný prumer hodnot + lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3) + + #výpočet úhlu + slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100 + slope = round(slope, 4) + state.indicators.slope.append(slope) + + #angle je ze slope + state.statinds.angle = dict(time=state.bars.time[-1], price=state.bars.close[-1], lookbacktime=state.bars.time[-slope_lookback], lookbackprice=lookbackprice, minimum_slope=minimum_slope) + + #slope MA vyrovna vykyvy ve slope, dále pracujeme se slopeMA + slope_MA_length = 5 + source = state.indicators.slope[-slope_MA_length:] + slopeMAseries = ema(source, slope_MA_length) #state.bars.vwap + slopeMA = slopeMAseries[-1] + state.indicators.slopeMA.append(slopeMA) + + state.ilog(e=f"{slope=} {slopeMA=}", msg=f"{lookbackprice=}", lookbackoffset=lookback_offset, minimum_slope=minimum_slope, last_slopes=state.indicators.slope[-10:]) + + #dale pracujeme s timto MAckovanym slope + slope = slopeMA + else: + #pokud plnime historii musime ji plnit od zacatku, vsehcny idenitifkatory maji spolecny time + #kvuli spravnemu zobrazovani na gui + state.indicators.slope.append(0) + state.indicators.slopeMA.append(0) + state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback, slope=state.indicators.slope, slopeMA=state.indicators.slopeMA) + except Exception as e: + print("Exception in NEXT Slope Indicator section", str(e)) + state.ilog(e="EXCEPTION", msg="Exception in Slope Indicator section" + str(e) + format_exc()) + + print("is falling",isfalling(state.indicators.ema,state.vars.Trend)) + print("is rising",isrising(state.indicators.ema,state.vars.Trend)) + + consolidation() + + #HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru + lp = state.interface.get_last_price(symbol=state.symbol) + state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)} DEF:{str(is_defensive_mode())}", last_price=lp, data=data, stratvars=state.vars) + + #SLOPE ANGLE PROTECTIONs + #slope zachycuje rychle sestupy, pripadne zrusi nakupni objednavky + if slope < minimum_slope: # or slopeMA0: + print("CANCEL PENDINGBUYS") + #ic(state.vars.pendingbuys) + res = asyncio.run(state.cancel_pending_buys()) + #ic(state.vars.pendingbuys) + state.ilog(e="Rusime pendingbuyes", pb=state.vars.pendingbuys, res=res) + print("slope", slope) + print("min slope", minimum_slope) + + if state.vars.jevylozeno == 0: + print("Neni vylozeno, muzeme testovat nakup") + + #pokud je defenziva, buy triggeruje defenzivni def_trend + #TBD + + + if isfalling(state.indicators.ema,state.vars.Trend) and slope > minimum_slope: + vyloz() + + if state.vars.jevylozeno == 1: + #pokud mame vylozeno a cena je vetsi nez tick2reset + if len(state.vars.pendingbuys)>0: + maxprice = max(state.vars.pendingbuys.values()) + print("max cena v orderbuys", maxprice) + if state.interface.get_last_price(state.symbol) > float(maxprice) + float(state.vars.ticks2reset): + ##TODO toto nejak vymyslet - duplikovat? + res = asyncio.run(state.cancel_pending_buys()) + state.ilog(e=f"UJELO to. Rusime PB", msg=f"{state.vars.ticks2reset=}", pb=state.vars.pendingbuys) + + #PENDING BUYS SPENT - PART + #pokud mame vylozeno a pendingbuys se vyklepou a + # 1 vykladame idned znovu + # vyloz() + # 2 nebo - počkat zase na signál a pokračovat dál + # state.vars.blockbuy = 0 + # state.vars.jevylozeno = 0 + # 3 nebo - počkat na signál s enablovaným lastbuy indexem (tzn. počká nutně ještě pár barů) + #podle BT vyhodnejsi vylozit ihned + if len(state.vars.pendingbuys) == 0: + state.vars.blockbuy = 0 + state.vars.jevylozeno = 0 + 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*"*") + +def init(state: StrategyState): + #place to declare new vars + print("INIT v main",state.name) + state.indicators['ema'] = [] + state.indicators['slope'] = [] + state.indicators['slopeMA'] = [] + state.indicators['emaSlow'] = [] + state.indicators['emaFast'] = [] + state.indicators['RSI14'] = [] + state.indicators['stoch1'] = [] + state.indicators['stoch2'] = [] + state.indicators['apo'] = [] + state.indicators['ppo'] = [] + #static indicators - those not series based + state.statinds['angle'] = dict(minimum_slope=state.vars["minimum_slope"]) + state.vars["ticks2reset_backup"] = state.vars.ticks2reset + + \ No newline at end of file diff --git a/v2realbot/ENTRY_Vykladaci_RSI.py b/v2realbot/ENTRY_Vykladaci_RSI.py new file mode 100644 index 0000000..ae39f3a --- /dev/null +++ b/v2realbot/ENTRY_Vykladaci_RSI.py @@ -0,0 +1,521 @@ +import os,sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from v2realbot.strategy.base import StrategyState +from v2realbot.strategy.StrategyOrderLimitVykladaci import StrategyOrderLimitVykladaci +from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType +from v2realbot.indicators.indicators import ema +from v2realbot.indicators.oscillators import rsi +from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get +from datetime import datetime +#from icecream import install, ic +#from rich import print +from threading import Event +from msgpack import packb, unpackb +import asyncio +import os +from traceback import format_exc + +print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +"""" +Kope Vykladaci strategie s navic pridanym RSI + +- pokud RSI > 50 + - rusime pendingbuye + - netriggerujeme buy + +""" +stratvars = AttributeDict(maxpozic = 400, + def_mode_from = 200, + chunk = 10, + MA = 2, + Trend = 2, + profit = 0.02, + def_profit = 0.01, + lastbuyindex=-6, + pendingbuys={}, + limitka = None, + limitka_price = None, + jevylozeno=0, + vykladka=5, + curve = [0.01, 0.01, 0.01, 0, 0.02, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01], + curve_def = [0.02, 0.02, 0.02, 0, 0, 0.02, 0, 0, 0, 0.02], + blockbuy = 0, + ticks2reset = 0.04, + consolidation_bar_count = 10, + slope_lookback = 300, + lookback_offset = 20, + minimum_slope = -0.05, + first_buy_market = False + ) +##toto rozparsovat a strategii spustit stejne jako v main +toml_string = """ +[[strategies]] +name = "V1 na BAC" +symbol = "BAC" +script = "ENTRY_backtest_strategyVykladaci" +class = "StrategyOrderLimitVykladaci" +open_rush = 0 +close_rush = 0 +[strategies.stratvars] +maxpozic = 200 +chunk = 10 +MA = 6 +Trend = 5 +profit = 0.02 +lastbuyindex=-6 +pendingbuys={} +limitka = "None" +jevylozeno=0 +vykladka=5 +curve = [0.01, 0.01, 0.01,0.01, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01] +blockbuy = 0 +ticks2reset = 0.04 +[[strategies.add_data]] +symbol="BAC" +rectype="bar" +timeframe=5 +update_ltp=true +align="round" +mintick=0 +minsize=100 +exthours=false +""" + +def next(data, state: StrategyState): + print(10*"*","NEXT START",10*"*") + #ic(state.avgp, state.positions) + #ic(state.vars) + #ic(data) + + # + def is_defensive_mode(): + akt_pozic = int(state.positions) + max_pozic = int(state.vars.maxpozic) + def_mode_from = safe_get(state.vars, "def_mode_from") + if def_mode_from == None: def_mode_from = max_pozic/2 + if akt_pozic >= int(def_mode_from): + state.ilog(e=f"DEFENSIVE mode ACTIVE {state.vars.def_mode_from=}", msg=state.positions) + return True + else: + state.ilog(e=f"STANDARD mode ACTIVE {state.vars.def_mode_from=}", msg=state.positions) + return False + + def get_limitka_price(): + def_profit = safe_get(state.vars, "def_profit") + if def_profit == None: def_profit = state.vars.profit + if is_defensive_mode(): + return price2dec(float(state.avgp)+float(def_profit)) + else: + return price2dec(float(state.avgp)+float(state.vars.profit)) + + def consolidation(): + ##CONSOLIDATION PART - moved here, musí být před nákupem, jinak to dělalo nepořádek v pendingbuys + #docasne zkusime konzolidovat i kdyz neni vylozeno (aby se srovnala limitka ve vsech situacich) + if state.vars.jevylozeno == 1 or 1==1: + ##CONSOLIDATION PART kazdy Nty bar dle nastaveni + if int(data["index"])%int(state.vars.consolidation_bar_count) == 0: + print("***CONSOLIDATION ENTRY***") + state.ilog(e="CONSOLIDATION ENTRY ***") + + orderlist = state.interface.get_open_orders(symbol=state.symbol, side=None) + #pro jistotu jeste dotahneme aktualni pozice + state.avgp, state.positions = state.interface.pos() + + #print(orderlist) + pendingbuys_new = {} + limitka_old = state.vars.limitka + #print("Puvodni LIMITKA", limitka_old) + #zaciname s cistym stitem + state.vars.limitka = None + state.vars.limitka_price = None + limitka_found = False + limitka_qty = 0 + limitka_filled_qty = 0 + for o in orderlist: + if o.side == OrderSide.SELL: + + if limitka_found: + state.ilog(e="nalezeno vicero sell objednavek, bereme prvni, ostatni - rusime") + result=state.interface.cancel(o.id) + state.ilog(e="zrusena objednavka"+str(o.id), message=result) + continue + + #print("Nalezena LIMITKA") + limitka_found = True + state.vars.limitka = o.id + state.vars.limitka_price = o.limit_price + limitka_qty = int(o.qty) + limitka_filled_qty = int(o.filled_qty) + + #aktualni mnozstvi = puvodni minus filled + if limitka_filled_qty is not None: + print("prepocitavam filledmnozstvi od limitka_qty a filled_qty", limitka_qty, limitka_filled_qty) + limitka_qty = int(limitka_qty) - int(limitka_filled_qty) + ##TODO sem pridat upravu ceny + if o.side == OrderSide.BUY and o.order_type == OrderType.LIMIT: + pendingbuys_new[str(o.id)]=float(o.limit_price) + + state.ilog(e="Konzolidace limitky", msg=f"stejna:{(str(limitka_old)==str(state.vars.limitka))}", limitka_old=str(limitka_old), limitka_new=str(state.vars.limitka), limitka_new_price=state.vars.limitka_price, limitka_qty=limitka_qty, limitka_filled_qty=limitka_filled_qty) + + #pokud mame + + #neni limitka, ale mela by byt - vytváříme ji + if int(state.positions) > 0 and state.vars.limitka is None: + state.ilog(e="Limitka neni, ale mela by být.", msg=f"{state.positions=}") + price=get_limitka_price() + state.vars.limitka = asyncio.run(state.interface.sell_l(price=price, size=int(state.positions))) + state.vars.limitka_price = price + if state.vars.limitka == -1: + state.ilog(e="Vytvoreni limitky neprobehlo, vracime None", msg=f"{state.vars.limitka=}") + state.vars.limitka = None + state.vars.limitka_price = None + else: + state.ilog(e="Vytvořena nová limitka", limitka=str(state.vars.limitka), limtka_price=state.vars.limitka_price, qty=state.positions) + + #existuje a nesedi mnozstvi nebo cena + elif state.vars.limitka is not None and int(state.positions) > 0 and ((int(state.positions) != int(limitka_qty)) or float(state.vars.limitka_price) != float(get_limitka_price())): + #limitka existuje, ale spatne mnostvi - updatujeme + state.ilog(e=f"Limitka existuje, ale spatne mnozstvi nebo CENA - updatujeme", msg=f"{state.positions=} {limitka_qty=} {state.vars.limitka_price=}", nastavenacena=state.vars.limitka_price, spravna_cena=get_limitka_price(), pos=state.positions, limitka_qty=limitka_qty) + #snad to nespadne, kdyztak pridat exception handling + puvodni = state.vars.limitka + #TBD zde odchytit nejak result + state.vars.limitka = asyncio.run(state.interface.repl(price=get_limitka_price(), orderid=state.vars.limitka, size=int(state.positions))) + + if state.vars.limitka == -1: + state.ilog(e="Replace limitky neprobehl, vracime puvodni", msg=f"{state.vars.limitka=}", puvodni=puvodni) + state.vars.limitka = puvodni + else: + limitka_qty = int(state.positions) + state.ilog(e="Změněna limitka", limitka=str(state.vars.limitka), limitka_price=state.vars.limitka_price, limitka_qty=limitka_qty) + + #tbd pokud se bude vyskytovat pak pridat ještě konzolidaci ceny limitky + + if pendingbuys_new != state.vars.pendingbuys: + state.ilog(e="Rozdilna PB prepsana", pb_new=pendingbuys_new, pb_old = state.vars.pendingbuys) + print("ROZDILNA PENDINGBUYS přepsána") + print("OLD",state.vars.pendingbuys) + state.vars.pendingbuys = unpackb(packb(pendingbuys_new)) + print("NEW", state.vars.pendingbuys) + else: + print("PENDINGBUYS sedí - necháváme", state.vars.pendingbuys) + state.ilog(e="PB sedi nechavame", pb_new=pendingbuys_new, pb_old = state.vars.pendingbuys) + print("OLD jevylozeno", state.vars.jevylozeno) + if len(state.vars.pendingbuys) > 0: + state.vars.jevylozeno = 1 + else: + state.vars.jevylozeno = 0 + print("NEW jevylozeno", state.vars.jevylozeno) + state.ilog(e="Nove jevylozeno", msg=state.vars.jevylozeno) + + #print(limitka) + #print(pendingbuys_new) + #print(pendingbuys) + #print(len(pendingbuys)) + #print(len(pendingbuys_new)) + #print(jevylozeno) + print("***CONSOLIDATION EXIT***") + state.ilog(e="CONSOLIDATION EXIT ***") + else: + state.ilog(e="No time for consolidation", msg=data["index"]) + print("no time for consolidation", data["index"]) + + #mozna presunout o level vys + def vyloz(): + ##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci + #curve = [0.01, 0.01, 0, 0, 0.01, 0, 0, 0, 0.02, 0, 0, 0, 0.03, 0,0,0,0,0, 0.02, 0,0,0,0,0,0, 0.02] + curve = state.vars.curve + ##defenzivni krivka pro + curve_def = state.vars.curve_def + #vykladani po 5ti kusech, když zbývají 2 a méně, tak děláme nový výklad + vykladka = state.vars.vykladka + #kolik muzu max vylozit + kolikmuzu = int((int(state.vars.maxpozic) - int(state.positions))/int(state.vars.chunk)) + akt_pozic = int(state.positions) + max_pozic = int(state.vars.maxpozic) + + #mame polovinu a vic vylozeno, pouzivame defenzicni krivku + if is_defensive_mode(): + state.ilog(e="DEF: Pouzivame defenzivni krivku", akt_pozic=akt_pozic, max_pozic=max_pozic, curve_def=curve_def) + curve = curve_def + #zaroven docasne menime ticks2reset na defenzivni 0.06 + state.vars.ticks2reset = 0.06 + state.ilog(e="DEF: Menime tick2reset na 0.06", ticks2reset=state.vars.ticks2reset, ticks2reset_backup=state.vars.ticks2reset_backup) + else: + #vracime zpet, pokud bylo zmeneno + if state.vars.ticks2reset != state.vars.ticks2reset_backup: + state.vars.ticks2reset = state.vars.ticks2reset_backup + state.ilog(e="DEF: Menime tick2reset zpet na"+str(state.vars.ticks2reset), ticks2reset=state.vars.ticks2reset, ticks2reset_backup=state.vars.ticks2reset_backup) + + if kolikmuzu < vykladka: vykladka = kolikmuzu + + if len(curve) < vykladka: + vykladka = len(curve) + qty = int(state.vars.chunk) + last_price = price2dec(state.interface.get_last_price(state.symbol)) + #profit = float(state.vars.profit) + price = last_price + state.ilog(e="BUY Vykladame", msg=f"first price {price=} {vykladka=}", curve=curve, ema=state.indicators.ema[-1], trend=state.vars.Trend, price=price, vykladka=vykladka) + ##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci + + ##VAR - na zaklade conf. muzeme jako prvni posilat MARKET order + if safe_get(state.vars, "first_buy_market") == True: + #pri defenzivnim rezimu pouzivame vzdy LIMIT order + if is_defensive_mode(): + state.ilog(e="DEF mode on, odesilame jako prvni limitku") + state.buy_l(price=price, size=qty) + else: + state.ilog(e="Posilame jako prvni MARKET order") + state.buy(size=qty) + else: + state.buy_l(price=price, size=qty) + print("prvni limitka na aktuální cenu. Další podle křivky", price, qty) + for i in range(0,vykladka-1): + price = price2dec(float(price - curve[i])) + if price == last_price: + qty = qty + int(state.vars.chunk) + else: + state.buy_l(price=price, size=qty) + #print(i,"BUY limitka - delta",curve[i]," cena:", price, "mnozstvi:", qty) + qty = int(state.vars.chunk) + last_price = price + state.vars.blockbuy = 1 + state.vars.jevylozeno = 1 + + #CBAR protection, only 1x order per CBAR - then wait until another confirmed bar + if state.vars.blockbuy == 1 and state.rectype == RecordType.CBAR: + if state.bars.confirmed[-1] == 0: + print("OCHR: multibuy protection. waiting for next bar") + return 0 + # pop potvrzeni jeste jednou vratime (aby se nekoupilo znova, je stale ten stejny bar) + # a pak dalsi vejde az po minticku + else: + # pro vykladaci + state.vars.blockbuy = 0 + return 0 + + state.ilog(e="-----") + + #EMA INDICATOR - + #plnime MAcko - nyni posilame jen N poslednich hodnot + #zaroven osetrujeme pripady, kdy je malo dat a ukladame nulu + try: + ma = int(state.vars.MA) + #poslednich ma hodnot + source = state.bars.close[-ma:] #state.bars.vwap + ema_value = ema(source, ma) + state.indicators.ema.append(trunc(ema_value[-1],3)) + except Exception as e: + state.ilog(e="EMA ukladame 0", message=str(e)+format_exc()) + state.indicators.ema.append(0) + + #RSI14 INDICATOR + try: + ##mame v atributech nastaveni? + rsi_dont_buy_above = safe_get(state.vars, "rsi_dont_buy_above") + if rsi_dont_buy_above == None: + rsi_dont_buy_above = 50 + rsi_buy_signal = False + rsi_dont_buy = False + rsi_length = 14 + source = state.bars.close #[-rsi_length:] #state.bars.vwap + rsi_res = rsi(source, rsi_length) + rsi_value = trunc(rsi_res[-1],3) + state.indicators.RSI14.append(rsi_value) + rsi_dont_buy = rsi_value > rsi_dont_buy_above + rsi_buy_signal = rsi_value < 40 + state.ilog(e=f"RSI {rsi_length=} {rsi_value=} {rsi_dont_buy=} {rsi_buy_signal=}", rsi_indicator=state.indicators.RSI14[-5:]) + except Exception as e: + state.ilog(e=f"RSI {rsi_length=} ukladame 0", message=str(e)+format_exc()) + state.indicators.RSI14.append(0) + + + #SLOPE INDICATOR + #úhel stoupání a klesání vyjádřený mezi -1 až 1 + #pravý bod přímky je aktuální cena, levý je průměr X(lookback offset) starších hodnot od slope_lookback. + #obsahuje statický indikátor (angle) pro vizualizaci + try: + slope = 99 + slope_lookback = int(state.vars.slope_lookback) + minimum_slope = float(state.vars.minimum_slope) + lookback_offset = int(state.vars.lookback_offset) + + if len(state.bars.close) > (slope_lookback + lookback_offset): + array_od = slope_lookback + lookback_offset + array_do = slope_lookback + lookbackprice_array = state.bars.vwap[-array_od:-array_do] + #obycejný prumer hodnot + lookbackprice = round(sum(lookbackprice_array)/lookback_offset,3) + + #výpočet úhlu + slope = ((state.bars.close[-1] - lookbackprice)/lookbackprice)*100 + slope = round(slope, 4) + state.indicators.slope.append(slope) + + #angle je ze slope + state.statinds.angle = dict(time=state.bars.time[-1], price=state.bars.close[-1], lookbacktime=state.bars.time[-slope_lookback], lookbackprice=lookbackprice, minimum_slope=minimum_slope) + + #slope MA vyrovna vykyvy ve slope, dále pracujeme se slopeMA + slope_MA_length = 5 + source = state.indicators.slope[-slope_MA_length:] + slopeMAseries = ema(source, slope_MA_length) #state.bars.vwap + slopeMA = slopeMAseries[-1] + state.indicators.slopeMA.append(slopeMA) + + state.ilog(e=f"{slope=} {slopeMA=}", msg=f"{lookbackprice=}", lookbackoffset=lookback_offset, minimum_slope=minimum_slope, last_slopes=state.indicators.slope[-10:], last_slopesMA=state.indicators.slopeMA[-10:]) + + #dale pracujeme s timto MAckovanym slope + slope = slopeMA + else: + #pokud plnime historii musime ji plnit od zacatku, vsehcny idenitifkatory maji spolecny time + #kvuli spravnemu zobrazovani na gui + state.indicators.slope.append(0) + state.indicators.slopeMA.append(0) + state.ilog(e="Slope - not enough data", slope_lookback=slope_lookback, slope=state.indicators.slope, slopeMA=state.indicators.slopeMA) + except Exception as e: + print("Exception in NEXT Slope Indicator section", str(e)) + state.ilog(e="EXCEPTION", msg="Exception in Slope Indicator section" + str(e) + format_exc()) + + print("is falling",isfalling(state.indicators.ema,state.vars.Trend)) + print("is rising",isrising(state.indicators.ema,state.vars.Trend)) + + consolidation() + + #HLAVNI ITERACNI LOG JESTE PRED AKCI - obsahuje aktualni hodnoty vetsiny parametru + #TODO sem pridat aktualni hodnoty vsech indikatoru + lp = state.interface.get_last_price(symbol=state.symbol) + state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)} DEF:{str(is_defensive_mode())}", last_price=lp, data=data, stratvars=state.vars) + + #SLOPE ANGLE PROTECTIONs + #slope zachycuje rychle sestupy, pripadne zrusi nakupni objednavky + if slope < minimum_slope or rsi_dont_buy: # or slopeMA0: + print("CANCEL PENDINGBUYS") + #ic(state.vars.pendingbuys) + res = asyncio.run(state.cancel_pending_buys()) + #ic(state.vars.pendingbuys) + state.ilog(e="Rusime pendingbuyes", pb=state.vars.pendingbuys, res=res) + print("slope", slope) + print("min slope", minimum_slope) + + if state.vars.jevylozeno == 0: + print("Neni vylozeno, muzeme testovat nakup") + + #pokud je defenziva, buy triggeruje defenzivni def_trend + #TBD + + #NOVY BUY SIGNAL z RSI < 35 + #buy_signal = isfalling(state.indicators.ema,state.vars.Trend) + buy_signal = rsi_buy_signal + + if buy_signal and slope > minimum_slope and not rsi_dont_buy: + vyloz() + + ## testuje aktualni cenu od nejvyssi visici limitky + ##toto spoustet jednou za X iterací - ted to jede pokazdé + #pokud to ujede o vic, rusime limitky + #TODO: zvazit jestli nechat i pri otevrenych pozicich, zatim nechavame + #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 + if state.vars.jevylozeno == 1: + #pokud mame vylozeno a cena je vetsi nez tick2reset + if len(state.vars.pendingbuys)>0: + maxprice = max(state.vars.pendingbuys.values()) + print("max cena v orderbuys", maxprice) + if state.interface.get_last_price(state.symbol) > float(maxprice) + float(state.vars.ticks2reset): + ##TODO toto nejak vymyslet - duplikovat? + res = asyncio.run(state.cancel_pending_buys()) + state.ilog(e=f"UJELO to. Rusime PB", msg=f"{state.vars.ticks2reset=}", pb=state.vars.pendingbuys) + + #PENDING BUYS SPENT - PART + #pokud mame vylozeno a pendingbuys se vyklepou a + # 1 vykladame idned znovu + # vyloz() + # 2 nebo - počkat zase na signál a pokračovat dál + # state.vars.blockbuy = 0 + # state.vars.jevylozeno = 0 + # 3 nebo - počkat na signál s enablovaným lastbuy indexem (tzn. počká nutně ještě pár barů) + #podle BT vyhodnejsi vylozit ihned + if len(state.vars.pendingbuys) == 0: + state.vars.blockbuy = 0 + state.vars.jevylozeno = 0 + 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*"*") + +def init(state: StrategyState): + #place to declare new vars + print("INIT v main",state.name) + state.indicators['ema'] = [] + state.indicators['slope'] = [] + state.indicators['slopeMA'] = [] + state.indicators['RSI14'] = [] + #static indicators - those not series based + state.statinds['angle'] = dict(minimum_slope=state.vars["minimum_slope"]) + state.vars["ticks2reset_backup"] = state.vars.ticks2reset + +def main(): + # try: + # strat_settings = tomli.loads("]] this is invalid TOML [[") + # except tomli.TOMLDecodeError: + # print("Yep, definitely not valid.") + + #strat_settings = dict_replace_value(strat_settings, "None", None) + + name = os.path.basename(__file__) + se = Event() + pe = Event() + s = StrategyOrderLimitVykladaci(name = name, symbol = "BAC", account=Account.ACCOUNT1, next=next, init=init, stratvars=stratvars, open_rush=10, close_rush=0, pe=pe, se=se, ilog_save=True) + s.set_mode(mode = Mode.BT, + debug = False, + start = datetime(2023, 4, 14, 10, 42, 0, 0, tzinfo=zoneNY), + end = datetime(2023, 4, 14, 14, 35, 0, 0, tzinfo=zoneNY), + cash=100000) + + #na sekundovem baru nezaokrouhlovat MAcko + s.add_data(symbol="BAC",rectype=RecordType.BAR,timeframe=2,minsize=100,update_ltp=True,align=StartBarAlign.ROUND,mintick=0, exthours=False) + #s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0) + + s.start() + print("zastavujeme") + +if __name__ == "__main__": + main() + + + + + \ No newline at end of file diff --git a/v2realbot/backtesting/__pycache__/backtester.cpython-310.pyc b/v2realbot/backtesting/__pycache__/backtester.cpython-310.pyc index c64b33f3dece88777fe68672289e34aeb0f83180..02bb5685a898f7c450239fe3b10cbb8f9690253a 100644 GIT binary patch delta 2534 zcmZ`)S!`5Q80O5<&d^R+yO7qVP`05^=)SMhg<@NZK#>6*Z)fh%t9R~9O0wrjI;}#W(8^jPX8hp8G)CZ%H7>!TF2jltv3(VN)Jbd$?e>vx0zH{!( zaW-?D`Qkp`{CV;hJhvxu^@8sY4-1OFJuk89)Jboti`9usz6Mq)Zu%~FRY^BqC&9`N zk157JH5qi9@rbGiy_ykG4}?@B!W&?B$9Y8M7E>&y@oIz-)I7W38CIVrCloFyoLxJ2TVB-|kEvWSRo)FtE}#HrS8wO` z9hPhRz}8(n58BPpF4nb}W`?M=P&^t^_)-Og2u4nCrST0%$V};0v zDz$Q=ny!b;c*uyG()eLv$x1rIcE*-Yt|sShsHL3CK4yCwrG3h0s8fcb#FWycCXXJL zI)9FxJ=JliYkUt*4FJLb)FzwMGcZ8?p-%Z-+%8cpFUnsyr;KQ-uV{cR!>N7;FTx&c zAZOVz<>Gwts+^1%@ojO}A{=kIlG>DN4|F88@ve(bHY|QC9c4{oc=J|c z$x@1=Sayrb6?xMSKnw!%0bqb#O9IkT099HEiFTmb^DaSx<60B%O+^u(gF6Ra)u zTG>%1d(gUS?{qUfD0K=*=ao*W11=K)Du?n%P5x{VA zh9gx(ENWh6;K_AQIQGpG%ge>KDH%E>YoaTg&V@pdhCSO}9N@NhOAk8^``oNj?)7WE z(;YC;Cge`@O4CAx6)wGgX1O9fo{+caF(~nL{bwBYcPFg70K}<#??j+UQ#m7wKCYIk zoX5R-WkeMmObncLSdNNK77J(cQFJ$-V^50To9~w~LoIJ_I0N^yw`2J19f?R& zgXMV%74quxS4CCpE9_};y>&(N1*!DQ6SOE#Nl_l=xm%s=kptjXRy59IiaG1x7ZFbd zW^%ByZK~x1SS|rR0(=U{@WJx3gdonnA_dD69@F#)R}Fqj{M_~xJ1yRAU&+SA_w6f6 zPr);5)C@&5E|YR4;@q?uYgZwbcg(VD;%Y}xv4NeTm3~eok>J;ED|n4?bk0oE(?Em- z^LhGac3_$2HBp+#P0t;8Z8;JV%PWVd^l~MpT3-3d3(=?E{-)96L^S_DN?R_G)74)6 zAU1PFw6JM8^>}#B6CBJ5C`LqAlcr>Y<(k;9B>4N{U>DD!*{?;OSH)jlWqIo%ZU9sO z=+$l(Re?jSO1u{sDLw}6%Myawu}M=ibyaq8+%)3`KP@(Nf4&+c;9mf!WlsJjWNH~b zFW`q&AhvZBr0RQ09P9@%(pNH#p2>MBx~`1ss^!wPq-nqSn-N|M*b1OeK|dr9Km$Yp z^zon%&jgm)fCGR@z$1W10S5tp0@B>6c4J*5ZuXtXx&bjJUfldTyDkR%<&z`!_jC4( R__co^<8yn9CB0j^{{e>SKY{=N delta 2548 zcmZ`)TWnNS6!pxbouQrZ?1Qv6E#);>3X~2leYZ2y(%P0bU@HSUxt+O%3%7TMGq)|R zV#AkEK#4~*8pIGnqy$N1@DUB7!GOe_$h7V(WMH7FBAI7ux05djmKGy8B&))m& z$2#XuA7|6YnJ4D)%y~rqn$CAbzkJ8@r;AzQAJ?0#I(gDv>R>hEil>%UiF=-FZR@2O zuaRJ7M|LU3PBjs7nz5*=huoSGRriHeBg!{o-x=dkm0L`)n8vH&M&NsC?h#g-JUX}D z!RnGlt9QF%aA z=2cEr$bIh`-YWmdmZKQH3E&50OR${Xojsjfc^ecCDC})dY_r^|F*>4h#Z-9^JGp=Y zY&+Upw`{c>Te>$7@;qoep`ZnPb+g4d z)obHLh_M24R?4SBoG)IMli?%2D{h;Q<1I%*n^aQ+9Zary^s=4xi{DF!SiR^g`@q|b zNW=i@In8|-I!c`qj$j!S>z3qADG;{<@&VvOs6QiO-Y*zd6r7GRv83b&A&mlvDqA2?k+>(B zvlR2oPq60Xh4L4e>`3FX9a9akpw!7BomV;~>eM*ws2s{4HF>jBX^|uD6{n0S8RQXi z8tS;bk{J_{TFb-LxUNK0x){mA<&}0;Cpx^ox^ya(1%*WyA8>?@AT3py5mvnCy;KdF za0YuD{iS3^!;vbY88t7{@#Lxlwx~^%Jtq2}k*>Y6Cc41sT*!rnNlov-0nSvk^swVN zdv;byXT?hQRGMdGPV-9Bf@lYmb}zFWQ67uSyEKJpEr9f}(|*+70qi~rAWm(A)B-@0 zCUXW9eN-(~Ighz{FR#Z&op{&x7Bj_D8@kv5adyL@9S5O)8E_o%3gA`1VF{K~*9`e! zI|c?@`((jnd|NWM1W)Bc1>{Y z0Vz(-@hgd4rDMp}Yv-?FH)SFv%Mp-It#nGMMOSSldqYgr_7s5wmdgl7bS<(cVaZ$* z%9tAUGTxlLTl*tp&Ek4psGO*RgNcDNHp^DI)?$%NK8is7IrhBxqyA~>)7$X=>a#GP zdEmp(J(P$vJ}lpsP${o2zaXj_Pq8E7R^yWTi&E*8Cuqa`k`(1(uE*7hpd0|VvW8=P zL@{S<{A2i2fteg!*EHF11v{Srt^%$BGHkHCE+K?-KbL~#itN(#C|3=BTKv>>lbsPC zHZNtG_@Q}8>1kMI4VmGn#${5Dc#NAV#!^V1C~KKvH^f&hMa3~hhF03ym3W-rc%a}u zVQZb9qNjlf3Fh0-cp5l(ZQ0^c%Pohfv~y)dwcPRp7^V+@>I+AYQwyyB zkJ6Sy?odV)hae#MCDYQmIkupDFKN}OL7``dU9&3+{g_*(qk zR-U&SVkKY=fL`qeQRP3#){Bq)1I5RnJtZNOJu+cxrmo5^j+$o7;Ah0@z&Fb=0{$(4 zT4v`rAydofc>zDH0@2@6kgN%o*w|e$&`~mlp2>MBx~>fAs^!qNgqeErJK??o& zx&SV~Zop0eeLNhH#sS%Y3BV-aS-^h4bAZ19Y3@`zvG$339VfDWgJ_7?H(g}6MNg-f X-4WxRoc%0*>Fmz<_}*tpdspCJblgH@ diff --git a/v2realbot/backtesting/backtester.py b/v2realbot/backtesting/backtester.py index 7f5cd8b..04d4fef 100644 --- a/v2realbot/backtesting/backtester.py +++ b/v2realbot/backtesting/backtester.py @@ -572,7 +572,7 @@ class Backtester: print("BT:get open orders entry") if len(self.open_orders) == 0: print("BTC: order doesnt exist") - return 0 + return [] res = [] #with lock: for o in self.open_orders: diff --git a/v2realbot/common/__pycache__/model.cpython-310.pyc b/v2realbot/common/__pycache__/model.cpython-310.pyc index cd3cb2d68cb96cc1143c607a50432f8597b175f6..15f04b6df3df36d252b7246b3e3c08c1aa252b14 100644 GIT binary patch delta 1225 zcmY*YO-vI(6z%}K^l#f``wNJKA2AT(4>1ag2qK`OK#(9=VhGSdN}$Erwww${uj)ka zdeFp^L=WEdVB!%IPxj#1lg5LIN8k4yGFy^LQxe71VNJX~WCs^qhHA#PoEiNk2A^+5L2~iUI}zXZ60>btp+R=<^Fawg~eG z^S|oT4~&*qz4h;f7}7udv58^4)&>{>!~mm!bAU^LF+e9^9MA!n09*!K0bJEHM%ox8 zdP%Pby2T)l5r_55z-tlGGT6FX&uMVzTD5LMnli~c#yVBian|XoPOxsQ>LfI!B~>Ot+^$O*PD(05{rug7WiO_&(LZ&OJ zn&aH?uAHxft^dszXNePZ4F^|p19qLDZ01)s zr3B{9ZIxfk7Sw`%mN+GvxvPFA;y3eH;Q-k4|94f}c(pE*B`5D-4e!V8q9ED+1u(7# z53B$vve{cipVJ_-?9Xn+=*%8~ajd%JA(7&jQOz3XUJ0o!|+}0O{S4*k+`7862{)a*jI6DjPeEnFL#KYA?PBQ zAV?9MB{)qmMR1Owhu}OxH^DT)1%itNmmsfXl@X*<@Tj#p*+>NM+YQ)ftH-EOGP)k>v6%62+)6k%3!P2;2|3HZWX z(*(-tMk!nX-rf1ny>IHnB1df$WAH-H&hQ4@kLE7_^n<&=4QP~2M_xt<)$*OyV7V%< zP~v^m{k z5rh4(?z}c{V=PqaOZRI5`a6)C(rH!0#cpnW0X4wmRHrD={t%|>nN^JK;#*M>Xc!D3 zS1Bug;0$)aZo0gcWJgTwu!2(-3mQ6sI_cY}9nM>*Z9f&H|F~!SnZQL(&e^`loLsa0 zEUzKQY(K|w7rBD&d7*~xP0Hna#d`49L3onsKYkLg$Fm!@R;qHTT3?ctFkT6&)ne&x zWm*nVULb_(hAgiXt8xJpcMx{;cDU}Q=hi9D!_3p$+)kG0n`ohkzLaSVZ^U!GL6Uu3 zAjY 0: #print(db.runners) for i in db.runners: - i.run_profit = round(float(i.run_instance.state.profit),2) - i.run_trade_count = len(i.run_instance.state.tradeList) - i.run_positions = i.run_instance.state.positions - i.run_avgp = round(float(i.run_instance.state.avgp),3) + if i.run_instance: + i.run_profit = round(float(i.run_instance.state.profit),2) + i.run_trade_count = len(i.run_instance.state.tradeList) + i.run_positions = i.run_instance.state.positions + i.run_avgp = round(float(i.run_instance.state.avgp),3) return (0, db.runners) else: return (0, []) @@ -385,6 +386,7 @@ def run_stratin(id: UUID, runReq: RunRequest): run_symbol = symbol, run_note = runReq.note, run_stop_ev = se, + run_strat_json = runReq.strat_json, run_thread = vlakno, run_account = runReq.account, run_ilog_save = runReq.ilog_save, @@ -444,6 +446,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance): ilog_save=runner.run_ilog_save, bt_from=bp_from, bt_to = bp_to, + strat_json = runner.run_strat_json, stratvars = strat.state.vars, settings = settings, profit=round(float(strat.state.profit),2), diff --git a/v2realbot/indicators/__pycache__/indicators.cpython-310.pyc b/v2realbot/indicators/__pycache__/indicators.cpython-310.pyc index 9b8be40fd5694aece44fcd4d5e26d05d9718b964..0c9d5f06cce5b70330f74d286b2d032058a99212 100644 GIT binary patch delta 278 zcmeC=`ozwc&&$ij00b2`qf?xiC-V8aF{Ci(aOQGFapiJHaWgWcaHp`P@}x1Pu%~de zutxF1`J5?SKt5jzV=#jz&r6UQewvK8SW{99OH(zOCi5^VOq`!Czy=i8WGi9@QbjC4 zVzLgS93$IgZ^k-Cj>)GO4H?-d|6#ns%Ym$H@?9o*pqjr-ij4e|<(b8mG?|K6fePF- z`J>oMN^>#`DvLOQ{9CMfrMU%_x0vz@CQo3|&=D;&DoRbvNy;zLD=o>)DTa^;yKgaP i=9LtIyjvtV`7Dc?hyaku!N9@D!N>!|0*nGoBJ2RTH9d0x delta 587 zcmZWmJ4?hs5Z>9mHn}98j}ugUAqXk$>=iq0QV7?`;pQZACg<#4A&6IKV|nBcc%_}C zt^YwR@(*nN1HNy0m{rIJR1bWFb6nG;VF>%KE^i<_DIw&Db22#hFh+J z#^bjf+uC}t33aAC{mQ%N9^uQ^>_JRfN`Iv65snhS4sw+jO81J>Y+T!eTxsM~wC2UI z7JV!)a-CuA^ao{6V~a8vnzYd6Nin(@UOfUv2A%m)ZQ}`2DtoYSRD! diff --git a/v2realbot/indicators/indicators.py b/v2realbot/indicators/indicators.py index 69662e3..1e1b0ec 100644 --- a/v2realbot/indicators/indicators.py +++ b/v2realbot/indicators/indicators.py @@ -3,6 +3,7 @@ import numpy as np import pandas as pd from collections import deque import typing +from v2realbot.utils.utils import check_series, convert_to_numpy def ema(data, period: int = 50, use_series=False): if check_series(data): @@ -23,14 +24,3 @@ def sma(data, period: int = 50, use_series=False): data = convert_to_numpy(data) sma = ti.sma(data, period=period) return pd.Series(sma) if use_series else sma - -def convert_to_numpy(data): - if isinstance(data, list) or isinstance(data, deque): - return np.fromiter(data, float) - elif isinstance(data, pd.Series): - return data.to_numpy() - return data - - -def check_series(data): - return isinstance(data, pd.Series) \ No newline at end of file diff --git a/v2realbot/indicators/moving_averages.py b/v2realbot/indicators/moving_averages.py index a4db280..30bf63b 100644 --- a/v2realbot/indicators/moving_averages.py +++ b/v2realbot/indicators/moving_averages.py @@ -20,20 +20,9 @@ #inspirovat se, pripadne vyzkouset i TAlib from typing import Any - import pandas as pd import tulipy as ti - -def convert_to_numpy(data: Any): - if isinstance(data, list) or isinstance(data, deque): - return np.fromiter(data, float) - elif isinstance(data, pd.Series): - return data.to_numpy() - return data - - -def check_series(data: Any): - return isinstance(data, pd.Series) +from v2realbot.utils.utils import check_series, convert_to_numpy def ema(data: Any, period: int = 50, use_series=False) -> Any: if check_series(data): diff --git a/v2realbot/indicators/oscillators.py b/v2realbot/indicators/oscillators.py new file mode 100644 index 0000000..e7a4c56 --- /dev/null +++ b/v2realbot/indicators/oscillators.py @@ -0,0 +1,102 @@ +""" + Oscillator wrappers + Copyright (C) 2021 Brandon Fan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +""" + +from typing import Any + +import numpy as np +import pandas as pd +import tulipy as ti +from v2realbot.utils.utils import check_series, convert_to_numpy + + +def rsi(data: Any, period: int = 14, round_rsi: bool = False, use_series=False) -> np.array: + """ Implements RSI Indicator """ + if period >= len(data): + return pd.Series() if use_series else [] + if check_series(data): + use_series = True + data = convert_to_numpy(data) + rsi_values = ti.rsi(data, period) + if round_rsi: + rsi_values = np.round(rsi_values, 2) + return pd.Series(rsi_values) if use_series else rsi_values + + +def aroon_oscillator(high_data: Any, low_data: Any, period=14, use_series=False): + if check_series(high_data) or check_series(low_data): + use_series = True + high_data = convert_to_numpy(high_data) + low_data = convert_to_numpy(low_data) + aroonsc = ti.aroonosc(high_data, low_data, period=period) + return pd.Series(aroonsc) if use_series else aroonsc + + +def chande_momentum_oscillator(data, period=14, use_series=False): + if check_series(data): + use_series = True + data = convert_to_numpy(data) + cmo = ti.cmo(data, period) + return pd.Series(cmo) if use_series else cmo + + +def absolute_price_oscillator(data, short_period=12, long_period=26, use_series=False): + if check_series(data): + use_series = True + data = convert_to_numpy(data) + apo = ti.apo(data, short_period, long_period) + return pd.Series(apo) if use_series else apo + + +def percentage_price_oscillator(data, short_period=12, long_period=26, use_series=False): + if check_series(data): + use_series = True + data = convert_to_numpy(data) + ppo = ti.ppo(data, short_period, long_period) + return pd.Series(ppo) if use_series else ppo + + +def stochastic_oscillator(high_data, low_data, close_data, pct_k_period=14, pct_k_slowing_period=3, pct_d_period=3, + use_series=False): + if check_series(high_data) or check_series(low_data) or check_series(close_data): + use_series = True + high_data = convert_to_numpy(high_data) + low_data = convert_to_numpy(low_data) + close_data = convert_to_numpy(close_data) + stoch = ti.stoch(high_data, low_data, close_data, pct_k_period, pct_k_slowing_period, pct_d_period) + return pd.Series(stoch) if use_series else stoch + + +def stochastic_rsi(data, period=14, smooth_pct_k=3, smooth_pct_d=3): + """ Calculates Stochoastic RSI Courteous of @lukazbinden + :param data: + :param period: + :param smooth_pct_k: + :param smooth_pct_d: + :return: + """ + # Calculate RSI + rsi_values = rsi(data, period=period, round_rsi=False) + + # Calculate StochRSI + rsi_values = pd.Series(rsi_values) + stochrsi = (rsi_values - rsi_values.rolling(period).min()) / ( + rsi_values.rolling(period).max() - rsi_values.rolling(period).min()) + stochrsi_K = stochrsi.rolling(smooth_pct_k).mean() + stochrsi_D = stochrsi_K.rolling(smooth_pct_d).mean() + + return round(rsi_values, 2), round(stochrsi_K * 100, 2), round(stochrsi_D * 100, 2) diff --git a/v2realbot/interfaces/__pycache__/live_interface.cpython-310.pyc b/v2realbot/interfaces/__pycache__/live_interface.cpython-310.pyc index 7cbdf746808ed8669a15ee9591218ccb7f473ad0..088cee5a986103d221d1b9497ecdd0f8dbdbc8ad 100644 GIT binary patch delta 42 xcmaE-`A(BJpO=@50SJz+j7}-s$h%&Uk#X`~!G6ZAn|+0N7#X=YM+r}11OOKu408Yg delta 42 xcmaE-`A(BJpO=@50SMODIi^hB$h%&Uk!kW?!G6YVn|+0N7#VpsM+r}11OO1F3_Sn< diff --git a/v2realbot/interfaces/live_interface.py b/v2realbot/interfaces/live_interface.py index 42af49f..fab5037 100644 --- a/v2realbot/interfaces/live_interface.py +++ b/v2realbot/interfaces/live_interface.py @@ -170,7 +170,6 @@ class LiveInterface(GeneralInterface): def get_open_orders(self, symbol: str, side: OrderSide = OrderSide.SELL): # -> list(Order): getRequest = GetOrdersRequest(status=QueryOrderStatus.OPEN, side=side, symbols=[symbol]) try: - # Market order submit orderlist = self.trading_client.get_orders(getRequest) #list of Orders (orderlist[0].id) return orderlist diff --git a/v2realbot/static/index.html b/v2realbot/static/index.html index e5bd764..59a5ea9 100644 --- a/v2realbot/static/index.html +++ b/v2realbot/static/index.html @@ -187,6 +187,7 @@ trade pos pos_avgp + json open @@ -235,7 +236,11 @@
-
+ +
+ + +