pozn do runu, prgbar na queu,report

This commit is contained in:
David Brazda
2023-11-21 11:47:33 +01:00
parent dee223ce7e
commit 222c85e465
4 changed files with 211 additions and 127 deletions

View File

@ -157,7 +157,7 @@ def init(state: StrategyState):
today = time_to.date()
several_days_ago = today - timedelta(days=40)
printanyway(f"{today=}",f"{several_days_ago=}")
#printanyway(f"{today=}",f"{several_days_ago=}")
clientTrading = TradingClient(ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, raw_data=False)
#get all market days from here to 40days ago
calendar_request = GetCalendarRequest(start=several_days_ago,end=today)
@ -174,13 +174,13 @@ def init(state: StrategyState):
#history_datetime_to = zoneNY.localize(session.close)
history_datetime_to = session.close
break
printanyway("Previous Market Day Close:", history_datetime_to)
printanyway("Market day 40days ago Open:", history_datetime_from)
#printanyway("Previous Market Day Close:", history_datetime_to)
#printanyway("Market day 40days ago Open:", history_datetime_from)
printanyway(history_datetime_from, history_datetime_to)
#printanyway(history_datetime_from, history_datetime_to)
#az do predchziho market dne dne
state.dailyBars = get_historical_bars(state.symbol, history_datetime_from, history_datetime_to, TimeFrame.Day)
printanyway("daily bars FILLED", state.dailyBars)
#printanyway("daily bars FILLED", state.dailyBars)
#zatim ukladame do extData - pro instant indicatory a gui
state.extData["dailyBars"] = state.dailyBars

View File

@ -476,6 +476,8 @@ def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
note_from_run_request = runReq.note
first = None
last = None
first_frm = runReq.bt_from.strftime("%d.%m.")
last_frm = runReq.bt_to.strftime("%d.%m.")
for day in rundays:
cnt += 1
if cnt == 1:
@ -486,7 +488,7 @@ def batch_run_manager(id: UUID, runReq: RunRequest, rundays: list[RunDay]):
print("Datum do", day.end)
runReq.bt_from = day.start
runReq.bt_to = day.end
runReq.note = f"Batch {batch_id} #{cnt}/{cnt_max} {day.name} N:{day.note} {note_from_run_request}"
runReq.note = f"{first_frm}-{last_frm} Batch {batch_id} #{cnt}/{cnt_max} {day.name} N:{day.note} {note_from_run_request}"
#protoze jsme v ridicim vlaknu, poustime za sebou jednotlive stratiny v synchronnim modu
res, id_val = run_stratin(id=id, runReq=runReq, synchronous=True, inter_batch_params=inter_batch_params)

View File

