agregace volume baru, SL optimizace + goalprice

This commit is contained in:
David Brazda
2023-11-07 17:57:08 +01:00
parent 0db88b194c
commit b7f148fadd
24 changed files with 797 additions and 101 deletions

6
testy/ascending.py Normal file

File diff suppressed because one or more lines are too long

179
testy/fibonaccistoploss.py Normal file
View File

@ -0,0 +1,179 @@
import numpy as np
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from typing import Tuple
from copy import deepcopy
from v2realbot.strategy.base import StrategyState
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
from v2realbot.utils.utils import safe_get
# FIBONACCI PRO PROFIT A SL
##most used fibonacci retracement levels
# 23.6% retracement level = (stop loss price - current price) * 0.236 + current price
# 38.2% retracement level = (stop loss price - current price) * 0.382 + current price
# 50.0% retracement level = (stop loss price - current price) * 0.500 + current price
# 61.8% retracement level = (stop loss price - current price) * 0.618 + current price
# 78.6% retracement level = (stop loss price - current price) * 0.786 + current price
#cil: moznost pouzit fibanocci scale pro castecny stoploss exit (percentage at each downlevel)
#a zároveň exit, případně add at each up level
#up retracements (profit retracement)
# exit part of position at certain -
# [0.236, 0.382, 0.618, 1.0] - 25% off at each level? a nebo 5% add? - TBD vymyslet jak pojmout v direktive?
#down retracement (stoploss retracement)
# exit part of position at certain levels - TBD jak zapsat v dsirektive?
# [0.236, 0.382, 0.618, 1.0] - 25% off at each level
# #tridu, kterou muze vyuzivat SL a Profit optimizer
class SLOptimizer:
""""
Class to handle SL positition optimization for active trade. It is assumed that two instances exists
one for LONG trade and one for SHORT. During evaluate call, settings is initialized from trade setting
and used for every call on that trade. When evaluate is called on different trade, it is again initialized
according to new trade settings.
-samostatna instance pro short a long
-zatim pri opakovem prekroceni targetu nic nedelame (target aplikovany jen jednouo)
exit_levels = aktuální levely, prekroceny je povazovan za vyuzitý a maze se
exit_sizes = aktualní size multipliers, prekroceny je povazovan za vyuzitý a maze se
init_exit_levels, init_exit_sizes - puvodni plne
"""
def __init__(self, direction: TradeDirection) -> None:
##init - make exit size same length:
self.direction = direction
self.last_trade = 0
# def reset_levels(self):
# self.exit_levels = self.init_exit_levels
# self.exit_sizes = self.init_exit_sizes
def get_trade_details(self, state: StrategyState):
trade: Trade = state.vars.activeTrade
#jde o novy trade - resetujeme levely
if trade.id != self.last_trade:
#inicializujeme a vymazeme pripadne puvodni
if self.initialize_levels(state) is False:
return None, None
self.last_trade = trade.id
#return cost_price, sl_price
return state.avgp, trade.stoploss_value
def initialize_levels(self, state):
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
SL_opt_exit_sizes = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
if SL_opt_exit_levels is None or SL_opt_exit_sizes is not None:
print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
return False
if len(SL_opt_exit_sizes) == 1:
SL_opt_exit_sizes = SL_opt_exit_sizes * len(SL_opt_exit_levels)
if len(SL_opt_exit_sizes) != len(SL_opt_exit_levels):
raise Exception("exit_sizes doesnt fit exit_levels")
self.init_exit_levels = deepcopy(SL_opt_exit_levels)
self.init_exit_sizes = deepcopy(SL_opt_exit_sizes)
self.exit_levels = SL_opt_exit_levels
self.exit_sizes = SL_opt_exit_sizes
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
return True
def get_initial_abs_levels(self, state):
"""
Returns price levels corresponding to initial setting of exit_levels
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
if self.direction == TradeDirection.SHORT :
return [cost_price + exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
def get_remaining_abs_levels(self, state):
"""
Returns price levels corresponding to remaing exit_levels for current trade
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
if self.direction == TradeDirection.SHORT :
return [cost_price + exit_level * curr_sl_distance for exit_level in self.exit_levels]
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
def eval_position(self, state, data) -> Tuple[float, float]:
"""Evaluates optimalization for current position and returns if the given level was
met and how to adjust exit position.
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
print("no settings found")
return (None, None)
current_price = data["close"]
# Calculate the distance of the cost prcie from the stop-loss value
curr_sl_distance = np.abs(cost_price - sl_price)
level_met = None
exit_adjustment = None
if len(self.exit_levels) == 0 or len(self.exit_sizes) == 0:
print("levels exhausted")
return (None, None)
#for short
if self.direction == TradeDirection.SHORT :
#first available exit point
level_price = cost_price + self.exit_levels[0] * curr_sl_distance
if current_price > level_price:
# Remove the first element from exit_levels.
level_met = self.exit_levels.pop(0)
# Remove the first element from exit_sizes.
exit_adjustment = self.exit_sizes.pop(0)
#for shorts
else:
#price of first available exit point
level_price = cost_price - self.exit_levels[0] * curr_sl_distance
if current_price < level_price:
# Remove the first element from exit_levels.
level_met = self.exit_levels.pop(0)
# Remove the first element from exit_sizes.
exit_adjustment = self.exit_sizes.pop(0)
return level_met, exit_adjustment
#0.236, 0.382, 0.5, 0.618, 0.786, 1
exit_levels_input = [0.236, 0.382, 0.618, 1]
exit_sizes_input = [0.5] #or [0.25, 0.25, 0.25]
long_sl_optimizer = SLOptimizer(exit_levels_input, exit_sizes_input, TradeDirection.SHORT)
#short_sl_optimizer = SLOptimizer(exit_levels, exit_sizes, TradeDirection.SHORT)
print(long_sl_optimizer.get_remaining_abs_levels(90,100))
#new LONG trade
level_met, exit_adjustment = long_sl_optimizer.eval_position(current_price=95,cost_price=90,sl_price=100)
print(long_sl_optimizer.get_remaining_abs_levels(90,100))
print(level_met, exit_adjustment)
print(long_sl_optimizer.__dict__)
level_met, exit_adjustment = long_sl_optimizer.eval_position(current_price=95,cost_price=90,sl_price=100)
print(long_sl_optimizer.get_remaining_abs_levels(90,100))
print(level_met, exit_adjustment)
print(long_sl_optimizer.__dict__)
level_met, exit_adjustment = long_sl_optimizer.eval_position(current_price=95,cost_price=90,sl_price=100)
print(long_sl_optimizer.get_remaining_abs_levels(90,100))
print(level_met, exit_adjustment)
print(long_sl_optimizer.__dict__)
#long_sl_optimizer.eval_position(100,120)

View File

@ -161,7 +161,7 @@ def main():
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="BAC",rectype=RecordType.BAR,resolution=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()

View File

@ -1163,7 +1163,7 @@ def main():
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="BAC",rectype=RecordType.BAR,resolution=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()

View File

@ -804,7 +804,7 @@ class Backtester:
textik3 = html.Div('''
Stratvars:'''+ str(state.vars))
textik35 = html.Div('''
Resolution:'''+ str(state.timeframe) + "s rectype:" + str(state.rectype))
Resolution:'''+ str(state.resolution) + "s rectype:" + str(state.rectype))
textik4 = html.Div('''
Started at:''' + self.backtest_start.strftime("%d/%m/%Y, %H:%M:%S") + " Duration:"+str(self.backtest_end-self.backtest_start))
textik5 = html.Div('''

View File

@ -24,6 +24,7 @@ class Trade(BaseModel):
generated_by: Optional[str] = None
direction: TradeDirection
entry_price: Optional[float] = None
goal_price: Optional[float] = None
size: Optional[int] = None
# stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None

View File

@ -4,6 +4,10 @@ from appdirs import user_data_dir
#TODO vybrane dat do config db a managovat pres GUI
#AGGREGATOR filter trades
#NOTE pridana F - Inter Market Sweep Order - obcas vytvarela spajky
AGG_EXCLUDED_TRADES = ['C','O','4','B','7','V','P','W','U','Z','F']
OFFLINE_MODE = False
#ilog lvls = 0,1 - 0 debug, 1 info

View File

@ -492,7 +492,6 @@ def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
#i.history += str(runner.__dict__)+"<BR>"
db.save()
#stratin run
def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_batch_params: dict = None):
if runReq.mode == Mode.BT:
@ -756,7 +755,7 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
#get rid of attributes that are links to the models
strat.state.vars["loaded_models"] = {}
settings = dict(resolution=strat.state.timeframe,
settings = dict(resolution=strat.state.resolution,
rectype=strat.state.rectype,
configs=dict(
GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN=GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN,

View File

@ -55,6 +55,7 @@ class RecordType(str, Enum):
BAR = "bar"
CBAR = "cbar"
CBARVOLUME = "cbarvolume"
TRADE = "trade"
class Mode(str, Enum):

View File

@ -11,12 +11,12 @@ import threading
from copy import deepcopy
from msgpack import unpackb
import os
from v2realbot.config import DATA_DIR, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN
from v2realbot.config import DATA_DIR, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN, AGG_EXCLUDED_TRADES
class TradeAggregator:
def __init__(self,
rectype: RecordType = RecordType.BAR,
timeframe: int = 5,
resolution: int = 5,
minsize: int = 100,
update_ltp: bool = False,
align: StartBarAlign = StartBarAlign.ROUND,
@ -27,22 +27,22 @@ class TradeAggregator:
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
Bar - return finished bar in given resolution
CBar - returns continuous bar, finished bar is marked by confirmed status
Args:
timeframe (number): Resolution of bar in seconds
resolution (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
align: Defines alignement of first bar. ROUND - according to resolution( 5,10,15 - for 5s resolution), 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.resolution = resolution
self.minsize = minsize
self.update_ltp = update_ltp
self.exthours = exthours
if mintick >= timeframe:
print("Mintick musi byt mensi nez timeframe")
if mintick >= resolution:
print("Mintick musi byt mensi nez resolution")
raise Exception
self.mintick = mintick
@ -51,7 +51,10 @@ class TradeAggregator:
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.openedVolumeBar = None
self.lastConfirmedTime = 0
self.bar_start = 0
self.curr_bar_volume = None
self.align = align
self.tm: datetime = None
self.firstpass = True
@ -87,7 +90,7 @@ class TradeAggregator:
## přidán W - average price trade, U - Extended hours - sold out of sequence, Z - Sold(Out of sequence)
try:
for i in data['c']:
if i in ('C','O','4','B','7','V','P','W','U','Z'): return []
if i in AGG_EXCLUDED_TRADES: return []
except KeyError:
pass
@ -151,8 +154,8 @@ class TradeAggregator:
# 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.tm += timedelta(seconds=self.resolution)
# self.tm = self.tm - timedelta(seconds=self.tm.second % self.resolution,microseconds=self.tm.microsecond)
# self.firstpass = False
# print("trade: ",datetime.fromtimestamp(data['t']))
# print("required",self.tm)
@ -160,10 +163,17 @@ class TradeAggregator:
# return
# else: pass
if self.rectype in (RecordType.BAR, RecordType.CBAR):
return await self.calculate_time_bar(data, symbol)
if self.rectype == RecordType.CBARVOLUME:
return await self.calculate_volume_bar(data, symbol)
async def calculate_time_bar(self, data, symbol):
#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:
#print("resolution",self.resolution)
if int(data['t']) - self.bar_start < self.resolution:
issamebar = True
else:
issamebar = False
@ -202,10 +212,10 @@ class TradeAggregator:
#MUSIME VRATIT ZPET - ten upraveny cas způsobuje spatne plneni v BT, kdyz tento bar triggeruje nakup
# if self.align:
# t = datetime.fromtimestamp(data['t'])
# t = t - timedelta(seconds=t.second % self.timeframe,microseconds=t.microsecond)
# t = t - timedelta(seconds=t.second % self.resolution,microseconds=t.microsecond)
# #nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
# else:
# #ulozime si jeho timestamp (odtum pocitame timeframe)
# #ulozime si jeho timestamp (odtum pocitame resolution)
# t = datetime.fromtimestamp(int(data['t']))
# #self.newBar['updated'] = float(data['t']) - 0.001
@ -276,18 +286,18 @@ class TradeAggregator:
#zarovname time prvniho baru podle timeframu kam patří (např. 5, 10, 15 ...) (ROUND)
if self.align == StartBarAlign.ROUND and self.bar_start == 0:
t = datetime.fromtimestamp(data['t'])
t = t - timedelta(seconds=t.second % self.timeframe,microseconds=t.microsecond)
t = t - timedelta(seconds=t.second % self.resolution,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)
#ulozime si jeho timestamp (odtum pocitame resolution)
t = datetime.fromtimestamp(int(data['t']))
#timestamp
self.bar_start = int(data['t'])
self.newBar['time'] = t
self.newBar['resolution'] = self.timeframe
self.newBar['resolution'] = self.resolution
self.newBar['confirmed'] = 0
@ -358,14 +368,178 @@ class TradeAggregator:
else:
return []
async def calculate_volume_bar(self, data, symbol):
""""
Agreguje VOLUME BARS -
hlavni promenne
- self.openedVolumeBar (dict) = stavová obsahují aktivní nepotvrzený bar
- confirmedBars (list) = nestavová obsahuje confirmnute bary, které budou na konci funkceflushnuty
"""""
#volume_bucket = 10000 #daily MA volume z emackova na 30 deleno 50ti - dat do configu
volume_bucket = self.resolution
#potvrzene pripravene k vraceni
confirmedBars = []
#potvrdi existujici a nastavi k vraceni
def confirm_existing():
self.openedVolumeBar['confirmed'] = 1
self.openedVolumeBar['vwap'] = self.vwaphelper / self.openedVolumeBar['volume']
self.vwaphelper = 0
#ulozime zacatek potvrzeneho baru
self.lastBarConfirmed = self.openedVolumeBar['time']
self.openedVolumeBar['updated'] = data['t']
confirmedBars.append(deepcopy(self.openedVolumeBar))
self.openedVolumeBar = None
#TBD po každém potvrzení zvýšíme čas o nanosekundu (pro zobrazení v gui)
#data['t'] = data['t'] + 0.000001
#init unconfirmed - velikost bucketu kontrolovana predtim
def initialize_unconfirmed(size):
#inicializuji pro nový bar
self.vwaphelper += (data['p'] * size)
self.barindex +=1
self.openedVolumeBar = {
"close": data['p'],
"high": data['p'],
"low": data['p'],
"open": data['p'],
"volume": size,
"trades": 1,
"hlcc4": data['p'],
"confirmed": 0,
"time": datetime.fromtimestamp(data['t']),
"updated": data['t'],
"vwap": data['p'],
"index": self.barindex,
"resolution":volume_bucket
}
def update_unconfirmed(size):
#spočteme vwap - potřebujeme předchozí hodnoty
self.vwaphelper += (data['p'] * size)
self.openedVolumeBar['updated'] = data['t']
self.openedVolumeBar['close'] = data['p']
self.openedVolumeBar['high'] = max(self.openedVolumeBar['high'],data['p'])
self.openedVolumeBar['low'] = min(self.openedVolumeBar['low'],data['p'])
self.openedVolumeBar['volume'] = self.openedVolumeBar['volume'] + size
self.openedVolumeBar['trades'] = self.openedVolumeBar['trades'] + 1
self.openedVolumeBar['vwap'] = self.vwaphelper / self.openedVolumeBar['volume']
#pohrat si s timto round
self.openedVolumeBar['hlcc4'] = round((self.openedVolumeBar['high']+self.openedVolumeBar['low']+self.openedVolumeBar['close']+self.openedVolumeBar['close'])/4,3)
#init new - confirmed
def initialize_confirmed(size):
#ulozime zacatek potvrzeneho baru
self.lastBarConfirmed = datetime.fromtimestamp(data['t'])
self.barindex +=1
confirmedBars.append({
"close": data['p'],
"high": data['p'],
"low": data['p'],
"open": data['p'],
"volume": size,
"trades": 1,
"hlcc4":data['p'],
"confirmed": 1,
"time": datetime.fromtimestamp(data['t']),
"updated": data['t'],
"vwap": data['p'],
"index": self.barindex,
"resolution":volume_bucket
})
#existuje stávající bar a vejdeme se do nej
if self.openedVolumeBar is not None and int(data['s']) + self.openedVolumeBar['volume'] < volume_bucket:
#vejdeme se do stávajícího baru (tzn. neprekracujeme bucket)
update_unconfirmed(int(data['s']))
#updatujeme stávající nepotvrzeny bar
#nevejdem se do nej nebo neexistuje predchozi bar
else:
#1)existuje predchozi bar - doplnime zbytkem do valikosti bucketu a nastavime confirmed
if self.openedVolumeBar is not None:
#doplnime je zbytkem
bucket_left = volume_bucket - self.openedVolumeBar['volume']
# - update and confirm bar
update_unconfirmed(bucket_left)
confirm_existing()
#zbytek mnozství jde do dalsiho zpracovani
data['s'] = int(data['s']) - bucket_left
#nastavime cas o nanosekundu vyssi
data['t'] = data['t'] + 0.000001
#2 vytvarime novy bar (bary) a vejdeme se do nej
if int(data['s']) < volume_bucket:
#vytvarime novy nepotvrzeny bar
initialize_unconfirmed(int(data['s']))
#nevejdeme se do nej - pak vytvarime 1 až N dalsich baru (posledni nepotvrzený)
else:
# >>> for i in range(0, 550, 500):
# ... print(i)
# ...
# 0
# 500
#vytvarime plne potvrzene buckety (kolik se jich plne vejde)
for size in range(0, int(data['s']), volume_bucket):
initialize_confirmed(volume_bucket)
#nastavime cas o nanosekundu vyssi
data['t'] = data['t'] + 0.000001
#create complete full bucket with same prices and size
#naplnit do return pole
#pokud je zbytek vytvorime z nej nepotvrzeny bar
zbytek = int(data['s']) % volume_bucket
#ze zbytku vytvorime nepotvrzeny bar
if zbytek > 0:
initialize_unconfirmed(zbytek)
#create new open bar with size zbytek s otevrenym
#je cena stejna od predchoziho tradu? pro nepotvrzeny cbar vracime jen pri zmene ceny
if self.last_price == data['p']:
self.diff_price = False
else:
self.diff_price = True
self.last_price = data['p']
if float(data['t']) - float(self.lasttimestamp) < GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN:
self.trades_too_close = True
else:
self.trades_too_close = False
#uložíme do předchozí hodnoty (poznáme tak open a close)
self.lasttimestamp = data['t']
self.iterace += 1
# print(self.iterace, data)
#pokud mame confirm bary, tak FLUSHNEME confirm a i případný open (zrejme se pak nejaky vytvoril)
if len(confirmedBars) > 0:
return_set = confirmedBars + ([self.openedVolumeBar] if self.openedVolumeBar is not None else [])
confirmedBars = []
return return_set
#nemame confirm, FLUSHUJEME CBARVOLUME open - neresime zmenu ceny, ale neposilame kulomet (pokud nam nevytvari conf. bar)
if self.openedVolumeBar is not None and self.rectype == RecordType.CBARVOLUME:
#zkousime pustit i stejnou cenu(potrebujeme kvuli MYSELLU), ale blokoval kulomet,tzn. trady mensi nez GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN (1ms)
#if self.diff_price is True:
if self.trades_too_close is False:
return [self.openedVolumeBar]
else:
return []
else:
return []
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)
def __init__(self, symbol: str, queue: Queue, rectype: RecordType = RecordType.BAR, resolution: int = 5, minsize: int = 100, update_ltp: bool = False, align: StartBarAlign = StartBarAlign.ROUND, mintick: int = 0, exthours: bool = False):
super().__init__(rectype=rectype, resolution=resolution, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
self.queue = queue
self.symbol = symbol
@ -397,8 +571,8 @@ 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)
def __init__(self, symbol: str, btdata: list, rectype: RecordType = RecordType.BAR, resolution: int = 5, minsize: int = 100, update_ltp: bool = False, align: StartBarAlign = StartBarAlign.ROUND, mintick: int = 0, exthours: bool = False):
super().__init__(rectype=rectype, resolution=resolution, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
self.btdata = btdata
self.symbol = symbol
# self.debugfile = DATA_DIR + "/BACprices.txt"

View File

@ -32,7 +32,7 @@ class Cacher:
def __init__(self,
rectype: RecordType = RecordType.BAR,
timeframe: int = 5,
resolution: int = 5,
minsize: int = 100,
update_ltp: bool = False,
align: StartBarAlign = StartBarAlign.ROUND,

View File

@ -84,22 +84,37 @@ function transform_data(data) {
});
}
//pomocne
var last_time = 0
var time = 0
data.bars.time.forEach((element, index, array) => {
sbars = {};
svolume = {};
svwap = {};
sbars["time"] = element;
//tento algoritmus z duplicit dela posloupnosti a srovna i pripadne nekonzistence
//napr z .911 .911 .912 udela .911 .912 .913
//TODO - možná dat do backendu agregatoru
if (last_time>=element) {
console.log("bars", "problem v case - zarovnano",time, last_time, element)
data.bars.time[index] = data.bars.time[index-1] + 0.000001
}
last_time = data.bars.time[index]
sbars["time"] = data.bars.time[index];
sbars["close"] = data.bars.close[index]
sbars["open"] = data.bars.open[index]
sbars["high"] = data.bars.high[index]
sbars["low"] = data.bars.low[index]
svwap["time"] = element
svwap["time"] = data.bars.time[index];
svwap["value"] = data.bars.vwap[index]
svolume["time"] = element
svolume["time"] = data.bars.time[index];
svolume["value"] = data.bars.volume[index]
bars.push(sbars)
@ -107,6 +122,7 @@ function transform_data(data) {
volume.push(svolume)
});
transformed["bars"] = bars
//console.log(bars)
transformed["vwap"] = vwap
transformed["volume"] = volume
var bars = []
@ -585,7 +601,7 @@ function chart_indicators(data, visible, offset) {
//DEBUG
// if (key == 'tick_price') {
// console.log("problem tu",JSON.stringify(items))
// console.log("problem tu",JSON.stringify(items,null,2))
// }
//add data
obj.series.setData(items)

View File

@ -581,6 +581,21 @@ function toggleWide() {
}
}
//togle profit line
function toggleVolume() {
vis = true;
const elem = document.getElementById("volToggle");
if (elem.classList.contains("switcher-active-item")) {
vis = false;
}
elem.classList.toggle("switcher-active-item");
//v ifu kvuli workaroundu
if (volumeSeries) {
volumeSeries.applyOptions({
visible: vis });
}
}
//togle profit line
function mrkLineToggle() {
vis = true;
@ -613,15 +628,19 @@ function onItemClickedToggle(index) {
elem.classList.toggle("switcher-active-item");
//v ifu kvuli workaroundu
if (indList[index].series) {
indList[index].series.applyOptions({
visible: vis });
//console.log(indList[index].name, indList[index].series)
indList[index].series.applyOptions({
visible: vis });
}
//zatim takto workaround, pak vymyslet systemove pro vsechny tickbased indikatory
if (indList[index].name == "tick_price") {
tickIndicatorList = ["tick_price", "tick_volume"]
if (tickIndicatorList.includes(indList[index].name)) {
if (!vis && indList[index].series) {
//console.log("pred", indList[index].name, indList[index].series)
chart.removeSeries(indList[index].series)
chart.timeScale().fitContent();
indList[index].series = null
//console.log("po", indList[index].name, indList[index].series)
}
}
@ -687,6 +706,18 @@ function populate_indicator_buttons(def) {
});
buttonElement.appendChild(itemEl);
//button pro toggle fullscreenu
var itemEl = document.createElement('button');
itemEl.innerText = "vol"
itemEl.classList.add('switcher-item');
itemEl.classList.add('switcher-active-item');
itemEl.style.color = "#99912b"
itemEl.id = "volToggle"
itemEl.addEventListener('click', function(e) {
toggleVolume();
});
buttonElement.appendChild(itemEl);
// //button pro toggle markeru nakupu/prodeju
var itemEl = document.createElement('button');
itemEl.innerText = "mrk"

View File

@ -16,6 +16,7 @@ import numpy as np
from threading import Event
from uuid import UUID, uuid4
from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators
from v2realbot.strategyblocks.activetrade.helpers import get_profit_target_price
class StrategyClassicSL(Strategy):
"""
@ -69,7 +70,6 @@ class StrategyClassicSL(Strategy):
return False
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
trade_to_add = Trade(
id=uuid4(),
@ -130,7 +130,10 @@ class StrategyClassicSL(Strategy):
#pokud jde o finalni FILL - pridame do pole tento celkovy relativnich profit (ze ktereho se pocita kumulativni relativni profit)
rel_profit_cum_calculated = 0
if data.event == TradeEvent.FILL:
#TODO pokud mame partial exit, tak se spravne vypocita relativni profit, ale
# je jen na mensi mnozszvi take z nej delat cum_calculate je blbost - OPRAVIT
self.state.rel_profit_cum.append(rel_profit)
rel_profit_cum_calculated = round(np.mean(self.state.rel_profit_cum),5)
@ -193,6 +196,11 @@ class StrategyClassicSL(Strategy):
#zapisujeme last entry price
self.state.last_entry_price["long"] = data.price
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
if self.state.vars.activeTrade.goal_price is None:
dat = dict(close=data.price)
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.LONG)
#ic("vstupujeme do orderupdatebuy")
print(data)
#dostavame zde i celkové akutální množství - ukládáme
@ -308,6 +316,13 @@ class StrategyClassicSL(Strategy):
if data.event == TradeEvent.FILL:
#zapisujeme last entry price
self.state.last_entry_price["short"] = data.price
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
if self.state.vars.activeTrade.goal_price is None:
dat = dict(close=data.price)
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.SHORT)
#sem v budoucnu dat i update SL
#if self.state.vars.activeTrade.stoploss_value is None:
#update pozic, v trade update je i pocet zbylych pozic
old_avgp = self.state.avgp

View File

@ -16,6 +16,7 @@ from v2realbot.loader.trade_ws_streamer import Trade_WS_Streamer
from v2realbot.interfaces.general_interface import GeneralInterface
from v2realbot.interfaces.backtest_interface import BacktestInterface
from v2realbot.interfaces.live_interface import LiveInterface
import v2realbot.common.PrescribedTradeModel as ptm
from alpaca.trading.enums import OrderSide
from v2realbot.backtesting.backtester import Backtester
#from alpaca.trading.models import TradeUpdate
@ -26,6 +27,7 @@ import json
from uuid import UUID
from rich import print as printnow
from collections import defaultdict
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
if PROFILING_NEXT_ENABLED:
from pyinstrument import Profiler
@ -81,7 +83,7 @@ class Strategy:
def add_data(self,
symbol: str,
rectype: RecordType = RecordType.BAR,
timeframe: int = 5,
resolution: int = 5,
minsize: int = 100,
update_ltp: bool = False,
align: StartBarAlign = StartBarAlign.ROUND,
@ -93,8 +95,8 @@ class Strategy:
##stejne tak podporit i ruzne resolutions, zatim take natvrdo prvni
self.rectype = rectype
self.state.rectype = rectype
self.state.timeframe = timeframe
stream = TradeAggregator2Queue(symbol=symbol,queue=self.q1,rectype=rectype,timeframe=timeframe,update_ltp=update_ltp,align=align,mintick = mintick, exthours=exthours, minsize=minsize)
self.state.resolution = resolution
stream = TradeAggregator2Queue(symbol=symbol,queue=self.q1,rectype=rectype,resolution=resolution,update_ltp=update_ltp,align=align,mintick = mintick, exthours=exthours, minsize=minsize)
self._streams.append(stream)
self.dataloader.add_stream(stream)
@ -168,7 +170,7 @@ class Strategy:
#implementovat az podle skutecnych pozadavku
#self.state.indicators['time'].append(datetime.fromtimestamp(self.state.last_trade_time))
#self.append_trade(self.state.trades,item)
elif self.rectype == RecordType.CBAR:
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME):
if self.nextnew:
#standardni identifikatory - populace hist zaznamu pouze v novem baru (dale se deji jen udpaty)
for key in self.state.indicators:
@ -216,14 +218,14 @@ class Strategy:
#pokud jsou nastaveny secondary - zatím skrz stratvars - pripadne do do API
#zatim jedno, predelat pak na list
# if safe_get(self.state.vars, "secondary_timeframe",None):
# if safe_get(self.state.vars, "secondary_resolution",None):
# self.process_secondary_indicators(item)
# #tady jsem skoncil
# def process_secondary_indicators(self, item):
# #toto je voláno každý potvrzený CBAR
# resolution = int(safe_get(self.state.vars, "secondary_timeframe",10))
# resolution = int(safe_get(self.state.vars, "secondary_resolution",10))
# if int(item['resolution']) >= int(resolution) or int(resolution) % int(item['resolution']) != 0:
# self.state.ilog(e=f"Secondary res {resolution} must be higher than main resolution {item['resolution']} a jejim delitelem")
@ -273,14 +275,14 @@ class Strategy:
a,p = self.interface.pos()
if a != -1:
self.state.avgp, self.state.positions = a,p
elif self.rectype == RecordType.CBAR and item['confirmed'] == 1:
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME) and item['confirmed'] == 1:
a,p = self.interface.pos()
if a != -1:
self.state.avgp, self.state.positions = a,p
"""update state.last_trade_time a time of iteration"""
def update_times(self, item):
if self.rectype == RecordType.BAR or self.rectype == RecordType.CBAR:
if self.rectype == RecordType.BAR or self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME):
self.state.last_trade_time = item['updated']
elif self.rectype == RecordType.TRADE:
self.state.last_trade_time = item['t']
@ -522,7 +524,7 @@ class Strategy:
if self.rtqueue is not None:
rt_out = dict()
if self.rectype == RecordType.BAR or self.rectype == RecordType.CBAR:
if self.rectype == RecordType.BAR or self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME):
rt_out["bars"] = item
else:
rt_out["trades"] = item
@ -665,10 +667,12 @@ class StrategyState:
#time of last trade processed
self.last_trade_time = 0
self.last_entry_price=dict(long=0,short=999)
self.timeframe = None
self.resolution = None
self.runner_id = runner_id
self.bt = bt
self.ilog_save = ilog_save
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
self.sl_optimizer_long = optimsl.SLOptimizer(ptm.TradeDirection.LONG)
bars = {'high': [],
'low': [],
@ -698,7 +702,7 @@ class StrategyState:
#pro mapping indikatoru pro pouziti v operation expressionu
self.ind_mapping = {}
self.cbar_indicators = AttributeDict(time=[])
#secondary timeframe indicators
#secondary resolution indicators
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
self.statinds = AttributeDict()
#these methods can be overrided by StrategyType (to add or alter its functionality)

View File

@ -43,3 +43,27 @@ def close_position(state, data, direction: TradeDirection, reason: str, followup
state.vars.last_exit_index = data["index"]
if followup is not None:
state.vars.requested_followup = followup
#close only partial position - no followup here, size multiplier must be between 0 and 1
def close_position_partial(state, data, direction: TradeDirection, reason: str, size: float):
if size <= 0 or size >=1:
raise Exception(f"size must be betweem 0 and 1")
size_abs = abs(int(int(state.positions)*size))
state.ilog(lvl=1,e=f"CLOSING TRADE PART: {size_abs} {size} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
if direction == TradeDirection.SHORT:
res = state.buy(size=size_abs)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS PARTIAL BUY {reason} {res}")
elif direction == TradeDirection.LONG:
res = state.sell(size=size_abs)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS PARTIAL SELL {res}")
else:
raise Exception(f"unknow TradeDirection in close_position")
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
insert_SL_history(state)
state.vars.pending = state.vars.activeTrade.id
#state.vars.activeTrade = None
#state.vars.last_exit_index = data["index"]

View File

@ -1,4 +1,4 @@
from v2realbot.strategyblocks.activetrade.close.close_position import close_position
from v2realbot.strategyblocks.activetrade.close.close_position import close_position, close_position_partial
from v2realbot.strategy.base import StrategyState
from v2realbot.enums.enums import Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
@ -12,6 +12,7 @@ from traceback import format_exc
from v2realbot.strategyblocks.activetrade.close.eod_exit import eod_exit_activated
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_conditions_met
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
def eval_close_position(state: StrategyState, data):
curr_price = float(data['close'])
@ -25,11 +26,26 @@ def eval_close_position(state: StrategyState, data):
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
if int(state.positions) < 0:
#get TARGET PRICE pro dany smer a signal
goal_price = get_profit_target_price(state, data, TradeDirection.SHORT)
max_price = get_max_profit_price(state, data, TradeDirection.SHORT)
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
#SL - execution
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
if state.vars.activeTrade.goal_price is not None:
goal_price = state.vars.activeTrade.goal_price
else:
goal_price = get_profit_target_price(state, data, TradeDirection.SHORT)
max_price = get_max_profit_price(state, data, TradeDirection.SHORT)
state.ilog(lvl=1,e=f"Def Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
#SL OPTIMALIZATION - PARTIAL EXIT
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data)
if level_met is not None and exit_adjustment is not None:
position = state.positions * exit_adjustment
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_short.get_initial_abs_levels(state)), rem_levels=str(state.sl_optimizer_short.get_remaining_abs_levels(state)), exit_levels=str(state.sl_optimizer_short.exit_levels), exit_sizes=str(state.sl_optimizer_short.exit_sizes))
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}")
close_position_partial(state=state, data=data, direction=TradeDirection.SHORT, reason=F"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
return
#FULL SL reached - execution
if curr_price > state.vars.activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_short'
@ -90,13 +106,25 @@ def eval_close_position(state: StrategyState, data):
elif int(state.positions) > 0:
#get TARGET PRICE pro dany smer a signal
goal_price = get_profit_target_price(state, data, TradeDirection.LONG)
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
if state.vars.activeTrade.goal_price is not None:
goal_price = state.vars.activeTrade.goal_price
else:
goal_price = get_profit_target_price(state, data, TradeDirection.LONG)
max_price = get_max_profit_price(state, data, TradeDirection.LONG)
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
#EOD EXIT - TBD
#SL OPTIMALIZATION - PARTIAL EXIT
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data)
if level_met is not None and exit_adjustment is not None:
position = state.positions * exit_adjustment
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_long.get_initial_abs_levels(state)), rem_levels=str(state.sl_optimizer_long.get_remaining_abs_levels(state)), exit_levels=str(state.sl_optimizer_long.exit_levels), exit_sizes=str(state.sl_optimizer_long.exit_sizes))
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}")
close_position_partial(state=state, data=data, direction=TradeDirection.LONG, reason=f"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
return
#SL - execution
#SL FULL execution
if curr_price < state.vars.activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_long'
reverse_for_SL_exit = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))

View File

@ -1,5 +1,3 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
@ -161,11 +159,24 @@ def get_profit_target_price(state, data, direction: TradeDirection):
directive_name = 'profit_'+str(smer)
def_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
normalized_def_profit = normalize_tick(state, data, float(def_profit))
#mame v direktivve ticky
if isinstance(def_profit, (float, int)):
normalized_def_profit = normalize_tick(state, data, float(def_profit))
state.ilog(lvl=0,e=f"PROFIT {def_profit=} {normalized_def_profit=}")
state.ilog(lvl=0,e=f"PROFIT {def_profit=} {normalized_def_profit=}")
return price2dec(float(state.avgp)+normalized_def_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_def_profit,3)
base_price = state.avgp if state.avgp != 0 else data["close"]
to_return = price2dec(float(base_price)+normalized_def_profit,3) if direction == TradeDirection.LONG else price2dec(float(base_price)-normalized_def_profit,3)
#mame v direktive indikator
elif isinstance(def_profit, str):
to_return = float(value_or_indicator(state, def_profit))
if direction == TradeDirection.LONG and to_return < data['close'] or direction == TradeDirection.SHORT and to_return > data['close']:
state.ilog(lvl=1,e=f"SPATNA HODOTA DOTAZENEHO PROFITU z ind {def_profit} {to_return=} {smer} {data['close']}")
raise Exception(f"SPATNA HODOTA DOTAZENEHO PROFITU z ind{def_profit} {to_return=} {smer} {data['close']}")
state.ilog(lvl=1,e=f"DOTAZENY PROFIT z indikatoru {def_profit} {to_return=}")
return to_return
def get_max_profit_price(state, data, direction: TradeDirection):
if direction == TradeDirection.LONG:

