Merge pull request #108 from hummingbot/feat/supertrend_multitimeframe_example

Feat/supertrend multitimeframe example
This commit is contained in:
Michael Feng
2023-12-15 06:29:56 -08:00
committed by GitHub
14 changed files with 96513 additions and 675 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
import time
from typing import Optional, Callable
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 (
DirectionalTradingControllerBase,
DirectionalTradingControllerConfigBase,
)
class SuperTrendMTConfig(DirectionalTradingControllerConfigBase):
strategy_name: str = "supertrend_multitimeframe"
length: int = Field(default=20, ge=5, le=200)
multiplier: float = Field(default=4.0, ge=2.0, le=7.0)
percentage_threshold: float = Field(default=0.01, ge=0.005, le=0.05)
class SuperTrendMT(DirectionalTradingControllerBase):
def __init__(self, config: SuperTrendMTConfig):
super().__init__(config)
self.config = config
def early_stop_condition(self, executor: PositionExecutor, order_level: OrderLevel) -> bool:
# If an executor has an active position, should we close it based on a condition. This feature is not available
# for the backtesting yet
return False
def cooldown_condition(self, executor: PositionExecutor, order_level: OrderLevel) -> bool:
# After finishing an order, the executor will be in cooldown for a certain amount of time.
# This prevents the executor from creating a new order immediately after finishing one and execute a lot
# of orders in a short period of time from the same side.
if executor.close_timestamp and executor.close_timestamp + order_level.cooldown_time > time.time():
return True
return False
@staticmethod
def get_minutes_from_interval(interval: str):
unit = interval[-1]
quantity = int(interval[:-1])
conversion = {"m": 1, "h": 60, "d": 1440}
return conversion[unit] * quantity
def ordered_market_data_dfs(self):
market_data = {f"{candles.name}_{candles.interval}": candles.candles_df for candles in self.candles}
return sorted(market_data.items(), key=lambda x: self.get_minutes_from_interval(x[0].split("_")[-1]))
def get_dataframes_merged_by_min_resolution(self, add_indicators_func: Optional[Callable] = None):
ordered_data = self.ordered_market_data_dfs()
if add_indicators_func:
processed_data = []
for interval, df in ordered_data:
processed_df = add_indicators_func(df)
processed_data.append((interval, processed_df))
else:
processed_data = ordered_data
interval_suffixes = {key: f'_{key.split("_")[-1]}' for key, _ in processed_data}
merged_df = None
for interval, df in processed_data:
if merged_df is None:
merged_df = df.copy()
else:
merged_df = pd.merge_asof(merged_df, df.add_suffix(interval_suffixes[interval]),
left_on=f"timestamp", right_on=f"timestamp{interval_suffixes[interval]}",
direction="backward")
return merged_df
def add_indicators(self, df):
df.ta.supertrend(length=self.config.length, multiplier=self.config.multiplier, append=True)
return df
def get_processed_data(self) -> pd.DataFrame:
df = self.get_dataframes_merged_by_min_resolution(self.add_indicators)
df["percentage_distance"] = abs(df["close"] - df[f"SUPERT_{self.config.length}_{self.config.multiplier}"]) / df["close"]
columns_with_supertrend = [col for col in df.columns if "SUPERTd" in col]
# Conditions for long and short signals
long_condition = df[columns_with_supertrend].apply(lambda x: all(item == 1 for item in x), axis=1)
short_condition = df[columns_with_supertrend].apply(lambda x: all(item == -1 for item in x), axis=1)
# Choose side
df['signal'] = 0
df.loc[long_condition, 'signal'] = 1
df.loc[short_condition, 'signal'] = -1
return df

File diff suppressed because one or more lines are too long

View File

