pridany metriky a bugfixy pro live run

This commit is contained in:
David Brazda
2023-08-28 20:22:37 +02:00
parent a94e2ed5e4
commit 4c861c59a1
9 changed files with 112 additions and 29 deletions

View File

@ -6,7 +6,7 @@ from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Orde
from v2realbot.indicators.indicators import ema
from v2realbot.indicators.oscillators import rsi
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, get_tick, round2five, is_open_rush, is_close_rush, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, round2five, is_open_rush, is_close_rush, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial
from datetime import datetime
from uuid import uuid4
import json
@ -173,13 +173,13 @@ def next(data, state: StrategyState):
#vrati true pokud dany indikator prekrocil threshold dolu
def buy_if_crossed_down(indicator, value):
res = crossed_down(threshold=value, list=get_source_or_MA(indicator))
state.ilog(e=f"buy_if_crossed_down {indicator} {value} {res}")
state.ilog(e=f"signal_if_crossed_down {indicator} {value} {res}")
return res
#vrati true pokud dany indikator prekrocil threshold nahoru
def buy_if_crossed_up(indicator, value):
res = crossed_up(threshold=value, list=get_source_or_MA(indicator))
state.ilog(e=f"buy_if_crossed_up {indicator} {value} {res}")
state.ilog(e=f"signal_if_crossed_up {indicator} {value} {res}")
return res
def populate_cbar_tick_price_indicator():
@ -625,6 +625,36 @@ def next(data, state: StrategyState):
# state.ilog(e=f"SELL_PROTECTION {conditions_met} enabled")
# return result
def normalize_tick(tick: float, price: float = None, return_two_decimals: bool = False):
"""
Pokud je nastaveno v direktive:
#zda normalizovat vsechyn ticky (tzn. profit, maxprofit, SL atp.)
Normalize_ticks= true
Normalized Tick base price = 30
prevede normalizovany tick na tick odpovidajici vstupni cene
vysledek je zaokoruhleny na 2 des.mista
u cen pod 30, vrací 0.01. U cen nad 30 vrací pomerne zvetsene,
"""
#nemusime dodavat cenu, bereme aktualni
if price is None:
price = data["close"]
normalize_ticks = safe_get(state.vars, "normalize_ticks",False)
normalized_base_price = safe_get(state.vars, "normalized_base_price",30)
if normalize_ticks:
if price<normalized_base_price:
return tick
else:
#ratio of price vs base price
ratio = price/normalized_base_price
normalized_tick = ratio*tick
return price2dec(normalized_tick) if return_two_decimals else normalized_tick
else:
return tick
def get_default_sl_value(direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
@ -641,12 +671,12 @@ def next(data, state: StrategyState):
def get_profit_target_price():
def_profit = safe_get(state.vars, "def_profit",state.vars.profit)
cena = float(state.avgp)
return price2dec(cena+get_tick(cena,float(state.vars.profit)),3) if state.positions > 0 else price2dec(cena-get_tick(cena,float(state.vars.profit)),3)
return price2dec(cena+normalize_tick(float(state.vars.profit)),3) if int(state.positions) > 0 else price2dec(cena-normalize_tick(float(state.vars.profit)),3)
def get_max_profit_price():
max_profit = float(safe_get(state.vars, "max_profit",0.03))
cena = float(state.avgp)
return price2dec(cena+get_tick(cena,max_profit),3) if state.positions > 0 else price2dec(cena-get_tick(cena,max_profit),3)
return price2dec(cena+normalize_tick(max_profit),3) if int(state.positions) > 0 else price2dec(cena-normalize_tick(max_profit),3)
#TBD pripadne opet dat parsovani pole do INITu
@ -668,7 +698,7 @@ def next(data, state: StrategyState):
disable_exit_proteciton_when['disabled_in_config'] = safe_get(options, 'enabled', False) is False
#too good to be true (maximum profit)
#disable_sell_proteciton_when['tgtbt_reached'] = safe_get(options, 'tgtbt', False) is False
disable_exit_proteciton_when['disable_if_positions_above'] = int(safe_get(options, 'disable_if_positions_above', 0)) < abs(state.positions)
disable_exit_proteciton_when['disable_if_positions_above'] = int(safe_get(options, 'disable_if_positions_above', 0)) < abs(int(state.positions))
#testing preconditions
result, conditions_met = eval_cond_dict(disable_exit_proteciton_when)
@ -747,20 +777,22 @@ def next(data, state: StrategyState):
#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(e=f"SL trail stop at breakeven {str(smer)} SL {state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
state.ilog(e=f"SL trail stop at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
return
#IDEA: Nyni posouvame SL o offset, mozna ji posunout jen o direktivu step ?
offset_normalized = get_tick(data['close'],offset) #to ticks and from options
def_SL_normalized = get_tick(data['close'],def_SL)
offset_normalized = normalize_tick(offset) #to ticks and from options
def_SL_normalized = normalize_tick(def_SL)
if direction == TradeDirection.LONG:
move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized
state.ilog(e=f"SL trailing EVAL {smer} SL:{state.vars.activeTrade.stoploss_value} MOVETHRESHOLD:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
if (move_SL_threshold) < data['close']:
state.vars.activeTrade.stoploss_value += offset_normalized
state.ilog(e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
elif direction == TradeDirection.SHORT:
move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized
state.ilog(e=f"SL trailing EVAL {smer} SL:{state.vars.activeTrade.stoploss_value} MOVETHRESHOLD:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
if (move_SL_threshold) > data['close']:
state.vars.activeTrade.stoploss_value -= offset_normalized
state.ilog(e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, def_SL_normalized=def_SL_normalized)
@ -788,7 +820,7 @@ def next(data, state: StrategyState):
#SL - execution
if curr_price > state.vars.activeTrade.stoploss_value:
state.ilog(e=f"STOPLOSS reached on SHORT", curr_price=curr_price, trade=state.vars.activeTrade)
res = state.buy(size=abs(state.positions))
res = state.buy(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS BUY {res}")
state.vars.pending = state.vars.activeTrade.id
@ -797,7 +829,7 @@ def next(data, state: StrategyState):
#CLOSING BASED ON EXIT CONDITIONS
if exit_conditions_met(TradeDirection.SHORT):
res = state.buy(size=abs(state.positions))
res = state.buy(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation EXIT COND BUY {res}")
state.vars.pending = state.vars.activeTrade.id
@ -813,7 +845,7 @@ def next(data, state: StrategyState):
max_price_signal = curr_price<=max_price
#OPTIMALIZACE pri stoupajícím angle
if max_price_signal or sell_protection_enabled() is False:
res = state.buy(size=abs(state.positions))
res = state.buy(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation PROFIT BUY {res}")
state.vars.pending = state.vars.activeTrade.id
@ -838,7 +870,7 @@ def next(data, state: StrategyState):
return
if exit_conditions_met(TradeDirection.LONG):
res = state.sell(size=abs(state.positions))
res = state.sell(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation EXIT COND SELL {res}")
state.vars.pending = state.vars.activeTrade.id
@ -872,6 +904,7 @@ def next(data, state: StrategyState):
for trade in state.vars.prescribedTrades:
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.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
state.ilog(e=f"evaluated SHORT {str(trade)}", prescrTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)))
state.vars.activeTrade = trade
break
@ -881,6 +914,7 @@ def next(data, state: StrategyState):
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
state.ilog(e=f"evaluaed SHORT {str(trade)}", prescTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)))
trade.status = TradeStatus.ACTIVATED
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
state.vars.activeTrade = trade
break
@ -895,7 +929,7 @@ def next(data, state: StrategyState):
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(direction=state.vars.activeTrade.direction)
#normalizuji dle aktualni ceny
sl_defvalue_normalized = get_tick(data['close'],sl_defvalue)
sl_defvalue_normalized = normalize_tick(sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = state.vars.activeTrade.id
@ -908,7 +942,7 @@ def next(data, state: StrategyState):
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(direction=state.vars.activeTrade.direction)
#normalizuji dle aktualni ceny
sl_defvalue_normalized = get_tick(data['close'],sl_defvalue)
sl_defvalue_normalized = normalize_tick(sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = state.vars.activeTrade.id
@ -1028,25 +1062,27 @@ def next(data, state: StrategyState):
recurring = safe_get(options, "reccurring", False)
on_confirmed_only = safe_get(options, 'on_confirmed_only', False)
plugin = safe_get(options, 'plugin', None)
short_enabled = safe_get(state.vars, "short_enabled",True)
long_enabled = safe_get(state.vars, "long_enabled",True)
#pokud je plugin True, spusti se kod
if plugin:
execute_signal_generator_plugin(name)
else:
#common signals based on 1) configured signals in stratvars
#toto umoznuje jednoduchy prescribed trade bez ceny
if conditions_met(signalname=name, direction=TradeDirection.LONG):
#toto umoznuje jednoduchy prescribed trade bez ceny
if long_enabled and conditions_met(signalname=name, direction=TradeDirection.LONG):
state.vars.prescribedTrades.append(Trade(
id=uuid4(),
validfrom=datetime.now(tz=zoneNY),
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
direction=TradeDirection.LONG,
entry_price=None,
stoploss_value = None))
elif conditions_met(signalname=name, direction=TradeDirection.SHORT):
elif short_enabled and conditions_met(signalname=name, direction=TradeDirection.SHORT):
state.vars.prescribedTrades.append(Trade(
id=uuid4(),
validfrom=datetime.now(tz=zoneNY),
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
direction=TradeDirection.SHORT,
entry_price=None,

View File

@ -18,12 +18,12 @@ class TradeStoplossType(str, Enum):
class Trade(BaseModel):
id: UUID
validfrom: datetime
last_update: datetime
status: TradeStatus
direction: TradeDirection
entry_price: Optional[float] = None
# stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None
profit: Optional[float] = None
profit_sum: Optional[float] = None
profit: Optional[float] = 0
profit_sum: Optional[float] = 0

View File

@ -9,6 +9,7 @@ from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Orde
from v2realbot.common.model import StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram
from v2realbot.utils.ilog import delete_logs
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
from threading import Thread, current_thread, Event, enumerate
from v2realbot.config import STRATVARS_UNCHANGEABLES, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR,BT_FILL_CONS_TRADES_REQUIRED,BT_FILL_LOG_SURROUNDING_TRADES,BT_FILL_CONDITION_BUY_LIMIT,BT_FILL_CONDITION_SELL_LIMIT, GROUP_TRADES_WITH_TIMESTAMP_LESS_THAN
@ -466,9 +467,45 @@ def populate_metrics_output_directory(strat: StrategyInstance):
#filt = max_positions['side'] == 'OrderSide.BUY'
res = dict(zip(max_positions['qty'], max_positions['count']))
#pridani klice obsahujici prescribed trades
#metrikz z prescribedTrades, pokud existuji
try:
long_profit = 0
short_profit = 0
long_losses = 0
short_losses = 0
long_wins = 0
short_wins = 0
max_profit = 0
max_profit_time = None
for trade in strat.state.vars.prescribedTrades:
if trade.profit_sum > max_profit:
max_profit = trade.profit_sum
max_profit_time = trade.last_update
if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.LONG:
if trade.profit is not None:
long_profit += trade.profit
if trade.profit < 0:
long_losses += trade.profit
if trade.profit > 0:
long_wins += trade.profit
if trade.status == TradeStatus.ACTIVATED and trade.direction == TradeDirection.SHORT:
if trade.profit is not None:
short_profit += trade.profit
if trade.profit < 0:
short_losses += trade.profit
if trade.profit > 0:
short_wins += trade.profit
res["long_profit"] = round(long_profit,2)
res["short_profit"] = round(short_profit,2)
res["long_losses"] = round(long_losses,2)
res["short_losses"] = round(short_losses,2)
res["long_wins"] = round(long_wins,2)
res["short_wins"] = round(short_wins,2)
res["max_profit"] = round(max_profit,2)
res["max_profit_time"] = str(max_profit_time)
#vlozeni celeho listu
res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial))
except NameError:
pass

View File

@ -351,7 +351,7 @@
</div>
<div class="form-group">
<label for="note" class="form-label">note</label>
<textarea class="form-control" rows="2" id="note" name="note"></textarea>
<textarea class="form-control" rows="7" id="note" name="note"></textarea>
</div>
<div class="form-group">
<label for="history" class="form-label">history</label>

View File

@ -2,10 +2,12 @@ from v2realbot.strategy.base import Strategy
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print, json_serial, safe_get, get_tick
from v2realbot.utils.tlog import tlog, tlog_exception
from v2realbot.enums.enums import Mode, Order, Account, RecordType
from alpaca.trading.models import TradeUpdate
#from alpaca.trading.models import TradeUpdate
from v2realbot.common.model import TradeUpdate
from alpaca.trading.enums import TradeEvent, OrderStatus
from v2realbot.indicators.indicators import ema
import json
from datetime import datetime
#from rich import print
from random import randrange
from alpaca.common.exceptions import APIError
@ -49,7 +51,8 @@ class StrategyClassicSL(Strategy):
#zapsat profit do prescr.trades
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
trade.profit = trade_profit
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
trade.profit += trade_profit
trade.profit_sum = self.state.profit
#zapsat update profitu do tradeList
@ -96,7 +99,8 @@ class StrategyClassicSL(Strategy):
#zapsat profit do prescr.trades
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
trade.profit = trade_profit
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
trade.profit += trade_profit
trade.profit_sum = self.state.profit
#zapsat update profitu do tradeList

View File

@ -423,6 +423,8 @@ class Strategy:
async def order_updates(self, data: TradeUpdate):
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
now = datetime.now().timestamp()
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
data = TradeUpdate(**data.dict())
else:
now = self.bt.time

View File

@ -80,7 +80,11 @@ def crossed(threshold, list):
def get_tick(price: float, normalized_ticks: float = 0.01):
"""
prevede normalizovany tick na tick odpovidajici vstupni cene
Pozor existuje varianta "normalize_tick", ktera je lepsi a podporuje direktivy ve strategii:
Normalize_ticks= true
Normalized Tick base price = 30
Tahle verze pracuje s globalnim nastavenim.
Prevede normalizovany tick na tick odpovidajici vstupni cene
vysledek je zaokoruhleny na 2 des.mista
u cen pod 30, vrací 0.01. U cen nad 30 vrací pomerne zvetsene,