@ -16,6 +16,8 @@ from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
from io import BytesIO
from v2realbot.utils.historicals import get_historical_bars
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
# Assuming Trade, TradeStatus, TradeDirection, TradeStoplossType classes are defined elsewhere
def generate_trading_report_image(runner_ids: list = None, batch_id: str = None, stream: bool = False):
@ -35,7 +37,13 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
return -1, f"no batch {batch_id} found"
trades = []
cnt_max = len(runner_ids)
cnt = 0
#zatim zjistujeme start a end z min a max dni - jelikoz muze byt i seznam runner_ids a nejenom batch
end_date = None
start_date = None
for id in runner_ids:
cnt += 1
#get runner
res, sada =cs.get_archived_runner_header_byID(id)
if res != 0:
@ -45,8 +53,11 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
print("archrunner")
print(sada)
if cnt == 1:
start_date = sada.bt_from if sada.mode in [Mode.BT,Mode.PREP] else sada.started
if cnt == cnt_max:
end_date = sada.bt_to if sada.mode in [Mode.BT or Mode.PREP] else sada.stopped
# Parse trades
#trades = [Trade(**trade_dict) for trade_dict in set.metrics["prescr_trades"]]
trades_dicts = sada.metrics["prescr_trades"]
@ -58,6 +69,27 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
print(trades)
symbol = sada.symbol
#hour bars for backtested period
print(start_date,end_date)
bars= get_historical_bars(symbol, start_date, end_date, TimeFrame.Hour)
print("bars for given period",bars)
"""Bars a dictionary with the following keys:
* high: A list of high prices
* low: A list of low prices
* volume: A list of volumes
* close: A list of close prices
* hlcc4: A list of HLCC4 indicators
* open: A list of open prices
* time: A list of times in UTC (ISO 8601 format)
* trades: A list of number of trades
* resolution: A list of resolutions (all set to 'D')
* confirmed: A list of booleans (all set to True)
* vwap: A list of VWAP indicator
* updated: A list of booleans (all set to True)
* index: A list of integers (from 0 to the length of the list of daily bars)
"""
# Filter to only use trades with status 'CLOSED'
closed_trades = [trade for trade in trades if trade.status == TradeStatus.CLOSED]
@ -83,7 +115,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
long_profits = [trade.profit for trade in closed_trades if trade.direction == TradeDirection.LONG and trade.profit is not None]
short_profits = [trade.profit for trade in closed_trades if trade.direction == TradeDirection.SHORT and trade.profit is not None]
# # Setting up dark mode for the plots
# Setting up dark mode for the plots
plt.style.use('dark_background')
# Optionally, you can further customize colors, labels, and axes
@ -104,45 +136,44 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
}
plt.rcParams.update(params)
#NEW LOOK
# # Set the style to dark mode with custom settings
#Custom dark theme similar to the provided image
# dark_finance_theme = {
# 'background': '#1a1a1a', # Very dark (almost black) background
# 'text': '#eaeaea', # Light grey text for readability
# 'grid': '#333333', # Dark grey grid lines
# 'accent': '#2e91e5', # Bright blue accent for main elements
# 'secondary': '#e15f99', # Secondary pink/magenta color for highlights
# 'highlight': '#fcba03', # Gold-like color for special highlights
# }
# # Apply the theme settings
# plt.style.use('dark_background')
# plt.rcParams.update({
# 'figure.facecolor': dark_finance_theme['background'],
# 'axes.facecolor': dark_finance_theme['background'],
# 'axes.edgecolor': dark_finance_theme['text'],
# 'axes.labelcolor': dark_finance_theme['text'],
# 'axes.titlesize': 12,
# 'axes.labelsize': 10,
# 'xtick.color': dark_finance_theme['text'],
# 'xtick.labelsize': 8,
# 'ytick.color': dark_finance_theme['text'],
# 'ytick.labelsize': 8,
# 'grid.color': dark_finance_theme['grid'],
# 'grid.linestyle': '-',
# 'grid.linewidth': 0.6,
# 'legend.facecolor': dark_finance_theme['background'],
# 'legend.edgecolor': dark_finance_theme['background'],
# 'legend.fontsize': 10,
# 'text.color': dark_finance_theme['text'],
# 'lines.color': dark_finance_theme['accent'],
# 'patch.edgecolor': dark_finance_theme['accent'],
# })
# # Define a custom dark theme color palette
# dark_theme_colors = {
# 'background': '#1c1c1c', # Dark gray
# 'text': '#d6d6d6', # Light gray for a subtle contrast
# 'grid': '#414141', # Slightly lighter gray than background for grid
# 'highlight': '#3498db', # Bright blue for highlights (max/min points, etc.)
# 'warning': '#e74c3c', # Red color for warnings or important highlights
# 'neutral': '#7f8c8d', # Neutral color for less important elements
# }
# # Customize the color scheme
# params = {
# 'figure.facecolor': dark_theme_colors['background'],
# 'axes.titlesize': 10,
# 'axes.labelsize': 9,
# 'xtick.labelsize': 9,
# 'ytick.labelsize': 9,
# 'axes.labelcolor': dark_theme_colors['text'],
# 'axes.facecolor': dark_theme_colors['background'],
# 'axes.grid': False, # Control grid visibility
# 'axes.edgecolor': dark_theme_colors['text'],
# 'xtick.color': dark_theme_colors['text'],
# 'ytick.color': dark_theme_colors['text'],
# 'text.color': dark_theme_colors['text'],
# 'legend.facecolor': dark_theme_colors['background'],
# 'legend.edgecolor': dark_theme_colors['text'],
# 'legend.fontsize': 8,
# 'legend.title_fontsize': 9,
# }
# # Apply the custom color scheme
# plt.rcParams.update(params)
# Create a combined figure for all plots
fig, axs = plt.subplots(3, 4, figsize=(11, 7))
# Create a combined figure for all plots 11,7 ideal na 3,4
fig, axs = plt.subplots(3, 4, figsize=(12, 7))
#TITLE
title = ""
@ -229,43 +260,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
place_annotation(axs[0, 3], 0, long_count, offset)
place_annotation(axs[0, 3], 1, short_count, offset)
#Cumulative profit - bud 1 den nebo vice dni
if len(runner_ids)== 1:
if cumulative_profits.size > 0:
# Plot 3: Cumulative Profit Over Time with Max Profit Point
max_profit_time = exit_times[np.argmax(cumulative_profits)]
max_profit = max(cumulative_profits)
min_profit_time = exit_times[np.argmin(cumulative_profits)]
min_profit = min(cumulative_profits)
sns.lineplot(x=exit_times, y=cumulative_profits, label='Cumulative Profit', ax=axs[1, 3])
axs[1, 3].scatter(max_profit_time, max_profit, color='green', label='Max Profit')
axs[1, 3].scatter(min_profit_time, min_profit, color='red', label='Min Profit')
# Format dates on the x-axis
axs[1, 3].xaxis.set_major_formatter(mdates.DateFormatter('%H', tz=zoneNY))
axs[1, 3].set_title('Cumulative Profit Over Time')
axs[1, 3].legend()
else:
# Handle the case where cumulative_profits is empty
axs[1, 3].text(0.5, 0.5, 'No profit data available',
horizontalalignment='center',
verticalalignment='center',
transform=axs[1, 3].transAxes)
axs[1, 3].set_title('Cumulative Profit Over Time')
else:
# Calculate cumulative profit
# Additional Plot: Cumulative Profit Over Time
# Sort trades by exit time
sorted_trades = sorted([trade for trade in trades if trade.status == TradeStatus.CLOSED],
key=lambda x: x.exit_time)
cumulative_profits_sorted = np.cumsum([trade.profit for trade in sorted_trades])
exit_times_sorted = [trade.exit_time for trade in sorted_trades if trade.exit_time is not None]
axs[1, 3].plot(exit_times_sorted, cumulative_profits_sorted, color='blue')
axs[1, 3].set_title('Cumulative Profit Over Time')
axs[1, 3].set_xlabel('Time')
axs[1, 3].set_ylabel('Cumulative Profit')
axs[1, 3].xaxis.set_major_formatter(mdates.DateFormatter('%d', tz=zoneNY))
#PLOT 5 - Heatman (exit time)
# Creating a DataFrame for the heatmap
heatmap_data_list = []
for trade in trades:
@ -281,7 +276,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
heatmap_data = heatmap_data.groupby(['Day', 'Hour']).sum().reset_index()
heatmap_pivot = heatmap_data.pivot(index='Day', columns='Hour', values='Profit')
# Plot 3: Heatmap of Profits
# Heatmap of Profits
sns.heatmap(heatmap_pivot, cmap='viridis', ax=axs[1, 0])
axs[1, 0].set_title('Heatmap of Profits (based on Exit time)')
axs[1, 0].set_xlabel('Hour of Day')
@ -294,15 +289,15 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
transform=axs[1, 0].transAxes)
axs[1, 0].set_title('Heatmap of Profits (based on Exit time)')
# Plot 9: Profit/Loss Distribution Histogram
# Plot 6: Profit/Loss Distribution Histogram
sns.histplot(profits, bins=30, ax=axs[1, 1], kde=True, color='skyblue')
axs[1, 1].set_title('Profit/Loss Distribution')
axs[1, 1].set_xlabel('Profit/Loss')
axs[1, 1].set_ylabel('Frequency')
# Plot 5
# - pro 1 den: Position Size Distribution
# - pro vice dnu: Trade Duration vs. Profit/Loss
# Plot 7
# - for 1 den: Position Size Distribution
# - for more days: Trade Duration vs. Profit/Loss
if len(runner_ids) == 1:
sizes = [trade.size for trade in closed_trades if trade.size is not None]
@ -331,7 +326,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
##trade_volumes.append(trade.size) # or any other measure of trade size
trade_types.append('Long' if trade.direction == TradeDirection.LONG else 'Short')
# Plot 8: Trade Duration vs. Profit/Loss
# Trade Duration vs. Profit/Loss
scatter_data = pd.DataFrame({
'Duration': trade_durations,
'Profit': trade_profits,
@ -345,7 +340,91 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
axs[1, 2].set_ylabel('Profit/Loss')
# Plot 6: Daily Relative Profit Chart
#Plot 8 Cumulative profit - bud 1 den nebo vice dni + pridame pod to vyvoj ceny
# Extract the closing prices and times
closing_prices = bars['close']
#times = bars['time'] # Assuming this is a list of pandas Timestamp objects
times = pd.to_datetime(bars['time']) # Ensure this is a Pandas datetime series
# # Plot the closing prices over time
# axs[0, 4].plot(times, closing_prices, color='blue')
# axs[0, 4].tick_params(axis='x', rotation=45) # Rotate date labels if necessar
# axs[0, 4].xaxis.set_major_formatter(mdates.DateFormatter('%H', tz=zoneNY))
if len(runner_ids)== 1:
if cumulative_profits.size > 0:
# Plot 3: Cumulative Profit Over Time with Max Profit Point
max_profit_time = exit_times[np.argmax(cumulative_profits)]
max_profit = max(cumulative_profits)
min_profit_time = exit_times[np.argmin(cumulative_profits)]
min_profit = min(cumulative_profits)
#Plot Cumulative Profit Over Time with Max Profit Point on the primary y-axis
# Create a secondary y-axis for the closing prices
ax2 = axs[1, 3].twinx()
ax2.plot(times, closing_prices, label='Closing Price', color='orange')
ax2.set_ylabel('Closing Price', color='orange')
ax2.tick_params(axis='y', labelcolor='orange')
# Set the limits for the x-axis to cover the full range of 'times'
axs[1, 3].set_xlim(times.min(), times.max())
sns.lineplot(x=exit_times, y=cumulative_profits, ax=axs[1, 3], color='limegreen')
axs[1, 3].scatter(max_profit_time, max_profit, color='green', label='Max Profit')
axs[1, 3].scatter(min_profit_time, min_profit, color='red', label='Min Profit')
axs[1, 3].set_xlabel('Time')
axs[1, 3].set_ylabel('Cumulative Profit', color='limegreen')
axs[1, 3].tick_params(axis='y', labelcolor='limegreen')
axs[1, 3].xaxis.set_major_formatter(mdates.DateFormatter('%H', tz=zoneNY))
# Add legends to the plot
# lines, labels = axs[1, 3].get_legend_handles_labels()
# lines2, labels2 = ax2.get_legend_handles_labels()
# axs[1, 3].legend(lines + lines2, labels + labels2, loc='upper left')
else:
# Handle the case where cumulative_profits is empty
axs[1, 3].text(0.5, 0.5, 'No profit data available',
horizontalalignment='center',
verticalalignment='center',
transform=axs[1, 3].transAxes)
axs[1, 3].set_title('Cumulative Profit Over Time')
else:
# Calculate cumulative profit
# Additional Plot: Cumulative Profit Over Time
# Sort trades by exit time
# # Set the limits for the x-axis to cover the full range of 'times'
# axs[1, 3].set_xlim(times.min(), times.max())
sorted_trades = sorted([trade for trade in trades if trade.status == TradeStatus.CLOSED],
key=lambda x: x.exit_time)
cumulative_profits_sorted = np.cumsum([trade.profit for trade in sorted_trades])
exit_times_sorted = [trade.exit_time for trade in sorted_trades if trade.exit_time is not None]
# Create a secondary y-axis for the closing prices
ax2 = axs[1, 3].twinx()
ax2.plot(times, closing_prices, label='Closing Price', color='orange')
ax2.set_ylabel('Closing Price', color='orange')
ax2.tick_params(axis='y', labelcolor='orange')
axs[1, 3].set_xlim(times.min(), times.max())
# Plot Cumulative Profit Over Time on the primary y-axis
axs[1, 3].plot(exit_times_sorted, cumulative_profits_sorted, label='Cumulative Profit', color='blue')
axs[1, 3].set_xlabel('Time')
axs[1, 3].set_ylabel('Cumulative Profit', color='blue')
axs[1, 3].tick_params(axis='y', labelcolor='blue')
# Format dates on the x-axis
axs[1, 3].xaxis.set_major_formatter(mdates.DateFormatter('%d.%m.', tz=zoneNY))
axs[1, 3].tick_params(axis='x', rotation=45) # Rotate date labels if necessary
# Set the title
axs[1, 3].set_title('Cumulative Profit and Closing Price Over Time')
# Add legends to the plot
# axs[1, 3].legend(loc='upper left')
# ax2.legend(loc='upper right')
# Plot 9
# - for 1 day: Daily Relative Profit Chart
# - for more days: Heatmap of Profits (based on Entry time)
if len(runner_ids) == 1:
daily_rel_profits = [trade.rel_profit for trade in closed_trades if trade.rel_profit is not None]
sns.lineplot(x=range(len(daily_rel_profits)), y=daily_rel_profits, ax=axs[2, 0])
@ -365,13 +444,13 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
heatmap_data = heatmap_data.groupby(['Day', 'Hour']).sum().reset_index()
heatmap_pivot = heatmap_data.pivot(index='Day', columns='Hour', values='Profit')
# Plot 3: Heatmap of Profits
# Heatmap of Profits
sns.heatmap(heatmap_pivot, cmap='viridis', ax=axs[2, 0])
axs[2, 0].set_title('Heatmap of Profits (based on Entry time)')
axs[2, 0].set_xlabel('Hour of Day')
axs[2, 0].set_ylabel('Day')
# Plot 8: Profits Based on Hour of the Day (Entry)
# Plot 10: Profits Based on Hour of the Day (Entry)
entry_hours = [trade.entry_time.hour for trade in closed_trades if trade.entry_time is not None]
profits_by_hour = {}
for hour, trade in zip(entry_hours, closed_trades):
@ -396,7 +475,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
transform=axs[2, 1].transAxes)
axs[2, 1].set_title('Profits by Hour of Day (Entry)')
# Plot 9: Profits Based on Hour of the Day - based on Exit
# Plot 11: Profits Based on Hour of the Day - based on Exit
exit_hours = [trade.exit_time.hour for trade in closed_trades if trade.exit_time is not None]
profits_by_hour = {}
for hour, trade in zip(exit_hours, closed_trades):
@ -421,7 +500,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
transform=axs[2, 2].transAxes)
axs[2, 2].set_title('Profits by Hour of Day (Exit)')
# Calculate profits by day of the week
# Plot 12: Calculate profits by day of the week
day_of_week_profits = {i: 0 for i in range(7)} # Dictionary to store profits for each day of the week
for trade in trades:
@ -459,7 +538,7 @@ def generate_trading_report_image(runner_ids: list = None, batch_id: str = None,
# Example usage
# trades = [list of Trade objects]
if __name__ == '__main__':
id_list = ["c3e31cb5-ddf9-467e-a932-2118f6844355"]
generate_trading_report_image(runner_ids=id_list)
# batch_id = "90973e57"
# generate_trading_report_image(batch_id=batch_id)
# id_list = ["e8938b2e-8462-441a-8a82-d823c6a025cb"]
# generate_trading_report_image(runner_ids=id_list)
batch_id = "90973e57"
generate_trading_report_image(batch_id=batch_id)

View File

@ -28,6 +28,7 @@ from uuid import UUID
from rich import print as printnow
from collections import defaultdict
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
from tqdm import tqdm
if PROFILING_NEXT_ENABLED:
from pyinstrument import Profiler
@ -418,40 +419,42 @@ class Strategy:
#main strat loop
print(self.name, "Waiting for DATA")
while True:
try:
#block 5s, after that check signals
item = self.q1.get(timeout=HEARTBEAT_TIMEOUT)
#printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize())
except queue.Empty:
#check signals
if self.se.is_set():
print(current_thread().name, "Stopping signal")
with tqdm(total=self.q1.qsize()) as pbar:
while True:
try:
#block 5s, after that check signals
item = self.q1.get(timeout=HEARTBEAT_TIMEOUT)
#printnow(current_thread().name, "Items waiting in queue:", self.q1.qsize())
except queue.Empty:
#check signals
if self.se.is_set():
print(current_thread().name, "Stopping signal")
break
if self.pe.is_set():
print(current_thread().name, "Paused.")
continue
else:
print(current_thread().name, "HEARTBEAT - no trades or signals")
continue
#prijde posledni zaznam nebo stop event signal
if item == "last" or self.se.is_set():
print(current_thread().name, "stopping")
break
if self.pe.is_set():
elif self.pe.is_set():
print(current_thread().name, "Paused.")
continue
else:
print(current_thread().name, "HEARTBEAT - no trades or signals")
continue
#prijde posledni zaznam nebo stop event signal
if item == "last" or self.se.is_set():
print(current_thread().name, "stopping")
break
elif self.pe.is_set():
print(current_thread().name, "Paused.")
continue
#self.state.iter_log(event="INGEST",msg="New data ingested", item=item)
print("New data ingested", item)
print("bars list - previous", self.state.bars)
#TODO sem pridat ochranu kulometu
#pokud je updatetime aktualniho baru mensi nez LIMIT a nejde o potvrzovaci bar
#tak jej vyhodit
#zabraní se tím akcím na než bych stejně nešlo reagovat
#TODO jeste promyslet
#calling main loop
self.strat_loop(item=item)
#self.state.iter_log(event="INGEST",msg="New data ingested", item=item)
print("New data ingested", item)
print("bars list - previous", self.state.bars)
#TODO sem pridat ochranu kulometu
#pokud je updatetime aktualniho baru mensi nez LIMIT a nejde o potvrzovaci bar
#tak jej vyhodit
#zabraní se tím akcím na než bych stejně nešlo reagovat
#TODO jeste promyslet
#calling main loop
self.strat_loop(item=item)
pbar.update(1)
tlog(f"FINISHED")
print(40*"*",self.mode, "STRATEGY ", self.name,"STOPPING",40*"*")