From 8bf1a08f572df987a3efbaebbe877e716410e823 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 17:50:54 +0300 Subject: [PATCH 01/18] (feat) add credentials configuration --- credentials.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 credentials.yml diff --git a/credentials.yml b/credentials.yml new file mode 100644 index 0000000..5d2592b --- /dev/null +++ b/credentials.yml @@ -0,0 +1,15 @@ +# This only works if you change the env variable in the docker-compose.yml +credentials: + usernames: + admin: + email: admin@gmail.com + name: Admin User + logged_in: False + password: abc +cookie: + expiry_days: 30 + key: some_signature_key # Must be string + name: some_cookie_name +pre-authorized: + emails: + - admin@admin.com From 2bd2f55db1dff80c3b43d0f13e7f49bb1de408e8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:09:26 +0300 Subject: [PATCH 02/18] (feat) add credentials as a volume --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 6fb3f33..dd93bdb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,8 +5,11 @@ services: ports: - "8501:8501" environment: + - AUTH_SYSTEM_ENABLED=False - BACKEND_API_HOST=backend-api - BACKEND_API_PORT=8000 + volumes: + - credentials.yml:/dashboard/credentials.yml networks: - emqx-bridge backend-api: From ea3e4484a805efebe5d71adc349155d394ae5a66 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:10:06 +0300 Subject: [PATCH 03/18] (feat) add pages as a volume --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index dd93bdb..34c65d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: - BACKEND_API_PORT=8000 volumes: - credentials.yml:/dashboard/credentials.yml + - ./pages:/dashboard/frontend/pages networks: - emqx-bridge backend-api: From 23962833eb8d24951b11b198e84b7da388ead3cd Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:10:55 +0300 Subject: [PATCH 04/18] (feat) add config and backtesting pages --- pages/config/__init__.py | 0 pages/config/bollinger_v1/README.md | 67 ++++++ pages/config/bollinger_v1/__init__.py | 0 pages/config/bollinger_v1/app.py | 65 +++++ pages/config/bollinger_v1/user_inputs.py | 49 ++++ pages/config/dman_maker_v2/README.md | 61 +++++ pages/config/dman_maker_v2/__init__.py | 0 pages/config/dman_maker_v2/app.py | 71 ++++++ pages/config/dman_maker_v2/user_inputs.py | 37 +++ pages/config/dman_v5/README.md | 19 ++ pages/config/dman_v5/__init__.py | 0 pages/config/dman_v5/app.py | 147 ++++++++++++ pages/config/kalman_filter_v1/README.md | 19 ++ pages/config/kalman_filter_v1/__init__.py | 0 pages/config/kalman_filter_v1/app.py | 225 ++++++++++++++++++ pages/config/macd_bb_v1/README.md | 80 +++++++ pages/config/macd_bb_v1/__init__.py | 0 pages/config/macd_bb_v1/app.py | 64 +++++ pages/config/macd_bb_v1/user_inputs.py | 62 +++++ pages/config/pmm_dynamic/README.md | 62 +++++ pages/config/pmm_dynamic/__init__.py | 0 pages/config/pmm_dynamic/app.py | 85 +++++++ .../spread_and_price_multipliers.py | 17 ++ pages/config/pmm_dynamic/user_inputs.py | 56 +++++ pages/config/pmm_simple/README.md | 49 ++++ pages/config/pmm_simple/__init__.py | 0 pages/config/pmm_simple/app.py | 47 ++++ pages/config/pmm_simple/user_inputs.py | 38 +++ pages/config/position_builder/README.md | 0 pages/config/position_builder/__init__.py | 0 pages/config/position_builder/app.py | 207 ++++++++++++++++ pages/config/supertrend_v1/README.md | 72 ++++++ pages/config/supertrend_v1/__init__.py | 0 pages/config/supertrend_v1/app.py | 62 +++++ pages/config/supertrend_v1/user_inputs.py | 46 ++++ pages/config/utils.py | 28 +++ pages/config/xemm_controller/README.md | 60 +++++ pages/config/xemm_controller/__init__.py | 0 pages/config/xemm_controller/app.py | 134 +++++++++++ pages/config/xemm_controller/user_inputs.py | 46 ++++ 40 files changed, 1975 insertions(+) create mode 100644 pages/config/__init__.py create mode 100644 pages/config/bollinger_v1/README.md create mode 100644 pages/config/bollinger_v1/__init__.py create mode 100644 pages/config/bollinger_v1/app.py create mode 100644 pages/config/bollinger_v1/user_inputs.py create mode 100644 pages/config/dman_maker_v2/README.md create mode 100644 pages/config/dman_maker_v2/__init__.py create mode 100644 pages/config/dman_maker_v2/app.py create mode 100644 pages/config/dman_maker_v2/user_inputs.py create mode 100644 pages/config/dman_v5/README.md create mode 100644 pages/config/dman_v5/__init__.py create mode 100644 pages/config/dman_v5/app.py create mode 100644 pages/config/kalman_filter_v1/README.md create mode 100644 pages/config/kalman_filter_v1/__init__.py create mode 100644 pages/config/kalman_filter_v1/app.py create mode 100644 pages/config/macd_bb_v1/README.md create mode 100644 pages/config/macd_bb_v1/__init__.py create mode 100644 pages/config/macd_bb_v1/app.py create mode 100644 pages/config/macd_bb_v1/user_inputs.py create mode 100644 pages/config/pmm_dynamic/README.md create mode 100644 pages/config/pmm_dynamic/__init__.py create mode 100644 pages/config/pmm_dynamic/app.py create mode 100644 pages/config/pmm_dynamic/spread_and_price_multipliers.py create mode 100644 pages/config/pmm_dynamic/user_inputs.py create mode 100644 pages/config/pmm_simple/README.md create mode 100644 pages/config/pmm_simple/__init__.py create mode 100644 pages/config/pmm_simple/app.py create mode 100644 pages/config/pmm_simple/user_inputs.py create mode 100644 pages/config/position_builder/README.md create mode 100644 pages/config/position_builder/__init__.py create mode 100644 pages/config/position_builder/app.py create mode 100644 pages/config/supertrend_v1/README.md create mode 100644 pages/config/supertrend_v1/__init__.py create mode 100644 pages/config/supertrend_v1/app.py create mode 100644 pages/config/supertrend_v1/user_inputs.py create mode 100644 pages/config/utils.py create mode 100644 pages/config/xemm_controller/README.md create mode 100644 pages/config/xemm_controller/__init__.py create mode 100644 pages/config/xemm_controller/app.py create mode 100644 pages/config/xemm_controller/user_inputs.py diff --git a/pages/config/__init__.py b/pages/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/bollinger_v1/README.md b/pages/config/bollinger_v1/README.md new file mode 100644 index 0000000..0f0918a --- /dev/null +++ b/pages/config/bollinger_v1/README.md @@ -0,0 +1,67 @@ +# Bollinger V1 Configuration Tool + +Welcome to the Bollinger V1 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the Bollinger V1 directional trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the Bollinger V1 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Bollinger Bands Length**: Define the length of the Bollinger Bands. +- **Standard Deviation Multiplier**: Set the standard deviation multiplier for the Bollinger Bands. +- **Long Threshold**: Configure the threshold for long positions. +- **Short Threshold**: Configure the threshold for short positions. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Bollinger Bands + +Visualize the Bollinger Bands on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the Bollinger Bands: + +- **Bollinger Bands Length**: A larger length will make the Bollinger Bands wider and smoother, while a smaller length will make them narrower and more volatile. +- **Long Threshold**: This is a reference to the Bollinger Band. A value of 0 means the lower band, and a value of 1 means the upper band. For example, if the long threshold is 0, long positions will only be taken if the price is below the lower band. +- **Short Threshold**: Similarly, a value of 1.1 means the price must be above the upper band by 0.1 of the band’s range to take a short position. +- **Thresholds**: The closer you set the thresholds to 0.5, the more trades will be executed. The farther away they are, the fewer trades will be executed. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/pages/config/bollinger_v1/__init__.py b/pages/config/bollinger_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/bollinger_v1/app.py b/pages/config/bollinger_v1/app.py new file mode 100644 index 0000000..b0bbdd0 --- /dev/null +++ b/pages/config/bollinger_v1/app.py @@ -0,0 +1,65 @@ +import streamlit as st +import pandas_ta as ta # noqa: F401 + +from frontend.components.backtesting import backtesting_section +from frontend.components.config_loader import get_default_config_loader +from frontend.components.save_config import render_save_config +from frontend.pages.config.utils import get_candles +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.pages.config.bollinger_v1.user_inputs import user_inputs +from plotly.subplots import make_subplots + +from frontend.visualization import theme +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ + render_close_types +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.indicators import get_bbands_traces, get_volume_trace +from frontend.visualization.signals import get_bollinger_v1_signal_traces +from frontend.visualization.utils import add_traces_to_fig + +# Initialize the Streamlit page +initialize_st_page(title="Bollinger V1", icon="πŸ“ˆ", initial_sidebar_state="expanded") +backend_api_client = get_backend_api_client() + + +st.text("This tool will let you create a config for Bollinger V1 and visualize the strategy.") +get_default_config_loader("bollinger_v1") + +inputs = user_inputs() +st.session_state["default_config"].update(inputs) + +st.write("### Visualizing Bollinger Bands and Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) + +# Create a subplot with 2 rows +fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume'), + row_heights=[0.8, 0.2]) + +add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) +add_traces_to_fig(fig, get_bbands_traces(candles, inputs["bb_length"], inputs["bb_std"]), row=1, col=1) +add_traces_to_fig(fig, get_bollinger_v1_signal_traces(candles, inputs["bb_length"], inputs["bb_std"], inputs["bb_long_threshold"], inputs["bb_short_threshold"]), row=1, col=1) +add_traces_to_fig(fig, [get_volume_trace(candles)], row=2, col=1) + +fig.update_layout(**theme.get_default_layout()) +# Use Streamlit's functionality to display the plot +st.plotly_chart(fig, use_container_width=True) +bt_results = backtesting_section(inputs, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) diff --git a/pages/config/bollinger_v1/user_inputs.py b/pages/config/bollinger_v1/user_inputs.py new file mode 100644 index 0000000..8bc8988 --- /dev/null +++ b/pages/config/bollinger_v1/user_inputs.py @@ -0,0 +1,49 @@ +import streamlit as st +from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs +from frontend.components.risk_management import get_risk_management_inputs + + +def user_inputs(): + default_config = st.session_state.get("default_config", {}) + bb_length = default_config.get("bb_length", 100) + bb_std = default_config.get("bb_std", 2.0) + bb_long_threshold = default_config.get("bb_long_threshold", 0.0) + bb_short_threshold = default_config.get("bb_short_threshold", 1.0) + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() + with st.expander("Bollinger Bands Configuration", expanded=True): + c1, c2, c3, c4 = st.columns(4) + with c1: + bb_length = st.number_input("Bollinger Bands Length", min_value=5, max_value=1000, value=bb_length) + with c2: + bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=2.0, value=bb_std) + with c3: + bb_long_threshold = st.number_input("Long Threshold", value=bb_long_threshold) + with c4: + bb_short_threshold = st.number_input("Short Threshold", value=bb_short_threshold) + return { + "controller_name": "bollinger_v1", + "controller_type": "directional_trading", + "connector_name": connector_name, + "trading_pair": trading_pair, + "leverage": leverage, + "total_amount_quote": total_amount_quote, + "max_executors_per_side": max_executors_per_side, + "cooldown_time": cooldown_time, + "position_mode": position_mode, + "candles_connector": candles_connector_name, + "candles_trading_pair": candles_trading_pair, + "interval": interval, + "bb_length": bb_length, + "bb_std": bb_std, + "bb_long_threshold": bb_long_threshold, + "bb_short_threshold": bb_short_threshold, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + }, + "take_profit_order_type": take_profit_order_type.value + } diff --git a/pages/config/dman_maker_v2/README.md b/pages/config/dman_maker_v2/README.md new file mode 100644 index 0000000..da98f61 --- /dev/null +++ b/pages/config/dman_maker_v2/README.md @@ -0,0 +1,61 @@ +# D-Man Maker V2 Configuration Tool + +Welcome to the D-Man Maker V2 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the D-Man Maker V2 trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the D-Man Maker V2 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Buy/Sell Spread Distributions**: Configure the distribution of buy and sell spreads. +- **Order Amounts**: Specify the percentages for buy and sell order amounts. +- **Custom D-Man Maker V2 Settings**: Set specific parameters like top executor refresh time and activation bounds. + +### 3. Executor Distribution Visualization + +Visualize the distribution of your trading executors. This helps you understand how your buy and sell orders are spread across different price levels and amounts. + +### 4. DCA Distribution + +After setting the executor distribution, you will need to configure the internal distribution of the DCA (Dollar Cost Averaging). This involves multiple open orders and one close order per executor level. Visualize the DCA distribution to see how the entry prices are spread and ensure the initial DCA order amounts are above the minimum order size of the exchange. + +### 5. Risk Management + +Configure risk management settings, including take profit, stop loss, time limit, and trailing stop settings for each DCA. This step is crucial for managing your trades and minimizing risk. + +### 6. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 7. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/pages/config/dman_maker_v2/__init__.py b/pages/config/dman_maker_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/dman_maker_v2/app.py b/pages/config/dman_maker_v2/app.py new file mode 100644 index 0000000..830fe99 --- /dev/null +++ b/pages/config/dman_maker_v2/app.py @@ -0,0 +1,71 @@ +import streamlit as st + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient +from frontend.components.backtesting import backtesting_section +from frontend.components.config_loader import get_default_config_loader +from frontend.components.dca_distribution import get_dca_distribution_inputs +from frontend.components.save_config import render_save_config +from frontend.pages.config.dman_maker_v2.user_inputs import user_inputs +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ + render_close_types +from frontend.visualization.dca_builder import create_dca_graph +from frontend.visualization.executors_distribution import create_executors_distribution_traces + +# Initialize the Streamlit page +initialize_st_page(title="D-Man Maker V2", icon="πŸ§™β€β™‚οΈ") +backend_api_client = get_backend_api_client() + + +# Page content +st.text("This tool will let you create a config for D-Man Maker V2 and upload it to the BackendAPI.") +get_default_config_loader("dman_maker_v2") + +inputs = user_inputs() +with st.expander("Executor Distribution:", expanded=True): + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + st.plotly_chart(fig, use_container_width=True) + +dca_inputs = get_dca_distribution_inputs() + +st.write("### Visualizing DCA Distribution for specific Executor Level") +st.write("---") +buy_order_levels = len(inputs["buy_spreads"]) +sell_order_levels = len(inputs["sell_spreads"]) + +buy_executor_levels = [f"BUY_{i}" for i in range(buy_order_levels)] +sell_executor_levels = [f"SELL_{i}" for i in range(sell_order_levels)] +c1, c2 = st.columns(2) +with c1: + executor_level = st.selectbox("Executor Level", buy_executor_levels + sell_executor_levels) + side, level = executor_level.split("_") + if side == "BUY": + dca_amount = inputs["buy_amounts_pct"][int(level)] * inputs["total_amount_quote"] + else: + dca_amount = inputs["sell_amounts_pct"][int(level)] * inputs["total_amount_quote"] +with c2: + st.metric(label="DCA Amount", value=f"{dca_amount:.2f}") +fig = create_dca_graph(dca_inputs, dca_amount) +st.plotly_chart(fig, use_container_width=True) + +# Combine inputs and dca_inputs into final config +config = {**inputs, **dca_inputs} +st.session_state["default_config"].update(config) +bt_results = backtesting_section(config, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) diff --git a/pages/config/dman_maker_v2/user_inputs.py b/pages/config/dman_maker_v2/user_inputs.py new file mode 100644 index 0000000..5ccf4d9 --- /dev/null +++ b/pages/config/dman_maker_v2/user_inputs.py @@ -0,0 +1,37 @@ +import streamlit as st + +from frontend.components.executors_distribution import get_executors_distribution_inputs +from frontend.components.market_making_general_inputs import get_market_making_general_inputs + + +def user_inputs(): + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, _, _, _ = get_market_making_general_inputs() + buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() + with st.expander("Custom D-Man Maker V2 Settings"): + c1, c2 = st.columns(2) + with c1: + top_executor_refresh_time = st.number_input("Top Refresh Time (minutes)", value=60) * 60 + with c2: + executor_activation_bounds = st.number_input("Activation Bounds (%)", value=0.1) / 100 + # Create the config + config = { + "controller_name": "dman_maker_v2", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector_name, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "buy_spreads": buy_spread_distributions, + "sell_spreads": sell_spread_distributions, + "buy_amounts_pct": buy_order_amounts_pct, + "sell_amounts_pct": sell_order_amounts_pct, + "executor_refresh_time": executor_refresh_time, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "top_executor_refresh_time": top_executor_refresh_time, + "executor_activation_bounds": [executor_activation_bounds] + } + + return config diff --git a/pages/config/dman_v5/README.md b/pages/config/dman_v5/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/config/dman_v5/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/config/dman_v5/__init__.py b/pages/config/dman_v5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/dman_v5/app.py b/pages/config/dman_v5/app.py new file mode 100644 index 0000000..9522cb8 --- /dev/null +++ b/pages/config/dman_v5/app.py @@ -0,0 +1,147 @@ +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import yaml +from plotly.subplots import make_subplots + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page, get_backend_api_client + +# Initialize the Streamlit page +initialize_st_page(title="D-Man V5", icon="πŸ“Š", initial_sidebar_state="expanded") + +@st.cache_data +def get_candles(connector_name, trading_pair, interval, max_records): + backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) + return backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records) + +@st.cache_data +def add_indicators(df, macd_fast, macd_slow, macd_signal, diff_lookback): + # MACD + df.ta.macd(fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True) + + # Decision Logic + macdh = df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] + macdh_diff = df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"].diff(diff_lookback) + + long_condition = (macdh > 0) & (macdh_diff > 0) + short_condition = (macdh < 0) & (macdh_diff < 0) + + df["signal"] = 0 + df.loc[long_condition, "signal"] = 1 + df.loc[short_condition, "signal"] = -1 + + return df + +st.write("## Configuration") +c1, c2, c3 = st.columns(3) +with c1: + connector_name = st.text_input("Connector Name", value="binance_perpetual") + trading_pair = st.text_input("Trading Pair", value="WLD-USDT") +with c2: + interval = st.selectbox("Candle Interval", ["1m", "3m", "5m", "15m", "30m"], index=1) + max_records = st.number_input("Max Records", min_value=100, max_value=10000, value=1000) +with c3: + macd_fast = st.number_input("MACD Fast", min_value=1, value=21) + macd_slow = st.number_input("MACD Slow", min_value=1, value=42) + macd_signal = st.number_input("MACD Signal", min_value=1, value=9) + diff_lookback = st.number_input("MACD Diff Lookback", min_value=1, value=5) + +# Fetch and process data +candle_data = get_candles(connector_name, trading_pair, interval, max_records) +df = pd.DataFrame(candle_data) +df.index = pd.to_datetime(df['timestamp'], unit='s') +df = add_indicators(df, macd_fast, macd_slow, macd_signal, diff_lookback) + +# Prepare data for signals +signals = df[df['signal'] != 0] +buy_signals = signals[signals['signal'] == 1] +sell_signals = signals[signals['signal'] == -1] + + +# Define your color palette +tech_colors = { + 'upper_band': '#4682B4', + 'middle_band': '#FFD700', + 'lower_band': '#32CD32', + 'buy_signal': '#1E90FF', + 'sell_signal': '#FF0000', +} + +# Create a subplot with 3 rows +fig = make_subplots(rows=3, cols=1, shared_xaxes=True, + vertical_spacing=0.05, # Adjust spacing to make the plot look better + subplot_titles=('Candlestick', 'MACD Line and Histogram', 'Trading Signals'), + row_heights=[0.5, 0.3, 0.2]) # Adjust heights to give more space to candlestick and MACD + +# Candlestick and Bollinger Bands +fig.add_trace(go.Candlestick(x=df.index, + open=df['open'], + high=df['high'], + low=df['low'], + close=df['close'], + name="Candlesticks", increasing_line_color='#2ECC71', decreasing_line_color='#E74C3C'), + row=1, col=1) + +# MACD Line and Histogram +fig.add_trace(go.Scatter(x=df.index, y=df[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"], line=dict(color='orange'), name='MACD Line'), row=2, col=1) +fig.add_trace(go.Scatter(x=df.index, y=df[f"MACDs_{macd_fast}_{macd_slow}_{macd_signal}"], line=dict(color='purple'), name='MACD Signal'), row=2, col=1) +fig.add_trace(go.Bar(x=df.index, y=df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"], name='MACD Histogram', marker_color=df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"].apply(lambda x: '#FF6347' if x < 0 else '#32CD32')), row=2, col=1) +# Signals plot +fig.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['close'], mode='markers', + marker=dict(color=tech_colors['buy_signal'], size=10, symbol='triangle-up'), + name='Buy Signal'), row=1, col=1) +fig.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['close'], mode='markers', + marker=dict(color=tech_colors['sell_signal'], size=10, symbol='triangle-down'), + name='Sell Signal'), row=1, col=1) + +# Trading Signals +fig.add_trace(go.Scatter(x=signals.index, y=signals['signal'], mode='markers', marker=dict(color=signals['signal'].map({1: '#1E90FF', -1: '#FF0000'}), size=10), name='Trading Signals'), row=3, col=1) + +# Update layout settings for a clean look +fig.update_layout(height=1000, title="MACD and Bollinger Bands Strategy", xaxis_title="Time", yaxis_title="Price", template="plotly_dark", showlegend=True) +fig.update_xaxes(rangeslider_visible=False, row=1, col=1) +fig.update_xaxes(rangeslider_visible=False, row=2, col=1) +fig.update_xaxes(rangeslider_visible=False, row=3, col=1) + +# Display the chart +st.plotly_chart(fig, use_container_width=True) + + +c1, c2, c3 = st.columns([2, 2, 1]) + +with c1: + config_base = st.text_input("Config Base", value=f"macd_bb_v1-{connector_name}-{trading_pair.split('-')[0]}") +with c2: + config_tag = st.text_input("Config Tag", value="1.1") + +# Save the configuration +id = f"{config_base}-{config_tag}" + +config = { + "id": id, + "connector_name": connector_name, + "trading_pair": trading_pair, + "interval": interval, + "macd_fast": macd_fast, + "macd_slow": macd_slow, + "macd_signal": macd_signal, +} + +yaml_config = yaml.dump(config, default_flow_style=False) + +with c3: + download_config = st.download_button( + label="Download YAML", + data=yaml_config, + file_name=f'{id.lower()}.yml', + mime='text/yaml' + ) + upload_config_to_backend = st.button("Upload Config to BackendAPI") + + +if upload_config_to_backend: + backend_api_client = get_backend_api_client() + backend_api_client.add_controller_config(config) + st.success("Config uploaded successfully!") diff --git a/pages/config/kalman_filter_v1/README.md b/pages/config/kalman_filter_v1/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/config/kalman_filter_v1/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/config/kalman_filter_v1/__init__.py b/pages/config/kalman_filter_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/kalman_filter_v1/app.py b/pages/config/kalman_filter_v1/app.py new file mode 100644 index 0000000..2479d18 --- /dev/null +++ b/pages/config/kalman_filter_v1/app.py @@ -0,0 +1,225 @@ +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import yaml +from hummingbot.connector.connector_base import OrderType +from pykalman import KalmanFilter + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page, get_backend_api_client + +# Initialize the Streamlit page +initialize_st_page(title="Kalman Filter V1", icon="πŸ“ˆ", initial_sidebar_state="expanded") + + +@st.cache_data +def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m", max_records=5000): + backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) + return backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records) + +@st.cache_data +def add_indicators(df, observation_covariance=1, transition_covariance=0.01, initial_state_covariance=0.001): + # Add Bollinger Bands + # Construct a Kalman filter + kf = KalmanFilter(transition_matrices=[1], + observation_matrices=[1], + initial_state_mean=df["close"].values[0], + initial_state_covariance=initial_state_covariance, + observation_covariance=observation_covariance, + transition_covariance=transition_covariance) + mean, cov = kf.filter(df["close"].values) + df["kf"] = pd.Series(mean.flatten(), index=df["close"].index) + df["kf_upper"] = pd.Series(mean.flatten() + 1.96 * cov.flatten(), index=df["close"].index) + df["kf_lower"] = pd.Series(mean.flatten() - 1.96 * cov.flatten(), index=df["close"].index) + + # Generate signal + long_condition = df["close"] < df["kf_lower"] + short_condition = df["close"] > df["kf_upper"] + + # Generate signal + df["signal"] = 0 + df.loc[long_condition, "signal"] = 1 + df.loc[short_condition, "signal"] = -1 + return df + + +st.text("This tool will let you create a config for Kalman Filter V1 and visualize the strategy.") +st.write("---") + +# Inputs for Kalman Filter configuration +st.write("## Candles Configuration") +c1, c2, c3, c4 = st.columns(4) +with c1: + connector_name = st.text_input("Connector Name", value="binance_perpetual") + candles_connector = st.text_input("Candles Connector", value="binance_perpetual") +with c2: + trading_pair = st.text_input("Trading Pair", value="WLD-USDT") + candles_trading_pair = st.text_input("Candles Trading Pair", value="WLD-USDT") +with c3: + interval = st.selectbox("Candle Interval", options=["1m", "3m", "5m", "15m", "30m"], index=1) +with c4: + max_records = st.number_input("Max Records", min_value=100, max_value=10000, value=1000) + + +st.write("## Positions Configuration") +c1, c2, c3, c4 = st.columns(4) +with c1: + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=2.0, step=0.1) + tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=3.0, step=0.1) + take_profit_order_type = st.selectbox("Take Profit Order Type", (OrderType.LIMIT, OrderType.MARKET)) +with c2: + ts_ap = st.number_input("Trailing Stop Activation Price (%)", min_value=0.0, max_value=100.0, value=1.0, step=0.1) + ts_delta = st.number_input("Trailing Stop Delta (%)", min_value=0.0, max_value=100.0, value=0.3, step=0.1) + time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=60 * 6) +with c3: + executor_amount_quote = st.number_input("Executor Amount Quote", min_value=10.0, value=100.0, step=1.0) + max_executors_per_side = st.number_input("Max Executors Per Side", min_value=1, value=2) + cooldown_time = st.number_input("Cooldown Time (seconds)", min_value=0, value=300) +with c4: + leverage = st.number_input("Leverage", min_value=1, value=20) + position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY")) + +st.write("## Kalman Filter Configuration") +c1, c2 = st.columns(2) +with c1: + observation_covariance = st.number_input("Observation Covariance", value=1.0) +with c2: + transition_covariance = st.number_input("Transition Covariance", value=0.001, step=0.0001, format="%.4f") + + +# Load candle data +candle_data = get_candles(connector_name=candles_connector, trading_pair=candles_trading_pair, interval=interval, max_records=max_records) +df = pd.DataFrame(candle_data) +df.index = pd.to_datetime(df['timestamp'], unit='s') +candles_processed = add_indicators(df, observation_covariance, transition_covariance) + + + +# Prepare data for signals +signals = candles_processed[candles_processed['signal'] != 0] +buy_signals = signals[signals['signal'] == 1] +sell_signals = signals[signals['signal'] == -1] + +from plotly.subplots import make_subplots + +# Define your color palette +tech_colors = { + 'upper_band': '#4682B4', # Steel Blue for the Upper Bollinger Band + 'middle_band': '#FFD700', # Gold for the Middle Bollinger Band + 'lower_band': '#32CD32', # Green for the Lower Bollinger Band + 'buy_signal': '#1E90FF', # Dodger Blue for Buy Signals + 'sell_signal': '#FF0000', # Red for Sell Signals +} + +# Create a subplot with 2 rows +fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Kalman Filter', 'Trading Signals'), + row_heights=[0.7, 0.3]) + +# Candlestick plot +fig.add_trace(go.Candlestick(x=candles_processed.index, + open=candles_processed['open'], + high=candles_processed['high'], + low=candles_processed['low'], + close=candles_processed['close'], + name="Candlesticks", increasing_line_color='#2ECC71', decreasing_line_color='#E74C3C'), + row=1, col=1) + +# Bollinger Bands +fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf_upper'], line=dict(color=tech_colors['upper_band']), name='Upper Band'), row=1, col=1) +fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf'], line=dict(color=tech_colors['middle_band']), name='Middle Band'), row=1, col=1) +fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf_lower'], line=dict(color=tech_colors['lower_band']), name='Lower Band'), row=1, col=1) + +# Signals plot +fig.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['close'], mode='markers', + marker=dict(color=tech_colors['buy_signal'], size=10, symbol='triangle-up'), + name='Buy Signal'), row=1, col=1) +fig.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['close'], mode='markers', + marker=dict(color=tech_colors['sell_signal'], size=10, symbol='triangle-down'), + name='Sell Signal'), row=1, col=1) + +fig.add_trace(go.Scatter(x=signals.index, y=signals['signal'], mode='markers', + marker=dict(color=signals['signal'].map({1: tech_colors['buy_signal'], -1: tech_colors['sell_signal']}), size=10), + showlegend=False), row=2, col=1) + +# Update layout +fig.update_layout( + height=1000, # Increased height for better visibility + title="Kalman Filter and Trading Signals", + xaxis_title="Time", + yaxis_title="Price", + template="plotly_dark", + showlegend=False +) + +# Update xaxis properties +fig.update_xaxes( + rangeslider_visible=False, # Disable range slider for all + row=1, col=1 +) +fig.update_xaxes( + row=2, col=1 +) + +# Update yaxis properties +fig.update_yaxes( + title_text="Price", row=1, col=1 +) +fig.update_yaxes( + title_text="Signal", row=2, col=1 +) + +# Use Streamlit's functionality to display the plot +st.plotly_chart(fig, use_container_width=True) + +c1, c2, c3 = st.columns([2, 2, 1]) + +with c1: + config_base = st.text_input("Config Base", value=f"bollinger_v1-{connector_name}-{trading_pair.split('-')[0]}") +with c2: + config_tag = st.text_input("Config Tag", value="1.1") + +id = f"{config_base}-{config_tag}" +config = { + "id": id, + "controller_name": "bollinger_v1", + "controller_type": "directional_trading", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector_name, + "trading_pair": trading_pair, + "executor_amount_quote": executor_amount_quote, + "max_executors_per_side": max_executors_per_side, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "stop_loss": sl / 100, + "take_profit": tp / 100, + "time_limit": time_limit, + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap / 100, + "trailing_delta": ts_delta / 100 + }, + "candles_connector": candles_connector, + "candles_trading_pair": candles_trading_pair, + "interval": interval, +} + +yaml_config = yaml.dump(config, default_flow_style=False) + +with c3: + download_config = st.download_button( + label="Download YAML", + data=yaml_config, + file_name=f'{id.lower()}.yml', + mime='text/yaml' + ) + upload_config_to_backend = st.button("Upload Config to BackendAPI") + + +if upload_config_to_backend: + backend_api_client = get_backend_api_client() + backend_api_client.add_controller_config(config) + st.success("Config uploaded successfully!") \ No newline at end of file diff --git a/pages/config/macd_bb_v1/README.md b/pages/config/macd_bb_v1/README.md new file mode 100644 index 0000000..b7e7adb --- /dev/null +++ b/pages/config/macd_bb_v1/README.md @@ -0,0 +1,80 @@ +# MACD BB V1 Configuration Tool + +Welcome to the MACD BB V1 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the MACD BB V1 directional trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the MACD BB V1 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Bollinger Bands Length**: Define the length of the Bollinger Bands. +- **Standard Deviation Multiplier**: Set the standard deviation multiplier for the Bollinger Bands. +- **Long Threshold**: Configure the threshold for long positions. +- **Short Threshold**: Configure the threshold for short positions. +- **MACD Fast**: Set the fast period for the MACD indicator. +- **MACD Slow**: Set the slow period for the MACD indicator. +- **MACD Signal**: Set the signal period for the MACD indicator. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Indicators + +Visualize the Bollinger Bands and MACD on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the indicators: + +- **Bollinger Bands Length**: A larger length will make the Bollinger Bands wider and smoother, while a smaller length will make them narrower and more volatile. +- **Long Threshold**: This is a reference to the Bollinger Band. A value of 0 means the lower band, and a value of 1 means the upper band. For example, if the long threshold is 0, long positions will only be taken if the price is below the lower band. +- **Short Threshold**: Similarly, a value of 1.1 means the price must be above the upper band by 0.1 of the band’s range to take a short position. +- **Thresholds**: The closer you set the thresholds to 0.5, the more trades will be executed. The farther away they are, the fewer trades will be executed. +- **MACD**: The MACD is used to determine trend changes. If the MACD value is negative and the histogram becomes positive, it signals a market trend up, suggesting a long position. Conversely, if the MACD value is positive and the histogram becomes negative, it signals a market trend down, suggesting a short position. + +### Combining MACD and Bollinger Bands for Trade Signals + +The MACD BB V1 strategy uses the MACD to identify potential trend changes and the Bollinger Bands to filter these signals: + +- **Long Signal**: The MACD value must be negative, and the histogram must become positive, indicating a potential uptrend. The price must also be below the long threshold of the Bollinger Bands (e.g., below the lower band if the threshold is 0). +- **Short Signal**: The MACD value must be positive, and the histogram must become negative, indicating a potential downtrend. The price must also be above the short threshold of the Bollinger Bands (e.g., above the upper band if the threshold is 1.1). + +This combination ensures that you only take trend-following trades when the market is already deviated from the mean, enhancing the effectiveness of your trading strategy. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/pages/config/macd_bb_v1/__init__.py b/pages/config/macd_bb_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/macd_bb_v1/app.py b/pages/config/macd_bb_v1/app.py new file mode 100644 index 0000000..3857587 --- /dev/null +++ b/pages/config/macd_bb_v1/app.py @@ -0,0 +1,64 @@ +import streamlit as st +from plotly.subplots import make_subplots + +from frontend.components.backtesting import backtesting_section +from frontend.components.config_loader import get_default_config_loader +from frontend.components.save_config import render_save_config +from frontend.pages.config.macd_bb_v1.user_inputs import user_inputs +from frontend.pages.config.utils import get_candles +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.visualization import theme +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ + render_close_types +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.indicators import get_bbands_traces, get_macd_traces +from frontend.visualization.signals import get_macdbb_v1_signal_traces +from frontend.visualization.utils import add_traces_to_fig + +# Initialize the Streamlit page +initialize_st_page(title="MACD_BB V1", icon="πŸ“Š", initial_sidebar_state="expanded") +backend_api_client = get_backend_api_client() + +get_default_config_loader("macd_bb_v1") +# User inputs +inputs = user_inputs() +st.session_state["default_config"].update(inputs) + + +st.write("### Visualizing MACD Bollinger Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) + +# Create a subplot with 2 rows +fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume', "MACD"), + row_heights=[0.8, 0.2]) +add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) +add_traces_to_fig(fig, get_bbands_traces(candles, inputs["bb_length"], inputs["bb_std"]), row=1, col=1) +add_traces_to_fig(fig, get_macdbb_v1_signal_traces(df=candles, bb_length=inputs["bb_length"], bb_std=inputs["bb_std"], + bb_long_threshold=inputs["bb_long_threshold"], bb_short_threshold=inputs["bb_short_threshold"], + macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=1, col=1) +add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=2, col=1) + +fig.update_layout(**theme.get_default_layout()) +# Use Streamlit's functionality to display the plot +st.plotly_chart(fig, use_container_width=True) +bt_results = backtesting_section(inputs, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) + diff --git a/pages/config/macd_bb_v1/user_inputs.py b/pages/config/macd_bb_v1/user_inputs.py new file mode 100644 index 0000000..3e7e212 --- /dev/null +++ b/pages/config/macd_bb_v1/user_inputs.py @@ -0,0 +1,62 @@ +import streamlit as st +from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs +from frontend.components.risk_management import get_risk_management_inputs + + +def user_inputs(): + default_config = st.session_state.get("default_config", {}) + bb_length = default_config.get("bb_length", 100) + bb_std = default_config.get("bb_std", 2.0) + bb_long_threshold = default_config.get("bb_long_threshold", 0.0) + bb_short_threshold = default_config.get("bb_short_threshold", 1.0) + macd_fast = default_config.get("macd_fast", 21) + macd_slow = default_config.get("macd_slow", 42) + macd_signal = default_config.get("macd_signal", 9) + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() + with st.expander("MACD Bollinger Configuration", expanded=True): + c1, c2, c3, c4, c5, c6, c7 = st.columns(7) + with c1: + bb_length = st.number_input("Bollinger Bands Length", min_value=5, max_value=1000, value=bb_length) + with c2: + bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=2.0, value=bb_std) + with c3: + bb_long_threshold = st.number_input("Long Threshold", value=bb_long_threshold) + with c4: + bb_short_threshold = st.number_input("Short Threshold", value=bb_short_threshold) + with c5: + macd_fast = st.number_input("MACD Fast", min_value=1, value=macd_fast) + with c6: + macd_slow = st.number_input("MACD Slow", min_value=1, value=macd_slow) + with c7: + macd_signal = st.number_input("MACD Signal", min_value=1, value=macd_signal) + + return { + "controller_name": "macd_bb_v1", + "controller_type": "directional_trading", + "connector_name": connector_name, + "trading_pair": trading_pair, + "leverage": leverage, + "total_amount_quote": total_amount_quote, + "max_executors_per_side": max_executors_per_side, + "cooldown_time": cooldown_time, + "position_mode": position_mode, + "candles_connector": candles_connector_name, + "candles_trading_pair": candles_trading_pair, + "interval": interval, + "bb_length": bb_length, + "bb_std": bb_std, + "bb_long_threshold": bb_long_threshold, + "bb_short_threshold": bb_short_threshold, + "macd_fast": macd_fast, + "macd_slow": macd_slow, + "macd_signal": macd_signal, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + }, + "take_profit_order_type": take_profit_order_type.value + } diff --git a/pages/config/pmm_dynamic/README.md b/pages/config/pmm_dynamic/README.md new file mode 100644 index 0000000..c16ce62 --- /dev/null +++ b/pages/config/pmm_dynamic/README.md @@ -0,0 +1,62 @@ +# PMM Dynamic Configuration Tool + +Welcome to the PMM Dynamic Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the PMM Dynamic trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts, including indicators like MACD and NATR. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the PMM Dynamic strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **MACD Fast Period**: Set the fast period for the MACD indicator. +- **MACD Slow Period**: Set the slow period for the MACD indicator. +- **MACD Signal Period**: Set the signal period for the MACD indicator. +- **NATR Length**: Define the length for the NATR indicator. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Indicator Visualization + +Visualize the candlestick data along with the MACD and NATR indicators. This helps you understand how the MACD will shift the mid-price and how the NATR will be used as a base multiplier for spreads. + +### 4. Executor Distribution + +The distribution of orders is now a multiplier of the base spread, which is determined by the NATR indicator. This allows the algorithm to adapt to changing market conditions by adjusting the spread based on the average size of the candles. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/pages/config/pmm_dynamic/__init__.py b/pages/config/pmm_dynamic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/pmm_dynamic/app.py b/pages/config/pmm_dynamic/app.py new file mode 100644 index 0000000..f154444 --- /dev/null +++ b/pages/config/pmm_dynamic/app.py @@ -0,0 +1,85 @@ +import streamlit as st +import plotly.graph_objects as go +from plotly.subplots import make_subplots + +from frontend.components.config_loader import get_default_config_loader +from frontend.components.executors_distribution import get_executors_distribution_inputs +from frontend.components.save_config import render_save_config + +# Import submodules +from frontend.components.backtesting import backtesting_section +from frontend.pages.config.pmm_dynamic.spread_and_price_multipliers import get_pmm_dynamic_multipliers +from frontend.pages.config.pmm_dynamic.user_inputs import user_inputs +from frontend.pages.config.utils import get_candles +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.visualization import theme +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.executors_distribution import create_executors_distribution_traces +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_close_types, \ + render_accuracy_metrics +from frontend.visualization.indicators import get_macd_traces +from frontend.visualization.utils import add_traces_to_fig + +# Initialize the Streamlit page +initialize_st_page(title="PMM Dynamic", icon="πŸ‘©β€πŸ«") +backend_api_client = get_backend_api_client() + +# Page content +st.text("This tool will let you create a config for PMM Dynamic, backtest and upload it to the Backend API.") +get_default_config_loader("pmm_dynamic") +# Get user inputs +inputs = user_inputs() +st.write("### Visualizing MACD and NATR indicators for PMM Dynamic") +st.text("The MACD is used to shift the mid price and the NATR to make the spreads dynamic. " + "In the order distributions graph, we are going to see the values of the orders affected by the average NATR") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) +with st.expander("Visualizing PMM Dynamic Indicators", expanded=True): + fig = make_subplots(rows=4, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'MACD', "Price Multiplier", "Spreads Multiplier"), + row_heights=[0.8, 0.2, 0.2, 0.2]) + add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) + add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=2, col=1) + price_multiplier, spreads_multiplier = get_pmm_dynamic_multipliers(candles, inputs["macd_fast"], inputs["macd_slow"], inputs["macd_signal"], inputs["natr_length"]) + add_traces_to_fig(fig, [go.Scatter(x=candles.index, y=price_multiplier, name="Price Multiplier", line=dict(color="blue"))], row=3, col=1) + add_traces_to_fig(fig, [go.Scatter(x=candles.index, y=spreads_multiplier, name="Base Spread", line=dict(color="red"))], row=4, col=1) + fig.update_layout(**theme.get_default_layout(height=1000)) + fig.update_yaxes(tickformat=".2%", row=3, col=1) + fig.update_yaxes(tickformat=".2%", row=4, col=1) + st.plotly_chart(fig, use_container_width=True) + +st.write("### Executors Distribution") +st.write("The order distributions are affected by the average NATR. This means that if the first order has a spread of " + "1 and the NATR is 0.005, the first order will have a spread of 0.5% of the mid price.") +buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs(use_custom_spread_units=True) +inputs["buy_spreads"] = [spread * 100 for spread in buy_spread_distributions] +inputs["sell_spreads"] = [spread * 100 for spread in sell_spread_distributions] +inputs["buy_amounts_pct"] = buy_order_amounts_pct +inputs["sell_amounts_pct"] = sell_order_amounts_pct +st.session_state["default_config"].update(inputs) +with st.expander("Executor Distribution:", expanded=True): + natr_avarage = spreads_multiplier.mean() + buy_spreads = [spread * natr_avarage for spread in inputs["buy_spreads"]] + sell_spreads = [spread * natr_avarage for spread in inputs["sell_spreads"]] + st.write(f"Average NATR: {natr_avarage:.2%}") + fig = create_executors_distribution_traces(buy_spreads, sell_spreads, inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + st.plotly_chart(fig, use_container_width=True) + +bt_results = backtesting_section(inputs, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) diff --git a/pages/config/pmm_dynamic/spread_and_price_multipliers.py b/pages/config/pmm_dynamic/spread_and_price_multipliers.py new file mode 100644 index 0000000..dfb85f2 --- /dev/null +++ b/pages/config/pmm_dynamic/spread_and_price_multipliers.py @@ -0,0 +1,17 @@ +import pandas_ta as ta # noqa: F401 + + +def get_pmm_dynamic_multipliers(df, macd_fast, macd_slow, macd_signal, natr_length): + """ + Get the spread and price multipliers for PMM Dynamic + """ + natr = ta.natr(df["high"], df["low"], df["close"], length=natr_length) / 100 + macd_output = ta.macd(df["close"], fast=macd_fast, + slow=macd_slow, signal=macd_signal) + macd = macd_output[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"] + macdh = macd_output[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] + macd_signal = - (macd - macd.mean()) / macd.std() + macdh_signal = macdh.apply(lambda x: 1 if x > 0 else -1) + max_price_shift = natr / 2 + price_multiplier = ((0.5 * macd_signal + 0.5 * macdh_signal) * max_price_shift) + return price_multiplier, natr diff --git a/pages/config/pmm_dynamic/user_inputs.py b/pages/config/pmm_dynamic/user_inputs.py new file mode 100644 index 0000000..21e7736 --- /dev/null +++ b/pages/config/pmm_dynamic/user_inputs.py @@ -0,0 +1,56 @@ +import streamlit as st + +from frontend.components.market_making_general_inputs import get_market_making_general_inputs +from frontend.components.risk_management import get_risk_management_inputs + + +def user_inputs(): + default_config = st.session_state.get("default_config", {}) + macd_fast = default_config.get("macd_fast", 21) + macd_slow = default_config.get("macd_slow", 42) + macd_signal = default_config.get("macd_signal", 9) + natr_length = default_config.get("natr_length", 14) + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, candles_connector, candles_trading_pair, interval = get_market_making_general_inputs(custom_candles=True) + sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() + with st.expander("PMM Dynamic Configuration", expanded=True): + c1, c2, c3, c4 = st.columns(4) + with c1: + macd_fast = st.number_input("MACD Fast Period", min_value=1, max_value=200, value=macd_fast) + with c2: + macd_slow = st.number_input("MACD Slow Period", min_value=1, max_value=200, value=macd_slow) + with c3: + macd_signal = st.number_input("MACD Signal Period", min_value=1, max_value=200, value=macd_signal) + with c4: + natr_length = st.number_input("NATR Length", min_value=1, max_value=200, value=natr_length) + + # Create the config + config = { + "controller_name": "pmm_dynamic", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector_name, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "executor_refresh_time": executor_refresh_time, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "candles_connector": candles_connector, + "candles_trading_pair": candles_trading_pair, + "interval": interval, + "macd_fast": macd_fast, + "macd_slow": macd_slow, + "macd_signal": macd_signal, + "natr_length": natr_length, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + } + } + + return config diff --git a/pages/config/pmm_simple/README.md b/pages/config/pmm_simple/README.md new file mode 100644 index 0000000..4b3640d --- /dev/null +++ b/pages/config/pmm_simple/README.md @@ -0,0 +1,49 @@ +# PMM Simple Configuration Tool + +Welcome to the PMM Simple Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the PMM Simple trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the PMM Simple strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Buy/Sell Spread Distributions**: Configure the distribution of buy and sell spreads. +- **Order Amounts**: Specify the percentages for buy and sell order amounts. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Executor Distribution Visualization + +Visualize the distribution of your trading executors. This helps you understand how your buy and sell orders are spread across different price levels and amounts. + +### 4. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 5. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. \ No newline at end of file diff --git a/pages/config/pmm_simple/__init__.py b/pages/config/pmm_simple/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/pmm_simple/app.py b/pages/config/pmm_simple/app.py new file mode 100644 index 0000000..445c362 --- /dev/null +++ b/pages/config/pmm_simple/app.py @@ -0,0 +1,47 @@ +import streamlit as st +from backend.services.backend_api_client import BackendAPIClient +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from frontend.components.config_loader import get_default_config_loader +from frontend.components.save_config import render_save_config + +# Import submodules +from frontend.pages.config.pmm_simple.user_inputs import user_inputs +from frontend.components.backtesting import backtesting_section +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.executors_distribution import create_executors_distribution_traces +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_close_types, \ + render_accuracy_metrics + +# Initialize the Streamlit page +initialize_st_page(title="PMM Simple", icon="πŸ‘¨β€πŸ«") +backend_api_client = get_backend_api_client() + + +# Page content +st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") +get_default_config_loader("pmm_simple") + +inputs = user_inputs() + +st.session_state["default_config"].update(inputs) +with st.expander("Executor Distribution:", expanded=True): + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + st.plotly_chart(fig, use_container_width=True) + +bt_results = backtesting_section(inputs, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) diff --git a/pages/config/pmm_simple/user_inputs.py b/pages/config/pmm_simple/user_inputs.py new file mode 100644 index 0000000..b160481 --- /dev/null +++ b/pages/config/pmm_simple/user_inputs.py @@ -0,0 +1,38 @@ +import streamlit as st + +from frontend.components.executors_distribution import get_executors_distribution_inputs +from frontend.components.market_making_general_inputs import get_market_making_general_inputs +from frontend.components.risk_management import get_risk_management_inputs + + +def user_inputs(): + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, _, _, _ = get_market_making_general_inputs() + buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() + sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() + # Create the config + config = { + "controller_name": "pmm_simple", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector_name, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "buy_spreads": buy_spread_distributions, + "sell_spreads": sell_spread_distributions, + "buy_amounts_pct": buy_order_amounts_pct, + "sell_amounts_pct": sell_order_amounts_pct, + "executor_refresh_time": executor_refresh_time, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + } + } + return config diff --git a/pages/config/position_builder/README.md b/pages/config/position_builder/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/position_builder/__init__.py b/pages/config/position_builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/position_builder/app.py b/pages/config/position_builder/app.py new file mode 100644 index 0000000..8feb50a --- /dev/null +++ b/pages/config/position_builder/app.py @@ -0,0 +1,207 @@ +import streamlit as st +from plotly.subplots import make_subplots +import plotly.graph_objects as go +from decimal import Decimal +import yaml + +from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution +from frontend.st_utils import initialize_st_page + +# Initialize the Streamlit page +initialize_st_page(title="Position Generator", icon="πŸ”­") + +# 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 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) + +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) + + +# 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", n_levels) +amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs(col_amount_dist, "Amount", n_levels) + +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 = [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): + 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) + diff --git a/pages/config/supertrend_v1/README.md b/pages/config/supertrend_v1/README.md new file mode 100644 index 0000000..f93bf3c --- /dev/null +++ b/pages/config/supertrend_v1/README.md @@ -0,0 +1,72 @@ +# Super Trend Configuration Tool + +Welcome to the Super Trend Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the Super Trend directional trading strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the Super Trend strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Super Trend Length**: Define the length of the Super Trend indicator. +- **Super Trend Multiplier**: Set the multiplier for the Super Trend indicator. +- **Percentage Threshold**: Set the percentage threshold for signal generation. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Indicators + +Visualize the Super Trend indicator on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the indicators: + +- **Super Trend Length**: A larger length will make the Super Trend indicator smoother and less sensitive to short-term price fluctuations, while a smaller length will make it more responsive to recent price changes. +- **Super Trend Multiplier**: Adjusting the multiplier affects the sensitivity of the Super Trend indicator. A higher multiplier makes the trend detection more conservative, while a lower multiplier makes it more aggressive. +- **Percentage Threshold**: This defines how close the price needs to be to the Super Trend band to generate a signal. For example, a 0.5% threshold means the price needs to be within 0.5% of the Super Trend band to consider a trade. + +### Combining Super Trend and Percentage Threshold for Trade Signals + +The Super Trend V1 strategy uses the Super Trend indicator combined with a percentage threshold to generate trade signals: + +- **Long Signal**: The Super Trend indicator must signal a long trend, and the price must be within the percentage threshold of the Super Trend long band. For example, if the threshold is 0.5%, the price must be within 0.5% of the Super Trend long band to trigger a long trade. +- **Short Signal**: The Super Trend indicator must signal a short trend, and the price must be within the percentage threshold of the Super Trend short band. Similarly, if the threshold is 0.5%, the price must be within 0.5% of the Super Trend short band to trigger a short trade. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/pages/config/supertrend_v1/__init__.py b/pages/config/supertrend_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/supertrend_v1/app.py b/pages/config/supertrend_v1/app.py new file mode 100644 index 0000000..83e6633 --- /dev/null +++ b/pages/config/supertrend_v1/app.py @@ -0,0 +1,62 @@ +import streamlit as st +from plotly.subplots import make_subplots + +from frontend.components.backtesting import backtesting_section +from frontend.components.config_loader import get_default_config_loader +from frontend.components.save_config import render_save_config +from frontend.pages.config.supertrend_v1.user_inputs import user_inputs +from frontend.pages.config.utils import get_candles +from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.visualization import theme +from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ + render_close_types +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.indicators import get_volume_trace, get_supertrend_traces +from frontend.visualization.signals import get_supertrend_v1_signal_traces +from frontend.visualization.utils import add_traces_to_fig + +# Initialize the Streamlit page +initialize_st_page(title="SuperTrend V1", icon="πŸ“Š", initial_sidebar_state="expanded") +backend_api_client = get_backend_api_client() + +get_default_config_loader("supertrend_v1") +# User inputs +inputs = user_inputs() +st.session_state["default_config"].update(inputs) + +st.write("### Visualizing Supertrend Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) + +# Create a subplot with 2 rows +fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume', "MACD"), + row_heights=[0.8, 0.2]) +add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) +add_traces_to_fig(fig, get_supertrend_traces(candles, inputs["length"], inputs["multiplier"]), row=1, col=1) +add_traces_to_fig(fig, get_supertrend_v1_signal_traces(candles, inputs["length"], inputs["multiplier"], inputs["percentage_threshold"]), row=1, col=1) +add_traces_to_fig(fig, [get_volume_trace(candles)], row=2, col=1) + +layout_settings = theme.get_default_layout() +layout_settings["showlegend"] = False +fig.update_layout(**layout_settings) +# Use Streamlit's functionality to display the plot +st.plotly_chart(fig, use_container_width=True) +bt_results = backtesting_section(inputs, backend_api_client) +if bt_results: + fig = create_backtesting_figure( + df=bt_results["processed_data"], + executors=bt_results["executors"], + config=inputs) + c1, c2 = st.columns([0.9, 0.1]) + with c1: + render_backtesting_metrics(bt_results["results"]) + st.plotly_chart(fig, use_container_width=True) + with c2: + render_accuracy_metrics(bt_results["results"]) + st.write("---") + render_close_types(bt_results["results"]) +st.write("---") +render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) diff --git a/pages/config/supertrend_v1/user_inputs.py b/pages/config/supertrend_v1/user_inputs.py new file mode 100644 index 0000000..05245eb --- /dev/null +++ b/pages/config/supertrend_v1/user_inputs.py @@ -0,0 +1,46 @@ +import streamlit as st +from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs +from frontend.components.risk_management import get_risk_management_inputs + + +def user_inputs(): + default_config = st.session_state.get("default_config", {}) + length = default_config.get("length", 20) + multiplier = default_config.get("multiplier", 3.0) + percentage_threshold = default_config.get("percentage_threshold", 0.5) + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() + + with st.expander("SuperTrend Configuration", expanded=True): + c1, c2, c3 = st.columns(3) + with c1: + length = st.number_input("Supertrend Length", min_value=1, max_value=200, value=length) + with c2: + multiplier = st.number_input("Supertrend Multiplier", min_value=1.0, max_value=5.0, value=multiplier) + with c3: + percentage_threshold = st.number_input("Percentage Threshold (%)", value=percentage_threshold) / 100 + return { + "controller_name": "supertrend_v1", + "controller_type": "directional_trading", + "connector_name": connector_name, + "trading_pair": trading_pair, + "leverage": leverage, + "total_amount_quote": total_amount_quote, + "max_executors_per_side": max_executors_per_side, + "cooldown_time": cooldown_time, + "position_mode": position_mode, + "candles_connector": candles_connector_name, + "candles_trading_pair": candles_trading_pair, + "interval": interval, + "length": length, + "multiplier": multiplier, + "percentage_threshold": percentage_threshold, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + }, + "take_profit_order_type": take_profit_order_type.value + } diff --git a/pages/config/utils.py b/pages/config/utils.py new file mode 100644 index 0000000..cfebbd2 --- /dev/null +++ b/pages/config/utils.py @@ -0,0 +1,28 @@ +import datetime + +import streamlit as st +import pandas as pd + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient + + +def get_max_records(days_to_download: int, interval: str) -> int: + conversion = {"s": 1 / 60, "m": 1, "h": 60, "d": 1440} + unit = interval[-1] + quantity = int(interval[:-1]) + return int(days_to_download * 24 * 60 / (quantity * conversion[unit])) + + +@st.cache_data +def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m", days=7): + backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) + end_time = datetime.datetime.now() - datetime.timedelta(minutes=15) + start_time = end_time - datetime.timedelta(days=days) + + df = pd.DataFrame(backend_client.get_historical_candles(connector_name, trading_pair, interval, + start_time=int(start_time.timestamp() * 1000), + end_time=int(end_time.timestamp() * 1000))) + df.index = pd.to_datetime(df.timestamp, unit='s') + return df + diff --git a/pages/config/xemm_controller/README.md b/pages/config/xemm_controller/README.md new file mode 100644 index 0000000..a5ae110 --- /dev/null +++ b/pages/config/xemm_controller/README.md @@ -0,0 +1,60 @@ +# XEMM Configuration Tool + +Welcome to the XEMM Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the XEMM (Cross-Exchange Market Making) strategy. Here’s how you can make the most out of it. + +## Features + +- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration. +- **Modify Configuration Values**: Change various parameters of the configuration to suit your trading strategy. +- **Visualize Results**: See the impact of your changes through visual charts. +- **Backtest Your Strategy**: Run backtests to evaluate the performance of your strategy. +- **Save and Deploy**: Once satisfied, save the configuration to deploy it later. + +## How to Use + +### 1. Load Default Configuration + +Start by loading the default configuration for the XEMM strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Maker Connector**: Select the maker trading platform or exchange where limit orders will be placed. +- **Maker Trading Pair**: Choose the trading pair on the maker exchange. +- **Taker Connector**: Select the taker trading platform or exchange where market orders will be executed to hedge the imbalance. +- **Taker Trading Pair**: Choose the trading pair on the taker exchange. +- **Min Profitability**: Set the minimum profitability percentage at which orders will be refreshed to avoid risking liquidity. +- **Max Profitability**: Set the maximum profitability percentage at which orders will be refreshed to avoid being too far from the mid-price. +- **Buy Maker Levels**: Specify the number of buy maker levels. +- **Buy Targets and Amounts**: Define the target profitability and amounts for each buy maker level. +- **Sell Maker Levels**: Specify the number of sell maker levels. +- **Sell Targets and Amounts**: Define the target profitability and amounts for each sell maker level. + +### 3. Visualize Order Distribution + +Visualize the order distribution with profitability targets using Plotly charts. This helps you understand how your buy and sell orders are distributed across different profitability levels. + +### Min and Max Profitability + +The XEMM strategy uses min and max profitability bounds to manage the placement of limit orders: + +- **Min Profitability**: If the expected profitability of a limit order drops below this value, the order will be refreshed to avoid risking liquidity. +- **Max Profitability**: If the expected profitability of a limit order exceeds this value, the order will be refreshed to avoid being too far from the mid-price. + +### Combining Profitability Targets and Order Amounts + +- **Buy Orders**: Configure the target profitability and amounts for each buy maker level. The orders will be refreshed if they fall outside the min and max profitability bounds. +- **Sell Orders**: Similarly, configure the target profitability and amounts for each sell maker level, with orders being refreshed based on the profitability bounds. + +### 4. Save and Download Configuration + +Once you have configured your strategy, you can save and download the configuration as a YAML file. This allows you to deploy the strategy later without having to reconfigure it from scratch. + +### 5. Upload Configuration to Backend API + +You can also upload the configuration directly to the Backend API for immediate deployment. This ensures that your strategy is ready to be executed in real-time. + +## Conclusion + +By following these steps, you can efficiently configure your XEMM strategy, visualize its potential performance, and deploy it for trading. Feel free to experiment with different configurations to find the optimal setup for your trading needs. Happy trading! \ No newline at end of file diff --git a/pages/config/xemm_controller/__init__.py b/pages/config/xemm_controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/config/xemm_controller/app.py b/pages/config/xemm_controller/app.py new file mode 100644 index 0000000..f8431e8 --- /dev/null +++ b/pages/config/xemm_controller/app.py @@ -0,0 +1,134 @@ +import streamlit as st +import plotly.graph_objects as go +import yaml + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page, get_backend_api_client + +# Initialize the Streamlit page +initialize_st_page(title="XEMM Multiple Levels", icon="⚑️") + +# Page content +st.text("This tool will let you create a config for XEMM Controller and upload it to the BackendAPI.") +st.write("---") +c1, c2, c3, c4, c5 = st.columns([1, 1, 1, 1, 1]) + +with c1: + maker_connector = st.text_input("Maker Connector", value="kucoin") + maker_trading_pair = st.text_input("Maker Trading Pair", value="LBR-USDT") +with c2: + taker_connector = st.text_input("Taker Connector", value="okx") + taker_trading_pair = st.text_input("Taker Trading Pair", value="LBR-USDT") +with c3: + min_profitability = st.number_input("Min Profitability (%)", value=0.2, step=0.01) / 100 + max_profitability = st.number_input("Max Profitability (%)", value=1.0, step=0.01) / 100 +with c4: + buy_maker_levels = st.number_input("Buy Maker Levels", value=1, step=1) + buy_targets_amounts = [] + c41, c42 = st.columns([1, 1]) + for i in range(buy_maker_levels): + with c41: + target_profitability = st.number_input(f"Target Profitability {i+1} B% ", value=0.3, step=0.01) + with c42: + amount = st.number_input(f"Amount {i+1}B Quote", value=10, step=1) + buy_targets_amounts.append([target_profitability / 100, amount]) +with c5: + sell_maker_levels = st.number_input("Sell Maker Levels", value=1, step=1) + sell_targets_amounts = [] + c51, c52 = st.columns([1, 1]) + for i in range(sell_maker_levels): + with c51: + target_profitability = st.number_input(f"Target Profitability {i+1}S %", value=0.3, step=0.001) + with c52: + amount = st.number_input(f"Amount {i+1} S Quote", value=10, step=1) + sell_targets_amounts.append([target_profitability / 100, amount]) + + +def create_order_graph(order_type, targets, min_profit, max_profit): + # Create a figure + fig = go.Figure() + + # Convert profit targets to percentage for x-axis and prepare data for bar chart + x_values = [t[0] * 100 for t in targets] # Convert to percentage + y_values = [t[1] for t in targets] + x_labels = [f"{x:.2f}%" for x in x_values] # Format x labels as strings with percentage sign + + # Add bar plot for visualization of targets + fig.add_trace(go.Bar( + x=x_labels, + y=y_values, + width=0.01, + name=f'{order_type.capitalize()} Targets', + marker=dict(color='gold') + )) + + # Convert min and max profitability to percentages for reference lines + min_profit_percent = min_profit * 100 + max_profit_percent = max_profit * 100 + + # Add vertical lines for min and max profitability + fig.add_shape(type="line", + x0=min_profit_percent, y0=0, x1=min_profit_percent, y1=max(y_values, default=10), + line=dict(color="red", width=2), + name='Min Profitability') + fig.add_shape(type="line", + x0=max_profit_percent, y0=0, x1=max_profit_percent, y1=max(y_values, default=10), + line=dict(color="red", width=2), + name='Max Profitability') + + # Update layouts with x-axis starting at 0 + fig.update_layout( + title=f"{order_type.capitalize()} Order Distribution with Profitability Targets", + xaxis=dict( + title="Profitability (%)", + range=[0, max(max(x_values + [min_profit_percent, max_profit_percent]) + 0.1, 1)] # Adjust range to include a buffer + ), + yaxis=dict( + title="Order Amount" + ), + height=400, + width=600 + ) + + return fig + +# Use the function for both buy and sell orders +buy_order_fig = create_order_graph('buy', buy_targets_amounts, min_profitability, max_profitability) +sell_order_fig = create_order_graph('sell', sell_targets_amounts, min_profitability, max_profitability) + +# Display the Plotly graphs in Streamlit +st.plotly_chart(buy_order_fig, use_container_width=True) +st.plotly_chart(sell_order_fig, use_container_width=True) + +# Display in Streamlit +c1, c2, c3 = st.columns([2, 2, 1]) +with c1: + config_base = st.text_input("Config Base", value=f"xemm-{maker_connector}-{taker_connector}-{maker_trading_pair.split('-')[0]}") +with c2: + config_tag = st.text_input("Config Tag", value="1.1") + +id = f"{config_base}_{config_tag}" +config = { + "id": id.lower(), + "controller_name": "xemm_multiple_levels", + "controller_type": "generic", + "maker_connector": maker_connector, + "maker_trading_pair": maker_trading_pair, + "taker_connector": taker_connector, + "taker_trading_pair": taker_trading_pair, + "min_profitability": min_profitability, + "max_profitability": max_profitability, + "buy_levels_targets_amount": buy_targets_amounts, + "sell_levels_targets_amount": sell_targets_amounts +} +yaml_config = yaml.dump(config, default_flow_style=False) + +with c3: + upload_config_to_backend = st.button("Upload Config to BackendAPI") + + +if upload_config_to_backend: + backend_api_client = get_backend_api_client() + backend_api_client.add_controller_config(config) + st.success("Config uploaded successfully!") \ No newline at end of file diff --git a/pages/config/xemm_controller/user_inputs.py b/pages/config/xemm_controller/user_inputs.py new file mode 100644 index 0000000..9a2a9b1 --- /dev/null +++ b/pages/config/xemm_controller/user_inputs.py @@ -0,0 +1,46 @@ +import streamlit as st + + +def user_inputs(): + c1, c2, c3, c4, c5 = st.columns([1, 1, 1, 1, 1]) + with c1: + maker_connector = st.text_input("Maker Connector", value="kucoin") + maker_trading_pair = st.text_input("Maker Trading Pair", value="LBR-USDT") + with c2: + taker_connector = st.text_input("Taker Connector", value="okx") + taker_trading_pair = st.text_input("Taker Trading Pair", value="LBR-USDT") + with c3: + min_profitability = st.number_input("Min Profitability (%)", value=0.2, step=0.01) / 100 + max_profitability = st.number_input("Max Profitability (%)", value=1.0, step=0.01) / 100 + with c4: + buy_maker_levels = st.number_input("Buy Maker Levels", value=1, step=1) + buy_targets_amounts = [] + c41, c42 = st.columns([1, 1]) + for i in range(buy_maker_levels): + with c41: + target_profitability = st.number_input(f"Target Profitability {i + 1} B% ", value=0.3, step=0.01) + with c42: + amount = st.number_input(f"Amount {i + 1}B Quote", value=10, step=1) + buy_targets_amounts.append([target_profitability / 100, amount]) + with c5: + sell_maker_levels = st.number_input("Sell Maker Levels", value=1, step=1) + sell_targets_amounts = [] + c51, c52 = st.columns([1, 1]) + for i in range(sell_maker_levels): + with c51: + target_profitability = st.number_input(f"Target Profitability {i + 1}S %", value=0.3, step=0.001) + with c52: + amount = st.number_input(f"Amount {i + 1} S Quote", value=10, step=1) + sell_targets_amounts.append([target_profitability / 100, amount]) + return { + "controller_name": "xemm_multiple_levels", + "controller_type": "generic", + "maker_connector": maker_connector, + "maker_trading_pair": maker_trading_pair, + "taker_connector": taker_connector, + "taker_trading_pair": taker_trading_pair, + "min_profitability": min_profitability, + "max_profitability": max_profitability, + "buy_levels_targets_amount": buy_targets_amounts, + "sell_levels_targets_amount": sell_targets_amounts + } From 291252c85378acc570dc8934d57a6fa33548c247 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:11:03 +0300 Subject: [PATCH 05/18] (feat) add data pages --- pages/data/__init__.py | 0 pages/data/download_candles/README.md | 1 + pages/data/download_candles/__init__.py | 0 pages/data/download_candles/app.py | 69 ++++++++++++++++++++++ pages/data/token_spreads/README.md | 1 + pages/data/token_spreads/__init__.py | 0 pages/data/token_spreads/app.py | 77 +++++++++++++++++++++++++ pages/data/tvl_vs_mcap/README.md | 3 + pages/data/tvl_vs_mcap/__init__.py | 0 pages/data/tvl_vs_mcap/app.py | 70 ++++++++++++++++++++++ 10 files changed, 221 insertions(+) create mode 100644 pages/data/__init__.py create mode 100644 pages/data/download_candles/README.md create mode 100644 pages/data/download_candles/__init__.py create mode 100644 pages/data/download_candles/app.py create mode 100644 pages/data/token_spreads/README.md create mode 100644 pages/data/token_spreads/__init__.py create mode 100644 pages/data/token_spreads/app.py create mode 100644 pages/data/tvl_vs_mcap/README.md create mode 100644 pages/data/tvl_vs_mcap/__init__.py create mode 100644 pages/data/tvl_vs_mcap/app.py diff --git a/pages/data/__init__.py b/pages/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/data/download_candles/README.md b/pages/data/download_candles/README.md new file mode 100644 index 0000000..1d08d65 --- /dev/null +++ b/pages/data/download_candles/README.md @@ -0,0 +1 @@ +Download historical exchange data as OHLVC candles. Supports multiple trading pairs and custom time ranges/intervals. \ No newline at end of file diff --git a/pages/data/download_candles/__init__.py b/pages/data/download_candles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/data/download_candles/app.py b/pages/data/download_candles/app.py new file mode 100644 index 0000000..d03aca0 --- /dev/null +++ b/pages/data/download_candles/app.py @@ -0,0 +1,69 @@ +import streamlit as st +from datetime import datetime, time +import pandas as pd +import plotly.graph_objects as go + +from frontend.st_utils import initialize_st_page, get_backend_api_client + +# Initialize Streamlit page +initialize_st_page(title="Download Candles", icon="πŸ’Ύ") +backend_api_client = get_backend_api_client() + +c1, c2, c3, c4 = st.columns([2, 2, 2, 0.5]) +with c1: + connector = st.selectbox("Exchange", ["binance_perpetual", "binance", "gate_io", "gate_io_perpetual", "kucoin", "ascend_ex"], index=0) + trading_pair = st.text_input("Trading Pair", value="BTC-USDT") +with c2: + interval = st.selectbox("Interval", options=["1m", "3m", "5m", "15m", "1h", "4h", "1d", "1s"]) +with c3: + start_date = st.date_input("Start Date", value=datetime(2023, 1, 1)) + end_date = st.date_input("End Date", value=datetime(2023, 1, 2)) +with c4: + get_data_button = st.button("Get Candles!") + +if get_data_button: + start_datetime = datetime.combine(start_date, time.min) + end_datetime = datetime.combine(end_date, time.max) + + candles = backend_api_client.get_historical_candles( + connector=connector, + trading_pair=trading_pair, + interval=interval, + start_time=int(start_datetime.timestamp()) * 1000, + end_time=int(end_datetime.timestamp()) * 1000 + ) + + candles_df = pd.DataFrame(candles) + candles_df.index = pd.to_datetime(candles_df["timestamp"], unit='s') + + # Plotting the candlestick chart + fig = go.Figure(data=[go.Candlestick( + x=candles_df.index, + open=candles_df['open'], + high=candles_df['high'], + low=candles_df['low'], + close=candles_df['close'], + increasing_line_color='#2ECC71', + decreasing_line_color='#E74C3C' + )]) + fig.update_layout( + height=1000, + title="Candlesticks", + xaxis_title="Time", + yaxis_title="Price", + template="plotly_dark", + showlegend=False + ) + fig.update_xaxes(rangeslider_visible=False) + fig.update_yaxes(title_text="Price") + st.plotly_chart(fig, use_container_width=True) + + # Generating CSV and download button + csv = candles_df.to_csv(index=False) + filename = f"{connector}_{trading_pair}_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}.csv" + st.download_button( + label="Download Candles as CSV", + data=csv, + file_name=filename, + mime='text/csv', + ) diff --git a/pages/data/token_spreads/README.md b/pages/data/token_spreads/README.md new file mode 100644 index 0000000..4225df8 --- /dev/null +++ b/pages/data/token_spreads/README.md @@ -0,0 +1 @@ +Identify cross-exchange trading opportunities by analyzing differences in token spreads across venues \ No newline at end of file diff --git a/pages/data/token_spreads/__init__.py b/pages/data/token_spreads/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/data/token_spreads/app.py b/pages/data/token_spreads/app.py new file mode 100644 index 0000000..56a30f6 --- /dev/null +++ b/pages/data/token_spreads/app.py @@ -0,0 +1,77 @@ +import streamlit as st +import plotly.express as px +import CONFIG +from backend.services.coingecko_client import CoinGeckoClient +from backend.services.miner_client import MinerClient +from frontend.st_utils import initialize_st_page + +initialize_st_page(title="Token Spreads", icon="πŸ§™") + +# Start content here +cg_utils = CoinGeckoClient() +miner_utils = MinerClient() + +@st.cache_data +def get_all_coins_df(): + return cg_utils.get_all_coins_df() + +@st.cache_data +def get_all_exchanges_df(): + return cg_utils.get_all_exchanges_df() + +@st.cache_data +def get_miner_stats_df(): + return miner_utils.get_miner_stats_df() + +@st.cache_data +def get_coin_tickers_by_id_list(coins_id: list): + return cg_utils.get_coin_tickers_by_id_list(coins_id) + +with st.spinner(text='In progress'): + exchanges_df = get_all_exchanges_df() + coins_df = get_all_coins_df() + miner_stats_df = get_miner_stats_df() + +miner_coins = coins_df.loc[coins_df["symbol"].isin(miner_stats_df["base"].str.lower().unique()), "name"] + +tokens = st.multiselect( + "Select the tokens to analyze:", + options=coins_df["name"], + default=CONFIG.DEFAULT_MINER_COINS +) + +coins_id = coins_df.loc[coins_df["name"].isin(tokens), "id"].tolist() + +coin_tickers_df = get_coin_tickers_by_id_list(coins_id) +coin_tickers_df["coin_name"] = coin_tickers_df.apply(lambda x: coins_df.loc[coins_df["id"] == x.token_id, "name"].item(), axis=1) + +exchanges = st.multiselect( + "Select the exchanges to analyze:", + options=exchanges_df["name"], + default=[exchange for exchange in CONFIG.MINER_EXCHANGES if exchange in exchanges_df["name"].unique()] +) + +height = len(coin_tickers_df["coin_name"].unique()) * 500 + +fig = px.scatter( + data_frame=coin_tickers_df[coin_tickers_df["exchange"].isin(exchanges)], + x="volume", + y="bid_ask_spread_percentage", + color="exchange", + log_x=True, + log_y=True, + facet_col="coin_name", + hover_data=["trading_pair"], + facet_col_wrap=1, + height=height, + template="plotly_dark", + title="Spread and Volume Chart", + labels={ + "volume": 'Volume (USD)', + 'bid_ask_spread_percentage': 'Bid Ask Spread (%)' + } +) + +# st.write("# Data filters 🏷") +# st.code("🧳 New filters coming. \nReach us on discord \nif you want to propose one!") +st.plotly_chart(fig, use_container_width=True) diff --git a/pages/data/tvl_vs_mcap/README.md b/pages/data/tvl_vs_mcap/README.md new file mode 100644 index 0000000..251fc88 --- /dev/null +++ b/pages/data/tvl_vs_mcap/README.md @@ -0,0 +1,3 @@ +Easily compare various DeFi protocols based on their market capitalization and total value locked, using DeFiLlama data. + +Data Source: [DefiLlama](https://defillama.com/) diff --git a/pages/data/tvl_vs_mcap/__init__.py b/pages/data/tvl_vs_mcap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/data/tvl_vs_mcap/app.py b/pages/data/tvl_vs_mcap/app.py new file mode 100644 index 0000000..48e276f --- /dev/null +++ b/pages/data/tvl_vs_mcap/app.py @@ -0,0 +1,70 @@ +import numpy as np +import streamlit as st +import pandas as pd +import plotly.express as px +from defillama import DefiLlama + +from frontend.st_utils import initialize_st_page + +initialize_st_page(title="TVL vs Market Cap", icon="πŸ¦‰") + +# Start content here +MIN_TVL = 1000000. +MIN_MCAP = 1000000. + +@st.cache_data +def get_tvl_mcap_data(): + llama = DefiLlama() + df = pd.DataFrame(llama.get_all_protocols()) + tvl_mcap_df = df.loc[(df["tvl"]>0) & (df["mcap"]>0), ["name", "tvl", "mcap", "chain", "category", "slug"]].sort_values(by=["mcap"], ascending=False) + return tvl_mcap_df[(tvl_mcap_df["tvl"] > MIN_TVL) & (tvl_mcap_df["mcap"]> MIN_MCAP)] + +def get_protocols_by_chain_category(protocols: pd.DataFrame, group_by: list, nth: list): + return protocols.sort_values('tvl', ascending=False).groupby(group_by).nth(nth).reset_index() + +with st.spinner(text='In progress'): + tvl_mcap_df = get_tvl_mcap_data() + +default_chains = ["Ethereum", "Solana", "Binance", "Polygon", "Multi-Chain", "Avalanche"] + +st.write("### Chains πŸ”—") +chains = st.multiselect( + "Select the chains to analyze:", + options=tvl_mcap_df["chain"].unique(), + default=default_chains) + +scatter = px.scatter( + data_frame=tvl_mcap_df[tvl_mcap_df["chain"].isin(chains)], + x="tvl", + y="mcap", + color="chain", + trendline="ols", + log_x=True, + log_y=True, + height=800, + hover_data=["name"], + template="plotly_dark", + title="TVL vs MCAP", + labels={ + "tvl": 'TVL (USD)', + 'mcap': 'Market Cap (USD)' + }) + +st.plotly_chart(scatter, use_container_width=True) + +st.write("---") +st.write("### SunBurst 🌞") +groupby = st.selectbox('Group by:', [['chain', 'category'], ['category', 'chain']]) +nth = st.slider('Top protocols by Category', min_value=1, max_value=5) + +proto_agg = get_protocols_by_chain_category(tvl_mcap_df[tvl_mcap_df["chain"].isin(chains)], groupby, np.arange(0, nth, 1).tolist()) +groupby.append("slug") +sunburst = px.sunburst( + proto_agg, + path=groupby, + values='tvl', + height=800, + title="SunBurst", + template="plotly_dark",) + +st.plotly_chart(sunburst, use_container_width=True) \ No newline at end of file From abfb9e96aea389d8dc1638f3199c0110b219d7a7 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:11:12 +0300 Subject: [PATCH 06/18] (feat) add orchestration pages --- pages/orchestration/__init__.py | 0 pages/orchestration/credentials/README.md | 19 +++ pages/orchestration/credentials/__init__.py | 0 pages/orchestration/credentials/app.py | 106 ++++++++++++ pages/orchestration/file_manager/README.md | 13 ++ pages/orchestration/file_manager/__init__.py | 0 pages/orchestration/file_manager/app.py | 39 +++++ pages/orchestration/instances/README.md | 19 +++ pages/orchestration/instances/__init__.py | 0 pages/orchestration/instances/app.py | 73 +++++++++ pages/orchestration/launch_bot_v2/README.md | 19 +++ pages/orchestration/launch_bot_v2/__init__.py | 0 pages/orchestration/launch_bot_v2/app.py | 31 ++++ .../orchestration/launch_bot_v2_st/README.md | 19 +++ .../launch_bot_v2_st/__init__.py | 0 pages/orchestration/launch_bot_v2_st/app.py | 9 + pages/orchestration/portfolio/README.md | 19 +++ pages/orchestration/portfolio/__init__.py | 0 pages/orchestration/portfolio/app.py | 154 ++++++++++++++++++ 19 files changed, 520 insertions(+) create mode 100644 pages/orchestration/__init__.py create mode 100644 pages/orchestration/credentials/README.md create mode 100644 pages/orchestration/credentials/__init__.py create mode 100644 pages/orchestration/credentials/app.py create mode 100644 pages/orchestration/file_manager/README.md create mode 100644 pages/orchestration/file_manager/__init__.py create mode 100644 pages/orchestration/file_manager/app.py create mode 100644 pages/orchestration/instances/README.md create mode 100644 pages/orchestration/instances/__init__.py create mode 100644 pages/orchestration/instances/app.py create mode 100644 pages/orchestration/launch_bot_v2/README.md create mode 100644 pages/orchestration/launch_bot_v2/__init__.py create mode 100644 pages/orchestration/launch_bot_v2/app.py create mode 100644 pages/orchestration/launch_bot_v2_st/README.md create mode 100644 pages/orchestration/launch_bot_v2_st/__init__.py create mode 100644 pages/orchestration/launch_bot_v2_st/app.py create mode 100644 pages/orchestration/portfolio/README.md create mode 100644 pages/orchestration/portfolio/__init__.py create mode 100644 pages/orchestration/portfolio/app.py diff --git a/pages/orchestration/__init__.py b/pages/orchestration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/credentials/README.md b/pages/orchestration/credentials/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/pages/orchestration/credentials/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/pages/orchestration/credentials/__init__.py b/pages/orchestration/credentials/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/credentials/app.py b/pages/orchestration/credentials/app.py new file mode 100644 index 0000000..666f054 --- /dev/null +++ b/pages/orchestration/credentials/app.py @@ -0,0 +1,106 @@ +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page, get_backend_api_client +import streamlit as st + + +initialize_st_page(title="Credentials", icon="πŸ”‘") + +# Page content +client = get_backend_api_client() +NUM_COLUMNS = 4 + + +@st.cache_data +def get_all_connectors_config_map(): + return client.get_all_connectors_config_map() + +# Section to display available accounts and credentials +accounts = client.get_accounts() +all_connector_config_map = get_all_connectors_config_map() +st.header("Available Accounts and Credentials") + +if accounts: + n_accounts = len(accounts) + accounts.remove("master_account") + accounts.insert(0, "master_account") + for i in range(0, n_accounts, NUM_COLUMNS): + cols = st.columns(NUM_COLUMNS) + for j, account in enumerate(accounts[i:i + NUM_COLUMNS]): + with cols[j]: + st.subheader(f"🏦 {account}") + credentials = client.get_credentials(account) + st.json(credentials) +else: + st.write("No accounts available.") + +st.markdown("---") + +c1, c2, c3 = st.columns([1, 1, 1]) +with c1: + # Section to create a new account + st.header("Create a New Account") + new_account_name = st.text_input("New Account Name") + if st.button("Create Account"): + new_account_name = new_account_name.replace(" ", "_") + if new_account_name: + if new_account_name in accounts: + st.warning(f"Account {new_account_name} already exists.") + st.stop() + elif new_account_name == "" or all(char == "_" for char in new_account_name): + st.warning("Please enter a valid account name.") + st.stop() + response = client.add_account(new_account_name) + st.write(response) + else: + st.write("Please enter an account name.") + +with c2: + # Section to delete an existing account + st.header("Delete an Account") + delete_account_name = st.selectbox("Select Account to Delete", options=accounts if accounts else ["No accounts available"], ) + if st.button("Delete Account"): + if delete_account_name and delete_account_name != "No accounts available": + response = client.delete_account(delete_account_name) + st.warning(response) + else: + st.write("Please select a valid account.") + +with c3: + # Section to delete a credential from an existing account + st.header("Delete Credential") + delete_account_cred_name = st.selectbox("Select the credentials account", options=accounts if accounts else ["No accounts available"],) + creds_for_account = [credential.split(".")[0] for credential in client.get_credentials(delete_account_cred_name)] + delete_cred_name = st.selectbox("Select a Credential to Delete", options=creds_for_account if creds_for_account else ["No credentials available"]) + if st.button("Delete Credential"): + if (delete_account_cred_name and delete_account_cred_name != "No accounts available") and (delete_cred_name and delete_cred_name != "No credentials available"): + response = client.delete_credential(delete_account_cred_name, delete_cred_name) + st.warning(response) + else: + st.write("Please select a valid account.") + +st.markdown("---") + +# Section to add credentials +st.header("Add Credentials") +c1, c2 = st.columns([1, 1]) +with c1: + account_name = st.selectbox("Select Account", options=accounts if accounts else ["No accounts available"]) +with c2: + all_connectors = list(all_connector_config_map.keys()) + binance_perpetual_index = all_connectors.index("binance_perpetual") if "binance_perpetual" in all_connectors else None + connector_name = st.selectbox("Select Connector", options=all_connectors, index=binance_perpetual_index) + config_map = all_connector_config_map[connector_name] + +st.write(f"Configuration Map for {connector_name}:") +config_inputs = {} +cols = st.columns(NUM_COLUMNS) +for i, config in enumerate(config_map): + with cols[i % (NUM_COLUMNS - 1)]: + config_inputs[config] = st.text_input(config, type="password", key=f"{connector_name}_{config}") + +with cols[-1]: + if st.button("Submit Credentials"): + response = client.add_connector_keys(account_name, connector_name, config_inputs) + if response: + st.success(response) \ No newline at end of file diff --git a/pages/orchestration/file_manager/README.md b/pages/orchestration/file_manager/README.md new file mode 100644 index 0000000..d82ed9e --- /dev/null +++ b/pages/orchestration/file_manager/README.md @@ -0,0 +1,13 @@ +### Description + +This page helps you manage and edit script files to run with Hummingbot instances: + +- Selecting files +- Editing and saving files + +### Maintainers + +This page is maintained by Hummingbot Foundation: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) diff --git a/pages/orchestration/file_manager/__init__.py b/pages/orchestration/file_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/file_manager/app.py b/pages/orchestration/file_manager/app.py new file mode 100644 index 0000000..8f20839 --- /dev/null +++ b/pages/orchestration/file_manager/app.py @@ -0,0 +1,39 @@ +from types import SimpleNamespace +import streamlit as st +from streamlit_elements import elements, mui + +from frontend.components.bots_file_explorer import BotsFileExplorer +from frontend.components.dashboard import Dashboard +from frontend.components.editor import Editor +from frontend.st_utils import initialize_st_page + +initialize_st_page(title="Strategy Configs", icon="πŸ—‚οΈ") + + +if "fe_board" not in st.session_state: + board = Dashboard() + fe_board = SimpleNamespace( + dashboard=board, + file_explorer=BotsFileExplorer(board, 0, 0, 3, 7), + editor=Editor(board, 4, 0, 9, 7), + ) + st.session_state.fe_board = fe_board + +else: + fe_board = st.session_state.fe_board + +# Add new tabs +for tab_name, content in fe_board.file_explorer.tabs.items(): + if tab_name not in fe_board.editor.tabs: + fe_board.editor.add_tab(tab_name, content["content"], content["language"]) + +# Remove deleted tabs +for tab_name in list(fe_board.editor.tabs.keys()): + if tab_name not in fe_board.file_explorer.tabs: + fe_board.editor.remove_tab(tab_name) + +with elements("file_manager"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with fe_board.dashboard(): + fe_board.file_explorer() + fe_board.editor() diff --git a/pages/orchestration/instances/README.md b/pages/orchestration/instances/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/pages/orchestration/instances/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/pages/orchestration/instances/__init__.py b/pages/orchestration/instances/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/instances/app.py b/pages/orchestration/instances/app.py new file mode 100644 index 0000000..d506e95 --- /dev/null +++ b/pages/orchestration/instances/app.py @@ -0,0 +1,73 @@ +import time + +import streamlit as st +from streamlit_elements import elements, mui +from types import SimpleNamespace + +from frontend.components.bot_performance_card import BotPerformanceCardV2 +from frontend.components.dashboard import Dashboard +from frontend.st_utils import initialize_st_page, get_backend_api_client + +# Constants for UI layout +CARD_WIDTH = 12 +CARD_HEIGHT = 4 +NUM_CARD_COLS = 1 + +def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_WIDTH, card_height: int = CARD_HEIGHT): + rows = n_cards // cols + 1 + x_y = [(x * card_width, y * card_height) for x in range(cols) for y in range(rows)] + return sorted(x_y, key=lambda x: (x[1], x[0])) + + +def update_active_bots(api_client): + active_bots_response = api_client.get_active_bots_status() + if active_bots_response.get("status") == "success": + current_active_bots = active_bots_response.get("data") + stored_bots = {card[1]: card for card in st.session_state.active_instances_board.bot_cards} + + new_bots = set(current_active_bots.keys()) - set(stored_bots.keys()) + removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys()) + for bot in removed_bots: + st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot] + positions = get_grid_positions(len(current_active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for bot, (x, y) in zip(new_bots, positions[:len(new_bots)]): + card = BotPerformanceCardV2(st.session_state.active_instances_board.dashboard, x, y, CARD_WIDTH, CARD_HEIGHT) + st.session_state.active_instances_board.bot_cards.append((card, bot)) + + +initialize_st_page(title="Instances", icon="πŸ¦…") +api_client = get_backend_api_client() + +if not api_client.is_docker_running(): + st.warning("Docker is not running. Please start Docker and refresh the page.") + st.stop() + +if "active_instances_board" not in st.session_state: + active_bots_response = api_client.get_active_bots_status() + bot_cards = [] + board = Dashboard() + st.session_state.active_instances_board = SimpleNamespace( + dashboard=board, + bot_cards=bot_cards, + ) + active_bots = active_bots_response.get("data") + number_of_bots = len(active_bots) + if number_of_bots > 0: + positions = get_grid_positions(number_of_bots, NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): + bot_status = api_client.get_bot_status(bot) + card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT) + st.session_state.active_instances_board.bot_cards.append((card, bot)) +else: + update_active_bots(api_client) + +with elements("active_instances_board"): + with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): + mui.Typography("🏠 Local Instances", variant="h5") + for card, bot in st.session_state.active_instances_board.bot_cards: + with st.session_state.active_instances_board.dashboard(): + card(bot) + +while True: + time.sleep(10) + st.rerun() \ No newline at end of file diff --git a/pages/orchestration/launch_bot_v2/README.md b/pages/orchestration/launch_bot_v2/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/pages/orchestration/launch_bot_v2/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/pages/orchestration/launch_bot_v2/__init__.py b/pages/orchestration/launch_bot_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/launch_bot_v2/app.py b/pages/orchestration/launch_bot_v2/app.py new file mode 100644 index 0000000..bf1e15b --- /dev/null +++ b/pages/orchestration/launch_bot_v2/app.py @@ -0,0 +1,31 @@ +from types import SimpleNamespace + +import streamlit as st +from streamlit_elements import elements, mui + +from frontend.components.dashboard import Dashboard +from frontend.components.launch_strategy_v2 import LaunchStrategyV2 +from frontend.st_utils import initialize_st_page + +CARD_WIDTH = 6 +CARD_HEIGHT = 3 +NUM_CARD_COLS = 2 + +initialize_st_page(title="Launch Bot", icon="πŸ™Œ") + +if "launch_bots_board" not in st.session_state: + board = Dashboard() + launch_bots_board = SimpleNamespace( + dashboard=board, + launch_bot=LaunchStrategyV2(board, 0, 0, 12, 10), + ) + st.session_state.launch_bots_board = launch_bots_board + +else: + launch_bots_board = st.session_state.launch_bots_board + + +with elements("create_bot"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with launch_bots_board.dashboard(): + launch_bots_board.launch_bot() diff --git a/pages/orchestration/launch_bot_v2_st/README.md b/pages/orchestration/launch_bot_v2_st/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/pages/orchestration/launch_bot_v2_st/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/pages/orchestration/launch_bot_v2_st/__init__.py b/pages/orchestration/launch_bot_v2_st/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/launch_bot_v2_st/app.py b/pages/orchestration/launch_bot_v2_st/app.py new file mode 100644 index 0000000..50c2c08 --- /dev/null +++ b/pages/orchestration/launch_bot_v2_st/app.py @@ -0,0 +1,9 @@ +from frontend.components.deploy_v2_with_controllers import LaunchV2WithControllers +from frontend.st_utils import initialize_st_page + + +initialize_st_page(title="Launch Bot ST", icon="πŸ™Œ") + + +launcher = LaunchV2WithControllers() +launcher() diff --git a/pages/orchestration/portfolio/README.md b/pages/orchestration/portfolio/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/pages/orchestration/portfolio/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/pages/orchestration/portfolio/__init__.py b/pages/orchestration/portfolio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/orchestration/portfolio/app.py b/pages/orchestration/portfolio/app.py new file mode 100644 index 0000000..27d8607 --- /dev/null +++ b/pages/orchestration/portfolio/app.py @@ -0,0 +1,154 @@ +from frontend.st_utils import initialize_st_page, get_backend_api_client +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import plotly.express as px + +initialize_st_page(title="Portfolio", icon="πŸ’°") + +# Page content +client = get_backend_api_client() +NUM_COLUMNS = 4 + + +# Convert balances to a DataFrame for easier manipulation +def account_state_to_df(account_state): + data = [] + for account, exchanges in account_state.items(): + for exchange, tokens_info in exchanges.items(): + for info in tokens_info: + data.append({ + "account": account, + "exchange": exchange, + "token": info["token"], + "price": info["price"], + "units": info["units"], + "value": info["value"], + "available_units": info["available_units"], + }) + return pd.DataFrame(data) + + +# Convert historical account states to a DataFrame +def account_history_to_df(history): + data = [] + for record in history: + timestamp = record["timestamp"] + for account, exchanges in record["state"].items(): + for exchange, tokens_info in exchanges.items(): + for info in tokens_info: + data.append({ + "timestamp": timestamp, + "account": account, + "exchange": exchange, + "token": info["token"], + "price": info["price"], + "units": info["units"], + "value": info["value"], + "available_units": info["available_units"], + }) + return pd.DataFrame(data) + + +# Fetch account state from the backend +account_state = client.get_accounts_state() +account_history = client.get_account_state_history() +if len(account_state) == 0: + st.warning("No accounts found.") + st.stop() + +# Display the accounts available +accounts = st.multiselect("Select Accounts", list(account_state.keys()), list(account_state.keys())) +if len(accounts) == 0: + st.warning("Please select an account.") + st.stop() + +# Display the exchanges available +exchanges_available = [] +for account in accounts: + exchanges_available += account_state[account].keys() + +if len(exchanges_available) == 0: + st.warning("No exchanges found.") + st.stop() +exchanges = st.multiselect("Select Exchanges", exchanges_available, exchanges_available) + +# Display the tokens available +tokens_available = [] +for account in accounts: + for exchange in exchanges: + if exchange in account_state[account]: + tokens_available += [info["token"] for info in account_state[account][exchange]] + +token_options = set(tokens_available) +tokens_available = st.multiselect("Select Tokens", token_options, token_options) + + +st.write("---") + +filtered_account_state = {} +for account in accounts: + filtered_account_state[account] = {} + for exchange in exchanges: + if exchange in account_state[account]: + filtered_account_state[account][exchange] = [token_info for token_info in account_state[account][exchange] if token_info["token"] in tokens_available] + +filtered_account_history = [] +for record in account_history: + filtered_record = {"timestamp": record["timestamp"], "state": {}} + for account in accounts: + if account in record["state"]: + filtered_record["state"][account] = {} + for exchange in exchanges: + if exchange in record["state"][account]: + filtered_record["state"][account][exchange] = [token_info for token_info in record["state"][account][exchange] if token_info["token"] in tokens_available] + filtered_account_history.append(filtered_record) + +if len(filtered_account_state) > 0: + account_state_df = account_state_to_df(filtered_account_state) + total_balance_usd = round(account_state_df["value"].sum(), 2) + c1, c2 = st.columns([1, 5]) + with c1: + st.metric("Total Balance (USD)", total_balance_usd) + with c2: + account_state_df['% Allocation'] = (account_state_df['value'] / total_balance_usd) * 100 + account_state_df['label'] = account_state_df['token'] + ' ($' + account_state_df['value'].apply( + lambda x: f'{x:,.2f}') + ')' + + # Create a sunburst chart with Plotly Express + fig = px.sunburst(account_state_df, + path=['account', 'exchange', 'label'], + values='value', + hover_data={'% Allocation': ':.2f'}, + title='% Allocation by Account, Exchange, and Token', + color='account', + color_discrete_sequence=px.colors.qualitative.Vivid) + + fig.update_traces(textinfo='label+percent entry') + + fig.update_layout(margin=dict(t=0, l=0, r=0, b=0), height=800, title_x=0.01, title_y=1,) + + st.plotly_chart(fig, use_container_width=True) + + st.dataframe(account_state_df[['exchange', 'token', 'units', 'price', 'value', 'available_units']], width=1800, + height=600) + +# Plot the evolution of the portfolio over time +if len(filtered_account_history) > 0: + account_history_df = account_history_to_df(filtered_account_history) + account_history_df['timestamp'] = pd.to_datetime(account_history_df['timestamp']) + + # Aggregate the value of the portfolio over time + portfolio_evolution_df = account_history_df.groupby('timestamp')['value'].sum().reset_index() + + fig = px.line(portfolio_evolution_df, x='timestamp', y='value', title='Portfolio Evolution Over Time') + fig.update_layout(xaxis_title='Time', yaxis_title='Total Value (USD)', height=600) + st.plotly_chart(fig, use_container_width=True) + + # Plot the evolution of each token's value over time + token_evolution_df = account_history_df.groupby(['timestamp', 'token'])['value'].sum().reset_index() + + fig = px.area(token_evolution_df, x='timestamp', y='value', color='token', title='Token Value Evolution Over Time', + color_discrete_sequence=px.colors.qualitative.Vivid) + fig.update_layout(xaxis_title='Time', yaxis_title='Value (USD)', height=600) + st.plotly_chart(fig, use_container_width=True) From a700f702208ab59fa9952811b6a14fa0aae85d67 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:11:23 +0300 Subject: [PATCH 07/18] (feat) permissions --- pages/__init__.py | 0 pages/permissions.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 pages/__init__.py create mode 100644 pages/permissions.py diff --git a/pages/__init__.py b/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/permissions.py b/pages/permissions.py new file mode 100644 index 0000000..d196a85 --- /dev/null +++ b/pages/permissions.py @@ -0,0 +1,34 @@ +from st_pages import Page, Section + + +def main_page(): + return [Page("main.py", "Hummingbot Dashboard", "πŸ“Š"),] + + +def public_pages(): + return [ + Section("Config Generator", "πŸŽ›οΈ"), + Page("frontend/pages/config/pmm_simple/app.py", "PMM Simple", "πŸ‘¨β€πŸ«"), + Page("frontend/pages/config/pmm_dynamic/app.py", "PMM Dynamic", "πŸ‘©β€πŸ«"), + Page("frontend/pages/config/dman_maker_v2/app.py", "D-Man Maker V2", "πŸ€–"), + Page("frontend/pages/config/bollinger_v1/app.py", "Bollinger V1", "πŸ“ˆ"), + Page("frontend/pages/config/macd_bb_v1/app.py", "MACD_BB V1", "πŸ“Š"), + Page("frontend/pages/config/supertrend_v1/app.py", "SuperTrend V1", "πŸ‘¨β€πŸ”¬"), + Page("frontend/pages/config/xemm_controller/app.py", "XEMM Controller", "⚑️"), + Section("Data", "πŸ’Ύ"), + Page("frontend/pages/data/download_candles/app.py", "Download Candles", "πŸ’Ή"), + Section("Community Pages", "πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦"), + Page("frontend/pages/data/token_spreads/app.py", "Token Spreads", "πŸ§™"), + Page("frontend/pages/data/tvl_vs_mcap/app.py", "TVL vs Market Cap", "πŸ¦‰"), + ] + + +def private_pages(): + return [ + Section("Bot Orchestration", "πŸ™"), + Page("frontend/pages/orchestration/instances/app.py", "Instances", "πŸ¦…"), + Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy V2", "πŸš€"), + Page("frontend/pages/orchestration/credentials/app.py", "Credentials", "πŸ”‘"), + Page("frontend/pages/orchestration/portfolio/app.py", "Portfolio", "πŸ’°"), + + ] From b462c099bcafbca33521ed340ffb481eb3386d91 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 9 Jul 2024 18:11:52 +0300 Subject: [PATCH 08/18] (feat) remove empty space --- pages/permissions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/permissions.py b/pages/permissions.py index d196a85..2c4b2a1 100644 --- a/pages/permissions.py +++ b/pages/permissions.py @@ -30,5 +30,4 @@ def private_pages(): Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy V2", "πŸš€"), Page("frontend/pages/orchestration/credentials/app.py", "Credentials", "πŸ”‘"), Page("frontend/pages/orchestration/portfolio/app.py", "Portfolio", "πŸ’°"), - ] From fb6e843f02790c397c83a1aa253e82845a9aaa90 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 22 Jul 2024 23:51:27 +0300 Subject: [PATCH 09/18] (feat) add more records to candles --- bots/controllers/directional_trading/macd_bb_v1.py | 2 +- bots/controllers/market_making/pmm_dynamic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bots/controllers/directional_trading/macd_bb_v1.py b/bots/controllers/directional_trading/macd_bb_v1.py index 0dfc905..a516472 100644 --- a/bots/controllers/directional_trading/macd_bb_v1.py +++ b/bots/controllers/directional_trading/macd_bb_v1.py @@ -84,7 +84,7 @@ class MACDBBV1Controller(DirectionalTradingControllerBase): def __init__(self, config: MACDBBV1ControllerConfig, *args, **kwargs): self.config = config - self.max_records = max(config.macd_slow, config.macd_fast, config.macd_signal, config.bb_length) + self.max_records = max(config.macd_slow, config.macd_fast, config.macd_signal, config.bb_length) + 200 if len(self.config.candles_config) == 0: self.config.candles_config = [CandlesConfig( connector=config.candles_connector, diff --git a/bots/controllers/market_making/pmm_dynamic.py b/bots/controllers/market_making/pmm_dynamic.py index 9d02733..3b90fad 100644 --- a/bots/controllers/market_making/pmm_dynamic.py +++ b/bots/controllers/market_making/pmm_dynamic.py @@ -87,7 +87,7 @@ class PMMDynamicController(MarketMakingControllerBase): """ def __init__(self, config: PMMDynamicControllerConfig, *args, **kwargs): self.config = config - self.max_records = max(config.macd_slow, config.macd_fast, config.macd_signal, config.natr_length) + 10 + self.max_records = max(config.macd_slow, config.macd_fast, config.macd_signal, config.natr_length) + 200 if len(self.config.candles_config) == 0: self.config.candles_config = [CandlesConfig( connector=config.candles_connector, From 86675d64d0910b7fa9d4f75e26cc18e51ad86caa Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:01:40 +0300 Subject: [PATCH 10/18] (feat) update directional trading --- pages/config/bollinger_v1/app.py | 20 ++++++------ pages/config/bollinger_v1/user_inputs.py | 4 ++- pages/config/kalman_filter_v1/app.py | 40 +++++++++++++---------- pages/config/macd_bb_v1/app.py | 19 ++++++----- pages/config/macd_bb_v1/user_inputs.py | 4 ++- pages/config/supertrend_v1/app.py | 13 ++++---- pages/config/supertrend_v1/user_inputs.py | 4 ++- 7 files changed, 58 insertions(+), 46 deletions(-) diff --git a/pages/config/bollinger_v1/app.py b/pages/config/bollinger_v1/app.py index b0bbdd0..977c26e 100644 --- a/pages/config/bollinger_v1/app.py +++ b/pages/config/bollinger_v1/app.py @@ -1,18 +1,16 @@ -import streamlit as st import pandas_ta as ta # noqa: F401 +import streamlit as st +from plotly.subplots import make_subplots from frontend.components.backtesting import backtesting_section from frontend.components.config_loader import get_default_config_loader from frontend.components.save_config import render_save_config -from frontend.pages.config.utils import get_candles -from frontend.st_utils import initialize_st_page, get_backend_api_client from frontend.pages.config.bollinger_v1.user_inputs import user_inputs -from plotly.subplots import make_subplots - +from frontend.pages.config.utils import get_candles +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization import theme from frontend.visualization.backtesting import create_backtesting_figure -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ - render_close_types +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.candles import get_candlestick_trace from frontend.visualization.indicators import get_bbands_traces, get_volume_trace from frontend.visualization.signals import get_bollinger_v1_signal_traces @@ -22,7 +20,6 @@ from frontend.visualization.utils import add_traces_to_fig initialize_st_page(title="Bollinger V1", icon="πŸ“ˆ", initial_sidebar_state="expanded") backend_api_client = get_backend_api_client() - st.text("This tool will let you create a config for Bollinger V1 and visualize the strategy.") get_default_config_loader("bollinger_v1") @@ -32,7 +29,8 @@ st.session_state["default_config"].update(inputs) st.write("### Visualizing Bollinger Bands and Trading Signals") days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) # Load candle data -candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], + interval=inputs["interval"], days=days_to_visualize) # Create a subplot with 2 rows fig = make_subplots(rows=2, cols=1, shared_xaxes=True, @@ -41,7 +39,9 @@ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) add_traces_to_fig(fig, get_bbands_traces(candles, inputs["bb_length"], inputs["bb_std"]), row=1, col=1) -add_traces_to_fig(fig, get_bollinger_v1_signal_traces(candles, inputs["bb_length"], inputs["bb_std"], inputs["bb_long_threshold"], inputs["bb_short_threshold"]), row=1, col=1) +add_traces_to_fig(fig, get_bollinger_v1_signal_traces(candles, inputs["bb_length"], inputs["bb_std"], + inputs["bb_long_threshold"], inputs["bb_short_threshold"]), row=1, + col=1) add_traces_to_fig(fig, [get_volume_trace(candles)], row=2, col=1) fig.update_layout(**theme.get_default_layout()) diff --git a/pages/config/bollinger_v1/user_inputs.py b/pages/config/bollinger_v1/user_inputs.py index 8bc8988..d5a7e0a 100644 --- a/pages/config/bollinger_v1/user_inputs.py +++ b/pages/config/bollinger_v1/user_inputs.py @@ -1,4 +1,5 @@ import streamlit as st + from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs from frontend.components.risk_management import get_risk_management_inputs @@ -9,7 +10,8 @@ def user_inputs(): bb_std = default_config.get("bb_std", 2.0) bb_long_threshold = default_config.get("bb_long_threshold", 0.0) bb_short_threshold = default_config.get("bb_short_threshold", 1.0) - connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, \ + candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() with st.expander("Bollinger Bands Configuration", expanded=True): c1, c2, c3, c4 = st.columns(4) diff --git a/pages/config/kalman_filter_v1/app.py b/pages/config/kalman_filter_v1/app.py index 2479d18..08749dc 100644 --- a/pages/config/kalman_filter_v1/app.py +++ b/pages/config/kalman_filter_v1/app.py @@ -1,13 +1,14 @@ -import streamlit as st import pandas as pd import plotly.graph_objects as go +import streamlit as st import yaml from hummingbot.connector.connector_base import OrderType +from plotly.subplots import make_subplots from pykalman import KalmanFilter -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from backend.services.backend_api_client import BackendAPIClient -from frontend.st_utils import initialize_st_page, get_backend_api_client +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from frontend.st_utils import get_backend_api_client, initialize_st_page # Initialize the Streamlit page initialize_st_page(title="Kalman Filter V1", icon="πŸ“ˆ", initial_sidebar_state="expanded") @@ -18,6 +19,7 @@ def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m" backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) return backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records) + @st.cache_data def add_indicators(df, observation_covariance=1, transition_covariance=0.01, initial_state_covariance=0.001): # Add Bollinger Bands @@ -61,7 +63,6 @@ with c3: with c4: max_records = st.number_input("Max Records", min_value=100, max_value=10000, value=1000) - st.write("## Positions Configuration") c1, c2, c3, c4 = st.columns(4) with c1: @@ -87,28 +88,25 @@ with c1: with c2: transition_covariance = st.number_input("Transition Covariance", value=0.001, step=0.0001, format="%.4f") - # Load candle data -candle_data = get_candles(connector_name=candles_connector, trading_pair=candles_trading_pair, interval=interval, max_records=max_records) +candle_data = get_candles(connector_name=candles_connector, trading_pair=candles_trading_pair, interval=interval, + max_records=max_records) df = pd.DataFrame(candle_data) df.index = pd.to_datetime(df['timestamp'], unit='s') candles_processed = add_indicators(df, observation_covariance, transition_covariance) - - # Prepare data for signals signals = candles_processed[candles_processed['signal'] != 0] buy_signals = signals[signals['signal'] == 1] sell_signals = signals[signals['signal'] == -1] -from plotly.subplots import make_subplots # Define your color palette tech_colors = { - 'upper_band': '#4682B4', # Steel Blue for the Upper Bollinger Band + 'upper_band': '#4682B4', # Steel Blue for the Upper Bollinger Band 'middle_band': '#FFD700', # Gold for the Middle Bollinger Band - 'lower_band': '#32CD32', # Green for the Lower Bollinger Band - 'buy_signal': '#1E90FF', # Dodger Blue for Buy Signals + 'lower_band': '#32CD32', # Green for the Lower Bollinger Band + 'buy_signal': '#1E90FF', # Dodger Blue for Buy Signals 'sell_signal': '#FF0000', # Red for Sell Signals } @@ -127,9 +125,15 @@ fig.add_trace(go.Candlestick(x=candles_processed.index, row=1, col=1) # Bollinger Bands -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf_upper'], line=dict(color=tech_colors['upper_band']), name='Upper Band'), row=1, col=1) -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf'], line=dict(color=tech_colors['middle_band']), name='Middle Band'), row=1, col=1) -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed['kf_lower'], line=dict(color=tech_colors['lower_band']), name='Lower Band'), row=1, col=1) +fig.add_trace( + go.Scatter(x=candles_processed.index, y=candles_processed['kf_upper'], line=dict(color=tech_colors['upper_band']), + name='Upper Band'), row=1, col=1) +fig.add_trace( + go.Scatter(x=candles_processed.index, y=candles_processed['kf'], line=dict(color=tech_colors['middle_band']), + name='Middle Band'), row=1, col=1) +fig.add_trace( + go.Scatter(x=candles_processed.index, y=candles_processed['kf_lower'], line=dict(color=tech_colors['lower_band']), + name='Lower Band'), row=1, col=1) # Signals plot fig.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['close'], mode='markers', @@ -140,7 +144,8 @@ fig.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['close'], mode='ma name='Sell Signal'), row=1, col=1) fig.add_trace(go.Scatter(x=signals.index, y=signals['signal'], mode='markers', - marker=dict(color=signals['signal'].map({1: tech_colors['buy_signal'], -1: tech_colors['sell_signal']}), size=10), + marker=dict(color=signals['signal'].map( + {1: tech_colors['buy_signal'], -1: tech_colors['sell_signal']}), size=10), showlegend=False), row=2, col=1) # Update layout @@ -218,8 +223,7 @@ with c3: ) upload_config_to_backend = st.button("Upload Config to BackendAPI") - if upload_config_to_backend: backend_api_client = get_backend_api_client() backend_api_client.add_controller_config(config) - st.success("Config uploaded successfully!") \ No newline at end of file + st.success("Config uploaded successfully!") diff --git a/pages/config/macd_bb_v1/app.py b/pages/config/macd_bb_v1/app.py index 3857587..0237292 100644 --- a/pages/config/macd_bb_v1/app.py +++ b/pages/config/macd_bb_v1/app.py @@ -6,11 +6,10 @@ from frontend.components.config_loader import get_default_config_loader from frontend.components.save_config import render_save_config from frontend.pages.config.macd_bb_v1.user_inputs import user_inputs from frontend.pages.config.utils import get_candles -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization import theme from frontend.visualization.backtesting import create_backtesting_figure -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ - render_close_types +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.candles import get_candlestick_trace from frontend.visualization.indicators import get_bbands_traces, get_macd_traces from frontend.visualization.signals import get_macdbb_v1_signal_traces @@ -25,11 +24,11 @@ get_default_config_loader("macd_bb_v1") inputs = user_inputs() st.session_state["default_config"].update(inputs) - st.write("### Visualizing MACD Bollinger Trading Signals") days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) # Load candle data -candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], + interval=inputs["interval"], days=days_to_visualize) # Create a subplot with 2 rows fig = make_subplots(rows=2, cols=1, shared_xaxes=True, @@ -38,9 +37,12 @@ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) add_traces_to_fig(fig, get_bbands_traces(candles, inputs["bb_length"], inputs["bb_std"]), row=1, col=1) add_traces_to_fig(fig, get_macdbb_v1_signal_traces(df=candles, bb_length=inputs["bb_length"], bb_std=inputs["bb_std"], - bb_long_threshold=inputs["bb_long_threshold"], bb_short_threshold=inputs["bb_short_threshold"], - macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=1, col=1) -add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=2, col=1) + bb_long_threshold=inputs["bb_long_threshold"], + bb_short_threshold=inputs["bb_short_threshold"], + macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], + macd_signal=inputs["macd_signal"]), row=1, col=1) +add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], + macd_signal=inputs["macd_signal"]), row=2, col=1) fig.update_layout(**theme.get_default_layout()) # Use Streamlit's functionality to display the plot @@ -61,4 +63,3 @@ if bt_results: render_close_types(bt_results["results"]) st.write("---") render_save_config(st.session_state["default_config"]["id"], st.session_state["default_config"]) - diff --git a/pages/config/macd_bb_v1/user_inputs.py b/pages/config/macd_bb_v1/user_inputs.py index 3e7e212..b928a82 100644 --- a/pages/config/macd_bb_v1/user_inputs.py +++ b/pages/config/macd_bb_v1/user_inputs.py @@ -1,4 +1,5 @@ import streamlit as st + from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs from frontend.components.risk_management import get_risk_management_inputs @@ -12,7 +13,8 @@ def user_inputs(): macd_fast = default_config.get("macd_fast", 21) macd_slow = default_config.get("macd_slow", 42) macd_signal = default_config.get("macd_signal", 9) - connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode,\ + candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() with st.expander("MACD Bollinger Configuration", expanded=True): c1, c2, c3, c4, c5, c6, c7 = st.columns(7) diff --git a/pages/config/supertrend_v1/app.py b/pages/config/supertrend_v1/app.py index 83e6633..97e68bf 100644 --- a/pages/config/supertrend_v1/app.py +++ b/pages/config/supertrend_v1/app.py @@ -6,13 +6,12 @@ from frontend.components.config_loader import get_default_config_loader from frontend.components.save_config import render_save_config from frontend.pages.config.supertrend_v1.user_inputs import user_inputs from frontend.pages.config.utils import get_candles -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization import theme from frontend.visualization.backtesting import create_backtesting_figure -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ - render_close_types +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.candles import get_candlestick_trace -from frontend.visualization.indicators import get_volume_trace, get_supertrend_traces +from frontend.visualization.indicators import get_supertrend_traces, get_volume_trace from frontend.visualization.signals import get_supertrend_v1_signal_traces from frontend.visualization.utils import add_traces_to_fig @@ -28,7 +27,8 @@ st.session_state["default_config"].update(inputs) st.write("### Visualizing Supertrend Trading Signals") days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) # Load candle data -candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], + interval=inputs["interval"], days=days_to_visualize) # Create a subplot with 2 rows fig = make_subplots(rows=2, cols=1, shared_xaxes=True, @@ -36,7 +36,8 @@ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.8, 0.2]) add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) add_traces_to_fig(fig, get_supertrend_traces(candles, inputs["length"], inputs["multiplier"]), row=1, col=1) -add_traces_to_fig(fig, get_supertrend_v1_signal_traces(candles, inputs["length"], inputs["multiplier"], inputs["percentage_threshold"]), row=1, col=1) +add_traces_to_fig(fig, get_supertrend_v1_signal_traces(candles, inputs["length"], inputs["multiplier"], + inputs["percentage_threshold"]), row=1, col=1) add_traces_to_fig(fig, [get_volume_trace(candles)], row=2, col=1) layout_settings = theme.get_default_layout() diff --git a/pages/config/supertrend_v1/user_inputs.py b/pages/config/supertrend_v1/user_inputs.py index 05245eb..d4a9436 100644 --- a/pages/config/supertrend_v1/user_inputs.py +++ b/pages/config/supertrend_v1/user_inputs.py @@ -1,4 +1,5 @@ import streamlit as st + from frontend.components.directional_trading_general_inputs import get_directional_trading_general_inputs from frontend.components.risk_management import get_risk_management_inputs @@ -8,7 +9,8 @@ def user_inputs(): length = default_config.get("length", 20) multiplier = default_config.get("multiplier", 3.0) percentage_threshold = default_config.get("percentage_threshold", 0.5) - connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() + connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, \ + candles_connector_name, candles_trading_pair, interval = get_directional_trading_general_inputs() sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() with st.expander("SuperTrend Configuration", expanded=True): From 4c4abd330709074810f920b65026c4980ad8b1c4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:01:52 +0300 Subject: [PATCH 11/18] (feat) update utils and permissions --- pages/config/utils.py | 9 ++++----- pages/permissions.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pages/config/utils.py b/pages/config/utils.py index cfebbd2..591cc68 100644 --- a/pages/config/utils.py +++ b/pages/config/utils.py @@ -1,10 +1,10 @@ import datetime -import streamlit as st import pandas as pd +import streamlit as st -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from backend.services.backend_api_client import BackendAPIClient +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT def get_max_records(days_to_download: int, interval: str) -> int: @@ -21,8 +21,7 @@ def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m" start_time = end_time - datetime.timedelta(days=days) df = pd.DataFrame(backend_client.get_historical_candles(connector_name, trading_pair, interval, - start_time=int(start_time.timestamp() * 1000), - end_time=int(end_time.timestamp() * 1000))) + start_time=int(start_time.timestamp()), + end_time=int(end_time.timestamp()))) df.index = pd.to_datetime(df.timestamp, unit='s') return df - diff --git a/pages/permissions.py b/pages/permissions.py index 2c4b2a1..bca3e9e 100644 --- a/pages/permissions.py +++ b/pages/permissions.py @@ -2,7 +2,7 @@ from st_pages import Page, Section def main_page(): - return [Page("main.py", "Hummingbot Dashboard", "πŸ“Š"),] + return [Page("main.py", "Hummingbot Dashboard", "πŸ“Š")] def public_pages(): From 681b75055deed8f1c9b00df007f0ee203319d353 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:02:00 +0300 Subject: [PATCH 12/18] (feat) update mm controllers --- pages/config/dman_maker_v2/app.py | 11 ++--- pages/config/dman_maker_v2/user_inputs.py | 6 ++- pages/config/pmm_dynamic/app.py | 41 +++++++++++-------- .../spread_and_price_multipliers.py | 2 +- pages/config/pmm_dynamic/user_inputs.py | 3 +- pages/config/pmm_simple/app.py | 14 +++---- pages/config/pmm_simple/user_inputs.py | 8 ++-- 7 files changed, 46 insertions(+), 39 deletions(-) diff --git a/pages/config/dman_maker_v2/app.py b/pages/config/dman_maker_v2/app.py index 830fe99..0649f9c 100644 --- a/pages/config/dman_maker_v2/app.py +++ b/pages/config/dman_maker_v2/app.py @@ -1,16 +1,13 @@ import streamlit as st -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from backend.services.backend_api_client import BackendAPIClient from frontend.components.backtesting import backtesting_section from frontend.components.config_loader import get_default_config_loader from frontend.components.dca_distribution import get_dca_distribution_inputs from frontend.components.save_config import render_save_config from frontend.pages.config.dman_maker_v2.user_inputs import user_inputs -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization.backtesting import create_backtesting_figure -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_accuracy_metrics, \ - render_close_types +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.dca_builder import create_dca_graph from frontend.visualization.executors_distribution import create_executors_distribution_traces @@ -18,14 +15,14 @@ from frontend.visualization.executors_distribution import create_executors_distr initialize_st_page(title="D-Man Maker V2", icon="πŸ§™β€β™‚οΈ") backend_api_client = get_backend_api_client() - # Page content st.text("This tool will let you create a config for D-Man Maker V2 and upload it to the BackendAPI.") get_default_config_loader("dman_maker_v2") inputs = user_inputs() with st.expander("Executor Distribution:", expanded=True): - fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], + inputs["sell_amounts_pct"], inputs["total_amount_quote"]) st.plotly_chart(fig, use_container_width=True) dca_inputs = get_dca_distribution_inputs() diff --git a/pages/config/dman_maker_v2/user_inputs.py b/pages/config/dman_maker_v2/user_inputs.py index 5ccf4d9..8f207f3 100644 --- a/pages/config/dman_maker_v2/user_inputs.py +++ b/pages/config/dman_maker_v2/user_inputs.py @@ -5,8 +5,10 @@ from frontend.components.market_making_general_inputs import get_market_making_g def user_inputs(): - connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, _, _, _ = get_market_making_general_inputs() - buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time,\ + executor_refresh_time, _, _, _ = get_market_making_general_inputs() + buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, \ + sell_order_amounts_pct = get_executors_distribution_inputs() with st.expander("Custom D-Man Maker V2 Settings"): c1, c2 = st.columns(2) with c1: diff --git a/pages/config/pmm_dynamic/app.py b/pages/config/pmm_dynamic/app.py index f154444..3c7eb52 100644 --- a/pages/config/pmm_dynamic/app.py +++ b/pages/config/pmm_dynamic/app.py @@ -1,23 +1,21 @@ -import streamlit as st import plotly.graph_objects as go +import streamlit as st from plotly.subplots import make_subplots -from frontend.components.config_loader import get_default_config_loader -from frontend.components.executors_distribution import get_executors_distribution_inputs -from frontend.components.save_config import render_save_config - # Import submodules from frontend.components.backtesting import backtesting_section +from frontend.components.config_loader import get_default_config_loader +from frontend.components.executors_distribution import get_executors_distribution_inputs +from frontend.components.save_config import render_save_config from frontend.pages.config.pmm_dynamic.spread_and_price_multipliers import get_pmm_dynamic_multipliers from frontend.pages.config.pmm_dynamic.user_inputs import user_inputs from frontend.pages.config.utils import get_candles -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization import theme from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.candles import get_candlestick_trace from frontend.visualization.executors_distribution import create_executors_distribution_traces -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_close_types, \ - render_accuracy_metrics from frontend.visualization.indicators import get_macd_traces from frontend.visualization.utils import add_traces_to_fig @@ -35,16 +33,25 @@ st.text("The MACD is used to shift the mid price and the NATR to make the spread "In the order distributions graph, we are going to see the values of the orders affected by the average NATR") days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) # Load candle data -candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], days=days_to_visualize) +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], + interval=inputs["interval"], days=days_to_visualize) with st.expander("Visualizing PMM Dynamic Indicators", expanded=True): fig = make_subplots(rows=4, cols=1, shared_xaxes=True, - vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'MACD', "Price Multiplier", "Spreads Multiplier"), + vertical_spacing=0.02, subplot_titles=("Candlestick with Bollinger Bands", "MACD", + "Price Multiplier", "Spreads Multiplier"), row_heights=[0.8, 0.2, 0.2, 0.2]) add_traces_to_fig(fig, [get_candlestick_trace(candles)], row=1, col=1) - add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], macd_signal=inputs["macd_signal"]), row=2, col=1) - price_multiplier, spreads_multiplier = get_pmm_dynamic_multipliers(candles, inputs["macd_fast"], inputs["macd_slow"], inputs["macd_signal"], inputs["natr_length"]) - add_traces_to_fig(fig, [go.Scatter(x=candles.index, y=price_multiplier, name="Price Multiplier", line=dict(color="blue"))], row=3, col=1) - add_traces_to_fig(fig, [go.Scatter(x=candles.index, y=spreads_multiplier, name="Base Spread", line=dict(color="red"))], row=4, col=1) + add_traces_to_fig(fig, get_macd_traces(df=candles, macd_fast=inputs["macd_fast"], macd_slow=inputs["macd_slow"], + macd_signal=inputs["macd_signal"]), row=2, col=1) + price_multiplier, spreads_multiplier = get_pmm_dynamic_multipliers(candles, inputs["macd_fast"], + inputs["macd_slow"], inputs["macd_signal"], + inputs["natr_length"]) + add_traces_to_fig(fig, [ + go.Scatter(x=candles.index, y=price_multiplier, name="Price Multiplier", line=dict(color="blue"))], row=3, + col=1) + add_traces_to_fig(fig, + [go.Scatter(x=candles.index, y=spreads_multiplier, name="Base Spread", line=dict(color="red"))], + row=4, col=1) fig.update_layout(**theme.get_default_layout(height=1000)) fig.update_yaxes(tickformat=".2%", row=3, col=1) fig.update_yaxes(tickformat=".2%", row=4, col=1) @@ -53,7 +60,8 @@ with st.expander("Visualizing PMM Dynamic Indicators", expanded=True): st.write("### Executors Distribution") st.write("The order distributions are affected by the average NATR. This means that if the first order has a spread of " "1 and the NATR is 0.005, the first order will have a spread of 0.5% of the mid price.") -buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs(use_custom_spread_units=True) +buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, \ + sell_order_amounts_pct = get_executors_distribution_inputs(use_custom_spread_units=True) inputs["buy_spreads"] = [spread * 100 for spread in buy_spread_distributions] inputs["sell_spreads"] = [spread * 100 for spread in sell_spread_distributions] inputs["buy_amounts_pct"] = buy_order_amounts_pct @@ -64,7 +72,8 @@ with st.expander("Executor Distribution:", expanded=True): buy_spreads = [spread * natr_avarage for spread in inputs["buy_spreads"]] sell_spreads = [spread * natr_avarage for spread in inputs["sell_spreads"]] st.write(f"Average NATR: {natr_avarage:.2%}") - fig = create_executors_distribution_traces(buy_spreads, sell_spreads, inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + fig = create_executors_distribution_traces(buy_spreads, sell_spreads, inputs["buy_amounts_pct"], + inputs["sell_amounts_pct"], inputs["total_amount_quote"]) st.plotly_chart(fig, use_container_width=True) bt_results = backtesting_section(inputs, backend_api_client) diff --git a/pages/config/pmm_dynamic/spread_and_price_multipliers.py b/pages/config/pmm_dynamic/spread_and_price_multipliers.py index dfb85f2..efd99c5 100644 --- a/pages/config/pmm_dynamic/spread_and_price_multipliers.py +++ b/pages/config/pmm_dynamic/spread_and_price_multipliers.py @@ -1,4 +1,4 @@ -import pandas_ta as ta # noqa: F401 +import pandas_ta as ta # noqa: F401 def get_pmm_dynamic_multipliers(df, macd_fast, macd_slow, macd_signal, natr_length): diff --git a/pages/config/pmm_dynamic/user_inputs.py b/pages/config/pmm_dynamic/user_inputs.py index 21e7736..1487e42 100644 --- a/pages/config/pmm_dynamic/user_inputs.py +++ b/pages/config/pmm_dynamic/user_inputs.py @@ -10,7 +10,8 @@ def user_inputs(): macd_slow = default_config.get("macd_slow", 42) macd_signal = default_config.get("macd_signal", 9) natr_length = default_config.get("natr_length", 14) - connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, candles_connector, candles_trading_pair, interval = get_market_making_general_inputs(custom_candles=True) + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, \ + candles_connector, candles_trading_pair, interval = get_market_making_general_inputs(custom_candles=True) sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() with st.expander("PMM Dynamic Configuration", expanded=True): c1, c2, c3, c4 = st.columns(4) diff --git a/pages/config/pmm_simple/app.py b/pages/config/pmm_simple/app.py index 445c362..e6f2598 100644 --- a/pages/config/pmm_simple/app.py +++ b/pages/config/pmm_simple/app.py @@ -1,23 +1,20 @@ import streamlit as st -from backend.services.backend_api_client import BackendAPIClient -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT + +from frontend.components.backtesting import backtesting_section from frontend.components.config_loader import get_default_config_loader from frontend.components.save_config import render_save_config # Import submodules from frontend.pages.config.pmm_simple.user_inputs import user_inputs -from frontend.components.backtesting import backtesting_section -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page from frontend.visualization.backtesting import create_backtesting_figure +from frontend.visualization.backtesting_metrics import render_accuracy_metrics, render_backtesting_metrics, render_close_types from frontend.visualization.executors_distribution import create_executors_distribution_traces -from frontend.visualization.backtesting_metrics import render_backtesting_metrics, render_close_types, \ - render_accuracy_metrics # Initialize the Streamlit page initialize_st_page(title="PMM Simple", icon="πŸ‘¨β€πŸ«") backend_api_client = get_backend_api_client() - # Page content st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") get_default_config_loader("pmm_simple") @@ -26,7 +23,8 @@ inputs = user_inputs() st.session_state["default_config"].update(inputs) with st.expander("Executor Distribution:", expanded=True): - fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], + inputs["sell_amounts_pct"], inputs["total_amount_quote"]) st.plotly_chart(fig, use_container_width=True) bt_results = backtesting_section(inputs, backend_api_client) diff --git a/pages/config/pmm_simple/user_inputs.py b/pages/config/pmm_simple/user_inputs.py index b160481..6c4f62d 100644 --- a/pages/config/pmm_simple/user_inputs.py +++ b/pages/config/pmm_simple/user_inputs.py @@ -1,13 +1,13 @@ -import streamlit as st - from frontend.components.executors_distribution import get_executors_distribution_inputs from frontend.components.market_making_general_inputs import get_market_making_general_inputs from frontend.components.risk_management import get_risk_management_inputs def user_inputs(): - connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, _, _, _ = get_market_making_general_inputs() - buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, \ + executor_refresh_time, _, _, _ = get_market_making_general_inputs() + buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, \ + sell_order_amounts_pct = get_executors_distribution_inputs() sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type = get_risk_management_inputs() # Create the config config = { From 5fecfb37b96820ab9efe653b67e6e292aa06863a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:02:05 +0300 Subject: [PATCH 13/18] (feat) update xemm --- pages/config/xemm_controller/app.py | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/pages/config/xemm_controller/app.py b/pages/config/xemm_controller/app.py index f8431e8..f916dad 100644 --- a/pages/config/xemm_controller/app.py +++ b/pages/config/xemm_controller/app.py @@ -1,10 +1,8 @@ -import streamlit as st import plotly.graph_objects as go +import streamlit as st import yaml -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from backend.services.backend_api_client import BackendAPIClient -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page # Initialize the Streamlit page initialize_st_page(title="XEMM Multiple Levels", icon="⚑️") @@ -29,9 +27,9 @@ with c4: c41, c42 = st.columns([1, 1]) for i in range(buy_maker_levels): with c41: - target_profitability = st.number_input(f"Target Profitability {i+1} B% ", value=0.3, step=0.01) + target_profitability = st.number_input(f"Target Profitability {i + 1} B% ", value=0.3, step=0.01) with c42: - amount = st.number_input(f"Amount {i+1}B Quote", value=10, step=1) + amount = st.number_input(f"Amount {i + 1}B Quote", value=10, step=1) buy_targets_amounts.append([target_profitability / 100, amount]) with c5: sell_maker_levels = st.number_input("Sell Maker Levels", value=1, step=1) @@ -39,9 +37,9 @@ with c5: c51, c52 = st.columns([1, 1]) for i in range(sell_maker_levels): with c51: - target_profitability = st.number_input(f"Target Profitability {i+1}S %", value=0.3, step=0.001) + target_profitability = st.number_input(f"Target Profitability {i + 1}S %", value=0.3, step=0.001) with c52: - amount = st.number_input(f"Amount {i+1} S Quote", value=10, step=1) + amount = st.number_input(f"Amount {i + 1} S Quote", value=10, step=1) sell_targets_amounts.append([target_profitability / 100, amount]) @@ -82,7 +80,8 @@ def create_order_graph(order_type, targets, min_profit, max_profit): title=f"{order_type.capitalize()} Order Distribution with Profitability Targets", xaxis=dict( title="Profitability (%)", - range=[0, max(max(x_values + [min_profit_percent, max_profit_percent]) + 0.1, 1)] # Adjust range to include a buffer + range=[0, max(max(x_values + [min_profit_percent, max_profit_percent]) + 0.1, 1)] + # Adjust range to include a buffer ), yaxis=dict( title="Order Amount" @@ -93,6 +92,7 @@ def create_order_graph(order_type, targets, min_profit, max_profit): return fig + # Use the function for both buy and sell orders buy_order_fig = create_order_graph('buy', buy_targets_amounts, min_profitability, max_profitability) sell_order_fig = create_order_graph('sell', sell_targets_amounts, min_profitability, max_profitability) @@ -104,31 +104,31 @@ st.plotly_chart(sell_order_fig, use_container_width=True) # Display in Streamlit c1, c2, c3 = st.columns([2, 2, 1]) with c1: - config_base = st.text_input("Config Base", value=f"xemm-{maker_connector}-{taker_connector}-{maker_trading_pair.split('-')[0]}") + config_base = st.text_input("Config Base", + value=f"xemm-{maker_connector}-{taker_connector}-{maker_trading_pair.split('-')[0]}") with c2: config_tag = st.text_input("Config Tag", value="1.1") id = f"{config_base}_{config_tag}" config = { - "id": id.lower(), - "controller_name": "xemm_multiple_levels", - "controller_type": "generic", - "maker_connector": maker_connector, - "maker_trading_pair": maker_trading_pair, - "taker_connector": taker_connector, - "taker_trading_pair": taker_trading_pair, - "min_profitability": min_profitability, - "max_profitability": max_profitability, - "buy_levels_targets_amount": buy_targets_amounts, - "sell_levels_targets_amount": sell_targets_amounts + "id": id.lower(), + "controller_name": "xemm_multiple_levels", + "controller_type": "generic", + "maker_connector": maker_connector, + "maker_trading_pair": maker_trading_pair, + "taker_connector": taker_connector, + "taker_trading_pair": taker_trading_pair, + "min_profitability": min_profitability, + "max_profitability": max_profitability, + "buy_levels_targets_amount": buy_targets_amounts, + "sell_levels_targets_amount": sell_targets_amounts } yaml_config = yaml.dump(config, default_flow_style=False) with c3: upload_config_to_backend = st.button("Upload Config to BackendAPI") - if upload_config_to_backend: backend_api_client = get_backend_api_client() backend_api_client.add_controller_config(config) - st.success("Config uploaded successfully!") \ No newline at end of file + st.success("Config uploaded successfully!") From 4fb0d86d96a2d879909578a974c4ad534f53d5d0 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:02:12 +0300 Subject: [PATCH 14/18] (feat) update data --- pages/data/download_candles/app.py | 13 ++++++++----- pages/data/token_spreads/app.py | 11 +++++++++-- pages/data/tvl_vs_mcap/app.py | 18 ++++++++++++------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/pages/data/download_candles/app.py b/pages/data/download_candles/app.py index d03aca0..5245458 100644 --- a/pages/data/download_candles/app.py +++ b/pages/data/download_candles/app.py @@ -1,9 +1,10 @@ -import streamlit as st from datetime import datetime, time + import pandas as pd import plotly.graph_objects as go +import streamlit as st -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page # Initialize Streamlit page initialize_st_page(title="Download Candles", icon="πŸ’Ύ") @@ -11,7 +12,9 @@ backend_api_client = get_backend_api_client() c1, c2, c3, c4 = st.columns([2, 2, 2, 0.5]) with c1: - connector = st.selectbox("Exchange", ["binance_perpetual", "binance", "gate_io", "gate_io_perpetual", "kucoin", "ascend_ex"], index=0) + connector = st.selectbox("Exchange", + ["binance_perpetual", "binance", "gate_io", "gate_io_perpetual", "kucoin", "ascend_ex"], + index=0) trading_pair = st.text_input("Trading Pair", value="BTC-USDT") with c2: interval = st.selectbox("Interval", options=["1m", "3m", "5m", "15m", "1h", "4h", "1d", "1s"]) @@ -29,8 +32,8 @@ if get_data_button: connector=connector, trading_pair=trading_pair, interval=interval, - start_time=int(start_datetime.timestamp()) * 1000, - end_time=int(end_datetime.timestamp()) * 1000 + start_time=int(start_datetime.timestamp()), + end_time=int(end_datetime.timestamp()) ) candles_df = pd.DataFrame(candles) diff --git a/pages/data/token_spreads/app.py b/pages/data/token_spreads/app.py index 56a30f6..5581f68 100644 --- a/pages/data/token_spreads/app.py +++ b/pages/data/token_spreads/app.py @@ -1,5 +1,6 @@ -import streamlit as st import plotly.express as px +import streamlit as st + import CONFIG from backend.services.coingecko_client import CoinGeckoClient from backend.services.miner_client import MinerClient @@ -11,22 +12,27 @@ initialize_st_page(title="Token Spreads", icon="πŸ§™") cg_utils = CoinGeckoClient() miner_utils = MinerClient() + @st.cache_data def get_all_coins_df(): return cg_utils.get_all_coins_df() + @st.cache_data def get_all_exchanges_df(): return cg_utils.get_all_exchanges_df() + @st.cache_data def get_miner_stats_df(): return miner_utils.get_miner_stats_df() + @st.cache_data def get_coin_tickers_by_id_list(coins_id: list): return cg_utils.get_coin_tickers_by_id_list(coins_id) + with st.spinner(text='In progress'): exchanges_df = get_all_exchanges_df() coins_df = get_all_coins_df() @@ -43,7 +49,8 @@ tokens = st.multiselect( coins_id = coins_df.loc[coins_df["name"].isin(tokens), "id"].tolist() coin_tickers_df = get_coin_tickers_by_id_list(coins_id) -coin_tickers_df["coin_name"] = coin_tickers_df.apply(lambda x: coins_df.loc[coins_df["id"] == x.token_id, "name"].item(), axis=1) +coin_tickers_df["coin_name"] = coin_tickers_df.apply( + lambda x: coins_df.loc[coins_df["id"] == x.token_id, "name"].item(), axis=1) exchanges = st.multiselect( "Select the exchanges to analyze:", diff --git a/pages/data/tvl_vs_mcap/app.py b/pages/data/tvl_vs_mcap/app.py index 48e276f..d6e7de4 100644 --- a/pages/data/tvl_vs_mcap/app.py +++ b/pages/data/tvl_vs_mcap/app.py @@ -1,7 +1,7 @@ import numpy as np -import streamlit as st import pandas as pd import plotly.express as px +import streamlit as st from defillama import DefiLlama from frontend.st_utils import initialize_st_page @@ -12,16 +12,21 @@ initialize_st_page(title="TVL vs Market Cap", icon="πŸ¦‰") MIN_TVL = 1000000. MIN_MCAP = 1000000. + @st.cache_data def get_tvl_mcap_data(): llama = DefiLlama() df = pd.DataFrame(llama.get_all_protocols()) - tvl_mcap_df = df.loc[(df["tvl"]>0) & (df["mcap"]>0), ["name", "tvl", "mcap", "chain", "category", "slug"]].sort_values(by=["mcap"], ascending=False) - return tvl_mcap_df[(tvl_mcap_df["tvl"] > MIN_TVL) & (tvl_mcap_df["mcap"]> MIN_MCAP)] + tvl_mcap_df = df.loc[ + (df["tvl"] > 0) & (df["mcap"] > 0), ["name", "tvl", "mcap", "chain", "category", "slug"]].sort_values( + by=["mcap"], ascending=False) + return tvl_mcap_df[(tvl_mcap_df["tvl"] > MIN_TVL) & (tvl_mcap_df["mcap"] > MIN_MCAP)] + def get_protocols_by_chain_category(protocols: pd.DataFrame, group_by: list, nth: list): return protocols.sort_values('tvl', ascending=False).groupby(group_by).nth(nth).reset_index() + with st.spinner(text='In progress'): tvl_mcap_df = get_tvl_mcap_data() @@ -57,7 +62,8 @@ st.write("### SunBurst 🌞") groupby = st.selectbox('Group by:', [['chain', 'category'], ['category', 'chain']]) nth = st.slider('Top protocols by Category', min_value=1, max_value=5) -proto_agg = get_protocols_by_chain_category(tvl_mcap_df[tvl_mcap_df["chain"].isin(chains)], groupby, np.arange(0, nth, 1).tolist()) +proto_agg = get_protocols_by_chain_category(tvl_mcap_df[tvl_mcap_df["chain"].isin(chains)], + groupby, np.arange(0, nth, 1).tolist()) groupby.append("slug") sunburst = px.sunburst( proto_agg, @@ -65,6 +71,6 @@ sunburst = px.sunburst( values='tvl', height=800, title="SunBurst", - template="plotly_dark",) + template="plotly_dark", ) -st.plotly_chart(sunburst, use_container_width=True) \ No newline at end of file +st.plotly_chart(sunburst, use_container_width=True) From 44816d023892bf5535a5de56e5d9820a3e478fb9 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 15:02:31 +0300 Subject: [PATCH 15/18] (feat) update orchestration pages --- pages/orchestration/credentials/app.py | 22 ++++++++++++--------- pages/orchestration/file_manager/app.py | 1 + pages/orchestration/instances/app.py | 11 +++++++---- pages/orchestration/launch_bot_v2_st/app.py | 1 - pages/orchestration/portfolio/app.py | 13 +++++++----- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/pages/orchestration/credentials/app.py b/pages/orchestration/credentials/app.py index 666f054..3e57f88 100644 --- a/pages/orchestration/credentials/app.py +++ b/pages/orchestration/credentials/app.py @@ -1,8 +1,6 @@ -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from backend.services.backend_api_client import BackendAPIClient -from frontend.st_utils import initialize_st_page, get_backend_api_client import streamlit as st +from frontend.st_utils import get_backend_api_client, initialize_st_page initialize_st_page(title="Credentials", icon="πŸ”‘") @@ -15,6 +13,7 @@ NUM_COLUMNS = 4 def get_all_connectors_config_map(): return client.get_all_connectors_config_map() + # Section to display available accounts and credentials accounts = client.get_accounts() all_connector_config_map = get_all_connectors_config_map() @@ -58,7 +57,8 @@ with c1: with c2: # Section to delete an existing account st.header("Delete an Account") - delete_account_name = st.selectbox("Select Account to Delete", options=accounts if accounts else ["No accounts available"], ) + delete_account_name = st.selectbox("Select Account to Delete", + options=accounts if accounts else ["No accounts available"], ) if st.button("Delete Account"): if delete_account_name and delete_account_name != "No accounts available": response = client.delete_account(delete_account_name) @@ -69,11 +69,14 @@ with c2: with c3: # Section to delete a credential from an existing account st.header("Delete Credential") - delete_account_cred_name = st.selectbox("Select the credentials account", options=accounts if accounts else ["No accounts available"],) + delete_account_cred_name = st.selectbox("Select the credentials account", + options=accounts if accounts else ["No accounts available"], ) creds_for_account = [credential.split(".")[0] for credential in client.get_credentials(delete_account_cred_name)] - delete_cred_name = st.selectbox("Select a Credential to Delete", options=creds_for_account if creds_for_account else ["No credentials available"]) + delete_cred_name = st.selectbox("Select a Credential to Delete", + options=creds_for_account if creds_for_account else ["No credentials available"]) if st.button("Delete Credential"): - if (delete_account_cred_name and delete_account_cred_name != "No accounts available") and (delete_cred_name and delete_cred_name != "No credentials available"): + if (delete_account_cred_name and delete_account_cred_name != "No accounts available") and \ + (delete_cred_name and delete_cred_name != "No credentials available"): response = client.delete_credential(delete_account_cred_name, delete_cred_name) st.warning(response) else: @@ -88,7 +91,8 @@ with c1: account_name = st.selectbox("Select Account", options=accounts if accounts else ["No accounts available"]) with c2: all_connectors = list(all_connector_config_map.keys()) - binance_perpetual_index = all_connectors.index("binance_perpetual") if "binance_perpetual" in all_connectors else None + binance_perpetual_index = all_connectors.index( + "binance_perpetual") if "binance_perpetual" in all_connectors else None connector_name = st.selectbox("Select Connector", options=all_connectors, index=binance_perpetual_index) config_map = all_connector_config_map[connector_name] @@ -103,4 +107,4 @@ with cols[-1]: if st.button("Submit Credentials"): response = client.add_connector_keys(account_name, connector_name, config_inputs) if response: - st.success(response) \ No newline at end of file + st.success(response) diff --git a/pages/orchestration/file_manager/app.py b/pages/orchestration/file_manager/app.py index 8f20839..f771d54 100644 --- a/pages/orchestration/file_manager/app.py +++ b/pages/orchestration/file_manager/app.py @@ -1,4 +1,5 @@ from types import SimpleNamespace + import streamlit as st from streamlit_elements import elements, mui diff --git a/pages/orchestration/instances/app.py b/pages/orchestration/instances/app.py index d506e95..59aaa99 100644 --- a/pages/orchestration/instances/app.py +++ b/pages/orchestration/instances/app.py @@ -1,18 +1,19 @@ import time +from types import SimpleNamespace import streamlit as st from streamlit_elements import elements, mui -from types import SimpleNamespace from frontend.components.bot_performance_card import BotPerformanceCardV2 from frontend.components.dashboard import Dashboard -from frontend.st_utils import initialize_st_page, get_backend_api_client +from frontend.st_utils import get_backend_api_client, initialize_st_page # Constants for UI layout CARD_WIDTH = 12 CARD_HEIGHT = 4 NUM_CARD_COLS = 1 + def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_WIDTH, card_height: int = CARD_HEIGHT): rows = n_cards // cols + 1 x_y = [(x * card_width, y * card_height) for x in range(cols) for y in range(rows)] @@ -28,7 +29,9 @@ def update_active_bots(api_client): new_bots = set(current_active_bots.keys()) - set(stored_bots.keys()) removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys()) for bot in removed_bots: - st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot] + st.session_state.active_instances_board.bot_cards = [card for card in + st.session_state.active_instances_board.bot_cards + if card[1] != bot] positions = get_grid_positions(len(current_active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) for bot, (x, y) in zip(new_bots, positions[:len(new_bots)]): card = BotPerformanceCardV2(st.session_state.active_instances_board.dashboard, x, y, CARD_WIDTH, CARD_HEIGHT) @@ -70,4 +73,4 @@ with elements("active_instances_board"): while True: time.sleep(10) - st.rerun() \ No newline at end of file + st.rerun() diff --git a/pages/orchestration/launch_bot_v2_st/app.py b/pages/orchestration/launch_bot_v2_st/app.py index 50c2c08..0766de0 100644 --- a/pages/orchestration/launch_bot_v2_st/app.py +++ b/pages/orchestration/launch_bot_v2_st/app.py @@ -1,7 +1,6 @@ from frontend.components.deploy_v2_with_controllers import LaunchV2WithControllers from frontend.st_utils import initialize_st_page - initialize_st_page(title="Launch Bot ST", icon="πŸ™Œ") diff --git a/pages/orchestration/portfolio/app.py b/pages/orchestration/portfolio/app.py index 27d8607..eefc60f 100644 --- a/pages/orchestration/portfolio/app.py +++ b/pages/orchestration/portfolio/app.py @@ -1,8 +1,8 @@ -from frontend.st_utils import initialize_st_page, get_backend_api_client -import streamlit as st import pandas as pd -import plotly.graph_objects as go import plotly.express as px +import streamlit as st + +from frontend.st_utils import get_backend_api_client, initialize_st_page initialize_st_page(title="Portfolio", icon="πŸ’°") @@ -91,7 +91,8 @@ for account in accounts: filtered_account_state[account] = {} for exchange in exchanges: if exchange in account_state[account]: - filtered_account_state[account][exchange] = [token_info for token_info in account_state[account][exchange] if token_info["token"] in tokens_available] + filtered_account_state[account][exchange] = [token_info for token_info in account_state[account][exchange] + if token_info["token"] in tokens_available] filtered_account_history = [] for record in account_history: @@ -101,7 +102,9 @@ for record in account_history: filtered_record["state"][account] = {} for exchange in exchanges: if exchange in record["state"][account]: - filtered_record["state"][account][exchange] = [token_info for token_info in record["state"][account][exchange] if token_info["token"] in tokens_available] + filtered_record["state"][account][exchange] = [token_info for token_info in + record["state"][account][exchange] if + token_info["token"] in tokens_available] filtered_account_history.append(filtered_record) if len(filtered_account_state) > 0: From fcf39eb9011ea962fd3c12db6523aea568a6e508 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 19:56:49 +0300 Subject: [PATCH 16/18] (feat) fix mounts --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 34c65d9..c5019b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,8 +9,8 @@ services: - BACKEND_API_HOST=backend-api - BACKEND_API_PORT=8000 volumes: - - credentials.yml:/dashboard/credentials.yml - - ./pages:/dashboard/frontend/pages + - ./credentials.yml:/home/dashboard/credentials.yml + - ./pages:/home/dashboard/frontend/pages networks: - emqx-bridge backend-api: From 34879eb57a2c320c2aca2dc3d9c7f5203341e660 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 19:57:04 +0300 Subject: [PATCH 17/18] (feat) add no expiry for cookies --- credentials.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credentials.yml b/credentials.yml index 5d2592b..f7368d4 100644 --- a/credentials.yml +++ b/credentials.yml @@ -7,7 +7,7 @@ credentials: logged_in: False password: abc cookie: - expiry_days: 30 + expiry_days: 0 key: some_signature_key # Must be string name: some_cookie_name pre-authorized: From 78f5fa5733ebeaee06c0369addd3a77da2800d4f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 23 Jul 2024 20:00:30 +0300 Subject: [PATCH 18/18] (feat) apply changes to development version --- docker-compose-dev.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 316afcc..9fd7dde 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -5,8 +5,12 @@ services: ports: - "8501:8501" environment: + - AUTH_SYSTEM_ENABLED=False - BACKEND_API_HOST=backend-api - BACKEND_API_PORT=8000 + volumes: + - ./credentials.yml:/home/dashboard/credentials.yml + - ./pages:/home/dashboard/frontend/pages networks: - emqx-bridge backend-api: