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):
READY = "ready"
ACTIVATED = "activated"
CLOSED = "closed"
#FINISHED = "finished"
class TradeDirection(str, Enum):
@ -28,4 +29,6 @@ class Trade(BaseModel):
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

View File

@ -79,7 +79,7 @@ def row_to_runarchiveview(row: dict) -> RunArchiveView:
trade_count=int(row['trade_count']),
end_positions=int(row['end_positions']),
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

View File

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

View File

@ -7,7 +7,7 @@ from alpaca.data.enums import DataFeed
from alpaca.data.timeframe import TimeFrame
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.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.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
@ -21,6 +21,7 @@ from queue import Queue
from tinydb import TinyDB, Query, where
from tinydb.operations import set
import json
import numpy as np
from numpy import ndarray
from rich import print
import pandas as pd
@ -374,9 +375,10 @@ def run_batch_stratin(id: UUID, runReq: RunRequest):
cal_list = []
#interval dame do formatu list(RunDays)
#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:
start_time = zoneNY.localize(datetime.fromisoformat(intrvl.start))
end_time = zoneNY.localize(datetime.fromisoformat(intrvl.end))
start_time = zonePRG.localize(datetime.fromisoformat(intrvl.start))
end_time = zonePRG.localize(datetime.fromisoformat(intrvl.end))
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}")
@ -443,7 +445,7 @@ def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
cnt_max = len(rundays)
cnt = 0
#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
for day in rundays:
cnt += 1
@ -636,6 +638,12 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
#naplneni batch sum profitu
if inter_batch_params is not None:
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
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 ""
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
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
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
if inter_batch_params is not None:
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
#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
//console.log(data)
short = null
if ((data.profit) && (data.profit.sum)) {
if ((data) && (data.profit) && (data.profit.sum)) {
short = data.profit.sum
}
else {

View File

@ -28,7 +28,7 @@ Prism.languages.log = {
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']
},
{

View File

@ -12,7 +12,7 @@ from datetime import datetime
#from rich import print
from random import randrange
from alpaca.common.exceptions import APIError
import copy
import numpy as np
from threading import Event
from uuid import UUID, uuid4
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)
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
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)
trade_profit = trade.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
if data.event == TradeEvent.FILL:
trade.status == TradeStatus.CLOSED
break
if data.event == TradeEvent.FILL:
@ -124,6 +140,8 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#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
if await self.stop_when_max_profit_loss() is False:
@ -142,7 +160,7 @@ class StrategyClassicSL(Strategy):
if trade.id == self.state.vars.pending:
signal_name = trade.generated_by
#zapsat update profitu do tradeList
#zapsat do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
setattr(tradeData, "signal_name", signal_name)
@ -194,7 +212,19 @@ class StrategyClassicSL(Strategy):
trade_profit = round((sold_amount - avg_costs),2)
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
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)
trade_profit = trade.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
if data.event == TradeEvent.FILL:
trade.status == TradeStatus.CLOSED
break
if data.event == TradeEvent.FILL:
@ -218,6 +252,9 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#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:

View File

@ -705,7 +705,10 @@ class StrategyState:
self.sell_l = self.interface.sell_l
self.cancel_pending_buys = None
self.iter_log_list = []
#celkovy profit (prejmennovat na profit_cum)
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 = []
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
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)
#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
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
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))
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
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
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):
##evaluate prescribed trade, prvni eligible presuneme do activeTrade, zmenime stav and vytvorime objednavky