{ "cells": [ { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from backtesting import Strategy\n", "from backtesting import Backtest\n", "import pandas as pd\n", "import numpy as np\n", "import talib\n", "from talib import MA_Type\n", "from datetime import timedelta, datetime\n", "import backtesting.lib as bt_lib\n", "import math\n", "import warnings\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings('ignore')\n", "import pandas as pd\n", "data = pd.read_csv(f'/Users/alex/Dev/MarketAlgorithms/test/data/DOGEUSDT_1.csv', parse_dates=[0], index_col=0)\n", "data = data.rename(columns={'timestamp': 'Timestamp',\n", " 'open': 'Open',\n", " 'high': 'High',\n", " 'low': 'Low',\n", " 'close': 'Close',\n", " 'volume': 'Volume',\n", " 'turnover': 'Turnover'})\n", "data" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "class BBDca(Strategy):\n", " bb_len = 201\n", " bb_mul = 1.8\n", " bo_amount = 50\n", " so_amount = 270\n", " tp = 0.005\n", " so_price_deviation = 0.02\n", " so_volume_scale = 1.6\n", " so_step_scale = 1.6\n", " so_count = 5\n", " \n", " max_days_in_trade = 30000\n", " so_enabled = False\n", " so_order_policy = \"place_at_start\" #\"trigger_level\"|\"strategy\"\n", " LONG = 'long'\n", " SHORT = 'short'\n", " \n", " def safety_order_deviation(self, index):\n", " return self.so_price_deviation * math.pow(self.so_step_scale, index - 1)\n", " \n", " def safety_order_price(self, index, last_safety_order_price, side):\n", " qty = self.safety_order_qty(index)\n", " if side == 'long':\n", " return last_safety_order_price * (1 - self.safety_order_deviation(index)), qty\n", " else:\n", " return last_safety_order_price * (1 + self.safety_order_deviation(index)), qty\n", " \n", " def safety_order_qty(self, index):\n", " return self.so_amount * math.pow(self.so_volume_scale, index - 1)\n", " \n", " def place_safety_order(self, bo_level, side):\n", " tot_order_size = self.bo_amount\n", " last_so_level = bo_level\n", " for i in range(1, self.so_count + 1):\n", " so_level, amount = self.safety_order_price(i, last_so_level, side)\n", " average_price = (tot_order_size * last_so_level + amount * so_level) / (amount + tot_order_size)\n", " tot_order_size += amount\n", " last_so_level = so_level\n", " if side == self.LONG:\n", " so_tp_level = average_price * (1 + self.tp)\n", " self.buy(tp=so_tp_level, limit=so_level, size=round(amount / so_level))\n", " else:\n", " so_tp_level = average_price * (1 - self.tp)\n", " self.sell(tp=so_tp_level, limit=so_level, size=round(amount / so_level))\n", " \n", " def init(self):\n", " super().init()\n", " self.bb_up, self.bb_mid, self.bb_low = self.I(talib.BBANDS, self.data.Close, self.bb_len, self.bb_mul, self.bb_mul, matype=MA_Type.EMA)\n", " self.tp_level = self.I(lambda: np.repeat(np.nan, len(self.data)), name='TP level', overlay=True)\n", " self.trade_abort = self.I(lambda: np.repeat(np.nan, len(self.data)), name='TA', scatter=True)\n", " self.tp_level.flags.writeable = True\n", " self.trade_abort.flags.writeable = True\n", " \n", " def next(self):\n", " super().next()\n", " \n", " if self.position:\n", " if self.so_order_policy == \"place_at_start\":\n", " if self.trades[-1].entry_bar == len(self.data) - 1:\n", " # adjust tp of previous trade\n", " #if len(self.trades) == 2:\n", " new_tp = self.trades[-1].tp\n", " for ti in range(0, len(self.trades) - 1):\n", " self.trades[ti].tp = new_tp\n", " # update tp level line on chart\n", " self.tp_level[-1] = self.trades[0].tp \n", " \n", " else:\n", " self.so_enabled = False\n", " for order in self.orders:\n", " order.cancel()\n", "\n", " # handle new trade\n", " long_condition = self.data.Close[-1] >= self.bb_low[-1] and self.data.Close[-2] < self.bb_low[-1]\n", " short_condition = self.data.Close[-1] <= self.bb_up[-1] and self.data.Close[-2] > self.bb_up[-1]\n", "\n", " be = self.data.Close[-1]\n", " if long_condition and not self.position:\n", " tp_level = self.data.Close[-1] * (1 + self.tp)\n", " self.buy(tp=tp_level, size=round(self.bo_amount / self.data.Close[-1]))\n", " if self.so_order_policy == \"place_at_start\":\n", " self.place_safety_order(be, \"long\")\n", " elif short_condition and not self.position:\n", " tp_level = self.data.Close[-1] * (1 - self.tp)\n", " self.sell(tp=tp_level, size=round(self.bo_amount / self.data.Close[-1]))\n", " if self.so_order_policy == \"place_at_start\":\n", " self.place_safety_order(be, \"short\")\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bt = Backtest(data, BBDca, cash=600, margin=0.2, commission=0.0003)\n", "stats = bt.run()\n", "print(stats)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bt.plot(show_legend=False, resample=False, plot_pl=True, relative_equity=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bt = Backtest(data, BBDca, cash=600, margin=0.2, commission=0.0003)\n", "stats, heatmap = bt.optimize(\n", " bb_len = range(190,210,1),\n", " bb_mul = list(np.arange(1.8, 3.0, 0.1)),\n", " bo_amount = range(50,100,10),\n", " so_amount = range(100,300,10),\n", " tp = list(np.arange(0.005, 0.01, 0.001)),\n", " so_price_deviation = list(np.arange(0.02, 0.06, 0.01)),\n", " so_volume_scale = list(np.arange(1.0, 2.0, 0.1)),\n", " so_step_scale = list(np.arange(1.0, 2.0, 0.1)),\n", " so_count = range(1,5,1),\n", " constraint= lambda x: x.bo_amount < x.so_amount / 2, #and x.bo_amount + x.so_amount * x.count ,\n", " maximize='Equity Final [$]',\n", " method='grid',\n", " max_tries=50,\n", " random_state=0,\n", " return_heatmap=True)\n", "\n", "print(stats)\n", "print(heatmap.sort_values(ascending=False))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bt.plot(show_legend=False, resample=False, plot_pl=True, relative_equity=False)" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.3" } }, "nbformat": 4, "nbformat_minor": 2 }