first commit
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from v2realbot.strategy.base import StrategyState
|
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.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive, keyword_conditions_met
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
# FIBONACCI PRO PROFIT A SL
|
# FIBONACCI PRO PROFIT A SL
|
||||||
|
|
||||||
@ -63,10 +63,10 @@ class SLOptimizer:
|
|||||||
|
|
||||||
def initialize_levels(self, state):
|
def initialize_levels(self, state):
|
||||||
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
|
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))
|
SL_opt_exit_levels = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
|
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))
|
SL_opt_exit_sizes = get_signal_section_directive(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:
|
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")
|
print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
||||||
|
|||||||
@ -59,25 +59,12 @@ Hlavní loop:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def next(data, state: StrategyState):
|
def next(data, state: StrategyState):
|
||||||
##print(10*"*","NEXT START",10*"*")
|
print(10*"*", state.account_variables)
|
||||||
# important vars state.avgp, state.positions, state.vars, data
|
|
||||||
|
|
||||||
#indicators moved to call_next in upper class
|
|
||||||
|
|
||||||
#pokud mame prazdne pozice a neceka se na nic
|
execute_prescribed_trades(state, data)
|
||||||
if state.positions == 0 and state.vars.pending is None:
|
signal_search(state, data)
|
||||||
#vykoname trady ve fronte
|
execute_prescribed_trades(state, data) #pro jistotu ihned zpracujeme
|
||||||
execute_prescribed_trades(state, data)
|
manage_active_trade(state, data)
|
||||||
#pokud se neaktivoval nejaky trade, poustime signal search - ale jen jednou za bar?
|
|
||||||
#if conf_bar == 1:
|
|
||||||
if state.vars.pending is None:
|
|
||||||
signal_search(state, data)
|
|
||||||
#pro jistotu ihned zpracujeme
|
|
||||||
execute_prescribed_trades(state, data)
|
|
||||||
|
|
||||||
#mame aktivni trade a neceka se n anic
|
|
||||||
elif state.vars.activeTrade and state.vars.pending is None:
|
|
||||||
manage_active_trade(state, data)
|
|
||||||
|
|
||||||
def init(state: StrategyState):
|
def init(state: StrategyState):
|
||||||
#place to declare new vars
|
#place to declare new vars
|
||||||
@ -88,13 +75,13 @@ def init(state: StrategyState):
|
|||||||
|
|
||||||
#nove atributy na rizeni tradu
|
#nove atributy na rizeni tradu
|
||||||
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
|
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
|
||||||
state.vars.pending = None
|
#state.vars.pending = None #nahrazeno pebnding pod accountem state.account_variables[account.name].pending
|
||||||
#obsahuje aktivni Trade a jeho nastaveni
|
#obsahuje aktivni Trade a jeho nastaveni
|
||||||
state.vars.activeTrade = None #pending/Trade
|
#state.vars.activeTrade = None #pending/Trade moved to account_variables
|
||||||
#obsahuje pripravene Trady ve frontě
|
#obsahuje pripravene Trady ve frontě
|
||||||
state.vars.prescribedTrades = []
|
state.vars.prescribedTrades = []
|
||||||
#flag pro reversal
|
#flag pro reversal
|
||||||
state.vars.requested_followup = None
|
#state.vars.requested_followup = None #nahrazeno pod accountem
|
||||||
|
|
||||||
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
||||||
#pripadne udelat refresh kazdych x-iterací
|
#pripadne udelat refresh kazdych x-iterací
|
||||||
@ -102,9 +89,8 @@ def init(state: StrategyState):
|
|||||||
state.vars.mode = None
|
state.vars.mode = None
|
||||||
state.vars.last_50_deltas = []
|
state.vars.last_50_deltas = []
|
||||||
state.vars.next_new = 0
|
state.vars.next_new = 0
|
||||||
state.vars.last_buy_index = None
|
state.vars.last_entry_index = None #mponechano obecne pro vsechny accounty
|
||||||
state.vars.last_exit_index = None
|
state.vars.last_exit_index = None #obecna varianta ponechana
|
||||||
state.vars.last_in_index = None
|
|
||||||
state.vars.last_update_time = 0
|
state.vars.last_update_time = 0
|
||||||
state.vars.reverse_position_waiting_amount = 0
|
state.vars.reverse_position_waiting_amount = 0
|
||||||
#INIT promenne, ktere byly zbytecne ve stratvars
|
#INIT promenne, ktere byly zbytecne ve stratvars
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
"""
|
"""
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
||||||
from v2realbot.common.model import TradeUpdate, Order
|
from v2realbot.common.model import TradeUpdate, Order, Account
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -61,6 +61,7 @@ import dash_bootstrap_components as dbc
|
|||||||
from dash.dependencies import Input, Output
|
from dash.dependencies import Input, Output
|
||||||
from dash import dcc, html, dash_table, Dash
|
from dash import dcc, html, dash_table, Dash
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
|
from typing import Set
|
||||||
""""
|
""""
|
||||||
LATENCY DELAYS
|
LATENCY DELAYS
|
||||||
.000 trigger - last_trade_time (.4246266)
|
.000 trigger - last_trade_time (.4246266)
|
||||||
@ -74,7 +75,20 @@ lock = threading.Lock
|
|||||||
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
||||||
#ted je globalni promena last_time_now a self.account a cash
|
#ted je globalni promena last_time_now a self.account a cash
|
||||||
class Backtester:
|
class Backtester:
|
||||||
def __init__(self, symbol: str, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
|
"""
|
||||||
|
Initializes a new instance of the Backtester class.
|
||||||
|
Args:
|
||||||
|
symbol (str): The symbol of the security being backtested.
|
||||||
|
accounts (set): A set of accounts to use for backtesting.
|
||||||
|
order_fill_callback (callable): A callback function to handle order fills.
|
||||||
|
btdata (list): A list of backtesting data.
|
||||||
|
bp_from (datetime): The start date of the backtesting period.
|
||||||
|
bp_to (datetime): The end date of the backtesting period.
|
||||||
|
cash (float, optional): The initial cash balance. Defaults to 100000.
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
def __init__(self, symbol: str, accounts: Set, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
|
||||||
#this TIME value determines true time for submit, replace, cancel order should happen (allowing past)
|
#this TIME value determines true time for submit, replace, cancel order should happen (allowing past)
|
||||||
#it is set by every iteration of BT or before fill callback - allowing past events to happen
|
#it is set by every iteration of BT or before fill callback - allowing past events to happen
|
||||||
self.time = None
|
self.time = None
|
||||||
@ -83,6 +97,7 @@ class Backtester:
|
|||||||
self.btdata = btdata
|
self.btdata = btdata
|
||||||
self.backtest_start = None
|
self.backtest_start = None
|
||||||
self.backtest_end = None
|
self.backtest_end = None
|
||||||
|
self.accounts = accounts
|
||||||
self.cash_init = cash
|
self.cash_init = cash
|
||||||
#backtesting period
|
#backtesting period
|
||||||
self.bp_from = bp_from
|
self.bp_from = bp_from
|
||||||
@ -90,9 +105,11 @@ class Backtester:
|
|||||||
self.cash = cash
|
self.cash = cash
|
||||||
self.cash_reserved_for_shorting = 0
|
self.cash_reserved_for_shorting = 0
|
||||||
self.trades = []
|
self.trades = []
|
||||||
self.account = { "BAC": [0, 0] }
|
def_acc_value = {self.symbol: [0, 0]}
|
||||||
# { "BAC": [avgp, size] }
|
self.internal_account = { account.name:def_acc_value for account in accounts }
|
||||||
self.open_orders =[]
|
# { "ACCOUNT1": {}"BAC": [avgp, size]}, .... }
|
||||||
|
self.open_orders =[] #open orders shared for all accounts, account being an attribute
|
||||||
|
|
||||||
# self.open_orders = [Order(id=uuid4(),
|
# self.open_orders = [Order(id=uuid4(),
|
||||||
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||||
# symbol = "BAC",
|
# symbol = "BAC",
|
||||||
@ -110,6 +127,8 @@ class Backtester:
|
|||||||
# side = OrderSide.BUY)]
|
# side = OrderSide.BUY)]
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def execute_orders_and_callbacks(self, intime: float):
|
def execute_orders_and_callbacks(self, intime: float):
|
||||||
"""""
|
"""""
|
||||||
Voláno ze strategie před každou iterací s časem T.
|
Voláno ze strategie před každou iterací s časem T.
|
||||||
@ -166,7 +185,7 @@ class Backtester:
|
|||||||
|
|
||||||
for order in self.open_orders:
|
for order in self.open_orders:
|
||||||
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
||||||
print(order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
|
print(order.account.name, order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
|
||||||
if order.canceled_at:
|
if order.canceled_at:
|
||||||
#ic("deleting canceled order",order.id)
|
#ic("deleting canceled order",order.id)
|
||||||
todel.append(order)
|
todel.append(order)
|
||||||
@ -348,21 +367,22 @@ class Backtester:
|
|||||||
|
|
||||||
#ic(o.filled_at, o.filled_avg_price)
|
#ic(o.filled_at, o.filled_avg_price)
|
||||||
|
|
||||||
a = self.update_account(o = o)
|
a = self.update_internal_account(o = o)
|
||||||
if a < 0:
|
if a < 0:
|
||||||
tlog("update_account ERROR")
|
tlog("update_account ERROR")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
trade = TradeUpdate(order = o,
|
trade = TradeUpdate(account=o.account,
|
||||||
|
order = o,
|
||||||
event = TradeEvent.FILL,
|
event = TradeEvent.FILL,
|
||||||
execution_id = str(uuid4()),
|
execution_id = str(uuid4()),
|
||||||
timestamp = datetime.fromtimestamp(fill_time),
|
timestamp = datetime.fromtimestamp(fill_time),
|
||||||
position_qty= self.account[o.symbol][0],
|
position_qty= self.internal_account[o.symbol][0],
|
||||||
price=float(fill_price),
|
price=float(fill_price),
|
||||||
qty = o.qty,
|
qty = o.qty,
|
||||||
value = float(o.qty*fill_price),
|
value = float(o.qty*fill_price),
|
||||||
cash = self.cash,
|
cash = self.cash,
|
||||||
pos_avg_price = self.account[o.symbol][1])
|
pos_avg_price = self.internal_account[o.symbol][1])
|
||||||
|
|
||||||
self.trades.append(trade)
|
self.trades.append(trade)
|
||||||
|
|
||||||
@ -379,49 +399,49 @@ class Backtester:
|
|||||||
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
|
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
|
||||||
print("current bt.time",self.time)
|
print("current bt.time",self.time)
|
||||||
#print("FILL NOTIFICATION: ", tradeupdate)
|
#print("FILL NOTIFICATION: ", tradeupdate)
|
||||||
res = asyncio.run(self.order_fill_callback(tradeupdate))
|
res = asyncio.run(self.order_fill_callback(tradeupdate, tradeupdate.account))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def update_account(self, o: Order):
|
def update_internal_account(self, o: Order):
|
||||||
#updatujeme self.account
|
#updatujeme self.account
|
||||||
#pokud neexistuje klic v accountu vytvorime si ho
|
#pokud neexistuje klic v accountu vytvorime si ho
|
||||||
if o.symbol not in self.account:
|
if o.symbol not in self.internal_account[o.account.name]:
|
||||||
# { "BAC": [size, avgp] }
|
# { "BAC": [size, avgp] }
|
||||||
self.account[o.symbol] = [0,0]
|
self.internal_account[o.account.name][o.symbol] = [0,0]
|
||||||
|
|
||||||
if o.side == OrderSide.BUY:
|
if o.side == OrderSide.BUY:
|
||||||
#[size, avgp]
|
#[size, avgp]
|
||||||
newsize = (self.account[o.symbol][0] + o.qty)
|
newsize = (self.internal_account[o.account.name][o.symbol][0] + o.qty)
|
||||||
#JPLNE UZAVRENI SHORT (avgp 0)
|
#JPLNE UZAVRENI SHORT (avgp 0)
|
||||||
if newsize == 0: newavgp = 0
|
if newsize == 0: newavgp = 0
|
||||||
#CASTECNE UZAVRENI SHORT (avgp puvodni)
|
#CASTECNE UZAVRENI SHORT (avgp puvodni)
|
||||||
elif newsize < 0: newavgp = self.account[o.symbol][1]
|
elif newsize < 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
||||||
#JDE O LONG (avgp nove)
|
#JDE O LONG (avgp nove)
|
||||||
else:
|
else:
|
||||||
newavgp = ((self.account[o.symbol][0] * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.account[o.symbol][0] + o.qty)
|
newavgp = ((self.internal_account[o.account.name][o.symbol][0] * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.internal_account[account.name][o.symbol][0] + o.qty)
|
||||||
|
|
||||||
self.account[o.symbol] = [newsize, newavgp]
|
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
||||||
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
||||||
return 1
|
return 1
|
||||||
#sell
|
#sell
|
||||||
elif o.side == OrderSide.SELL:
|
elif o.side == OrderSide.SELL:
|
||||||
newsize = self.account[o.symbol][0]-o.qty
|
newsize = self.internal_account[o.account.name][o.symbol][0]-o.qty
|
||||||
#UPLNE UZAVRENI LONGU (avgp 0)
|
#UPLNE UZAVRENI LONGU (avgp 0)
|
||||||
if newsize == 0: newavgp = 0
|
if newsize == 0: newavgp = 0
|
||||||
#CASTECNE UZAVRENI LONGU (avgp puvodni)
|
#CASTECNE UZAVRENI LONGU (avgp puvodni)
|
||||||
elif newsize > 0: newavgp = self.account[o.symbol][1]
|
elif newsize > 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
||||||
#jde o SHORT (avgp nove)
|
#jde o SHORT (avgp nove)
|
||||||
else:
|
else:
|
||||||
#pokud je predchozi 0 - tzn. jde o prvni short
|
#pokud je predchozi 0 - tzn. jde o prvni short
|
||||||
if self.account[o.symbol][1] == 0:
|
if self.internal_account[o.account.name][o.symbol][1] == 0:
|
||||||
newavgp = o.filled_avg_price
|
newavgp = o.filled_avg_price
|
||||||
else:
|
else:
|
||||||
newavgp = ((abs(self.account[o.symbol][0]) * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.account[o.symbol][0]) + o.qty)
|
newavgp = ((abs(self.internal_account[o.account.name][o.symbol][0]) * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.internal_account[o.account.name][o.symbol][0]) + o.qty)
|
||||||
|
|
||||||
self.account[o.symbol] = [newsize, newavgp]
|
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
||||||
|
|
||||||
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
|
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
|
||||||
if self.account[o.symbol][0] >= 0:
|
if self.internal_account[o.account.name][o.symbol][0] >= 0:
|
||||||
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
||||||
print("uprava cashe, jde o prodej longu")
|
print("uprava cashe, jde o prodej longu")
|
||||||
else:
|
else:
|
||||||
@ -466,7 +486,7 @@ class Backtester:
|
|||||||
# #ic("get last price")
|
# #ic("get last price")
|
||||||
# return self.btdata[i-1][1]
|
# return self.btdata[i-1][1]
|
||||||
|
|
||||||
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, price: float = None):
|
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, account: Account, price: float = None):
|
||||||
"""submit order
|
"""submit order
|
||||||
- zakladni validace
|
- zakladni validace
|
||||||
- vloží do self.open_orders s daným časem
|
- vloží do self.open_orders s daným časem
|
||||||
@ -499,9 +519,9 @@ class Backtester:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
#pokud neexistuje klic v accountu vytvorime si ho
|
#pokud neexistuje klic v accountu vytvorime si ho
|
||||||
if symbol not in self.account:
|
if symbol not in self.internal_account[account.name]:
|
||||||
# { "BAC": [size, avgp] }
|
# { "BAC": [size, avgp] }
|
||||||
self.account[symbol] = [0,0]
|
self.internal_account[account.name][symbol] = [0,0]
|
||||||
|
|
||||||
#check for available quantity
|
#check for available quantity
|
||||||
if side == OrderSide.SELL:
|
if side == OrderSide.SELL:
|
||||||
@ -509,15 +529,15 @@ class Backtester:
|
|||||||
reserved_price = 0
|
reserved_price = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None:
|
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None and o.account==account:
|
||||||
reserved += o.qty
|
reserved += o.qty
|
||||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||||
reserved_price += o.qty * cena
|
reserved_price += o.qty * cena
|
||||||
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
|
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
|
||||||
|
|
||||||
actual_minus_reserved = int(self.account[symbol][0]) - reserved
|
actual_minus_reserved = int(self.internal_account[account.name][symbol][0]) - reserved
|
||||||
if actual_minus_reserved > 0 and actual_minus_reserved - int(size) < 0:
|
if actual_minus_reserved > 0 and actual_minus_reserved - int(size) < 0:
|
||||||
printanyway("not enough shares available to sell or shorting while long position",self.account[symbol][0],"reserved",reserved,"available",int(self.account[symbol][0]) - reserved,"selling",size)
|
printanyway("not enough shares available to sell or shorting while long position",self.internal_account[account.name][symbol][0],"reserved",reserved,"available",int(self.internal_account[account.name][symbol][0]) - reserved,"selling",size)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
#if is shorting - check available cash to short
|
#if is shorting - check available cash to short
|
||||||
@ -533,13 +553,13 @@ class Backtester:
|
|||||||
reserved_price = 0
|
reserved_price = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.side == OrderSide.BUY and o.canceled_at is None:
|
if o.side == OrderSide.BUY and o.canceled_at is None and o.account==account:
|
||||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||||
reserved_price += o.qty * cena
|
reserved_price += o.qty * cena
|
||||||
reserved_qty += o.qty
|
reserved_qty += o.qty
|
||||||
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
|
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
|
||||||
|
|
||||||
actual_plus_reserved_qty = int(self.account[symbol][0]) + reserved_qty
|
actual_plus_reserved_qty = int(self.internal_account[account.name][symbol][0]) + reserved_qty
|
||||||
|
|
||||||
#jde o uzavreni shortu
|
#jde o uzavreni shortu
|
||||||
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
|
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
|
||||||
@ -555,6 +575,7 @@ class Backtester:
|
|||||||
|
|
||||||
id = str(uuid4())
|
id = str(uuid4())
|
||||||
order = Order(id=id,
|
order = Order(id=id,
|
||||||
|
account=account,
|
||||||
submitted_at = datetime.fromtimestamp(float(time)),
|
submitted_at = datetime.fromtimestamp(float(time)),
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
order_type = order_type,
|
order_type = order_type,
|
||||||
@ -569,7 +590,7 @@ class Backtester:
|
|||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
def replace_order(self, id: str, time: float, size: int = None, price: float = None):
|
def replace_order(self, id: str, time: float, account: Account, size: int = None, price: float = None):
|
||||||
"""replace order
|
"""replace order
|
||||||
- zakladni validace vrací synchronně
|
- zakladni validace vrací synchronně
|
||||||
- vrací číslo nové objednávky
|
- vrací číslo nové objednávky
|
||||||
@ -586,7 +607,7 @@ class Backtester:
|
|||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
print(o.id)
|
print(o.id)
|
||||||
if str(o.id) == str(id):
|
if str(o.id) == str(id) and o.account == account:
|
||||||
newid = str(uuid4())
|
newid = str(uuid4())
|
||||||
o.id = newid
|
o.id = newid
|
||||||
o.submitted_at = datetime.fromtimestamp(time)
|
o.submitted_at = datetime.fromtimestamp(time)
|
||||||
@ -597,7 +618,7 @@ class Backtester:
|
|||||||
print("BT: replacement order doesnt exist")
|
print("BT: replacement order doesnt exist")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def cancel_order(self, time: float, id: str):
|
def cancel_order(self, time: float, id: str, account: Account):
|
||||||
"""cancel order
|
"""cancel order
|
||||||
- základní validace vrací synchronně
|
- základní validace vrací synchronně
|
||||||
- vymaže objednávku z open orders
|
- vymaže objednávku z open orders
|
||||||
@ -613,22 +634,22 @@ class Backtester:
|
|||||||
return 0
|
return 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if str(o.id) == id:
|
if str(o.id) == id and o.account == account:
|
||||||
o.canceled_at = time
|
o.canceled_at = time
|
||||||
print("set as canceled in self.open_orders")
|
print("set as canceled in self.open_orders")
|
||||||
return 1
|
return 1
|
||||||
print("BTC: cantchange. open order doesnt exist")
|
print("BTC: cantchange. open order doesnt exist")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_open_position(self, symbol: str):
|
def get_open_position(self, symbol: str, account: Account):
|
||||||
"""get positions ->(avg,size)"""
|
"""get positions ->(avg,size)"""
|
||||||
#print("BT:get open positions entry")
|
#print("BT:get open positions entry")
|
||||||
try:
|
try:
|
||||||
return self.account[symbol][1], self.account[symbol][0]
|
return self.internal_account[account.name][symbol][1], self.internal_account[account.name][symbol][0]
|
||||||
except:
|
except:
|
||||||
return (0,0)
|
return (0,0)
|
||||||
|
|
||||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
def get_open_orders(self, side: OrderSide, symbol: str, account: Account):
|
||||||
"""get open orders ->list(OrderNotification)"""
|
"""get open orders ->list(OrderNotification)"""
|
||||||
print("BT:get open orders entry")
|
print("BT:get open orders entry")
|
||||||
if len(self.open_orders) == 0:
|
if len(self.open_orders) == 0:
|
||||||
@ -638,7 +659,7 @@ class Backtester:
|
|||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
#print(o)
|
#print(o)
|
||||||
if o.symbol == symbol and o.canceled_at is None:
|
if o.symbol == symbol and o.canceled_at is None and o.account == account:
|
||||||
if side is None or o.side == side:
|
if side is None or o.side == side:
|
||||||
res.append(o)
|
res.append(o)
|
||||||
return res
|
return res
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
from datetime import datetime
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing import Any, Optional, List, Union
|
|
||||||
from uuid import UUID
|
|
||||||
class TradeStatus(str, Enum):
|
|
||||||
READY = "ready"
|
|
||||||
ACTIVATED = "activated"
|
|
||||||
CLOSED = "closed"
|
|
||||||
#FINISHED = "finished"
|
|
||||||
|
|
||||||
class TradeDirection(str, Enum):
|
|
||||||
LONG = "long"
|
|
||||||
SHORT = "short"
|
|
||||||
|
|
||||||
class TradeStoplossType(str, Enum):
|
|
||||||
FIXED = "fixed"
|
|
||||||
TRAILING = "trailing"
|
|
||||||
|
|
||||||
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
|
|
||||||
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
|
|
||||||
class Trade(BaseModel):
|
|
||||||
id: UUID
|
|
||||||
last_update: datetime
|
|
||||||
entry_time: Optional[datetime] = None
|
|
||||||
exit_time: Optional[datetime] = None
|
|
||||||
status: TradeStatus
|
|
||||||
generated_by: Optional[str] = None
|
|
||||||
direction: TradeDirection
|
|
||||||
entry_price: Optional[float] = None
|
|
||||||
goal_price: Optional[float] = None
|
|
||||||
size: Optional[int] = None
|
|
||||||
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
|
|
||||||
size_multiplier: Optional[float] = None
|
|
||||||
# stoploss_type: TradeStoplossType
|
|
||||||
stoploss_value: Optional[float] = None
|
|
||||||
profit: Optional[float] = 0
|
|
||||||
profit_sum: Optional[float] = 0
|
|
||||||
rel_profit: Optional[float] = 0
|
|
||||||
rel_profit_cum: Optional[float] = 0
|
|
||||||
|
|
||||||
@ -5,10 +5,75 @@ from rich import print
|
|||||||
from typing import Any, Optional, List, Union
|
from typing import Any, Optional, List, Union
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market
|
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market, Followup
|
||||||
from alpaca.data.enums import Exchange
|
from alpaca.data.enums import Exchange
|
||||||
|
from enum import Enum
|
||||||
|
from datetime import datetime
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Any, Optional, List, Union
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
#prescribed model
|
||||||
|
#from prescribed model
|
||||||
|
class InstantIndicator(BaseModel):
|
||||||
|
name: str
|
||||||
|
toml: str
|
||||||
|
|
||||||
|
|
||||||
|
class TradeStatus(str, Enum):
|
||||||
|
READY = "ready"
|
||||||
|
ACTIVATED = "activated"
|
||||||
|
CLOSED = "closed"
|
||||||
|
#FINISHED = "finished"
|
||||||
|
|
||||||
|
class TradeDirection(str, Enum):
|
||||||
|
LONG = "long"
|
||||||
|
SHORT = "short"
|
||||||
|
|
||||||
|
class TradeStoplossType(str, Enum):
|
||||||
|
FIXED = "fixed"
|
||||||
|
TRAILING = "trailing"
|
||||||
|
|
||||||
|
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
|
||||||
|
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
|
||||||
|
class Trade(BaseModel):
|
||||||
|
account: Account
|
||||||
|
id: UUID
|
||||||
|
last_update: datetime
|
||||||
|
entry_time: Optional[datetime] = None
|
||||||
|
exit_time: Optional[datetime] = None
|
||||||
|
status: TradeStatus
|
||||||
|
generated_by: Optional[str] = None
|
||||||
|
direction: TradeDirection
|
||||||
|
entry_price: Optional[float] = None
|
||||||
|
goal_price: Optional[float] = None
|
||||||
|
size: Optional[int] = None
|
||||||
|
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
|
||||||
|
size_multiplier: Optional[float] = None
|
||||||
|
# stoploss_type: TradeStoplossType
|
||||||
|
stoploss_value: Optional[float] = None
|
||||||
|
profit: Optional[float] = 0
|
||||||
|
profit_sum: Optional[float] = 0
|
||||||
|
rel_profit: Optional[float] = 0
|
||||||
|
rel_profit_cum: Optional[float] = 0
|
||||||
|
|
||||||
|
#account variables that can be accessed by ACCOUNT key dictionary
|
||||||
|
class AccountVariables(BaseModel):
|
||||||
|
positions: float = 0
|
||||||
|
avgp: float = 0
|
||||||
|
pending: str = None
|
||||||
|
blockbuy: int = 0
|
||||||
|
wait_for_fill: float = None
|
||||||
|
profit: float = 0
|
||||||
|
docasny_rel_profit: list = []
|
||||||
|
rel_profit_cum: list = []
|
||||||
|
last_entry_index: int = None #acc varianta, mame taky obnecnou state.vars.last_entry_index
|
||||||
|
requested_followup: Followup = None
|
||||||
|
activeTrade: Trade = None
|
||||||
|
dont_exit_already_activated: bool = False
|
||||||
|
#activeTrade, prescribedTrades
|
||||||
|
#tbd transferables?
|
||||||
|
|
||||||
|
|
||||||
#models for server side datatables
|
#models for server side datatables
|
||||||
@ -91,7 +156,7 @@ class TestList(BaseModel):
|
|||||||
dates: List[Intervals]
|
dates: List[Intervals]
|
||||||
|
|
||||||
#for GUI to fetch historical trades on given symbol
|
#for GUI to fetch historical trades on given symbol
|
||||||
class Trade(BaseModel):
|
class TradeView(BaseModel):
|
||||||
symbol: str
|
symbol: str
|
||||||
timestamp: datetime
|
timestamp: datetime
|
||||||
exchange: Optional[Union[Exchange, str]] = None
|
exchange: Optional[Union[Exchange, str]] = None
|
||||||
@ -189,8 +254,8 @@ class RunnerView(BaseModel):
|
|||||||
run_symbol: Optional[str] = None
|
run_symbol: Optional[str] = None
|
||||||
run_trade_count: Optional[int] = 0
|
run_trade_count: Optional[int] = 0
|
||||||
run_profit: Optional[float] = 0
|
run_profit: Optional[float] = 0
|
||||||
run_positions: Optional[int] = 0
|
run_positions: Optional[dict] = 0
|
||||||
run_avgp: Optional[float] = 0
|
run_avgp: Optional[dict] = 0
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
|
|
||||||
@ -208,8 +273,8 @@ class Runner(BaseModel):
|
|||||||
run_ilog_save: Optional[bool] = False
|
run_ilog_save: Optional[bool] = False
|
||||||
run_trade_count: Optional[int] = None
|
run_trade_count: Optional[int] = None
|
||||||
run_profit: Optional[float] = None
|
run_profit: Optional[float] = None
|
||||||
run_positions: Optional[int] = None
|
run_positions: Optional[dict] = None
|
||||||
run_avgp: Optional[float] = None
|
run_avgp: Optional[dict] = None
|
||||||
run_strat_json: Optional[str] = None
|
run_strat_json: Optional[str] = None
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
@ -247,6 +312,7 @@ class Bar(BaseModel):
|
|||||||
vwap: Optional[float] = 0
|
vwap: Optional[float] = 0
|
||||||
|
|
||||||
class Order(BaseModel):
|
class Order(BaseModel):
|
||||||
|
account: Account
|
||||||
id: UUID
|
id: UUID
|
||||||
submitted_at: datetime
|
submitted_at: datetime
|
||||||
filled_at: Optional[datetime] = None
|
filled_at: Optional[datetime] = None
|
||||||
@ -262,6 +328,7 @@ class Order(BaseModel):
|
|||||||
|
|
||||||
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
|
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
|
||||||
class TradeUpdate(BaseModel):
|
class TradeUpdate(BaseModel):
|
||||||
|
account: Account
|
||||||
event: Union[TradeEvent, str]
|
event: Union[TradeEvent, str]
|
||||||
execution_id: Optional[UUID] = None
|
execution_id: Optional[UUID] = None
|
||||||
order: Order
|
order: Order
|
||||||
@ -307,8 +374,8 @@ class RunArchive(BaseModel):
|
|||||||
ilog_save: Optional[bool] = False
|
ilog_save: Optional[bool] = False
|
||||||
profit: float = 0
|
profit: float = 0
|
||||||
trade_count: int = 0
|
trade_count: int = 0
|
||||||
end_positions: int = 0
|
end_positions: Union[dict,str] = None
|
||||||
end_positions_avgp: float = 0
|
end_positions_avgp: Union[dict,str] = None
|
||||||
metrics: Union[dict, str] = None
|
metrics: Union[dict, str] = None
|
||||||
stratvars_toml: Optional[str] = None
|
stratvars_toml: Optional[str] = None
|
||||||
|
|
||||||
@ -329,8 +396,8 @@ class RunArchiveView(BaseModel):
|
|||||||
ilog_save: Optional[bool] = False
|
ilog_save: Optional[bool] = False
|
||||||
profit: float = 0
|
profit: float = 0
|
||||||
trade_count: int = 0
|
trade_count: int = 0
|
||||||
end_positions: int = 0
|
end_positions: Union[dict,int] = None
|
||||||
end_positions_avgp: float = 0
|
end_positions_avgp: Union[dict,float] = None
|
||||||
metrics: Union[dict, str] = None
|
metrics: Union[dict, str] = None
|
||||||
batch_profit: float = 0 # Total profit for the batch - now calculated during query
|
batch_profit: float = 0 # Total profit for the batch - now calculated during query
|
||||||
batch_count: int = 0 # Count of runs in the batch - now calculated during query
|
batch_count: int = 0 # Count of runs in the batch - now calculated during query
|
||||||
@ -359,9 +426,3 @@ class RunArchiveDetail(BaseModel):
|
|||||||
trades: List[TradeUpdate]
|
trades: List[TradeUpdate]
|
||||||
ext_data: Optional[dict] = None
|
ext_data: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
class InstantIndicator(BaseModel):
|
|
||||||
name: str
|
|
||||||
toml: str
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -51,8 +51,8 @@ def row_to_runarchiveview(row: dict) -> RunArchiveView:
|
|||||||
ilog_save=bool(row['ilog_save']),
|
ilog_save=bool(row['ilog_save']),
|
||||||
profit=float(row['profit']),
|
profit=float(row['profit']),
|
||||||
trade_count=int(row['trade_count']),
|
trade_count=int(row['trade_count']),
|
||||||
end_positions=int(row['end_positions']),
|
end_positions=orjson.loads(row['end_positions']),
|
||||||
end_positions_avgp=float(row['end_positions_avgp']),
|
end_positions_avgp=orjson.loads(row['end_positions_avgp']),
|
||||||
metrics=orjson.loads(row['metrics']) if row['metrics'] else None,
|
metrics=orjson.loads(row['metrics']) if row['metrics'] else None,
|
||||||
batch_profit=int(row['batch_profit']) if row['batch_profit'] and row['batch_id'] else 0,
|
batch_profit=int(row['batch_profit']) if row['batch_profit'] and row['batch_id'] else 0,
|
||||||
batch_count=int(row['batch_count']) if row['batch_count'] and row['batch_id'] else 0,
|
batch_count=int(row['batch_count']) if row['batch_count'] and row['batch_id'] else 0,
|
||||||
@ -79,8 +79,8 @@ def row_to_runarchive(row: dict) -> RunArchive:
|
|||||||
ilog_save=bool(row['ilog_save']),
|
ilog_save=bool(row['ilog_save']),
|
||||||
profit=float(row['profit']),
|
profit=float(row['profit']),
|
||||||
trade_count=int(row['trade_count']),
|
trade_count=int(row['trade_count']),
|
||||||
end_positions=int(row['end_positions']),
|
end_positions=str(row['end_positions']),
|
||||||
end_positions_avgp=float(row['end_positions_avgp']),
|
end_positions_avgp=str(row['end_positions_avgp']),
|
||||||
metrics=orjson.loads(row['metrics']),
|
metrics=orjson.loads(row['metrics']),
|
||||||
stratvars_toml=row['stratvars_toml'],
|
stratvars_toml=row['stratvars_toml'],
|
||||||
transferables=orjson.loads(row['transferables']) if row['transferables'] else None
|
transferables=orjson.loads(row['transferables']) if row['transferables'] else None
|
||||||
|
|||||||
@ -66,10 +66,10 @@ def get_key(mode: Mode, account: Account):
|
|||||||
return None
|
return None
|
||||||
dict = globals()
|
dict = globals()
|
||||||
try:
|
try:
|
||||||
API_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_API_KEY" ]
|
API_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_API_KEY" ]
|
||||||
SECRET_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_SECRET_KEY" ]
|
SECRET_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_SECRET_KEY" ]
|
||||||
PAPER = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_PAPER" ]
|
PAPER = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_PAPER" ]
|
||||||
FEED = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_FEED" ]
|
FEED = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_FEED" ]
|
||||||
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print("Not valid combination to get keys for", mode, account)
|
print("Not valid combination to get keys for", mode, account)
|
||||||
@ -93,7 +93,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_PAPER_FEED', 'iex') # Default to
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||||
except ValueError:
|
except nameError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# Handle the case where the environment variable does not match any enum member
|
||||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_PAPER_FEED defaulting to 'iex'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_PAPER_FEED defaulting to 'iex'")
|
||||||
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
||||||
@ -111,7 +111,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_LIVE_FEED', 'iex') # Default to '
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||||
except ValueError:
|
except nameError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# Handle the case where the environment variable does not match any enum member
|
||||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_LIVE_FEED defaulting to 'iex'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_LIVE_FEED defaulting to 'iex'")
|
||||||
ACCOUNT1_LIVE_FEED = DataFeed.IEX
|
ACCOUNT1_LIVE_FEED = DataFeed.IEX
|
||||||
@ -129,7 +129,7 @@ data_feed_type_str = os.environ.get('ACCOUNT2_PAPER_FEED', 'iex') # Default to
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||||
except ValueError:
|
except nameError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# Handle the case where the environment variable does not match any enum member
|
||||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_PAPER_FEED defaulting to 'iex'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_PAPER_FEED defaulting to 'iex'")
|
||||||
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
||||||
@ -148,7 +148,7 @@ except ValueError:
|
|||||||
# # Convert the string to DataFeed enum
|
# # Convert the string to DataFeed enum
|
||||||
# try:
|
# try:
|
||||||
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
|
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||||
# except ValueError:
|
# except nameError:
|
||||||
# # Handle the case where the environment variable does not match any enum member
|
# # Handle the case where the environment variable does not match any enum member
|
||||||
# print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_LIVE_FEED defaulting to 'iex'")
|
# print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_LIVE_FEED defaulting to 'iex'")
|
||||||
# ACCOUNT2_LIVE_FEED = DataFeed.IEX
|
# ACCOUNT2_LIVE_FEED = DataFeed.IEX
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from uuid import UUID, uuid4
|
|||||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
||||||
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
||||||
from v2realbot.utils.ilog import delete_logs
|
from v2realbot.utils.ilog import delete_logs
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||||
from threading import Thread, current_thread, Event, enumerate
|
from threading import Thread, current_thread, Event, enumerate
|
||||||
|
|||||||
@ -8,9 +8,9 @@ from alpaca.data.timeframe import TimeFrame
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
|
||||||
from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
||||||
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data, gaka
|
||||||
from v2realbot.utils.ilog import delete_logs
|
from v2realbot.utils.ilog import delete_logs
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||||
from threading import Thread, current_thread, Event, enumerate
|
from threading import Thread, current_thread, Event, enumerate
|
||||||
@ -71,8 +71,8 @@ def get_all_runners():
|
|||||||
if i.run_instance:
|
if i.run_instance:
|
||||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||||
i.run_positions = i.run_instance.state.positions
|
i.run_positions = gaka(i.run_instance.state.account_variables, "positions")
|
||||||
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
||||||
return (0, db.runners)
|
return (0, db.runners)
|
||||||
else:
|
else:
|
||||||
return (0, [])
|
return (0, [])
|
||||||
@ -94,8 +94,8 @@ def get_runner(id: UUID):
|
|||||||
if str(i.id) == str(id):
|
if str(i.id) == str(id):
|
||||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||||
i.run_positions = i.run_instance.state.positions
|
i.run_positions =gaka(i.run_instance.state.account_variables, "positions")
|
||||||
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
||||||
return (0, i)
|
return (0, i)
|
||||||
return (-2, "not found")
|
return (-2, "not found")
|
||||||
|
|
||||||
@ -738,13 +738,14 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
|||||||
|
|
||||||
tradeList = strat.state.tradeList
|
tradeList = strat.state.tradeList
|
||||||
|
|
||||||
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
trade_dict = AttributeDict(account=[],orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
||||||
if strat.mode == Mode.BT:
|
if strat.mode == Mode.BT:
|
||||||
trade_dict["value"] = []
|
trade_dict["value"] = []
|
||||||
trade_dict["cash"] = []
|
trade_dict["cash"] = []
|
||||||
trade_dict["pos_avg_price"] = []
|
trade_dict["pos_avg_price"] = []
|
||||||
for t in tradeList:
|
for t in tradeList:
|
||||||
if t.event == TradeEvent.FILL:
|
if t.event == TradeEvent.FILL:
|
||||||
|
trade_dict.account.append(t.account)
|
||||||
trade_dict.orderid.append(str(t.order.id))
|
trade_dict.orderid.append(str(t.order.id))
|
||||||
trade_dict.timestamp.append(t.timestamp)
|
trade_dict.timestamp.append(t.timestamp)
|
||||||
trade_dict.symbol.append(t.order.symbol)
|
trade_dict.symbol.append(t.order.symbol)
|
||||||
@ -768,10 +769,12 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
|||||||
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
|
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
|
||||||
max_positions = max_positions.drop(columns=['side'], axis=1)
|
max_positions = max_positions.drop(columns=['side'], axis=1)
|
||||||
|
|
||||||
res = dict(profit={})
|
res = dict(account_variables={}, profit={})
|
||||||
#filt = max_positions['side'] == 'OrderSide.BUY'
|
#filt = max_positions['side'] == 'OrderSide.BUY'
|
||||||
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
|
||||||
|
|
||||||
|
res["account_variables"] = transform_data(strat.state.account_variables, json_serial)
|
||||||
|
|
||||||
|
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
||||||
#naplneni batch sum profitu
|
#naplneni batch sum profitu
|
||||||
if inter_batch_params is not None:
|
if inter_batch_params is not None:
|
||||||
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
|
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
|
||||||
@ -923,8 +926,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
|
|||||||
settings = settings,
|
settings = settings,
|
||||||
profit=round(float(strat.state.profit),2),
|
profit=round(float(strat.state.profit),2),
|
||||||
trade_count=len(strat.state.tradeList),
|
trade_count=len(strat.state.tradeList),
|
||||||
end_positions=strat.state.positions,
|
end_positions=gaka(strat.state.account_variables, "positions"),
|
||||||
end_positions_avgp=round(float(strat.state.avgp),3),
|
end_positions_avgp=gaka(strat.state.account_variables, "avgp", lambda x: round(float(x),3)),
|
||||||
metrics=results_metrics,
|
metrics=results_metrics,
|
||||||
stratvars_toml=runner.run_stratvars_toml,
|
stratvars_toml=runner.run_stratvars_toml,
|
||||||
transferables=strat.state.vars["transferables"]
|
transferables=strat.state.vars["transferables"]
|
||||||
@ -1264,6 +1267,7 @@ def insert_archive_header(archeader: RunArchive):
|
|||||||
try:
|
try:
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
|
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
|
||||||
|
print(archeader)
|
||||||
|
|
||||||
res = c.execute("""
|
res = c.execute("""
|
||||||
INSERT INTO runner_header
|
INSERT INTO runner_header
|
||||||
@ -1271,7 +1275,7 @@ def insert_archive_header(archeader: RunArchive):
|
|||||||
VALUES
|
VALUES
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, archeader.end_positions, archeader.end_positions_avgp, orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
|
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, orjson.dumps(archeader.end_positions).decode('utf-8'), orjson.dumps(archeader.end_positions_avgp).decode('utf-8'), orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
|
||||||
|
|
||||||
#retry not yet supported for statement format above
|
#retry not yet supported for statement format above
|
||||||
#res = execute_with_retry(c,statement)
|
#res = execute_with_retry(c,statement)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from v2realbot.backtesting.backtester import Backtester
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.utils.utils import zoneNY
|
from v2realbot.utils.utils import zoneNY
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
|
from v2realbot.common.model import Account
|
||||||
|
|
||||||
""""
|
""""
|
||||||
backtester methods can be called
|
backtester methods can be called
|
||||||
@ -16,8 +17,9 @@ both should be backtestable
|
|||||||
if method are called for the past self.time must be set accordingly
|
if method are called for the past self.time must be set accordingly
|
||||||
"""
|
"""
|
||||||
class BacktestInterface(GeneralInterface):
|
class BacktestInterface(GeneralInterface):
|
||||||
def __init__(self, symbol, bt: Backtester) -> None:
|
def __init__(self, symbol, bt: Backtester, account: Account) -> None:
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
|
self.account = account
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
|
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
|
||||||
self.mincnt = list([dict(minute=0,count=0)])
|
self.mincnt = list([dict(minute=0,count=0)])
|
||||||
@ -43,48 +45,48 @@ class BacktestInterface(GeneralInterface):
|
|||||||
def buy(self, size = 1, repeat: bool = False):
|
def buy(self, size = 1, repeat: bool = False):
|
||||||
self.count()
|
self.count()
|
||||||
#add REST API latency
|
#add REST API latency
|
||||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET)
|
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET, account=self.account)
|
||||||
|
|
||||||
"""buy limit"""
|
"""buy limit"""
|
||||||
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT)
|
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
|
||||||
|
|
||||||
"""sell market"""
|
"""sell market"""
|
||||||
def sell(self, size = 1, repeat: bool = False):
|
def sell(self, size = 1, repeat: bool = False):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET)
|
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET, account=self.account)
|
||||||
|
|
||||||
"""sell limit"""
|
"""sell limit"""
|
||||||
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT)
|
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
|
||||||
|
|
||||||
"""replace order"""
|
"""replace order"""
|
||||||
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
|
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price)
|
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price, account=self.account)
|
||||||
|
|
||||||
"""cancel order"""
|
"""cancel order"""
|
||||||
#TBD exec predtim?
|
#TBD exec predtim?
|
||||||
def cancel(self, orderid: str):
|
def cancel(self, orderid: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid)
|
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid, account=self.account)
|
||||||
|
|
||||||
"""get positions ->(size,avgp)"""
|
"""get positions ->(size,avgp)"""
|
||||||
#TBD exec predtim?
|
#TBD exec predtim?
|
||||||
def pos(self):
|
def pos(self):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_open_position(symbol=self.symbol)
|
return self.bt.get_open_position(symbol=self.symbol, account=self.account)
|
||||||
|
|
||||||
"""get open orders ->list(Order)"""
|
"""get open orders ->list(Order)"""
|
||||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_open_orders(side=side, symbol=symbol)
|
return self.bt.get_open_orders(side=side, symbol=symbol, account=self.account)
|
||||||
|
|
||||||
def get_last_price(self, symbol: str):
|
def get_last_price(self, symbol: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_last_price(time=self.bt.time)
|
return self.bt.get_last_price(time=self.bt.time, account=self.account)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class LiveInterface(GeneralInterface):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
"""sell limit"""
|
"""sell limit"""
|
||||||
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.repeat = repeat
|
self.repeat = repeat
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ class LiveInterface(GeneralInterface):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
"""order replace"""
|
"""order replace"""
|
||||||
async def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
||||||
|
|
||||||
if not price and not size:
|
if not price and not size:
|
||||||
print("price or size has to be filled")
|
print("price or size has to be filled")
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from alpaca.trading.stream import TradingStream
|
from alpaca.trading.stream import TradingStream
|
||||||
from v2realbot.config import Keys
|
from v2realbot.config import Keys
|
||||||
|
from v2realbot.common.model import Account
|
||||||
|
|
||||||
#jelikoz Alpaca podporuje pripojeni libovolneho poctu websocket instanci na order updates
|
#jelikoz Alpaca podporuje pripojeni libovolneho poctu websocket instanci na order updates
|
||||||
#vytvorime pro kazdou bezici instanci vlastni webservisu (jinak bychom museli delat instanci pro kombinaci ACCOUNT1 - LIVE, ACCOUNT1 - PAPER, ACCOUNT2 - PAPER ..)
|
#vytvorime pro kazdou bezici instanci vlastni webservisu (jinak bychom museli delat instanci pro kombinaci ACCOUNT1 - LIVE, ACCOUNT1 - PAPER, ACCOUNT2 - PAPER ..)
|
||||||
@ -14,15 +15,16 @@ As Alpaca supports connecting of any number of trade updates clients
|
|||||||
new instance of this websocket thread is created for each strategy instance.
|
new instance of this websocket thread is created for each strategy instance.
|
||||||
"""""
|
"""""
|
||||||
class LiveOrderUpdatesStreamer(Thread):
|
class LiveOrderUpdatesStreamer(Thread):
|
||||||
def __init__(self, key: Keys, name: str) -> None:
|
def __init__(self, key: Keys, name: str, account: Account) -> None:
|
||||||
self.key = key
|
self.key = key
|
||||||
|
self.account = account
|
||||||
self.strategy = None
|
self.strategy = None
|
||||||
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
|
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
|
||||||
Thread.__init__(self, name=name)
|
Thread.__init__(self, name=name)
|
||||||
|
|
||||||
#notif dispatcher - pouze 1 strategie
|
#notif dispatcher - pouze 1 strategie
|
||||||
async def distributor(self,data):
|
async def distributor(self,data):
|
||||||
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data)
|
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data, self.account)
|
||||||
|
|
||||||
# connects callback to interface object - responses for given symbol are routed to interface callback
|
# connects callback to interface object - responses for given symbol are routed to interface callback
|
||||||
def connect_callback(self, st):
|
def connect_callback(self, st):
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from fastapi.security import APIKeyHeader
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from v2realbot.utils.ilog import get_log_window
|
from v2realbot.utils.ilog import get_log_window
|
||||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
|
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, TradeView, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
|
||||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query, Request
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query, Request
|
||||||
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
|
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
@ -334,7 +334,7 @@ def stop_all_runners():
|
|||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
||||||
|
|
||||||
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
|
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
|
||||||
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[Trade]:
|
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[TradeView]:
|
||||||
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
|
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
return set
|
return set
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
@ -23,7 +23,7 @@ from collections import defaultdict
|
|||||||
from scipy.stats import zscore
|
from scipy.stats import zscore
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Tuple, Optional, List
|
from typing import Tuple, Optional, List
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
|
|
||||||
def load_trades(runner_ids: List = None, batch_id: str = None) -> Tuple[int, List[Trade], int]:
|
def load_trades(runner_ids: List = None, batch_id: str = None) -> Tuple[int, List[Trade], int]:
|
||||||
if runner_ids is None and batch_id is None:
|
if runner_ids is None and batch_id is None:
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from uuid import UUID, uuid4
|
|||||||
from v2realbot.enums.enums import Moddus, SchedulerStatus, RecordType, StartBarAlign, Mode, Account, OrderSide
|
from v2realbot.enums.enums import Moddus, SchedulerStatus, RecordType, StartBarAlign, Mode, Account, OrderSide
|
||||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest, Market
|
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest, Market
|
||||||
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.config import JOB_LOG_FILE, STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR, MEDIA_DIRECTORY, RUNNER_DETAIL_DIRECTORY
|
from v2realbot.config import JOB_LOG_FILE, STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR, MEDIA_DIRECTORY, RUNNER_DETAIL_DIRECTORY
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|||||||
@ -172,7 +172,7 @@ function initialize_archiveRecords() {
|
|||||||
{
|
{
|
||||||
targets: [13,14,15],
|
targets: [13,14,15],
|
||||||
render: function ( data, type, row ) {
|
render: function ( data, type, row ) {
|
||||||
return '<div class="tdsmall">'+data+'</div>'
|
return '<div class="tdsmall">'+JSON.stringify(data, null, 2)+'</div>'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from v2realbot.utils.tlog import tlog, tlog_exception
|
|||||||
from v2realbot.enums.enums import Mode, Order, Account, RecordType, Followup
|
from v2realbot.enums.enums import Mode, Order, Account, RecordType, Followup
|
||||||
#from alpaca.trading.models import TradeUpdate
|
#from alpaca.trading.models import TradeUpdate
|
||||||
from v2realbot.common.model import TradeUpdate
|
from v2realbot.common.model import TradeUpdate
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from v2realbot.indicators.indicators import ema
|
from v2realbot.indicators.indicators import ema
|
||||||
import orjson
|
import orjson
|
||||||
@ -44,7 +44,8 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
self.state.vars.pending = "max_sum_profit_to_quit_rel"
|
for account in self.accounts:
|
||||||
|
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit_rel"
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -57,7 +58,8 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
self.state.vars.pending = "max_sum_loss_to_quit_rel"
|
for account in self.accounts:
|
||||||
|
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit_rel"
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -71,7 +73,8 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
self.state.vars.pending = "max_sum_profit_to_quit"
|
for account in self.accounts:
|
||||||
|
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit"
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -84,7 +87,8 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
self.state.vars.pending = "max_sum_loss_to_quit"
|
for account in self.accounts:
|
||||||
|
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit"
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -95,9 +99,10 @@ 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, account: Account):
|
||||||
trade_to_add = Trade(
|
trade_to_add = Trade(
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
|
account=account,
|
||||||
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
size=size,
|
size=size,
|
||||||
@ -108,45 +113,48 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
self.state.vars.prescribedTrades.append(trade_to_add)
|
self.state.vars.prescribedTrades.append(trade_to_add)
|
||||||
|
|
||||||
self.state.vars.requested_followup = None
|
self.state.account_variables[account.name].requested_followup = None
|
||||||
|
|
||||||
self.state.ilog(e=f"FOLLOWUP {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
|
self.state.ilog(e=f"FOLLOWUP {direction} - {account} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
|
||||||
|
|
||||||
async def orderUpdateBuy(self, data: TradeUpdate):
|
async def orderUpdateBuy(self, data: TradeUpdate):
|
||||||
o: Order = data.order
|
o: Order = data.order
|
||||||
signal_name = None
|
signal_name = None
|
||||||
|
account = data.account
|
||||||
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
||||||
self.state.ilog(e="Příchozí BUY notif", msg=o.status, trade=transform_data(data, json_serial))
|
self.state.ilog(e="Příchozí BUY notif"+account, msg=o.status, trade=transform_data(data, json_serial))
|
||||||
|
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
||||||
|
|
||||||
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
||||||
#jde o uzavření short pozice - počítáme PROFIT
|
#jde o uzavření short pozice - počítáme PROFIT
|
||||||
if int(self.state.positions) < 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
|
if int(self.state.account_variables[account.name].positions) < 0 or (int(self.state.account_variables[account.name].positions) == 0 and self.state.account_variables[account.name].wait_for_fill is not None):
|
||||||
|
|
||||||
if data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].wait_for_fill is None:
|
||||||
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
||||||
self.state.wait_for_fill = float(self.state.avgp)
|
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
|
||||||
|
|
||||||
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
bought_amount = data.qty * data.price
|
bought_amount = data.qty * data.price
|
||||||
#podle prumerne vstupni ceny, kolik stalo toto mnozstvi
|
#podle prumerne vstupni ceny, kolik stalo toto mnozstvi
|
||||||
if float(self.state.avgp) > 0:
|
if float(self.state.account_variables[account.name].avgp) > 0:
|
||||||
vstup_cena = float(self.state.avgp)
|
vstup_cena = float(self.state.account_variables[account.name].avgp)
|
||||||
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
|
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
|
||||||
vstup_cena = float(self.state.wait_for_fill)
|
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
|
||||||
else:
|
else:
|
||||||
vstup_cena = 0
|
vstup_cena = 0
|
||||||
|
|
||||||
avg_costs = vstup_cena * float(data.qty)
|
avg_costs = vstup_cena * float(data.qty)
|
||||||
|
|
||||||
if avg_costs == 0:
|
if avg_costs == 0:
|
||||||
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0"+account, msg="naklady=utrzena cena. TBD opravit.")
|
||||||
avg_costs = bought_amount
|
avg_costs = bought_amount
|
||||||
|
|
||||||
trade_profit = round((avg_costs-bought_amount),2)
|
trade_profit = round((avg_costs-bought_amount),2)
|
||||||
self.state.profit += trade_profit
|
#celkovy profit
|
||||||
|
self.state.profit += trade_profit #overall abs profit
|
||||||
|
self.state.account_variables[account.name].profit += trade_profit #account profit
|
||||||
|
|
||||||
rel_profit = 0
|
rel_profit = 0
|
||||||
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
||||||
@ -160,30 +168,32 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
||||||
if data.position_qty != 0:
|
if data.position_qty != 0:
|
||||||
self.state.docasny_rel_profit.append(rel_profit)
|
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
||||||
partial_exit = True
|
partial_exit = True
|
||||||
else:
|
else:
|
||||||
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
||||||
if len(self.state.docasny_rel_profit) > 0:
|
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
|
||||||
#pricteme aktualni rel profit
|
#pricteme aktualni rel profit
|
||||||
self.state.docasny_rel_profit.append(rel_profit)
|
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
||||||
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
||||||
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
|
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
|
||||||
self.state.docasny_rel_profit = []
|
self.state.account_variables[account.name].docasny_rel_profit = []
|
||||||
partial_last = True
|
partial_last = True
|
||||||
|
|
||||||
self.state.rel_profit_cum.append(rel_profit)
|
self.state.rel_profit_cum.append(rel_profit) #overall cum rel profit
|
||||||
|
self.state.account_variables[account.name].rel_profit_cum.append(rel_profit) #account cum rel profit
|
||||||
|
|
||||||
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
||||||
|
|
||||||
#pro martingale updatujeme loss_series_cnt
|
#pro martingale updatujeme loss_series_cnt -
|
||||||
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
||||||
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
||||||
|
|
||||||
self.state.ilog(e=f"BUY notif - SHORT PROFIT: {partial_exit=} {partial_last=} {round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
self.state.ilog(e=f"BUY notif {account} - SHORT PROFIT: {partial_exit=} {partial_last=} {round(float(trade_profit),3)} celkem abs:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
||||||
|
|
||||||
#zapsat profit do prescr.trades
|
#zapsat profit do prescr.trades
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.vars.pending:
|
if trade.id == self.state.account_variables[account.name].pending:
|
||||||
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
||||||
trade.profit += trade_profit
|
trade.profit += trade_profit
|
||||||
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
||||||
@ -201,7 +211,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#mazeme self.state.
|
#mazeme self.state.
|
||||||
self.state.wait_for_fill = None
|
self.state.account_variables[account.name].wait_for_fill = None
|
||||||
#zapsat update profitu do tradeList
|
#zapsat update profitu do tradeList
|
||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
@ -209,7 +219,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
setattr(tradeData, "profit", trade_profit)
|
setattr(tradeData, "profit", trade_profit)
|
||||||
setattr(tradeData, "profit_sum", self.state.profit)
|
setattr(tradeData, "profit_sum", self.state.profit)
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
||||||
#self.state.ilog(f"updatnut tradeList o profit", tradeData=orjson.loads(orjson.dumps(tradeData, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)))
|
#self.state.ilog(f"updatnut tradeList o profit", tradeData=orjson.loads(orjson.dumps(tradeData, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)))
|
||||||
setattr(tradeData, "rel_profit", rel_profit)
|
setattr(tradeData, "rel_profit", rel_profit)
|
||||||
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
||||||
@ -220,16 +230,16 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||||
#jen při celém FILLU
|
#jen při celém FILLU
|
||||||
if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
|
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
|
||||||
if self.state.vars.requested_followup == Followup.REVERSE:
|
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
|
||||||
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name, account=account)
|
||||||
elif self.state.vars.requested_followup == Followup.ADD:
|
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
|
||||||
#zatim stejna SIZE
|
#zatim stejna SIZE
|
||||||
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name, account=account)
|
||||||
else:
|
else:
|
||||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.vars.pending:
|
if trade.id == self.state.account_variables[account.name].pending:
|
||||||
signal_name = trade.generated_by
|
signal_name = trade.generated_by
|
||||||
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
||||||
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
||||||
@ -239,7 +249,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
||||||
|
|
||||||
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
|
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
|
||||||
|
|
||||||
@ -248,43 +258,47 @@ class StrategyClassicSL(Strategy):
|
|||||||
self.state.last_entry_price["long"] = data.price
|
self.state.last_entry_price["long"] = data.price
|
||||||
|
|
||||||
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
||||||
if self.state.vars.activeTrade.goal_price is None:
|
if self.state.account_variables[account.name].activeTrade.goal_price is None:
|
||||||
dat = dict(close=data.price)
|
dat = dict(close=data.price)
|
||||||
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.LONG)
|
self.state.account_variables[account.name].activeTrade.goal_price = get_profit_target_price(self.state, dat, self.state.account_variables[account.name].activeTrade, 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
|
||||||
self.state.positions = data.position_qty
|
self.state.account_variables[account.name].positions = data.position_qty
|
||||||
self.state.avgp, self.state.positions = self.state.interface.pos()
|
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.state.interface.pos()
|
||||||
|
|
||||||
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
||||||
#davame pryc pending
|
#davame pryc pending
|
||||||
self.state.vars.pending = None
|
self.state.account_variables[account.name].pending = None
|
||||||
|
|
||||||
|
|
||||||
async def orderUpdateSell(self, data: TradeUpdate):
|
async def orderUpdateSell(self, data: TradeUpdate):
|
||||||
|
|
||||||
self.state.ilog(e="Příchozí SELL notif", msg=data.order.status, trade=transform_data(data, json_serial))
|
account = data.account
|
||||||
|
#TODO tady jsem skoncil, pak projit vsechen kod na state.avgp a prehodit
|
||||||
|
|
||||||
|
|
||||||
|
self.state.ilog(e=f"Příchozí SELL notif - {account}", msg=data.order.status, trade=transform_data(data, json_serial))
|
||||||
|
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
||||||
|
|
||||||
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
||||||
#jde o uzavření long pozice - počítáme PROFIT
|
#jde o uzavření long pozice - počítáme PROFIT
|
||||||
if int(self.state.positions) > 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
|
if int(self.state.account_variables[account.name].positions) > 0 or (int(self.state.account_variables[account.name].positions) == 0 and self.state.account_variables[account.name].wait_for_fill is not None):
|
||||||
|
|
||||||
if data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].wait_for_fill is None:
|
||||||
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
||||||
self.state.wait_for_fill = float(self.state.avgp)
|
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
|
||||||
|
|
||||||
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
sold_amount = data.qty * data.price
|
sold_amount = data.qty * data.price
|
||||||
if float(self.state.avgp) > 0:
|
if float(self.state.account_variables[account.name].avgp) > 0:
|
||||||
vstup_cena = float(self.state.avgp)
|
vstup_cena = float(self.state.account_variables[account.name].avgp)
|
||||||
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
|
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
|
||||||
vstup_cena = float(self.state.wait_for_fill)
|
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
|
||||||
else:
|
else:
|
||||||
vstup_cena = 0
|
vstup_cena = 0
|
||||||
|
|
||||||
@ -292,12 +306,13 @@ class StrategyClassicSL(Strategy):
|
|||||||
avg_costs = vstup_cena * float(data.qty)
|
avg_costs = vstup_cena * float(data.qty)
|
||||||
|
|
||||||
if avg_costs == 0:
|
if avg_costs == 0:
|
||||||
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
self.state.ilog(e=f"ERR: {account} Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
||||||
avg_costs = sold_amount
|
avg_costs = sold_amount
|
||||||
|
|
||||||
trade_profit = round((sold_amount - avg_costs),2)
|
trade_profit = round((sold_amount - avg_costs),2)
|
||||||
self.state.profit += trade_profit
|
self.state.profit += trade_profit #celkový profit
|
||||||
|
self.state.account_variables[account.name].profit += trade_profit #account specific profit
|
||||||
|
|
||||||
rel_profit = 0
|
rel_profit = 0
|
||||||
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
||||||
if vstup_cena != 0 and data.order.qty != 0:
|
if vstup_cena != 0 and data.order.qty != 0:
|
||||||
@ -309,30 +324,31 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
||||||
if data.position_qty != 0:
|
if data.position_qty != 0:
|
||||||
self.state.docasny_rel_profit.append(rel_profit)
|
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
||||||
partial_exit = True
|
partial_exit = True
|
||||||
else:
|
else:
|
||||||
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
||||||
if len(self.state.docasny_rel_profit) > 0:
|
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
|
||||||
#pricteme aktualni rel profit
|
#pricteme aktualni rel profit
|
||||||
self.state.docasny_rel_profit.append(rel_profit)
|
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
||||||
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
||||||
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
|
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
|
||||||
self.state.docasny_rel_profit = []
|
self.state.account_variables[account.name].docasny_rel_profit = []
|
||||||
partial_last = True
|
partial_last = True
|
||||||
|
|
||||||
self.state.rel_profit_cum.append(rel_profit)
|
self.state.rel_profit_cum.append(rel_profit) #overall rel profit
|
||||||
|
self.state.account_variables[account.name].rel_profit_cum.append(rel_profit) #account cum rel profit
|
||||||
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
||||||
|
|
||||||
#pro martingale updatujeme loss_series_cnt
|
#pro martingale updatujeme loss_series_cnt
|
||||||
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
||||||
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
||||||
|
|
||||||
self.state.ilog(e=f"SELL notif - LONG PROFIT {partial_exit=} {partial_last=}:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum = str(self.state.rel_profit_cum), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
self.state.ilog(e=f"SELL notif {account}- LONG PROFIT {partial_exit=} {partial_last=}:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum = str(self.state.rel_profit_cum), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
||||||
|
|
||||||
#zapsat profit do prescr.trades
|
#zapsat profit do prescr.trades
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.vars.pending:
|
if trade.id == self.state.account_variables[account.name].pending:
|
||||||
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
||||||
trade.profit += trade_profit
|
trade.profit += trade_profit
|
||||||
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
||||||
@ -349,7 +365,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#mazeme self.state.
|
#mazeme self.state.
|
||||||
self.state.wait_for_fill = None
|
self.state.account_variables[account.name].wait_for_fill = None
|
||||||
#zapsat update profitu do tradeList
|
#zapsat update profitu do tradeList
|
||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
@ -357,7 +373,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
setattr(tradeData, "profit", trade_profit)
|
setattr(tradeData, "profit", trade_profit)
|
||||||
setattr(tradeData, "profit_sum", self.state.profit)
|
setattr(tradeData, "profit_sum", self.state.profit)
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
||||||
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
|
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
|
||||||
setattr(tradeData, "rel_profit", rel_profit)
|
setattr(tradeData, "rel_profit", rel_profit)
|
||||||
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
||||||
@ -367,17 +383,17 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL and await self.stop_when_max_profit_loss() is False:
|
if data.event == TradeEvent.FILL and await self.stop_when_max_profit_loss() is False:
|
||||||
|
|
||||||
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||||
if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
|
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
|
||||||
if self.state.vars.requested_followup == Followup.REVERSE:
|
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
|
||||||
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
|
||||||
elif self.state.vars.requested_followup == Followup.ADD:
|
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
|
||||||
#zatim stejna SIZE
|
#zatim stejna SIZE
|
||||||
await self.add_followup(direction=TradeDirection.LONG, size=data.order.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.LONG, size=data.order.qty, signal_name=signal_name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.vars.pending:
|
if trade.id == self.state.account_variables[account.name].pending:
|
||||||
signal_name = trade.generated_by
|
signal_name = trade.generated_by
|
||||||
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
||||||
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
||||||
@ -387,7 +403,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
||||||
|
|
||||||
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
|
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
|
||||||
|
|
||||||
@ -395,32 +411,32 @@ class StrategyClassicSL(Strategy):
|
|||||||
#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
|
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
||||||
if self.state.vars.activeTrade.goal_price is None:
|
if self.state.account_variables[account.name].activeTrade.goal_price is None:
|
||||||
dat = dict(close=data.price)
|
dat = dict(close=data.price)
|
||||||
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.SHORT)
|
self.state.account_variables[account.name].activeTrade.goal_price = get_profit_target_price(self.state, dat, self.state.account_variables[account.name].activeTrade, TradeDirection.SHORT)
|
||||||
#sem v budoucnu dat i update SL
|
#sem v budoucnu dat i update SL
|
||||||
#if self.state.vars.activeTrade.stoploss_value is None:
|
#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.account_variables[account.name].avgp
|
||||||
old_pos = self.state.positions
|
old_pos = self.state.account_variables[account.name].positions
|
||||||
self.state.positions = int(data.position_qty)
|
self.state.account_variables[account.name].positions = int(data.position_qty)
|
||||||
if int(data.position_qty) == 0:
|
if int(data.position_qty) == 0:
|
||||||
self.state.avgp = 0
|
self.state.account_variables[account.name].avgp = 0
|
||||||
|
|
||||||
self.state.ilog(e="SELL notifikace "+str(data.order.status), msg="update pozic", old_avgp=old_avgp, old_pos=old_pos, avgp=self.state.avgp, pos=self.state.positions, orderid=str(data.order.id))
|
self.state.ilog(e="SELL notifikace "+str(data.order.status), msg="update pozic", old_avgp=old_avgp, old_pos=old_pos, avgp=self.state.account_variables[account.name].avgp, pos=self.state.account_variables[account.name].positions, orderid=str(data.order.id))
|
||||||
#self.state.avgp, self.state.positions = self.interface.pos()
|
#self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.interface.pos()
|
||||||
|
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.CANCELED:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.CANCELED:
|
||||||
print("Příchozí SELL notifikace - complete FILL nebo CANCEL", data.event)
|
print("Příchozí SELL notifikace - complete FILL nebo CANCEL", data.event)
|
||||||
self.state.vars.pending = None
|
self.state.account_variables[account.name].pending = None
|
||||||
a,p = self.interface.pos()
|
a,p = self.interface.pos()
|
||||||
#pri chybe api nechavame puvodni hodnoty
|
#pri chybe api nechavame puvodni hodnoty
|
||||||
if a != -1:
|
if a != -1:
|
||||||
self.state.avgp, self.state.positions = a,p
|
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = a,p
|
||||||
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
|
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
|
||||||
#ic(self.state.avgp, self.state.positions)
|
#ic(self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions)
|
||||||
|
|
||||||
#this parent method is called by strategy just once before waiting for first data
|
#this parent method is called by strategy just once before waiting for first data
|
||||||
def strat_init(self):
|
def strat_init(self):
|
||||||
@ -441,48 +457,47 @@ class StrategyClassicSL(Strategy):
|
|||||||
else:
|
else:
|
||||||
self.next(item, self.state)
|
self.next(item, self.state)
|
||||||
|
|
||||||
|
|
||||||
#overidden methods
|
#overidden methods
|
||||||
# pouziva se pri vstupu long nebo exitu short
|
# pouziva se pri vstupu long nebo exitu short
|
||||||
# osetrit uzavreni s vice nez mam
|
# osetrit uzavreni s vice nez mam
|
||||||
def buy(self, size = None, repeat: bool = False):
|
def buy(self, account: Account, size = None, repeat: bool = False):
|
||||||
print("overriden buy method")
|
print("overriden buy method")
|
||||||
if size is None:
|
if size is None:
|
||||||
sizer = self.state.vars.chunk
|
sizer = self.state.vars.chunk
|
||||||
else:
|
else:
|
||||||
sizer = size
|
sizer = size
|
||||||
#jde o uzavreni short pozice
|
#jde o uzavreni short pozice
|
||||||
if int(self.state.positions) < 0 and (int(self.state.positions) + int(sizer)) > 0:
|
if int(self.state.account_variables[account.name].positions) < 0 and (int(self.state.account_variables[account.name].positions) + int(sizer)) > 0:
|
||||||
self.state.ilog(e="buy nelze nakoupit vic nez shortuji", positions=self.state.positions, size=size)
|
self.state.ilog(e="buy nelze nakoupit vic nez shortuji", positions=self.state.account_variables[account.name].positions, size=size)
|
||||||
printanyway("buy nelze nakoupit vic nez shortuji")
|
printanyway("buy nelze nakoupit vic nez shortuji")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
if int(self.state.positions) >= self.state.vars.maxpozic:
|
if int(self.state.account_variables[account.name].positions) >= self.state.vars.maxpozic:
|
||||||
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.positions)
|
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions)
|
||||||
printanyway("max mnostvi naplneno")
|
printanyway("max mnostvi naplneno")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
self.state.blockbuy = 1
|
|
||||||
self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
|
||||||
#self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
#self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
||||||
self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
||||||
return self.state.interface.buy(size=sizer)
|
return self.state.interface[account.name].buy(size=sizer)
|
||||||
|
|
||||||
#overidden methods
|
#overidden methods
|
||||||
# pouziva se pri vstupu short nebo exitu long
|
# pouziva se pri vstupu short nebo exitu long
|
||||||
def sell(self, size = None, repeat: bool = False):
|
def sell(self, account: Account, size = None, repeat: bool = False):
|
||||||
print("overriden sell method")
|
print("overriden sell method")
|
||||||
if size is None:
|
if size is None:
|
||||||
size = abs(int(self.state.positions))
|
size = abs(int(self.state.account_variables[account.name].positions))
|
||||||
|
|
||||||
#jde o uzavreni long pozice
|
#jde o uzavreni long pozice
|
||||||
if int(self.state.positions) > 0 and (int(self.state.positions) - int(size)) < 0:
|
if int(self.state.account_variables[account.name].positions) > 0 and (int(self.state.account_variables[account.name].positions) - int(size)) < 0:
|
||||||
self.state.ilog(e="nelze prodat vic nez longuji", positions=self.state.positions, size=size)
|
self.state.ilog(e="nelze prodat vic nez longuji", positions=self.state.account_variables[account.name].positions, size=size)
|
||||||
printanyway("nelze prodat vic nez longuji")
|
printanyway("nelze prodat vic nez longuji")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
#pokud shortuji a mam max pozic
|
#pokud shortuji a mam max pozic
|
||||||
if int(self.state.positions) < 0 and abs(int(self.state.positions)) >= self.state.vars.maxpozic:
|
if int(self.state.account_variables[account.name].positions) < 0 and abs(int(self.state.account_variables[account.name].positions)) >= self.state.vars.maxpozic:
|
||||||
self.state.ilog(e="short - Maxim mnozstvi naplneno", positions=self.state.positions, size=size)
|
self.state.ilog(e="short - Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions, size=size)
|
||||||
printanyway("short - Maxim mnozstvi naplneno")
|
printanyway("short - Maxim mnozstvi naplneno")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -490,4 +505,4 @@ class StrategyClassicSL(Strategy):
|
|||||||
#self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
#self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
||||||
#self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
#self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
||||||
self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
||||||
return self.state.interface.sell(size=size)
|
return self.state.interface[account.name].sell(size=size)
|
||||||
@ -2,7 +2,7 @@
|
|||||||
Strategy base class
|
Strategy base class
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print
|
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print, gaka
|
||||||
from v2realbot.utils.tlog import tlog
|
from v2realbot.utils.tlog import tlog
|
||||||
from v2realbot.utils.ilog import insert_log, insert_log_multiple_queue
|
from v2realbot.utils.ilog import insert_log, insert_log_multiple_queue
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order, Account
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order, Account
|
||||||
@ -16,11 +16,11 @@ 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
|
import v2realbot.common.model 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
|
||||||
from v2realbot.common.model import TradeUpdate
|
from v2realbot.common.model import TradeUpdate, AccountVariables
|
||||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from threading import Event, current_thread
|
from threading import Event, current_thread
|
||||||
import orjson
|
import orjson
|
||||||
@ -30,6 +30,7 @@ from collections import defaultdict
|
|||||||
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
|
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
|
from typing import Dict, Set
|
||||||
|
|
||||||
if PROFILING_NEXT_ENABLED:
|
if PROFILING_NEXT_ENABLED:
|
||||||
from pyinstrument import Profiler
|
from pyinstrument import Profiler
|
||||||
@ -50,7 +51,8 @@ class Strategy:
|
|||||||
self.rectype: RecordType = None
|
self.rectype: RecordType = None
|
||||||
self.nextnew = 1
|
self.nextnew = 1
|
||||||
self.btdata: list = []
|
self.btdata: list = []
|
||||||
self.interface: GeneralInterface = None
|
self.interface: Dict[str, GeneralInterface] = {}
|
||||||
|
self.order_notifs: Dict[str, LiveOrderUpdatesStreamer] = {}
|
||||||
self.state: StrategyState = None
|
self.state: StrategyState = None
|
||||||
self.bt: Backtester = None
|
self.bt: Backtester = None
|
||||||
self.debug = False
|
self.debug = False
|
||||||
@ -60,8 +62,8 @@ class Strategy:
|
|||||||
self.open_rush = open_rush
|
self.open_rush = open_rush
|
||||||
self.close_rush = close_rush
|
self.close_rush = close_rush
|
||||||
self._streams = []
|
self._streams = []
|
||||||
|
#primary account from runReqs
|
||||||
self.account = account
|
self.account = account
|
||||||
self.key = get_key(mode=self.mode, account=self.account)
|
|
||||||
self.rtqueue = None
|
self.rtqueue = None
|
||||||
self.runner_id = runner_id
|
self.runner_id = runner_id
|
||||||
self.ilog_save = ilog_save
|
self.ilog_save = ilog_save
|
||||||
@ -69,6 +71,9 @@ class Strategy:
|
|||||||
self.secondary_res_start_index = dict()
|
self.secondary_res_start_index = dict()
|
||||||
self.last_index = -1
|
self.last_index = -1
|
||||||
|
|
||||||
|
#set of all accounts (Account) including those from stratvars
|
||||||
|
self.accounts = self.get_accounts_in_stratvars_and_reqs()
|
||||||
|
|
||||||
#TODO predelat na dynamické queues
|
#TODO predelat na dynamické queues
|
||||||
self.q1 = queue.Queue()
|
self.q1 = queue.Queue()
|
||||||
self.q2 = queue.Queue()
|
self.q2 = queue.Queue()
|
||||||
@ -83,6 +88,25 @@ class Strategy:
|
|||||||
self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie
|
self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie
|
||||||
self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu
|
self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu
|
||||||
|
|
||||||
|
def get_accounts_in_stratvars_and_reqs(self) -> Set:
|
||||||
|
"""
|
||||||
|
Helper that retrieves distinct account values used in stratvars and in runRequest.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
set: A set of unique account values.
|
||||||
|
"""
|
||||||
|
account_keywords = ['account', 'account_long', 'account_short']
|
||||||
|
account_values = set()
|
||||||
|
|
||||||
|
for signal_value in self.stratvars.get('signals', {}).values():
|
||||||
|
for key in account_keywords:
|
||||||
|
if key in signal_value:
|
||||||
|
account_values.add(Account(signal_value[key]))
|
||||||
|
|
||||||
|
account_values.add(Account(self.account))
|
||||||
|
printnow("Distinct account values:", account_values)
|
||||||
|
return account_values
|
||||||
|
|
||||||
#prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
|
#prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
|
||||||
#zatim jen jedna q1
|
#zatim jen jedna q1
|
||||||
#TODO zaroven strategie musi vedet o rectypu, protoze je zpracovava
|
#TODO zaroven strategie musi vedet o rectypu, protoze je zpracovava
|
||||||
@ -116,25 +140,33 @@ class Strategy:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.key = get_key(mode=mode, account=self.account)
|
|
||||||
|
|
||||||
if mode == Mode.LIVE or mode == Mode.PAPER:
|
if mode == Mode.LIVE or mode == Mode.PAPER:
|
||||||
#data loader thread
|
#data loader thread
|
||||||
self.dataloader = Trade_WS_Streamer(name="WS-LDR-"+self.name)
|
self.dataloader = Trade_WS_Streamer(name="WS-LDR-"+self.name)
|
||||||
self.interface = LiveInterface(symbol=self.symbol, key=self.key)
|
#populate interfaces for each account
|
||||||
# order notif thread
|
for account in self.accounts:
|
||||||
self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name)
|
#get key for account
|
||||||
#propojujeme notifice s interfacem (pro callback)
|
key = get_key(mode=mode, account=Account(account))
|
||||||
self.order_notifs.connect_callback(self)
|
self.interface[account.name] = LiveInterface(symbol=self.symbol, key=key)
|
||||||
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, ilog_save=self.ilog_save)
|
# order notif thread
|
||||||
|
self.order_notifs = LiveOrderUpdatesStreamer(key=key, name="WS-STRMR-" + account.name + "-" + self.name, account=account)
|
||||||
|
#propojujeme notifice s interfacem (pro callback)
|
||||||
|
self.order_notifs.connect_callback(self)
|
||||||
|
|
||||||
|
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, ilog_save=self.ilog_save)
|
||||||
|
|
||||||
elif mode == Mode.BT:
|
elif mode == Mode.BT:
|
||||||
|
|
||||||
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
|
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
|
||||||
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
self.bt = Backtester(symbol = self.symbol, accounts=self.accounts, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
||||||
|
|
||||||
self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
#populate interfaces for each account
|
||||||
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
for account in self.accounts:
|
||||||
|
#pro backtest volame stejne oklicujeme interface
|
||||||
|
self.interface[account.name] = BacktestInterface(symbol=self.symbol, bt=self.bt, account=account)
|
||||||
|
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
||||||
|
#no callback from bt, it is called directly
|
||||||
self.order_notifs = None
|
self.order_notifs = None
|
||||||
|
|
||||||
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
||||||
@ -142,10 +174,10 @@ class Strategy:
|
|||||||
self.dataloader.add_stream(TradeAggregator2List(symbol=self.symbol,btdata=self.btdata,rectype=RecordType.TRADE))
|
self.dataloader.add_stream(TradeAggregator2List(symbol=self.symbol,btdata=self.btdata,rectype=RecordType.TRADE))
|
||||||
elif mode == Mode.PREP:
|
elif mode == Mode.PREP:
|
||||||
#bt je zde jen pro udrzeni BT casu v logu atp. JInak jej nepouzivame.
|
#bt je zde jen pro udrzeni BT casu v logu atp. JInak jej nepouzivame.
|
||||||
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
self.bt = Backtester(symbol = self.symbol, accounts=self.accounts, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
||||||
self.interface = None
|
self.interface = None
|
||||||
#self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
#self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
||||||
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
||||||
self.order_notifs = None
|
self.order_notifs = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -314,13 +346,15 @@ class Strategy:
|
|||||||
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
|
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
|
||||||
def refresh_positions(self, item):
|
def refresh_positions(self, item):
|
||||||
if self.rectype == RecordType.BAR:
|
if self.rectype == RecordType.BAR:
|
||||||
a,p = self.interface.pos()
|
for account in self.accounts:
|
||||||
if a != -1:
|
a,p = self.interface[account.name].pos()
|
||||||
self.state.avgp, self.state.positions = a,p
|
if a != -1:
|
||||||
|
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = a, p
|
||||||
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME, RecordType.CBARDOLLAR, RecordType.CBARRENKO) and item['confirmed'] == 1:
|
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME, RecordType.CBARDOLLAR, RecordType.CBARRENKO) and item['confirmed'] == 1:
|
||||||
a,p = self.interface.pos()
|
for account in self.accounts:
|
||||||
if a != -1:
|
a,p = self.interface[account.name].pos()
|
||||||
self.state.avgp, self.state.positions = a,p
|
if a != -1:
|
||||||
|
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].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):
|
||||||
@ -416,7 +450,11 @@ class Strategy:
|
|||||||
|
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
#live notification thread
|
#live notification thread
|
||||||
self.order_notifs.start()
|
#for all keys in self.order_notifs call start()
|
||||||
|
for key in self.order_notifs:
|
||||||
|
self.order_notifs[key].start()
|
||||||
|
|
||||||
|
#self.order_notifs.start()
|
||||||
elif self.mode == Mode.BT or self.mode == Mode.PREP:
|
elif self.mode == Mode.BT or self.mode == Mode.PREP:
|
||||||
self.bt.backtest_start = datetime.now()
|
self.bt.backtest_start = datetime.now()
|
||||||
|
|
||||||
@ -486,7 +524,7 @@ class Strategy:
|
|||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
if self.mode == Mode.BT:
|
if self.mode == Mode.BT:
|
||||||
print("REQUEST COUNT:", self.interface.mincnt)
|
print("REQUEST COUNT:", {account_str:self.interface[account_str].mincnt for account_str in self.interface})
|
||||||
|
|
||||||
self.bt.backtest_end = datetime.now()
|
self.bt.backtest_end = datetime.now()
|
||||||
#print(40*"*",self.mode, "BACKTEST RESULTS",40*"*")
|
#print(40*"*",self.mode, "BACKTEST RESULTS",40*"*")
|
||||||
@ -500,7 +538,9 @@ class Strategy:
|
|||||||
|
|
||||||
#disconnect strategy from websocket trader updates
|
#disconnect strategy from websocket trader updates
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
self.order_notifs.disconnect_callback(self)
|
for key in self.order_notifs:
|
||||||
|
self.order_notifs[key].disconnect_callback(self)
|
||||||
|
#self.order_notifs.disconnect_callback(self)
|
||||||
|
|
||||||
#necessary only for shared loaders (to keep it running for other stratefies)
|
#necessary only for shared loaders (to keep it running for other stratefies)
|
||||||
for i in self._streams:
|
for i in self._streams:
|
||||||
@ -541,11 +581,11 @@ class Strategy:
|
|||||||
#for order updates from LIVE or BACKTEST
|
#for order updates from LIVE or BACKTEST
|
||||||
#updates are sent only for SYMBOL of strategy
|
#updates are sent only for SYMBOL of strategy
|
||||||
|
|
||||||
async def order_updates(self, data: TradeUpdate):
|
async def order_updates(self, data: TradeUpdate, account: Account):
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
now = datetime.now().timestamp()
|
now = datetime.now().timestamp()
|
||||||
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
|
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
|
||||||
data = TradeUpdate(**data.dict())
|
data = TradeUpdate(**data.dict(), account=account)
|
||||||
else:
|
else:
|
||||||
now = self.bt.time
|
now = self.bt.time
|
||||||
|
|
||||||
@ -632,17 +672,14 @@ class Strategy:
|
|||||||
rt_out["statinds"] = dict()
|
rt_out["statinds"] = dict()
|
||||||
for key, value in self.state.statinds.items():
|
for key, value in self.state.statinds.items():
|
||||||
rt_out["statinds"][key] = value
|
rt_out["statinds"][key] = value
|
||||||
|
|
||||||
#vkladame average price and positions, pokud existuji
|
|
||||||
#self.state.avgp , self.state.positions
|
|
||||||
|
|
||||||
#pro typ strategie Classic, posilame i vysi stoploss
|
#pro typ strategie Classic, posilame i vysi stoploss
|
||||||
try:
|
try:
|
||||||
sl_value = self.state.vars["activeTrade"].stoploss_value
|
sl_value = gaka(self.state.account_variables, "activeTrade", lambda x: x.stoploss_value)
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
sl_value = None
|
sl_value = None
|
||||||
|
|
||||||
rt_out["positions"] = dict(time=self.state.time, positions=self.state.positions, avgp=self.state.avgp, sl_value=sl_value)
|
rt_out["positions"] = dict(time=self.state.time, positions=gaka(self.state.account_variables, "positions"), avgp=gaka(self.state.account_variables,), sl_value=sl_value)
|
||||||
|
|
||||||
#vkladame limitku a pendingbuys
|
#vkladame limitku a pendingbuys
|
||||||
try:
|
try:
|
||||||
@ -718,17 +755,21 @@ class StrategyState:
|
|||||||
"""Strategy Stat object that is passed to callbacks
|
"""Strategy Stat object that is passed to callbacks
|
||||||
note:
|
note:
|
||||||
state.time
|
state.time
|
||||||
state.interface.time
|
state.interface[account.name].time
|
||||||
|
accounts = set of all accounts (strings)
|
||||||
|
account = enum of primary account (Account)
|
||||||
většinou mají stejnou hodnotu, ale lišit se mužou např. v případě BT callbacku - kdy se v rámci okna končící state.time realizují objednávky, které
|
většinou mají stejnou hodnotu, ale lišit se mužou např. v případě BT callbacku - kdy se v rámci okna končící state.time realizují objednávky, které
|
||||||
triggerují callback, který následně vyvolá např. buy (ten se musí ale udít v čase fillu, tzn. callback si nastaví čas interfacu na filltime)
|
triggerují callback, který následně vyvolá např. buy (ten se musí ale udít v čase fillu, tzn. callback si nastaví čas interfacu na filltime)
|
||||||
po dokončení bt kroků před zahájením iterace "NEXT" se časy znovu updatnout na původni state.time
|
po dokončení bt kroků před zahájením iterace "NEXT" se časy znovu updatnout na původni state.time
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str, symbol: str, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR, runner_id: UUID = None, bt: Backtester = None, ilog_save: bool = False):
|
def __init__(self, name: str, symbol: str, accounts: set, account: Account, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR, runner_id: UUID = None, bt: Backtester = None, ilog_save: bool = False):
|
||||||
self.vars = stratvars
|
self.vars = stratvars
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.positions = 0
|
self.account = account #primary account
|
||||||
self.avgp = 0
|
self.accounts = accounts
|
||||||
self.blockbuy = 0
|
#populate account variables dictionary
|
||||||
|
self.account_variables: Dict[str, AccountVariables] = {account.name: AccountVariables() for account in self.accounts}
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.rectype = rectype
|
self.rectype = rectype
|
||||||
@ -737,11 +778,10 @@ class StrategyState:
|
|||||||
self.time = None
|
self.time = None
|
||||||
#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={key:dict(long=0,short=999) for key in self.accounts}
|
||||||
self.resolution = None
|
self.resolution = None
|
||||||
self.runner_id = runner_id
|
self.runner_id = runner_id
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
self.dont_exit_already_activated = False
|
|
||||||
self.docasny_rel_profit = []
|
self.docasny_rel_profit = []
|
||||||
self.ilog_save = ilog_save
|
self.ilog_save = ilog_save
|
||||||
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
|
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
|
||||||
@ -779,18 +819,14 @@ class StrategyState:
|
|||||||
#secondary resolution 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)
|
|
||||||
self.buy = self.interface.buy
|
|
||||||
self.buy_l = self.interface.buy_l
|
|
||||||
self.sell = self.interface.sell
|
|
||||||
self.sell_l = self.interface.sell_l
|
|
||||||
self.cancel_pending_buys = None
|
self.cancel_pending_buys = None
|
||||||
self.iter_log_list = []
|
self.iter_log_list = []
|
||||||
self.dailyBars = defaultdict(dict)
|
self.dailyBars = defaultdict(dict)
|
||||||
#celkovy profit (prejmennovat na profit_cum)
|
#celkovy profit (prejmennovat na profit_cum)
|
||||||
self.profit = 0
|
self.profit = 0 #TODO key by account?
|
||||||
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
|
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
|
||||||
self.rel_profit_cum = []
|
self.rel_profit_cum = []#TODO key by account?
|
||||||
self.tradeList = []
|
self.tradeList = []
|
||||||
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
|
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
|
||||||
self.extData = defaultdict(dict)
|
self.extData = defaultdict(dict)
|
||||||
@ -799,6 +835,25 @@ class StrategyState:
|
|||||||
self.today_market_close = None
|
self.today_market_close = None
|
||||||
self.classed_indicators = {}
|
self.classed_indicators = {}
|
||||||
|
|
||||||
|
#quick interface actions to access from state without having to write interface[account.name].buy_l
|
||||||
|
def buy_l(self, account: Account, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
||||||
|
self.interface[account.name].buy_l(price, size, repeat, force)
|
||||||
|
|
||||||
|
def buy(self, account: Account, size = 1, repeat: bool = False):
|
||||||
|
self.interface[account.name].buy(size, repeat)
|
||||||
|
|
||||||
|
def sell_l(self, account: Account, price: float, size: int = 1, repeat: bool = False):
|
||||||
|
self.interface[account.name].sell_l(price, size, repeat)
|
||||||
|
|
||||||
|
def sell(self, account: Account, size = 1, repeat: bool = False):
|
||||||
|
self.interface[account.name].sell(size, repeat)
|
||||||
|
|
||||||
|
def repl(self, account: Account, orderid: str, price: float = None, size: int = 1, repeat: bool = False):
|
||||||
|
self.interface[account.name].repl(orderid, price, size, repeat)
|
||||||
|
|
||||||
|
def cancel(self, account: Account, orderid: str):
|
||||||
|
self.interface[account.name].cancel(orderid)
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
#release large variables
|
#release large variables
|
||||||
self.bars = None
|
self.bars = None
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
from v2realbot.strategyblocks.activetrade.sl.trailsl import trail_SL_management
|
from v2realbot.strategyblocks.activetrade.sl.trailsl import trail_SL_management
|
||||||
from v2realbot.strategyblocks.activetrade.close.evaluate_close import eval_close_position
|
from v2realbot.strategyblocks.activetrade.close.evaluate_close import eval_close_position
|
||||||
|
from v2realbot.utils.utils import gaka
|
||||||
|
def manage_active_trade(state, data):
|
||||||
|
accountsWithActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is not None)
|
||||||
|
# {"account1": activeTrade,
|
||||||
|
# "account2": activeTrade}
|
||||||
|
|
||||||
def manage_active_trade(state, data):
|
if len(accountsWithActiveTrade.values()) == 0:
|
||||||
trade = state.vars.activeTrade
|
return
|
||||||
if trade is None:
|
|
||||||
return -1
|
trail_SL_management(state, accountsWithActiveTrade, data)
|
||||||
trail_SL_management(state, data)
|
eval_close_position(state, accountsWithActiveTrade, data)
|
||||||
eval_close_position(state, data)
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,17 +18,20 @@ import os
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
||||||
|
|
||||||
|
#TODO tady jsem taky skoncil a pak zpetna evaluate_close (mozna zde staci jen account?)
|
||||||
|
|
||||||
# - close means change status in prescribed Trends,update profit, delete from activeTrade
|
# - close means change status in prescribed Trends,update profit, delete from activeTrade
|
||||||
def close_position(state, data, direction: TradeDirection, reason: str, followup: Followup = None):
|
def close_position(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection, reason: str, followup: Followup = None):
|
||||||
followup_text = str(followup) if followup is not None else ""
|
followup_text = str(followup) if followup is not None else ""
|
||||||
state.ilog(lvl=1,e=f"CLOSING TRADE {followup_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
|
positions = state.account_variables[activeTrade.account.name].positions
|
||||||
|
state.ilog(lvl=1,e=f"CLOSING TRADE {followup_text} {reason} {str(direction)}", curr_price=data["close"], trade=activeTrade)
|
||||||
if direction == TradeDirection.SHORT:
|
if direction == TradeDirection.SHORT:
|
||||||
res = state.buy(size=abs(int(state.positions)))
|
res = state.buy(size=abs(int(positions)))
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation {reason} {res}")
|
raise Exception(f"error in required operation {reason} {res}")
|
||||||
|
|
||||||
elif direction == TradeDirection.LONG:
|
elif direction == TradeDirection.LONG:
|
||||||
res = state.sell(size=state.positions)
|
res = state.sell(size=positions)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation STOPLOSS SELL {res}")
|
raise Exception(f"error in required operation STOPLOSS SELL {res}")
|
||||||
|
|
||||||
@ -37,19 +40,21 @@ def close_position(state, data, direction: TradeDirection, reason: str, followup
|
|||||||
|
|
||||||
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
||||||
insert_SL_history(state)
|
insert_SL_history(state)
|
||||||
state.dont_exit_already_activated = False
|
state.account_variables[activeTrade.account.name].pending = activeTrade.id
|
||||||
state.vars.pending = state.vars.activeTrade.id
|
state.account_variables[activeTrade.account.name].activeTrade = None
|
||||||
state.vars.activeTrade = None
|
#state.account_variables[activeTrade.account.name].last_exit_index = data["index"]
|
||||||
state.vars.last_exit_index = data["index"]
|
state.vars.last_exit_index = data["index"]
|
||||||
|
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
||||||
if followup is not None:
|
if followup is not None:
|
||||||
state.vars.requested_followup = followup
|
state.account_variables[activeTrade.account.name].requested_followup = followup
|
||||||
|
|
||||||
#close only partial position - no followup here, size multiplier must be between 0 and 1
|
#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):
|
def close_position_partial(state, activeTrade: Trade,data, direction: TradeDirection, reason: str, size: float):
|
||||||
|
positions = state.account_variables[activeTrade.account.name].positions
|
||||||
if size <= 0 or size >=1:
|
if size <= 0 or size >=1:
|
||||||
raise Exception(f"size must be betweem 0 and 1")
|
raise Exception(f"size must be betweem 0 and 1")
|
||||||
size_abs = abs(int(int(state.positions)*size))
|
size_abs = abs(int(int(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)
|
state.ilog(lvl=1,e=f"CLOSING TRADE PART: {size_abs} {size} {reason} {str(direction)}", curr_price=data["close"], trade=activeTrade)
|
||||||
if direction == TradeDirection.SHORT:
|
if direction == TradeDirection.SHORT:
|
||||||
res = state.buy(size=size_abs)
|
res = state.buy(size=size_abs)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
@ -64,6 +69,10 @@ def close_position_partial(state, data, direction: TradeDirection, reason: str,
|
|||||||
|
|
||||||
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
||||||
insert_SL_history(state)
|
insert_SL_history(state)
|
||||||
state.vars.pending = state.vars.activeTrade.id
|
state.account_variables[activeTrade.account.name].pending = activeTrade.id
|
||||||
|
state.account_variables[activeTrade.account.name].activeTrade = None
|
||||||
|
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
||||||
|
#state.account_variables[activeTrade.account.name].last_exit_index = data["index"]
|
||||||
|
|
||||||
#state.vars.activeTrade = None
|
#state.vars.activeTrade = None
|
||||||
#state.vars.last_exit_index = data["index"]
|
state.vars.last_exit_index = data["index"] #ponechano mimo account
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -12,9 +12,9 @@ from threading import Event
|
|||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick
|
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick
|
||||||
|
|
||||||
def dontexit_protection_met(state, data, direction: TradeDirection):
|
def dontexit_protection_met(state, activeTrade: Trade, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
@ -24,58 +24,64 @@ def dontexit_protection_met(state, data, direction: TradeDirection):
|
|||||||
#vyreseno pri kazde aktivaci se vyplni flag already_activated
|
#vyreseno pri kazde aktivaci se vyplni flag already_activated
|
||||||
#pri naslednem false podminky se v pripade, ze je aktivovany flag posle True -
|
#pri naslednem false podminky se v pripade, ze je aktivovany flag posle True -
|
||||||
#take se vyrusi v closu
|
#take se vyrusi v closu
|
||||||
def process_result(result):
|
def process_result(result, account):
|
||||||
if result:
|
if result:
|
||||||
state.dont_exit_already_activated = True
|
state.account_variables[account.name].dont_exit_already_activated = True
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def evaluate_result():
|
def evaluate_result():
|
||||||
mother_signal = state.vars.activeTrade.generated_by
|
mother_signal = activeTrade.generated_by
|
||||||
|
dont_exit_already_activated = state.account_variables[activeTrade.account.name].dont_exit_already_activated
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
#TESTUJEME DONT_EXIT_
|
#TESTUJEME DONT_EXIT_
|
||||||
cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer]
|
cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer]
|
||||||
#OR
|
#OR
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#OR neprosly testujeme AND
|
#OR neprosly testujeme AND
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer]
|
cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer]
|
||||||
#OR
|
#OR
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#OR neprosly testujeme AND
|
#OR neprosly testujeme AND
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
#nejprve evaluujeme vsechny podminky
|
#nejprve evaluujeme vsechny podminky
|
||||||
result = evaluate_result()
|
result = evaluate_result()
|
||||||
|
|
||||||
#pak evaluujeme vysledek a vracíme
|
#pak evaluujeme vysledek a vracíme
|
||||||
return process_result(result)
|
return process_result(result, activeTrade.account)
|
||||||
|
|
||||||
|
|
||||||
def exit_conditions_met(state, data, direction: TradeDirection):
|
def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
|
signal_name = activeTrade.generated_by
|
||||||
|
last_entry_index = state.account_variables[activeTrade.account.name].last_entry_index
|
||||||
|
avgp = state.account_variables[activeTrade.account.name].avgp
|
||||||
|
positions = state.account_variables[activeTrade.account.name].positions
|
||||||
|
|
||||||
directive_name = "exit_cond_only_on_confirmed"
|
directive_name = "exit_cond_only_on_confirmed"
|
||||||
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
exit_cond_only_on_confirmed = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
|
||||||
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
||||||
state.ilog(lvl=0,e="EXIT COND ONLY ON CONFIRMED BAR")
|
state.ilog(lvl=0,e="EXIT COND ONLY ON CONFIRMED BAR")
|
||||||
@ -83,20 +89,20 @@ def exit_conditions_met(state, data, direction: TradeDirection):
|
|||||||
|
|
||||||
## minimální počet barů od vstupu
|
## minimální počet barů od vstupu
|
||||||
directive_name = "exit_cond_req_bars"
|
directive_name = "exit_cond_req_bars"
|
||||||
exit_cond_req_bars = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 1))
|
exit_cond_req_bars = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 1))
|
||||||
|
|
||||||
if state.vars.last_in_index is not None:
|
if last_entry_index is not None:
|
||||||
index_to_compare = int(state.vars.last_in_index)+int(exit_cond_req_bars)
|
index_to_compare = int(last_entry_index)+int(exit_cond_req_bars)
|
||||||
if int(data["index"]) < index_to_compare:
|
if int(data["index"]) < index_to_compare:
|
||||||
state.ilog(lvl=1,e=f"EXIT COND WAITING on required bars from IN {exit_cond_req_bars} TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_in_index=state.vars.last_in_index)
|
state.ilog(lvl=1,e=f"EXIT COND WAITING on required bars from IN {exit_cond_req_bars} TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_entry_index=last_entry_index)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
#POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
||||||
directive_name = "exit_cond_min_profit"
|
directive_name = "exit_cond_min_profit"
|
||||||
exit_cond_min_profit_nodir = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
exit_cond_min_profit_nodir = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
directive_name = "exit_cond_min_profit_" + str(smer)
|
directive_name = "exit_cond_min_profit_" + str(smer)
|
||||||
exit_cond_min_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
|
exit_cond_min_profit = get_signal_section_directive(state, signal_name=signal_name,directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
|
||||||
|
|
||||||
|
|
||||||
#máme nastavený exit_cond_min_profit
|
#máme nastavený exit_cond_min_profit
|
||||||
@ -105,10 +111,10 @@ def exit_conditions_met(state, data, direction: TradeDirection):
|
|||||||
|
|
||||||
if exit_cond_min_profit is not None:
|
if exit_cond_min_profit is not None:
|
||||||
exit_cond_min_profit_normalized = normalize_tick(state, data, float(exit_cond_min_profit))
|
exit_cond_min_profit_normalized = normalize_tick(state, data, float(exit_cond_min_profit))
|
||||||
exit_cond_goal_price = price2dec(float(state.avgp)+exit_cond_min_profit_normalized,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-exit_cond_min_profit_normalized,3)
|
exit_cond_goal_price = price2dec(float(avgp)+exit_cond_min_profit_normalized,3) if int(positions) > 0 else price2dec(float(avgp)-exit_cond_min_profit_normalized,3)
|
||||||
curr_price = float(data["close"])
|
curr_price = float(data["close"])
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit {exit_cond_goal_price=} {exit_cond_min_profit=} {exit_cond_min_profit_normalized=} {curr_price=}")
|
state.ilog(lvl=1,e=f"EXIT COND min profit {exit_cond_goal_price=} {exit_cond_min_profit=} {exit_cond_min_profit_normalized=} {curr_price=}")
|
||||||
if (int(state.positions) < 0 and curr_price<=exit_cond_goal_price) or (int(state.positions) > 0 and curr_price>=exit_cond_goal_price):
|
if (int(positions) < 0 and curr_price<=exit_cond_goal_price) or (int(positions) > 0 and curr_price>=exit_cond_goal_price):
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit PASS - POKRACUJEME")
|
state.ilog(lvl=1,e=f"EXIT COND min profit PASS - POKRACUJEME")
|
||||||
else:
|
else:
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit NOT PASS")
|
state.ilog(lvl=1,e=f"EXIT COND min profit NOT PASS")
|
||||||
@ -137,10 +143,10 @@ def exit_conditions_met(state, data, direction: TradeDirection):
|
|||||||
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
||||||
state.ilog(lvl=0,e=f"EXIT CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.exit])
|
state.ilog(lvl=0,e=f"EXIT CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.exit])
|
||||||
|
|
||||||
mother_signal = state.vars.activeTrade.generated_by
|
mother_signal = signal_name
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
cond_dict = state.vars.conditions[KW.exit][state.vars.activeTrade.generated_by][smer]
|
cond_dict = state.vars.conditions[KW.exit][signal_name][smer]
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"EXIT CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
state.ilog(lvl=1,e=f"EXIT CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
||||||
if result:
|
if result:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,10 +18,10 @@ import os
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
||||||
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_signal_section_directive
|
||||||
|
|
||||||
|
|
||||||
def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
|
||||||
"""
|
"""
|
||||||
Function responsible for end of day management
|
Function responsible for end of day management
|
||||||
|
|
||||||
@ -38,8 +38,10 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
|||||||
- 1 min forced immediate
|
- 1 min forced immediate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
avgp = state.account_variables[activeTrade.account.name].avgp
|
||||||
|
|
||||||
directive_name = "forced_exit_window_start"
|
directive_name = "forced_exit_window_start"
|
||||||
forced_exit_window_start = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
forced_exit_window_start = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
if forced_exit_window_start is None:
|
if forced_exit_window_start is None:
|
||||||
state.ilog(lvl=0,e="Forced exit not required.")
|
state.ilog(lvl=0,e="Forced exit not required.")
|
||||||
@ -47,7 +49,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
|||||||
|
|
||||||
|
|
||||||
directive_name = "forced_exit_window_end"
|
directive_name = "forced_exit_window_end"
|
||||||
forced_exit_window_end = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 389))
|
forced_exit_window_end = get_signal_section_directive(state, signal_name=activeTrade.generated_by,directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 389))
|
||||||
|
|
||||||
if forced_exit_window_start>389:
|
if forced_exit_window_start>389:
|
||||||
state.ilog(lvl=0,e="Forced exit window end max is 389")
|
state.ilog(lvl=0,e="Forced exit window end max is 389")
|
||||||
@ -60,7 +62,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
|||||||
|
|
||||||
# #dokdy konci okno snizujiciho se profitu (zbytek je breakeven a posledni minuta forced) - default pulka okna
|
# #dokdy konci okno snizujiciho se profitu (zbytek je breakeven a posledni minuta forced) - default pulka okna
|
||||||
# directive_name = "forced_exit_decreasing_profit_window_end"
|
# directive_name = "forced_exit_decreasing_profit_window_end"
|
||||||
# forced_exit_decreasing_profit_window_end = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, (forced_exit_window_end-forced_exit_window_end)/2))
|
# forced_exit_decreasing_profit_window_end = get_signal_section_directive(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, (forced_exit_window_end-forced_exit_window_end)/2))
|
||||||
|
|
||||||
# if forced_exit_decreasing_profit_window_end > forced_exit_window_end-1:
|
# if forced_exit_decreasing_profit_window_end > forced_exit_window_end-1:
|
||||||
# state.ilog(lvl=0,e="Decreasing profit window must be less than window end -1.")
|
# state.ilog(lvl=0,e="Decreasing profit window must be less than window end -1.")
|
||||||
@ -72,7 +74,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
|||||||
state.ilog(lvl=1,e=f"Forced Exit Window OPEN - breakeven check", msg=f"{forced_exit_window_start=} {forced_exit_window_end=} ", time=str(datetime.fromtimestamp(data['updated']).astimezone(zoneNY)))
|
state.ilog(lvl=1,e=f"Forced Exit Window OPEN - breakeven check", msg=f"{forced_exit_window_start=} {forced_exit_window_end=} ", time=str(datetime.fromtimestamp(data['updated']).astimezone(zoneNY)))
|
||||||
|
|
||||||
directive_name = "forced_exit_breakeven_period"
|
directive_name = "forced_exit_breakeven_period"
|
||||||
forced_exit_breakeven_period = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, True))
|
forced_exit_breakeven_period = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, True))
|
||||||
|
|
||||||
if forced_exit_breakeven_period is False:
|
if forced_exit_breakeven_period is False:
|
||||||
return False
|
return False
|
||||||
@ -80,11 +82,11 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
|||||||
#zatim krom posledni minuty cekame alespon na breakeven
|
#zatim krom posledni minuty cekame alespon na breakeven
|
||||||
curr_price = float(data['close'])
|
curr_price = float(data['close'])
|
||||||
#short smer
|
#short smer
|
||||||
if direction == TradeDirection.SHORT and curr_price<=float(state.avgp):
|
if direction == TradeDirection.SHORT and curr_price<=float(avgp):
|
||||||
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir SHORT")
|
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir SHORT")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if direction == TradeDirection.LONG and curr_price>=float(state.avgp):
|
if direction == TradeDirection.LONG and curr_price>=float(avgp):
|
||||||
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir LONG")
|
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir LONG")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -1,198 +1,207 @@
|
|||||||
from v2realbot.strategyblocks.activetrade.close.close_position import close_position, close_position_partial
|
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.model import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
#from icecream import install, ic
|
#from icecream import install, ic
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
#import gaka
|
||||||
|
from v2realbot.utils.utils import gaka
|
||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
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_signal_section_directive, keyword_conditions_met
|
||||||
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
|
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
|
||||||
|
|
||||||
def eval_close_position(state: StrategyState, data):
|
#TODO tady odsud
|
||||||
|
def eval_close_position(state: StrategyState, accountsWithActiveTrade, data):
|
||||||
|
|
||||||
curr_price = float(data['close'])
|
curr_price = float(data['close'])
|
||||||
state.ilog(lvl=0,e="Eval CLOSE", price=curr_price, pos=state.positions, avgp=state.avgp, pending=state.vars.pending, activeTrade=str(state.vars.activeTrade))
|
state.ilog(lvl=0,e="Eval CLOSE", price=curr_price, pos=gaka(state.account_variables, "positions"), avgp=gaka(state.account_variables, "avgp"), pending=gaka(state.account_variables, "pending"), activeTrade=str(gaka(state.account_variables, "activeTrade")))
|
||||||
|
|
||||||
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
|
#iterate over accountsWithActiveTrade
|
||||||
|
for account_str, activeTrade in accountsWithActiveTrade.items():
|
||||||
#close position handling
|
positions = state.account_variables[account_str].positions
|
||||||
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
|
avgp = state.account_variables[account_str].avgp
|
||||||
|
pending = state.account_variables[account_str].pending
|
||||||
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
|
if int(positions) != 0 and float(avgp)>0 and pending is None:
|
||||||
if 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.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
|
#close position handling
|
||||||
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data)
|
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
|
||||||
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'
|
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
|
||||||
reverse_for_SL_exit = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
if int(positions) < 0:
|
||||||
|
#get TARGET PRICE pro dany smer a signal
|
||||||
|
|
||||||
if reverse_for_SL_exit == "always":
|
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
||||||
followup_action = Followup.REVERSE
|
if activeTrade.goal_price is not None:
|
||||||
elif reverse_for_SL_exit == "cond":
|
goal_price = activeTrade.goal_price
|
||||||
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
goal_price = get_profit_target_price(state, data, TradeDirection.SHORT, activeTrade)
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
|
|
||||||
return
|
max_price = get_max_profit_price(state, data, TradeDirection.SHORT, activeTrade)
|
||||||
|
state.ilog(lvl=1,e=f"Def Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
|
||||||
|
|
||||||
|
#SL OPTIMALIZATION - PARTIAL EXIT
|
||||||
#REVERSE BASED ON REVERSE CONDITIONS
|
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data, activeTrade)
|
||||||
if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.reverse):
|
if level_met is not None and exit_adjustment is not None:
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
position = positions * exit_adjustment
|
||||||
return
|
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, activeTrade)), rem_levels=str(state.sl_optimizer_short.get_remaining_abs_levels(state, activeTrade)), 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, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=F"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
||||||
|
return
|
||||||
|
|
||||||
|
#FULL SL reached - execution
|
||||||
|
if curr_price > activeTrade.stoploss_value:
|
||||||
|
|
||||||
#EXIT ADD CONDITIONS MET (exit and add)
|
directive_name = 'reverse_for_SL_exit_short'
|
||||||
if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.exitadd):
|
reverse_for_SL_exit = get_signal_section_directive(state=state, activeTrade=activeTrade, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
|
|
||||||
return
|
|
||||||
|
|
||||||
#CLOSING BASED ON EXIT CONDITIONS
|
if reverse_for_SL_exit == "always":
|
||||||
if exit_conditions_met(state, data, TradeDirection.SHORT):
|
|
||||||
directive_name = 'reverse_for_cond_exit_short'
|
|
||||||
reverse_for_cond_exit_short = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
directive_name = 'add_for_cond_exit_short'
|
|
||||||
add_for_cond_exit_short = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
if reverse_for_cond_exit_short:
|
|
||||||
followup_action = Followup.REVERSE
|
followup_action = Followup.REVERSE
|
||||||
elif add_for_cond_exit_short:
|
elif reverse_for_SL_exit == "cond":
|
||||||
followup_action = Followup.ADD
|
followup_action = Followup.REVERSE if keyword_conditions_met(state, data=data, activeTrade=activeTrade.generated_by, direction=TradeDirection.SHORT, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
followup_action = None
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
|
||||||
return
|
|
||||||
|
|
||||||
#PROFIT
|
|
||||||
if curr_price<=goal_price:
|
|
||||||
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
|
||||||
#TODO mozna cekat na nejaky signal RSI
|
|
||||||
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
|
||||||
max_price_signal = curr_price<=max_price
|
|
||||||
#OPTIMALIZACE pri stoupajícím angle
|
|
||||||
if max_price_signal or dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
|
||||||
return
|
return
|
||||||
#pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také
|
|
||||||
elif state.dont_exit_already_activated == True:
|
|
||||||
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
#REVERSE BASED ON REVERSE CONDITIONS
|
||||||
#if dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
if keyword_conditions_met(state, data, activeTrade=activeTrade.generated_by, direction=TradeDirection.SHORT, keyword=KW.reverse):
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
|
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
||||||
state.dont_exit_already_activated = False
|
return
|
||||||
return
|
|
||||||
|
|
||||||
#FORCED EXIT PRI KONCI DNE
|
#EXIT ADD CONDITIONS MET (exit and add)
|
||||||
if eod_exit_activated(state, data, TradeDirection.SHORT):
|
if keyword_conditions_met(state, data, activeTrade=activeTrade.generated_by, direction=TradeDirection.SHORT, keyword=KW.exitadd):
|
||||||
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
|
||||||
return
|
return
|
||||||
|
|
||||||
#mame long
|
|
||||||
elif int(state.positions) > 0:
|
|
||||||
|
|
||||||
#get TARGET PRICE pro dany smer a signal
|
#CLOSING BASED ON EXIT CONDITIONS
|
||||||
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
if exit_conditions_met(state, data, TradeDirection.SHORT):
|
||||||
if state.vars.activeTrade.goal_price is not None:
|
directive_name = 'reverse_for_cond_exit_short'
|
||||||
goal_price = state.vars.activeTrade.goal_price
|
reverse_for_cond_exit_short = get_signal_section_directive(state=state, signal_name=activeTrade.signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
else:
|
directive_name = 'add_for_cond_exit_short'
|
||||||
goal_price = get_profit_target_price(state, data, TradeDirection.LONG)
|
add_for_cond_exit_short = get_signal_section_directive(state=state, signal_name=activeTrade.signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
if reverse_for_cond_exit_short:
|
||||||
max_price = get_max_profit_price(state, data, TradeDirection.LONG)
|
followup_action = Followup.REVERSE
|
||||||
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
|
elif add_for_cond_exit_short:
|
||||||
|
followup_action = Followup.ADD
|
||||||
|
else:
|
||||||
|
followup_action = None
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
|
||||||
|
return
|
||||||
|
|
||||||
#SL OPTIMALIZATION - PARTIAL EXIT
|
#PROFIT
|
||||||
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data)
|
if curr_price<=goal_price:
|
||||||
if level_met is not None and exit_adjustment is not None:
|
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
||||||
position = state.positions * exit_adjustment
|
#TODO mozna cekat na nejaky signal RSI
|
||||||
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))
|
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
||||||
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}")
|
max_price_signal = curr_price<=max_price
|
||||||
close_position_partial(state=state, data=data, direction=TradeDirection.LONG, reason=f"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
#OPTIMALIZACE pri stoupajícím angle
|
||||||
return
|
if max_price_signal or dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
||||||
|
return
|
||||||
|
#pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také
|
||||||
|
elif state.account_variables[activeTrade.account.name].dont_exit_already_activated == True:
|
||||||
|
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
||||||
|
#if dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.account_variables[activeTrade.account.name].dont_exit_already_activated=}")
|
||||||
|
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
||||||
|
return
|
||||||
|
|
||||||
#SL FULL execution
|
#FORCED EXIT PRI KONCI DNE
|
||||||
if curr_price < state.vars.activeTrade.stoploss_value:
|
if eod_exit_activated(state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT):
|
||||||
directive_name = 'reverse_for_SL_exit_long'
|
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
|
||||||
reverse_for_SL_exit = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
return
|
||||||
|
|
||||||
|
#mame long
|
||||||
|
elif int(positions) > 0:
|
||||||
|
|
||||||
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
|
#get TARGET PRICE pro dany smer a signal
|
||||||
|
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
||||||
if reverse_for_SL_exit == "always":
|
if activeTrade.goal_price is not None:
|
||||||
followup_action = Followup.REVERSE
|
goal_price = activeTrade.goal_price
|
||||||
elif reverse_for_SL_exit == "cond":
|
|
||||||
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, direction=TradeDirection.LONG, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
goal_price = get_profit_target_price(state, data, activeTrade, TradeDirection.LONG)
|
||||||
|
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
max_price = get_max_profit_price(state, activeTrade, data, TradeDirection.LONG)
|
||||||
|
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
|
||||||
|
|
||||||
#REVERSE BASED ON REVERSE CONDITIONS
|
#SL OPTIMALIZATION - PARTIAL EXIT
|
||||||
if keyword_conditions_met(state, data,TradeDirection.LONG, KW.reverse):
|
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data, activeTrade)
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
if level_met is not None and exit_adjustment is not None:
|
||||||
return
|
position = 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, activeTrade)), rem_levels=str(state.sl_optimizer_long.get_remaining_abs_levels(state, activeTrade)), 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
|
||||||
|
|
||||||
#EXIT ADD CONDITIONS MET (exit and add)
|
#SL FULL execution
|
||||||
if keyword_conditions_met(state, data, TradeDirection.LONG, KW.exitadd):
|
if curr_price < activeTrade.stoploss_value:
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
|
directive_name = 'reverse_for_SL_exit_long'
|
||||||
return
|
reverse_for_SL_exit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
||||||
|
|
||||||
#EXIT CONDITIONS
|
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
|
||||||
if exit_conditions_met(state, data, TradeDirection.LONG):
|
|
||||||
directive_name = 'reverse_for_cond_exit_long'
|
if reverse_for_SL_exit == "always":
|
||||||
reverse_for_cond_exit_long = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
directive_name = 'add_for_cond_exit_long'
|
|
||||||
add_for_cond_exit_long = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
if reverse_for_cond_exit_long:
|
|
||||||
followup_action = Followup.REVERSE
|
followup_action = Followup.REVERSE
|
||||||
elif add_for_cond_exit_long:
|
elif reverse_for_SL_exit == "cond":
|
||||||
followup_action = Followup.ADD
|
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, activeTrade, direction=TradeDirection.LONG, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
followup_action = None
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
|
|
||||||
return
|
|
||||||
|
|
||||||
#PROFIT
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
|
||||||
if curr_price>=goal_price:
|
|
||||||
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
|
||||||
#TODO mozna cekat na nejaky signal RSI
|
|
||||||
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
|
||||||
max_price_signal = curr_price>=max_price
|
|
||||||
#OPTIMALIZACE pri stoupajícím angle
|
|
||||||
if max_price_signal or dontexit_protection_met(state, data, direction=TradeDirection.LONG) is False:
|
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
|
||||||
return
|
return
|
||||||
#pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také
|
|
||||||
elif state.dont_exit_already_activated == True:
|
|
||||||
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
#REVERSE BASED ON REVERSE CONDITIONS
|
||||||
# if dontexit_protection_met(state=state, data=data,direction=TradeDirection.LONG) is False:
|
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.reverse):
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
||||||
state.dont_exit_already_activated = False
|
return
|
||||||
return
|
|
||||||
|
#EXIT ADD CONDITIONS MET (exit and add)
|
||||||
#FORCED EXIT PRI KONCI DNE
|
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.exitadd):
|
||||||
if eod_exit_activated(state, data, TradeDirection.LONG):
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
|
||||||
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED")
|
return
|
||||||
return
|
|
||||||
|
#EXIT CONDITIONS
|
||||||
|
if exit_conditions_met(state, activeTrade, data, TradeDirection.LONG):
|
||||||
|
directive_name = 'reverse_for_cond_exit_long'
|
||||||
|
reverse_for_cond_exit_long = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
directive_name = 'add_for_cond_exit_long'
|
||||||
|
add_for_cond_exit_long = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
if reverse_for_cond_exit_long:
|
||||||
|
followup_action = Followup.REVERSE
|
||||||
|
elif add_for_cond_exit_long:
|
||||||
|
followup_action = Followup.ADD
|
||||||
|
else:
|
||||||
|
followup_action = None
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
|
||||||
|
return
|
||||||
|
|
||||||
|
#PROFIT
|
||||||
|
if curr_price>=goal_price:
|
||||||
|
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
||||||
|
#TODO mozna cekat na nejaky signal RSI
|
||||||
|
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
||||||
|
max_price_signal = curr_price>=max_price
|
||||||
|
#OPTIMALIZACE pri stoupajícím angle
|
||||||
|
if max_price_signal or dontexit_protection_met(state, data, direction=TradeDirection.LONG) is False:
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
||||||
|
return
|
||||||
|
#pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také
|
||||||
|
elif state.account_variables[activeTrade.account.name].dont_exit_already_activated == True:
|
||||||
|
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
||||||
|
# if dontexit_protection_met(state=state, data=data,direction=TradeDirection.LONG) is False:
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.account_variables[activeTrade.account.name].dont_exit_already_activated=}")
|
||||||
|
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
||||||
|
return
|
||||||
|
|
||||||
|
#FORCED EXIT PRI KONCI DNE
|
||||||
|
if eod_exit_activated(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG):
|
||||||
|
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED")
|
||||||
|
return
|
||||||
@ -1,5 +1,5 @@
|
|||||||
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,17 +18,20 @@ from traceback import format_exc
|
|||||||
from v2realbot.strategyblocks.helpers import normalize_tick
|
from v2realbot.strategyblocks.helpers import normalize_tick
|
||||||
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
||||||
|
|
||||||
|
#TODO zde dodelat viz nize get get_signal_section_directive a pak pokracovat v close positions
|
||||||
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
|
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
|
||||||
def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
|
def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
|
||||||
action = str(keyword).upper()
|
action = str(keyword).upper()
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
|
mother_signal = activeTrade.generated_by
|
||||||
|
|
||||||
if skip_conf_validation is False:
|
if skip_conf_validation is False:
|
||||||
directive_name = "exit_cond_only_on_confirmed"
|
directive_name = "exit_cond_only_on_confirmed"
|
||||||
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
exit_cond_only_on_confirmed = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
|
||||||
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
||||||
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
|
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
|
||||||
@ -37,7 +40,7 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
|
|||||||
#TOTO zatim u REVERSU neresime
|
#TOTO zatim u REVERSU neresime
|
||||||
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
||||||
# directive_name = "exit_cond_min_profit"
|
# directive_name = "exit_cond_min_profit"
|
||||||
# exit_cond_min_profit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
# exit_cond_min_profit = get_signal_section_directive(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
# #máme nastavený exit_cond_min_profit
|
# #máme nastavený exit_cond_min_profit
|
||||||
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
|
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
|
||||||
@ -77,8 +80,6 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
|
|||||||
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
||||||
state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
|
state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
|
||||||
|
|
||||||
mother_signal = state.vars.activeTrade.generated_by
|
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
cond_dict = state.vars.conditions[keyword][mother_signal][smer]
|
cond_dict = state.vars.conditions[keyword][mother_signal][smer]
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
@ -108,12 +109,12 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
|
|||||||
|
|
||||||
|
|
||||||
#mozna do SL helpers tuto
|
#mozna do SL helpers tuto
|
||||||
def insert_SL_history(state):
|
def insert_SL_history(state, activeTrade: Trade):
|
||||||
#insert stoploss history as key sl_history into runner archive extended data
|
#insert stoploss history as key sl_history into runner archive extended data
|
||||||
state.extData["sl_history"].append(SLHistory(id=state.vars.activeTrade.id, time=state.time, sl_val=state.vars.activeTrade.stoploss_value))
|
state.extData["sl_history"].append(SLHistory(id=activeTrade.id, time=state.time, sl_val=activeTrade.stoploss_value))
|
||||||
|
|
||||||
|
|
||||||
def get_default_sl_value(state, direction: TradeDirection):
|
def get_default_sl_value(state, signal_name, direction: TradeDirection):
|
||||||
|
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
@ -128,15 +129,16 @@ def get_default_sl_value(state, direction: TradeDirection):
|
|||||||
state.ilog(lvl=1,e="No options for exit in stratvars. Fallback.")
|
state.ilog(lvl=1,e="No options for exit in stratvars. Fallback.")
|
||||||
return 0.01
|
return 0.01
|
||||||
directive_name = 'SL_defval_'+str(smer)
|
directive_name = 'SL_defval_'+str(smer)
|
||||||
val = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
val = get_signal_section_directive(state, signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
return val
|
return val
|
||||||
#funkce pro direktivy, ktere muzou byt overridnute v signal sekci
|
#funkce pro direktivy, ktere muzou byt overridnute v signal sekci
|
||||||
#tato funkce vyhleda signal sekci aktivniho tradu a pokusi se danou direktivu vyhledat tam,
|
#tato funkce vyhleda signal sekci aktivniho tradu a pokusi se danou direktivu vyhledat tam,
|
||||||
#pokud nenajde tak vrati default, ktery byl poskytnut
|
#pokud nenajde tak vrati default, ktery byl poskytnut
|
||||||
def get_override_for_active_trade(state, directive_name: str, default_value: str):
|
#TODO toto predelat na jiny nazev get_overide_for_directive_section (vstup muze byt opuze signal_name)
|
||||||
|
def get_signal_section_directive(state, signal_name: str, directive_name: str, default_value: str):
|
||||||
val = default_value
|
val = default_value
|
||||||
override = "NO"
|
override = "NO"
|
||||||
mother_signal = state.vars.activeTrade.generated_by
|
mother_signal = signal_name
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
override = "YES "+mother_signal
|
override = "YES "+mother_signal
|
||||||
@ -145,30 +147,30 @@ def get_override_for_active_trade(state, directive_name: str, default_value: str
|
|||||||
state.ilog(lvl=0,e=f"{directive_name} OVERRIDE {override} NEWVAL:{val} ORIGINAL:{default_value} {mother_signal}", mother_signal=mother_signal,default_value=default_value)
|
state.ilog(lvl=0,e=f"{directive_name} OVERRIDE {override} NEWVAL:{val} ORIGINAL:{default_value} {mother_signal}", mother_signal=mother_signal,default_value=default_value)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def get_profit_target_price(state, data, direction: TradeDirection):
|
def get_profit_target_price(state, data, activeTrade, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
directive_name = "profit"
|
directive_name = "profit"
|
||||||
def_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.50))
|
def_profit_both_directions = get_signal_section_directive(state, activeTrade=activeTrade, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.50))
|
||||||
|
|
||||||
#profit pro dany smer
|
#profit pro dany smer
|
||||||
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_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=def_profit_both_directions)
|
||||||
|
|
||||||
#mame v direktivve ticky
|
#mame v direktivve ticky
|
||||||
if isinstance(def_profit, (float, int)):
|
if isinstance(def_profit, (float, int)):
|
||||||
to_return = get_normalized_profitprice_from_tick(state, data, def_profit, direction)
|
to_return = get_normalized_profitprice_from_tick(state, data, activeTrade.account, def_profit, direction)
|
||||||
#mame v direktive indikator
|
#mame v direktive indikator
|
||||||
elif isinstance(def_profit, str):
|
elif isinstance(def_profit, str):
|
||||||
to_return = float(value_or_indicator(state, def_profit))
|
to_return = float(value_or_indicator(state, def_profit))
|
||||||
|
|
||||||
#min profit (ochrana extremnich hodnot indikatoru)
|
#min profit (ochrana extremnich hodnot indikatoru)
|
||||||
directive_name = 'profit_min_ind_tick_value'
|
directive_name = 'profit_min_ind_tick_value'
|
||||||
profit_min_ind_tick_value = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
|
profit_min_ind_tick_value = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=def_profit_both_directions)
|
||||||
profit_min_ind_price_value = get_normalized_profitprice_from_tick(state, data, profit_min_ind_tick_value, direction)
|
profit_min_ind_price_value = get_normalized_profitprice_from_tick(state, data, activeTrade.account, profit_min_ind_tick_value, direction)
|
||||||
|
|
||||||
#ochrana pri nastaveni profitu prilis nizko
|
#ochrana pri nastaveni profitu prilis nizko
|
||||||
if direction == TradeDirection.LONG and to_return < profit_min_ind_price_value or direction == TradeDirection.SHORT and to_return > profit_min_ind_price_value:
|
if direction == TradeDirection.LONG and to_return < profit_min_ind_price_value or direction == TradeDirection.SHORT and to_return > profit_min_ind_price_value:
|
||||||
@ -179,28 +181,32 @@ def get_profit_target_price(state, data, direction: TradeDirection):
|
|||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
##based on tick a direction, returns normalized prfoit price (LONG = avgp(nebo currprice)+norm.tick, SHORT=avgp(or currprice)-norm.tick)
|
##based on tick a direction, returns normalized prfoit price (LONG = avgp(nebo currprice)+norm.tick, SHORT=avgp(or currprice)-norm.tick)
|
||||||
def get_normalized_profitprice_from_tick(state, data, tick, direction: TradeDirection):
|
def get_normalized_profitprice_from_tick(state, data, tick, account, direction: TradeDirection):
|
||||||
|
avgp = state.account_variables[account.name].avgp
|
||||||
normalized_tick = normalize_tick(state, data, float(tick))
|
normalized_tick = normalize_tick(state, data, float(tick))
|
||||||
base_price = state.avgp if state.avgp != 0 else data["close"]
|
base_price = avgp if avgp != 0 else data["close"]
|
||||||
returned_price = price2dec(float(base_price)+normalized_tick,3) if direction == TradeDirection.LONG else price2dec(float(base_price)-normalized_tick,3)
|
returned_price = price2dec(float(base_price)+normalized_tick,3) if direction == TradeDirection.LONG else price2dec(float(base_price)-normalized_tick,3)
|
||||||
state.ilog(lvl=0,e=f"NORMALIZED TICK {tick=} {normalized_tick=} NORM.PRICE {returned_price}")
|
state.ilog(lvl=0,e=f"NORMALIZED TICK {tick=} {normalized_tick=} NORM.PRICE {returned_price}")
|
||||||
return returned_price
|
return returned_price
|
||||||
|
|
||||||
def get_max_profit_price(state, data, direction: TradeDirection):
|
def get_max_profit_price(state, activeTrade: Trade, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
directive_name = "max_profit"
|
directive_name = "max_profit"
|
||||||
max_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.35))
|
max_profit_both_directions = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.35))
|
||||||
|
|
||||||
|
avgp = state.account_variables[activeTrade.account.name].avgp
|
||||||
|
positions = state.account_variables[activeTrade.account.name].positions
|
||||||
|
|
||||||
#max profit pro dany smer, s fallbackem na bez smeru
|
#max profit pro dany smer, s fallbackem na bez smeru
|
||||||
directive_name = 'max_profit_'+str(smer)
|
directive_name = 'max_profit_'+str(smer)
|
||||||
max_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=max_profit_both_directions)
|
max_profit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=max_profit_both_directions)
|
||||||
|
|
||||||
normalized_max_profit = normalize_tick(state,data,float(max_profit))
|
normalized_max_profit = normalize_tick(state,data,float(max_profit))
|
||||||
|
|
||||||
state.ilog(lvl=0,e=f"MAX PROFIT {max_profit=} {normalized_max_profit=}")
|
state.ilog(lvl=0,e=f"MAX PROFIT {max_profit=} {normalized_max_profit=}")
|
||||||
|
|
||||||
return price2dec(float(state.avgp)+normalized_max_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_max_profit,3)
|
return price2dec(float(avgp)+normalized_max_profit,3) if int(positions) > 0 else price2dec(float(avgp)-normalized_max_profit,3)
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade
|
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
# FIBONACCI PRO PROFIT A SL
|
# FIBONACCI PRO PROFIT A SL
|
||||||
|
|
||||||
@ -49,8 +49,8 @@ class SLOptimizer:
|
|||||||
# self.exit_levels = self.init_exit_levels
|
# self.exit_levels = self.init_exit_levels
|
||||||
# self.exit_sizes = self.init_exit_sizes
|
# self.exit_sizes = self.init_exit_sizes
|
||||||
|
|
||||||
def get_trade_details(self, state):
|
def get_trade_details(self, state, activeTrade):
|
||||||
trade: Trade = state.vars.activeTrade
|
trade: Trade = activeTrade
|
||||||
#jde o novy trade - resetujeme levely
|
#jde o novy trade - resetujeme levely
|
||||||
if trade.id != self.last_trade:
|
if trade.id != self.last_trade:
|
||||||
#inicializujeme a vymazeme pripadne puvodni
|
#inicializujeme a vymazeme pripadne puvodni
|
||||||
@ -58,14 +58,14 @@ class SLOptimizer:
|
|||||||
return None, None
|
return None, None
|
||||||
self.last_trade = trade.id
|
self.last_trade = trade.id
|
||||||
#return cost_price, sl_price
|
#return cost_price, sl_price
|
||||||
return state.avgp, trade.stoploss_value
|
return state.account_variables[trade.account.name].avgp, trade.stoploss_value
|
||||||
|
|
||||||
def initialize_levels(self, state):
|
def initialize_levels(self, state):
|
||||||
directive_name = 'SL_opt_exit_levels_'+str(self.direction.value)
|
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))
|
SL_opt_exit_levels = get_signal_section_directive(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)
|
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))
|
SL_opt_exit_sizes = get_signal_section_directive(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:
|
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")
|
#print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
||||||
@ -83,11 +83,11 @@ class SLOptimizer:
|
|||||||
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
|
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_initial_abs_levels(self, state):
|
def get_initial_abs_levels(self, state, activeTrade):
|
||||||
"""
|
"""
|
||||||
Returns price levels corresponding to initial setting of exit_levels
|
Returns price levels corresponding to initial setting of exit_levels
|
||||||
"""
|
"""
|
||||||
cost_price, sl_price = self.get_trade_details(state)
|
cost_price, sl_price = self.get_trade_details(state, activeTrade)
|
||||||
if cost_price is None or sl_price is None:
|
if cost_price is None or sl_price is None:
|
||||||
return []
|
return []
|
||||||
curr_sl_distance = np.abs(cost_price - sl_price)
|
curr_sl_distance = np.abs(cost_price - sl_price)
|
||||||
@ -96,11 +96,11 @@ class SLOptimizer:
|
|||||||
else:
|
else:
|
||||||
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
|
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
|
||||||
|
|
||||||
def get_remaining_abs_levels(self, state):
|
def get_remaining_abs_levels(self, state, activeTrade):
|
||||||
"""
|
"""
|
||||||
Returns price levels corresponding to remaing exit_levels for current trade
|
Returns price levels corresponding to remaing exit_levels for current trade
|
||||||
"""
|
"""
|
||||||
cost_price, sl_price = self.get_trade_details(state)
|
cost_price, sl_price = self.get_trade_details(state, activeTrade)
|
||||||
if cost_price is None or sl_price is None:
|
if cost_price is None or sl_price is None:
|
||||||
return []
|
return []
|
||||||
curr_sl_distance = np.abs(cost_price - sl_price)
|
curr_sl_distance = np.abs(cost_price - sl_price)
|
||||||
@ -109,7 +109,7 @@ class SLOptimizer:
|
|||||||
else:
|
else:
|
||||||
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
|
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
|
||||||
|
|
||||||
def eval_position(self, state, data) -> Tuple[float, float]:
|
def eval_position(self, state, data, activeTrade) -> Tuple[float, float]:
|
||||||
"""Evaluates optimalization for current position and returns if the given level was
|
"""Evaluates optimalization for current position and returns if the given level was
|
||||||
met and how to adjust exit position.
|
met and how to adjust exit position.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
|
from v2realbot.utils.utils import gaka, isrising, isfalling,zoneNY, price2dec, print, safe_get
|
||||||
#from icecream import install, ic
|
#from icecream import install, ic
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
from threading import Event
|
from threading import Event
|
||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick, insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick, insert_SL_history
|
||||||
|
|
||||||
|
|
||||||
#pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset
|
#pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset
|
||||||
#+ varianta - skoncit breakeven
|
#+ varianta - skoncit breakeven
|
||||||
@ -25,68 +24,75 @@ from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active
|
|||||||
# SL_trailing_stop_at_breakeven_short = true
|
# SL_trailing_stop_at_breakeven_short = true
|
||||||
# SL_trailing_stop_at_breakeven_long = true
|
# SL_trailing_stop_at_breakeven_long = true
|
||||||
|
|
||||||
def trail_SL_management(state: StrategyState, data):
|
def trail_SL_management(state: StrategyState, accountsWithActiveTrade, data):
|
||||||
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
|
#iterate over accountsWithActiveTrade
|
||||||
|
for account_str, activeTrade in accountsWithActiveTrade.items():
|
||||||
|
positions = state.account_variables[account_str].positions
|
||||||
|
avgp = state.account_variables[account_str].avgp
|
||||||
|
pending = state.account_variables[account_str].pending
|
||||||
|
signal_name = activeTrade.generated_by
|
||||||
|
last_entry_index = state.account_variables[account_str].last_entry_index
|
||||||
|
if int(positions) != 0 and float(avgp)>0 and pending is None:
|
||||||
|
|
||||||
if int(state.positions) < 0:
|
if int(positions) < 0:
|
||||||
direction = TradeDirection.SHORT
|
direction = TradeDirection.SHORT
|
||||||
smer = "short"
|
smer = "short"
|
||||||
else:
|
else:
|
||||||
direction = TradeDirection.LONG
|
direction = TradeDirection.LONG
|
||||||
smer = "long"
|
smer = "long"
|
||||||
|
|
||||||
# zatim nastaveni SL plati pro vsechny - do budoucna per signal - pridat sekci
|
# zatim nastaveni SL plati pro vsechny - do budoucna per signal - pridat sekci
|
||||||
|
|
||||||
options = safe_get(state.vars, 'exit', None)
|
options = safe_get(state.vars, 'exit', None)
|
||||||
if options is None:
|
if options is None:
|
||||||
state.ilog(lvl=1,e="Trail SL. No options for exit conditions in stratvars.")
|
state.ilog(lvl=1,e="Trail SL. No options for exit conditions in stratvars.")
|
||||||
return
|
|
||||||
|
|
||||||
directive_name = 'SL_trailing_enabled_'+str(smer)
|
|
||||||
sl_trailing_enabled = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
|
||||||
|
|
||||||
|
|
||||||
#SL_trailing_protection_window_short
|
|
||||||
directive_name = 'SL_trailing_protection_window_'+str(smer)
|
|
||||||
SL_trailing_protection_window = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0))
|
|
||||||
index_to_compare = int(state.vars.last_in_index)+int(SL_trailing_protection_window)
|
|
||||||
if index_to_compare > int(data["index"]):
|
|
||||||
state.ilog(lvl=1,e=f"SL trail PROTECTION WINDOW {SL_trailing_protection_window} - TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_in_index=state.vars.last_in_index)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if sl_trailing_enabled is True:
|
|
||||||
directive_name = 'SL_trailing_stop_at_breakeven_'+str(smer)
|
|
||||||
stop_breakeven = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
|
||||||
directive_name = 'SL_defval_'+str(smer)
|
|
||||||
def_SL = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
|
||||||
directive_name = "SL_trailing_offset_"+str(smer)
|
|
||||||
offset = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
|
||||||
directive_name = "SL_trailing_step_"+str(smer)
|
|
||||||
step = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, offset))
|
|
||||||
|
|
||||||
#pokud je pozadovan trail jen do breakeven a uz prekroceno
|
|
||||||
if (direction == TradeDirection.LONG and stop_breakeven and state.vars.activeTrade.stoploss_value >= float(state.avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and state.vars.activeTrade.stoploss_value <= float(state.avgp)):
|
|
||||||
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
|
directive_name = 'SL_trailing_enabled_'+str(smer)
|
||||||
|
sl_trailing_enabled = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
||||||
|
|
||||||
|
|
||||||
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
|
#SL_trailing_protection_window_short
|
||||||
step_normalized = normalize_tick(state, data, step)
|
directive_name = 'SL_trailing_protection_window_'+str(smer)
|
||||||
def_SL_normalized = normalize_tick(state, data, def_SL)
|
SL_trailing_protection_window = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0))
|
||||||
if direction == TradeDirection.LONG:
|
index_to_compare = int(last_entry_index)+int(SL_trailing_protection_window)
|
||||||
move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized
|
if index_to_compare > int(data["index"]):
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
state.ilog(lvl=1,e=f"SL trail PROTECTION WINDOW {SL_trailing_protection_window} - TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_entry_index=last_entry_index)
|
||||||
if (move_SL_threshold) < data['close']:
|
return
|
||||||
state.vars.activeTrade.stoploss_value += step_normalized
|
|
||||||
insert_SL_history(state)
|
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
|
||||||
elif direction == TradeDirection.SHORT:
|
if sl_trailing_enabled is True:
|
||||||
move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized
|
directive_name = 'SL_trailing_stop_at_breakeven_'+str(smer)
|
||||||
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
stop_breakeven = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
||||||
if (move_SL_threshold) > data['close']:
|
directive_name = 'SL_defval_'+str(smer)
|
||||||
state.vars.activeTrade.stoploss_value -= step_normalized
|
def_SL = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
insert_SL_history(state)
|
directive_name = "SL_trailing_offset_"+str(smer)
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
offset = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
|
directive_name = "SL_trailing_step_"+str(smer)
|
||||||
|
step = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, offset))
|
||||||
|
|
||||||
|
#pokud je pozadovan trail jen do breakeven a uz prekroceno
|
||||||
|
if (direction == TradeDirection.LONG and stop_breakeven and activeTrade.stoploss_value >= float(avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and activeTrade.stoploss_value <= float(avgp)):
|
||||||
|
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
|
||||||
|
return
|
||||||
|
|
||||||
|
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
|
||||||
|
|
||||||
|
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
|
||||||
|
step_normalized = normalize_tick(state, data, step)
|
||||||
|
def_SL_normalized = normalize_tick(state, data, def_SL)
|
||||||
|
if direction == TradeDirection.LONG:
|
||||||
|
move_SL_threshold = activeTrade.stoploss_value + offset_normalized + def_SL_normalized
|
||||||
|
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
|
if (move_SL_threshold) < data['close']:
|
||||||
|
activeTrade.stoploss_value += step_normalized
|
||||||
|
insert_SL_history(state)
|
||||||
|
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
|
elif direction == TradeDirection.SHORT:
|
||||||
|
move_SL_threshold = activeTrade.stoploss_value - offset_normalized - def_SL_normalized
|
||||||
|
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
|
if (move_SL_threshold) > data['close']:
|
||||||
|
activeTrade.stoploss_value -= step_normalized
|
||||||
|
insert_SL_history(state)
|
||||||
|
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
|
|||||||
@ -55,7 +55,11 @@ 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} 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.sum(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=transform_data(state.vars.activeTrade, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial), pending=str(state.vars.pending))
|
positions = state.account_variables[state.account].positions
|
||||||
|
avgp = state.account_variables[state.account].avgp
|
||||||
|
#state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{positions}/{round(float(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.sum(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=transform_data(state.vars.activeTrade, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial), pending=str(state.vars.pending))
|
||||||
|
|
||||||
|
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} ", accountVars=transform_data(state.account_variables, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
|
|
||||||
#kroky pro CONFIRMED BAR only
|
#kroky pro CONFIRMED BAR only
|
||||||
if conf_bar == 1:
|
if conf_bar == 1:
|
||||||
|
|||||||
@ -46,6 +46,10 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
|
|||||||
#typ leveho bodu [lastbuy - cena posledniho nakupu, baropen - cena otevreni baru]
|
#typ leveho bodu [lastbuy - cena posledniho nakupu, baropen - cena otevreni baru]
|
||||||
leftpoint = safe_get(options, 'leftpoint', "lastbuy")
|
leftpoint = safe_get(options, 'leftpoint', "lastbuy")
|
||||||
|
|
||||||
|
#REFACTOR multiaccount
|
||||||
|
#avgp bereme z primarni accountu (state.account)
|
||||||
|
avgp = state.account_variables[state.account].avgp
|
||||||
|
|
||||||
#lookback has to be even
|
#lookback has to be even
|
||||||
if lookback_offset % 2 != 0:
|
if lookback_offset % 2 != 0:
|
||||||
lookback_offset += 1
|
lookback_offset += 1
|
||||||
@ -65,8 +69,8 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
|
|||||||
|
|
||||||
#pokud mame aktivni pozice, nastavime lookbackprice a time podle posledniho tradu
|
#pokud mame aktivni pozice, nastavime lookbackprice a time podle posledniho tradu
|
||||||
#pokud se ale dlouho nenakupuje (uplynulo od posledniho nakupu vic nez back_to_standard_after baru), tak se vracime k prumeru
|
#pokud se ale dlouho nenakupuje (uplynulo od posledniho nakupu vic nez back_to_standard_after baru), tak se vracime k prumeru
|
||||||
if state.avgp > 0 and state.bars.index[-1] < int(state.vars.last_buy_index)+back_to_standard_after:
|
if avgp > 0 and state.bars.index[-1] < int(state.vars.last_entry_index)+back_to_standard_after:
|
||||||
lb_index = -1 - (state.bars.index[-1] - int(state.vars.last_buy_index))
|
lb_index = -1 - (state.bars.index[-1] - int(state.vars.last_entry_index))
|
||||||
lookbackprice = state.bars.vwap[lb_index]
|
lookbackprice = state.bars.vwap[lb_index]
|
||||||
state.ilog(lvl=0,e=f"IND {name} slope {leftpoint}- LEFT POINT OVERRIDE bereme ajko cenu lastbuy {lookbackprice=} {lookbacktime=} {lb_index=}")
|
state.ilog(lvl=0,e=f"IND {name} slope {leftpoint}- LEFT POINT OVERRIDE bereme ajko cenu lastbuy {lookbackprice=} {lookbacktime=} {lb_index=}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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
|
||||||
#import mlroom.utils.mlutils as ml
|
#import mlroom.utils.mlutils as ml
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
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.model 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.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus
|
from v2realbot.common.model import TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import zoneNY, json_serial,transform_data
|
from v2realbot.utils.utils import zoneNY, json_serial,transform_data, gaka
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
#import random
|
#import random
|
||||||
import orjson
|
import orjson
|
||||||
@ -10,97 +10,108 @@ 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):
|
||||||
##evaluate prescribed trade, prvni eligible presuneme do activeTrade, zmenime stav and vytvorime objednavky
|
##evaluate prescribed trade, prvni eligible presuneme do activeTrade, zmenime stav and vytvorime objednavky
|
||||||
|
|
||||||
|
#for multiaccount setup we check if there is active trade for each account
|
||||||
|
|
||||||
if state.vars.activeTrade is not None or len(state.vars.prescribedTrades) == 0:
|
if len(state.vars.prescribedTrades) == 0 :
|
||||||
return
|
return
|
||||||
|
|
||||||
|
accountsWithNoActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is None)
|
||||||
|
|
||||||
|
if len(accountsWithNoActiveTrade.values()) == 0:
|
||||||
|
print("active trades on all accounts")
|
||||||
|
|
||||||
|
#returns true if all values are not None
|
||||||
|
#all(v is not None for v in d.keys())
|
||||||
|
|
||||||
#evaluate long (price/market)
|
#evaluate long (price/market)
|
||||||
|
#support multiaccount trades
|
||||||
state.ilog(lvl=1,e="evaluating prescr trades", trades=transform_data(state.vars.prescribedTrades, json_serial))
|
state.ilog(lvl=1,e="evaluating prescr trades", trades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
for trade in state.vars.prescribedTrades:
|
for trade in state.vars.prescribedTrades:
|
||||||
|
if trade.account.name not in accountsWithNoActiveTrade.keys() or state.account_variables[trade.account.name].pending is not None: #availability or pending
|
||||||
|
continue
|
||||||
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.LONG and (trade.entry_price is None or trade.entry_price >= data['close']):
|
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.LONG and (trade.entry_price is None or trade.entry_price >= data['close']):
|
||||||
trade.status = TradeStatus.ACTIVATED
|
trade.status = TradeStatus.ACTIVATED
|
||||||
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
||||||
state.ilog(lvl=1,e=f"evaluated LONG", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
state.ilog(lvl=1,e=f"evaluated LONG", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
state.vars.activeTrade = trade
|
execute_trade(state, data, trade) #TBD ERROR HANDLING
|
||||||
state.vars.last_buy_index = data["index"]
|
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
|
||||||
state.vars.last_in_index = data["index"]
|
continue
|
||||||
break
|
|
||||||
#evaluate shorts
|
#evaluate shorts
|
||||||
if not state.vars.activeTrade:
|
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
|
||||||
for trade in state.vars.prescribedTrades:
|
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
|
trade.status = TradeStatus.ACTIVATED
|
||||||
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
||||||
trade.status = TradeStatus.ACTIVATED
|
execute_trade(state, data, trade) #TBD ERROR HANDLING
|
||||||
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
|
||||||
state.vars.activeTrade = trade
|
continue
|
||||||
state.vars.last_buy_index = data["index"]
|
|
||||||
state.vars.last_in_index = data["index"]
|
|
||||||
break
|
|
||||||
|
|
||||||
#odeslani ORDER + NASTAVENI STOPLOSS (zatim hardcoded)
|
|
||||||
if state.vars.activeTrade:
|
|
||||||
if state.vars.activeTrade.direction == TradeDirection.LONG:
|
|
||||||
state.ilog(lvl=1,e="odesilame LONG ORDER", trade=transform_data(state.vars.activeTrade, json_serial))
|
|
||||||
if state.vars.activeTrade.size is not None:
|
|
||||||
size = state.vars.activeTrade.size
|
|
||||||
else:
|
|
||||||
size = state.vars.chunk
|
|
||||||
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 konzolidovat nize na spolecny kod pro short a long
|
||||||
|
#odeslani ORDER + NASTAVENI STOPLOSS (zatim hardcoded)
|
||||||
|
#TODO doplnit error management
|
||||||
|
def execute_trade(state, data, trade):
|
||||||
|
if trade.direction == TradeDirection.LONG:
|
||||||
|
state.ilog(lvl=1,e="odesilame LONG ORDER", trade=transform_data(trade, json_serial))
|
||||||
|
size = trade.size if trade.size is not None else state.vars.chunk
|
||||||
|
res = state.buy(size=size, account=trade.account)
|
||||||
|
#TODO ukládáme někam ID objednávky? už zde je vráceno v res
|
||||||
|
#TODO error handling
|
||||||
|
if isinstance(res, int) and res < 0:
|
||||||
|
raise Exception(f"error in required operation LONG {res}")
|
||||||
|
#TODO error handling
|
||||||
|
#defaultni goal price pripadne nastavujeme az v notifikaci
|
||||||
|
state.account_variables[trade.account.name].activeTrade = trade
|
||||||
|
|
||||||
#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 trade.stoploss_value is None:
|
||||||
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
sl_defvalue = get_default_sl_value(state=state, signal_name=trade.generated_by, direction=trade.direction)
|
||||||
|
|
||||||
if isinstance(sl_defvalue, (float, int)):
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
#normalizuji dle aktualni ceny
|
#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
|
state.account_variables[trade.account.name].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 }")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.account_variables[trade.account.name].activeTrade.stoploss_value }")
|
||||||
elif isinstance(sl_defvalue, str):
|
elif isinstance(sl_defvalue, str):
|
||||||
#from indicator
|
#from indicator
|
||||||
ind = sl_defvalue
|
ind = sl_defvalue
|
||||||
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
||||||
if sl_defvalue_abs >= float(data['close']):
|
if sl_defvalue_abs >= float(data['close']):
|
||||||
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} >= curr price")
|
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} >= curr price")
|
||||||
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
|
state.account_variables[trade.account.name].activeTrade.stoploss_value = sl_defvalue_abs
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
||||||
insert_SL_history(state)
|
insert_SL_history(state, state.account_variables[trade.account.name].activeTrade)
|
||||||
state.vars.pending = state.vars.activeTrade.id
|
elif trade.direction == TradeDirection.SHORT:
|
||||||
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
|
state.ilog(lvl=1,e="odesilame SHORT ORDER", trade=transform_data(trade, json_serial))
|
||||||
state.ilog(lvl=1,e="odesilame SHORT ORDER", trade=transform_data(state.vars.activeTrade, json_serial))
|
size = trade.size if trade.size is not None else state.vars.chunk
|
||||||
if state.vars.activeTrade.size is not None:
|
res = state.sell(size=size, account=trade.account)
|
||||||
size = state.vars.activeTrade.size
|
if isinstance(res, int) and res < 0:
|
||||||
else:
|
print(f"error in required operation SHORT {res}")
|
||||||
size = state.vars.chunk
|
raise Exception(f"error in required operation SHORT {res}")
|
||||||
res = state.sell(size=size)
|
#defaultní goalprice nastavujeme az v notifikaci
|
||||||
if isinstance(res, int) and res < 0:
|
|
||||||
print(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
|
state.account_variables[trade.account.name].activeTrade = trade
|
||||||
if state.vars.activeTrade.stoploss_value is None:
|
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||||
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
if trade.stoploss_value is None:
|
||||||
|
sl_defvalue = get_default_sl_value(state, signal_name=trade.generated_by,direction=trade.direction)
|
||||||
|
|
||||||
if isinstance(sl_defvalue, (float, int)):
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
#normalizuji dle aktualni ceny
|
#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
|
state.account_variables[trade.account.name].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 }")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.account_variables[trade.account.name].activeTrade.stoploss_value }")
|
||||||
elif isinstance(sl_defvalue, str):
|
elif isinstance(sl_defvalue, str):
|
||||||
#from indicator
|
#from indicator
|
||||||
ind = sl_defvalue
|
ind = sl_defvalue
|
||||||
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
||||||
if sl_defvalue_abs <= float(data['close']):
|
if sl_defvalue_abs <= float(data['close']):
|
||||||
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} <= curr price")
|
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} <= curr price")
|
||||||
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
|
state.account_variables[trade.account.name].activeTrade.stoploss_value = sl_defvalue_abs
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
||||||
insert_SL_history(state)
|
insert_SL_history(state, state.account_variables[trade.account.name].activeTrade)
|
||||||
state.vars.pending = state.vars.activeTrade.id
|
|
||||||
else:
|
state.account_variables[trade.account.name].pending = trade.id
|
||||||
state.ilog(lvl=1,e="unknow direction")
|
state.account_variables[trade.account.name].activeTrade = trade
|
||||||
state.vars.activeTrade = None
|
state.account_variables[trade.account.name].last_entry_index =data["index"] #last_entry_index per account
|
||||||
|
state.vars.last_entry_index = data["index"] #spolecne pro vsechny accounty
|
||||||
@ -1,6 +1,6 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, gaka
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -31,6 +31,12 @@ def signal_search(state: StrategyState, data):
|
|||||||
# slope10.out_short_if_above = 0
|
# slope10.out_short_if_above = 0
|
||||||
# ema.AND.short_if_below = 28
|
# ema.AND.short_if_below = 28
|
||||||
|
|
||||||
|
accountsWithNoActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is None)
|
||||||
|
|
||||||
|
if len(accountsWithNoActiveTrade.values()) == 0:
|
||||||
|
print("active trades on all accounts")
|
||||||
|
return
|
||||||
|
|
||||||
for signalname, signalsettings in state.vars.signals.items():
|
for signalname, signalsettings in state.vars.signals.items():
|
||||||
execute_signal_generator(state, data, signalname)
|
execute_signal_generator(state, data, signalname)
|
||||||
|
|
||||||
@ -38,14 +44,21 @@ def signal_search(state: StrategyState, data):
|
|||||||
# pokud je s cenou ceka se na cenu, pokud immmediate tak se hned provede
|
# pokud je s cenou ceka se na cenu, pokud immmediate tak se hned provede
|
||||||
# to vse za predpokladu, ze neni aktivni trade
|
# to vse za predpokladu, ze neni aktivni trade
|
||||||
|
|
||||||
def execute_signal_generator(state, data, name):
|
def execute_signal_generator(state: StrategyState, data, name):
|
||||||
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
|
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
|
||||||
options = safe_get(state.vars.signals, name, None)
|
options = safe_get(state.vars.signals, name, None)
|
||||||
|
|
||||||
|
#add account from stratvars (if there) or default to self.state.account
|
||||||
|
|
||||||
if options is None:
|
if options is None:
|
||||||
state.ilog(lvl=1,e=f"No options for {name} in stratvars")
|
state.ilog(lvl=1,e=f"No options for {name} in stratvars")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#get account of the signal, fallback to default
|
||||||
|
account = safe_get(options, "account", state.account)
|
||||||
|
account_long = safe_get(options, "account_long", account)
|
||||||
|
account_short = safe_get(options, "account_short", account)
|
||||||
|
|
||||||
if common_go_preconditions_check(state, data, signalname=name, options=options) is False:
|
if common_go_preconditions_check(state, data, signalname=name, options=options) is False:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -71,9 +84,11 @@ def execute_signal_generator(state, data, name):
|
|||||||
state.ilog(lvl=1,e=f"{name} SHORT DISABLED")
|
state.ilog(lvl=1,e=f"{name} SHORT DISABLED")
|
||||||
if long_enabled is False:
|
if long_enabled is False:
|
||||||
state.ilog(lvl=1,e=f"{name} LONG DISABLED")
|
state.ilog(lvl=1,e=f"{name} LONG DISABLED")
|
||||||
if long_enabled and go_conditions_met(state, data,signalname=name, direction=TradeDirection.LONG):
|
#predkontroloa zda neni pending na accountu nebo aktivni trade
|
||||||
|
if state.account_variables[account_long].pending is None and state.account_variables[account_long].activeTrade is None and long_enabled and go_conditions_met(state, data,signalname=name, direction=TradeDirection.LONG):
|
||||||
multiplier = get_multiplier(state, data, options, TradeDirection.LONG)
|
multiplier = get_multiplier(state, data, options, TradeDirection.LONG)
|
||||||
state.vars.prescribedTrades.append(Trade(
|
state.vars.prescribedTrades.append(Trade(
|
||||||
|
account = account_long,
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
@ -83,9 +98,10 @@ def execute_signal_generator(state, data, name):
|
|||||||
direction=TradeDirection.LONG,
|
direction=TradeDirection.LONG,
|
||||||
entry_price=None,
|
entry_price=None,
|
||||||
stoploss_value = None))
|
stoploss_value = None))
|
||||||
elif short_enabled and go_conditions_met(state, data, signalname=name, direction=TradeDirection.SHORT):
|
elif state.account_variables[account_short].pending is None and state.account_variables[account_short].activeTrade is None and short_enabled and go_conditions_met(state, data, signalname=name, direction=TradeDirection.SHORT):
|
||||||
multiplier = get_multiplier(state, data, options, TradeDirection.SHORT)
|
multiplier = get_multiplier(state, data, options, TradeDirection.SHORT)
|
||||||
state.vars.prescribedTrades.append(Trade(
|
state.vars.prescribedTrades.append(Trade(
|
||||||
|
account=account_short,
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||||
import v2realbot.utils.utils as utls
|
import v2realbot.utils.utils as utls
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
@ -99,7 +99,8 @@ def get_multiplier(state: StrategyState, data, signaloptions: dict, direction: T
|
|||||||
|
|
||||||
if probe_enabled:
|
if probe_enabled:
|
||||||
#zatim pouze probe number 1 natvrdo, tzn. nesmi byt trade pro aktivace
|
#zatim pouze probe number 1 natvrdo, tzn. nesmi byt trade pro aktivace
|
||||||
if state.vars.last_in_index is None:
|
#zatim funguje pouze pro primarni
|
||||||
|
if state.account_variables[state.account].last_entry_index is None:
|
||||||
#probe_number = utls.safe_get(options, "probe_number",1)
|
#probe_number = utls.safe_get(options, "probe_number",1)
|
||||||
probe_size = float(utls.safe_get(options, "probe_size", 0.1))
|
probe_size = float(utls.safe_get(options, "probe_size", 0.1))
|
||||||
state.ilog(lvl=1,e=f"SIZER - PROBE - setting multiplier to {probe_size}", options=options)
|
state.ilog(lvl=1,e=f"SIZER - PROBE - setting multiplier to {probe_size}", options=options)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
#from rich import print
|
#from rich import print
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print as richprint
|
from rich import print as richprint
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
@ -23,7 +23,7 @@ from collections import defaultdict
|
|||||||
from scipy.stats import zscore
|
from scipy.stats import zscore
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Tuple, Optional, List
|
from typing import Tuple, Optional, List
|
||||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
import vectorbtpro as vbt
|
import vectorbtpro as vbt
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import decimal
|
|||||||
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator
|
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator, AccountVariables
|
||||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from typing import List
|
from typing import List
|
||||||
import tomli
|
import tomli
|
||||||
from v2realbot.config import DATA_DIR, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY
|
from v2realbot.config import DATA_DIR, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY
|
||||||
@ -36,6 +36,220 @@ import shutil
|
|||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
import pandas_market_calendars as mcal
|
import pandas_market_calendars as mcal
|
||||||
|
from typing import Dict, Any, Callable, Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
def get_attribute(obj: Any, attr: str) -> Any:
|
||||||
|
"""
|
||||||
|
Returns the value of given attribute from the object being it dict or BaseModel
|
||||||
|
"""
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return obj.get(attr)
|
||||||
|
if isinstance(obj, BaseModel):
|
||||||
|
return getattr(obj, attr, None)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def gaka(
|
||||||
|
account_variables: Dict[str, Any],
|
||||||
|
name_of_attribute: str,
|
||||||
|
transform_function: Optional[Callable[[Any], Any]] = None,
|
||||||
|
condition_function: Optional[Callable[[Any], bool]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Gets Account Keyed Attribute
|
||||||
|
Extracts the specified attribute from each account variable in the given dictionary.
|
||||||
|
It also contains transformation function and condition function.
|
||||||
|
|
||||||
|
```
|
||||||
|
avgps = gaka(account_variables, "avgp",
|
||||||
|
transform_function=lambda x: round(x, 3),
|
||||||
|
condition_function=lambda x: x > 3)
|
||||||
|
|
||||||
|
returns:
|
||||||
|
{
|
||||||
|
'account2': 5000.654,
|
||||||
|
'account3': 3000.789,
|
||||||
|
'account4': 8000.235
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Args:
|
||||||
|
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
||||||
|
name_of_attribute (str): The name of the attribute to extract.
|
||||||
|
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
||||||
|
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for account_str, acc_vars in account_variables.items():
|
||||||
|
value = get_attribute(acc_vars, name_of_attribute)
|
||||||
|
|
||||||
|
if value is None and not hasattr(acc_vars, name_of_attribute):
|
||||||
|
continue
|
||||||
|
|
||||||
|
transformed_value = transform_function(value) if transform_function else value
|
||||||
|
|
||||||
|
if condition_function is None or condition_function(transformed_value):
|
||||||
|
result[account_str] = transformed_value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def gaka_unoptimized(
|
||||||
|
account_variables: Dict[str, Any],
|
||||||
|
name_of_attribute: str,
|
||||||
|
transform_function: Optional[Callable[[Any], Any]] = None,
|
||||||
|
condition_function: Optional[Callable[[Any], bool]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Gets Account Keyed Attribute
|
||||||
|
Extracts the specified attribute from each account variable in the given dictionary.
|
||||||
|
It also contains transformation function and condition function.
|
||||||
|
|
||||||
|
```
|
||||||
|
avgps = gaka(account_variables, "avgp",
|
||||||
|
transform_function=lambda x: round(x, 3),
|
||||||
|
condition_function=lambda x: x > 3)
|
||||||
|
|
||||||
|
returns:
|
||||||
|
{
|
||||||
|
'account2': 5000.654,
|
||||||
|
'account3': 3000.789,
|
||||||
|
'account4': 8000.235
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Args:
|
||||||
|
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
||||||
|
name_of_attribute (str): The name of the attribute to extract.
|
||||||
|
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
||||||
|
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for account, acc_vars in account_variables.items():
|
||||||
|
if isinstance(acc_vars, BaseModel):
|
||||||
|
value = getattr(acc_vars, name_of_attribute, None)
|
||||||
|
elif isinstance(acc_vars, dict):
|
||||||
|
value = acc_vars.get(name_of_attribute, None)
|
||||||
|
else:
|
||||||
|
continue # Skip if acc_vars is neither BaseModel nor dict
|
||||||
|
|
||||||
|
if value is None and not hasattr(acc_vars, name_of_attribute):
|
||||||
|
continue # Skip if attribute doesn't exist
|
||||||
|
|
||||||
|
transformed_value = transform_function(value) if transform_function else value
|
||||||
|
|
||||||
|
if condition_function is None or condition_function(transformed_value):
|
||||||
|
result[account] = transformed_value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def gaka_old_with_comprehesion(
|
||||||
|
account_variables: Dict[str, Any],
|
||||||
|
name_of_attribute: str,
|
||||||
|
transform_function: Optional[Callable[[Any], Any]] = None,
|
||||||
|
condition_function: Optional[Callable[[Any], bool]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Gets Account Keyed Attribute
|
||||||
|
Extracts the specified attribute from each account variable in the given dictionary.
|
||||||
|
|
||||||
|
It also contains transformation function and condition function.
|
||||||
|
|
||||||
|
```
|
||||||
|
avgps = gaka(account_variables, "avgp",
|
||||||
|
transform_function=lambda x: round(x, 3),
|
||||||
|
condition_function=lambda x: x > 3)
|
||||||
|
|
||||||
|
returns:
|
||||||
|
{
|
||||||
|
'account2': 5000.654,
|
||||||
|
'account3': 3000.789,
|
||||||
|
'account4': 8000.235
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Args:
|
||||||
|
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
||||||
|
name_of_attribute (str): The name of the attribute to extract.
|
||||||
|
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
||||||
|
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
account: transformed_value
|
||||||
|
for account, acc_vars in account_variables.items()
|
||||||
|
if (value := (
|
||||||
|
getattr(acc_vars, name_of_attribute, None)
|
||||||
|
if isinstance(acc_vars, BaseModel)
|
||||||
|
else acc_vars.get(name_of_attribute, None)
|
||||||
|
if isinstance(acc_vars, dict)
|
||||||
|
else None
|
||||||
|
)) is not None or name_of_attribute in acc_vars.__dict__
|
||||||
|
and (transformed_value := (
|
||||||
|
transform_function(value) if transform_function else value
|
||||||
|
)) is not None
|
||||||
|
and (not condition_function or condition_function(transformed_value))
|
||||||
|
}
|
||||||
|
|
||||||
|
def gaka_old(account_variables: Dict[str, Any], name_of_attribute: str, transform_function: Optional[Callable[[Any], Any]] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Gets Account Keyed Attribute
|
||||||
|
Extracts the specified attribute from each account variable in the given dictionary.
|
||||||
|
|
||||||
|
It also contain transformation function.
|
||||||
|
|
||||||
|
```
|
||||||
|
avgps = extract_attribute(account_variables, "avgp", lambda x: round(x, 3))
|
||||||
|
|
||||||
|
returns:
|
||||||
|
{
|
||||||
|
'account1': 1000.123,
|
||||||
|
'account2': 5000.654,
|
||||||
|
'account3': 3000.789,
|
||||||
|
'account4': 8000.235
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Args:
|
||||||
|
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
||||||
|
name_of_attribute (str): The name of the attribute to extract.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: A dictionary containing the extracted attribute for each account.
|
||||||
|
"""
|
||||||
|
#return {account: getattr(acc_vars, name_of_attribute) for account, acc_vars in account_variables.items()}
|
||||||
|
return {
|
||||||
|
account: (
|
||||||
|
transform_function(value) if transform_function and value is not None else value
|
||||||
|
)
|
||||||
|
for account, acc_vars in account_variables.items()
|
||||||
|
if (value := (
|
||||||
|
getattr(acc_vars, name_of_attribute, None)
|
||||||
|
if isinstance(acc_vars, BaseModel)
|
||||||
|
else acc_vars.get(name_of_attribute, None)
|
||||||
|
if isinstance(acc_vars, dict)
|
||||||
|
else None
|
||||||
|
)) is not None
|
||||||
|
}
|
||||||
|
|
||||||
|
def empty_lists_in_dict(d: dict):
|
||||||
|
"""
|
||||||
|
Assumes all values of dict are list. Returns true if all lists are empty.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
d (dict): The dictionary to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if all lists in the dictionary are empty, False otherwise.
|
||||||
|
"""
|
||||||
|
return all(len(v) == 0 for v in d.values())
|
||||||
|
|
||||||
def validate_and_format_time(time_string):
|
def validate_and_format_time(time_string):
|
||||||
"""
|
"""
|
||||||
@ -675,6 +889,7 @@ def json_serial(obj):
|
|||||||
SLHistory: lambda obj: obj.__dict__,
|
SLHistory: lambda obj: obj.__dict__,
|
||||||
InstantIndicator: lambda obj: obj.__dict__,
|
InstantIndicator: lambda obj: obj.__dict__,
|
||||||
StrategyInstance: lambda obj: obj.__dict__,
|
StrategyInstance: lambda obj: obj.__dict__,
|
||||||
|
AccountVariables: lambda obj: obj.__dict__
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer = type_map.get(type(obj))
|
serializer = type_map.get(type(obj))
|
||||||
|
|||||||
Reference in New Issue
Block a user