Merge pull request #137 from hummingbot/feat/revamp_dashboard

Feat/revamp dashboard
This commit is contained in:
Michael Feng
2024-05-30 13:29:36 -05:00
committed by GitHub
210 changed files with 67862 additions and 7067 deletions

View File

@@ -1,5 +1,5 @@
[theme]
base="light"
base="dark"
font="monospace"
[server]
port=8501

View File

@@ -1,3 +1,10 @@
import os
from dotenv import load_dotenv
load_dotenv()
MINER_COINS = ["Algorand", "Avalanche", "DAO Maker", "Faith Tribe", "Fear", "Frontier",
"Harmony", "Hot Cross", "HUMAN Protocol", "Oddz", "Shera", "Firo",
"Vesper Finance", "Youclout", "Nimiq"]
@@ -12,3 +19,6 @@ CERTIFIED_EXCHANGES = ["ascendex", "binance", "bybit", "gate.io", "hitbtc", "huo
CERTIFIED_STRATEGIES = ["xemm", "cross exchange market making", "pmm", "pure market making"]
AUTH_SYSTEM_ENABLED = False
BACKEND_API_HOST = os.getenv("BACKEND_API_HOST", "127.0.0.1")
BACKEND_API_PORT = os.getenv("BACKEND_API_PORT", 8000)

View File

@@ -39,18 +39,11 @@ ENV COMMIT_SHA=${COMMIT}
ENV COMMIT_BRANCH=${BRANCH}
ENV BUILD_DATE=${DATE}
ENV INSTALLATION_TYPE=docker
# Install system dependencies
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
# Install Docker CLI
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
sh get-docker.sh && \
rm get-docker.sh
# Create mount points
RUN mkdir -p /home/dashboard/data

View File

@@ -1,19 +1,19 @@
.ONESHELL:
.PHONY: run
.PHONY: conda_remove
.PHONY: conda_create
.PHONY: uninstall
.PHONY: install
run:
streamlit run main.py
env_remove:
uninstall:
conda env remove -n dashboard
env_create:
install:
conda env create -f environment_conda.yml
docker_build:
docker build -t dashboard:latest .
docker build -t hummingbot/dashboard:latest .
docker_run:
docker run -p 8501:8501 dashboard:latest

View File

@@ -0,0 +1,281 @@
import pandas as pd
import requests
from hummingbot.strategy_v2.models.executors_info import ExecutorInfo
class BackendAPIClient:
"""
This class is a client to interact with the backend API. The Backend API is a REST API that provides endpoints to
create new Hummingbot instances, start and stop them, add new script and controller config files, and get the status
of the active bots.
"""
_shared_instance = None
@classmethod
def get_instance(cls, *args, **kwargs) -> "MarketsRecorder":
if cls._shared_instance is None:
cls._shared_instance = BackendAPIClient(*args, **kwargs)
return cls._shared_instance
def __init__(self, host: str = "localhost", port: int = 8000):
self.host = host
self.port = port
self.base_url = f"http://{self.host}:{self.port}"
def is_docker_running(self):
"""Check if Docker is running."""
url = f"{self.base_url}/is-docker-running"
response = requests.get(url)
return response.json()
def pull_image(self, image_name: str):
"""Pull a Docker image."""
url = f"{self.base_url}/pull-image/"
payload = {"image_name": image_name}
response = requests.post(url, json=payload)
return response.json()
def list_available_images(self, image_name: str):
"""List available images by name."""
url = f"{self.base_url}/available-images/{image_name}"
response = requests.get(url)
return response.json()
def list_active_containers(self):
"""List all active containers."""
url = f"{self.base_url}/active-containers"
response = requests.get(url)
return response.json()
def list_exited_containers(self):
"""List all exited containers."""
url = f"{self.base_url}/exited-containers"
response = requests.get(url)
return response.json()
def clean_exited_containers(self):
"""Clean up exited containers."""
url = f"{self.base_url}/clean-exited-containers"
response = requests.post(url)
return response.json()
def remove_container(self, container_name: str, archive_locally: bool = True, s3_bucket: str = None):
"""Remove a specific container."""
url = f"{self.base_url}/remove-container/{container_name}"
params = {"archive_locally": archive_locally}
if s3_bucket:
params["s3_bucket"] = s3_bucket
response = requests.post(url, params=params)
return response.json()
def stop_container(self, container_name: str):
"""Stop a specific container."""
url = f"{self.base_url}/stop-container/{container_name}"
response = requests.post(url)
return response.json()
def start_container(self, container_name: str):
"""Start a specific container."""
url = f"{self.base_url}/start-container/{container_name}"
response = requests.post(url)
return response.json()
def create_hummingbot_instance(self, instance_config: dict):
"""Create a new Hummingbot instance."""
url = f"{self.base_url}/create-hummingbot-instance"
response = requests.post(url, json=instance_config)
return response.json()
def start_bot(self, start_bot_config: dict):
"""Start a Hummingbot bot."""
url = f"{self.base_url}/start-bot"
response = requests.post(url, json=start_bot_config)
return response.json()
def stop_bot(self, bot_name: str, skip_order_cancellation: bool = False, async_backend: bool = True):
"""Stop a Hummingbot bot."""
url = f"{self.base_url}/stop-bot"
response = requests.post(url, json={"bot_name": bot_name, "skip_order_cancellation": skip_order_cancellation, "async_backend": async_backend})
return response.json()
def import_strategy(self, strategy_config: dict):
"""Import a trading strategy to a bot."""
url = f"{self.base_url}/import-strategy"
response = requests.post(url, json=strategy_config)
return response.json()
def get_bot_status(self, bot_name: str):
"""Get the status of a bot."""
url = f"{self.base_url}/get-bot-status/{bot_name}"
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
return {"status": "error", "data": "Bot not found"}
def get_bot_history(self, bot_name: str):
"""Get the historical data of a bot."""
url = f"{self.base_url}/get-bot-history/{bot_name}"
response = requests.get(url)
return response.json()
def get_active_bots_status(self):
"""
Retrieve the cached status of all active bots.
Returns a JSON response with the status and data of active bots.
"""
url = f"{self.base_url}/get-active-bots-status"
response = requests.get(url)
if response.status_code == 200:
return response.json() # Successful request
else:
return {"status": "error", "data": "No active bots found"}
def get_all_controllers_config(self):
"""Get all controller configurations."""
url = f"{self.base_url}/all-controller-configs"
response = requests.get(url)
return response.json()
def get_available_images(self, image_name: str = "hummingbot"):
"""Get available images."""
url = f"{self.base_url}/available-images/{image_name}"
response = requests.get(url)
return response.json()["available_images"]
def add_script_config(self, script_config: dict):
"""Add a new script configuration."""
url = f"{self.base_url}/add-script-config"
response = requests.post(url, json=script_config)
return response.json()
def add_controller_config(self, controller_config: dict):
"""Add a new controller configuration."""
url = f"{self.base_url}/add-controller-config"
config = {
"name": controller_config["id"],
"content": controller_config
}
response = requests.post(url, json=config)
return response.json()
def get_real_time_candles(self, connector: str, trading_pair: str, interval: str, max_records: int):
"""Get candles data."""
url = f"{self.base_url}/real-time-candles"
payload = {
"connector": connector,
"trading_pair": trading_pair,
"interval": interval,
"max_records": max_records
}
response = requests.post(url, json=payload)
return response.json()
def get_historical_candles(self, connector: str, trading_pair: str, interval: str, start_time: int, end_time: int):
"""Get historical candles data."""
url = f"{self.base_url}/historical-candles"
payload = {
"connector": connector,
"trading_pair": trading_pair,
"interval": interval,
"start_time": start_time,
"end_time": end_time
}
response = requests.post(url, json=payload)
return response.json()
def run_backtesting(self, start_time: int, end_time: int, backtesting_resolution: str, trade_cost: float, config: dict):
"""Run backtesting."""
url = f"{self.base_url}/run-backtesting"
payload = {
"start_time": start_time,
"end_time": end_time,
"backtesting_resolution": backtesting_resolution,
"trade_cost": trade_cost,
"config": config
}
response = requests.post(url, json=payload)
backtesting_results = response.json()
if "processed_data" not in backtesting_results:
data = None
else:
data = pd.DataFrame(backtesting_results["processed_data"])
return {
"processed_data": data,
"executors": [ExecutorInfo(**executor) for executor in backtesting_results["executors"]],
"results": backtesting_results["results"]
}
def get_all_configs_from_bot(self, bot_name: str):
"""Get all configurations from a bot."""
url = f"{self.base_url}/all-controller-configs/bot/{bot_name}"
response = requests.get(url)
return response.json()
def stop_controller_from_bot(self, bot_name: str, controller_id: str):
"""Stop a controller from a bot."""
config = {"manual_kill_switch": True}
url = f"{self.base_url}/update-controller-config/bot/{bot_name}/{controller_id}"
response = requests.post(url, json=config)
return response.json()
def start_controller_from_bot(self, bot_name: str, controller_id: str):
"""Start a controller from a bot."""
config = {"manual_kill_switch": False}
url = f"{self.base_url}/update-controller-config/bot/{bot_name}/{controller_id}"
response = requests.post(url, json=config)
return response.json()
def get_connector_config_map(self, connector_name: str):
"""Get connector configuration map."""
url = f"{self.base_url}/connector-config-map/{connector_name}"
response = requests.get(url)
return response.json()
def get_all_connectors_config_map(self):
"""Get all connector configuration maps."""
url = f"{self.base_url}/all-connectors-config-map"
response = requests.get(url)
return response.json()
def add_account(self, account_name: str):
"""Add a new account."""
url = f"{self.base_url}/add-account"
response = requests.post(url, params={"account_name": account_name})
return response.json()
def delete_account(self, account_name: str):
"""Delete an account."""
url = f"{self.base_url}/delete-account/"
response = requests.post(url, params={"account_name": account_name})
return response.json()
def delete_credential(self, account_name: str, connector_name: str):
"""Delete credentials."""
url = f"{self.base_url}/delete-credential/{account_name}/{connector_name}"
response = requests.post(url)
return response.json()
def add_connector_keys(self, account_name: str, connector_name: str, connector_config: dict):
"""Add connector keys."""
url = f"{self.base_url}/add-connector-keys/{account_name}/{connector_name}"
response = requests.post(url, json=connector_config)
return response.json()
def get_accounts(self):
"""Get available credentials."""
url = f"{self.base_url}/list-accounts"
response = requests.get(url)
return response.json()
def get_credentials(self, account_name: str):
"""Get available credentials."""
url = f"{self.base_url}/list-credentials/{account_name}"
response = requests.get(url)
return response.json()
def get_all_balances(self):
"""Get all balances."""
url = f"{self.base_url}/get-all-balances"
response = requests.get(url)
return response.json()

View File

@@ -5,7 +5,7 @@ import pandas as pd
import re
class CoinGeckoUtils:
class CoinGeckoClient:
def __init__(self):
self.connector = CoinGeckoAPI()

View File

@@ -3,7 +3,7 @@ import requests
from glom import *
class MinerUtils:
class MinerClient:
MARKETS_ENDPOINT = "https://api.hummingbot.io/bounty/markets"
@staticmethod

View File

@@ -10,9 +10,9 @@ from typing import Optional
import pandas as pd
from pydantic import Field
from hummingbot.smart_components.executors.position_executor.position_executor import PositionExecutor
from hummingbot.smart_components.strategy_frameworks.data_types import OrderLevel
from hummingbot.smart_components.strategy_frameworks.directional_trading.directional_trading_controller_base import (
from hummingbot.strategy_v2.executors.position_executor.position_executor import PositionExecutor
from hummingbot.strategy_v2.strategy_frameworks.data_types import OrderLevel
from hummingbot.strategy_v2.strategy_frameworks.directional_trading.directional_trading_controller_base import (
DirectionalTradingControllerBase,
DirectionalTradingControllerConfigBase,
)
@@ -100,9 +100,9 @@ from decimal import Decimal
from hummingbot.core.data_type.common import PositionMode, TradeType, OrderType
from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig
from hummingbot.smart_components.strategy_frameworks.data_types import TripleBarrierConf, OrderLevel
from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine
from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder
from hummingbot.strategy_v2.strategy_frameworks.data_types import TripleBarrierConf, OrderLevel
from hummingbot.strategy_v2.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine
from hummingbot.strategy_v2.utils.config_encoder_decoder import ConfigEncoderDecoder
from optuna import TrialPruned
from quants_lab.controllers.{strategy_module} import {strategy_cls.__name__}, {strategy_config.__name__}

View File

@@ -6,8 +6,6 @@ import pandas as pd
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from utils.data_manipulation import StrategyData
class OptunaDBManager:
def __init__(self, db_name, db_root_path: Optional[str]):

View File

@@ -5,11 +5,10 @@ import inspect
import os
import pandas as pd
from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingControllerBase, DirectionalTradingControllerConfigBase
import yaml
from hummingbot.smart_components.strategy_frameworks.market_making import MarketMakingControllerBase, \
MarketMakingControllerConfigBase
from hummingbot.strategy_v2.controllers.directional_trading_controller_base import DirectionalTradingControllerBase, DirectionalTradingControllerConfigBase
from hummingbot.strategy_v2.controllers.market_making_controller_base import MarketMakingControllerBase, MarketMakingControllerConfigBase
def remove_files_from_directory(directory: str):

View File

@@ -4,13 +4,13 @@ channels:
- conda-forge
dependencies:
- python=3.10
- sqlalchemy
- pydantic=1.9.*
- sqlalchemy=1.4
- pip
- pip:
- hummingbot
- streamlit
- streamlit==1.33.0
- watchdog
- python-dotenv
- plotly
- pycoingecko
- glom
@@ -18,14 +18,11 @@ dependencies:
- statsmodels
- pandas_ta==0.3.14b
- pyyaml
- commlib-py
- jupyter
- optuna
- optuna-dashboard
- pathlib
- streamlit-ace
- st-pages
- streamlit-elements==0.1.*
- streamlit-authenticator
- git+https://github.com/hummingbot/hbot-remote-client-py.git
- git+https://github.com/hummingbot/docker-manager.git
- pydantic==1.10.4

View File

@@ -0,0 +1,38 @@
import streamlit as st
from datetime import datetime, timedelta
def backtesting_section(inputs, backend_api_client):
st.write("### Backtesting")
c1, c2, c3, c4, c5 = st.columns(5)
default_end_time = datetime.now().date() - timedelta(days=1)
default_start_time = default_end_time - timedelta(days=2)
with c1:
start_date = st.date_input("Start Date", default_start_time)
with c2:
end_date = st.date_input("End Date", default_end_time,
help="End date is inclusive, make sure that you are not including the current date.")
with c3:
backtesting_resolution = st.selectbox("Backtesting Resolution", options=["1m", "3m", "5m", "15m", "30m", "1h", "1s"], index=0)
with c4:
trade_cost = st.number_input("Trade Cost (%)", min_value=0.0, value=0.06, step=0.01, format="%.2f")
with c5:
run_backtesting = st.button("Run Backtesting")
if run_backtesting:
start_datetime = datetime.combine(start_date, datetime.min.time())
end_datetime = datetime.combine(end_date, datetime.max.time())
backtesting_results = backend_api_client.run_backtesting(
start_time=int(start_datetime.timestamp()) * 1000,
end_time=int(end_datetime.timestamp()) * 1000,
backtesting_resolution=backtesting_resolution,
trade_cost=trade_cost / 100,
config=inputs,
)
if len(backtesting_results["processed_data"]) == 0:
st.error("No trades were executed during the backtesting period.")
return None
if len(backtesting_results["executors"]) == 0:
st.error("No executors were found during the backtesting period.")
return None
return backtesting_results

View File

@@ -0,0 +1,285 @@
import time
from streamlit_elements import mui
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from frontend.components.dashboard import Dashboard
from backend.services.backend_api_client import BackendAPIClient
TRADES_TO_SHOW = 5
WIDE_COL_WIDTH = 250
MEDIUM_COL_WIDTH = 170
SMALL_COL_WIDTH = 100
backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
def stop_bot(bot_name):
backend_api_client.stop_bot(bot_name)
def archive_bot(bot_name):
backend_api_client.stop_container(bot_name)
backend_api_client.remove_container(bot_name)
class BotPerformanceCardV2(Dashboard.Item):
DEFAULT_COLUMNS = [
{"field": 'id', "headerName": 'ID', "width": WIDE_COL_WIDTH},
{"field": 'realized_pnl_quote', "headerName": 'Realized PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'unrealized_pnl_quote', "headerName": 'Unrealized PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'global_pnl_quote', "headerName": 'NET PNL ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'volume_traded', "headerName": 'Volume ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'open_order_volume', "headerName": 'Open Order Volume ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
{"field": 'imbalance', "headerName": 'Imbalance ($)', "width": MEDIUM_COL_WIDTH, "editable": False},
]
_active_controller_config_selected = []
_stopped_controller_config_selected = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
def _handle_stopped_row_selection(self, params, _):
self._stopped_controller_config_selected = params
def _handle_active_row_selection(self, params, _):
self._active_controller_config_selected = params
def _handle_errors_row_selection(self, params, _):
self._error_controller_config_selected = params
def stop_active_controllers(self, bot_name):
for controller in self._active_controller_config_selected:
self._backend_api_client.stop_controller_from_bot(bot_name, controller)
def stop_errors_controllers(self, bot_name):
for controller in self._error_controller_config_selected:
self._backend_api_client.stop_controller_from_bot(bot_name, controller)
def start_controllers(self, bot_name):
for controller in self._stopped_controller_config_selected:
self._backend_api_client.start_controller_from_bot(bot_name, controller)
def __call__(self, bot_name: str):
try:
controller_configs = backend_api_client.get_all_configs_from_bot(bot_name)
bot_status = backend_api_client.get_bot_status(bot_name)
# Controllers Table
active_controllers_list = []
stopped_controllers_list = []
error_controllers_list = []
total_global_pnl_quote = 0
total_volume_traded = 0
total_open_order_volume = 0
total_imbalance = 0
total_unrealized_pnl_quote = 0
if bot_status.get("status") == "error":
with mui.Card(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
elevation=2):
mui.CardHeader(
title=bot_name,
subheader="Not Available",
avatar=mui.Avatar("🤖", sx={"bgcolor": "red"}),
className=self._draggable_class)
mui.Alert(f"An error occurred while fetching bot status of the bot {bot_name}. Please check the bot client.", severity="error")
else:
bot_data = bot_status.get("data")
is_running = bot_data.get("status") == "running"
performance = bot_data.get("performance")
if is_running:
for controller, inner_dict in performance.items():
controller_status = inner_dict.get("status")
if controller_status == "error":
error_controllers_list.append(
{"id": controller, "error": inner_dict.get("error")})
continue
controller_performance = inner_dict.get("performance")
controller_config = next((config for config in controller_configs if config.get("id") == controller), {})
kill_switch_status = True if controller_config.get("manual_kill_switch") is True else False
realized_pnl_quote = controller_performance.get("realized_pnl_quote", 0)
unrealized_pnl_quote = controller_performance.get("unrealized_pnl_quote", 0)
global_pnl_quote = controller_performance.get("global_pnl_quote", 0)
volume_traded = controller_performance.get("volume_traded", 0)
open_order_volume = controller_performance.get("open_order_volume", 0)
imbalance = controller_performance.get("imbalance", 0)
controller_info = {
"id": controller,
"realized_pnl_quote": realized_pnl_quote,
"unrealized_pnl_quote": unrealized_pnl_quote,
"global_pnl_quote": global_pnl_quote,
"volume_traded": volume_traded,
"open_order_volume": open_order_volume,
"imbalance": imbalance,
}
if kill_switch_status:
stopped_controllers_list.append(controller_info)
else:
active_controllers_list.append(controller_info)
total_global_pnl_quote += global_pnl_quote
total_volume_traded += volume_traded
total_open_order_volume += open_order_volume
total_imbalance += imbalance
total_unrealized_pnl_quote += unrealized_pnl_quote
total_global_pnl_pct = total_global_pnl_quote / total_volume_traded if total_volume_traded > 0 else 0
if is_running:
status = "Running"
color = "green"
else:
status = "Stopped"
color = "red"
with mui.Card(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
elevation=2):
mui.CardHeader(
title=bot_name,
subheader=status,
avatar=mui.Avatar("🤖", sx={"bgcolor": color}),
action=mui.IconButton(mui.icon.Stop, onClick=lambda: stop_bot(bot_name)) if is_running else mui.IconButton(mui.icon.Archive, onClick=lambda: archive_bot(bot_name)),
className=self._draggable_class)
if is_running:
with mui.CardContent(sx={"flex": 1}):
with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("🏦 NET PNL", variant="h6")
mui.Typography(f"$ {total_global_pnl_quote:.3f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📊 NET PNL (%)", variant="h6")
mui.Typography(f"{total_global_pnl_pct:.3%}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💸 Volume Traded", variant="h6")
mui.Typography(f"$ {total_volume_traded:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📖 Liquidity Placed", variant="h6")
mui.Typography(f"$ {total_open_order_volume:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💹 Unrealized PNL", variant="h6")
mui.Typography(f"$ {total_unrealized_pnl_quote:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(item=True, xs=2):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("📊 Imbalance", variant="h6")
mui.Typography(f"$ {total_imbalance:.2f}", variant="h6", sx={"padding": "10px 15px 10px 15px"})
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("🚀 Active Controllers", variant="h6")
mui.DataGrid(
rows=active_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_active_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.stop_active_controllers(bot_name),
variant="outlined",
color="warning",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Stop")
if len(stopped_controllers_list) > 0:
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💤 Stopped Controllers", variant="h6")
mui.DataGrid(
rows=stopped_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_stopped_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.start_controllers(bot_name),
variant="outlined",
color="success",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Start")
if len(error_controllers_list) > 0:
with mui.Grid(container=True, spacing=1, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=11):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("💀 Controllers with errors", variant="h6")
mui.DataGrid(
rows=error_controllers_list,
columns=self.DEFAULT_COLUMNS,
autoHeight=True,
density="compact",
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_errors_row_selection,
hideFooter=True
)
with mui.Grid(item=True, xs=1):
with mui.Button(onClick=lambda x: self.stop_errors_controllers(bot_name),
variant="outlined",
color="warning",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Stop")
except Exception as e:
print(e)
with mui.Card(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
elevation=2):
mui.CardHeader(
title=bot_name,
subheader="Error",
avatar=mui.Avatar("🤖", sx={"bgcolor": "red"}),
action=mui.IconButton(mui.icon.Stop, onClick=lambda: stop_bot(bot_name)),
className=self._draggable_class)
with mui.CardContent(sx={"flex": 1}):
mui.Typography("An error occurred while fetching bot status.", sx={"padding": "10px 15px 10px 15px"})
mui.Typography(str(e), sx={"padding": "10px 15px 10px 15px"})

View File

@@ -1,9 +1,9 @@
from streamlit_elements import mui
import constants
from ui_components.file_explorer_base import FileExplorerBase
from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \
from backend.utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \
get_yml_files_from_directory, get_log_files_from_directory
from frontend.components.file_explorer_base import FileExplorerBase
class BotsFileExplorer(FileExplorerBase):

View File

@@ -1,5 +1,5 @@
from streamlit_elements import mui
from ui_components.dashboard import Dashboard
from frontend.components.dashboard import Dashboard
class Card(Dashboard.Item):

View File

@@ -0,0 +1,17 @@
import streamlit as st
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from backend.services.backend_api_client import BackendAPIClient
backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
def get_default_config_loader(controller_name: str):
use_default_config = st.checkbox("Use default config", value=True)
all_configs = backend_api_client.get_all_controllers_config()
if use_default_config:
st.session_state["default_config"] = {}
else:
configs = [config for config in all_configs if config["controller_name"] == controller_name]
default_config = st.selectbox("Select a config", [config["id"] for config in configs])
st.session_state["default_config"] = next((config for config in all_configs if config["id"] == default_config), {})

View File

@@ -1,8 +1,8 @@
from streamlit_elements import mui
import constants
from ui_components.file_explorer_base import FileExplorerBase
from utils.os_utils import get_python_files_from_directory, load_controllers
from backend.utils.os_utils import load_controllers
from frontend.components.file_explorer_base import FileExplorerBase
class ControllersFileExplorer(FileExplorerBase):

View File

@@ -0,0 +1,79 @@
import streamlit as st
from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs
def get_dca_distribution_inputs():
with st.expander("DCA Builder", expanded=True):
default_config = st.session_state.get("default_config", {})
dca_spreads = default_config.get("dca_spreads", [0.01, 0.02, 0.03])
dca_amounts = default_config.get("dca_amounts", [0.2, 0.5, 0.3])
tp = default_config.get("take_profit", 0.01) * 100
sl = default_config.get("stop_loss", 0.02) * 100
time_limit = default_config.get("time_limit", 60 * 6 * 60) // 60
ts_ap = default_config.get("trailing_stop", {}).get("activation_price", 0.018) * 100
ts_delta = default_config.get("trailing_stop", {}).get("trailing_delta", 0.002) * 100
levels_def = len(dca_spreads)
c1, c2 = st.columns([0.67, 0.33])
with c1:
st.header("DCA Distribution")
buy_order_levels = st.number_input("Number of Order Levels", min_value=1, value=levels_def,
help="Enter the number of order levels (e.g., 2).")
if buy_order_levels > levels_def:
dca_spreads += [0.01 + max(dca_spreads)] * (buy_order_levels - levels_def)
dca_amounts += [0.2 + max(dca_amounts)] * (buy_order_levels - levels_def)
elif buy_order_levels < levels_def:
dca_spreads = dca_spreads[:buy_order_levels]
dca_amounts = dca_amounts[:buy_order_levels]
col_spreads, col_amounts = st.columns(2)
with col_spreads:
buy_spread_dist_type, buy_spread_start, buy_spread_base, buy_spread_scaling, buy_spread_step, buy_spread_ratio, buy_manual_spreads = distribution_inputs(
col_spreads, "Spread", buy_order_levels, dca_spreads)
with col_amounts:
buy_amount_dist_type, buy_amount_start, buy_amount_base, buy_amount_scaling, buy_amount_step, buy_amount_ratio, buy_manual_amounts = distribution_inputs(
col_amounts, "Amount", buy_order_levels, dca_amounts)
# Generate distributions
spread_distributions = get_distribution(buy_spread_dist_type, buy_order_levels, buy_spread_start,
buy_spread_base, buy_spread_scaling, buy_spread_step, buy_spread_ratio,
buy_manual_spreads)
amount_distributions = get_distribution(buy_amount_dist_type, buy_order_levels, buy_amount_start,
buy_amount_base, buy_amount_scaling,
buy_amount_step, buy_amount_ratio, buy_manual_amounts)
# Normalize and calculate order amounts
orders_amount_normalized = normalize(amount_distributions)
spreads_normalized = [spread - spread_distributions[0] for spread in spread_distributions]
st.write("---")
# c1, c2, c3, c4, c5 = st.columns(5)
with c2:
st.header("Risk Management")
sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=sl, step=0.1,
help="Enter the stop loss as a percentage (e.g., 2.0 for 2%).") / 100
# with c2:
tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=tp, step=0.1,
help="Enter the take profit as a percentage (e.g., 3.0 for 3%).") / 100
# with c3:
time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=time_limit,
help="Enter the time limit in minutes (e.g., 360 for 6 hours).") * 60
# with c4:
ts_ap = st.number_input("Trailing Stop Act. Price (%)", min_value=0.0, max_value=100.0, value=ts_ap,
step=0.1,
help="Enter the tr ailing stop activation price as a percentage (e.g., 1.0 for 1%).") / 100
# with c5:
ts_delta = st.number_input("Trailing Stop Delta (%)", min_value=0.0, max_value=100.0, value=ts_delta, step=0.1,
help="Enter the trailing stop delta as a percentage (e.g., 0.3 for 0.3%).") / 100
return {
"dca_spreads": [spread / 100 for spread in spreads_normalized],
"dca_amounts": orders_amount_normalized,
"stop_loss": sl,
"take_profit": tp,
"time_limit": time_limit,
"trailing_stop": {
"activation_price": ts_ap,
"trailing_delta": ts_delta
},
}

View File

@@ -0,0 +1,110 @@
import time
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
class LaunchV2WithControllers:
DEFAULT_COLUMNS = [
"id", "controller_name", "controller_type", "connector_name",
"trading_pair", "total_amount_quote", "max_loss_quote", "stop_loss",
"take_profit", "trailing_stop", "time_limit", "selected"
]
def __init__(self):
self._backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
self._controller_configs_available = self._backend_api_client.get_all_controllers_config()
self._controller_config_selected = []
self._bot_name = None
self._image_name = "dardonacci/hummingbot:latest"
self._credentials = "master_account"
def _set_bot_name(self, bot_name):
self._bot_name = bot_name
def _set_image_name(self, image_name):
self._image_name = image_name
def _set_credentials(self, credentials):
self._credentials = credentials
def launch_new_bot(self):
if self._bot_name and self._image_name and self._controller_config_selected:
start_time_str = time.strftime("%Y.%m.%d_%H.%M")
bot_name = f"{self._bot_name}-{start_time_str}"
script_config = {
"name": bot_name,
"content": {
"markets": {},
"candles_config": [],
"controllers_config": self._controller_config_selected,
"config_update_interval": 20,
"script_file_name": "v2_with_controllers.py",
"time_to_cash_out": None,
}
}
self._backend_api_client.add_script_config(script_config)
deploy_config = {
"instance_name": bot_name,
"script": "v2_with_controllers.py",
"script_config": bot_name + ".yml",
"image": self._image_name,
"credentials_profile": self._credentials,
}
self._backend_api_client.create_hummingbot_instance(deploy_config)
with st.spinner('Starting Bot... This process may take a few seconds'):
time.sleep(3)
else:
st.warning("You need to define the bot name and select the controllers configs "
"that you want to deploy.")
def __call__(self):
st.write("#### Select the controllers configs that you want to deploy.")
all_controllers_config = self._controller_configs_available
data = []
for config in all_controllers_config:
connector_name = config.get("connector_name", "Unknown")
trading_pair = config.get("trading_pair", "Unknown")
total_amount_quote = config.get("total_amount_quote", 0)
stop_loss = config.get("stop_loss", 0)
take_profit = config.get("take_profit", 0)
trailing_stop = config.get("trailing_stop", {"activation_price": 0, "trailing_delta": 0})
time_limit = config.get("time_limit", 0)
data.append({
"selected": False,
"id": config["id"],
"controller_name": config["controller_name"],
"controller_type": config["controller_type"],
"connector_name": connector_name,
"trading_pair": trading_pair,
"total_amount_quote": total_amount_quote,
"max_loss_quote": total_amount_quote * stop_loss / 2,
"stop_loss": f"{stop_loss:.2%}",
"take_profit": f"{take_profit:.2%}",
"trailing_stop": f"{trailing_stop['activation_price']:.2%} / {trailing_stop['trailing_delta']:.2%}",
"time_limit": time_limit,
})
df = pd.DataFrame(data)
edited_df = st.data_editor(df, hide_index=True)
self._controller_config_selected = [f"{config}.yml" for config in edited_df[edited_df["selected"]]["id"].tolist()]
st.write(self._controller_config_selected)
c1, c2, c3, c4 = st.columns([1, 1, 1, 0.3])
with c1:
self._bot_name = st.text_input("Instance Name")
with c2:
available_images = self._backend_api_client.get_available_images("hummingbot")
self._image_name = st.selectbox("Hummingbot Image", available_images,
index=available_images.index("hummingbot/hummingbot:latest"))
with c3:
available_credentials = self._backend_api_client.get_accounts()
self._credentials = st.selectbox("Credentials", available_credentials, index=0)
with c4:
deploy_button = st.button("Deploy Bot")
if deploy_button:
self.launch_new_bot()

View File

@@ -0,0 +1,48 @@
import streamlit as st
def get_directional_trading_general_inputs():
with st.expander("General Settings", expanded=True):
c1, c2, c3, c4, c5, c6, c7 = st.columns(7)
default_config = st.session_state.get("default_config", {})
connector_name = default_config.get("connector_name", "kucoin")
trading_pair = default_config.get("trading_pair", "WLD-USDT")
leverage = default_config.get("leverage", 20)
total_amount_quote = default_config.get("total_amount_quote", 1000)
max_executors_per_side = default_config.get("max_executors_per_side", 5)
cooldown_time = default_config.get("cooldown_time", 60 * 60) / 60
position_mode = 0 if default_config.get("position_mode", "HEDGE") == "HEDGE" else 1
candles_connector_name = default_config.get("candles_connector_name", "kucoin")
candles_trading_pair = default_config.get("candles_trading_pair", "WLD-USDT")
interval = default_config.get("interval", "3m")
intervals = ["1m", "3m", "5m", "15m", "1h", "4h", "1d", "1s"]
interval_index = intervals.index(interval)
with c1:
connector_name = st.text_input("Connector", value=connector_name,
help="Enter the name of the exchange to trade on (e.g., binance_perpetual).")
candles_connector_name = st.text_input("Candles Connector", value=candles_connector_name,
help="Enter the name of the exchange to get candles from (e.g., binance_perpetual).")
with c2:
trading_pair = st.text_input("Trading Pair", value=trading_pair,
help="Enter the trading pair to trade on (e.g., WLD-USDT).")
candles_trading_pair = st.text_input("Candles Trading Pair", value=candles_trading_pair,
help="Enter the trading pair to get candles for (e.g., WLD-USDT).")
with c3:
leverage = st.number_input("Leverage", value=leverage,
help="Set the leverage to use for trading (e.g., 20 for 20x leverage). Set it to 1 for spot trading.")
interval = st.selectbox("Candles Interval", ("1m", "3m", "5m", "15m", "1h", "4h", "1d"), index=interval_index,
help="Enter the interval for candles (e.g., 1m).")
with c4:
total_amount_quote = st.number_input("Total amount of quote", value=total_amount_quote,
help="Enter the total amount in quote asset to use for trading (e.g., 1000).")
with c5:
max_executors_per_side = st.number_input("Max Executors Per Side", value=max_executors_per_side,
help="Enter the maximum number of executors per side (e.g., 5).")
with c6:
cooldown_time = st.number_input("Cooldown Time (minutes)", value=cooldown_time,
help="Time between accepting a new signal in minutes (e.g., 60).") * 60
with c7:
position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=position_mode,
help="Enter the position mode (HEDGE/ONEWAY).")
return connector_name, trading_pair, leverage, total_amount_quote, max_executors_per_side, cooldown_time, position_mode, candles_connector_name, candles_trading_pair, interval

View File

@@ -2,7 +2,7 @@ from functools import partial
import streamlit as st
from streamlit_elements import mui, editor, sync, lazy, event
from utils.os_utils import save_file
from backend.utils.os_utils import save_file
from .dashboard import Dashboard

View File

@@ -0,0 +1,68 @@
import streamlit as st
from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs
def get_executors_distribution_inputs(default_spreads=[0.01, 0.02], default_amounts=[0.2, 0.8]):
default_config = st.session_state.get("default_config", {})
buy_spreads = default_config.get("buy_spreads", default_spreads)
sell_spreads = default_config.get("sell_spreads", default_spreads)
buy_amounts_pct = default_config.get("buy_amounts_pct", default_amounts)
sell_amounts_pct = default_config.get("sell_amounts_pct", default_amounts)
buy_order_levels_def = len(buy_spreads)
sell_order_levels_def = len(sell_spreads)
with st.expander("Executors Configuration", expanded=True):
col_buy, col_sell = st.columns(2)
with col_buy:
st.header("Buy Order Settings")
buy_order_levels = st.number_input("Number of Buy Order Levels", min_value=1, value=buy_order_levels_def,
help="Enter the number of buy order levels (e.g., 2).")
with col_sell:
st.header("Sell Order Settings")
sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=sell_order_levels_def,
help="Enter the number of sell order levels (e.g., 2).")
if buy_order_levels > buy_order_levels_def:
buy_spreads += [0.01 + max(buy_spreads)] * (buy_order_levels - buy_order_levels_def)
buy_amounts_pct += [0.2 + max(buy_amounts_pct)] * (buy_order_levels - buy_order_levels_def)
elif buy_order_levels < buy_order_levels_def:
buy_spreads = buy_spreads[:buy_order_levels]
buy_amounts_pct = buy_amounts_pct[:buy_order_levels]
if sell_order_levels > sell_order_levels_def:
sell_spreads += [0.01 + max(sell_spreads)] * (sell_order_levels - sell_order_levels_def)
sell_amounts_pct += [0.2 + max(sell_amounts_pct)] * (sell_order_levels - sell_order_levels_def)
elif sell_order_levels < sell_order_levels_def:
sell_spreads = sell_spreads[:sell_order_levels]
sell_amounts_pct = sell_amounts_pct[:sell_order_levels]
col_buy_spreads, col_buy_amounts, col_sell_spreads, col_sell_amounts = st.columns(4)
with col_buy_spreads:
buy_spread_dist_type, buy_spread_start, buy_spread_base, buy_spread_scaling, buy_spread_step, buy_spread_ratio, buy_manual_spreads = distribution_inputs(
col_buy_spreads, "Spread", buy_order_levels, buy_spreads)
with col_buy_amounts:
buy_amount_dist_type, buy_amount_start, buy_amount_base, buy_amount_scaling, buy_amount_step, buy_amount_ratio, buy_manual_amounts = distribution_inputs(
col_buy_amounts, "Amount", buy_order_levels, buy_amounts_pct)
with col_sell_spreads:
sell_spread_dist_type, sell_spread_start, sell_spread_base, sell_spread_scaling, sell_spread_step, sell_spread_ratio, sell_manual_spreads = distribution_inputs(
col_sell_spreads, "Spread", sell_order_levels, sell_spreads)
with col_sell_amounts:
sell_amount_dist_type, sell_amount_start, sell_amount_base, sell_amount_scaling, sell_amount_step, sell_amount_ratio, sell_manual_amounts = distribution_inputs(
col_sell_amounts, "Amount", sell_order_levels, sell_amounts_pct)
# Generate distributions
buy_spread_distributions = get_distribution(buy_spread_dist_type, buy_order_levels, buy_spread_start,
buy_spread_base, buy_spread_scaling, buy_spread_step, buy_spread_ratio,
buy_manual_spreads)
sell_spread_distributions = get_distribution(sell_spread_dist_type, sell_order_levels, sell_spread_start,
sell_spread_base, sell_spread_scaling, sell_spread_step,
sell_spread_ratio, sell_manual_spreads)
buy_amount_distributions = get_distribution(buy_amount_dist_type, buy_order_levels, buy_amount_start, buy_amount_base, buy_amount_scaling,
buy_amount_step, buy_amount_ratio, buy_manual_amounts)
sell_amount_distributions = get_distribution(sell_amount_dist_type, sell_order_levels, sell_amount_start, sell_amount_base,
sell_amount_scaling, sell_amount_step, sell_amount_ratio, sell_manual_amounts)
# Normalize and calculate order amounts
all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions)
buy_order_amounts_pct = [amount for amount in all_orders_amount_normalized[:buy_order_levels]]
sell_order_amounts_pct = [amount for amount in all_orders_amount_normalized[buy_order_levels:]]
buy_spread_distributions = [spread / 100 for spread in buy_spread_distributions]
sell_spread_distributions = [spread / 100 for spread in sell_spread_distributions]
return buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct

View File

@@ -1,11 +1,8 @@
from docker_manager import DockerManager
from streamlit_elements import mui, lazy
from ui_components.dashboard import Dashboard
import streamlit as st
import time
from streamlit_elements import mui
from frontend.components.dashboard import Dashboard
from utils import os_utils
from utils.os_utils import get_python_files_from_directory, get_yml_files_from_directory
from backend.utils import os_utils
class ExitedBotCard(Dashboard.Item):

View File

@@ -1,7 +1,7 @@
import streamlit as st
from streamlit_elements import mui, elements
from streamlit_elements import mui
from utils.os_utils import load_file, remove_file
from backend.utils.os_utils import remove_file, load_file
from .dashboard import Dashboard
@@ -29,8 +29,9 @@ class FileExplorerBase(Dashboard.Item):
def add_file_to_tab(self):
language = "python" if self.selected_file.endswith(".py") else "yaml"
if self.is_file_editable:
self._tabs[self.selected_file] = {"content": load_file(self.selected_file),
"language": language}
self._tabs[self.selected_file] = {
"content": load_file(self.selected_file),
"language": language}
def remove_file_from_tab(self):
if self.is_file_editable and self.selected_file in self._tabs:

View File

@@ -6,7 +6,7 @@ import streamlit as st
from streamlit_elements import mui, lazy
import constants
from utils.os_utils import get_directories_from_directory
from backend.utils.os_utils import get_directories_from_directory
from .dashboard import Dashboard

View File

@@ -0,0 +1,160 @@
import time
import streamlit as st
from streamlit_elements import mui, lazy
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from backend.services.backend_api_client import BackendAPIClient
from .dashboard import Dashboard
class LaunchStrategyV2(Dashboard.Item):
DEFAULT_ROWS = []
DEFAULT_COLUMNS = [
{"field": 'id', "headerName": 'ID', "width": 230},
{"field": 'controller_name', "headerName": 'Controller Name', "width": 150, "editable": False, },
{"field": 'controller_type', "headerName": 'Controller Type', "width": 150, "editable": False, },
{"field": 'connector_name', "headerName": 'Connector', "width": 150, "editable": False, },
{"field": 'trading_pair', "headerName": 'Trading pair', "width": 140, "editable": False, },
{"field": 'total_amount_quote', "headerName": 'Total amount ($)', "width": 140, "editable": False, },
{"field": 'max_loss_quote', "headerName": 'Max loss ($)', "width": 120, "editable": False, },
{"field": 'stop_loss', "headerName": 'SL (%)', "width": 100, "editable": False, },
{"field": 'take_profit', "headerName": 'TP (%)', "width": 100, "editable": False, },
{"field": 'trailing_stop', "headerName": 'TS (%)', "width": 120, "editable": False, },
{"field": 'time_limit', "headerName": 'Time limit', "width": 100, "editable": False, },
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
self._controller_configs_available = self._backend_api_client.get_all_controllers_config()
self._controller_config_selected = None
self._bot_name = None
self._image_name = "dardonacci/hummingbot:latest"
self._credentials = "master_account"
def _set_bot_name(self, event):
self._bot_name = event.target.value
def _set_image_name(self, _, childs):
self._image_name = childs.props.value
def _set_credentials(self, _, childs):
self._credentials = childs.props.value
def _set_controller(self, event):
self._controller_selected = event.target.value
def _handle_row_selection(self, params, _):
self._controller_config_selected = [param + ".yml" for param in params]
def launch_new_bot(self):
if self._bot_name and self._image_name and len(self._controller_config_selected) > 0:
start_time_str = time.strftime("%Y.%m.%d_%H.%M")
bot_name = f"{self._bot_name}-{start_time_str}"
script_config = {
"name": bot_name,
"content": {
"markets": {},
"candles_config": [],
"controllers_config": self._controller_config_selected,
"config_update_interval": 10,
"script_file_name": "v2_with_controllers.py",
"time_to_cash_out": None,
}
}
self._backend_api_client.add_script_config(script_config)
deploy_config = {
"instance_name": bot_name,
"script": "v2_with_controllers.py",
"script_config": bot_name + ".yml",
"image": self._image_name,
"credentials_profile": self._credentials,
}
self._backend_api_client.create_hummingbot_instance(deploy_config)
with st.spinner('Starting Bot... This process may take a few seconds'):
time.sleep(3)
else:
st.warning("You need to define the bot name and select the controllers configs "
"that you want to deploy.")
def __call__(self):
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.Typography("🚀 Select the controller configs to launch", variant="h5")
with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=8):
mui.Alert(
"The new instance will contain the credentials configured in the following base instance:",
severity="info")
with mui.Grid(item=True, xs=4):
available_credentials = self._backend_api_client.get_accounts()
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Credentials")
with mui.Select(label="Credentials", defaultValue="master_account",
variant="standard", onChange=lazy(self._set_credentials)):
for master_config in available_credentials:
mui.MenuItem(master_config, value=master_config)
with mui.Grid(item=True, xs=4):
mui.TextField(label="Instance Name", variant="outlined", onChange=lazy(self._set_bot_name),
sx={"width": "100%"})
with mui.Grid(item=True, xs=4):
available_images = self._backend_api_client.get_available_images("hummingbot")
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Available Images")
with mui.Select(label="Hummingbot Image", defaultValue="dardonacci/hummingbot:latest",
variant="standard", onChange=lazy(self._set_image_name)):
for image in available_images:
mui.MenuItem(image, value=image)
with mui.Grid(item=True, xs=4):
with mui.Button(onClick=self.launch_new_bot,
variant="outlined",
color="success",
sx={"width": "100%", "height": "100%"}):
mui.icon.AddCircleOutline()
mui.Typography("Create")
all_controllers_config = self._backend_api_client.get_all_controllers_config()
data = []
for config in all_controllers_config:
connector_name = config.get("connector_name", "Unknown")
trading_pair = config.get("trading_pair", "Unknown")
total_amount_quote = config.get("total_amount_quote", 0)
stop_loss = config.get("stop_loss", 0)
take_profit = config.get("take_profit", 0)
trailing_stop = config.get("trailing_stop", {"activation_price": 0, "trailing_delta": 0})
time_limit = config.get("time_limit", 0)
data.append({"id": config["id"], "controller_name": config["controller_name"],
"controller_type": config["controller_type"],
"connector_name": connector_name,
"trading_pair": trading_pair,
"total_amount_quote": total_amount_quote,
"max_loss_quote": total_amount_quote * stop_loss / 2,
"stop_loss": stop_loss,
"take_profit": take_profit,
"trailing_stop": str(trailing_stop["activation_price"]) + " / " +
str(trailing_stop["trailing_delta"]),
"time_limit": time_limit})
with mui.Grid(item=True, xs=12):
mui.Alert("Select the controller configs to deploy", severity="info")
with mui.Paper(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 3,
"overflow": "hidden", "height": 1000},
elevation=1):
with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False):
mui.icon.ViewCompact()
mui.Typography("Controllers Config")
with mui.Box(sx={"flex": 1, "minHeight": 3}):
mui.DataGrid(
columns=self.DEFAULT_COLUMNS,
rows=data,
pageSize=15,
rowsPerPageOptions=[15],
checkboxSelection=True,
disableSelectionOnClick=True,
onSelectionModelChange=self._handle_row_selection,
)

View File

@@ -0,0 +1,54 @@
import streamlit as st
def get_market_making_general_inputs(custom_candles=False):
with st.expander("General Settings", expanded=True):
c1, c2, c3, c4, c5, c6, c7 = st.columns(7)
default_config = st.session_state.get("default_config", {})
connector_name = default_config.get("connector_name", "kucoin")
trading_pair = default_config.get("trading_pair", "WLD-USDT")
leverage = default_config.get("leverage", 20)
total_amount_quote = default_config.get("total_amount_quote", 1000)
position_mode = 0 if default_config.get("position_mode", "HEDGE") == "HEDGE" else 1
cooldown_time = default_config.get("cooldown_time", 60 * 60) / 60
executor_refresh_time = default_config.get("executor_refresh_time", 60 * 60) / 60
candles_connector = None
candles_trading_pair = None
interval = None
with c1:
connector_name = st.text_input("Connector", value=connector_name,
help="Enter the name of the exchange to trade on (e.g., binance_perpetual).")
with c2:
trading_pair = st.text_input("Trading Pair", value=trading_pair,
help="Enter the trading pair to trade on (e.g., WLD-USDT).")
with c3:
leverage = st.number_input("Leverage", value=leverage,
help="Set the leverage to use for trading (e.g., 20 for 20x leverage). Set it to 1 for spot trading.")
with c4:
total_amount_quote = st.number_input("Total amount of quote", value=total_amount_quote,
help="Enter the total amount in quote asset to use for trading (e.g., 1000).")
with c5:
position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=position_mode,
help="Enter the position mode (HEDGE/ONEWAY).")
with c6:
cooldown_time = st.number_input("Stop Loss Cooldown Time (minutes)", value=cooldown_time,
help="Specify the cooldown time in minutes after having a stop loss (e.g., 60).") * 60
with c7:
executor_refresh_time = st.number_input("Executor Refresh Time (minutes)", value=executor_refresh_time,
help="Enter the refresh time in minutes for executors (e.g., 60).") * 60
if custom_candles:
candles_connector = default_config.get("candles_connector", "kucoin")
candles_trading_pair = default_config.get("candles_trading_pair", "WLD-USDT")
interval = default_config.get("interval", "3m")
intervals = ["1m", "3m", "5m", "15m", "1h", "4h", "1d"]
interval_index = intervals.index(interval)
with c1:
candles_connector = st.text_input("Candles Connector", value=candles_connector,
help="Enter the name of the exchange to get candles from (e.g., binance_perpetual).")
with c2:
candles_trading_pair = st.text_input("Candles Trading Pair", value=candles_trading_pair,
help="Enter the trading pair to get candles for (e.g., WLD-USDT).")
with c3:
interval = st.selectbox("Candles Interval", intervals, index=interval_index,
help="Enter the interval for candles (e.g., 1m).")
return connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time, candles_connector, candles_trading_pair, interval