View File

@ -0,0 +1,151 @@
import numpy as np
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from typing import Tuple
from copy import deepcopy
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade
from v2realbot.utils.utils import safe_get
# FIBONACCI PRO PROFIT A SL
##most used fibonacci retracement levels
# 23.6% retracement level = (stop loss price - current price) * 0.236 + current price
# 38.2% retracement level = (stop loss price - current price) * 0.382 + current price
# 50.0% retracement level = (stop loss price - current price) * 0.500 + current price
# 61.8% retracement level = (stop loss price - current price) * 0.618 + current price
# 78.6% retracement level = (stop loss price - current price) * 0.786 + current price
#cil: moznost pouzit fibanocci scale pro castecny stoploss exit (percentage at each downlevel)
#a zároveň exit, případně add at each up level
#up retracements (profit retracement)
# exit part of position at certain -
# [0.236, 0.382, 0.618, 1.0] - 25% off at each level? a nebo 5% add? - TBD vymyslet jak pojmout v direktive?
#down retracement (stoploss retracement)
# exit part of position at certain levels - TBD jak zapsat v dsirektive?
# [0.236, 0.382, 0.618, 1.0] - 25% off at each level
# #tridu, kterou muze vyuzivat SL a Profit optimizer
class SLOptimizer:
""""
Class to handle SL positition optimization for active trade. It is assumed that two instances exists
one for LONG trade and one for SHORT. During evaluate call, settings is initialized from trade setting
and used for every call on that trade. When evaluate is called on different trade, it is again initialized
according to new trade settings.
-samostatna instance pro short a long
-zatim pri opakovem prekroceni targetu nic nedelame (target aplikovany jen jednouo)
exit_levels = aktuální levely, prekroceny je povazovan za vyuzitý a maze se
exit_sizes = aktualní size multipliers, prekroceny je povazovan za vyuzitý a maze se
init_exit_levels, init_exit_sizes - puvodni plne
"""
def __init__(self, direction: TradeDirection) -> None:
##init - make exit size same length:
self.direction = direction
self.last_trade = 0
# def reset_levels(self):
# self.exit_levels = self.init_exit_levels
# self.exit_sizes = self.init_exit_sizes
def get_trade_details(self, state):
trade: Trade = state.vars.activeTrade
#jde o novy trade - resetujeme levely
if trade.id != self.last_trade:
#inicializujeme a vymazeme pripadne puvodni
if self.initialize_levels(state) is False:
return None, None
self.last_trade = trade.id
#return cost_price, sl_price
return state.avgp, trade.stoploss_value
def initialize_levels(self, state):
directive_name = 'SL_opt_exit_levels_'+str(self.direction.value)
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
directive_name = 'SL_opt_exit_sizes_'+str(self.direction.value)
SL_opt_exit_sizes = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
if SL_opt_exit_levels is None or SL_opt_exit_sizes is None:
#print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
return False
if len(SL_opt_exit_sizes) == 1:
SL_opt_exit_sizes = SL_opt_exit_sizes * len(SL_opt_exit_levels)
if len(SL_opt_exit_sizes) != len(SL_opt_exit_levels):
raise Exception("exit_sizes doesnt fit exit_levels")
self.init_exit_levels = deepcopy(SL_opt_exit_levels)
self.init_exit_sizes = deepcopy(SL_opt_exit_sizes)
self.exit_levels = SL_opt_exit_levels
self.exit_sizes = SL_opt_exit_sizes
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
return True
def get_initial_abs_levels(self, state):
"""
Returns price levels corresponding to initial setting of exit_levels
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
if self.direction == TradeDirection.SHORT :
return [cost_price + exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
def get_remaining_abs_levels(self, state):
"""
Returns price levels corresponding to remaing exit_levels for current trade
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
if self.direction == TradeDirection.SHORT :
return [cost_price + exit_level * curr_sl_distance for exit_level in self.exit_levels]
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
def eval_position(self, state, data) -> Tuple[float, float]:
"""Evaluates optimalization for current position and returns if the given level was
met and how to adjust exit position.
"""
cost_price, sl_price = self.get_trade_details(state)
if cost_price is None or sl_price is None:
#print("no settings found")
return (None, None)
current_price = data["close"]
# Calculate the distance of the cost prcie from the stop-loss value
curr_sl_distance = np.abs(cost_price - sl_price)
level_met = None
exit_adjustment = None
if len(self.exit_levels) == 0 or len(self.exit_sizes) == 0:
#print("levels exhausted")
return (None, None)
#for short
if self.direction == TradeDirection.SHORT :
#first available exit point
level_price = cost_price + self.exit_levels[0] * curr_sl_distance
if current_price > level_price:
# Remove the first element from exit_levels.
level_met = self.exit_levels.pop(0)
# Remove the first element from exit_sizes.
exit_adjustment = self.exit_sizes.pop(0)
#for shorts
else:
#price of first available exit point
level_price = cost_price - self.exit_levels[0] * curr_sl_distance
if current_price < level_price:
# Remove the first element from exit_levels.
level_met = self.exit_levels.pop(0)
# Remove the first element from exit_sizes.
exit_adjustment = self.exit_sizes.pop(0)
return level_met, exit_adjustment

View File

@ -1,7 +1,3 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.utils.directive_utils import get_conditions_from_configuration
from v2realbot.ml.mlutils import load_model

View File

@ -1,23 +1,57 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.enums.enums import RecordType
def populate_cbar_tick_price_indicator(data, state: StrategyState):
try:
#pokud v potvrzovacím baru nebyly zmeny, nechavam puvodni hodnoty
# 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:
conf_bar = data['confirmed']
#tick_price = round2five(data['close'])
tick_price = data['close']
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
#specifická sekce pro CBARVOLUME, kde vzdy máme nova data v confirmation baru (tzn. tickprice pocitame jak pri potvrzenem tak nepotvrzenem)
if state.rectype == RecordType.CBARVOLUME:
try:
tick_price = data['close']
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
except:
pass
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
except:
pass
state.ilog(lvl=0,e=f"TICK PRICE {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume)
state.ilog(lvl=0,e=f"TICK PRICE CBARV {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume)
state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume']
state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume']
if conf_bar == 1:
#pri potvrzem CBARu nulujeme counter volume pro tick based indicator
state.vars.last_tick_volume = 0
state.vars.next_new = 1
#pro standardní CBARy
else:
if conf_bar == 1:
#pri potvrzem CBARu nulujeme counter volume pro tick based indicator
state.vars.last_tick_volume = 0
state.vars.next_new = 1
#naopak pri CBARu confirmation bar nema zadna nova data (tzn. tickprice pocitame jen pri potvrzenem)
else:
try:
#pokud v potvrzovacím baru nebyly zmeny, nechavam puvodni hodnoty
# 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:
#tick_price = round2five(data['close'])
tick_price = data['close']
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
state.cbar_indicators.tick_price[-1] = tick_price
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
except:
pass
state.ilog(lvl=0,e=f"TICK PRICE CBAR {tick_price} VOLUME {tick_delta_volume} {data['confirmed']=}", prev_price=state.vars.last_tick_price, prev_volume=state.vars.last_tick_volume)
state.vars.last_tick_price = tick_price
state.vars.last_tick_volume = data['volume']

View File

@ -1,5 +1,5 @@
from v2realbot.utils.utils import isrising, isfalling,isfallingc, isrisingc, zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
from v2realbot.strategy.base import StrategyState
#from v2realbot.strategy.base import StrategyState
from traceback import format_exc
#ZATIM tyto zkopirovany SEM DO HELPERS
@ -71,7 +71,7 @@ def get_source_or_MA(state, indicator):
except KeyError:
return state.bars[indicator]
def get_source_series(state: StrategyState, source: str):
def get_source_series(state, source: str):
"""
Podporujeme krome klice v bar a indikatoru a dalsi doplnujici, oddelene _ napr. dailyBars_close
vezme serii static.dailyBars[close]

View File

@ -55,20 +55,16 @@ def populate_all_indicators(data, state: StrategyState):
#TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
#PERF PROBLEM
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.mean(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending))
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} GP:{state.vars.activeTrade.goal_price if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.mean(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending))
#kroky pro CONFIRMED BAR only
if conf_bar == 1:
#logika pouze pro potvrzeny bar
state.ilog(lvl=0,e="BAR potvrzeny")
#pri potvrzem CBARu nulujeme counter volume pro tick based indicator
state.vars.last_tick_volume = 0
state.vars.next_new = 1
#kroky pro CONTINOUS TICKS only
pass
else:
#CBAR INDICATOR pro tick price a deltu VOLUME
populate_cbar_tick_price_indicator(data, state)
pass
populate_cbar_tick_price_indicator(data, state)
#TBD nize predelat na typizovane RSI (a to jak na urovni CBAR tak confirmed)
#populate_cbar_rsi_indicator()

View File

@ -4,7 +4,8 @@ from v2realbot.utils.utils import zoneNY, json_serial
from datetime import datetime
#import random
import json
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history, get_default_sl_value, normalize_tick
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history, get_default_sl_value, normalize_tick, get_profit_target_price
from v2realbot.strategyblocks.indicators.helpers import value_or_indicator
#TODO nad prescribed trades postavit vstupni funkce
def execute_prescribed_trades(state: StrategyState, data):
@ -46,15 +47,28 @@ def execute_prescribed_trades(state: StrategyState, data):
res = state.buy(size=size)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation LONG {res}")
#defaultni goal price pripadne nastavujeme az v notifikaci
#TODO nastaveni SL az do notifikace, kdy je známá
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue_abs
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs >= float(data['close']):
raise Exception(f"error in stoploss {sl_defvalue_abs} >= curr price")
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
insert_SL_history(state)
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = state.vars.activeTrade.id
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
state.ilog(lvl=1,e="odesilame SHORT ORDER",trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)))
@ -65,14 +79,26 @@ def execute_prescribed_trades(state: StrategyState, data):
res = state.sell(size=size)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation SHORT {res}")
#defaultní goalprice nastavujeme az v notifikaci
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data, sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue_abs
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs <= float(data['close']):
raise Exception(f"error in stoploss {sl_defvalue_abs} <= curr price")
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
insert_SL_history(state)
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = state.vars.activeTrade.id
else:
state.ilog(lvl=1,e="unknow direction")