agregace volume baru, SL optimizace + goalprice
This commit is contained in:
6
testy/ascending.py
Normal file
6
testy/ascending.py
Normal file
File diff suppressed because one or more lines are too long
179
testy/fibonaccistoploss.py
Normal file
179
testy/fibonaccistoploss.py
Normal 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)
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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('''
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -55,6 +55,7 @@ class RecordType(str, Enum):
|
||||
|
||||
BAR = "bar"
|
||||
CBAR = "cbar"
|
||||
CBARVOLUME = "cbarvolume"
|
||||
TRADE = "trade"
|
||||
|
||||
class Mode(str, Enum):
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
//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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"]
|
||||
@ -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
|
||||
#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"))
|
||||
|
||||
@ -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)
|
||||
|
||||
#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=}")
|
||||
|
||||
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:
|
||||
|
||||
151
v2realbot/strategyblocks/activetrade/sl/optimsl.py
Normal file
151
v2realbot/strategyblocks/activetrade/sl/optimsl.py
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -1,6 +1,40 @@
|
||||
from v2realbot.strategy.base import StrategyState
|
||||
from v2realbot.enums.enums import RecordType
|
||||
|
||||
def populate_cbar_tick_price_indicator(data, state: StrategyState):
|
||||
conf_bar = data['confirmed']
|
||||
|
||||
#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.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']
|
||||
|
||||
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:
|
||||
@ -17,7 +51,7 @@ def populate_cbar_tick_price_indicator(data, state: StrategyState):
|
||||
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 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']
|
||||
@ -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]
|
||||
|
||||
@ -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
|
||||
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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
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
|
||||
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 }")
|
||||
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.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)
|
||||
|
||||
if isinstance(sl_defvalue, (float, int)):
|
||||
#normalizuji dle aktualni ceny
|
||||
sl_defvalue_normalized = normalize_tick(state, data, sl_defvalue)
|
||||
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
|
||||
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
|
||||
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 }")
|
||||
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.vars.pending = state.vars.activeTrade.id
|
||||
else:
|
||||
state.ilog(lvl=1,e="unknow direction")
|
||||
|
||||
Reference in New Issue
Block a user