@@ -1,215 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"source": [
"# RESEARCH NOTEBOOK --> SUPERTREND"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import pandas as pd\n",
"import pandas_ta as ta # noqa: F401\n",
"\n",
"candles = pd.read_csv(\n",
" \"/Users/dardonacci/Documents/work/dashboard/data/candles/candles_binance_perpetual_WLD-USDT_3m.csv\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"candles.head()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"super_trend_lenght = 20\n",
"super_trend_multiplier = 3\n",
"candles.ta.supertrend(length=super_trend_lenght, multiplier=super_trend_multiplier)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"candles.ta.supertrend(length=super_trend_lenght, multiplier=super_trend_multiplier, append=True)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"candles[\"date\"] = pd.to_datetime(candles[\"timestamp\"], unit='ms')\n",
"candles.head()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import plotly.graph_objects as go\n",
"\n",
"# We are going to use just a subset to see the graph better\n",
"candles = candles.tail(800)\n",
"\n",
"# Create a candlestick chart\n",
"fig = go.Figure(data=[go.Candlestick(\n",
" x=candles['date'],\n",
" open=candles['open'],\n",
" high=candles['high'],\n",
" low=candles['low'],\n",
" close=candles['close'])\n",
"])\n",
"fig.show()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"super_trend_long = candles[candles[\"SUPERTd_20_3.0\"] == 1]\n",
"super_trend_short = candles[candles[\"SUPERTd_20_3.0\"] == -1]\n",
"# Add the SuperTrend line\n",
"fig.add_trace(go.Scatter(x=super_trend_long['date'], y=super_trend_long['SUPERT_20_3.0'],\n",
" mode='markers',\n",
" name='SuperTrend Long',\n",
" line=dict(color=\"green\"),\n",
" ))\n",
"# Add the SuperTrend line\n",
"fig.add_trace(go.Scatter(x=super_trend_short['date'], y=super_trend_short['SUPERT_20_3.0'],\n",
" mode='markers',\n",
" name='SuperTrend Short',\n",
" line=dict(color=\"red\")))"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"percentage_threshold = 0.01 # This is an example threshold value\n",
"\n",
"candles[\"percentage_distance\"] = abs(candles[\"close\"] - candles[\"SUPERT_20_3.0\"]) / candles[\"close\"]\n",
"\n",
"candles[\"signal\"] = 0\n",
"long_condition = (candles[\"SUPERTd_20_3.0\"] == 1) & (candles[\"percentage_distance\"] < percentage_threshold)\n",
"short_condition = (candles[\"SUPERTd_20_3.0\"] == -1) & (candles[\"percentage_distance\"] < percentage_threshold)\n",
"\n",
"candles.loc[long_condition, \"signal\"] = 1\n",
"candles.loc[short_condition, \"signal\"] = -1"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"from plotly.subplots import make_subplots\n",
"\n",
"fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.02, subplot_titles=('OHLC', 'Signal'),\n",
" row_heights=[0.7, 0.3])\n",
"\n",
"# Add candlestick\n",
"fig.add_trace(go.Candlestick(\n",
" x=candles['date'],\n",
" open=candles['open'],\n",
" high=candles['high'],\n",
" low=candles['low'],\n",
" close=candles['close']),\n",
" row=1, col=1)\n",
"\n",
"# Add the SuperTrend line\n",
"fig.add_trace(go.Scatter(x=super_trend_long['date'], y=super_trend_long['SUPERT_20_3.0'],\n",
" mode='markers',\n",
" name='SuperTrend Long',\n",
" line=dict(color=\"green\")),\n",
" row=1, col=1)\n",
"# Add the SuperTrend line\n",
"fig.add_trace(go.Scatter(x=super_trend_short['date'], y=super_trend_short['SUPERT_20_3.0'],\n",
" mode='markers',\n",
" name='SuperTrend Short',\n",
" line=dict(color=\"red\")),\n",
" row=1, col=1)\n",
"\n",
"# Add the signal line\n",
"fig.add_trace(go.Scatter(x=candles['date'], y=candles['signal'],\n",
" mode='lines',\n",
" name='SuperTrend',\n",
" line=dict(color=\"white\")),\n",
" row=2, col=1)\n",
"\n",
"# Update x-axis and grid properties\n",
"fig.update_xaxes(showline=True, linewidth=2, linecolor='grey', gridcolor='lightgrey')\n",
"fig.update_yaxes(showline=True, linewidth=2, linecolor='grey', gridcolor='lightgrey')\n",
"\n",
"# Update layout to adjust the size and title\n",
"fig.update_layout(height=800, title_text=\"OHLC Chart with SuperTrend and Signals\",\n",
" yaxis_title='Price',\n",
" xaxis_rangeslider_visible=False)\n"
],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -1,444 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../..'))\n",
"sys.path.append(root_path)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"\n",
"from utils.optuna_database_manager import OptunaDBManager\n",
"\n",
"db_root_path = \"../../data/backtesting/\"\n",
"db_name = \"backtesting_report.db\"\n",
"\n",
"optuna_db_manager = OptunaDBManager(db_name=db_name,\n",
" db_root_path=db_root_path)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"optuna_db_manager.studies"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"study_name = \"super_trend_optimization_1\"\n",
"df = optuna_db_manager.merged_df[optuna_db_manager.merged_df[\"study_name\"] == study_name]"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"df"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Bar Chart of Average Trading Time\n",
"This bar chart compares the average trading time across trials. It helps to quickly identify trials with unusually long or short trading times.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import plotly.express as px\n",
"\n",
"fig = px.bar(df, x='trial_id', y='avg_trading_time_in_hours', title='Average Trading Time per Trial')\n",
"fig.show()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Scatter Plot of Accuracy vs. Average Trading Time\n",
"This scatter plot shows the relationship between the accuracy and the average trading time of each trial. It can help to identify if there is any correlation between these two metrics.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"fig = px.scatter(df, x='avg_trading_time_in_hours', y='accuracy', title='Accuracy vs. Average Trading Time')\n",
"fig.show()\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Histogram of Total Positions\n",
"The histogram represents the distribution of total positions across all trials. This visualization is useful for understanding the general spread and most common values of positions.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"fig = px.histogram(df, x='total_positions', title='Distribution of Total Positions')\n",
"fig.show()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Pie Chart of Win Signals\n",
"This pie chart shows the proportion of win signals in each trial, providing a visual representation of the success rate distribution across trials.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"fig = px.pie(df, names='trial_id', values='win_signals', title='Proportion of Win Signals per Trial')\n",
"fig.show()\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Box Plot for Trial Value\n",
"A box plot for trial values to identify the range, median, and any potential outliers in the trial values.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"fig = px.box(df, y='value', title='Box Plot of Trial PNL')\n",
"fig.show()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Heatmap for Correlation Analysis\n",
"This heatmap illustrates the correlation between various numerical variables such as accuracy, average trading time, total positions, win signals, and value.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import plotly.express as px\n",
"\n",
"# Calculate the correlation matrix\n",
"correlation_matrix = df[['accuracy', 'avg_trading_time_in_hours', 'total_positions', 'win_signals', 'value']].corr()\n",
"\n",
"# Generate the heatmap\n",
"fig = px.imshow(correlation_matrix,\n",
" x=correlation_matrix.columns,\n",
" y=correlation_matrix.columns,\n",
" title='Correlation Heatmap',\n",
" labels=dict(x=\"Variable\", y=\"Variable\", color=\"Correlation\"),\n",
" color_continuous_scale='RdBu')\n",
"\n",
"fig.show()\n",
"\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Stacked Bar Chart for Win Signals vs Total Positions\n",
"A stacked bar chart displaying the ratio of win signals to total positions for each trial. This helps in visualizing the efficiency and effectiveness of each trial.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"df['loss_signals'] = df['total_positions'] - df['win_signals']\n",
"fig = px.bar(df, x='trial_id', y=['win_signals', 'loss_signals'], title='Win vs Loss Signals per Trial', labels={'value':'Number of Signals'}, hover_data=['total_positions'])\n",
"fig.show()\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Scatter Plots Against PNL\n",
"These scatter plots show how various metrics behave in relation to the PNL (Profit and Loss). This analysis can help in understanding which factors have a stronger relationship with financial outcomes.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"# Scatter Plot for Accuracy vs PNL\n",
"fig_accuracy_pnl = px.scatter(df, x='accuracy', y='value', title='Accuracy vs PNL')\n",
"fig_accuracy_pnl.show()\n",
"\n",
"# Scatter Plot for Average Trading Time vs PNL\n",
"fig_tradingtime_pnl = px.scatter(df, x='avg_trading_time_in_hours', y='value', title='Average Trading Time vs PNL')\n",
"fig_tradingtime_pnl.show()\n",
"\n",
"# Scatter Plot for Total Positions vs PNL\n",
"fig_positions_pnl = px.scatter(df, x='total_positions', y='value', title='Total Positions vs PNL')\n",
"fig_positions_pnl.show()\n",
"\n",
"# Scatter Plot for Win Signals vs PNL\n",
"fig_winsignals_pnl = px.scatter(df, x='win_signals', y='value', title='Win Signals vs PNL')\n",
"fig_winsignals_pnl.show()\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Scatter Matrix of All Variables\n",
"A scatter matrix allows us to see both the distribution of single variables and the relationships between two variables. The diagonal shows histograms for each variable, and the scatter plots show correlations between them.\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import plotly.express as px\n",
"\n",
"# Selecting columns for scatter matrix\n",
"selected_columns = ['accuracy', 'avg_trading_time_in_hours', 'total_positions', 'win_signals', 'value']\n",
"\n",
"# Creating scatter matrix\n",
"fig_matrix = px.scatter_matrix(df[selected_columns], dimensions=selected_columns, title='Scatter Matrix of Variables', height=800)\n",
"fig_matrix.show()\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import json\n",
"from hummingbot.core.data_type.common import PositionMode\n",
"from hummingbot.connector.connector_base import TradeType, OrderType\n",
"from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder\n",
"\n",
"trial_to_analyze = 36\n",
"\n",
"trial = df[df[\"trial_id\"] == trial_to_analyze]"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"trial"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"# Transform trial config in a dictionary\n",
"encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode)\n",
"trial_config = encoder_decoder.decode(json.loads(trial[\"config\"].item()))\n",
"trial_config"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"from quants_lab.controllers.supertrend import SuperTrend, SuperTrendConfig\n",
"\n",
"# In this case we are using the supertrend controller but we can also access to the controller by using the method load_controllers\n",
"config = SuperTrendConfig(**trial_config)\n",
"controller = SuperTrend(config)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"from quants_lab.strategy.strategy_analysis import StrategyAnalysis\n",
"from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine\n",
"\n",
"# Backtest configuration\n",
"trade_cost = 0.0006\n",
"initial_portfolio_usd = 1000\n",
"start = \"2023-01-01\"\n",
"end = \"2024-01-02\"\n",
"\n",
"# Load the data\n",
"engine = DirectionalTradingBacktestingEngine(controller=controller)\n",
"engine.load_controller_data(\"../../data/candles\")\n",
"backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd,\n",
" trade_cost=trade_cost,\n",
" start=start, end=end)\n",
"strategy_analysis = StrategyAnalysis(\n",
" positions=backtesting_results[\"executors_df\"],\n",
" candles_df=backtesting_results[\"processed_data\"],\n",
")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"strategy_analysis.create_base_figure(volume=False, positions=False, trade_pnl=True)\n",
"strategy_analysis.figure()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"strategy_analysis.pnl_over_time()"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@
"import os\n",
"import sys\n",
"\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../..'))\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../../..'))\n",
"sys.path.append(root_path)"
]
},
@@ -150,7 +150,7 @@
"\n",
"# Creating the backtesting engine and loading the historical data\n",
"engine = DirectionalTradingBacktestingEngine(controller=controller)\n",
"engine.load_controller_data(\"../../data/candles\")"
"engine.load_controller_data(\"../../../data/candles\")"
]
},
{

View File

@@ -8,7 +8,7 @@
"import os\n",
"import sys\n",
"\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../..'))\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../../..'))\n",
"sys.path.append(root_path)"
],
"metadata": {
@@ -93,7 +93,7 @@
" )\n",
" controller = SuperTrend(config=config)\n",
" engine = DirectionalTradingBacktestingEngine(controller=controller)\n",
" engine.load_controller_data(\"../../data/candles\")\n",
" engine.load_controller_data(\"../../../data/candles\")\n",
" backtesting_results = engine.run_backtesting(\n",
" initial_portfolio_usd=initial_portfolio_usd,\n",
" trade_cost=trade_cost,\n",
@@ -133,7 +133,7 @@
"\n",
"# Now let's configure the parameters for the optimization\n",
"study_name = \"super_trend_optimization_1\"\n",
"storage= \"sqlite:///../../data/backtesting/backtesting_report.db\"\n",
"storage= \"sqlite:///../../../data/backtesting/backtesting_report.db\"\n",
"\n",
"study = optuna.create_study(direction=\"maximize\", study_name=study_name,\n",
" storage=storage,\n",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,241 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"\n",
"root_path = os.path.abspath(os.path.join(os.getcwd(), '../../..'))\n",
"sys.path.append(root_path)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 2,
"outputs": [],
"source": [
"import traceback\n",
"from decimal import Decimal\n",
"import pandas_ta as ta # noqa: F401\n",
"\n",
"from hummingbot.core.data_type.common import PositionMode, TradeType, OrderType\n",
"from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig\n",
"from hummingbot.smart_components.strategy_frameworks.data_types import TripleBarrierConf, OrderLevel\n",
"from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingBacktestingEngine\n",
"from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder\n",
"from optuna import TrialPruned\n",
"from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder\n",
"\n",
"from quants_lab.controllers.supertrend_multitimeframe import SuperTrendMT, SuperTrendMTConfig\n",
"\n",
"# To run an optimization with optuna we need to define the objective function that will be executed for each trial\n",
"\n",
"def objective(trial):\n",
" try:\n",
" # Market configuration\n",
" exchange = \"binance_perpetual\"\n",
" trading_pair = \"WLD-USDT\"\n",
" intervals = [\"3m\", \"1h\"]\n",
"\n",
" # Account configuration\n",
" initial_portfolio_usd = 1000\n",
" order_amount = Decimal(\"25\")\n",
" n_levels = 1\n",
" leverage = 20\n",
" trade_cost = 0.0006\n",
"\n",
" # Backtest period\n",
" start = \"2023-01-01\"\n",
" end = \"2024-01-02\"\n",
"\n",
" # Triple barrier configuration\n",
" stop_loss = trial.suggest_float('stop_loss', 0.01, 0.02, step=0.01)\n",
" take_profit = trial.suggest_float('take_profit', 0.01, 0.04, step=0.01)\n",
" time_limit = 60 * 60 * 12 # 12 hours\n",
" trailing_stop_activation_price_delta = Decimal(\"0.008\")\n",
" trailing_stop_trailing_delta = Decimal(\"0.004\")\n",
"\n",
" length = trial.suggest_int('length', 20, 200, step=20)\n",
" multiplier = trial.suggest_float('multiplier', 2.0, 6.0, step=1.0)\n",
" percentage_threshold = trial.suggest_float('percentage_threshold', 0.01, 0.03, step=0.01)\n",
"\n",
" # Building the order levels\n",
" order_level_builder = OrderLevelBuilder(n_levels=n_levels)\n",
" order_levels = order_level_builder.build_order_levels(\n",
" amounts=order_amount,\n",
" spreads=Decimal(\"0\"),\n",
" triple_barrier_confs=TripleBarrierConf(\n",
" stop_loss=stop_loss, take_profit=take_profit, time_limit=time_limit,\n",
" trailing_stop_activation_price_delta=trailing_stop_activation_price_delta,\n",
" trailing_stop_trailing_delta=trailing_stop_trailing_delta),\n",
" )\n",
" config = SuperTrendMTConfig(\n",
" exchange=exchange,\n",
" trading_pair=trading_pair,\n",
" strategy_name='supertrend_multitimeframe',\n",
" candles_config=[\n",
" CandlesConfig(connector=exchange, trading_pair=trading_pair,\n",
" interval=intervals[0], max_records=sys.maxsize),\n",
" CandlesConfig(connector=exchange, trading_pair=trading_pair,\n",
" interval=intervals[1], max_records=sys.maxsize),\n",
" ],\n",
" order_levels=order_levels,\n",
" leverage=leverage,\n",
" position_mode=PositionMode.HEDGE,\n",
" length=length,\n",
" multiplier=multiplier,\n",
" percentage_threshold=percentage_threshold,\n",
"\n",
" )\n",
" controller = SuperTrendMT(config=config)\n",
" engine = DirectionalTradingBacktestingEngine(controller=controller)\n",
" engine.load_controller_data(\"../../../data/candles\")\n",
" backtesting_results = engine.run_backtesting(\n",
" initial_portfolio_usd=initial_portfolio_usd,\n",
" trade_cost=trade_cost,\n",
" start=start, end=end)\n",
"\n",
" strategy_analysis = backtesting_results[\"results\"]\n",
" encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode)\n",
"\n",
" trial.set_user_attr(\"net_pnl_quote\", strategy_analysis[\"net_pnl_quote\"])\n",
" trial.set_user_attr(\"net_pnl_pct\", strategy_analysis[\"net_pnl\"])\n",
" trial.set_user_attr(\"max_drawdown_usd\", strategy_analysis[\"max_drawdown_usd\"])\n",
" trial.set_user_attr(\"max_drawdown_pct\", strategy_analysis[\"max_drawdown_pct\"])\n",
" trial.set_user_attr(\"sharpe_ratio\", strategy_analysis[\"sharpe_ratio\"])\n",
" trial.set_user_attr(\"accuracy\", strategy_analysis[\"accuracy\"])\n",
" trial.set_user_attr(\"total_positions\", strategy_analysis[\"total_positions\"])\n",
" trial.set_user_attr(\"profit_factor\", strategy_analysis[\"profit_factor\"])\n",
" trial.set_user_attr(\"duration_in_hours\", strategy_analysis[\"duration_minutes\"] / 60)\n",
" trial.set_user_attr(\"avg_trading_time_in_hours\", strategy_analysis[\"avg_trading_time_minutes\"] / 60)\n",
" trial.set_user_attr(\"win_signals\", strategy_analysis[\"win_signals\"])\n",
" trial.set_user_attr(\"loss_signals\", strategy_analysis[\"loss_signals\"])\n",
" trial.set_user_attr(\"config\", encoder_decoder.encode(config.dict()))\n",
" return strategy_analysis[\"net_pnl\"]\n",
" except Exception as e:\n",
" traceback.print_exc()\n",
" raise TrialPruned()\n"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2023-12-12T01:02:29.309413Z",
"start_time": "2023-12-12T01:02:29.300687Z"
}
}
},
{
"cell_type": "code",
"execution_count": 4,
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[I 2023-12-11 22:02:37,318] A new study created in RDB with name: super_trend_optimization_mt\n"
]
}
],
"source": [
"import optuna\n",
"\n",
"# Now let's configure the parameters for the optimization\n",
"study_name = \"super_trend_optimization_mt\"\n",
"storage= \"sqlite:///../../../data/backtesting/backtesting_report.db\"\n",
"\n",
"study = optuna.create_study(direction=\"maximize\", study_name=study_name,\n",
" storage=storage,\n",
" load_if_exists=True # If the study already exists, we load it\n",
" )"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2023-12-12T01:02:37.322291Z",
"start_time": "2023-12-12T01:02:37.245002Z"
}
}
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[I 2023-12-11 22:03:00,563] Trial 0 finished with value: -0.35098199626293336 and parameters: {'stop_loss': 0.02, 'take_profit': 0.02, 'length': 40, 'multiplier': 6.0, 'percentage_threshold': 0.03}. Best is trial 0 with value: -0.35098199626293336.\n",
"[I 2023-12-11 22:03:17,061] Trial 1 finished with value: 0.12488305093272298 and parameters: {'stop_loss': 0.02, 'take_profit': 0.03, 'length': 140, 'multiplier': 3.0, 'percentage_threshold': 0.01}. Best is trial 1 with value: 0.12488305093272298.\n",
"[I 2023-12-11 22:03:33,423] Trial 2 finished with value: -0.4536291130718499 and parameters: {'stop_loss': 0.01, 'take_profit': 0.02, 'length': 120, 'multiplier': 5.0, 'percentage_threshold': 0.02}. Best is trial 1 with value: 0.12488305093272298.\n",
"[I 2023-12-11 22:03:49,953] Trial 3 finished with value: -0.05073410868582624 and parameters: {'stop_loss': 0.01, 'take_profit': 0.03, 'length': 100, 'multiplier': 4.0, 'percentage_threshold': 0.01}. Best is trial 1 with value: 0.12488305093272298.\n",
"[I 2023-12-11 22:04:07,164] Trial 4 finished with value: 0.1619153622180836 and parameters: {'stop_loss': 0.02, 'take_profit': 0.03, 'length': 200, 'multiplier': 6.0, 'percentage_threshold': 0.01}. Best is trial 4 with value: 0.1619153622180836.\n",
"[I 2023-12-11 22:04:24,073] Trial 5 finished with value: -0.18547605063707406 and parameters: {'stop_loss': 0.02, 'take_profit': 0.01, 'length': 40, 'multiplier': 3.0, 'percentage_threshold': 0.02}. Best is trial 4 with value: 0.1619153622180836.\n",
"[I 2023-12-11 22:04:41,109] Trial 6 finished with value: -0.3164755434895661 and parameters: {'stop_loss': 0.02, 'take_profit': 0.01, 'length': 180, 'multiplier': 6.0, 'percentage_threshold': 0.01}. Best is trial 4 with value: 0.1619153622180836.\n",
"[I 2023-12-11 22:04:57,609] Trial 7 finished with value: -0.1038527397065552 and parameters: {'stop_loss': 0.01, 'take_profit': 0.02, 'length': 140, 'multiplier': 4.0, 'percentage_threshold': 0.03}. Best is trial 4 with value: 0.1619153622180836.\n",
"[I 2023-12-11 22:05:15,271] Trial 8 finished with value: 0.7404879450347805 and parameters: {'stop_loss': 0.01, 'take_profit': 0.01, 'length': 40, 'multiplier': 2.0, 'percentage_threshold': 0.01}. Best is trial 8 with value: 0.7404879450347805.\n",
"[I 2023-12-11 22:05:33,215] Trial 9 finished with value: -0.960719787819319 and parameters: {'stop_loss': 0.01, 'take_profit': 0.01, 'length': 60, 'multiplier': 6.0, 'percentage_threshold': 0.01}. Best is trial 8 with value: 0.7404879450347805.\n",
"[I 2023-12-11 22:05:50,582] Trial 10 finished with value: 1.1998584529952074 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 80, 'multiplier': 2.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:06:07,814] Trial 11 finished with value: 1.1998584529952074 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 80, 'multiplier': 2.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:06:24,455] Trial 12 finished with value: 1.1998584529952074 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 80, 'multiplier': 2.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:06:41,101] Trial 13 finished with value: 1.1998584529952074 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 80, 'multiplier': 2.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:06:57,606] Trial 14 finished with value: 0.02047001353630673 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 20, 'multiplier': 3.0, 'percentage_threshold': 0.03}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:07:14,208] Trial 15 finished with value: 0.9931433766610962 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 100, 'multiplier': 2.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:07:30,823] Trial 16 finished with value: 0.37864993105614836 and parameters: {'stop_loss': 0.01, 'take_profit': 0.03, 'length': 80, 'multiplier': 3.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:07:47,525] Trial 17 finished with value: 1.089455391608849 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 140, 'multiplier': 2.0, 'percentage_threshold': 0.03}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:08:04,394] Trial 18 finished with value: -0.2011129787326267 and parameters: {'stop_loss': 0.01, 'take_profit': 0.04, 'length': 60, 'multiplier': 5.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n",
"[I 2023-12-11 22:08:20,927] Trial 19 finished with value: 0.19209940796981184 and parameters: {'stop_loss': 0.01, 'take_profit': 0.03, 'length': 120, 'multiplier': 3.0, 'percentage_threshold': 0.02}. Best is trial 10 with value: 1.1998584529952074.\n"
]
}
],
"source": [
"# Not let's run the optimization!\n",
"\n",
"n_trials = 20\n",
"study.optimize(objective, n_trials=n_trials)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2023-12-12T01:08:20.930649Z",
"start_time": "2023-12-12T01:02:43.228031Z"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -23,7 +23,7 @@ class StrategyAnalysis:
self.base_figure = make_subplots(rows=rows, cols=1, shared_xaxes=True, vertical_spacing=0.01,
row_heights=heights, specs=specs)
if candlestick:
self.add_candles_graph()
self.add_candles_graph(row=1, col=1)
if volume:
self.add_volume()
if positions:
@@ -81,17 +81,17 @@ class StrategyAnalysis:
def figure(self):
return self.base_figure
def add_candles_graph(self):
def add_candles_graph(self, row, col, name_suffix='', timeframe_suffix=''):
self.base_figure.add_trace(
go.Candlestick(
x=self.candles_df["timestamp"],
open=self.candles_df["open"],
high=self.candles_df["high"],
low=self.candles_df["low"],
close=self.candles_df["close"],
name="OHLC"
x=self.candles_df[f"timestamp{timeframe_suffix}"],
open=self.candles_df[f"open{timeframe_suffix}"],
high=self.candles_df[f"high{timeframe_suffix}"],
low=self.candles_df[f"low{timeframe_suffix}"],
close=self.candles_df[f"close{timeframe_suffix}"],
name=f"OHLC_{name_suffix}"
),
row=1, col=1,
row=row, col=col,
)
def add_volume(self):
@@ -185,7 +185,7 @@ class StrategyAnalysis:
return returns.mean() / returns.std()
def profit_factor(self):
total_won = self.win_signals().loc[:, "net_pnl_quote"].sum()
total_won = self.win_signals().loc[:, "net_pnl_quote"].sum()
total_loss = - self.loss_signals().loc[:, "net_pnl_quote"].sum()
return total_won / total_loss
@@ -231,4 +231,4 @@ Strategy Performance Report:
xaxis_title="N° Position",
yaxis=dict(title="Net PnL USD", side="left", showgrid=False),
)
return fig
return fig

View File

@@ -3,6 +3,8 @@ import subprocess
import importlib.util
import inspect
import os
import pandas as pd
from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingControllerBase, DirectionalTradingControllerConfigBase
import yaml
@@ -151,3 +153,12 @@ def execute_bash_command(command: str, shell: bool = True, wait: bool = False):
process = subprocess.Popen(command, shell=shell)
if wait:
process.wait()
def safe_read_csv(path):
try:
df = pd.read_csv(path)
except pd.errors.ParserError as e:
print("Error occurred while reading CSV:", str(e))
df = pd.DataFrame()
return df