rel profit first version

This commit is contained in:
David Brazda
2023-10-20 21:48:46 +02:00
parent d5ebfee762
commit 47e2e255c7
11 changed files with 73 additions and 15 deletions

View File

@ -6,6 +6,7 @@ from uuid import UUID
class TradeStatus(str, Enum): class TradeStatus(str, Enum):
READY = "ready" READY = "ready"
ACTIVATED = "activated" ACTIVATED = "activated"
CLOSED = "closed"
#FINISHED = "finished" #FINISHED = "finished"
class TradeDirection(str, Enum): class TradeDirection(str, Enum):
@ -28,4 +29,6 @@ class Trade(BaseModel):
stoploss_value: Optional[float] = None stoploss_value: Optional[float] = None
profit: Optional[float] = 0 profit: Optional[float] = 0
profit_sum: Optional[float] = 0 profit_sum: Optional[float] = 0
rel_profit: Optional[float] = 0
rel_profit_cum: Optional[float] = 0

View File

@ -79,7 +79,7 @@ def row_to_runarchiveview(row: dict) -> RunArchiveView:
trade_count=int(row['trade_count']), trade_count=int(row['trade_count']),
end_positions=int(row['end_positions']), end_positions=int(row['end_positions']),
end_positions_avgp=float(row['end_positions_avgp']), end_positions_avgp=float(row['end_positions_avgp']),
metrics=json.loads(row['metrics']), metrics=json.loads(row['metrics']) if row['metrics'] else None
) )
#prevede dict radku zpatky na objekt vcetme retypizace #prevede dict radku zpatky na objekt vcetme retypizace

View File

@ -191,6 +191,8 @@ class TradeUpdate(BaseModel):
pos_avg_price: Optional[float] pos_avg_price: Optional[float]
profit: Optional[float] profit: Optional[float]
profit_sum: Optional[float] profit_sum: Optional[float]
rel_profit: Optional[float]
rel_profit_cum: Optional[float]
signal_name: Optional[str] signal_name: Optional[str]

View File

@ -7,7 +7,7 @@ from alpaca.data.enums import DataFeed
from alpaca.data.timeframe import TimeFrame from alpaca.data.timeframe import TimeFrame
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, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem
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.utils import AttributeDict, zoneNY, zonePRG, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram
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.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime from datetime import datetime
@ -21,6 +21,7 @@ from queue import Queue
from tinydb import TinyDB, Query, where from tinydb import TinyDB, Query, where
from tinydb.operations import set from tinydb.operations import set
import json import json
import numpy as np
from numpy import ndarray from numpy import ndarray
from rich import print from rich import print
import pandas as pd import pandas as pd
@ -374,9 +375,10 @@ def run_batch_stratin(id: UUID, runReq: RunRequest):
cal_list = [] cal_list = []
#interval dame do formatu list(RunDays) #interval dame do formatu list(RunDays)
#TODO do budoucna predelat Interval na RunDays a na zone aware datetime #TODO do budoucna predelat Interval na RunDays a na zone aware datetime
#zatim testlisty dávám v cz casu
for intrvl in testlist.dates: for intrvl in testlist.dates:
start_time = zoneNY.localize(datetime.fromisoformat(intrvl.start)) start_time = zonePRG.localize(datetime.fromisoformat(intrvl.start))
end_time = zoneNY.localize(datetime.fromisoformat(intrvl.end)) end_time = zonePRG.localize(datetime.fromisoformat(intrvl.end))
cal_list.append(RunDay(start = start_time, end = end_time, note=intrvl.note, id=testlist.id)) cal_list.append(RunDay(start = start_time, end = end_time, note=intrvl.note, id=testlist.id))
print(f"Getting intervals - RESULT ({len(cal_list)}): {cal_list}") print(f"Getting intervals - RESULT ({len(cal_list)}): {cal_list}")
@ -443,7 +445,7 @@ def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
cnt_max = len(rundays) cnt_max = len(rundays)
cnt = 0 cnt = 0
#promenna pro sdileni mezi runy jednotlivych batchů (např. daily profit) #promenna pro sdileni mezi runy jednotlivych batchů (např. daily profit)
inter_batch_params = dict(batch_profit=0) inter_batch_params = dict(batch_profit=0, batch_rel_profit=0)
note_from_run_request = runReq.note note_from_run_request = runReq.note
for day in rundays: for day in rundays:
cnt += 1 cnt += 1
@ -636,6 +638,12 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
#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"] = inter_batch_params["batch_profit"] res["profit"]["batch_sum_profit"] = inter_batch_params["batch_profit"]
res["profit"]["batch_sum_rel_profit"] = inter_batch_params["batch_rel_profit"]
#rel_profit rozepsane zisky
res["profit"]["rel_profits"] = strat.state.rel_profit_cum
#rel_profit zprumerovane
res["profit"]["rel_profit_cum"] = float(np.mean(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0
#metrikz z prescribedTrades, pokud existuji #metrikz z prescribedTrades, pokud existuji
try: try:
@ -691,8 +699,10 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
mpt_string = "PT"+str(max_profit_time.hour)+":"+str(max_profit_time.minute) if max_profit_time is not None else "" mpt_string = "PT"+str(max_profit_time.hour)+":"+str(max_profit_time.minute) if max_profit_time is not None else ""
mlt_string ="LT"+str(max_loss_time.hour)+":"+str(max_loss_time.minute) if max_loss_time is not None else "" mlt_string ="LT"+str(max_loss_time.hour)+":"+str(max_loss_time.minute) if max_loss_time is not None else ""
rp_string = "RP" + str(float(np.mean(strat.state.rel_profit_cum))) if len(strat.state.rel_profit_cum) >0 else "noRP"
##summary pro rychle zobrazeni P333L-222 PT9:30 PL10:30 ##summary pro rychle zobrazeni P333L-222 PT9:30 PL10:30
res["profit"]["sum"]="P"+str(int(max_profit))+"L"+str(int(max_loss))+" "+ mpt_string+" " + mlt_string res["profit"]["sum"]="P"+str(int(max_profit))+"L"+str(int(max_loss))+" "+ mpt_string+" " + mlt_string + rp_string + " "+str(strat.state.rel_profit_cum)
#vlozeni celeho listu #vlozeni celeho listu
res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial)) res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial))
@ -729,6 +739,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
#add profit of this batch iteration to batch_sum_profit #add profit of this batch iteration to batch_sum_profit
if inter_batch_params is not None: if inter_batch_params is not None:
inter_batch_params["batch_profit"] += round(float(strat.state.profit),2) inter_batch_params["batch_profit"] += round(float(strat.state.profit),2)
inter_batch_params["batch_rel_profit"] += float(np.mean(strat.state.rel_profit_cum)) if len(strat.state.rel_profit_cum) > 0 else 0
#WIP #WIP
#populate result metrics dictionary (max drawdown etc.) #populate result metrics dictionary (max drawdown etc.)

