From 79afdc62ffd38ee47cc354a217b21bc84f4f5f79 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 27 Mar 2024 14:59:50 -0300 Subject: [PATCH 1/5] (feat) add position builder page --- main.py | 2 + pages/config_generator/app.py | 105 +++++++++++++++++++++++++++++++ pages/position_builder/README.md | 0 3 files changed, 107 insertions(+) create mode 100644 pages/config_generator/app.py create mode 100644 pages/position_builder/README.md diff --git a/main.py b/main.py index 42aa7cf0..eed1db5c 100644 --- a/main.py +++ b/main.py @@ -14,6 +14,8 @@ def main_page(): Page("pages/master_conf/app.py", "Credentials", "🗝️"), Page("pages/bot_orchestration/app.py", "Instances", "🦅"), Page("pages/file_manager/app.py", "File Explorer", "🗂"), + Page("pages/config_generator/app.py", "Config Generator", "🎛️"), + Page("pages/position_builder/app.py", "Position Builder", "🔭"), Section("Backtest Manager", "⚙️"), Page("pages/backtest_get_data/app.py", "Get Data", "💾"), Page("pages/backtest_create/create.py", "Create", "⚔️"), diff --git a/pages/config_generator/app.py b/pages/config_generator/app.py new file mode 100644 index 00000000..35fd73f6 --- /dev/null +++ b/pages/config_generator/app.py @@ -0,0 +1,105 @@ +import os +import pandas as pd +import streamlit as st +from typing import List +from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig + +import constants +from utils.st_utils import initialize_st_page + +initialize_st_page(title="Config Generator", icon="🎛️", initial_sidebar_state="collapsed") + + +def get_available_candles() -> List[CandlesConfig]: + """Retrieve the available candles from the candles folder.""" + try: + candles_config = [] + # Listing all CSV files in the specified directory + csv_files = [f for f in os.listdir(constants.CANDLES_DATA_PATH) if f.endswith('.csv')] + for file in csv_files: + file_splitted = file.replace('.csv', '').split("_") + interval = file_splitted.pop(-1) + trading_pair = file_splitted.pop(-1) + if len(file_splitted) == 2: + connector = file_splitted.pop(-1) + elif len(file_splitted) == 3: + connector = file_splitted.pop(-2) + "_" + file_splitted.pop(-1) + else: + raise ValueError(f"Invalid file name: {file}") + candles_config.append(CandlesConfig(connector=connector, trading_pair=trading_pair, interval=interval)) + return candles_config + except Exception as e: + st.warning("An error occurred:", e) + +def get_candles_df(candles_config: CandlesConfig): + """Retrieve the candles DataFrame from the specified candles config.""" + try: + candles_df = pd.read_csv(constants.CANDLES_DATA_PATH + f"/candles_{candles_config.connector}_{candles_config.trading_pair}_{candles_config.interval}.csv") + return candles_df + except Exception as e: + st.warning("An error occurred:", e) + +# Start content here +st.text("This tool will let you analyze and generate a config for market making controllers.") +st.write("---") + +st.write("## 📊 Market Data") +st.write("### Candlestick Available") +available_candles = get_available_candles() + +if available_candles: + df = pd.DataFrame([candles.dict() for candles in available_candles]) + df = df[['connector', 'trading_pair', 'interval']].copy() + + # Initialize selection options + connectors = ['All'] + sorted(set(df['connector'])) + trading_pairs = ['All'] + sorted(set(df['trading_pair'])) + intervals = ['All'] + sorted(set(df['interval'])) + + # Select boxes + c1, c2, c3 = st.columns(3) + with c1: + selected_connector = st.selectbox("Select Connector", connectors, index=0) + + # Filter trading pairs and intervals based on selected connector + if selected_connector != 'All': + filtered_pairs = sorted(set(df[df['connector'] == selected_connector]['trading_pair'])) + filtered_intervals = sorted(set(df[df['connector'] == selected_connector]['interval'])) + else: + filtered_pairs = trading_pairs + filtered_intervals = intervals + + with c2: + selected_trading_pair = st.selectbox("Select Trading Pair", ['All'] + filtered_pairs, index=0, disabled=(selected_connector == 'All')) + with c3: + selected_interval = st.selectbox("Select Interval", ['All'] + filtered_intervals, index=0, disabled=(selected_connector == 'All')) + + # Further filter DataFrame based on selections + if selected_connector != 'All': + df = df[df['connector'] == selected_connector] + if selected_trading_pair != 'All': + df = df[df['trading_pair'] == selected_trading_pair] + if selected_interval != 'All': + df = df[df['interval'] == selected_interval] + + c1, c2 = st.columns(2) + with c1: + st.write("### Candlestick Data") + st.data_editor(df) + # Check the length of the filtered DataFrame + with c2: + if len(df) == 1: + # Show 'Render Candles' button + if st.button('Render Candles'): + st.write("Rendering Candles...") + candles_config_data = df.iloc[0].to_dict() + candles_config = CandlesConfig(connector=candles_config_data['connector'], + trading_pair=candles_config_data['trading_pair'], + interval=candles_config_data['interval']) + st.write(candles_config) + candles_df = get_candles_df(candles_config) + st.data_editor(candles_df) + elif df.empty: + # Show 'Download Candles' button + if st.button('Download Candles'): + st.write("Downloading Candles...") diff --git a/pages/position_builder/README.md b/pages/position_builder/README.md new file mode 100644 index 00000000..e69de29b From 64926e1bf8ad54e602acfbe117453b13f0bcfbc3 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 27 Mar 2024 14:59:59 -0300 Subject: [PATCH 2/5] (feat) add config generator page --- pages/config_generator/README.md | 0 pages/position_builder/app.py | 244 +++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 pages/config_generator/README.md create mode 100644 pages/position_builder/app.py diff --git a/pages/config_generator/README.md b/pages/config_generator/README.md new file mode 100644 index 00000000..e69de29b diff --git a/pages/position_builder/app.py b/pages/position_builder/app.py new file mode 100644 index 00000000..5446b7da --- /dev/null +++ b/pages/position_builder/app.py @@ -0,0 +1,244 @@ +from math import exp +import streamlit as st +from plotly.subplots import make_subplots +import plotly.graph_objects as go +from decimal import Decimal + +from utils.st_utils import initialize_st_page +from hummingbot.smart_components.utils.distributions import Distributions + +# Initialize the Streamlit page +initialize_st_page(title="Position Generator", icon="🔭", initial_sidebar_state="collapsed") + +# Page content +st.text("This tool will help you analyze and generate a position config.") +st.write("---") + +# Layout in columns +col_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2]) + + +def normalize(values): + total = sum(values) + return [val / total for val in values] + + +with col_quote: + total_amount_quote = st.number_input("Total amount of quote", value=1000) + +with col_tp_sl: + tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=2.0, step=0.1) + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=8.0, step=0.1) + +with col_levels: + n_levels = st.number_input("Number of Levels", min_value=1, value=5) + + +def distribution_inputs(column, dist_type_name): + if dist_type_name == "Spread": + dist_type = column.selectbox( + f"Type of {dist_type_name} Distribution", + ("GeoCustom", "Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"), + key=f"{dist_type_name.lower()}_dist_type", + # Set the default value + ) + else: + dist_type = column.selectbox( + f"Type of {dist_type_name} Distribution", + ("Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"), + key=f"{dist_type_name.lower()}_dist_type", + # Set the default value + ) + base, scaling_factor, step, ratio, manual_values = None, None, None, None, None + + if dist_type != "Manual": + start = column.number_input(f"{dist_type_name} Start Value", value=1.0, key=f"{dist_type_name.lower()}_start") + if dist_type == "Logarithmic": + base = column.number_input(f"{dist_type_name} Log Base", value=exp(1), key=f"{dist_type_name.lower()}_base") + scaling_factor = column.number_input(f"{dist_type_name} Scaling Factor", value=2.0, key=f"{dist_type_name.lower()}_scaling") + elif dist_type == "Arithmetic": + step = column.number_input(f"{dist_type_name} Step", value=0.1, key=f"{dist_type_name.lower()}_step") + elif dist_type == "Geometric": + ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio") + elif dist_type == "GeoCustom": + ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio") + else: + manual_values = [column.number_input(f"{dist_type_name} for level {i+1}", value=1.0, key=f"{dist_type_name.lower()}_{i}") for i in range(n_levels)] + start = None # As start is not relevant for Manual type + + return dist_type, start, base, scaling_factor, step, ratio, manual_values + + +# Spread and Amount Distributions +spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs(col_spread_dist, "Spread") +amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs(col_amount_dist, "Amount") + + +def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, step=None, ratio=None, manual_values=None): + if dist_type == "Manual": + return manual_values + elif dist_type == "Linear": + return Distributions.linear(n_levels, start, start + tp) + elif dist_type == "Fibonacci": + return Distributions.fibonacci(n_levels, start) + elif dist_type == "Logarithmic": + return Distributions.logarithmic(n_levels, base, scaling_factor, start) + elif dist_type == "Arithmetic": + return Distributions.arithmetic(n_levels, start, step) + elif dist_type == "Geometric": + return Distributions.geometric(n_levels, start, ratio) + elif dist_type == "GeoCustom": + return [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio) + +spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads) +amount_distribution = normalize(get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts)) +order_amounts = [Decimal(amount_dist * total_amount_quote) for amount_dist in amount_distribution] +spreads = [spread - spread_distribution[0] for spread in spread_distribution] +break_even_values = [] +take_profit_values = [] +for level in range(n_levels): + spreads_normalized = [Decimal(spread) + Decimal(0.01) for spread in spreads[:level+1]] + amounts = order_amounts[:level+1] + break_even = (sum([spread * amount for spread, amount in zip(spreads_normalized, amounts)]) / sum(amounts)) - Decimal(0.01) + break_even_values.append(break_even) + take_profit_values.append(break_even - Decimal(tp)) + +accumulated_amount = [sum(order_amounts[:i+1]) for i in range(len(order_amounts))] + +def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): + unrealized_pnl = [] + for i in range(len(spreads)): + distance = abs(spreads[i] - break_even_values[i]) + pnl = accumulated_amount[i] * distance / 100 # PNL calculation + unrealized_pnl.append(pnl) + return unrealized_pnl + +# Calculate unrealized PNL +cum_unrealized_pnl = calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount) + + +tech_colors = { + 'spread': '#00BFFF', # Deep Sky Blue + 'break_even': '#FFD700', # Gold + 'take_profit': '#32CD32', # Green + 'order_amount': '#1E90FF', # Dodger Blue + 'cum_amount': '#4682B4', # Steel Blue + 'stop_loss': '#FF0000', # Red +} + +# Create Plotly figure with secondary y-axis and a dark theme +fig = make_subplots(specs=[[{"secondary_y": True}]]) +fig.update_layout(template="plotly_dark") + +# Update the Scatter Plots and Horizontal Lines +fig.add_trace(go.Scatter(x=list(range(len(spreads))), y=spreads, name='Spread (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) +fig.add_trace(go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) +fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), secondary_y=False) + +# Add the new Bar Plot for Cumulative Unrealized PNL +fig.add_trace(go.Bar( + x=list(range(len(cum_unrealized_pnl))), + y=cum_unrealized_pnl, + text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], + textposition='auto', + textfont=dict(color='white', size=12), + name='Cum Unrealized PNL', + marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed +), secondary_y=True) + +fig.add_trace(go.Bar( + x=list(range(len(order_amounts))), + y=order_amounts, + text=[f"{amt:.2f}" for amt in order_amounts], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Order Amount', + marker=dict(color=tech_colors['order_amount'], opacity=0.5), +), secondary_y=True) + +# Modify the Bar Plot for Accumulated Amount +fig.add_trace(go.Bar( + x=list(range(len(accumulated_amount))), + y=accumulated_amount, + text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Cum Amount', + marker=dict(color=tech_colors['cum_amount'], opacity=0.5), +), secondary_y=True) + + +# Add Horizontal Lines for Last Breakeven Price and Stop Loss Level +last_break_even = break_even_values[-1] +stop_loss_value = last_break_even + Decimal(sl) +# Horizontal Lines for Last Breakeven and Stop Loss +fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", annotation_position="top left", line_color=tech_colors['break_even']) +fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", annotation_position="bottom right", line_color=tech_colors['stop_loss']) + +# Update Annotations for Spread and Break Even +for i, (spread, be_value, tp_value) in enumerate(zip(spreads, break_even_values, take_profit_values)): + fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['spread'])) + fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, font=dict(color=tech_colors['break_even'])) + fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['take_profit'])) +# Update Layout with a Dark Theme +fig.update_layout( + title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", + xaxis_title="Order Level", + yaxis_title="Spread (%)", + yaxis2_title="Amount (Quote)", + height=800, + width=1800, + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + font=dict(color='white') # Font color +) + +# Calculate metrics +max_loss = total_amount_quote * Decimal(sl / 100) +profit_per_level = [cum_amount * Decimal(tp / 100) for cum_amount in accumulated_amount] +loots_to_recover = [max_loss / profit for profit in profit_per_level] + +# Define a consistent annotation size and maximum value for the secondary y-axis +circle_text = "●" # Unicode character for a circle +max_secondary_value = max(max(accumulated_amount), max(order_amounts), max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data + +# Determine an appropriate y-offset for annotations +y_offset_secondary = max_secondary_value * Decimal(0.1) # Adjusts the height relative to the maximum value on the secondary y-axis + +# Add annotations to the Plotly figure for the secondary y-axis +for i, loot in enumerate(loots_to_recover): + fig.add_annotation( + x=i, + y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset + text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines + showarrow=False, + font=dict(size=16, color='purple'), + xanchor="center", # Centers the text above the x coordinate + yanchor="bottom", # Anchors the text at its bottom to avoid overlapping + align="center", + yref="y2" # Reference the secondary y-axis + ) +# Add Max Loss Metric as an Annotation +max_loss_annotation_text = f"Max Loss (Quote): {max_loss:.2f}" +fig.add_annotation( + x=max(len(spreads), len(break_even_values)) - 1, # Positioning the annotation to the right + text=max_loss_annotation_text, + showarrow=False, + font=dict(size=20, color='white'), + bgcolor='red', # Red background for emphasis + xanchor="left", + yanchor="top", + yref="y2" # Reference the secondary y-axis +) + +st.write("---") + +# Display in Streamlit +st.plotly_chart(fig) + From 214a763af48004658bae4b56ffa45a7e3d7a4750 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 1 Apr 2024 18:19:37 -0300 Subject: [PATCH 3/5] (feat) remove config generator --- pages/config_generator/README.md | 0 pages/config_generator/app.py | 105 ------------------------------- 2 files changed, 105 deletions(-) delete mode 100644 pages/config_generator/README.md delete mode 100644 pages/config_generator/app.py diff --git a/pages/config_generator/README.md b/pages/config_generator/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/pages/config_generator/app.py b/pages/config_generator/app.py deleted file mode 100644 index 35fd73f6..00000000 --- a/pages/config_generator/app.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import pandas as pd -import streamlit as st -from typing import List -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig - -import constants -from utils.st_utils import initialize_st_page - -initialize_st_page(title="Config Generator", icon="🎛️", initial_sidebar_state="collapsed") - - -def get_available_candles() -> List[CandlesConfig]: - """Retrieve the available candles from the candles folder.""" - try: - candles_config = [] - # Listing all CSV files in the specified directory - csv_files = [f for f in os.listdir(constants.CANDLES_DATA_PATH) if f.endswith('.csv')] - for file in csv_files: - file_splitted = file.replace('.csv', '').split("_") - interval = file_splitted.pop(-1) - trading_pair = file_splitted.pop(-1) - if len(file_splitted) == 2: - connector = file_splitted.pop(-1) - elif len(file_splitted) == 3: - connector = file_splitted.pop(-2) + "_" + file_splitted.pop(-1) - else: - raise ValueError(f"Invalid file name: {file}") - candles_config.append(CandlesConfig(connector=connector, trading_pair=trading_pair, interval=interval)) - return candles_config - except Exception as e: - st.warning("An error occurred:", e) - -def get_candles_df(candles_config: CandlesConfig): - """Retrieve the candles DataFrame from the specified candles config.""" - try: - candles_df = pd.read_csv(constants.CANDLES_DATA_PATH + f"/candles_{candles_config.connector}_{candles_config.trading_pair}_{candles_config.interval}.csv") - return candles_df - except Exception as e: - st.warning("An error occurred:", e) - -# Start content here -st.text("This tool will let you analyze and generate a config for market making controllers.") -st.write("---") - -st.write("## 📊 Market Data") -st.write("### Candlestick Available") -available_candles = get_available_candles() - -if available_candles: - df = pd.DataFrame([candles.dict() for candles in available_candles]) - df = df[['connector', 'trading_pair', 'interval']].copy() - - # Initialize selection options - connectors = ['All'] + sorted(set(df['connector'])) - trading_pairs = ['All'] + sorted(set(df['trading_pair'])) - intervals = ['All'] + sorted(set(df['interval'])) - - # Select boxes - c1, c2, c3 = st.columns(3) - with c1: - selected_connector = st.selectbox("Select Connector", connectors, index=0) - - # Filter trading pairs and intervals based on selected connector - if selected_connector != 'All': - filtered_pairs = sorted(set(df[df['connector'] == selected_connector]['trading_pair'])) - filtered_intervals = sorted(set(df[df['connector'] == selected_connector]['interval'])) - else: - filtered_pairs = trading_pairs - filtered_intervals = intervals - - with c2: - selected_trading_pair = st.selectbox("Select Trading Pair", ['All'] + filtered_pairs, index=0, disabled=(selected_connector == 'All')) - with c3: - selected_interval = st.selectbox("Select Interval", ['All'] + filtered_intervals, index=0, disabled=(selected_connector == 'All')) - - # Further filter DataFrame based on selections - if selected_connector != 'All': - df = df[df['connector'] == selected_connector] - if selected_trading_pair != 'All': - df = df[df['trading_pair'] == selected_trading_pair] - if selected_interval != 'All': - df = df[df['interval'] == selected_interval] - - c1, c2 = st.columns(2) - with c1: - st.write("### Candlestick Data") - st.data_editor(df) - # Check the length of the filtered DataFrame - with c2: - if len(df) == 1: - # Show 'Render Candles' button - if st.button('Render Candles'): - st.write("Rendering Candles...") - candles_config_data = df.iloc[0].to_dict() - candles_config = CandlesConfig(connector=candles_config_data['connector'], - trading_pair=candles_config_data['trading_pair'], - interval=candles_config_data['interval']) - st.write(candles_config) - candles_df = get_candles_df(candles_config) - st.data_editor(candles_df) - elif df.empty: - # Show 'Download Candles' button - if st.button('Download Candles'): - st.write("Downloading Candles...") From ea4e1d1dc977472f3ff9c66044a6a60ecf64ddf6 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 1 Apr 2024 18:19:53 -0300 Subject: [PATCH 4/5] (feat) make config download --- pages/position_builder/app.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pages/position_builder/app.py b/pages/position_builder/app.py index 5446b7da..7667caa0 100644 --- a/pages/position_builder/app.py +++ b/pages/position_builder/app.py @@ -3,6 +3,7 @@ from plotly.subplots import make_subplots import plotly.graph_objects as go from decimal import Decimal +import yaml from utils.st_utils import initialize_st_page from hummingbot.smart_components.utils.distributions import Distributions @@ -23,6 +24,14 @@ def normalize(values): return [val / total for val in values] +def convert_to_yaml(spreads, order_amounts): + data = { + 'dca_spreads': [float(spread)/100 for spread in spreads], + 'dca_amounts': [float(amount) for amount in order_amounts] + } + return yaml.dump(data, default_flow_style=False) + + with col_quote: total_amount_quote = st.number_input("Total amount of quote", value=1000) @@ -93,7 +102,19 @@ def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads) amount_distribution = normalize(get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts)) order_amounts = [Decimal(amount_dist * total_amount_quote) for amount_dist in amount_distribution] -spreads = [spread - spread_distribution[0] for spread in spread_distribution] +spreads = [Decimal(spread - spread_distribution[0]) for spread in spread_distribution] + + +# Export Button +if st.button('Export as YAML'): + yaml_data = convert_to_yaml(spreads, order_amounts) + st.download_button( + label="Download YAML", + data=yaml_data, + file_name='config.yaml', + mime='text/yaml' + ) + break_even_values = [] take_profit_values = [] for level in range(n_levels): @@ -105,10 +126,11 @@ def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, accumulated_amount = [sum(order_amounts[:i+1]) for i in range(len(order_amounts))] + def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): unrealized_pnl = [] for i in range(len(spreads)): - distance = abs(spreads[i] - break_even_values[i]) + distance = abs(spreads[i] - break_even_values[i]) pnl = accumulated_amount[i] * distance / 100 # PNL calculation unrealized_pnl.append(pnl) return unrealized_pnl From 3424d6891024e1d55e6ce2fd8e215b1469313c41 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 1 Apr 2024 18:19:57 -0300 Subject: [PATCH 5/5] (feat) refactor pages --- main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main.py b/main.py index eed1db5c..cebdbb81 100644 --- a/main.py +++ b/main.py @@ -14,7 +14,6 @@ def main_page(): Page("pages/master_conf/app.py", "Credentials", "🗝️"), Page("pages/bot_orchestration/app.py", "Instances", "🦅"), Page("pages/file_manager/app.py", "File Explorer", "🗂"), - Page("pages/config_generator/app.py", "Config Generator", "🎛️"), Page("pages/position_builder/app.py", "Position Builder", "🔭"), Section("Backtest Manager", "⚙️"), Page("pages/backtest_get_data/app.py", "Get Data", "💾"),