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)
|
cash=100000)
|
||||||
|
|
||||||
#na sekundovem baru nezaokrouhlovat MAcko
|
#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.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||||
|
|
||||||
s.start()
|
s.start()
|
||||||
|
|||||||
@ -1163,7 +1163,7 @@ def main():
|
|||||||
cash=100000)
|
cash=100000)
|
||||||
|
|
||||||
#na sekundovem baru nezaokrouhlovat MAcko
|
#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.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||||
|
|
||||||
s.start()
|
s.start()
|
||||||
|
|||||||
@ -804,7 +804,7 @@ class Backtester:
|
|||||||
textik3 = html.Div('''
|
textik3 = html.Div('''
|
||||||
Stratvars:'''+ str(state.vars))
|
Stratvars:'''+ str(state.vars))
|
||||||
textik35 = html.Div('''
|
textik35 = html.Div('''
|
||||||
Resolution:'''+ str(state.timeframe) + "s rectype:" + str(state.rectype))
|
Resolution:'''+ str(state.resolution) + "s rectype:" + str(state.rectype))
|
||||||
textik4 = html.Div('''
|
textik4 = html.Div('''
|
||||||
Started at:''' + self.backtest_start.strftime("%d/%m/%Y, %H:%M:%S") + " Duration:"+str(self.backtest_end-self.backtest_start))
|
Started at:''' + self.backtest_start.strftime("%d/%m/%Y, %H:%M:%S") + " Duration:"+str(self.backtest_end-self.backtest_start))
|
||||||
textik5 = html.Div('''
|
textik5 = html.Div('''
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class Trade(BaseModel):
|
|||||||
generated_by: Optional[str] = None
|
generated_by: Optional[str] = None
|
||||||
direction: TradeDirection
|
direction: TradeDirection
|
||||||
entry_price: Optional[float] = None
|
entry_price: Optional[float] = None
|
||||||
|
goal_price: Optional[float] = None
|
||||||
size: Optional[int] = None
|
size: Optional[int] = None
|
||||||
# stoploss_type: TradeStoplossType
|
# stoploss_type: TradeStoplossType
|
||||||
stoploss_value: Optional[float] = None
|
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
|
#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
|
OFFLINE_MODE = False
|
||||||
|
|
||||||
#ilog lvls = 0,1 - 0 debug, 1 info
|
#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>"
|
#i.history += str(runner.__dict__)+"<BR>"
|
||||||
db.save()
|
db.save()
|
||||||
|
|
||||||
|
|
||||||
#stratin run
|
#stratin run
|
||||||
def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_batch_params: dict = None):
|
def run_stratin(id: UUID, runReq: RunRequest, synchronous: bool = False, inter_batch_params: dict = None):
|
||||||
if runReq.mode == Mode.BT:
|
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
|
#get rid of attributes that are links to the models
|
||||||
strat.state.vars["loaded_models"] = {}
|
strat.state.vars["loaded_models"] = {}
|
||||||
|
|
||||||
settings = dict(resolution=strat.state.timeframe,
|
settings = dict(resolution=strat.state.resolution,
|
||||||
rectype=strat.state.rectype,
|
rectype=strat.state.rectype,
|
||||||
configs=dict(
|
configs=dict(
|
||||||
GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN=GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN,
|
GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN=GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN,
|
||||||
|
|||||||
@ -55,6 +55,7 @@ class RecordType(str, Enum):
|
|||||||
|
|
||||||
BAR = "bar"
|
BAR = "bar"
|
||||||
CBAR = "cbar"
|
CBAR = "cbar"
|
||||||
|
CBARVOLUME = "cbarvolume"
|
||||||
TRADE = "trade"
|
TRADE = "trade"
|
||||||
|
|
||||||
class Mode(str, Enum):
|
class Mode(str, Enum):
|
||||||
|
|||||||
@ -11,12 +11,12 @@ import threading
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from msgpack import unpackb
|
from msgpack import unpackb
|
||||||
import os
|
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:
|
class TradeAggregator:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
rectype: RecordType = RecordType.BAR,
|
rectype: RecordType = RecordType.BAR,
|
||||||
timeframe: int = 5,
|
resolution: int = 5,
|
||||||
minsize: int = 100,
|
minsize: int = 100,
|
||||||
update_ltp: bool = False,
|
update_ltp: bool = False,
|
||||||
align: StartBarAlign = StartBarAlign.ROUND,
|
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
|
Create trade agregator. Instance accepts trades one by one and process them and returns output type
|
||||||
Trade - return trade one by one (no change)
|
Trade - return trade one by one (no change)
|
||||||
Bar - return finished bar in given timeframe
|
Bar - return finished bar in given resolution
|
||||||
CBar - returns continuous bar, finished bar is marked by confirmed status
|
CBar - returns continuous bar, finished bar is marked by confirmed status
|
||||||
Args:
|
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)
|
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.
|
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.rectype: RecordType = rectype
|
||||||
self.timeframe = timeframe
|
self.resolution = resolution
|
||||||
self.minsize = minsize
|
self.minsize = minsize
|
||||||
self.update_ltp = update_ltp
|
self.update_ltp = update_ltp
|
||||||
self.exthours = exthours
|
self.exthours = exthours
|
||||||
|
|
||||||
if mintick >= timeframe:
|
if mintick >= resolution:
|
||||||
print("Mintick musi byt mensi nez timeframe")
|
print("Mintick musi byt mensi nez resolution")
|
||||||
raise Exception
|
raise Exception
|
||||||
|
|
||||||
self.mintick = mintick
|
self.mintick = mintick
|
||||||
@ -51,7 +51,10 @@ class TradeAggregator:
|
|||||||
self.lasttimestamp = 0
|
self.lasttimestamp = 0
|
||||||
#inicalizace pro prvni agregaci
|
#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.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.bar_start = 0
|
||||||
|
self.curr_bar_volume = None
|
||||||
self.align = align
|
self.align = align
|
||||||
self.tm: datetime = None
|
self.tm: datetime = None
|
||||||
self.firstpass = True
|
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)
|
## přidán W - average price trade, U - Extended hours - sold out of sequence, Z - Sold(Out of sequence)
|
||||||
try:
|
try:
|
||||||
for i in data['c']:
|
for i in data['c']:
|
||||||
if i in ('C','O','4','B','7','V','P','W','U','Z'): return []
|
if i in AGG_EXCLUDED_TRADES: return []
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -151,8 +154,8 @@ class TradeAggregator:
|
|||||||
# if self.lasttimestamp ==0 and self.align:
|
# if self.lasttimestamp ==0 and self.align:
|
||||||
# if self.firstpass:
|
# if self.firstpass:
|
||||||
# self.tm = datetime.fromtimestamp(data['t'])
|
# self.tm = datetime.fromtimestamp(data['t'])
|
||||||
# self.tm += timedelta(seconds=self.timeframe)
|
# self.tm += timedelta(seconds=self.resolution)
|
||||||
# self.tm = self.tm - timedelta(seconds=self.tm.second % self.timeframe,microseconds=self.tm.microsecond)
|
# self.tm = self.tm - timedelta(seconds=self.tm.second % self.resolution,microseconds=self.tm.microsecond)
|
||||||
# self.firstpass = False
|
# self.firstpass = False
|
||||||
# print("trade: ",datetime.fromtimestamp(data['t']))
|
# print("trade: ",datetime.fromtimestamp(data['t']))
|
||||||
# print("required",self.tm)
|
# print("required",self.tm)
|
||||||
@ -160,10 +163,17 @@ class TradeAggregator:
|
|||||||
# return
|
# return
|
||||||
# else: pass
|
# 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("barstart",datetime.fromtimestamp(self.bar_start))
|
||||||
#print("oriznute data z tradu", datetime.fromtimestamp(int(data['t'])))
|
#print("oriznute data z tradu", datetime.fromtimestamp(int(data['t'])))
|
||||||
#print("timeframe",self.timeframe)
|
#print("resolution",self.resolution)
|
||||||
if int(data['t']) - self.bar_start < self.timeframe:
|
if int(data['t']) - self.bar_start < self.resolution:
|
||||||
issamebar = True
|
issamebar = True
|
||||||
else:
|
else:
|
||||||
issamebar = False
|
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
|
#MUSIME VRATIT ZPET - ten upraveny cas způsobuje spatne plneni v BT, kdyz tento bar triggeruje nakup
|
||||||
# if self.align:
|
# if self.align:
|
||||||
# t = datetime.fromtimestamp(data['t'])
|
# 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)
|
# #nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
|
||||||
# else:
|
# else:
|
||||||
# #ulozime si jeho timestamp (odtum pocitame timeframe)
|
# #ulozime si jeho timestamp (odtum pocitame resolution)
|
||||||
# t = datetime.fromtimestamp(int(data['t']))
|
# t = datetime.fromtimestamp(int(data['t']))
|
||||||
|
|
||||||
# #self.newBar['updated'] = float(data['t']) - 0.001
|
# #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)
|
#zarovname time prvniho baru podle timeframu kam patří (např. 5, 10, 15 ...) (ROUND)
|
||||||
if self.align == StartBarAlign.ROUND and self.bar_start == 0:
|
if self.align == StartBarAlign.ROUND and self.bar_start == 0:
|
||||||
t = datetime.fromtimestamp(data['t'])
|
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)
|
self.bar_start = datetime.timestamp(t)
|
||||||
#nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
|
#nebo pouzijeme datum tradu zaokrouhlene na vteriny (RANDOM)
|
||||||
else:
|
else:
|
||||||
#ulozime si jeho timestamp (odtum pocitame timeframe)
|
#ulozime si jeho timestamp (odtum pocitame resolution)
|
||||||
t = datetime.fromtimestamp(int(data['t']))
|
t = datetime.fromtimestamp(int(data['t']))
|
||||||
#timestamp
|
#timestamp
|
||||||
self.bar_start = int(data['t'])
|
self.bar_start = int(data['t'])
|
||||||
|
|
||||||
|
|
||||||
self.newBar['time'] = t
|
self.newBar['time'] = t
|
||||||
self.newBar['resolution'] = self.timeframe
|
self.newBar['resolution'] = self.resolution
|
||||||
self.newBar['confirmed'] = 0
|
self.newBar['confirmed'] = 0
|
||||||
|
|
||||||
|
|
||||||
@ -358,14 +368,178 @@ class TradeAggregator:
|
|||||||
else:
|
else:
|
||||||
return []
|
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):
|
class TradeAggregator2Queue(TradeAggregator):
|
||||||
"""
|
"""
|
||||||
Child of TradeAggregator - sends items to given queue
|
Child of TradeAggregator - sends items to given queue
|
||||||
In the future others will be added - TradeAggToTxT etc.
|
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):
|
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, timeframe=timeframe, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
|
super().__init__(rectype=rectype, resolution=resolution, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
|
|
||||||
@ -397,8 +571,8 @@ class TradeAggregator2List(TradeAggregator):
|
|||||||
""""
|
""""
|
||||||
stores records to the list
|
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):
|
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, timeframe=timeframe, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
|
super().__init__(rectype=rectype, resolution=resolution, minsize=minsize, update_ltp=update_ltp, align=align, mintick=mintick, exthours=exthours)
|
||||||
self.btdata = btdata
|
self.btdata = btdata
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
# self.debugfile = DATA_DIR + "/BACprices.txt"
|
# self.debugfile = DATA_DIR + "/BACprices.txt"
|
||||||
|
|||||||
@ -32,7 +32,7 @@ class Cacher:
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
|
||||||
rectype: RecordType = RecordType.BAR,
|
rectype: RecordType = RecordType.BAR,
|
||||||
timeframe: int = 5,
|
resolution: int = 5,
|
||||||
minsize: int = 100,
|
minsize: int = 100,
|
||||||
update_ltp: bool = False,
|
update_ltp: bool = False,
|
||||||
align: StartBarAlign = StartBarAlign.ROUND,
|
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) => {
|
data.bars.time.forEach((element, index, array) => {
|
||||||
sbars = {};
|
sbars = {};
|
||||||
svolume = {};
|
svolume = {};
|
||||||
svwap = {};
|
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["close"] = data.bars.close[index]
|
||||||
sbars["open"] = data.bars.open[index]
|
sbars["open"] = data.bars.open[index]
|
||||||
sbars["high"] = data.bars.high[index]
|
sbars["high"] = data.bars.high[index]
|
||||||
sbars["low"] = data.bars.low[index]
|
sbars["low"] = data.bars.low[index]
|
||||||
|
|
||||||
|
|
||||||
svwap["time"] = element
|
svwap["time"] = data.bars.time[index];
|
||||||
svwap["value"] = data.bars.vwap[index]
|
svwap["value"] = data.bars.vwap[index]
|
||||||
|
|
||||||
svolume["time"] = element
|
svolume["time"] = data.bars.time[index];
|
||||||
svolume["value"] = data.bars.volume[index]
|
svolume["value"] = data.bars.volume[index]
|
||||||
|
|
||||||
bars.push(sbars)
|
bars.push(sbars)
|
||||||
@ -107,6 +122,7 @@ function transform_data(data) {
|
|||||||
volume.push(svolume)
|
volume.push(svolume)
|
||||||
});
|
});
|
||||||
transformed["bars"] = bars
|
transformed["bars"] = bars
|
||||||
|
//console.log(bars)
|
||||||
transformed["vwap"] = vwap
|
transformed["vwap"] = vwap
|
||||||
transformed["volume"] = volume
|
transformed["volume"] = volume
|
||||||
var bars = []
|
var bars = []
|
||||||
@ -585,7 +601,7 @@ function chart_indicators(data, visible, offset) {
|
|||||||
|
|
||||||
//DEBUG
|
//DEBUG
|
||||||
// if (key == 'tick_price') {
|
// if (key == 'tick_price') {
|
||||||
// console.log("problem tu",JSON.stringify(items))
|
// console.log("problem tu",JSON.stringify(items,null,2))
|
||||||
// }
|
// }
|
||||||
//add data
|
//add data
|
||||||
obj.series.setData(items)
|
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
|
//togle profit line
|
||||||
function mrkLineToggle() {
|
function mrkLineToggle() {
|
||||||
vis = true;
|
vis = true;
|
||||||
@ -613,15 +628,19 @@ function onItemClickedToggle(index) {
|
|||||||
elem.classList.toggle("switcher-active-item");
|
elem.classList.toggle("switcher-active-item");
|
||||||
//v ifu kvuli workaroundu
|
//v ifu kvuli workaroundu
|
||||||
if (indList[index].series) {
|
if (indList[index].series) {
|
||||||
indList[index].series.applyOptions({
|
//console.log(indList[index].name, indList[index].series)
|
||||||
visible: vis });
|
indList[index].series.applyOptions({
|
||||||
|
visible: vis });
|
||||||
}
|
}
|
||||||
//zatim takto workaround, pak vymyslet systemove pro vsechny tickbased indikatory
|
//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) {
|
if (!vis && indList[index].series) {
|
||||||
|
//console.log("pred", indList[index].name, indList[index].series)
|
||||||
chart.removeSeries(indList[index].series)
|
chart.removeSeries(indList[index].series)
|
||||||
chart.timeScale().fitContent();
|
chart.timeScale().fitContent();
|
||||||
indList[index].series = null
|
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);
|
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
|
// //button pro toggle markeru nakupu/prodeju
|
||||||
var itemEl = document.createElement('button');
|
var itemEl = document.createElement('button');
|
||||||
itemEl.innerText = "mrk"
|
itemEl.innerText = "mrk"
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import numpy as np
|
|||||||
from threading import Event
|
from threading import Event
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators
|
from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators
|
||||||
|
from v2realbot.strategyblocks.activetrade.helpers import get_profit_target_price
|
||||||
|
|
||||||
class StrategyClassicSL(Strategy):
|
class StrategyClassicSL(Strategy):
|
||||||
"""
|
"""
|
||||||
@ -69,7 +70,6 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
|
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
|
||||||
trade_to_add = Trade(
|
trade_to_add = Trade(
|
||||||
id=uuid4(),
|
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)
|
#pokud jde o finalni FILL - pridame do pole tento celkovy relativnich profit (ze ktereho se pocita kumulativni relativni profit)
|
||||||
rel_profit_cum_calculated = 0
|
rel_profit_cum_calculated = 0
|
||||||
|
|
||||||
if data.event == TradeEvent.FILL:
|
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)
|
self.state.rel_profit_cum.append(rel_profit)
|
||||||
rel_profit_cum_calculated = round(np.mean(self.state.rel_profit_cum),5)
|
rel_profit_cum_calculated = round(np.mean(self.state.rel_profit_cum),5)
|
||||||
|
|
||||||
@ -193,6 +196,11 @@ class StrategyClassicSL(Strategy):
|
|||||||
#zapisujeme last entry price
|
#zapisujeme last entry price
|
||||||
self.state.last_entry_price["long"] = data.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")
|
#ic("vstupujeme do orderupdatebuy")
|
||||||
print(data)
|
print(data)
|
||||||
#dostavame zde i celkové akutální množství - ukládáme
|
#dostavame zde i celkové akutální množství - ukládáme
|
||||||
@ -308,6 +316,13 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#zapisujeme last entry price
|
#zapisujeme last entry price
|
||||||
self.state.last_entry_price["short"] = data.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
|
#update pozic, v trade update je i pocet zbylych pozic
|
||||||
old_avgp = self.state.avgp
|
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.general_interface import GeneralInterface
|
||||||
from v2realbot.interfaces.backtest_interface import BacktestInterface
|
from v2realbot.interfaces.backtest_interface import BacktestInterface
|
||||||
from v2realbot.interfaces.live_interface import LiveInterface
|
from v2realbot.interfaces.live_interface import LiveInterface
|
||||||
|
import v2realbot.common.PrescribedTradeModel as ptm
|
||||||
from alpaca.trading.enums import OrderSide
|
from alpaca.trading.enums import OrderSide
|
||||||
from v2realbot.backtesting.backtester import Backtester
|
from v2realbot.backtesting.backtester import Backtester
|
||||||
#from alpaca.trading.models import TradeUpdate
|
#from alpaca.trading.models import TradeUpdate
|
||||||
@ -26,6 +27,7 @@ import json
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from rich import print as printnow
|
from rich import print as printnow
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
|
||||||
|
|
||||||
if PROFILING_NEXT_ENABLED:
|
if PROFILING_NEXT_ENABLED:
|
||||||
from pyinstrument import Profiler
|
from pyinstrument import Profiler
|
||||||
@ -81,7 +83,7 @@ class Strategy:
|
|||||||
def add_data(self,
|
def add_data(self,
|
||||||
symbol: str,
|
symbol: str,
|
||||||
rectype: RecordType = RecordType.BAR,
|
rectype: RecordType = RecordType.BAR,
|
||||||
timeframe: int = 5,
|
resolution: int = 5,
|
||||||
minsize: int = 100,
|
minsize: int = 100,
|
||||||
update_ltp: bool = False,
|
update_ltp: bool = False,
|
||||||
align: StartBarAlign = StartBarAlign.ROUND,
|
align: StartBarAlign = StartBarAlign.ROUND,
|
||||||
@ -93,8 +95,8 @@ class Strategy:
|
|||||||
##stejne tak podporit i ruzne resolutions, zatim take natvrdo prvni
|
##stejne tak podporit i ruzne resolutions, zatim take natvrdo prvni
|
||||||
self.rectype = rectype
|
self.rectype = rectype
|
||||||
self.state.rectype = rectype
|
self.state.rectype = rectype
|
||||||
self.state.timeframe = timeframe
|
self.state.resolution = resolution
|
||||||
stream = TradeAggregator2Queue(symbol=symbol,queue=self.q1,rectype=rectype,timeframe=timeframe,update_ltp=update_ltp,align=align,mintick = mintick, exthours=exthours, minsize=minsize)
|
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._streams.append(stream)
|
||||||
self.dataloader.add_stream(stream)
|
self.dataloader.add_stream(stream)
|
||||||
|
|
||||||
@ -168,7 +170,7 @@ class Strategy:
|
|||||||
#implementovat az podle skutecnych pozadavku
|
#implementovat az podle skutecnych pozadavku
|
||||||
#self.state.indicators['time'].append(datetime.fromtimestamp(self.state.last_trade_time))
|
#self.state.indicators['time'].append(datetime.fromtimestamp(self.state.last_trade_time))
|
||||||
#self.append_trade(self.state.trades,item)
|
#self.append_trade(self.state.trades,item)
|
||||||
elif self.rectype == RecordType.CBAR:
|
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME):
|
||||||
if self.nextnew:
|
if self.nextnew:
|
||||||
#standardni identifikatory - populace hist zaznamu pouze v novem baru (dale se deji jen udpaty)
|
#standardni identifikatory - populace hist zaznamu pouze v novem baru (dale se deji jen udpaty)
|
||||||
for key in self.state.indicators:
|
for key in self.state.indicators:
|
||||||
@ -216,14 +218,14 @@ class Strategy:
|
|||||||
|
|
||||||
#pokud jsou nastaveny secondary - zatím skrz stratvars - pripadne do do API
|
#pokud jsou nastaveny secondary - zatím skrz stratvars - pripadne do do API
|
||||||
#zatim jedno, predelat pak na list
|
#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)
|
# self.process_secondary_indicators(item)
|
||||||
|
|
||||||
|
|
||||||
# #tady jsem skoncil
|
# #tady jsem skoncil
|
||||||
# def process_secondary_indicators(self, item):
|
# def process_secondary_indicators(self, item):
|
||||||
# #toto je voláno každý potvrzený CBAR
|
# #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:
|
# 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")
|
# 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()
|
a,p = self.interface.pos()
|
||||||
if a != -1:
|
if a != -1:
|
||||||
self.state.avgp, self.state.positions = a,p
|
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()
|
a,p = self.interface.pos()
|
||||||
if a != -1:
|
if a != -1:
|
||||||
self.state.avgp, self.state.positions = a,p
|
self.state.avgp, self.state.positions = a,p
|
||||||
|
|
||||||
"""update state.last_trade_time a time of iteration"""
|
"""update state.last_trade_time a time of iteration"""
|
||||||
def update_times(self, item):
|
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']
|
self.state.last_trade_time = item['updated']
|
||||||
elif self.rectype == RecordType.TRADE:
|
elif self.rectype == RecordType.TRADE:
|
||||||
self.state.last_trade_time = item['t']
|
self.state.last_trade_time = item['t']
|
||||||
@ -522,7 +524,7 @@ class Strategy:
|
|||||||
if self.rtqueue is not None:
|
if self.rtqueue is not None:
|
||||||
rt_out = dict()
|
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
|
rt_out["bars"] = item
|
||||||
else:
|
else:
|
||||||
rt_out["trades"] = item
|
rt_out["trades"] = item
|
||||||
@ -665,10 +667,12 @@ class StrategyState:
|
|||||||
#time of last trade processed
|
#time of last trade processed
|
||||||
self.last_trade_time = 0
|
self.last_trade_time = 0
|
||||||
self.last_entry_price=dict(long=0,short=999)
|
self.last_entry_price=dict(long=0,short=999)
|
||||||
self.timeframe = None
|
self.resolution = None
|
||||||
self.runner_id = runner_id
|
self.runner_id = runner_id
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
self.ilog_save = ilog_save
|
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': [],
|
bars = {'high': [],
|
||||||
'low': [],
|
'low': [],
|
||||||
@ -698,7 +702,7 @@ class StrategyState:
|
|||||||
#pro mapping indikatoru pro pouziti v operation expressionu
|
#pro mapping indikatoru pro pouziti v operation expressionu
|
||||||
self.ind_mapping = {}
|
self.ind_mapping = {}
|
||||||
self.cbar_indicators = AttributeDict(time=[])
|
self.cbar_indicators = AttributeDict(time=[])
|
||||||
#secondary timeframe indicators
|
#secondary resolution indicators
|
||||||
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
|
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
|
||||||
self.statinds = AttributeDict()
|
self.statinds = AttributeDict()
|
||||||
#these methods can be overrided by StrategyType (to add or alter its functionality)
|
#these methods can be overrided by StrategyType (to add or alter its functionality)
|
||||||
|
|||||||
@ -43,3 +43,27 @@ def close_position(state, data, direction: TradeDirection, reason: str, followup
|
|||||||
state.vars.last_exit_index = data["index"]
|
state.vars.last_exit_index = data["index"]
|
||||||
if followup is not None:
|
if followup is not None:
|
||||||
state.vars.requested_followup = followup
|
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.strategy.base import StrategyState
|
||||||
from v2realbot.enums.enums import Followup
|
from v2realbot.enums.enums import Followup
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
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.eod_exit import eod_exit_activated
|
||||||
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_conditions_met
|
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.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):
|
def eval_close_position(state: StrategyState, data):
|
||||||
curr_price = float(data['close'])
|
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)
|
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
|
||||||
if int(state.positions) < 0:
|
if int(state.positions) < 0:
|
||||||
#get TARGET PRICE pro dany smer a signal
|
#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:
|
if curr_price > state.vars.activeTrade.stoploss_value:
|
||||||
|
|
||||||
directive_name = 'reverse_for_SL_exit_short'
|
directive_name = 'reverse_for_SL_exit_short'
|
||||||
@ -90,13 +106,25 @@ def eval_close_position(state: StrategyState, data):
|
|||||||
elif int(state.positions) > 0:
|
elif int(state.positions) > 0:
|
||||||
|
|
||||||
#get TARGET PRICE pro dany smer a signal
|
#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)
|
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}")
|
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:
|
if curr_price < state.vars.activeTrade.stoploss_value:
|
||||||
directive_name = 'reverse_for_SL_exit_long'
|
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"))
|
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.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
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.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)
|
directive_name = 'profit_'+str(smer)
|
||||||
def_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
|
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):
|
def get_max_profit_price(state, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
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.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.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.ml.mlutils import load_model
|
from v2realbot.ml.mlutils import load_model
|
||||||
|
|||||||
@ -1,23 +1,57 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
|
from v2realbot.enums.enums import RecordType
|
||||||
|
|
||||||
def populate_cbar_tick_price_indicator(data, state: StrategyState):
|
def populate_cbar_tick_price_indicator(data, state: StrategyState):
|
||||||
try:
|
conf_bar = data['confirmed']
|
||||||
#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'])
|
#specifická sekce pro CBARVOLUME, kde vzdy máme nova data v confirmation baru (tzn. tickprice pocitame jak pri potvrzenem tak nepotvrzenem)
|
||||||
tick_price = data['close']
|
if state.rectype == RecordType.CBARVOLUME:
|
||||||
tick_delta_volume = data['volume'] - state.vars.last_tick_volume
|
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_price[-1] = tick_price
|
||||||
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
|
state.cbar_indicators.tick_volume[-1] = tick_delta_volume
|
||||||
except:
|
except:
|
||||||
pass
|
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_price = tick_price
|
||||||
state.vars.last_tick_volume = data['volume']
|
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']
|
||||||
@ -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.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
|
from traceback import format_exc
|
||||||
|
|
||||||
#ZATIM tyto zkopirovany SEM DO HELPERS
|
#ZATIM tyto zkopirovany SEM DO HELPERS
|
||||||
@ -71,7 +71,7 @@ def get_source_or_MA(state, indicator):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return state.bars[indicator]
|
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
|
Podporujeme krome klice v bar a indikatoru a dalsi doplnujici, oddelene _ napr. dailyBars_close
|
||||||
vezme serii static.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 tento lof patri spis do nextu classic SL - je poplatny typu stratefie
|
||||||
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
|
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
|
||||||
#PERF PROBLEM
|
#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
|
#kroky pro CONFIRMED BAR only
|
||||||
if conf_bar == 1:
|
if conf_bar == 1:
|
||||||
#logika pouze pro potvrzeny bar
|
pass
|
||||||
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
|
|
||||||
else:
|
else:
|
||||||
#CBAR INDICATOR pro tick price a deltu VOLUME
|
pass
|
||||||
populate_cbar_tick_price_indicator(data, state)
|
|
||||||
|
populate_cbar_tick_price_indicator(data, state)
|
||||||
|
|
||||||
#TBD nize predelat na typizovane RSI (a to jak na urovni CBAR tak confirmed)
|
#TBD nize predelat na typizovane RSI (a to jak na urovni CBAR tak confirmed)
|
||||||
#populate_cbar_rsi_indicator()
|
#populate_cbar_rsi_indicator()
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,8 @@ from v2realbot.utils.utils import zoneNY, json_serial
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
#import random
|
#import random
|
||||||
import json
|
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
|
#TODO nad prescribed trades postavit vstupni funkce
|
||||||
def execute_prescribed_trades(state: StrategyState, data):
|
def execute_prescribed_trades(state: StrategyState, data):
|
||||||
@ -46,15 +47,28 @@ def execute_prescribed_trades(state: StrategyState, data):
|
|||||||
res = state.buy(size=size)
|
res = state.buy(size=size)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation LONG {res}")
|
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á
|
#TODO nastaveni SL az do notifikace, kdy je známá
|
||||||
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||||
if state.vars.activeTrade.stoploss_value is None:
|
if state.vars.activeTrade.stoploss_value is None:
|
||||||
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
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)
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
|
#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)
|
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
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
|
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)))
|
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)
|
res = state.sell(size=size)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation SHORT {res}")
|
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
|
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||||
if state.vars.activeTrade.stoploss_value is None:
|
if state.vars.activeTrade.stoploss_value is None:
|
||||||
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
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)
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
|
#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)
|
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
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
else:
|
else:
|
||||||
state.ilog(lvl=1,e="unknow direction")
|
state.ilog(lvl=1,e="unknow direction")
|
||||||
|
|||||||
Reference in New Issue
Block a user