View File

@@ -2,8 +2,8 @@ from streamlit_elements import mui, lazy
import datetime
import constants
from utils.file_templates import strategy_optimization_template
from utils.os_utils import save_file, load_controllers
from backend.utils.file_templates import strategy_optimization_template
from backend.utils.os_utils import load_controllers, save_file
from .dashboard import Dashboard

View File

@@ -4,8 +4,7 @@ import optuna
from streamlit_elements import mui, lazy
import constants
from utils.os_utils import get_python_files_from_directory, \
get_function_from_file
from backend.utils.os_utils import get_function_from_file, get_python_files_from_directory
from .dashboard import Dashboard

View File

@@ -1,8 +1,8 @@
from streamlit_elements import mui
import constants
from ui_components.file_explorer_base import FileExplorerBase
from utils.os_utils import get_python_files_from_directory
from backend.utils.os_utils import get_python_files_from_directory
from frontend.components.file_explorer_base import FileExplorerBase
class OptimizationsStrategiesFileExplorer(FileExplorerBase):

View File

@@ -0,0 +1,38 @@
import streamlit as st
from hummingbot.connector.connector_base import OrderType
def get_risk_management_inputs():
default_config = st.session_state.get("default_config", {})
sl = default_config.get("stop_loss", 0.05) * 100
tp = default_config.get("take_profit", 0.02) * 100
time_limit = default_config.get("time_limit", 60 * 12 * 60) // 60
ts_ap = default_config.get("trailing_stop", {}).get("activation_price", 0.018) * 100
ts_delta = default_config.get("trailing_stop", {}).get("trailing_delta", 0.002) * 100
take_profit_order_type = OrderType(default_config.get("take_profit_order_type", 2))
order_types = [OrderType.LIMIT, OrderType.MARKET]
order_type_index = order_types.index(take_profit_order_type)
with st.expander("Risk Management", expanded=True):
c1, c2, c3, c4, c5, c6 = st.columns(6)
with c1:
sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=sl, step=0.1,
help="Enter the stop loss as a percentage (e.g., 2.0 for 2%).") / 100
with c2:
tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=tp, step=0.1,
help="Enter the take profit as a percentage (e.g., 3.0 for 3%).") / 100
with c3:
time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=time_limit,
help="Enter the time limit in minutes (e.g., 360 for 6 hours).") * 60
with c4:
ts_ap = st.number_input("Trailing Stop Act. Price (%)", min_value=0.0, max_value=100.0, value=ts_ap,
step=0.1,
help="Enter the tr ailing stop activation price as a percentage (e.g., 1.0 for 1%).") / 100
with c5:
ts_delta = st.number_input("Trailing Stop Delta (%)", min_value=0.0, max_value=100.0, value=ts_delta, step=0.1,
help="Enter the trailing stop delta as a percentage (e.g., 0.3 for 0.3%).") / 100
with c6:
take_profit_order_type = st.selectbox("Take Profit Order Type", (OrderType.LIMIT, OrderType.MARKET),
index=order_type_index,
help="Enter the order type for taking profit (LIMIT/MARKET).")
return sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type

View File

@@ -0,0 +1,22 @@
import streamlit as st
from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT
from backend.services.backend_api_client import BackendAPIClient
def render_save_config(controller_name: str, config_data: dict):
st.write("### Upload Config to BackendAPI")
c1, c2, c3 = st.columns([1, 1, 0.5])
connector = config_data.get("connector_name", "")
trading_pair = config_data.get("trading_pair", "")
with c1:
config_base = st.text_input("Config Base", value=f"{controller_name}-{connector}-{trading_pair.split('-')[0]}")
with c2:
config_tag = st.text_input("Config Tag", value="1.1")
with c3:
upload_config_to_backend = st.button("Upload")
if upload_config_to_backend:
config_data["id"] = f"{config_base}-{config_tag}"
backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
backend_api_client.add_controller_config(config_data)
st.success("Config uploaded successfully!")

View File

@@ -0,0 +1,79 @@
from _decimal import Decimal
from math import exp
from hummingbot.strategy_v2.utils.distributions import Distributions
def normalize(values):
total = sum(values)
return [val / total for val in values]
def distribution_inputs(column, dist_type_name, levels=3, default_values=None):
if dist_type_name == "Spread":
dist_type = column.selectbox(
f"Type of {dist_type_name} Distribution",
("Manual", "GeoCustom", "Geometric", "Fibonacci", "Logarithmic", "Arithmetic", "Linear"),
key=f"{column}_{dist_type_name.lower()}_dist_type",
# Set the default value
)
else:
dist_type = column.selectbox(
f"Type of {dist_type_name} Distribution",
("Manual", "Geometric", "Fibonacci", "Logarithmic", "Arithmetic"),
key=f"{column}_{dist_type_name.lower()}_dist_type",
# Set the default value
)
base, scaling_factor, step, ratio, manual_values = None, None, None, None, None
if dist_type != "Manual":
start = column.number_input(f"{dist_type_name} Start Value", value=1.0,
key=f"{column}_{dist_type_name.lower()}_start")
if dist_type == "Logarithmic":
base = column.number_input(f"{dist_type_name} Log Base", value=exp(1),
key=f"{column}_{dist_type_name.lower()}_base")
scaling_factor = column.number_input(f"{dist_type_name} Scaling Factor", value=2.0,
key=f"{column}_{dist_type_name.lower()}_scaling")
elif dist_type == "Arithmetic":
step = column.number_input(f"{dist_type_name} Step", value=0.3,
key=f"{column}_{dist_type_name.lower()}_step")
elif dist_type == "Geometric":
ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0,
key=f"{column}_{dist_type_name.lower()}_ratio")
elif dist_type == "GeoCustom":
ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0,
key=f"{column}_{dist_type_name.lower()}_ratio")
elif dist_type == "Linear":
step = column.number_input(f"{dist_type_name} End", value=1.0,
key=f"{column}_{dist_type_name.lower()}_end")
else:
if default_values:
manual_values = [column.number_input(f"{dist_type_name} for level {i + 1}", value=value * 100.0,
key=f"{column}_{dist_type_name.lower()}_{i}") for i, value in
enumerate(default_values)]
else:
manual_values = [column.number_input(f"{dist_type_name} for level {i + 1}", value=i + 1.0,
key=f"{column}_{dist_type_name.lower()}_{i}") for i, value in range(levels)]
start = None # As start is not relevant for Manual type
return dist_type, start, base, scaling_factor, step, ratio, manual_values
def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, step=None, ratio=None,
manual_values=None):
distribution = []
if dist_type == "Manual":
distribution = manual_values
elif dist_type == "Linear":
distribution = Distributions.linear(n_levels, start, step)
elif dist_type == "Fibonacci":
distribution = Distributions.fibonacci(n_levels, start)
elif dist_type == "Logarithmic":
distribution = Distributions.logarithmic(n_levels, base, scaling_factor, start)
elif dist_type == "Arithmetic":
distribution = Distributions.arithmetic(n_levels, start, step)
elif dist_type == "Geometric":
distribution = Distributions.geometric(n_levels, start, ratio)
elif dist_type == "GeoCustom":
distribution = [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio)
return [float(val) for val in distribution]

View File

View File

@@ -1,8 +1,8 @@
from hummingbot.core.data_type.common import PositionMode, TradeType, OrderType
from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig
from hummingbot.smart_components.strategy_frameworks.data_types import OrderLevel, TripleBarrierConf
from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine
from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder
from hummingbot.strategy_v2.strategy_frameworks.data_types import OrderLevel, TripleBarrierConf
from hummingbot.strategy_v2.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine
from hummingbot.strategy_v2.utils.config_encoder_decoder import ConfigEncoderDecoder
import constants
import os
@@ -10,14 +10,13 @@ import json
import streamlit as st
from decimal import Decimal
from quants_lab.strategy.strategy_analysis import StrategyAnalysis
from utils.graphs import BacktestingGraphs
from utils.optuna_database_manager import OptunaDBManager
from utils.os_utils import load_controllers
from utils.st_utils import initialize_st_page
from backend.utils.optuna_database_manager import OptunaDBManager
from backend.utils.os_utils import load_controllers
from frontend.visualization.graphs import BacktestingGraphs
from frontend.visualization.strategy_analysis import StrategyAnalysis
from frontend.st_utils import initialize_st_page
initialize_st_page(title="Analyze", icon="🔬", initial_sidebar_state="collapsed")
initialize_st_page(title="Analyze", icon="🔬")
BASE_DATA_DIR = "data/backtesting"

View File

@@ -3,15 +3,13 @@ from types import SimpleNamespace
import streamlit as st
from streamlit_elements import elements, mui
from ui_components.dashboard import Dashboard
from ui_components.controllers_file_explorer import ControllersFileExplorer
from ui_components.directional_strategy_creation_card import DirectionalStrategyCreationCard
from ui_components.editor import Editor
from frontend.components.dashboard import Dashboard
from frontend.components.controllers_file_explorer import ControllersFileExplorer
from frontend.components.directional_strategy_creation_card import DirectionalStrategyCreationCard
from frontend.components.editor import Editor
from frontend.st_utils import initialize_st_page
from utils.st_utils import initialize_st_page
initialize_st_page(title="Create", icon="️⚔️", initial_sidebar_state="collapsed")
initialize_st_page(title="Create", icon="️⚔️")
# TODO:
# * Add videos explaining how to the triple barrier method works and how the backtesting is designed,

View File

@@ -5,16 +5,15 @@ from types import SimpleNamespace
import streamlit as st
from streamlit_elements import elements, mui
from ui_components.dashboard import Dashboard
from ui_components.editor import Editor
from ui_components.optimization_creation_card import OptimizationCreationCard
from ui_components.optimization_run_card import OptimizationRunCard
from ui_components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer
from utils import os_utils
from frontend.components.dashboard import Dashboard
from frontend.components.editor import Editor
from frontend.components.optimization_creation_card import OptimizationCreationCard
from frontend.components.optimization_run_card import OptimizationRunCard
from frontend.components.optimizations_file_explorer import OptimizationsStrategiesFileExplorer
from backend.utils import os_utils
from frontend.st_utils import initialize_st_page
from utils.st_utils import initialize_st_page
initialize_st_page(title="Optimize", icon="🧪", initial_sidebar_state="collapsed")
initialize_st_page(title="Optimize", icon="🧪")
def run_optuna_dashboard():
os_utils.execute_bash_command(f"optuna-dashboard sqlite:///data/backtesting/backtesting_report.db")

View File

View File

@@ -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. Heres 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 bands 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 strategys 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!

View File

@@ -0,0 +1,71 @@
from datetime import datetime
import streamlit as st
import pandas as pd
import yaml
import pandas_ta as ta # noqa: F401
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.save_config import render_save_config
from frontend.pages.config.utils import get_max_records, get_candles
from frontend.st_utils import initialize_st_page
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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
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"] = 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=3)
# 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("bollinger_v1", inputs)

View File

@@ -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
}

View File

@@ -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. Heres 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 strategys 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!

View File

@@ -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
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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
# 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"] = 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("dman_maker_v2", config)

View File

@@ -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

View File

@@ -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.

View File

@@ -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
# 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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
backend_api_client.add_controller_config(config)
st.success("Config uploaded successfully!")

View File

@@ -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.

View File

@@ -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
# 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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
backend_api_client.add_controller_config(config)
st.success("Config uploaded successfully!")

View File

@@ -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. Heres 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 bands 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 strategys 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!

View File

@@ -0,0 +1,67 @@
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.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, get_max_records
from frontend.st_utils import 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.candles import get_candlestick_trace
from frontend.visualization.indicators import get_bbands_traces, get_volume_trace, 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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
get_default_config_loader("macd_bb_v1")
# User inputs
inputs = user_inputs()
st.session_state["default_config"] = inputs
st.write("### Visualizing MACD Bollinger Trading Signals")
days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=3)
# 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("bollinger_v1", inputs)

View File

@@ -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
}

View File

@@ -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. Heres 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 strategys 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!

View File

@@ -0,0 +1,87 @@
import streamlit as st
import plotly.graph_objects as go
from plotly.subplots import make_subplots
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.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_max_records, get_candles
from frontend.st_utils import initialize_st_page
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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
# 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=3)
# 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(default_spreads=[1, 2], default_amounts=[1, 2])
inputs["buy_spreads"] = buy_spread_distributions
inputs["sell_spreads"] = sell_spread_distributions
inputs["buy_amounts_pct"] = buy_order_amounts_pct
inputs["sell_amounts_pct"] = sell_order_amounts_pct
st.session_state["default_config"] = 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("pmm_dynamic", inputs)

View File

@@ -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

View File

@@ -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

View File

@@ -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. Heres 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 strategys 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.

View File

@@ -0,0 +1,44 @@
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
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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
# 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()
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("pmm_simple", inputs)

View File

@@ -0,0 +1,39 @@
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
}
}
st.session_state["default_config"] = config
return config

View File

@@ -1,15 +1,14 @@
from math import exp
import streamlit as st
from plotly.subplots import make_subplots
import plotly.graph_objects as go
from decimal import Decimal
import yaml
from utils.st_utils import initialize_st_page
from hummingbot.smart_components.utils.distributions import Distributions
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="🔭", initial_sidebar_state="collapsed")
initialize_st_page(title="Position Generator", icon="🔭")
# Page content
st.text("This tool will help you analyze and generate a position config.")
@@ -18,12 +17,6 @@ st.write("---")
# Layout in columns
col_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2])
def normalize(values):
total = sum(values)
return [val / total for val in values]
def convert_to_yaml(spreads, order_amounts):
data = {
'dca_spreads': [float(spread)/100 for spread in spreads],
@@ -43,61 +36,9 @@ with col_levels:
n_levels = st.number_input("Number of Levels", min_value=1, value=5)
def distribution_inputs(column, dist_type_name):
if dist_type_name == "Spread":
dist_type = column.selectbox(
f"Type of {dist_type_name} Distribution",
("GeoCustom", "Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"),
key=f"{dist_type_name.lower()}_dist_type",
# Set the default value
)
else:
dist_type = column.selectbox(
f"Type of {dist_type_name} Distribution",
("Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"),
key=f"{dist_type_name.lower()}_dist_type",
# Set the default value
)
base, scaling_factor, step, ratio, manual_values = None, None, None, None, None
if dist_type != "Manual":
start = column.number_input(f"{dist_type_name} Start Value", value=1.0, key=f"{dist_type_name.lower()}_start")
if dist_type == "Logarithmic":
base = column.number_input(f"{dist_type_name} Log Base", value=exp(1), key=f"{dist_type_name.lower()}_base")
scaling_factor = column.number_input(f"{dist_type_name} Scaling Factor", value=2.0, key=f"{dist_type_name.lower()}_scaling")
elif dist_type == "Arithmetic":
step = column.number_input(f"{dist_type_name} Step", value=0.1, key=f"{dist_type_name.lower()}_step")
elif dist_type == "Geometric":
ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio")
elif dist_type == "GeoCustom":
ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio")
else:
manual_values = [column.number_input(f"{dist_type_name} for level {i+1}", value=1.0, key=f"{dist_type_name.lower()}_{i}") for i in range(n_levels)]
start = None # As start is not relevant for Manual type
return dist_type, start, base, scaling_factor, step, ratio, manual_values
# Spread and Amount Distributions
spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs(col_spread_dist, "Spread")
amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs(col_amount_dist, "Amount")
def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, step=None, ratio=None, manual_values=None):
if dist_type == "Manual":
return manual_values
elif dist_type == "Linear":
return Distributions.linear(n_levels, start, start + tp)
elif dist_type == "Fibonacci":
return Distributions.fibonacci(n_levels, start)
elif dist_type == "Logarithmic":
return Distributions.logarithmic(n_levels, base, scaling_factor, start)
elif dist_type == "Arithmetic":
return Distributions.arithmetic(n_levels, start, step)
elif dist_type == "Geometric":
return Distributions.geometric(n_levels, start, ratio)
elif dist_type == "GeoCustom":
return [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio)
spread_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))

View File

@@ -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. Heres 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 strategys 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!

View File

@@ -0,0 +1,64 @@
import streamlit as st
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.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, get_max_records
from frontend.st_utils import 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.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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
get_default_config_loader("supertrend_v1")
# User inputs
inputs = user_inputs()
st.session_state["default_config"] = inputs
st.write("### Visualizing Supertrend Trading Signals")
days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=3)
# 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("bollinger_v1", inputs)

View File

@@ -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
}

View File

@@ -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

View File

@@ -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. Heres 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!

View File

@@ -0,0 +1,140 @@
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
# 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:
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 = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT)
backend_api_client.add_controller_config(config)
st.success("Config uploaded successfully!")

View File

View File

@@ -0,0 +1,70 @@
import streamlit as st
from datetime import datetime, time
import pandas as pd
import plotly.graph_objects as go
from backend.services.backend_api_client import BackendAPIClient
from frontend.st_utils import initialize_st_page
# Initialize Streamlit page
initialize_st_page(title="Download Candles", icon="💾")
backend_api_client = BackendAPIClient.get_instance()
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',
)

View File

@@ -1,17 +1,15 @@
import streamlit as st
from pathlib import Path
import plotly.express as px
import CONFIG
from utils.coingecko_utils import CoinGeckoUtils
from utils.miner_utils import MinerUtils
from utils.st_utils import initialize_st_page
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 = CoinGeckoUtils()
miner_utils = MinerUtils()
cg_utils = CoinGeckoClient()
miner_utils = MinerClient()
@st.cache_data
def get_all_coins_df():

Some files were not shown because too many files have changed in this diff Show More