View File

@ -710,7 +710,7 @@ var archiveRecords =
//zobrazujeme jen kratkou summary pokud mame, jinak davame vse, do titlu davame vzdy vse //zobrazujeme jen kratkou summary pokud mame, jinak davame vse, do titlu davame vzdy vse
//console.log(data) //console.log(data)
short = null short = null
if ((data.profit) && (data.profit.sum)) { if ((data) && (data.profit) && (data.profit.sum)) {
short = data.profit.sum short = data.profit.sum
} }
else { else {

View File

@ -28,7 +28,7 @@ Prism.languages.log = {
alias: ['error', 'important'] alias: ['error', 'important']
}, },
{ {
pattern: /\b(?:WARN|WARNING|WRN|ENTRY|LP|SL)\b/, pattern: /\b(?:WARN|WARNING|WRN|ENTRY|LP|SL|PROFIT|profit|profit_rel)\b/,
alias: ['warning', 'important'] alias: ['warning', 'important']
}, },
{ {

View File

@ -12,7 +12,7 @@ from datetime import datetime
#from rich import print #from rich import print
from random import randrange from random import randrange
from alpaca.common.exceptions import APIError from alpaca.common.exceptions import APIError
import copy import numpy as np
from threading import Event from threading import Event
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators
@ -100,7 +100,19 @@ class StrategyClassicSL(Strategy):
trade_profit = round((avg_costs-bought_amount),2) trade_profit = round((avg_costs-bought_amount),2)
self.state.profit += trade_profit self.state.profit += trade_profit
self.state.ilog(e=f"BUY notif - SHORT PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)}", msg=str(data.event), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
rel_profit = 0
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
if vstup_cena != 0 and int(data.order.qty) != 0:
rel_profit = (trade_profit / (vstup_cena * float(data.order.qty))) * 100
#pokud jde o finalni FILL - pridame do pole tento celkovy relativnich profit (ze ktereho se pocita kumulativni relativni profit)
rel_profit_cum_calculated = 0
if data.event == TradeEvent.FILL:
self.state.rel_profit_cum.append(rel_profit)
rel_profit_cum_calculated = np.mean(self.state.rel_profit_cum)
self.state.ilog(e=f"BUY notif - SHORT PROFIT:{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))
#zapsat profit do prescr.trades #zapsat profit do prescr.trades
for trade in self.state.vars.prescribedTrades: for trade in self.state.vars.prescribedTrades:
@ -110,7 +122,11 @@ class StrategyClassicSL(Strategy):
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum) #pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
trade_profit = trade.profit trade_profit = trade.profit
trade.profit_sum = self.state.profit trade.profit_sum = self.state.profit
trade.rel_profit = rel_profit
trade.rel_profit_cum = rel_profit_cum_calculated
signal_name = trade.generated_by signal_name = trade.generated_by
if data.event == TradeEvent.FILL:
trade.status == TradeStatus.CLOSED
break break
if data.event == TradeEvent.FILL: if data.event == TradeEvent.FILL:
@ -124,6 +140,8 @@ class StrategyClassicSL(Strategy):
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)
#self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial))) #self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial)))
setattr(tradeData, "rel_profit", rel_profit)
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
#test na maximalni profit/loss, pokud vypiname pak uz nedelame pripdany reverzal #test na maximalni profit/loss, pokud vypiname pak uz nedelame pripdany reverzal
if await self.stop_when_max_profit_loss() is False: if await self.stop_when_max_profit_loss() is False:
@ -142,7 +160,7 @@ class StrategyClassicSL(Strategy):
if trade.id == self.state.vars.pending: if trade.id == self.state.vars.pending:
signal_name = trade.generated_by signal_name = trade.generated_by
#zapsat update profitu do tradeList #zapsat 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:
setattr(tradeData, "signal_name", signal_name) setattr(tradeData, "signal_name", signal_name)
@ -194,7 +212,19 @@ class StrategyClassicSL(Strategy):
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
self.state.ilog(e=f"SELL notif - PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)}", msg=str(data.event), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
rel_profit = 0
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
if vstup_cena != 0 and data.order.qty != 0:
rel_profit = (trade_profit / (vstup_cena * float(data.order.qty))) * 100
rel_profit_cum_calculated = 0
#pokud jde o finalni FILL - pridame do pole relativnich profit (ze ktereho se pocita kumulativni relativni profit)
if data.event == TradeEvent.FILL:
self.state.rel_profit_cum.append(rel_profit)
rel_profit_cum_calculated = np.mean(self.state.rel_profit_cum)
self.state.ilog(e=f"SELL notif - PROFIT:{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:
@ -204,7 +234,11 @@ class StrategyClassicSL(Strategy):
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum) #pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
trade_profit = trade.profit trade_profit = trade.profit
trade.profit_sum = self.state.profit trade.profit_sum = self.state.profit
trade.rel_profit = rel_profit
trade.rel_profit_cum = rel_profit_cum_calculated
signal_name = trade.generated_by signal_name = trade.generated_by
if data.event == TradeEvent.FILL:
trade.status == TradeStatus.CLOSED
break break
if data.event == TradeEvent.FILL: if data.event == TradeEvent.FILL:
@ -218,6 +252,9 @@ class StrategyClassicSL(Strategy):
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)
#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_cum", rel_profit_cum_calculated)
#sem nejspis update skutecne vstupni ceny (celk.mnozstvi(order.qty) a avg_costs), to same i druhy smer
if await self.stop_when_max_profit_loss() is False: if await self.stop_when_max_profit_loss() is False:

View File

@ -705,7 +705,10 @@ class StrategyState:
self.sell_l = self.interface.sell_l 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 = []
#celkovy profit (prejmennovat na profit_cum)
self.profit = 0 self.profit = 0
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
self.rel_profit_cum = []
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 = {} self.extData = {}

View File

@ -53,9 +53,9 @@ def populate_all_indicators(data, state: StrategyState):
state.ilog(lvl=1,e=f"{conf} {data['index']}-{conf_bar}--delta:{last_update_delta}---AVGdelta:{avg_delta}", data=data) state.ilog(lvl=1,e=f"{conf} {data['index']}-{conf_bar}--delta:{last_update_delta}---AVGdelta:{avg_delta}", data=data)
#TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie #TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie
#TODO na toto se podivam, nejak moc zajasonovani a zpatky #TODO na toto se podivam, nejak moc zajasonovani a zpatky -
#PERF PROBLEM #PERF PROBLEM
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)} pend:{state.vars.pending}", activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending)) state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.mean(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending))
#kroky pro CONFIRMED BAR only #kroky pro CONFIRMED BAR only
if conf_bar == 1: if conf_bar == 1:

View File

@ -97,7 +97,7 @@ def common_go_preconditions_check(state, data, signalname: str, options: dict):
window_close = safe_get(options, "window_close",safe_get(state.vars, "window_close",390)) window_close = safe_get(options, "window_close",safe_get(state.vars, "window_close",390))
if is_window_open(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), window_open, window_close) is False: if is_window_open(datetime.fromtimestamp(data['updated']).astimezone(zoneNY), window_open, window_close) is False:
state.ilog(lvl=1,e=f"SIGNAL {signalname} - WINDOW CLOSED", msg=f"{window_open=} {window_close=} ") state.ilog(lvl=1,e=f"SIGNAL {signalname} - WINDOW CLOSED", msg=f"{window_open=} {window_close=} ", time=str(datetime.fromtimestamp(data['updated']).astimezone(zoneNY)))
return False return False
min_bar_index = safe_get(options, "min_bar_index",safe_get(state.vars, "min_bar_index",0)) min_bar_index = safe_get(options, "min_bar_index",safe_get(state.vars, "min_bar_index",0))

View File

@ -6,6 +6,7 @@ from datetime import datetime
import json import json
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history, get_default_sl_value, normalize_tick from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history, get_default_sl_value, normalize_tick
#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