From 934c74bc21f5d27b97c7518e931a0de554016cc5 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 11 Apr 2024 18:29:48 -0300 Subject: [PATCH 001/139] (feat) add methods to download candles async --- .../01_strategy_design_dman_maker.ipynb | 210 ++++++++++++++++++ utils/hummingbot_processes.py | 41 ++++ 2 files changed, 251 insertions(+) create mode 100644 quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb create mode 100644 utils/hummingbot_processes.py diff --git a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb new file mode 100644 index 0000000..0a479b9 --- /dev/null +++ b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb @@ -0,0 +1,210 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# RESEARCH NOTEBOOK --> SUPERTREND" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 10080/10080 [00:12<00:00, 837.18it/s] \n", + "Unclosed client session\n", + "client_session: \n", + "Unclosed connector\n", + "connections: ['[(, 28510.732667916)]']\n", + "connector: \n" + ] + } + ], + "source": [ + "from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig\n", + "import asyncio\n", + "from utils.hummingbot_processes import aget_candles_list\n", + "\n", + "exchange = \"binance_perpetual\"\n", + "symbols = [\"MATIC-USDT\"]\n", + "intervals = [\"1m\"]\n", + "max_records = 10080\n", + "\n", + "candles_config = [CandlesConfig(connector=exchange,\n", + " trading_pair=symbol,\n", + " interval=interval,\n", + " max_records=max_records) for symbol in symbols for interval in intervals]\n", + "\n", + "all_candles = await aget_candles_list(candles_config)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-11T21:29:22.391314Z", + "start_time": "2024-04-11T21:29:10.277137Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "def analyze_candle_ranges(df, window_length):\n", + " # List to store range data\n", + " range_data = []\n", + "\n", + " # Loop through the DataFrame based on the window length\n", + " for start_idx in range(len(df) - window_length + 1):\n", + " # Define the window\n", + " end_idx = start_idx + window_length\n", + " window = df[start_idx:end_idx]\n", + "\n", + " # Find the max and min values within the window\n", + " max_price = window['high'].max()\n", + " min_price = window['low'].min()\n", + "\n", + " # Calculate the range\n", + " price_range = max_price - min_price\n", + " price_range_pct = price_range / min_price\n", + "\n", + " # Store the data\n", + " range_data.append((start_idx, start_idx + window_length, max_price, min_price, price_range, price_range_pct))\n", + "\n", + " # Create a DataFrame from the range data\n", + " columns = ['Start_Index', 'End_Index', 'Max_Price', 'Min_Price', 'Price_Range', 'Price_Range_Pct']\n", + " ranges_df = pd.DataFrame(range_data, columns=columns)\n", + "\n", + " return ranges_df\n", + "\n", + "# To use the function:\n", + "# Assuming 'df' is your DataFrame with 'high' and 'low' price columns\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-11T21:09:39.343126Z", + "start_time": "2024-04-11T21:09:39.339565Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [], + "source": [ + "window_length = 240 # Replace with your desired\n", + "\n", + "range_analysis_df = analyze_candle_ranges(all_candles[0], window_length)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-11T21:15:49.256567Z", + "start_time": "2024-04-11T21:15:48.820292Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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07x5c9Wq5fjP3YWOY0bnzp2VlZWl5cuXa+3atfr111/1/vvvl7slsmnTppfcV1ngaNOmzQXbHD58WEePHtU777xjD0JlX2XhuaCgoNJ1L1myRK+99prCwsL0r3/9q1y/ycnJ0YgRIxQeHm5/7un222+XJB07dsyhrb+/f7mweP77s3//fjVo0EABAQEO7Zo1a+bwes+ePTIMQ88880y583zuuecczjMtLU1Hjx5VixYt1LZtWz3++OP6/vvvL3kNAMAVeEYKAKrBgQMHdOzYsXJ/NJ6rTp06WrNmjVatWqUvv/xSS5Ys0bx589SzZ08tW7asUtNkm3muqbIu9KHBJSUl1TZ194WOY7jxEzyuvvpqxcXFXbKdq96TskD9wAMPVPhckSSH6fUv5Ny6ExIS1KpVK91555164403lJqaKunse9u7d28dOXJETz75pFq1aqXAwEAdPHhQI0aMKBfuXdkPyvY9btw4JSQkVNim7Ofotttu0969e/X5559r2bJl+tvf/qapU6dq1qxZevDBB11WEwBUhCAFANXggw8+kKQL/mFYplatWurVq5d69eqlKVOmaNKkSXrqqae0atUqxcXFXTDUOOv8W6IMw9CePXsc/iC/6qqrdPTo0XLb7t+/X9dee639tZnaoqOjtXz5ch0/ftxhVOrHH3+0r3eF6Ohoff/99yotLXUYlXL1cS5X2XX84YcfLtimXr16Cg4OVklJSaUCXGX169dPt99+uyZNmqQ//vGPCgwM1LZt27Rr1y699957Gj58uL3t5cyGFx0drVWrVtmn5C+zZ88eh3Zl18JqtVbqPMPDwzVy5EiNHDlSRUVFuu222zRhwgSCFIAqx619AFDFVq5cqeeff15NmzbV0KFDL9juyJEj5Za1b99eklRcXCxJCgwMlKQKg40z3n//fYfntj766CPl5uaqT58+9mXXXXed1q1bZ/9QX0lauHBhuWnSzdTWt29flZSU6C9/+YvD8qlTp8pisTgc/3L07dtXeXl5mjdvnn3ZmTNn9OabbyooKMh+q5q71atXT7fddpveffdd5eTkOKwrG3WrXbu2Bg4cqI8//rjCwHX48GGnj//kk0/qt99+01//+lf7sc49dtn3b7zxhtPHSEhIkM1msx9DOjv6NGPGDId29evXV/fu3fX2228rNze33H7OPc/ffvvNYV1QUJCaNWtm/3kBgKrEiBQAuNDixYv1448/6syZM8rPz9fKlSuVlZWl6OhoffHFFxf9ENa0tDStWbNG/fr1U3R0tAoKCvTWW2+pYcOG6tq1q6SzoSYsLEyzZs1ScHCwAgMD1blz50o9h1OR8PBwde3aVSNHjlR+fr6mTZumZs2aOUwI8OCDD+qjjz7SHXfcod///vfau3ev5syZ4zD5g9na+vfvrx49euipp57Szz//rBtuuEHLli3T559/rkcffbTcvp01evRovf322xoxYoQ2bdqkJk2a6KOPPtI333yjadOmXfSZteo2ffp0de3aVTfeeKNGjx6tpk2b6ueff9aXX36pLVu2SJJeeuklrVq1Sp07d9aoUaMUExOjI0eOaPPmzVq+fHmFYbwy+vTpozZt2mjKlClKSUlRq1atdN1112ncuHE6ePCgQkJC9PHHH1/ymbSLGTBggDp16qTHHntMe/bsUatWrfTFF1/Yaz53RHPGjBnq2rWr2rZtq1GjRunaa69Vfn6+srOzdeDAAW3dulWSFBMTo+7du6tjx44KDw/Xxo0b9dFHH2nMmDFO1wkAleau6QIB4EpSNv152Zevr68RGRlp9O7d23jjjTccptkuc/705ytWrDDuvvtuIyoqyvD19TWioqKM+++/39i1a5fDdp9//rkRExNj+Pj4OEwbffvttxvXX399hfVdaPrz//u//zPGjx9v1K9f36hTp47Rr1+/ctNvG4ZhvP7668Y111xj+Pn5GbfeequxcePGcvu8WG3nT39uGIZx/PhxY+zYsUZUVJRhtVqN5s2bG6+++qrDdN+GYVQ4NbdhXHha9vPl5+cbI0eONK6++mrD19fXaNu2bYVTtJud/vxSbcumOH/11VcvuO78On744QfjnnvuMcLCwgx/f3+jZcuWxjPPPFPufFJSUoxGjRoZVqvViIyMNHr16mW88847l1V3RkaGQ007duww4uLijKCgIOPqq682Ro0aZZ92/ty6ExMTjcDAwHL7O79/G4ZhHD582BgyZIgRHBxshIaGGiNGjDC++eYbQ5Ixd+5ch7Z79+41hg8fbkRGRhpWq9W45pprjDvvvNP46KOP7G1eeOEFo1OnTkZYWJhRp04do1WrVsaLL75onD59+pLXAgAul8Uw3PikLgAAqNE+++wz3XPPPfr666916623urscAKg0ghQAAKgW//73vx1m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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Assuming range_analysis_df is your DataFrame from the previous function and has a 'Price_Range' column\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(range_analysis_df['Price_Range_Pct'] * 100, bins=50, alpha=0.7, color='blue')\n", + "plt.title('Distribution of Price Ranges')\n", + "plt.xlabel('Price Range')\n", + "plt.ylabel('Frequency')\n", + "plt.grid(True)\n", + "plt.show()\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-11T21:15:49.347509Z", + "start_time": "2024-04-11T21:15:49.262675Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABIQAAAJwCAYAAAD4AboDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXgT1foH8G+aLlAKZZG9ZRFwQ3BBUMAquIDiUgy44IJcl6sCCqLwA+/iLsqOV/EqXkVxQcFQrooKKmCviqCCF/WqgCBQKCBLS6F0SfL74zhknzkzmaSZzPfzPH2aTM9MTttMZuad97zH4fP5fCAiIiIiIiIiIttIq+sOEBERERERERFRYjEgRERERERERERkMwwIERERERERERHZDANCREREREREREQ2w4AQEREREREREZHNMCBERERERERERGQzDAgREREREREREdkMA0JERERERERERDbDgBARERERERERkc0wIERERES6jBgxAh06dKjrbpDFPPTQQ3A4HHXdDSIiIvoDA0JEREQ2Mm/ePDgcjmNf9erVwwknnIDRo0dj9+7ddd29qEL7nZ6ejrZt22LEiBEoKSmp6+6Z7vPPP8dVV12Fli1bIisrCx06dMAdd9yBbdu21XXXgnTo0CHo/xLta968eXXdVSIiIgrh8Pl8vrruBBERESXGvHnz8Kc//QmPPPIIOnbsiKNHj+I///kP5s+fj/bt2+P7779Hdna26jZqamrg9XqRlZWVoF5H7vfq1asxb948dOjQAd9//z3q1auXsP7E0z/+8Q+MGTMGxx9/PEaMGIHWrVvjf//7H1588UUAwNKlS9GnT5867qVQVFSEioqKY8+XLl2KN998EzNnzsRxxx13bHmfPn3Qrl071NbWpsz/iYiIyOrS67oDRERElHiXXnopzjrrLADAbbfdhmbNmmHGjBlYsmQJhg0bFnGdw4cPo0GDBsjIyEhkV4OE9vu4447DU089hX//+9+45ppr6qxfZvn8888xduxYnHvuufjwww+DgnN33XUX+vbti6FDh+KHH35AkyZNEtYv5X8favDgwUHPS0tL8eabb2Lw4MERhxWmp/PUk4iIKFlwyBgRERHhggsuAABs2bIFgKgTlJOTg82bN2PQoEFo2LAhbrjhhmM/C73Y93q9mD17Nrp164Z69eqhefPmuOSSS/D1118HtXvttdfQo0cP1K9fH02bNsV1112H7du3G+53QUEBAGDz5s3HllVXV+Pvf/87evTogdzcXDRo0AAFBQVYsWJF0Lpbt26Fw+HAtGnT8MILL6BTp07IyspCz549sXbt2rDXWrhwIU455RTUq1cPp556KhYvXhz1bzFr1ix07doV9erVQ8uWLXHHHXfgwIEDmr/Po48+CofDgVdeeSUsU6tTp06YMmUKdu3aheeffx4AMG3aNDgcDvz2229h25o0aRIyMzODXverr77CJZdcgtzcXGRnZ+P888/H559/HrSeUuvnxx9/xPXXX48mTZrg3HPP1ey7lkg1hBwOB0aPHn3sb1u/fn307t0bGzZsAAA8//zz6Ny5M+rVq4d+/fph69atYduV+Z2IiIgoHANCREREdCyg0qxZs2PLamtrMXDgQLRo0QLTpk3DkCFDoq5/6623YuzYscjPz8dTTz2FiRMnol69eli9evWxNo8//jiGDx+OLl26YMaMGRg7diw++eQTnHfeeTh48KChfisBgsBsmfLycrz44ovo168fnnrqKTz00EPYu3cvBg4ciPXr14dt44033sDUqVNxxx134LHHHsPWrVvhcrlQU1NzrM3777+Pa6+9FhkZGZg8eTJcLhduvfVWfPPNN2Hbu+OOOzB+/Hj07dsXs2fPxp/+9Ce8/vrrGDhwYNA2Qx05cgSffPIJCgoK0LFjx4htrr32WmRlZeG9994DAFxzzTVwOBx4++23w9q+/fbbGDBgwLG/zaefforzzjsP5eXlePDBB/HEE0/g4MGDuOCCC7BmzZqw9a+++mocOXIETzzxBG6//fao/Y5VcXEx7rvvPtx888146KGH8L///Q+XX345nn32WTz99NMYOXIkxo8fjy+//BK33HJL0Lp6fyciIiIK4CMiIiLbePnll30AfB9//LFv7969vu3bt/sWLFjga9asma9+/fq+HTt2+Hw+n+/mm2/2AfBNnDgxbBs333yzr3379seef/rppz4AvnvuuSesrdfr9fl8Pt/WrVt9TqfT9/jjjwf9fMOGDb709PSw5TL9XrRoka958+a+rKws3/bt24+1ra2t9VVVVQWtf+DAAV/Lli19t9xyy7FlW7Zs8QHwNWvWzLd///5jy5csWeID4Hv33XePLevWrZsvLy/Pd+jQoWPLVq5c6QMQ9LcoLi72AfC9/vrrQa//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v/wl4lAyQAwb+/jjj9GwYUMMGjQo7Of9+vXDl19+iUcffRTPPPMMKioq0KpVK5x99tlhGUdWkYq/ExERUaI4fEYqJhIRERERTj/9dDRv3hzLly+v664QERER6cIaQkREREQaampqUFtbG7Rs5cqV+O6779CvX7+66RQRERFRDJghRERERKRh69atuOiii3DjjTeiTZs2+Omnn/DPf/4Tubm5+P7779GsWbO67iIRERGRLqwhRERERKShSZMm6NGjB1588UXs3bsXDRo0wGWXXYYnn3ySwSAiIiKyJGYIERERERERERHZDGsIERERERERERHZDANCREREREREREQ2Y7saQl6vFzt37kTDhg3hcDjqujtERERERERERKbw+Xw4dOgQ2rRpg7Q09Rwg2wWEdu7cifz8/LruBhERERERERFRXGzfvh15eXmqbWwXEGrYsCEA8cdp1KhRHffGuJqaGixbtgwDBgxARkZGXXeHKClwvyAKx/2CKBz3C6Jw3C+IwllxvygvL0d+fv6x2Ica2wWElGFijRo1snxAKDs7G40aNbLMG5Mo3rhfEIXjfkEUjvsFUTjuF0ThrLxfyJTIYVFpIiIiIiIiIiKbYUCIiIiIiIiIiMhmGBAiIiIiIiIiIrIZBoSIiIiIiIiIiGyGASEiIiIiIiIiIpthQIiIiIiIiIiIyGYYECIiIiIiIiIishkGhIiIiIiIiIiIbIYBISIiIiIiIiIim2FAiIiIiIiIiIjIZhgQIiIiIiIiIiKyGQaEiIiIiIiIiIhshgEhIiIiIiIiIiKbYUCIiIiIiIiIiMhmGBAiIiIiIiIiIrIZBoSIiIiIiIiIiGyGASEiIiIiIhsrKwPOPhvIzBRf55wjlhERUWpLr+sOEBERERFR3ejcGdi8OXjZV18BjRsDnToBmzbVSbeIiCgBmCFERERERGRDkYJBgTZvFm2IiCg1MSBERERERGQzZWXqwSDF5s0cPkZElKoYECIiIiIispnLLotPWyIisg4GhIiIiIiIbGbbtvi0JSIi62BAiIiIiIjIZtq1i09bIiKyDgaEiIiIiIhs5v3349OWiIisgwEhIiIiIiKbyc0V08pr6dRJtCUiotTDgBARERERkQ1t2qQeFOrUSbQhIqLUxIAQEREREZFNbdoEHDwYvOzss8UyBoOIiFJbel13gIiIiIiI6k7okLDV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w4fry5Yt01977TW9qKhIf/rpp1P6eQlChAceeEBv0aKF/t577+mrVq3S33zzTb1Ro0b6Y489tq8MtQsiF5g2bZp+xx136GVlZToA/e233054P1Xt4PPPP9c1TdPHjRunf//99/qdd96p5+fn60uXLg38OxCFDEJZRq9evfRrr7123/NoNKq3bdtWf/DBB9NYK4IIhk2bNukA9E8++UTXdV2vqKjQ8/Pz9TfffHNfmR9++EEHoH/55Ze6rrMBIBwO6+Xl5fvK/Pvf/9abNGmi7927V9d1XR8zZox++OGHJ1zrwgsv1E8//fSgPxJBeGbnzp36gQceqM+cOVM/6aST9hmEqF0Qucitt96q9+3b1/b9WCyml5aW6g899NC+1yoqKvTCwkL9tdde03Vd17///nsdgL5w4cJ9ZT744AM9FArp69ev13Vd15966im9WbNm+9oJv/bBBx+s+iMRhG8GDRqkX3nllQmvRSIRffjw4bquU7sgchOzQSiV7eCCCy7QBw0alFCf4447Tv/zn/+s9DP6gULGsoiamhosXrwYAwcO3PdaOBzGwIED8eWXX6axZgQRDJWVlQCA5s2bAwAWL16M2trahDZwyCGHoGPHjvvawJdffokjjzwSrVu33lfm9NNPx44dO/Ddd9/tK2M8By9D7YjIZK699loMGjQo6d6ldkHkIu+88w569OiB888/H/vttx+6deuGZ599dt/7q1atQnl5ecI93bRpUxx33HEJ7aKkpAQ9evTYV2bgwIEIh8OYP3/+vjInnngiCgoK9pU5/fTT8dNPP2H79u1Bf0yCkKJPnz6YNWsWfv75ZwDAN998g88++wxnnHEGAGoXBAGkth1kw9yKDEJZxJYtWxCNRhMm9ADQunVrlJeXp6lWBBEMsVgMN9xwA0444QQcccQRAIDy8nIUFBSgpKQkoayxDZSXl1u2Ef6eU5kdO3aguro6iI9DEL6YPHkyvvrqKzz44INJ71G7IHKRlStX4t///jcOPPBATJ8+HX/5y19w/fXX47///S+A+H3tNGcqLy/Hfvvtl/B+Xl4emjdvLtV2CCJTuO2223DRRRfhkEMOQX5+Prp164YbbrgBw4cPB0DtgiCA1LYDuzKZ1E7y0l0BgiAIK6699losW7YMn332WbqrQhBpZe3atRg1ahRmzpyJBg0apLs6BJERxGIx9OjRA//8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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(14, 7))\n", + "plt.plot(range_analysis_df['Start_Index'], range_analysis_df['Price_Range'] * 100, marker='o', linestyle='-', color='blue')\n", + "plt.title('Price Range Over Time')\n", + "plt.xlabel('Index')\n", + "plt.ylabel('Price Range')\n", + "plt.grid(True)\n", + "plt.show()\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-11T21:15:35.060071Z", + "start_time": "2024-04-11T21:15:34.953227Z" + } + } + }, + { + "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 +} diff --git a/utils/hummingbot_processes.py b/utils/hummingbot_processes.py new file mode 100644 index 0000000..7a8e501 --- /dev/null +++ b/utils/hummingbot_processes.py @@ -0,0 +1,41 @@ +import asyncio +from typing import List + +from tqdm import tqdm + +from hummingbot.data_feed.candles_feed.candles_factory import CandlesFactory, CandlesConfig + + +async def aget_candles_list(candles_configs: List[CandlesConfig]): + tasks = [aget_candles(config.connector, config.trading_pair, config.interval, config.max_records) for config in candles_configs] + return await asyncio.gather(*tasks) + + +async def aget_candles(connector_name: str, trading_pair: str, interval: str, max_records: int): + candles = CandlesFactory.get_candle(CandlesConfig(connector=connector_name, + trading_pair=trading_pair, + interval=interval, max_records=max_records)) + candles.start() + + pbar = tqdm(total=candles._candles.maxlen) + while not candles.is_ready: + await asyncio.sleep(1) + awaited_records = candles._candles.maxlen - len(candles._candles) + pbar.update(candles._candles.maxlen - awaited_records - pbar.n) + + pbar.close() + df = candles.candles_df + candles.stop() + return df + + +async def adownload_candles(connector_name: str, trading_pair: str, interval: str, max_records: int, download_path: str): + candles = CandlesFactory.get_candle(CandlesConfig(connector_name, trading_pair, interval, max_records)) + candles.start() + while not candles.is_ready: + print(f"Candles not ready yet! Missing {candles._candles.maxlen - len(candles._candles)}") + await asyncio.sleep(1) + df = candles.candles_df + df.to_csv(download_path, index=False) + candles.stop() + From 373bdea0f763661ec524913a89d1895c9a708f91 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:54:24 -0300 Subject: [PATCH 002/139] (feat) add research notebooks --- .../01_strategy_design_dman_maker.ipynb | 14 +- .../02_single_controller_backtest.ipynb | 591 + .../dman_maker/03_optimization_notebook.ipynb | 183 + .../marroq/01_strategy_design_marroq.ipynb | 22870 ++++++++++++++++ .../02_single_controller_backtest.ipynb | 591 + .../marroq/03_optimization_notebook.ipynb | 183 + .../01_strategy_design_supertrend.ipynb | 9 + 7 files changed, 24434 insertions(+), 7 deletions(-) create mode 100644 quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb create mode 100644 quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb create mode 100644 quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb create mode 100644 quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb create mode 100644 quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb diff --git a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb index 0a479b9..6c5a876 100644 --- a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb +++ b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb @@ -11,18 +11,18 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 10080/10080 [00:12<00:00, 837.18it/s] \n", + "100%|██████████| 10080/10080 [00:09<00:00, 1117.51it/s]\n", "Unclosed client session\n", - "client_session: \n", + "client_session: \n", "Unclosed connector\n", - "connections: ['[(, 28510.732667916)]']\n", - "connector: \n" + "connections: ['[(, 28635.105190041)]']\n", + "connector: \n" ] } ], @@ -46,8 +46,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:29:22.391314Z", - "start_time": "2024-04-11T21:29:10.277137Z" + "end_time": "2024-04-11T21:31:26.786920Z", + "start_time": "2024-04-11T21:31:17.744594Z" } } }, diff --git a/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb b/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb new file mode 100644 index 0000000..6d32473 --- /dev/null +++ b/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb @@ -0,0 +1,591 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "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)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "from decimal import Decimal\n", + "\n", + "# Market configuration\n", + "exchange = \"binance_perpetual\"\n", + "trading_pair = \"WLD-USDT\"\n", + "interval = \"3m\"\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 = Decimal(\"0.015\")\n", + "take_profit = Decimal(\"0.03\")\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\")" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder\n", + "from hummingbot.smart_components.strategy_frameworks.data_types import (\n", + " TripleBarrierConf\n", + ")\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", + " # for directional strategies we don't need spreads since we are going to use market orders to enter\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", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's inpect the order levels\n", + "order_levels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "import sys\n", + "from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig\n", + "from quants_lab.controllers.supertrend import SuperTrend, SuperTrendConfig\n", + "\n", + "# Controller configuration\n", + "length = 100\n", + "multiplier = 3.0\n", + "percentage_threshold = 0.01\n", + "\n", + "# Creating the instance of the configuration and the controller\n", + "config = SuperTrendConfig(\n", + " exchange=exchange,\n", + " trading_pair=trading_pair,\n", + " order_levels=order_levels,\n", + " candles_config=[\n", + " CandlesConfig(connector=exchange, trading_pair=trading_pair, interval=interval, max_records=sys.maxsize),\n", + " ],\n", + " leverage=leverage,\n", + " length=length,\n", + " multiplier=multiplier,\n", + " percentage_threshold=percentage_threshold,\n", + ")\n", + "controller = SuperTrend(config=config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from quants_lab.strategy.strategy_analysis import StrategyAnalysis\n", + "\n", + "from hummingbot.smart_components.strategy_frameworks.directional_trading.directional_trading_backtesting_engine import \\\n", + " DirectionalTradingBacktestingEngine\n", + "\n", + "# Creating the backtesting engine and loading the historical data\n", + "engine = DirectionalTradingBacktestingEngine(controller=controller)\n", + "engine.load_controller_data(\"../../../data/candles\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's see what is inside the candles of the controller\n", + "engine.controller.candles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "engine.controller.candles[0].candles_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's understand what is inside the processed data since this is what we are going to use when generating the signal ;)\n", + "engine.controller.get_processed_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's run the backtesting\n", + "\n", + "backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd,\n", + " trade_cost=trade_cost,\n", + " start=start, end=end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's see what is inside the backtesting results\n", + "backtesting_results.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# now let's analyze each of the dataframes\n", + "\n", + "# 1. The processed data: this is the data that we are going to use to generate the signal\n", + "backtesting_results[\"processed_data\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# 2. The executors dataframe: this is the dataframe that contains the information of the orders that were executed\n", + "backtesting_results[\"executors_df\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# 3. The results dataframe: this is the dataframe that contains the information of the pnl of the strategy\n", + "backtesting_results[\"results\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Now let's analyze the results using the StrategyAnalysis class\n", + "strategy_analysis = StrategyAnalysis(\n", + " positions=backtesting_results[\"executors_df\"],\n", + " candles_df=backtesting_results[\"processed_data\"],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# let's visualize the PNL over time of the strategy\n", + "strategy_analysis.pnl_over_time()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "strategy_analysis.create_base_figure(volume=False, positions=False, trade_pnl=True)\n", + "fig = strategy_analysis.figure()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Now let's see how we can add the SuperTrend to the plot\n", + "\n", + "import plotly.graph_objects as go\n", + "\n", + "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", + "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", + "# Add the SuperTrend line\n", + "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", + " mode='markers',\n", + " name='SuperTrend Short',\n", + " line=dict(color=\"red\")),\n", + " row=1, col=1)\n", + "\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# To see the trades we will need to select a lower timeframe due the restrictions and speed of the plotly library\n", + "start_time = \"2023-11-03\"\n", + "end_time = \"2023-11-05\"\n", + "\n", + "processed_data_filtered = backtesting_results[\"processed_data\"][\n", + " (backtesting_results[\"processed_data\"][\"timestamp\"] >= start_time) &\n", + " (backtesting_results[\"processed_data\"][\"timestamp\"] <= end_time)\n", + "]\n", + "\n", + "executors_filtered = backtesting_results[\"executors_df\"][\n", + " (backtesting_results[\"executors_df\"][\"timestamp\"] >= start_time) &\n", + " (backtesting_results[\"executors_df\"][\"timestamp\"] <= end_time)\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "executors_filtered" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "\n", + "strategy_analysis = StrategyAnalysis(\n", + " positions=executors_filtered,\n", + " candles_df=processed_data_filtered,\n", + ")\n", + "\n", + "strategy_analysis.create_base_figure(volume=False, positions=True, trade_pnl=True)\n", + "fig = strategy_analysis.figure()\n", + "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", + "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", + "# Add the SuperTrend line\n", + "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", + " mode='markers',\n", + " name='SuperTrend Short',\n", + " line=dict(color=\"red\")),\n", + " row=1, col=1)\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Scatter of PNL per Trade\n", + "This bar chart illustrates the PNL for each individual trade. Positive PNLs are shown in green and negative PNLs in red, providing a clear view of profitable vs. unprofitable trades.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "executors_df = backtesting_results[\"executors_df\"]\n", + "\n", + "fig = px.scatter(executors_df, x=\"timestamp\", y='net_pnl_quote', title='PNL per Trade',\n", + " color='profitable', color_continuous_scale=['red', 'green'])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Scatter Plot of Volume vs. PNL\n", + "This scatter plot explores the relationship between the trade volume and the PNL for each trade. It can reveal if larger volumes are associated with higher profits or losses.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig = px.scatter(executors_df, x='volume', y='net_pnl_quote', title='Trade Volume vs. PNL')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Histogram of PNL Distribution\n", + "The histogram displays the distribution of PNL values across all trades. It helps in understanding the frequency and range of profit and loss outcomes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig = px.histogram(executors_df, x='net_pnl_quote', title='PNL Distribution')\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# Conclusion\n", + "We can see that the indicator has potential to bring good signals to trade and might be interesting to see how we can design a market maker that shifts the mid price based on this indicator.\n", + "A lot of the short signals are wrong but if we zoom in into the loss signals we can see that the losses are not that big and the wins are bigger and if we had implemented the trailing stop feature probably a lot of them are going to be profits." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# Next steps\n", + "- Filter only the loss signals and understand what you can do to prevent them\n", + "- Try different configuration values for the indicator\n", + "- Test in multiple markets, pick mature markets like BTC-USDT or ETH-USDT and also volatile markets like DOGE-USDT or SHIB-USDT" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb b/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb new file mode 100644 index 0000000..c09c9b7 --- /dev/null +++ b/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb @@ -0,0 +1,183 @@ +{ + "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": [ + "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 import SuperTrend, SuperTrendConfig\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", + " interval = \"3m\"\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 = SuperTrendConfig(\n", + " exchange=exchange,\n", + " trading_pair=trading_pair,\n", + " strategy_name='supertrend',\n", + " candles_config=[\n", + " CandlesConfig(connector=exchange, trading_pair=trading_pair,\n", + " interval=interval, 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 = SuperTrend(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 + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "import optuna\n", + "\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", + "\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 + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Not let's run the optimization!\n", + "\n", + "n_trials = 200\n", + "study.optimize(objective, n_trials=n_trials)" + ], + "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 +} diff --git a/quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb b/quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb new file mode 100644 index 0000000..089f07d --- /dev/null +++ b/quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb @@ -0,0 +1,22870 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# RESEARCH NOTEBOOK --> SUPERTREND" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 1, + "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\")\n", + "candles[\"date\"] = pd.to_datetime(candles[\"timestamp\"], unit=\"ms\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:09.980351Z", + "start_time": "2024-04-08T15:18:09.379563Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": " timestamp open high low close \\\ncount 4.320000e+04 43200.000000 43200.000000 43200.000000 43200.000000 \nmean 1.698444e+12 1.884709 1.888771 1.880592 1.884742 \nmin 1.694556e+12 0.974400 0.975800 0.970900 0.974400 \n25% 1.696500e+12 1.568600 1.571875 1.565400 1.568575 \n50% 1.698444e+12 1.791200 1.794100 1.788400 1.791200 \n75% 1.700388e+12 2.353225 2.361425 2.346425 2.353325 \nmax 1.702332e+12 2.873500 2.880700 2.854600 2.873300 \nstd 2.244764e+09 0.418210 0.419342 0.417014 0.418201 \n\n volume quote_asset_volume n_trades taker_buy_base_volume \\\ncount 4.320000e+04 4.320000e+04 43200.000000 4.320000e+04 \nmean 3.826104e+05 6.612243e+05 2724.278426 1.898674e+05 \nmin 0.000000e+00 0.000000e+00 0.000000 0.000000e+00 \n25% 9.382700e+04 1.799700e+05 962.750000 4.501600e+04 \n50% 1.842400e+05 3.429340e+05 1627.000000 8.972850e+04 \n75% 4.003505e+05 7.076663e+05 3029.250000 1.976002e+05 \nmax 3.518280e+07 5.409945e+07 138928.000000 2.052616e+07 \nstd 6.960429e+05 1.142285e+06 3828.753703 3.567905e+05 \n\n taker_buy_quote_volume date \ncount 4.320000e+04 43200 \nmean 3.279029e+05 2023-10-27 21:58:30 \nmin 0.000000e+00 2023-09-12 22:00:00 \n25% 8.572581e+04 2023-10-05 09:59:15 \n50% 1.659423e+05 2023-10-27 21:58:30 \n75% 3.482184e+05 2023-11-19 09:57:45 \nmax 3.142120e+07 2023-12-11 21:57:00 \nstd 5.868375e+05 NaN ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
timestampopenhighlowclosevolumequote_asset_volumen_tradestaker_buy_base_volumetaker_buy_quote_volumedate
count4.320000e+0443200.00000043200.00000043200.00000043200.0000004.320000e+044.320000e+0443200.0000004.320000e+044.320000e+0443200
mean1.698444e+121.8847091.8887711.8805921.8847423.826104e+056.612243e+052724.2784261.898674e+053.279029e+052023-10-27 21:58:30
min1.694556e+120.9744000.9758000.9709000.9744000.000000e+000.000000e+000.0000000.000000e+000.000000e+002023-09-12 22:00:00
25%1.696500e+121.5686001.5718751.5654001.5685759.382700e+041.799700e+05962.7500004.501600e+048.572581e+042023-10-05 09:59:15
50%1.698444e+121.7912001.7941001.7884001.7912001.842400e+053.429340e+051627.0000008.972850e+041.659423e+052023-10-27 21:58:30
75%1.700388e+122.3532252.3614252.3464252.3533254.003505e+057.076663e+053029.2500001.976002e+053.482184e+052023-11-19 09:57:45
max1.702332e+122.8735002.8807002.8546002.8733003.518280e+075.409945e+07138928.0000002.052616e+073.142120e+072023-12-11 21:57:00
std2.244764e+090.4182100.4193420.4170140.4182016.960429e+051.142285e+063828.7537033.567905e+055.868375e+05NaN
\n
" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "candles.describe()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:10.001886Z", + "start_time": "2024-04-08T15:18:09.977947Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from scipy.signal import find_peaks\n", + "import plotly.graph_objects as go\n", + "from scipy.cluster.hierarchy import linkage, fcluster\n", + "\n", + "def calculate_prominence(candles, prominence_percentage):\n", + " price_range = candles['high'].max() - candles['low'].min()\n", + " return price_range * prominence_percentage\n", + "\n", + "def find_price_peaks(candles, prominence_nominal, distance):\n", + " high_peaks, _ = find_peaks(candles['high'], prominence=prominence_nominal, distance=distance)\n", + " low_peaks, _ = find_peaks(-candles['low'], prominence=prominence_nominal, distance=distance)\n", + " return high_peaks, low_peaks\n", + "\n", + "def hierarchical_clustering(peaks, num_clusters=3):\n", + " Z = linkage(peaks.reshape(-1, 1), method='ward')\n", + " labels = fcluster(Z, num_clusters, criterion='maxclust')\n", + " centroids = [peaks[labels == k].mean() for k in range(1, num_clusters + 1)]\n", + " return centroids, labels\n", + "\n", + "def plot_price_chart_with_clusters(candles, high_peaks, low_peaks, high_clusters, low_clusters):\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", + " name='OHLC Data'\n", + " )])\n", + " fig.add_trace(go.Scatter(\n", + " x=candles['date'].iloc[high_peaks],\n", + " y=candles['high'].iloc[high_peaks],\n", + " mode='markers',\n", + " marker=dict(size=7, color='red'),\n", + " name='High Peaks'\n", + " ))\n", + " fig.add_trace(go.Scatter(\n", + " x=candles['date'].iloc[low_peaks],\n", + " y=candles['low'].iloc[low_peaks],\n", + " mode='markers',\n", + " marker=dict(size=7, color='green'),\n", + " name='Low Peaks'\n", + " ))\n", + " for level in high_clusters:\n", + " fig.add_hline(y=level, line=dict(color='orange', width=2), annotation_text=f\"High Cluster: {level:.2f}\")\n", + " for level in low_clusters:\n", + " fig.add_hline(y=level, line=dict(color='blue', width=2, dash='dash'), annotation_text=f\"Low Cluster: {level:.2f}\")\n", + " fig.update_layout(xaxis_rangeslider_visible=False, height=800)\n", + " fig.show()\n", + "\n", + "def analyze_and_plot_candles_with_clusters(candles, prominence_percentage=1/100, distance=5, num_clusters=None):\n", + " candles['date'] = pd.to_datetime(candles['timestamp'], unit='ms')\n", + " prominence_nominal = calculate_prominence(candles, prominence_percentage)\n", + " high_peaks, low_peaks = find_price_peaks(candles, prominence_nominal, distance)\n", + " high_peak_prices = candles['high'].iloc[high_peaks].values\n", + " low_peak_prices = candles['low'].iloc[low_peaks].values\n", + " high_clusters, _ = hierarchical_clustering(high_peak_prices, num_clusters or 3)\n", + " low_clusters, _ = hierarchical_clustering(low_peak_prices, num_clusters or 3)\n", + " plot_price_chart_with_clusters(candles, high_peaks, low_peaks, high_clusters, low_clusters)\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:11.146361Z", + "start_time": "2024-04-08T15:18:09.980081Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/r8/2xrqvjjs37g0nbft2yz31b4m0000gn/T/ipykernel_20494/1161020026.py:52: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " candles['date'] = pd.to_datetime(candles['timestamp'], unit='ms')\n" + ] + }, + { + "data": { + "text/html": " \n " + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "close": [ + 2.3763, + 2.3799, + 2.3783, + 2.3778, + 2.3837, + 2.38, + 2.3748, + 2.3694, + 2.3727, + 2.3658, + 2.3631, + 2.3713, + 2.3717, + 2.3641, + 2.3654, + 2.3679, + 2.3631, + 2.3601, + 2.3651, + 2.369, + 2.3633, + 2.3828, + 2.3857, + 2.3949, + 2.3905, + 2.3888, + 2.3835, + 2.3952, + 2.3998, + 2.4063, + 2.4124, + 2.4089, + 2.4077, + 2.412, + 2.4138, + 2.4209, + 2.425, + 2.4268, + 2.4305, + 2.4284, + 2.431, + 2.4218, + 2.4309, + 2.4194, + 2.4177, + 2.4085, + 2.4037, + 2.4057, + 2.4079, + 2.4056, + 2.4012, + 2.4087, + 2.4086, + 2.4022, + 2.403, + 2.4072, + 2.4108, + 2.403, + 2.4096, + 2.4098, + 2.4159, + 2.4087, + 2.4109, + 2.4083, + 2.4106, + 2.4155, + 2.4127, + 2.414, + 2.4121, + 2.4111, + 2.4173, + 2.4206, + 2.4139, + 2.4148, + 2.4105, + 2.4182, + 2.4183, + 2.4189, + 2.418, + 2.4221, + 2.4232, + 2.4235, + 2.4216, + 2.4178, + 2.4136, + 2.4124, + 2.4201, + 2.4356, + 2.4285, + 2.4322, + 2.426, + 2.4183, + 2.4203, + 2.4193, + 2.4191, + 2.4177, + 2.4186, + 2.4264, + 2.4282, + 2.4287, + 2.4285, + 2.424, + 2.4214, + 2.4241, + 2.4241, + 2.4268, + 2.4289, + 2.4245, + 2.4322, + 2.4315, + 2.4278, + 2.4283, + 2.421, + 2.4233, + 2.4208, + 2.4167, + 2.4207, + 2.4174, + 2.417, + 2.4196, + 2.4246, + 2.43, + 2.427, + 2.4306, + 2.4281, + 2.4244, + 2.4295, + 2.4375, + 2.4386, + 2.437, + 2.4442, + 2.4447, + 2.4387, + 2.4361, + 2.4344, + 2.4377, + 2.4456, + 2.4413, + 2.4411, + 2.4432, + 2.446, + 2.4524, + 2.4488, + 2.449, + 2.4547, + 2.4574, + 2.4639, + 2.4562, + 2.4596, + 2.4642, + 2.4504, + 2.448, + 2.4519, + 2.442, + 2.4383, + 2.4239, + 2.4299, + 2.4237, + 2.4378, + 2.4326, + 2.4383, + 2.4432, + 2.4437, + 2.4465, + 2.4432, + 2.4386, + 2.4382, + 2.4387, + 2.4357, + 2.4348, + 2.4358, + 2.4527, + 2.4508, + 2.4537, + 2.457, + 2.459, + 2.4549, + 2.4605, + 2.4629, + 2.4646, + 2.4537, + 2.4449, + 2.4475, + 2.4547, + 2.4562, + 2.4636, + 2.4692, + 2.4786, + 2.4901, + 2.497, + 2.495, + 2.4966, + 2.4912, + 2.5053, + 2.519, + 2.5235, + 2.5229, + 2.5134, + 2.5088, + 2.5111, + 2.5132, + 2.5045, + 2.5182, + 2.5104, + 2.5051, + 2.503, + 2.5041, + 2.485, + 2.4828, + 2.4833, + 2.4733, + 2.4864, + 2.4809, + 2.4757, + 2.4713, + 2.4808, + 2.4657, + 2.4708, + 2.4735, + 2.4678, + 2.4584, + 2.4492, + 2.4382, + 2.4315, + 2.4412, + 2.4342, + 2.4297, + 2.4307, + 2.4214, + 2.4253, + 2.4219, + 2.4171, + 2.4193, + 2.4245, + 2.4214, + 2.425, + 2.4238, + 2.4257, + 2.4276, + 2.4235, + 2.4226, + 2.4284, + 2.4327, + 2.4341, + 2.4366, + 2.4407, + 2.4544, + 2.4604, + 2.4672, + 2.4625, + 2.4547, + 2.4542, + 2.4591, + 2.4483, + 2.4464, + 2.4381, + 2.44, + 2.4362, + 2.4448, + 2.4477, + 2.442, + 2.4398, + 2.444, + 2.4405, + 2.4354, + 2.4416, + 2.4407, + 2.4373, + 2.445, + 2.4419, + 2.4489, + 2.4575, + 2.4545, + 2.4489, + 2.4518, + 2.4503, + 2.4458, + 2.4478, + 2.4485, + 2.4483, + 2.4433, + 2.4448, + 2.4546, + 2.4606, + 2.46, + 2.459, + 2.4629, + 2.4652, + 2.4625, + 2.4614, + 2.461, + 2.4608, + 2.4622, + 2.4626, + 2.466, + 2.4722, + 2.4661, + 2.4667, + 2.4621, + 2.4605, + 2.4582, + 2.4611, + 2.4534, + 2.4561, + 2.4538, + 2.4494, + 2.4495, + 2.4526, + 2.4624, + 2.4569, + 2.4515, + 2.4415, + 2.4363, + 2.4358, + 2.4374, + 2.4463, + 2.4462, + 2.4468, + 2.4463, + 2.4375, + 2.4288, + 2.4246, + 2.4308, + 2.4312, + 2.4298, + 2.4324, + 2.4235, + 2.4043, + 2.4174, + 2.4168, + 2.4144, + 2.4051, + 2.3989, + 2.3866, + 2.3794, + 2.3966, + 2.3963, + 2.3955, + 2.3962, + 2.3995, + 2.4101, + 2.41, + 2.4113, + 2.415, + 2.4157, + 2.4107, + 2.4142, + 2.4121, + 2.4115, + 2.4019, + 2.396, + 2.3978, + 2.3926, + 2.3937, + 2.3894, + 2.399, + 2.3959, + 2.3943, + 2.3982, + 2.4039, + 2.4062, + 2.4037, + 2.4105, + 2.4138, + 2.4148, + 2.4138, + 2.418, + 2.4149, + 2.414, + 2.4142, + 2.415, + 2.4155, + 2.4167, + 2.4097, + 2.41, + 2.4108, + 2.4096, + 2.4129, + 2.4103, + 2.4058, + 2.4069, + 2.4094, + 2.405, + 2.4092, + 2.405, + 2.403, + 2.4081, + 2.4107, + 2.4033, + 2.4034, + 2.4031, + 2.4023, + 2.4046, + 2.4048, + 2.4048, + 2.4073, + 2.3971, + 2.3988, + 2.3918, + 2.3824, + 2.3732, + 2.387, + 2.3896, + 2.3877, + 2.3997, + 2.4048, + 2.4029, + 2.4015, + 2.3985, + 2.3963, + 2.3901, + 2.3914, + 2.3855, + 2.3858, + 2.3926, + 2.3921, + 2.3893, + 2.3947, + 2.3986, + 2.3961, + 2.3956, + 2.3997, + 2.4065, + 2.409, + 2.4075, + 2.4051, + 2.4028, + 2.4065, + 2.4019, + 2.403, + 2.4038, + 2.4096, + 2.4285, + 2.4185, + 2.4197, + 2.4214, + 2.4231, + 2.4226, + 2.4214, + 2.4214, + 2.4215, + 2.4217, + 2.4213, + 2.4176, + 2.4139, + 2.4228, + 2.4136, + 2.4182, + 2.4152, + 2.405, + 2.4072, + 2.4102, + 2.413, + 2.4124, + 2.4181, + 2.419, + 2.4133, + 2.4126, + 2.4069, + 2.4054, + 2.4206, + 2.4154, + 2.4242, + 2.4322, + 2.4291, + 2.4392, + 2.4391, + 2.4484, + 2.4476, + 2.4445, + 2.4362, + 2.4398, + 2.4446, + 2.4349, + 2.4464, + 2.4498, + 2.446, + 2.4491, + 2.451, + 2.4463, + 2.444, + 2.4498, + 2.4507, + 2.4454, + 2.4445, + 2.4447, + 2.4423, + 2.4486, + 2.4431, + 2.4514, + 2.4526, + 2.4619, + 2.4603, + 2.4591, + 2.4622, + 2.4464, + 2.4501, + 2.4566, + 2.4582, + 2.4558, + 2.4433, + 2.4361, + 2.4463, + 2.4412, + 2.4414, + 2.4365, + 2.431, + 2.4341, + 2.4368, + 2.4348, + 2.4368, + 2.4273, + 2.4248, + 2.4343, + 2.4496, + 2.4457, + 2.4409, + 2.4394, + 2.4419, + 2.4395, + 2.4428, + 2.4419, + 2.4447, + 2.4473, + 2.4582, + 2.4493, + 2.4422, + 2.4433, + 2.4436, + 2.4554, + 2.4432, + 2.4355, + 2.4396, + 2.4365, + 2.427, + 2.4392, + 2.4421, + 2.4515, + 2.4611, + 2.4674, + 2.4811, + 2.4854, + 2.4848, + 2.4838, + 2.4855, + 2.4747, + 2.4819, + 2.4734, + 2.4681, + 2.4694, + 2.4763, + 2.4776, + 2.478, + 2.4812, + 2.4783, + 2.4727, + 2.4741, + 2.4769, + 2.4715, + 2.4681, + 2.4738, + 2.4774, + 2.4852, + 2.4843, + 2.478, + 2.4781, + 2.4851, + 2.4825, + 2.4795, + 2.4783, + 2.4833, + 2.4787, + 2.4762, + 2.4805, + 2.484, + 2.4829, + 2.4923, + 2.491, + 2.4933, + 2.4959, + 2.4964, + 2.4974, + 2.4881, + 2.4947, + 2.4947, + 2.4976, + 2.4822, + 2.4861, + 2.4767, + 2.4812, + 2.4883, + 2.4887, + 2.4893, + 2.4928, + 2.499, + 2.5022, + 2.4932, + 2.4889, + 2.4867, + 2.4866, + 2.4803, + 2.4799, + 2.4762, + 2.4722, + 2.4638, + 2.4689, + 2.4661, + 2.4643, + 2.458, + 2.463, + 2.4674, + 2.4645, + 2.4601, + 2.456, + 2.4554, + 2.4651, + 2.4606, + 2.4555, + 2.4529, + 2.453, + 2.45, + 2.441, + 2.4431, + 2.446, + 2.4462, + 2.4495, + 2.4498, + 2.4414, + 2.4456, + 2.4367, + 2.4349, + 2.4327, + 2.4293, + 2.433, + 2.4341, + 2.4349, + 2.4342, + 2.4371, + 2.4352, + 2.4393, + 2.4466, + 2.4525, + 2.4559, + 2.4568, + 2.4578, + 2.4644, + 2.4591, + 2.4581, + 2.4625, + 2.4584, + 2.4568, + 2.4542, + 2.4521, + 2.4546, + 2.4544, + 2.4584, + 2.4604, + 2.4584, + 2.4542, + 2.4532, + 2.459, + 2.4767, + 2.466, + 2.4702, + 2.473, + 2.4687, + 2.4683, + 2.4768, + 2.4796, + 2.4826, + 2.478, + 2.4583, + 2.4695, + 2.4762, + 2.4737, + 2.4657, + 2.4786, + 2.478, + 2.4832, + 2.4788, + 2.4804, + 2.486, + 2.4838, + 2.4817, + 2.4861, + 2.4797, + 2.483, + 2.4762, + 2.4727, + 2.4689, + 2.4716, + 2.4725, + 2.4688, + 2.4623, + 2.463, + 2.4736, + 2.478, + 2.4812, + 2.4826, + 2.4825, + 2.4871, + 2.4885, + 2.4899, + 2.4842, + 2.472, + 2.4719, + 2.4705, + 2.4666, + 2.4622, + 2.4648, + 2.4596, + 2.4502, + 2.4565, + 2.4567, + 2.4625, + 2.4665, + 2.4639, + 2.4637, + 2.4592, + 2.4573, + 2.4544, + 2.4582, + 2.4595, + 2.4578, + 2.4562, + 2.4576, + 2.4522, + 2.4545, + 2.45, + 2.449, + 2.4429, + 2.4345, + 2.4393, + 2.4416, + 2.4373, + 2.4459, + 2.4472, + 2.4514, + 2.4531, + 2.453, + 2.4615, + 2.462, + 2.4673, + 2.4635, + 2.4686, + 2.4787, + 2.4887, + 2.4949, + 2.4942, + 2.4789, + 2.4784, + 2.5187, + 2.5084, + 2.5174, + 2.519, + 2.5168, + 2.5153, + 2.5127, + 2.5096, + 2.5175, + 2.519, + 2.518, + 2.5275, + 2.5188, + 2.5124, + 2.5157, + 2.5025, + 2.4734, + 2.4946, + 2.4865, + 2.4722, + 2.4669, + 2.4428, + 2.4431, + 2.4399, + 2.4567, + 2.4567, + 2.4491, + 2.4482, + 2.4475, + 2.4381, + 2.4229, + 2.4171, + 2.4259, + 2.4226, + 2.4221, + 2.4112, + 2.4105, + 2.4104, + 2.4123, + 2.4201, + 2.4265, + 2.4385, + 2.4408, + 2.4426, + 2.4469, + 2.4394, + 2.4406, + 2.453, + 2.447, + 2.4519, + 2.4593, + 2.472, + 2.4591, + 2.4512, + 2.4371, + 2.4396, + 2.4496, + 2.4517, + 2.4545, + 2.4644, + 2.4592, + 2.4783, + 2.4626, + 2.4712, + 2.4804, + 2.4809, + 2.4781, + 2.4755, + 2.4728, + 2.4811, + 2.4822, + 2.4862, + 2.4857, + 2.489, + 2.5031, + 2.4997, + 2.4995, + 2.5065, + 2.5248, + 2.528, + 2.5473, + 2.5408, + 2.5377, + 2.5337, + 2.5156, + 2.5228, + 2.5275, + 2.5282, + 2.5351, + 2.5233, + 2.5194, + 2.5109, + 2.5152, + 2.5098, + 2.5132, + 2.511, + 2.5011, + 2.502, + 2.4935, + 2.4971, + 2.4948, + 2.4923, + 2.4853, + 2.4852, + 2.4838, + 2.4841, + 2.485, + 2.4855, + 2.4822, + 2.4911, + 2.4839, + 2.4861, + 2.4916, + 2.4908, + 2.4816, + 2.4898, + 2.5036, + 2.5049, + 2.5066, + 2.5037, + 2.512, + 2.5188, + 2.5171, + 2.5208, + 2.5156, + 2.5086, + 2.5169, + 2.5199, + 2.5055, + 2.5032, + 2.4953, + 2.4701, + 2.4677, + 2.4536, + 2.4653, + 2.4495, + 2.4497, + 2.4448, + 2.4355, + 2.4224, + 2.4096, + 2.4261, + 2.4274, + 2.4302, + 2.4222, + 2.4252, + 2.4257, + 2.4299, + 2.4356, + 2.4458, + 2.459, + 2.4563, + 2.444, + 2.4388, + 2.449, + 2.4408, + 2.4357, + 2.4387, + 2.4296, + 2.4388, + 2.4294, + 2.4356, + 2.443, + 2.4508, + 2.4614, + 2.4621, + 2.4586, + 2.4616, + 2.4588, + 2.4603, + 2.463, + 2.4739, + 2.4728, + 2.4728, + 2.4603, + 2.4595, + 2.4657, + 2.4761, + 2.4748, + 2.4717, + 2.4729, + 2.4659, + 2.4463, + 2.4421, + 2.4379, + 2.4348, + 2.442, + 2.4394, + 2.4377, + 2.4936, + 2.4737, + 2.4854, + 2.504, + 2.4861, + 2.4651, + 2.4631, + 2.4515, + 2.453, + 2.455, + 2.4509, + 2.4502, + 2.4423, + 2.4382, + 2.447, + 2.4387, + 2.4412, + 2.428, + 2.4282, + 2.4307, + 2.4394, + 2.4503, + 2.4393, + 2.4462, + 2.4506, + 2.444, + 2.4529, + 2.4639, + 2.4437, + 2.4567, + 2.4657, + 2.4682, + 2.4628, + 2.4708, + 2.4649, + 2.4678, + 2.47, + 2.4684, + 2.4651, + 2.4652, + 2.4765, + 2.4849, + 2.4843, + 2.4862, + 2.4839, + 2.4824, + 2.4836, + 2.4859, + 2.487, + 2.4899, + 2.4877, + 2.4942, + 2.4993, + 2.4978, + 2.4983, + 2.4909, + 2.4888, + 2.4765, + 2.4862, + 2.4859, + 2.4852, + 2.489, + 2.4865, + 2.4813, + 2.4811, + 2.4818, + 2.4888, + 2.4925, + 2.4897, + 2.4876, + 2.4904, + 2.4857, + 2.4905, + 2.4972, + 2.4903, + 2.4881, + 2.4875, + 2.4851, + 2.4773, + 2.4835, + 2.4786, + 2.4791, + 2.4826, + 2.4832, + 2.4824, + 2.4831, + 2.4861, + 2.4885, + 2.4868, + 2.4847, + 2.4792, + 2.484, + 2.4849, + 2.4782, + 2.4784, + 2.483, + 2.48, + 2.485, + 2.4926, + 2.4948, + 2.4998, + 2.4942, + 2.4941, + 2.4905, + 2.4904, + 2.4903, + 2.495, + 2.4955, + 2.4962, + 2.499, + 2.5019, + 2.4995, + 2.4989, + 2.4896, + 2.4952, + 2.4943, + 2.4944, + 2.499, + 2.4983, + 2.4981, + 2.503, + 2.4964, + 2.4975, + 2.4987, + 2.4999, + 2.4912, + 2.487, + 2.4822, + 2.4807, + 2.4798, + 2.479, + 2.4844, + 2.4843, + 2.4791, + 2.4745, + 2.4739, + 2.4643, + 2.4618, + 2.4651, + 2.4585, + 2.4694, + 2.4708, + 2.4719, + 2.4702, + 2.4748, + 2.4749, + 2.4712, + 2.4725, + 2.4711, + 2.4656, + 2.4551, + 2.4443, + 2.4449, + 2.4548, + 2.4494, + 2.4569, + 2.4468, + 2.4496, + 2.4492, + 2.4489, + 2.4479, + 2.4558, + 2.4498, + 2.4542, + 2.4553, + 2.4565, + 2.4569, + 2.4587, + 2.4588, + 2.4565, + 2.4604, + 2.4628, + 2.4553, + 2.4433, + 2.4385, + 2.4275, + 2.4181, + 2.4116, + 2.4228, + 2.4278, + 2.424, + 2.4186, + 2.4202, + 2.4233, + 2.4245, + 2.4292, + 2.4319, + 2.4319, + 2.4298, + 2.4348, + 2.4339, + 2.4333, + 2.4288, + 2.4259, + 2.4363, + 2.435, + 2.4333, + 2.4295, + 2.4268, + 2.4312, + 2.4367, + 2.4385, + 2.4359, + 2.4368, + 2.4338, + 2.4422, + 2.4422, + 2.4458, + 2.4566, + 2.4606, + 2.4571, + 2.4661, + 2.4714, + 2.4777, + 2.4802, + 2.4869, + 2.4849, + 2.4834, + 2.473, + 2.4732, + 2.4812, + 2.4778, + 2.4721, + 2.4703, + 2.4732, + 2.4679, + 2.466, + 2.4638, + 2.464, + 2.4612, + 2.4586, + 2.4536, + 2.4532, + 2.4488, + 2.4476, + 2.4429, + 2.4433, + 2.4495, + 2.4549, + 2.4537, + 2.457, + 2.4601, + 2.4611, + 2.4589, + 2.4543, + 2.4534, + 2.4546, + 2.4542, + 2.4538, + 2.4589, + 2.461, + 2.4609, + 2.4624, + 2.4529, + 2.4497, + 2.4395, + 2.4464, + 2.4405, + 2.4394, + 2.4409, + 2.4393, + 2.4365, + 2.4271, + 2.4263, + 2.4323, + 2.4332, + 2.436, + 2.4418, + 2.4294, + 2.4276, + 2.4312, + 2.4287, + 2.4263, + 2.4248, + 2.4263, + 2.4328, + 2.4313, + 2.4356, + 2.4335, + 2.4366, + 2.4348, + 2.4392, + 2.4432, + 2.4438, + 2.4438, + 2.4401, + 2.4349, + 2.4391, + 2.4366, + 2.4454, + 2.4449, + 2.4513, + 2.4551, + 2.4587, + 2.4593, + 2.456, + 2.4581, + 2.4533, + 2.4534, + 2.4498, + 2.4486, + 2.4518, + 2.4532, + 2.4545, + 2.4536, + 2.4529, + 2.453, + 2.4568, + 2.4559, + 2.4604, + 2.4567, + 2.4622, + 2.4582, + 2.4544, + 2.4495, + 2.4467, + 2.4454, + 2.451, + 2.4485, + 2.4528, + 2.4486, + 2.4498, + 2.446, + 2.4435, + 2.4426, + 2.4474, + 2.4425, + 2.4451, + 2.4433, + 2.4441, + 2.4467, + 2.4457, + 2.4516, + 2.4539, + 2.4519, + 2.4394, + 2.4401, + 2.4452, + 2.4486, + 2.4462, + 2.4502, + 2.4501, + 2.4506, + 2.4526, + 2.452, + 2.4491, + 2.4449, + 2.4443, + 2.4487, + 2.4501, + 2.4454, + 2.4416, + 2.4455, + 2.446, + 2.4473, + 2.4435, + 2.4436, + 2.4408, + 2.444, + 2.4424, + 2.4446, + 2.4499, + 2.4484, + 2.4473, + 2.432, + 2.4354, + 2.4283, + 2.426, + 2.4225, + 2.4166, + 2.4214, + 2.4257, + 2.4301, + 2.425, + 2.422, + 2.4208, + 2.4142, + 2.414, + 2.4049, + 2.4038, + 2.4051, + 2.4003, + 2.4001, + 2.3971, + 2.388, + 2.3744, + 2.3873, + 2.37, + 2.357, + 2.3514, + 2.3622, + 2.3683, + 2.3646, + 2.3742, + 2.3749, + 2.3854, + 2.39, + 2.3833, + 2.3856, + 2.38, + 2.377, + 2.3666, + 2.3722, + 2.3733, + 2.3694, + 2.3733, + 2.3633, + 2.3686, + 2.3734, + 2.3725, + 2.3752, + 2.3788, + 2.3753, + 2.3731, + 2.3744, + 2.3701, + 2.3707, + 2.366, + 2.3638, + 2.3666, + 2.3744, + 2.368, + 2.37, + 2.3699, + 2.373, + 2.3797, + 2.3807, + 2.3834, + 2.385, + 2.3782, + 2.3753, + 2.3767, + 2.3788, + 2.3773, + 2.3754, + 2.3712, + 2.3707, + 2.3744, + 2.3691, + 2.3725, + 2.3628, + 2.3654, + 2.3621, + 2.376, + 2.3769, + 2.3788, + 2.3752, + 2.3814, + 2.3818, + 2.3897, + 2.4027, + 2.3978, + 2.4041, + 2.4007, + 2.4028, + 2.3962, + 2.3965, + 2.3972, + 2.3981, + 2.4059, + 2.4028, + 2.3994, + 2.3958, + 2.3967, + 2.3914, + 2.3929, + 2.3961, + 2.3939, + 2.3982, + 2.398, + 2.3998, + 2.394, + 2.3999, + 2.3983, + 2.3934, + 2.3906, + 2.389, + 2.396, + 2.3976, + 2.3955, + 2.3896, + 2.3939, + 2.3919, + 2.3915, + 2.3922, + 2.3883, + 2.3861, + 2.3877, + 2.3983, + 2.4014, + 2.4027, + 2.4066, + 2.403, + 2.396, + 2.3936, + 2.3947, + 2.3946, + 2.3986, + 2.4017, + 2.3988, + 2.411, + 2.4134, + 2.4132, + 2.4148, + 2.4166, + 2.422, + 2.4235, + 2.4307, + 2.4312, + 2.4347, + 2.429, + 2.4351, + 2.4353, + 2.4352, + 2.4362, + 2.4328, + 2.4335, + 2.4322, + 2.4296, + 2.4318, + 2.4269, + 2.4316, + 2.4322, + 2.4391, + 2.4401, + 2.4465, + 2.4452, + 2.4782, + 2.488, + 2.4964, + 2.4892, + 2.4928, + 2.5005, + 2.4978, + 2.4793, + 2.482, + 2.4813, + 2.4787, + 2.482, + 2.49, + 2.4841, + 2.508, + 2.508, + 2.4994, + 2.5, + 2.5005, + 2.5104, + 2.5111, + 2.5083, + 2.5083, + 2.5038, + 2.5047, + 2.5043, + 2.4987, + 2.4967, + 2.4919, + 2.49, + 2.4984, + 2.5051, + 2.5065, + 2.5125, + 2.5086, + 2.5126, + 2.5111, + 2.5038, + 2.5021, + 2.4999, + 2.5013, + 2.5031, + 2.5007, + 2.4965, + 2.4923, + 2.4836, + 2.4779, + 2.4773, + 2.4839, + 2.4853, + 2.487, + 2.4832, + 2.481, + 2.4785, + 2.4832, + 2.4818, + 2.4704, + 2.4695, + 2.4658, + 2.4634, + 2.4624, + 2.459, + 2.4596, + 2.4608, + 2.4614, + 2.4596, + 2.452, + 2.4442, + 2.451, + 2.4556, + 2.4539, + 2.4581, + 2.4572, + 2.456, + 2.4649, + 2.47, + 2.4719, + 2.4698, + 2.4696, + 2.4649, + 2.4616, + 2.4652, + 2.4645, + 2.4623, + 2.461, + 2.4548, + 2.4576, + 2.4599, + 2.4662, + 2.464, + 2.4687, + 2.4728, + 2.472, + 2.4725, + 2.4724, + 2.4716, + 2.4724, + 2.475, + 2.4774, + 2.4811, + 2.4834, + 2.4852, + 2.4894, + 2.4968, + 2.4944, + 2.4936, + 2.4889, + 2.4994, + 2.4952, + 2.4961, + 2.4975, + 2.4982, + 2.4986, + 2.4997, + 2.4913, + 2.4982, + 2.5034, + 2.506, + 2.5001, + 2.4916, + 2.5033, + 2.4959, + 2.4968, + 2.4924, + 2.4836, + 2.4908, + 2.4889, + 2.4882, + 2.4892, + 2.4864, + 2.4812, + 2.487, + 2.4843, + 2.4921, + 2.4988, + 2.5023, + 2.5041, + 2.5015, + 2.508, + 2.5092, + 2.5028, + 2.5041, + 2.5022, + 2.5114, + 2.5103, + 2.5097, + 2.5092, + 2.5114, + 2.508, + 2.5031, + 2.5014, + 2.5049, + 2.503, + 2.5128, + 2.5066, + 2.5035, + 2.5025, + 2.5008, + 2.4976, + 2.5082, + 2.5071, + 2.5138, + 2.5119, + 2.5105, + 2.5071, + 2.5039, + 2.5027, + 2.5006, + 2.4988, + 2.4986, + 2.4961, + 2.4957, + 2.4907, + 2.487, + 2.4812, + 2.4761, + 2.479, + 2.479, + 2.4859, + 2.4896, + 2.4904, + 2.4923, + 2.488, + 2.4913, + 2.4966, + 2.5005, + 2.5013, + 2.4956, + 2.4943, + 2.4892, + 2.4904, + 2.4852, + 2.489, + 2.4872, + 2.4913, + 2.4906, + 2.4918, + 2.4926, + 2.4948, + 2.4928, + 2.4977, + 2.5015, + 2.4981, + 2.4946, + 2.4943, + 2.492, + 2.4916, + 2.4938, + 2.4905, + 2.4922, + 2.5012, + 2.5056, + 2.5144, + 2.515, + 2.5158, + 2.5125, + 2.5131, + 2.5058, + 2.5197, + 2.5205, + 2.5199, + 2.5194, + 2.5185, + 2.5209, + 2.5255, + 2.5264, + 2.5268, + 2.5273, + 2.5293, + 2.5244, + 2.5214, + 2.5276, + 2.5239, + 2.5291, + 2.53, + 2.5278, + 2.5276, + 2.5313, + 2.5277, + 2.5294, + 2.5342, + 2.5264, + 2.5335, + 2.5346, + 2.5274, + 2.5234, + 2.5184, + 2.5273, + 2.523, + 2.5187, + 2.5179, + 2.5175, + 2.5168, + 2.5132, + 2.5159, + 2.513, + 2.513, + 2.5168, + 2.521, + 2.5225, + 2.5204, + 2.5246, + 2.5225, + 2.5169, + 2.5149, + 2.5124, + 2.5143, + 2.5243, + 2.5262, + 2.5372, + 2.5338, + 2.5368, + 2.5402, + 2.5365, + 2.5286, + 2.54, + 2.5388, + 2.5433, + 2.545, + 2.5483, + 2.5472, + 2.5459, + 2.5432, + 2.5436, + 2.5375, + 2.5347, + 2.5374, + 2.5283, + 2.5244, + 2.5304, + 2.5272, + 2.5249, + 2.5274, + 2.5245, + 2.5184, + 2.5238, + 2.5193, + 2.5142, + 2.5151, + 2.512, + 2.5067, + 2.5045, + 2.493, + 2.497, + 2.505, + 2.5042, + 2.5068, + 2.5048, + 2.5035, + 2.5018, + 2.4958, + 2.4987, + 2.4886, + 2.4832, + 2.4853, + 2.4848, + 2.4859, + 2.4849, + 2.482, + 2.488, + 2.4902, + 2.4888, + 2.4893, + 2.4896, + 2.4912, + 2.4929, + 2.4889, + 2.4871, + 2.4838, + 2.4846, + 2.4818, + 2.4789, + 2.4684, + 2.473, + 2.476, + 2.4728, + 2.4714, + 2.4772, + 2.4782, + 2.4844, + 2.4892, + 2.4912, + 2.4862, + 2.4888, + 2.4883, + 2.4828, + 2.4783, + 2.4791, + 2.4831, + 2.4791, + 2.4745, + 2.4773, + 2.4748, + 2.4736, + 2.4748, + 2.4811, + 2.484, + 2.4856, + 2.4803, + 2.4825, + 2.4859, + 2.4818, + 2.4856, + 2.4823, + 2.4834, + 2.4818, + 2.4816, + 2.4923, + 2.49, + 2.4904, + 2.4996, + 2.4959, + 2.4991, + 2.5015, + 2.5011, + 2.5053, + 2.5081, + 2.5152, + 2.5117, + 2.5098, + 2.5056, + 2.5065, + 2.5129, + 2.512, + 2.5166, + 2.5187, + 2.5181, + 2.5169, + 2.5179, + 2.5178, + 2.5159, + 2.5122, + 2.5111, + 2.5077, + 2.5028, + 2.5024, + 2.5036, + 2.5074, + 2.5112, + 2.5144, + 2.5164, + 2.5117, + 2.5181, + 2.5168, + 2.5145, + 2.517, + 2.5188, + 2.521, + 2.522, + 2.5189, + 2.5211, + 2.5162, + 2.5147, + 2.5079, + 2.4957, + 2.4713, + 2.4745, + 2.478, + 2.4762, + 2.4799, + 2.4655, + 2.4751, + 2.4707, + 2.4657, + 2.4724, + 2.4636, + 2.4645, + 2.4711, + 2.4706, + 2.4755, + 2.4567, + 2.446, + 2.4391, + 2.4534, + 2.4447, + 2.4336, + 2.4409, + 2.4338, + 2.4421, + 2.4394, + 2.4373, + 2.4335, + 2.4399, + 2.4506, + 2.4533, + 2.4532, + 2.4565, + 2.4537, + 2.4536, + 2.4611, + 2.4443, + 2.4508, + 2.4537, + 2.4572, + 2.4549, + 2.4561, + 2.4558, + 2.4536, + 2.4528, + 2.4575, + 2.4543, + 2.4531, + 2.4473, + 2.4515, + 2.451, + 2.45, + 2.4487, + 2.454, + 2.455, + 2.4512, + 2.441, + 2.444, + 2.4457, + 2.4523, + 2.458, + 2.4616, + 2.4589, + 2.4625, + 2.4662, + 2.4738, + 2.4753, + 2.4689, + 2.4648, + 2.47, + 2.4736, + 2.4761, + 2.473, + 2.4696, + 2.4682, + 2.4667, + 2.4679, + 2.465, + 2.466, + 2.4719, + 2.4666, + 2.4526, + 2.4522, + 2.4489, + 2.453, + 2.4538, + 2.4503, + 2.4472, + 2.4486, + 2.4512, + 2.4582, + 2.4595, + 2.4583, + 2.4581, + 2.4619, + 2.4591, + 2.4591, + 2.4626, + 2.4601, + 2.4613, + 2.461, + 2.4604, + 2.4603, + 2.4646, + 2.4599, + 2.4593, + 2.458, + 2.4567, + 2.4581, + 2.4544, + 2.4539, + 2.4534, + 2.4524, + 2.453, + 2.4599, + 2.4603, + 2.4603, + 2.4592, + 2.4577, + 2.4615, + 2.462, + 2.4654, + 2.4639, + 2.4639, + 2.4654, + 2.4636, + 2.4626, + 2.4593, + 2.4577, + 2.4647, + 2.4664, + 2.4637, + 2.4664, + 2.4692, + 2.4698, + 2.4686, + 2.4709, + 2.4697, + 2.4695, + 2.4729, + 2.4726, + 2.4737, + 2.4725, + 2.4732, + 2.4737, + 2.4762, + 2.4738, + 2.4749, + 2.4732, + 2.476, + 2.474, + 2.4702, + 2.4711, + 2.4756, + 2.4741, + 2.4775, + 2.4801, + 2.4825, + 2.481, + 2.485, + 2.4753, + 2.4752, + 2.4778, + 2.4756, + 2.4799, + 2.4783, + 2.4786, + 2.4786, + 2.4847, + 2.4879, + 2.4842, + 2.4867, + 2.4786, + 2.4764, + 2.4743, + 2.4722, + 2.4792, + 2.4847, + 2.4854, + 2.4825, + 2.4829, + 2.4789, + 2.4782, + 2.4783, + 2.4801, + 2.4764, + 2.475, + 2.4734, + 2.4726, + 2.4732, + 2.4733, + 2.471, + 2.4689, + 2.4693, + 2.4785, + 2.4785, + 2.4779, + 2.4735, + 2.4734, + 2.4777, + 2.4804, + 2.4809, + 2.4821, + 2.4832, + 2.4826, + 2.4852, + 2.4826, + 2.4868, + 2.4889, + 2.4881, + 2.4897, + 2.4891, + 2.4872, + 2.4893, + 2.486, + 2.4874, + 2.4883, + 2.4885, + 2.4895, + 2.4916, + 2.4965, + 2.498, + 2.4978, + 2.5004, + 2.4998, + 2.5021, + 2.503, + 2.5045, + 2.5084, + 2.5117, + 2.5108, + 2.5145, + 2.516, + 2.5162, + 2.519, + 2.5217, + 2.515, + 2.5185, + 2.5159, + 2.5061, + 2.5005, + 2.4851, + 2.4908, + 2.4915, + 2.5032, + 2.5056, + 2.5069, + 2.5016, + 2.502, + 2.5006, + 2.5139, + 2.5104, + 2.5119, + 2.5098, + 2.5064, + 2.5057, + 2.5015, + 2.5034, + 2.5039, + 2.4974, + 2.5058, + 2.5075, + 2.5096, + 2.5019, + 2.4975, + 2.5049, + 2.5016, + 2.5043, + 2.504, + 2.5103, + 2.5112, + 2.5097, + 2.5077, + 2.5043, + 2.5062, + 2.5064, + 2.5054, + 2.5056, + 2.5042, + 2.5023, + 2.5049, + 2.5069, + 2.5114, + 2.5106, + 2.5116, + 2.5038, + 2.5094, + 2.5078, + 2.5098, + 2.5121, + 2.5091, + 2.5052, + 2.5054, + 2.5053, + 2.5019, + 2.5028, + 2.5036, + 2.5025, + 2.4988, + 2.5023, + 2.5052, + 2.5041, + 2.5001, + 2.4998, + 2.4986, + 2.4968, + 2.494, + 2.5007, + 2.5062, + 2.5063, + 2.5055, + 2.508, + 2.5045, + 2.5009, + 2.5065, + 2.5112, + 2.5182, + 2.5254, + 2.5301, + 2.5212, + 2.5255, + 2.5202, + 2.5198, + 2.5256, + 2.528, + 2.5256, + 2.5196, + 2.5221, + 2.5182, + 2.5252, + 2.5237, + 2.5236, + 2.5237, + 2.5274, + 2.5271, + 2.5263, + 2.5271, + 2.5231, + 2.5274, + 2.5298, + 2.5266, + 2.5239, + 2.5188, + 2.5171, + 2.5176, + 2.5134, + 2.5174, + 2.522, + 2.5213, + 2.5208, + 2.5146, + 2.514, + 2.5104, + 2.5078, + 2.5092, + 2.5119, + 2.5096, + 2.5074, + 2.5091, + 2.5046, + 2.5053, + 2.5114, + 2.515, + 2.5162, + 2.5109, + 2.5041, + 2.5, + 2.5052, + 2.5051, + 2.5104, + 2.5117, + 2.5085, + 2.5105, + 2.5123, + 2.5227, + 2.5217, + 2.5204, + 2.5231, + 2.5183, + 2.523, + 2.5197, + 2.5192, + 2.5193, + 2.5208, + 2.5268, + 2.538, + 2.54, + 2.539, + 2.5388, + 2.5332, + 2.5077, + 2.4835, + 2.4773, + 2.4605, + 2.4797, + 2.4852, + 2.49, + 2.4806, + 2.4797, + 2.4893, + 2.4824, + 2.4838, + 2.4859, + 2.4852, + 2.4939, + 2.4894, + 2.4936, + 2.4852, + 2.4911, + 2.4898, + 2.4802, + 2.4819, + 2.4777, + 2.4614, + 2.466, + 2.4673, + 2.4557, + 2.4634, + 2.4662, + 2.4733, + 2.4696, + 2.4702, + 2.4703, + 2.4747, + 2.4708, + 2.4712, + 2.4683, + 2.4689, + 2.4651, + 2.4728, + 2.4793, + 2.477, + 2.4787, + 2.4785, + 2.4811, + 2.4844, + 2.4825, + 2.4837, + 2.4803, + 2.4836, + 2.4796, + 2.4835, + 2.4829, + 2.481, + 2.478, + 2.4798, + 2.4745, + 2.4735, + 2.4719, + 2.4803, + 2.4754, + 2.478, + 2.4784, + 2.472, + 2.472, + 2.4766, + 2.4772, + 2.4796, + 2.4808, + 2.475, + 2.4795, + 2.484, + 2.4882, + 2.4899, + 2.485, + 2.487, + 2.4814, + 2.4797, + 2.4858, + 2.4799, + 2.489, + 2.4887, + 2.4996, + 2.4968, + 2.5105, + 2.5118, + 2.5147, + 2.5063, + 2.5041, + 2.4998, + 2.5097, + 2.5172, + 2.517, + 2.5147, + 2.5139, + 2.5098, + 2.5115, + 2.5158, + 2.5117, + 2.5124, + 2.5128, + 2.5094, + 2.4986, + 2.4999, + 2.5055, + 2.507, + 2.507, + 2.5098, + 2.5145, + 2.5075, + 2.5179, + 2.5228, + 2.5231, + 2.5242, + 2.5249, + 2.5277, + 2.5286, + 2.541, + 2.5418, + 2.5356, + 2.5373, + 2.523, + 2.5111, + 2.5003, + 2.5095, + 2.4942, + 2.4963, + 2.5065, + 2.5119, + 2.5128, + 2.5093, + 2.4982, + 2.5029, + 2.4906, + 2.5004, + 2.5006, + 2.496, + 2.4935, + 2.4938, + 2.4978, + 2.5012, + 2.5005, + 2.503, + 2.5091, + 2.5135, + 2.527, + 2.5305, + 2.5344, + 2.5325, + 2.5244, + 2.5293, + 2.5418, + 2.5356, + 2.5477, + 2.5377, + 2.5397, + 2.5349, + 2.5266, + 2.5232, + 2.5384, + 2.5674, + 2.5805, + 2.5621, + 2.5599, + 2.5499, + 2.5443, + 2.5358, + 2.5342, + 2.5346, + 2.528, + 2.5165, + 2.506, + 2.5045, + 2.4948, + 2.5077, + 2.5128, + 2.5012, + 2.4986, + 2.4951, + 2.4977, + 2.5099, + 2.5123, + 2.5049, + 2.5091, + 2.5078, + 2.502, + 2.5069, + 2.5097, + 2.4926, + 2.4895, + 2.4979, + 2.5003, + 2.4793, + 2.4823, + 2.4926, + 2.4935, + 2.4931, + 2.4927, + 2.4917, + 2.4948, + 2.4992, + 2.5012, + 2.5021, + 2.5053, + 2.5011, + 2.5048, + 2.5052, + 2.5044, + 2.4998, + 2.5022, + 2.4998, + 2.4983, + 2.5148, + 2.5173, + 2.5166, + 2.5157, + 2.5203, + 2.5203, + 2.5169, + 2.5202, + 2.5213, + 2.5162, + 2.5179, + 2.5179, + 2.5184, + 2.519, + 2.5132, + 2.5155, + 2.513, + 2.5154, + 2.5137, + 2.5097, + 2.5104, + 2.5047, + 2.4958, + 2.4978, + 2.4943, + 2.4968, + 2.4877, + 2.4848, + 2.4892, + 2.4896, + 2.4984, + 2.5029, + 2.5015, + 2.4988, + 2.4998, + 2.4992, + 2.4969, + 2.4916, + 2.4899, + 2.4868, + 2.4852, + 2.4803, + 2.4799, + 2.4881, + 2.4873, + 2.489, + 2.4918, + 2.4952, + 2.5016, + 2.5073, + 2.5076, + 2.5135, + 2.5083, + 2.5118, + 2.5103, + 2.5087, + 2.5107, + 2.5098, + 2.5083, + 2.5043, + 2.5123, + 2.5179, + 2.5149, + 2.516, + 2.5146, + 2.5157, + 2.5133, + 2.5114, + 2.5112, + 2.5066, + 2.5044, + 2.5052, + 2.5113, + 2.5112, + 2.5029, + 2.4873, + 2.4949, + 2.5018, + 2.4988, + 2.4981, + 2.5015, + 2.4953, + 2.4972, + 2.4924, + 2.4818, + 2.4784, + 2.4697, + 2.4595, + 2.4472, + 2.459, + 2.4624, + 2.4655, + 2.4805, + 2.485, + 2.483, + 2.491, + 2.4892, + 2.484, + 2.4938, + 2.5039, + 2.5007, + 2.5143, + 2.5136, + 2.5201, + 2.5287, + 2.5297, + 2.5284, + 2.5808, + 2.5803, + 2.5813, + 2.5769, + 2.5668, + 2.5608, + 2.5688, + 2.576, + 2.582, + 2.5762, + 2.5724, + 2.564, + 2.5615, + 2.5642, + 2.5631, + 2.5689, + 2.5778, + 2.5765, + 2.5773, + 2.5706, + 2.5697, + 2.5654, + 2.5706, + 2.5843, + 2.5916, + 2.5858, + 2.596, + 2.6009, + 2.6175, + 2.609, + 2.6113, + 2.6055, + 2.5972, + 2.613, + 2.6197, + 2.6142, + 2.6011, + 2.6148, + 2.606, + 2.6077, + 2.6025, + 2.6034, + 2.6019, + 2.6032, + 2.6007, + 2.6062, + 2.6066, + 2.6047, + 2.6113, + 2.6009, + 2.6049, + 2.5879, + 2.5792, + 2.5892, + 2.5809, + 2.5804, + 2.5933, + 2.5938, + 2.588, + 2.5812, + 2.5898, + 2.5831, + 2.5812, + 2.5772, + 2.5768, + 2.5815, + 2.5768, + 2.5783, + 2.5769, + 2.5757, + 2.5748, + 2.5811, + 2.5867, + 2.5899, + 2.5927, + 2.5965, + 2.5987, + 2.5994, + 2.5999, + 2.5997, + 2.5978, + 2.5928, + 2.5987, + 2.5979, + 2.5878, + 2.585, + 2.5871, + 2.5864, + 2.5956, + 2.5945, + 2.592, + 2.5871, + 2.5829, + 2.5827, + 2.5821, + 2.5818, + 2.5851, + 2.5901, + 2.5873, + 2.5937, + 2.5915, + 2.5896, + 2.5925, + 2.5886, + 2.5867, + 2.5911, + 2.6031, + 2.6142, + 2.6115, + 2.6142, + 2.6136, + 2.6163, + 2.6207, + 2.6307, + 2.633, + 2.6243, + 2.628, + 2.639, + 2.6374, + 2.648, + 2.6382, + 2.6724, + 2.6584, + 2.6512, + 2.6614, + 2.67, + 2.6787, + 2.6751, + 2.6759, + 2.6783, + 2.6752, + 2.6686, + 2.6649, + 2.6685, + 2.656, + 2.6449, + 2.6435, + 2.6396, + 2.6475, + 2.656, + 2.6678, + 2.6442, + 2.6374, + 2.6263, + 2.6414, + 2.6451, + 2.6304, + 2.6327, + 2.631, + 2.6324, + 2.6302, + 2.6212, + 2.6212, + 2.62, + 2.6223, + 2.616, + 2.6269, + 2.6194, + 2.6038, + 2.6004, + 2.5909, + 2.5863, + 2.5882, + 2.5732, + 2.5701, + 2.5782, + 2.5784, + 2.5825, + 2.5906, + 2.5861, + 2.5886, + 2.5872, + 2.5805, + 2.5837, + 2.5816, + 2.5772, + 2.5769, + 2.567, + 2.5668, + 2.5601, + 2.5513, + 2.5565, + 2.5659, + 2.5628, + 2.5518, + 2.5582, + 2.5739, + 2.5687, + 2.5774, + 2.5516, + 2.5665, + 2.5714, + 2.5847, + 2.5852, + 2.5859, + 2.5955, + 2.5982, + 2.5953, + 2.592, + 2.5831, + 2.5869, + 2.5934, + 2.5883, + 2.5863, + 2.5849, + 2.5944, + 2.6024, + 2.6168, + 2.6116, + 2.6194, + 2.6173, + 2.6101, + 2.6054, + 2.6049, + 2.6122, + 2.6052, + 2.6045, + 2.5986, + 2.601, + 2.5997, + 2.5991, + 2.6002, + 2.602, + 2.6086, + 2.596, + 2.6052, + 2.6102, + 2.6076, + 2.6039, + 2.603, + 2.5962, + 2.6073, + 2.6092, + 2.6083, + 2.6039, + 2.6099, + 2.6126, + 2.61, + 2.6024, + 2.5972, + 2.5923, + 2.5897, + 2.5883, + 2.5926, + 2.599, + 2.5937, + 2.5952, + 2.5926, + 2.5973, + 2.6031, + 2.5985, + 2.5949, + 2.5939, + 2.5988, + 2.6006, + 2.6123, + 2.6096, + 2.6114, + 2.6124, + 2.6217, + 2.6249, + 2.624, + 2.6426, + 2.6482, + 2.6442, + 2.6547, + 2.6515, + 2.6499, + 2.652, + 2.6481, + 2.6428, + 2.6528, + 2.6423, + 2.6464, + 2.6512, + 2.6506, + 2.6466, + 2.6404, + 2.6305, + 2.6282, + 2.6214, + 2.6252, + 2.6135, + 2.6186, + 2.6271, + 2.6331, + 2.6338, + 2.6333, + 2.629, + 2.6235, + 2.6303, + 2.6363, + 2.6309, + 2.6286, + 2.638, + 2.6404, + 2.6446, + 2.6435, + 2.645, + 2.6367, + 2.6328, + 2.6325, + 2.6392, + 2.6442, + 2.6394, + 2.6406, + 2.642, + 2.6422, + 2.6479, + 2.645, + 2.6386, + 2.6313, + 2.6379, + 2.6388, + 2.637, + 2.6388, + 2.6507, + 2.6481, + 2.6496, + 2.6623, + 2.6531, + 2.6385, + 2.6178, + 2.627, + 2.629, + 2.6333, + 2.6379, + 2.6324, + 2.6365, + 2.635, + 2.6338, + 2.6281, + 2.6332, + 2.6302, + 2.6291, + 2.6325, + 2.628, + 2.6434, + 2.644, + 2.647, + 2.6373, + 2.6431, + 2.6481, + 2.6514, + 2.6543, + 2.6581, + 2.6539, + 2.6621, + 2.6628, + 2.6583, + 2.6588, + 2.6663, + 2.6725, + 2.6716, + 2.6711, + 2.6777, + 2.6847, + 2.6872, + 2.6838, + 2.6824, + 2.682, + 2.6865, + 2.6823, + 2.674, + 2.676, + 2.6699, + 2.6697, + 2.6675, + 2.6689, + 2.6698, + 2.6646, + 2.6724, + 2.6644, + 2.6565, + 2.6566, + 2.6522, + 2.6519, + 2.66, + 2.6568, + 2.6571, + 2.6612, + 2.6575, + 2.6573, + 2.6492, + 2.6474, + 2.649, + 2.6548, + 2.6547, + 2.6558, + 2.655, + 2.6497, + 2.6471, + 2.6472, + 2.6451, + 2.6418, + 2.6406, + 2.6415, + 2.6397, + 2.6506, + 2.6533, + 2.6469, + 2.651, + 2.6527, + 2.6738, + 2.6695, + 2.6605, + 2.6697, + 2.6733, + 2.6756, + 2.6816, + 2.6738, + 2.6894, + 2.6943, + 2.6916, + 2.6984, + 2.6864, + 2.6938, + 2.6944, + 2.6815, + 2.6792, + 2.6715, + 2.6735, + 2.6782, + 2.6981, + 2.7078, + 2.7202, + 2.7194, + 2.7248, + 2.7315, + 2.7386, + 2.7274, + 2.7246, + 2.7274, + 2.723, + 2.7179, + 2.7169, + 2.722, + 2.7248, + 2.7223, + 2.7238, + 2.7092, + 2.7075, + 2.7191, + 2.7192, + 2.7205, + 2.7146, + 2.7256, + 2.7112, + 2.7131, + 2.7182, + 2.7067, + 2.7065, + 2.7056, + 2.7036, + 2.7, + 2.6809, + 2.6841, + 2.6772, + 2.6833, + 2.6754, + 2.6678, + 2.6691, + 2.6746, + 2.6721, + 2.6782, + 2.6818, + 2.6806, + 2.6853, + 2.6869, + 2.6913, + 2.6922, + 2.697, + 2.6993, + 2.6945, + 2.6904, + 2.6792, + 2.6842, + 2.6791, + 2.684, + 2.676, + 2.6824, + 2.6813, + 2.6798, + 2.6793, + 2.6752, + 2.6759, + 2.6663, + 2.6645, + 2.6505, + 2.6481, + 2.633, + 2.633, + 2.6311, + 2.6342, + 2.6443, + 2.6428, + 2.6434, + 2.6609, + 2.6513, + 2.6522, + 2.6615, + 2.6484, + 2.6559, + 2.6624, + 2.6764, + 2.6909, + 2.6898, + 2.6912, + 2.6874, + 2.6852, + 2.6687, + 2.6694, + 2.6653, + 2.5812, + 2.4867, + 2.5074, + 2.5222, + 2.5892, + 2.5567, + 2.5925, + 2.5623, + 2.5712, + 2.5495, + 2.5363, + 2.5391, + 2.5231, + 2.5633, + 2.5712, + 2.565, + 2.5719, + 2.5722, + 2.5885, + 2.5951, + 2.6234, + 2.6307, + 2.633, + 2.6316, + 2.6398, + 2.6476, + 2.6492, + 2.6533, + 2.644, + 2.6547, + 2.6439, + 2.6462, + 2.6439, + 2.6403, + 2.6334, + 2.6328, + 2.5976, + 2.6058, + 2.5996, + 2.6147, + 2.6025, + 2.5941, + 2.5672, + 2.5541, + 2.5622, + 2.5419, + 2.5514, + 2.5437, + 2.547, + 2.5503, + 2.5456, + 2.533, + 2.5296, + 2.5369, + 2.5411, + 2.5349, + 2.5278, + 2.5236, + 2.5381, + 2.5364, + 2.5376, + 2.5515, + 2.5526, + 2.5417, + 2.5288, + 2.5008, + 2.4935, + 2.4865, + 2.4999, + 2.4885, + 2.4944, + 2.4862, + 2.4904, + 2.5026, + 2.4961, + 2.4994, + 2.5062, + 2.4914, + 2.4949, + 2.4957, + 2.4861, + 2.4802, + 2.4768, + 2.4668, + 2.4522, + 2.4592, + 2.4626, + 2.4512, + 2.4511, + 2.4406, + 2.4348, + 2.4394, + 2.4312, + 2.4453, + 2.4568, + 2.4655, + 2.466, + 2.4708, + 2.479, + 2.481, + 2.4742, + 2.4783, + 2.4767, + 2.4582, + 2.4566, + 2.4531, + 2.4533, + 2.4555, + 2.4506, + 2.4522, + 2.4517, + 2.449, + 2.4566, + 2.4517, + 2.447, + 2.4521, + 2.4582, + 2.4557, + 2.474, + 2.4631, + 2.4676, + 2.4855, + 2.4869, + 2.4755, + 2.4763, + 2.486, + 2.4899, + 2.4927, + 2.4997, + 2.4996, + 2.4966, + 2.4938, + 2.5015, + 2.498, + 2.4981, + 2.5097, + 2.5193, + 2.5108, + 2.5147, + 2.5222, + 2.5224, + 2.5303, + 2.5274, + 2.5254, + 2.5245, + 2.52, + 2.5005, + 2.4968, + 2.5045, + 2.5021, + 2.5126, + 2.5089, + 2.5018, + 2.5044, + 2.5058, + 2.5056, + 2.5051, + 2.5002, + 2.5047, + 2.5145, + 2.5134, + 2.5057, + 2.5074, + 2.5084, + 2.5075, + 2.5136, + 2.5182, + 2.5204, + 2.5158, + 2.5153, + 2.5094, + 2.5079, + 2.5131, + 2.5102, + 2.5106, + 2.5048, + 2.5021, + 2.5002, + 2.5072, + 2.5144, + 2.516, + 2.5177, + 2.5162, + 2.5097, + 2.5058, + 2.4982, + 2.5001, + 2.4941, + 2.5026, + 2.4987, + 2.5017, + 2.504, + 2.5022, + 2.5045, + 2.5039, + 2.503, + 2.5099, + 2.5077, + 2.4992, + 2.5066, + 2.5088, + 2.5102, + 2.5117, + 2.5063, + 2.5024, + 2.4997, + 2.5018, + 2.4975, + 2.4932, + 2.4952, + 2.4899, + 2.4832, + 2.4884, + 2.4829, + 2.4764, + 2.4797, + 2.4865, + 2.484, + 2.4888, + 2.4887, + 2.4864, + 2.4885, + 2.486, + 2.4872, + 2.4779, + 2.474, + 2.4728, + 2.4682, + 2.4631, + 2.4572, + 2.4617, + 2.4604, + 2.4566, + 2.4486, + 2.4649, + 2.4631, + 2.4612, + 2.4688, + 2.4648, + 2.4633, + 2.4623, + 2.464, + 2.4567, + 2.4608, + 2.4581, + 2.4474, + 2.4437, + 2.4372, + 2.4415, + 2.4564, + 2.444, + 2.4457, + 2.458, + 2.4452, + 2.4502, + 2.4543, + 2.4472, + 2.4462, + 2.4417, + 2.4414, + 2.4489, + 2.4572, + 2.4699, + 2.4687, + 2.471, + 2.4591, + 2.4691, + 2.4673, + 2.4588, + 2.4607, + 2.4643, + 2.4566, + 2.4685, + 2.4719, + 2.4657, + 2.4612, + 2.4664, + 2.4594, + 2.4553, + 2.4449, + 2.4585, + 2.4517, + 2.4516, + 2.4579, + 2.4473, + 2.4608, + 2.4626, + 2.4506, + 2.4501, + 2.4614, + 2.4544, + 2.4529, + 2.4484, + 2.4447, + 2.4482, + 2.4562, + 2.4618, + 2.4605, + 2.4568, + 2.4479, + 2.4475, + 2.4475, + 2.4388, + 2.431, + 2.4349, + 2.4345, + 2.4363, + 2.4282, + 2.4193, + 2.4297, + 2.4241, + 2.427, + 2.4264, + 2.4247, + 2.4152, + 2.4252, + 2.4199, + 2.4184, + 2.4175, + 2.4173, + 2.4122, + 2.3996, + 2.4104, + 2.4094, + 2.4044, + 2.3947, + 2.3822, + 2.3975, + 2.3935, + 2.4006, + 2.3993, + 2.3947, + 2.4093, + 2.4086, + 2.4131, + 2.4068, + 2.4131, + 2.4146, + 2.4106, + 2.4036, + 2.4021, + 2.4003, + 2.3948, + 2.3925, + 2.3894, + 2.3835, + 2.3829, + 2.3904, + 2.3837, + 2.3798, + 2.3751, + 2.3855, + 2.3964, + 2.4012, + 2.3971, + 2.4024, + 2.4065, + 2.4087, + 2.4078, + 2.4004, + 2.3996, + 2.3999, + 2.3968, + 2.4053, + 2.3994, + 2.3978, + 2.4049, + 2.3996, + 2.3991, + 2.4013, + 2.3964, + 2.4027, + 2.4101, + 2.41, + 2.4093, + 2.4069, + 2.4113, + 2.4146, + 2.4146, + 2.422, + 2.4257, + 2.4249, + 2.4321, + 2.4227, + 2.4234, + 2.428, + 2.4299, + 2.4314, + 2.429, + 2.4281, + 2.4315, + 2.4331, + 2.4322, + 2.4343, + 2.4355, + 2.4343, + 2.4347 + ], + "high": [ + 2.381, + 2.3848, + 2.385, + 2.3803, + 2.3876, + 2.3859, + 2.3816, + 2.3788, + 2.3748, + 2.3733, + 2.3691, + 2.3726, + 2.3764, + 2.3752, + 2.3679, + 2.3755, + 2.3678, + 2.3651, + 2.3694, + 2.3698, + 2.3697, + 2.3838, + 2.3862, + 2.3971, + 2.3963, + 2.3958, + 2.3901, + 2.3952, + 2.3998, + 2.4111, + 2.4129, + 2.4142, + 2.411, + 2.4142, + 2.4141, + 2.4225, + 2.4303, + 2.428, + 2.4319, + 2.4333, + 2.4316, + 2.4324, + 2.4336, + 2.4309, + 2.4235, + 2.4189, + 2.4114, + 2.4095, + 2.4109, + 2.4085, + 2.4055, + 2.4093, + 2.4128, + 2.4118, + 2.4039, + 2.4079, + 2.4119, + 2.4129, + 2.4097, + 2.4113, + 2.4178, + 2.4158, + 2.4131, + 2.411, + 2.4119, + 2.421, + 2.4176, + 2.415, + 2.4145, + 2.413, + 2.4177, + 2.4212, + 2.4207, + 2.4168, + 2.4156, + 2.4185, + 2.4189, + 2.4197, + 2.4216, + 2.4241, + 2.425, + 2.4263, + 2.4243, + 2.4219, + 2.4217, + 2.4154, + 2.4201, + 2.4383, + 2.4356, + 2.4327, + 2.433, + 2.4312, + 2.4231, + 2.4238, + 2.4212, + 2.4207, + 2.4202, + 2.4307, + 2.4293, + 2.4299, + 2.4304, + 2.431, + 2.4239, + 2.4251, + 2.4253, + 2.4276, + 2.4342, + 2.4299, + 2.4346, + 2.4339, + 2.4315, + 2.4311, + 2.4283, + 2.4247, + 2.4247, + 2.4214, + 2.4223, + 2.4214, + 2.4174, + 2.42, + 2.4248, + 2.4301, + 2.4303, + 2.4321, + 2.4307, + 2.4309, + 2.4302, + 2.44, + 2.4404, + 2.4408, + 2.4448, + 2.4457, + 2.4446, + 2.4388, + 2.436, + 2.4426, + 2.4479, + 2.4467, + 2.4429, + 2.4458, + 2.4463, + 2.4548, + 2.453, + 2.4521, + 2.4555, + 2.4587, + 2.4666, + 2.468, + 2.4605, + 2.4642, + 2.4644, + 2.4514, + 2.4538, + 2.4524, + 2.4459, + 2.4385, + 2.4299, + 2.4316, + 2.4385, + 2.4383, + 2.4387, + 2.444, + 2.444, + 2.4485, + 2.4467, + 2.4434, + 2.4412, + 2.4424, + 2.4406, + 2.4362, + 2.4369, + 2.4534, + 2.4551, + 2.4541, + 2.4595, + 2.4634, + 2.4594, + 2.461, + 2.464, + 2.4648, + 2.4645, + 2.4545, + 2.4486, + 2.4564, + 2.4574, + 2.4676, + 2.4699, + 2.4793, + 2.499, + 2.499, + 2.5, + 2.5002, + 2.4999, + 2.507, + 2.5222, + 2.5446, + 2.5306, + 2.5228, + 2.5184, + 2.513, + 2.5251, + 2.515, + 2.52, + 2.52, + 2.5117, + 2.5105, + 2.5063, + 2.5066, + 2.4895, + 2.4913, + 2.4851, + 2.487, + 2.4927, + 2.4818, + 2.4805, + 2.4832, + 2.4802, + 2.4724, + 2.4776, + 2.4772, + 2.4715, + 2.4587, + 2.4507, + 2.448, + 2.4439, + 2.4412, + 2.4404, + 2.4341, + 2.433, + 2.4278, + 2.4261, + 2.4268, + 2.4214, + 2.4245, + 2.4252, + 2.425, + 2.4304, + 2.4257, + 2.4322, + 2.4302, + 2.4294, + 2.4291, + 2.4342, + 2.4364, + 2.437, + 2.4413, + 2.4565, + 2.461, + 2.4708, + 2.4672, + 2.4625, + 2.4553, + 2.4607, + 2.4599, + 2.4495, + 2.4464, + 2.4468, + 2.4415, + 2.4455, + 2.449, + 2.4532, + 2.4492, + 2.4458, + 2.4459, + 2.4408, + 2.4434, + 2.449, + 2.4454, + 2.4474, + 2.4461, + 2.4496, + 2.4581, + 2.4629, + 2.4553, + 2.4534, + 2.4562, + 2.4532, + 2.4494, + 2.4491, + 2.4512, + 2.4495, + 2.4474, + 2.4548, + 2.4613, + 2.4619, + 2.461, + 2.4632, + 2.4685, + 2.4679, + 2.4644, + 2.4669, + 2.4643, + 2.4675, + 2.4649, + 2.4664, + 2.4732, + 2.4727, + 2.4698, + 2.4672, + 2.4657, + 2.4658, + 2.4636, + 2.4612, + 2.458, + 2.4574, + 2.4575, + 2.4556, + 2.4542, + 2.4658, + 2.4638, + 2.4581, + 2.4513, + 2.443, + 2.4375, + 2.442, + 2.448, + 2.448, + 2.4509, + 2.4484, + 2.4469, + 2.4421, + 2.4324, + 2.4308, + 2.4315, + 2.4339, + 2.4354, + 2.4324, + 2.4249, + 2.4188, + 2.4189, + 2.4188, + 2.4172, + 2.4066, + 2.4003, + 2.3939, + 2.4, + 2.4037, + 2.3988, + 2.3994, + 2.4, + 2.4106, + 2.413, + 2.4131, + 2.4157, + 2.4162, + 2.4156, + 2.4165, + 2.4159, + 2.4155, + 2.4114, + 2.4019, + 2.3987, + 2.4025, + 2.3976, + 2.3946, + 2.4019, + 2.3996, + 2.4014, + 2.3983, + 2.4045, + 2.4083, + 2.4077, + 2.4106, + 2.4146, + 2.4158, + 2.4151, + 2.4194, + 2.4179, + 2.4153, + 2.4145, + 2.416, + 2.4174, + 2.4175, + 2.4176, + 2.415, + 2.4109, + 2.4143, + 2.4133, + 2.4147, + 2.4103, + 2.4079, + 2.4094, + 2.4097, + 2.4116, + 2.4102, + 2.4066, + 2.4081, + 2.4117, + 2.4107, + 2.407, + 2.405, + 2.4034, + 2.4061, + 2.4066, + 2.4053, + 2.4074, + 2.4076, + 2.3998, + 2.4015, + 2.3926, + 2.3843, + 2.387, + 2.39, + 2.392, + 2.4025, + 2.412, + 2.4048, + 2.4051, + 2.402, + 2.3987, + 2.3974, + 2.3923, + 2.3926, + 2.3868, + 2.3956, + 2.3935, + 2.3939, + 2.398, + 2.4006, + 2.4, + 2.4, + 2.4005, + 2.4067, + 2.4092, + 2.4115, + 2.4096, + 2.409, + 2.4065, + 2.4081, + 2.4061, + 2.4055, + 2.4127, + 2.4337, + 2.4298, + 2.4205, + 2.4269, + 2.4251, + 2.4236, + 2.4245, + 2.4248, + 2.4228, + 2.4224, + 2.4258, + 2.4228, + 2.4227, + 2.4312, + 2.4259, + 2.4196, + 2.4212, + 2.4153, + 2.4078, + 2.4198, + 2.417, + 2.4204, + 2.4194, + 2.43, + 2.4232, + 2.4177, + 2.4154, + 2.4088, + 2.4254, + 2.4295, + 2.4259, + 2.4331, + 2.4346, + 2.44, + 2.4413, + 2.4488, + 2.45, + 2.4477, + 2.445, + 2.4409, + 2.4478, + 2.4471, + 2.4464, + 2.45, + 2.4534, + 2.4498, + 2.4551, + 2.4529, + 2.4504, + 2.4538, + 2.4549, + 2.4551, + 2.4518, + 2.4466, + 2.4461, + 2.45, + 2.4507, + 2.4515, + 2.4562, + 2.4633, + 2.4625, + 2.4642, + 2.4638, + 2.4709, + 2.4523, + 2.4587, + 2.4625, + 2.4598, + 2.4637, + 2.4458, + 2.4479, + 2.4478, + 2.4484, + 2.4461, + 2.4387, + 2.4399, + 2.439, + 2.4391, + 2.4428, + 2.4375, + 2.4285, + 2.4408, + 2.4579, + 2.4511, + 2.4488, + 2.4466, + 2.4431, + 2.4418, + 2.4443, + 2.4475, + 2.4475, + 2.4507, + 2.4601, + 2.4669, + 2.4498, + 2.4483, + 2.4505, + 2.466, + 2.4569, + 2.448, + 2.4404, + 2.4407, + 2.4367, + 2.4394, + 2.4427, + 2.455, + 2.464, + 2.4685, + 2.4817, + 2.49, + 2.4889, + 2.49, + 2.4866, + 2.4881, + 2.4835, + 2.4823, + 2.4751, + 2.4695, + 2.4766, + 2.4783, + 2.48, + 2.4865, + 2.4848, + 2.4783, + 2.4752, + 2.4791, + 2.478, + 2.4727, + 2.4739, + 2.4808, + 2.4852, + 2.4872, + 2.4862, + 2.4812, + 2.4879, + 2.4867, + 2.4831, + 2.483, + 2.4857, + 2.4865, + 2.482, + 2.4823, + 2.485, + 2.4879, + 2.4927, + 2.4948, + 2.4938, + 2.4996, + 2.4991, + 2.4999, + 2.4975, + 2.4965, + 2.4968, + 2.498, + 2.4975, + 2.4879, + 2.4904, + 2.4834, + 2.4908, + 2.4894, + 2.4926, + 2.4931, + 2.4994, + 2.504, + 2.5023, + 2.4939, + 2.489, + 2.4892, + 2.4878, + 2.487, + 2.4824, + 2.4761, + 2.473, + 2.4696, + 2.4709, + 2.4702, + 2.4642, + 2.463, + 2.4677, + 2.4693, + 2.466, + 2.464, + 2.4612, + 2.4664, + 2.4672, + 2.4638, + 2.4578, + 2.4575, + 2.4561, + 2.45, + 2.446, + 2.4474, + 2.451, + 2.4534, + 2.4538, + 2.449, + 2.4489, + 2.4454, + 2.4382, + 2.4367, + 2.4326, + 2.4338, + 2.4352, + 2.436, + 2.4356, + 2.4376, + 2.4377, + 2.4394, + 2.4484, + 2.4535, + 2.4579, + 2.4612, + 2.458, + 2.4651, + 2.4711, + 2.4672, + 2.4649, + 2.4645, + 2.4592, + 2.4599, + 2.4539, + 2.4553, + 2.4569, + 2.4598, + 2.4609, + 2.4615, + 2.4601, + 2.4543, + 2.46, + 2.4767, + 2.4801, + 2.478, + 2.4789, + 2.4762, + 2.473, + 2.4781, + 2.483, + 2.487, + 2.485, + 2.4792, + 2.4711, + 2.4783, + 2.4777, + 2.4738, + 2.4786, + 2.4803, + 2.487, + 2.4829, + 2.4831, + 2.4875, + 2.4888, + 2.4851, + 2.4877, + 2.4878, + 2.4846, + 2.4843, + 2.4773, + 2.4722, + 2.4759, + 2.4771, + 2.4788, + 2.4705, + 2.4659, + 2.4759, + 2.4795, + 2.4822, + 2.4851, + 2.4851, + 2.4885, + 2.4899, + 2.4913, + 2.4906, + 2.4841, + 2.4815, + 2.4729, + 2.472, + 2.4691, + 2.4682, + 2.4664, + 2.4604, + 2.457, + 2.4583, + 2.4628, + 2.4669, + 2.4676, + 2.4647, + 2.4644, + 2.4625, + 2.4576, + 2.4611, + 2.462, + 2.4612, + 2.4576, + 2.4629, + 2.4577, + 2.4587, + 2.4559, + 2.4514, + 2.4494, + 2.4455, + 2.4397, + 2.4417, + 2.4428, + 2.4476, + 2.448, + 2.453, + 2.4533, + 2.454, + 2.4619, + 2.4633, + 2.468, + 2.468, + 2.4704, + 2.4803, + 2.4897, + 2.506, + 2.5006, + 2.4944, + 2.4794, + 2.5257, + 2.53, + 2.5271, + 2.5263, + 2.5213, + 2.5236, + 2.5217, + 2.5164, + 2.5175, + 2.524, + 2.5243, + 2.5295, + 2.5287, + 2.5188, + 2.523, + 2.5158, + 2.5024, + 2.4946, + 2.4946, + 2.4878, + 2.4765, + 2.4734, + 2.4493, + 2.4489, + 2.461, + 2.4614, + 2.4606, + 2.4497, + 2.4491, + 2.4475, + 2.4413, + 2.4254, + 2.429, + 2.4271, + 2.4284, + 2.426, + 2.4181, + 2.4145, + 2.4136, + 2.4214, + 2.4299, + 2.439, + 2.4483, + 2.4448, + 2.4472, + 2.4485, + 2.4412, + 2.4545, + 2.4568, + 2.4568, + 2.4679, + 2.4749, + 2.4755, + 2.4605, + 2.452, + 2.4423, + 2.4542, + 2.4518, + 2.4563, + 2.4658, + 2.4657, + 2.4798, + 2.4797, + 2.4746, + 2.481, + 2.4818, + 2.482, + 2.4788, + 2.476, + 2.4837, + 2.4872, + 2.487, + 2.488, + 2.491, + 2.5066, + 2.5069, + 2.5016, + 2.5085, + 2.5266, + 2.5284, + 2.5485, + 2.5474, + 2.546, + 2.54, + 2.5349, + 2.5256, + 2.5275, + 2.5335, + 2.5365, + 2.5398, + 2.5255, + 2.5196, + 2.5157, + 2.5167, + 2.517, + 2.516, + 2.5124, + 2.5054, + 2.503, + 2.4995, + 2.4984, + 2.4953, + 2.4929, + 2.4923, + 2.4859, + 2.4846, + 2.4871, + 2.4868, + 2.4877, + 2.4988, + 2.493, + 2.4873, + 2.4919, + 2.4928, + 2.4924, + 2.49, + 2.504, + 2.509, + 2.5099, + 2.509, + 2.512, + 2.5233, + 2.5273, + 2.522, + 2.53, + 2.5162, + 2.5169, + 2.5243, + 2.5195, + 2.5066, + 2.515, + 2.4968, + 2.4822, + 2.4687, + 2.4707, + 2.4702, + 2.4564, + 2.4536, + 2.4495, + 2.4372, + 2.4239, + 2.4269, + 2.4306, + 2.4309, + 2.4368, + 2.4333, + 2.4295, + 2.4328, + 2.4364, + 2.4458, + 2.4614, + 2.4606, + 2.4586, + 2.4456, + 2.449, + 2.4494, + 2.4415, + 2.4399, + 2.443, + 2.4406, + 2.4387, + 2.4376, + 2.446, + 2.4534, + 2.4634, + 2.4647, + 2.4644, + 2.4643, + 2.4633, + 2.4611, + 2.4676, + 2.4745, + 2.4884, + 2.4735, + 2.4801, + 2.468, + 2.4706, + 2.4768, + 2.4776, + 2.4828, + 2.4783, + 2.4742, + 2.4682, + 2.4528, + 2.4538, + 2.4413, + 2.4462, + 2.443, + 2.4484, + 2.5242, + 2.4935, + 2.4875, + 2.5117, + 2.5126, + 2.4885, + 2.4737, + 2.463, + 2.4533, + 2.4648, + 2.4638, + 2.46, + 2.4501, + 2.4453, + 2.452, + 2.4493, + 2.4424, + 2.4416, + 2.4308, + 2.4309, + 2.4415, + 2.4528, + 2.4513, + 2.4496, + 2.452, + 2.4553, + 2.4544, + 2.4646, + 2.4639, + 2.457, + 2.4666, + 2.4692, + 2.4719, + 2.4719, + 2.4739, + 2.4732, + 2.4707, + 2.475, + 2.4716, + 2.4731, + 2.4785, + 2.4872, + 2.4883, + 2.4893, + 2.4871, + 2.4916, + 2.4843, + 2.4866, + 2.4925, + 2.4919, + 2.4914, + 2.4942, + 2.4993, + 2.5012, + 2.5022, + 2.5013, + 2.4915, + 2.4917, + 2.4871, + 2.494, + 2.4941, + 2.4895, + 2.4921, + 2.4882, + 2.4843, + 2.4849, + 2.4894, + 2.4931, + 2.4927, + 2.4918, + 2.4928, + 2.4913, + 2.4906, + 2.4977, + 2.4971, + 2.4928, + 2.4906, + 2.4878, + 2.4852, + 2.4835, + 2.4888, + 2.4794, + 2.4884, + 2.4848, + 2.4849, + 2.4851, + 2.4886, + 2.4885, + 2.4888, + 2.4878, + 2.4853, + 2.4844, + 2.4854, + 2.4854, + 2.4835, + 2.4833, + 2.4843, + 2.4858, + 2.4963, + 2.4982, + 2.5014, + 2.4996, + 2.4962, + 2.496, + 2.4948, + 2.4916, + 2.495, + 2.5, + 2.498, + 2.4997, + 2.5019, + 2.5098, + 2.5009, + 2.4998, + 2.4982, + 2.501, + 2.4986, + 2.499, + 2.4995, + 2.4995, + 2.505, + 2.5067, + 2.5002, + 2.4998, + 2.501, + 2.5003, + 2.493, + 2.4871, + 2.4864, + 2.4816, + 2.4815, + 2.4869, + 2.4861, + 2.4844, + 2.4795, + 2.4794, + 2.4744, + 2.4661, + 2.4699, + 2.465, + 2.4695, + 2.4709, + 2.4744, + 2.4732, + 2.483, + 2.4762, + 2.4754, + 2.4743, + 2.4726, + 2.4725, + 2.4679, + 2.4582, + 2.4469, + 2.4558, + 2.4574, + 2.4569, + 2.4586, + 2.4518, + 2.4534, + 2.4505, + 2.4506, + 2.4562, + 2.4563, + 2.4542, + 2.4578, + 2.4573, + 2.4581, + 2.4603, + 2.4615, + 2.459, + 2.4617, + 2.4632, + 2.4637, + 2.4558, + 2.4455, + 2.4385, + 2.4275, + 2.4172, + 2.4229, + 2.4285, + 2.4294, + 2.4256, + 2.4247, + 2.426, + 2.4261, + 2.4316, + 2.4319, + 2.4334, + 2.4331, + 2.4382, + 2.4398, + 2.4365, + 2.4334, + 2.43, + 2.4371, + 2.4389, + 2.4371, + 2.4343, + 2.4295, + 2.435, + 2.4428, + 2.4412, + 2.4413, + 2.439, + 2.4384, + 2.4442, + 2.4443, + 2.4493, + 2.4609, + 2.4619, + 2.4637, + 2.4664, + 2.4735, + 2.4778, + 2.4815, + 2.4878, + 2.4885, + 2.4911, + 2.4835, + 2.4757, + 2.4832, + 2.4857, + 2.4831, + 2.4758, + 2.4763, + 2.4811, + 2.4723, + 2.4684, + 2.4715, + 2.4646, + 2.4644, + 2.4622, + 2.4551, + 2.4537, + 2.4503, + 2.4556, + 2.4476, + 2.4504, + 2.4568, + 2.4588, + 2.4577, + 2.4642, + 2.4611, + 2.461, + 2.4595, + 2.4615, + 2.458, + 2.457, + 2.459, + 2.4615, + 2.462, + 2.4619, + 2.4626, + 2.4622, + 2.4529, + 2.4506, + 2.4477, + 2.4475, + 2.4447, + 2.4409, + 2.4413, + 2.4394, + 2.4372, + 2.4292, + 2.4336, + 2.4378, + 2.4379, + 2.4422, + 2.4423, + 2.4332, + 2.4329, + 2.433, + 2.4313, + 2.4272, + 2.432, + 2.4354, + 2.4335, + 2.4356, + 2.4361, + 2.4373, + 2.4385, + 2.44, + 2.4455, + 2.446, + 2.4458, + 2.4463, + 2.4419, + 2.4417, + 2.4399, + 2.4456, + 2.4456, + 2.452, + 2.4564, + 2.4592, + 2.4609, + 2.4627, + 2.4582, + 2.4584, + 2.4545, + 2.4538, + 2.4529, + 2.4548, + 2.4562, + 2.4545, + 2.4555, + 2.4571, + 2.4571, + 2.4574, + 2.4586, + 2.461, + 2.4619, + 2.4626, + 2.4622, + 2.4582, + 2.4543, + 2.45, + 2.4468, + 2.452, + 2.4537, + 2.4535, + 2.4545, + 2.4499, + 2.4499, + 2.4465, + 2.4456, + 2.4486, + 2.4476, + 2.4451, + 2.4451, + 2.4443, + 2.4479, + 2.4479, + 2.4516, + 2.4581, + 2.4551, + 2.4522, + 2.4409, + 2.4457, + 2.4488, + 2.4498, + 2.4511, + 2.4512, + 2.4514, + 2.4533, + 2.4531, + 2.4552, + 2.4491, + 2.4471, + 2.4487, + 2.4506, + 2.4501, + 2.4453, + 2.4465, + 2.4476, + 2.4478, + 2.4479, + 2.444, + 2.4439, + 2.4459, + 2.4449, + 2.4459, + 2.4499, + 2.4526, + 2.4491, + 2.4481, + 2.4356, + 2.44, + 2.429, + 2.4271, + 2.423, + 2.425, + 2.4333, + 2.4308, + 2.4322, + 2.4255, + 2.4293, + 2.4227, + 2.4179, + 2.4144, + 2.4056, + 2.4108, + 2.4099, + 2.4006, + 2.4039, + 2.4023, + 2.3887, + 2.3875, + 2.3894, + 2.3725, + 2.3569, + 2.3635, + 2.3688, + 2.3706, + 2.375, + 2.3786, + 2.3901, + 2.3902, + 2.3899, + 2.388, + 2.3864, + 2.3867, + 2.3802, + 2.3722, + 2.3763, + 2.3733, + 2.3754, + 2.3742, + 2.3717, + 2.375, + 2.3753, + 2.3753, + 2.3869, + 2.3822, + 2.3762, + 2.3766, + 2.3751, + 2.3724, + 2.3724, + 2.3669, + 2.3676, + 2.3766, + 2.3747, + 2.3714, + 2.3719, + 2.373, + 2.3798, + 2.3812, + 2.3852, + 2.3863, + 2.3856, + 2.383, + 2.3811, + 2.3795, + 2.3787, + 2.3774, + 2.381, + 2.3719, + 2.3771, + 2.3771, + 2.3764, + 2.3726, + 2.3659, + 2.3689, + 2.3764, + 2.3796, + 2.3861, + 2.3803, + 2.3828, + 2.3838, + 2.3903, + 2.4038, + 2.4048, + 2.4056, + 2.4056, + 2.4036, + 2.4028, + 2.3982, + 2.4058, + 2.4004, + 2.4069, + 2.4063, + 2.403, + 2.3998, + 2.3975, + 2.3986, + 2.395, + 2.3976, + 2.3968, + 2.3983, + 2.4, + 2.4002, + 2.3997, + 2.4012, + 2.4023, + 2.4005, + 2.3988, + 2.3931, + 2.3966, + 2.3989, + 2.3987, + 2.3969, + 2.3945, + 2.3941, + 2.3936, + 2.3937, + 2.3941, + 2.3891, + 2.3905, + 2.3983, + 2.4055, + 2.4038, + 2.4078, + 2.408, + 2.4034, + 2.3963, + 2.395, + 2.3972, + 2.4, + 2.402, + 2.4052, + 2.4141, + 2.422, + 2.4151, + 2.4206, + 2.4209, + 2.422, + 2.4251, + 2.4313, + 2.4316, + 2.4379, + 2.438, + 2.4361, + 2.4377, + 2.4417, + 2.4389, + 2.4367, + 2.4352, + 2.4342, + 2.4345, + 2.4352, + 2.4343, + 2.4349, + 2.4359, + 2.4397, + 2.4419, + 2.4475, + 2.4503, + 2.4797, + 2.4925, + 2.4974, + 2.4994, + 2.4998, + 2.5017, + 2.527, + 2.4982, + 2.4824, + 2.4845, + 2.4814, + 2.4836, + 2.49, + 2.4943, + 2.5272, + 2.51, + 2.51, + 2.5089, + 2.508, + 2.5149, + 2.5135, + 2.5116, + 2.5137, + 2.5092, + 2.508, + 2.5092, + 2.5062, + 2.4998, + 2.5, + 2.4942, + 2.499, + 2.5053, + 2.507, + 2.5142, + 2.5127, + 2.5147, + 2.5162, + 2.5115, + 2.5048, + 2.5029, + 2.502, + 2.5056, + 2.5039, + 2.5008, + 2.4965, + 2.4944, + 2.4887, + 2.483, + 2.4864, + 2.4907, + 2.4891, + 2.487, + 2.4862, + 2.4834, + 2.4835, + 2.4836, + 2.4817, + 2.4724, + 2.4704, + 2.4681, + 2.4639, + 2.4647, + 2.4605, + 2.464, + 2.4633, + 2.4639, + 2.4596, + 2.4544, + 2.4525, + 2.4556, + 2.4588, + 2.4591, + 2.4582, + 2.4585, + 2.4657, + 2.47, + 2.4728, + 2.4723, + 2.4723, + 2.4696, + 2.4665, + 2.466, + 2.4676, + 2.4674, + 2.4651, + 2.4617, + 2.4576, + 2.4612, + 2.4678, + 2.4668, + 2.4687, + 2.4733, + 2.4737, + 2.4748, + 2.4746, + 2.4739, + 2.4739, + 2.475, + 2.4789, + 2.4815, + 2.4855, + 2.4855, + 2.4906, + 2.4977, + 2.4987, + 2.4963, + 2.4935, + 2.4995, + 2.501, + 2.4985, + 2.4975, + 2.5, + 2.4995, + 2.5018, + 2.4993, + 2.4982, + 2.5043, + 2.5072, + 2.5065, + 2.5008, + 2.5034, + 2.5033, + 2.4984, + 2.499, + 2.4923, + 2.4913, + 2.4919, + 2.4907, + 2.4893, + 2.4891, + 2.4862, + 2.4874, + 2.4875, + 2.4923, + 2.4988, + 2.5023, + 2.5059, + 2.5052, + 2.5089, + 2.5103, + 2.5115, + 2.5117, + 2.5039, + 2.5125, + 2.515, + 2.5107, + 2.5123, + 2.512, + 2.515, + 2.5088, + 2.5038, + 2.5064, + 2.5056, + 2.5151, + 2.5134, + 2.5065, + 2.5055, + 2.5048, + 2.5044, + 2.5088, + 2.5102, + 2.5138, + 2.5134, + 2.5128, + 2.5138, + 2.51, + 2.5044, + 2.5038, + 2.5007, + 2.4996, + 2.4988, + 2.497, + 2.496, + 2.492, + 2.4885, + 2.4829, + 2.482, + 2.481, + 2.4876, + 2.4921, + 2.493, + 2.4924, + 2.4931, + 2.4938, + 2.4993, + 2.5026, + 2.5045, + 2.5021, + 2.4966, + 2.4956, + 2.4921, + 2.493, + 2.4912, + 2.4891, + 2.4922, + 2.5012, + 2.4929, + 2.4935, + 2.4958, + 2.4955, + 2.4982, + 2.5017, + 2.5015, + 2.5005, + 2.4946, + 2.4948, + 2.4961, + 2.4948, + 2.4975, + 2.4924, + 2.5014, + 2.5069, + 2.5152, + 2.518, + 2.5176, + 2.5156, + 2.514, + 2.5132, + 2.5197, + 2.5232, + 2.5212, + 2.5235, + 2.5209, + 2.5229, + 2.5269, + 2.529, + 2.527, + 2.5285, + 2.5328, + 2.5296, + 2.5251, + 2.5276, + 2.5298, + 2.5291, + 2.5325, + 2.531, + 2.5294, + 2.5313, + 2.5393, + 2.5346, + 2.5353, + 2.5351, + 2.5356, + 2.5351, + 2.539, + 2.5295, + 2.5274, + 2.5301, + 2.531, + 2.523, + 2.5212, + 2.5182, + 2.5206, + 2.5213, + 2.5162, + 2.5172, + 2.5147, + 2.5179, + 2.521, + 2.5231, + 2.5238, + 2.5246, + 2.526, + 2.5228, + 2.5184, + 2.5159, + 2.5186, + 2.5246, + 2.5277, + 2.538, + 2.538, + 2.5375, + 2.5421, + 2.5452, + 2.5411, + 2.541, + 2.5419, + 2.5449, + 2.545, + 2.5499, + 2.5512, + 2.55, + 2.5471, + 2.546, + 2.5458, + 2.5392, + 2.5389, + 2.5375, + 2.5303, + 2.5328, + 2.5303, + 2.5294, + 2.5325, + 2.5296, + 2.5258, + 2.5242, + 2.5272, + 2.5197, + 2.5169, + 2.5183, + 2.5128, + 2.508, + 2.5077, + 2.5016, + 2.5059, + 2.5068, + 2.5092, + 2.5081, + 2.51, + 2.5054, + 2.502, + 2.5011, + 2.4986, + 2.4956, + 2.4903, + 2.4868, + 2.4862, + 2.4873, + 2.4869, + 2.4902, + 2.4906, + 2.4909, + 2.4913, + 2.4919, + 2.4922, + 2.493, + 2.4928, + 2.4898, + 2.4874, + 2.4846, + 2.4848, + 2.4827, + 2.4797, + 2.476, + 2.476, + 2.4811, + 2.4747, + 2.48, + 2.48, + 2.4848, + 2.4912, + 2.4918, + 2.4917, + 2.4897, + 2.4897, + 2.4885, + 2.4843, + 2.4842, + 2.4847, + 2.4847, + 2.4807, + 2.4795, + 2.4788, + 2.4774, + 2.4789, + 2.4815, + 2.4841, + 2.4863, + 2.4888, + 2.483, + 2.4884, + 2.4878, + 2.4882, + 2.4873, + 2.4845, + 2.4843, + 2.485, + 2.4926, + 2.493, + 2.4928, + 2.4997, + 2.5018, + 2.5014, + 2.503, + 2.5026, + 2.5071, + 2.5088, + 2.5165, + 2.5157, + 2.5129, + 2.5103, + 2.5073, + 2.5136, + 2.5158, + 2.5168, + 2.5194, + 2.52, + 2.5214, + 2.52, + 2.5196, + 2.518, + 2.516, + 2.5123, + 2.5134, + 2.5094, + 2.504, + 2.5049, + 2.5079, + 2.5112, + 2.5158, + 2.5188, + 2.5162, + 2.5185, + 2.5187, + 2.5172, + 2.517, + 2.519, + 2.5211, + 2.5285, + 2.5224, + 2.5234, + 2.5211, + 2.5171, + 2.5164, + 2.5357, + 2.5003, + 2.4794, + 2.4807, + 2.48, + 2.4817, + 2.48, + 2.4769, + 2.4762, + 2.4709, + 2.4815, + 2.4763, + 2.4681, + 2.4731, + 2.4716, + 2.4774, + 2.4757, + 2.4606, + 2.4497, + 2.455, + 2.4576, + 2.4491, + 2.446, + 2.4434, + 2.4458, + 2.447, + 2.4422, + 2.4398, + 2.4448, + 2.4524, + 2.4565, + 2.4538, + 2.457, + 2.4566, + 2.4539, + 2.4612, + 2.463, + 2.4514, + 2.4537, + 2.4591, + 2.46, + 2.46, + 2.4574, + 2.4569, + 2.4545, + 2.4576, + 2.4594, + 2.4556, + 2.4558, + 2.4515, + 2.4548, + 2.4511, + 2.4538, + 2.4555, + 2.4552, + 2.4558, + 2.4517, + 2.4449, + 2.4479, + 2.4525, + 2.4611, + 2.4625, + 2.4621, + 2.4632, + 2.468, + 2.4754, + 2.4773, + 2.4758, + 2.4691, + 2.47, + 2.4736, + 2.4765, + 2.4777, + 2.4756, + 2.4722, + 2.4706, + 2.4709, + 2.4683, + 2.4716, + 2.4723, + 2.4727, + 2.4671, + 2.4595, + 2.4558, + 2.4555, + 2.4575, + 2.4577, + 2.4503, + 2.45, + 2.4538, + 2.4584, + 2.4623, + 2.4606, + 2.4588, + 2.4626, + 2.4617, + 2.4628, + 2.4639, + 2.4634, + 2.463, + 2.4624, + 2.463, + 2.4607, + 2.4647, + 2.4644, + 2.4612, + 2.4593, + 2.4606, + 2.4595, + 2.4581, + 2.457, + 2.4561, + 2.4541, + 2.453, + 2.4599, + 2.4615, + 2.4619, + 2.4612, + 2.4609, + 2.4637, + 2.4631, + 2.4665, + 2.4656, + 2.4643, + 2.4666, + 2.4662, + 2.464, + 2.465, + 2.4597, + 2.4647, + 2.4696, + 2.4682, + 2.4679, + 2.4697, + 2.471, + 2.4717, + 2.4714, + 2.4713, + 2.4717, + 2.4729, + 2.4737, + 2.4739, + 2.4745, + 2.4739, + 2.475, + 2.4765, + 2.4761, + 2.4751, + 2.4748, + 2.4762, + 2.4776, + 2.4739, + 2.4719, + 2.4756, + 2.476, + 2.4786, + 2.4809, + 2.4829, + 2.4825, + 2.4869, + 2.4863, + 2.4807, + 2.478, + 2.478, + 2.4811, + 2.4801, + 2.4787, + 2.483, + 2.486, + 2.4879, + 2.4875, + 2.4882, + 2.4873, + 2.4787, + 2.4779, + 2.4742, + 2.4792, + 2.4851, + 2.4895, + 2.4872, + 2.4833, + 2.4836, + 2.4789, + 2.4803, + 2.4808, + 2.4807, + 2.4763, + 2.476, + 2.4741, + 2.4743, + 2.4736, + 2.4734, + 2.4712, + 2.47, + 2.4785, + 2.4801, + 2.4796, + 2.4791, + 2.475, + 2.4779, + 2.4804, + 2.4821, + 2.4847, + 2.4845, + 2.4842, + 2.4872, + 2.4866, + 2.4871, + 2.4897, + 2.4898, + 2.4907, + 2.4908, + 2.4897, + 2.491, + 2.4919, + 2.4885, + 2.4887, + 2.4894, + 2.4907, + 2.4939, + 2.4976, + 2.498, + 2.499, + 2.5022, + 2.5031, + 2.5032, + 2.5037, + 2.505, + 2.5096, + 2.5118, + 2.5163, + 2.5161, + 2.5171, + 2.519, + 2.521, + 2.5222, + 2.5216, + 2.5199, + 2.5185, + 2.5167, + 2.5067, + 2.5025, + 2.4932, + 2.494, + 2.5093, + 2.507, + 2.5098, + 2.5088, + 2.5037, + 2.5023, + 2.5139, + 2.5148, + 2.5123, + 2.512, + 2.5099, + 2.508, + 2.5061, + 2.5043, + 2.507, + 2.5047, + 2.5062, + 2.5123, + 2.5126, + 2.5097, + 2.5031, + 2.5064, + 2.5052, + 2.5072, + 2.5055, + 2.5103, + 2.5144, + 2.512, + 2.5099, + 2.5079, + 2.5071, + 2.509, + 2.5074, + 2.5076, + 2.507, + 2.5058, + 2.5052, + 2.5097, + 2.5124, + 2.5122, + 2.514, + 2.5115, + 2.5105, + 2.5114, + 2.5127, + 2.5138, + 2.5126, + 2.5095, + 2.5076, + 2.5061, + 2.5057, + 2.5039, + 2.5057, + 2.5038, + 2.5024, + 2.5029, + 2.5058, + 2.5079, + 2.5042, + 2.5042, + 2.5001, + 2.5008, + 2.4981, + 2.5025, + 2.5068, + 2.5072, + 2.51, + 2.51, + 2.5087, + 2.5047, + 2.5069, + 2.5112, + 2.5188, + 2.5254, + 2.5314, + 2.5305, + 2.5265, + 2.5271, + 2.5225, + 2.5257, + 2.5283, + 2.5283, + 2.5255, + 2.5225, + 2.5221, + 2.5264, + 2.5271, + 2.5259, + 2.5256, + 2.5287, + 2.5292, + 2.5281, + 2.5283, + 2.5271, + 2.5285, + 2.5321, + 2.5297, + 2.5283, + 2.525, + 2.5205, + 2.5188, + 2.5177, + 2.5203, + 2.5223, + 2.5235, + 2.5227, + 2.5224, + 2.5182, + 2.514, + 2.5131, + 2.5136, + 2.512, + 2.5127, + 2.5106, + 2.5094, + 2.5106, + 2.5062, + 2.5124, + 2.5166, + 2.5168, + 2.5169, + 2.5116, + 2.5045, + 2.508, + 2.5068, + 2.511, + 2.512, + 2.5117, + 2.5109, + 2.5127, + 2.5233, + 2.5236, + 2.5223, + 2.5234, + 2.5256, + 2.5236, + 2.5235, + 2.5232, + 2.5237, + 2.521, + 2.5271, + 2.5406, + 2.5451, + 2.5441, + 2.5412, + 2.5423, + 2.5345, + 2.5075, + 2.4836, + 2.4775, + 2.4806, + 2.4872, + 2.4906, + 2.4903, + 2.485, + 2.4923, + 2.4928, + 2.4862, + 2.4875, + 2.4888, + 2.4948, + 2.4942, + 2.4969, + 2.4947, + 2.4919, + 2.4929, + 2.4922, + 2.4846, + 2.4818, + 2.4805, + 2.4722, + 2.471, + 2.47, + 2.471, + 2.4681, + 2.474, + 2.4763, + 2.4722, + 2.4719, + 2.475, + 2.4751, + 2.4772, + 2.4721, + 2.4711, + 2.4695, + 2.4731, + 2.4793, + 2.4798, + 2.4794, + 2.48, + 2.4822, + 2.4848, + 2.4866, + 2.4865, + 2.484, + 2.4855, + 2.4836, + 2.486, + 2.4855, + 2.4836, + 2.4809, + 2.483, + 2.4797, + 2.4743, + 2.4762, + 2.4803, + 2.4818, + 2.4807, + 2.4789, + 2.4784, + 2.4726, + 2.4767, + 2.4784, + 2.4809, + 2.4823, + 2.4808, + 2.4795, + 2.4849, + 2.4894, + 2.4934, + 2.4936, + 2.4876, + 2.487, + 2.4813, + 2.4891, + 2.4862, + 2.4914, + 2.4911, + 2.5025, + 2.5032, + 2.513, + 2.52, + 2.5175, + 2.5158, + 2.5087, + 2.5078, + 2.5109, + 2.5209, + 2.5213, + 2.5183, + 2.5186, + 2.514, + 2.512, + 2.5158, + 2.5198, + 2.5138, + 2.519, + 2.5139, + 2.5101, + 2.5055, + 2.5067, + 2.509, + 2.5093, + 2.5116, + 2.5167, + 2.5192, + 2.52, + 2.5269, + 2.525, + 2.5299, + 2.5303, + 2.5313, + 2.5299, + 2.5436, + 2.5511, + 2.5427, + 2.5412, + 2.5372, + 2.5252, + 2.5137, + 2.5116, + 2.5091, + 2.5056, + 2.508, + 2.5135, + 2.516, + 2.5241, + 2.5118, + 2.5057, + 2.5064, + 2.5015, + 2.5046, + 2.501, + 2.4983, + 2.4952, + 2.4994, + 2.5043, + 2.5043, + 2.5052, + 2.5098, + 2.5143, + 2.5284, + 2.532, + 2.5362, + 2.536, + 2.5365, + 2.5345, + 2.5418, + 2.5462, + 2.55, + 2.55, + 2.5416, + 2.55, + 2.5368, + 2.5267, + 2.5387, + 2.5686, + 2.5934, + 2.5807, + 2.5645, + 2.5605, + 2.5497, + 2.5465, + 2.5431, + 2.5354, + 2.5359, + 2.533, + 2.5205, + 2.5097, + 2.5069, + 2.5087, + 2.5152, + 2.5146, + 2.5079, + 2.5006, + 2.4986, + 2.5105, + 2.5125, + 2.513, + 2.5103, + 2.5096, + 2.5078, + 2.508, + 2.51, + 2.5097, + 2.4958, + 2.5014, + 2.503, + 2.5012, + 2.4876, + 2.4946, + 2.4941, + 2.495, + 2.4949, + 2.4931, + 2.496, + 2.5, + 2.502, + 2.5025, + 2.5053, + 2.5059, + 2.5054, + 2.5069, + 2.506, + 2.5045, + 2.5022, + 2.5045, + 2.4999, + 2.5152, + 2.518, + 2.5199, + 2.5199, + 2.5211, + 2.5216, + 2.5201, + 2.5207, + 2.5214, + 2.524, + 2.5183, + 2.5195, + 2.5192, + 2.5215, + 2.5193, + 2.5155, + 2.5159, + 2.5155, + 2.5199, + 2.5147, + 2.5115, + 2.5113, + 2.5048, + 2.4982, + 2.5037, + 2.4991, + 2.4989, + 2.4886, + 2.4898, + 2.4927, + 2.499, + 2.5033, + 2.5028, + 2.5032, + 2.4998, + 2.4999, + 2.4997, + 2.4969, + 2.493, + 2.4904, + 2.4868, + 2.4851, + 2.4841, + 2.4895, + 2.4913, + 2.489, + 2.4937, + 2.4956, + 2.5025, + 2.5075, + 2.5112, + 2.5135, + 2.5147, + 2.5118, + 2.5132, + 2.512, + 2.5111, + 2.5121, + 2.5121, + 2.5083, + 2.5137, + 2.5184, + 2.5185, + 2.5179, + 2.5168, + 2.5171, + 2.5156, + 2.514, + 2.5122, + 2.5113, + 2.5065, + 2.5056, + 2.5124, + 2.5127, + 2.5114, + 2.5028, + 2.5017, + 2.5054, + 2.5026, + 2.5003, + 2.5018, + 2.5015, + 2.5002, + 2.4979, + 2.494, + 2.4838, + 2.4799, + 2.4733, + 2.4597, + 2.4599, + 2.4632, + 2.4671, + 2.4809, + 2.4862, + 2.4854, + 2.4913, + 2.4924, + 2.4908, + 2.4949, + 2.505, + 2.5119, + 2.5147, + 2.5174, + 2.5206, + 2.5298, + 2.532, + 2.5299, + 2.5858, + 2.5851, + 2.605, + 2.59, + 2.5783, + 2.5732, + 2.5693, + 2.5817, + 2.5896, + 2.5852, + 2.582, + 2.5728, + 2.5707, + 2.5667, + 2.5641, + 2.571, + 2.5801, + 2.583, + 2.5785, + 2.5771, + 2.5764, + 2.5703, + 2.5712, + 2.5847, + 2.5973, + 2.5921, + 2.5965, + 2.602, + 2.6226, + 2.6224, + 2.6139, + 2.6119, + 2.6067, + 2.6136, + 2.6215, + 2.6196, + 2.6154, + 2.6161, + 2.6153, + 2.6094, + 2.611, + 2.607, + 2.6085, + 2.6043, + 2.6046, + 2.6142, + 2.6074, + 2.6093, + 2.6156, + 2.6113, + 2.6094, + 2.6048, + 2.5896, + 2.5914, + 2.5893, + 2.5893, + 2.5951, + 2.5952, + 2.5937, + 2.5879, + 2.5917, + 2.5898, + 2.5874, + 2.5877, + 2.5813, + 2.5855, + 2.5819, + 2.5833, + 2.5789, + 2.5821, + 2.5808, + 2.5846, + 2.5888, + 2.5912, + 2.5946, + 2.597, + 2.6049, + 2.6018, + 2.6059, + 2.604, + 2.6033, + 2.5989, + 2.5993, + 2.6019, + 2.5978, + 2.5907, + 2.5892, + 2.5872, + 2.5956, + 2.5961, + 2.5978, + 2.5919, + 2.5871, + 2.5854, + 2.5859, + 2.5855, + 2.5864, + 2.5902, + 2.5921, + 2.594, + 2.5952, + 2.5949, + 2.5942, + 2.593, + 2.5928, + 2.5972, + 2.6045, + 2.62, + 2.6175, + 2.6157, + 2.6146, + 2.6209, + 2.622, + 2.64, + 2.6362, + 2.6438, + 2.6382, + 2.641, + 2.644, + 2.65, + 2.6493, + 2.6773, + 2.6747, + 2.66, + 2.6616, + 2.6718, + 2.6922, + 2.6802, + 2.6811, + 2.6832, + 2.6784, + 2.6879, + 2.6705, + 2.6718, + 2.6693, + 2.6568, + 2.6473, + 2.644, + 2.652, + 2.6571, + 2.6695, + 2.6696, + 2.6516, + 2.6429, + 2.6448, + 2.6467, + 2.6456, + 2.6357, + 2.6364, + 2.637, + 2.6398, + 2.6301, + 2.6277, + 2.6295, + 2.6257, + 2.6249, + 2.6294, + 2.6286, + 2.6195, + 2.6065, + 2.6038, + 2.5932, + 2.5901, + 2.5898, + 2.5795, + 2.5829, + 2.5838, + 2.5868, + 2.5906, + 2.5931, + 2.5906, + 2.5929, + 2.5902, + 2.586, + 2.5866, + 2.5849, + 2.5802, + 2.578, + 2.5712, + 2.5668, + 2.562, + 2.5567, + 2.5726, + 2.5742, + 2.5686, + 2.5656, + 2.576, + 2.5742, + 2.5775, + 2.5792, + 2.571, + 2.5768, + 2.5941, + 2.591, + 2.5922, + 2.5966, + 2.6, + 2.6, + 2.5983, + 2.5922, + 2.5895, + 2.594, + 2.595, + 2.5934, + 2.5875, + 2.5957, + 2.6047, + 2.6198, + 2.6172, + 2.6222, + 2.622, + 2.6207, + 2.6151, + 2.6077, + 2.6163, + 2.6129, + 2.6072, + 2.6073, + 2.602, + 2.6042, + 2.6, + 2.6077, + 2.6077, + 2.6096, + 2.6084, + 2.6085, + 2.6102, + 2.6124, + 2.6077, + 2.6043, + 2.604, + 2.6092, + 2.61, + 2.6131, + 2.6083, + 2.6099, + 2.6156, + 2.6132, + 2.611, + 2.6023, + 2.5992, + 2.593, + 2.5914, + 2.5928, + 2.6008, + 2.6002, + 2.5953, + 2.598, + 2.5985, + 2.6051, + 2.6041, + 2.6035, + 2.596, + 2.5988, + 2.603, + 2.6137, + 2.6199, + 2.6174, + 2.622, + 2.6264, + 2.6261, + 2.6276, + 2.6478, + 2.6531, + 2.6542, + 2.6598, + 2.6587, + 2.6563, + 2.6556, + 2.6527, + 2.6516, + 2.6556, + 2.6566, + 2.6514, + 2.6515, + 2.6578, + 2.6519, + 2.6484, + 2.6415, + 2.6357, + 2.6323, + 2.6265, + 2.6273, + 2.6237, + 2.6293, + 2.6366, + 2.6384, + 2.6359, + 2.6343, + 2.6293, + 2.6323, + 2.6401, + 2.6366, + 2.6381, + 2.6383, + 2.6404, + 2.6447, + 2.6473, + 2.6468, + 2.6475, + 2.6422, + 2.6364, + 2.6425, + 2.6467, + 2.645, + 2.6435, + 2.6459, + 2.6487, + 2.6505, + 2.6512, + 2.6492, + 2.6402, + 2.6395, + 2.6505, + 2.6416, + 2.6414, + 2.6507, + 2.6535, + 2.6544, + 2.6648, + 2.6622, + 2.6534, + 2.6393, + 2.6283, + 2.6356, + 2.6387, + 2.6382, + 2.6435, + 2.6396, + 2.638, + 2.6413, + 2.6363, + 2.6356, + 2.6363, + 2.6304, + 2.6336, + 2.6328, + 2.6445, + 2.6542, + 2.6556, + 2.6558, + 2.6446, + 2.6549, + 2.6551, + 2.6552, + 2.6594, + 2.6635, + 2.6638, + 2.6699, + 2.6633, + 2.6654, + 2.6678, + 2.6733, + 2.6762, + 2.6762, + 2.6788, + 2.6872, + 2.6873, + 2.688, + 2.692, + 2.6883, + 2.6904, + 2.688, + 2.684, + 2.6786, + 2.6789, + 2.674, + 2.6699, + 2.6727, + 2.6715, + 2.6706, + 2.6759, + 2.6724, + 2.6644, + 2.6598, + 2.658, + 2.657, + 2.661, + 2.663, + 2.6583, + 2.6631, + 2.6633, + 2.6601, + 2.6573, + 2.6504, + 2.6555, + 2.6575, + 2.6582, + 2.6575, + 2.6565, + 2.6563, + 2.6509, + 2.6488, + 2.6479, + 2.648, + 2.6463, + 2.6441, + 2.642, + 2.6575, + 2.6536, + 2.6554, + 2.651, + 2.6531, + 2.674, + 2.6751, + 2.6699, + 2.6721, + 2.6753, + 2.6758, + 2.682, + 2.6826, + 2.6907, + 2.7, + 2.6962, + 2.7022, + 2.6983, + 2.7066, + 2.6976, + 2.6965, + 2.6823, + 2.6833, + 2.6745, + 2.6786, + 2.7018, + 2.7099, + 2.7253, + 2.7246, + 2.731, + 2.7418, + 2.741, + 2.75, + 2.7354, + 2.7333, + 2.7298, + 2.7277, + 2.7197, + 2.7232, + 2.728, + 2.7291, + 2.7238, + 2.7263, + 2.7131, + 2.722, + 2.7202, + 2.7242, + 2.7209, + 2.7349, + 2.7265, + 2.7151, + 2.721, + 2.7206, + 2.7131, + 2.7066, + 2.7118, + 2.7056, + 2.6999, + 2.689, + 2.684, + 2.6878, + 2.6832, + 2.68, + 2.6729, + 2.6785, + 2.6785, + 2.6816, + 2.6822, + 2.6845, + 2.6871, + 2.687, + 2.6941, + 2.6973, + 2.6989, + 2.6993, + 2.7035, + 2.6954, + 2.6922, + 2.6844, + 2.6856, + 2.6861, + 2.6862, + 2.6869, + 2.6867, + 2.686, + 2.6826, + 2.6809, + 2.6763, + 2.6795, + 2.6672, + 2.666, + 2.6534, + 2.6488, + 2.638, + 2.6426, + 2.6375, + 2.6466, + 2.6466, + 2.6488, + 2.6627, + 2.6642, + 2.6551, + 2.6639, + 2.6628, + 2.6577, + 2.666, + 2.6771, + 2.6935, + 2.6944, + 2.6948, + 2.692, + 2.6904, + 2.6867, + 2.6784, + 2.6768, + 2.6763, + 2.5959, + 2.5175, + 2.5393, + 2.5901, + 2.5929, + 2.5999, + 2.5942, + 2.5873, + 2.5773, + 2.5667, + 2.5449, + 2.5524, + 2.5635, + 2.589, + 2.5766, + 2.5748, + 2.5867, + 2.5906, + 2.5964, + 2.6268, + 2.6316, + 2.6446, + 2.6436, + 2.6419, + 2.6492, + 2.6554, + 2.6627, + 2.6535, + 2.6562, + 2.6576, + 2.6504, + 2.6487, + 2.6495, + 2.6466, + 2.6392, + 2.6375, + 2.6149, + 2.6079, + 2.6198, + 2.6159, + 2.6134, + 2.5952, + 2.575, + 2.5662, + 2.5624, + 2.5558, + 2.552, + 2.552, + 2.5562, + 2.5525, + 2.5456, + 2.536, + 2.5388, + 2.5432, + 2.5434, + 2.5348, + 2.5318, + 2.5393, + 2.5389, + 2.5418, + 2.5547, + 2.555, + 2.5552, + 2.5416, + 2.5304, + 2.5026, + 2.5011, + 2.5016, + 2.502, + 2.4988, + 2.4953, + 2.4979, + 2.5056, + 2.5049, + 2.5046, + 2.5093, + 2.5073, + 2.5025, + 2.4996, + 2.4962, + 2.4864, + 2.4873, + 2.4767, + 2.47, + 2.4729, + 2.4631, + 2.4643, + 2.4557, + 2.4532, + 2.4425, + 2.4397, + 2.4416, + 2.4474, + 2.4568, + 2.4671, + 2.4675, + 2.4713, + 2.4795, + 2.4819, + 2.4843, + 2.4785, + 2.4801, + 2.4772, + 2.4621, + 2.4665, + 2.4575, + 2.4579, + 2.4562, + 2.4572, + 2.4555, + 2.4537, + 2.4618, + 2.4579, + 2.4515, + 2.4522, + 2.4628, + 2.4598, + 2.4743, + 2.4781, + 2.474, + 2.4867, + 2.4894, + 2.4952, + 2.4873, + 2.4876, + 2.4902, + 2.5, + 2.5003, + 2.5016, + 2.5004, + 2.4993, + 2.5027, + 2.5077, + 2.5004, + 2.5119, + 2.5205, + 2.5234, + 2.5257, + 2.524, + 2.5279, + 2.5316, + 2.5336, + 2.5331, + 2.5281, + 2.5277, + 2.52, + 2.5006, + 2.5053, + 2.5068, + 2.513, + 2.5133, + 2.5107, + 2.5055, + 2.5062, + 2.5109, + 2.5082, + 2.5086, + 2.5069, + 2.5145, + 2.5179, + 2.5132, + 2.5075, + 2.5103, + 2.5098, + 2.5167, + 2.5188, + 2.5218, + 2.522, + 2.5177, + 2.5166, + 2.5118, + 2.5132, + 2.5156, + 2.511, + 2.5103, + 2.5053, + 2.507, + 2.5084, + 2.5144, + 2.5168, + 2.518, + 2.519, + 2.5167, + 2.5156, + 2.506, + 2.5034, + 2.5012, + 2.5033, + 2.5047, + 2.5058, + 2.5055, + 2.5073, + 2.5051, + 2.5051, + 2.5052, + 2.5108, + 2.5122, + 2.5095, + 2.5066, + 2.5093, + 2.5108, + 2.5137, + 2.5126, + 2.5071, + 2.5039, + 2.5029, + 2.5038, + 2.4992, + 2.497, + 2.4955, + 2.4898, + 2.4895, + 2.4911, + 2.4861, + 2.4815, + 2.4869, + 2.4921, + 2.4888, + 2.4888, + 2.4904, + 2.4897, + 2.489, + 2.4877, + 2.4871, + 2.4801, + 2.4741, + 2.4764, + 2.4687, + 2.465, + 2.4664, + 2.467, + 2.4639, + 2.4564, + 2.4653, + 2.476, + 2.4664, + 2.474, + 2.4739, + 2.4648, + 2.4682, + 2.4648, + 2.4659, + 2.4619, + 2.4616, + 2.4597, + 2.4479, + 2.4555, + 2.4452, + 2.4566, + 2.458, + 2.4473, + 2.4626, + 2.4596, + 2.4556, + 2.4586, + 2.4553, + 2.4506, + 2.4514, + 2.4479, + 2.4498, + 2.4573, + 2.4723, + 2.4735, + 2.4728, + 2.4712, + 2.4714, + 2.4725, + 2.4683, + 2.4635, + 2.4697, + 2.4652, + 2.4733, + 2.4771, + 2.4785, + 2.4713, + 2.4677, + 2.4672, + 2.4661, + 2.4556, + 2.4597, + 2.4578, + 2.4549, + 2.4579, + 2.4591, + 2.4608, + 2.463, + 2.4628, + 2.453, + 2.4617, + 2.4625, + 2.4576, + 2.4559, + 2.4495, + 2.451, + 2.4582, + 2.4634, + 2.4625, + 2.4622, + 2.458, + 2.4511, + 2.4526, + 2.4492, + 2.442, + 2.4398, + 2.4372, + 2.4402, + 2.4362, + 2.4305, + 2.4299, + 2.4352, + 2.4296, + 2.4316, + 2.4273, + 2.4251, + 2.4259, + 2.4255, + 2.4205, + 2.425, + 2.4221, + 2.4208, + 2.4131, + 2.4129, + 2.4157, + 2.4095, + 2.408, + 2.3957, + 2.3975, + 2.4051, + 2.4026, + 2.4033, + 2.401, + 2.41, + 2.4132, + 2.4223, + 2.415, + 2.4152, + 2.417, + 2.4153, + 2.4112, + 2.4052, + 2.4033, + 2.4041, + 2.3952, + 2.3944, + 2.39, + 2.3889, + 2.3922, + 2.3926, + 2.3859, + 2.3839, + 2.3888, + 2.3974, + 2.4033, + 2.4017, + 2.4054, + 2.409, + 2.4093, + 2.4144, + 2.4092, + 2.4019, + 2.405, + 2.4032, + 2.4053, + 2.4065, + 2.4007, + 2.4053, + 2.4083, + 2.4006, + 2.4021, + 2.4027, + 2.4033, + 2.4108, + 2.4137, + 2.4148, + 2.4104, + 2.4115, + 2.4197, + 2.4164, + 2.4221, + 2.4257, + 2.4256, + 2.4375, + 2.4323, + 2.4267, + 2.4312, + 2.43, + 2.4318, + 2.4338, + 2.4306, + 2.4349, + 2.4349, + 2.4351, + 2.435, + 2.4365, + 2.4379, + 2.4347 + ], + "low": [ + 2.3728, + 2.3719, + 2.376, + 2.3753, + 2.3775, + 2.3779, + 2.3746, + 2.3679, + 2.3662, + 2.3644, + 2.3616, + 2.3557, + 2.3648, + 2.3582, + 2.3625, + 2.3652, + 2.361, + 2.3592, + 2.3596, + 2.3643, + 2.3624, + 2.362, + 2.3799, + 2.3855, + 2.3873, + 2.3858, + 2.3806, + 2.3805, + 2.3922, + 2.3984, + 2.4023, + 2.4066, + 2.4048, + 2.4061, + 2.4061, + 2.4103, + 2.4204, + 2.4216, + 2.4268, + 2.4278, + 2.42, + 2.4184, + 2.42, + 2.4174, + 2.4147, + 2.4071, + 2.3998, + 2.3987, + 2.4037, + 2.405, + 2.396, + 2.4002, + 2.404, + 2.4016, + 2.3976, + 2.4027, + 2.4061, + 2.4027, + 2.403, + 2.4073, + 2.4084, + 2.4078, + 2.4074, + 2.4065, + 2.4085, + 2.4095, + 2.4117, + 2.4106, + 2.409, + 2.409, + 2.4092, + 2.4138, + 2.4131, + 2.412, + 2.4101, + 2.4102, + 2.4153, + 2.4169, + 2.4177, + 2.4173, + 2.4185, + 2.4222, + 2.415, + 2.4155, + 2.4134, + 2.4093, + 2.4118, + 2.4193, + 2.4273, + 2.4233, + 2.4187, + 2.4173, + 2.4168, + 2.4183, + 2.4172, + 2.4175, + 2.4171, + 2.4178, + 2.4229, + 2.425, + 2.4252, + 2.424, + 2.4202, + 2.4203, + 2.421, + 2.4216, + 2.4258, + 2.4227, + 2.4243, + 2.4309, + 2.4249, + 2.4274, + 2.4197, + 2.4192, + 2.4201, + 2.4163, + 2.4152, + 2.4155, + 2.4113, + 2.4157, + 2.4185, + 2.4239, + 2.4247, + 2.4264, + 2.4278, + 2.4234, + 2.4246, + 2.4292, + 2.4347, + 2.4364, + 2.4359, + 2.4426, + 2.4371, + 2.4272, + 2.4327, + 2.4342, + 2.4373, + 2.4408, + 2.4349, + 2.4355, + 2.4395, + 2.4451, + 2.4476, + 2.448, + 2.4481, + 2.4541, + 2.4572, + 2.4545, + 2.4546, + 2.4589, + 2.4501, + 2.4413, + 2.4467, + 2.44, + 2.4383, + 2.4217, + 2.412, + 2.423, + 2.4236, + 2.4315, + 2.4288, + 2.4381, + 2.4405, + 2.4434, + 2.4376, + 2.4386, + 2.4378, + 2.4361, + 2.4357, + 2.4306, + 2.4305, + 2.4357, + 2.4492, + 2.4474, + 2.4537, + 2.4563, + 2.4549, + 2.4544, + 2.4566, + 2.46, + 2.4531, + 2.4403, + 2.4428, + 2.4402, + 2.4505, + 2.4557, + 2.4615, + 2.465, + 2.478, + 2.4884, + 2.493, + 2.4915, + 2.4898, + 2.4904, + 2.5052, + 2.518, + 2.5208, + 2.5125, + 2.5059, + 2.5056, + 2.508, + 2.5031, + 2.4956, + 2.5074, + 2.4925, + 2.4984, + 2.4752, + 2.4849, + 2.4742, + 2.481, + 2.473, + 2.4727, + 2.4801, + 2.4698, + 2.4697, + 2.4698, + 2.4648, + 2.464, + 2.466, + 2.4669, + 2.458, + 2.4476, + 2.4302, + 2.4315, + 2.4303, + 2.4266, + 2.4288, + 2.4194, + 2.4196, + 2.4216, + 2.419, + 2.4151, + 2.4155, + 2.4175, + 2.42, + 2.4162, + 2.4193, + 2.4194, + 2.4253, + 2.4229, + 2.4202, + 2.4209, + 2.4264, + 2.4324, + 2.4305, + 2.436, + 2.4406, + 2.4543, + 2.4604, + 2.4616, + 2.4538, + 2.4522, + 2.4495, + 2.4477, + 2.4369, + 2.4348, + 2.4381, + 2.4342, + 2.4346, + 2.4432, + 2.442, + 2.4393, + 2.4396, + 2.4367, + 2.4314, + 2.4313, + 2.4402, + 2.4354, + 2.4355, + 2.4405, + 2.4406, + 2.449, + 2.4534, + 2.4488, + 2.4462, + 2.4488, + 2.4458, + 2.4427, + 2.4409, + 2.4463, + 2.4426, + 2.441, + 2.4437, + 2.4534, + 2.4558, + 2.4571, + 2.4586, + 2.463, + 2.4615, + 2.4602, + 2.4608, + 2.4604, + 2.4602, + 2.46, + 2.4604, + 2.4651, + 2.4651, + 2.4654, + 2.4615, + 2.4564, + 2.4556, + 2.4568, + 2.4517, + 2.4513, + 2.452, + 2.4472, + 2.4473, + 2.4485, + 2.4528, + 2.4522, + 2.4496, + 2.4405, + 2.4348, + 2.4261, + 2.4353, + 2.4314, + 2.4391, + 2.4454, + 2.4418, + 2.436, + 2.4269, + 2.4209, + 2.4173, + 2.4204, + 2.4268, + 2.4257, + 2.4221, + 2.402, + 2.3895, + 2.411, + 2.4083, + 2.405, + 2.3972, + 2.3866, + 2.3661, + 2.377, + 2.3952, + 2.3906, + 2.3945, + 2.3954, + 2.398, + 2.4038, + 2.4089, + 2.4096, + 2.409, + 2.409, + 2.4105, + 2.4115, + 2.4054, + 2.4011, + 2.39, + 2.3909, + 2.392, + 2.3912, + 2.3873, + 2.3875, + 2.3923, + 2.3931, + 2.3914, + 2.3976, + 2.4034, + 2.4012, + 2.403, + 2.4078, + 2.4116, + 2.4115, + 2.4132, + 2.4115, + 2.4117, + 2.4103, + 2.4119, + 2.413, + 2.4139, + 2.4083, + 2.4079, + 2.404, + 2.4076, + 2.4085, + 2.4087, + 2.4041, + 2.404, + 2.4051, + 2.4036, + 2.4043, + 2.4042, + 2.4008, + 2.4025, + 2.4072, + 2.4021, + 2.4017, + 2.402, + 2.4005, + 2.4006, + 2.4035, + 2.4014, + 2.4042, + 2.3961, + 2.396, + 2.3916, + 2.3799, + 2.3731, + 2.3721, + 2.3832, + 2.384, + 2.3878, + 2.3995, + 2.3969, + 2.4001, + 2.3965, + 2.3945, + 2.3895, + 2.3859, + 2.3848, + 2.3831, + 2.3821, + 2.385, + 2.3865, + 2.389, + 2.3931, + 2.3955, + 2.3908, + 2.3954, + 2.3992, + 2.4037, + 2.4058, + 2.4015, + 2.3998, + 2.3986, + 2.3991, + 2.4007, + 2.3999, + 2.4022, + 2.4084, + 2.4148, + 2.4072, + 2.414, + 2.4203, + 2.4199, + 2.4206, + 2.4207, + 2.4187, + 2.4197, + 2.4199, + 2.4168, + 2.4137, + 2.4127, + 2.4134, + 2.4072, + 2.4119, + 2.4009, + 2.396, + 2.407, + 2.4064, + 2.4101, + 2.4122, + 2.4149, + 2.4133, + 2.4077, + 2.4066, + 2.4017, + 2.3996, + 2.415, + 2.4146, + 2.4238, + 2.4242, + 2.4294, + 2.4366, + 2.4384, + 2.4441, + 2.4434, + 2.43, + 2.4256, + 2.4355, + 2.4338, + 2.4338, + 2.4422, + 2.4456, + 2.4409, + 2.4473, + 2.4453, + 2.4325, + 2.438, + 2.4441, + 2.444, + 2.4408, + 2.4379, + 2.4342, + 2.4388, + 2.4415, + 2.4401, + 2.4497, + 2.4529, + 2.4549, + 2.4558, + 2.456, + 2.4431, + 2.4408, + 2.4475, + 2.4517, + 2.4536, + 2.4433, + 2.4317, + 2.4355, + 2.4411, + 2.4395, + 2.4365, + 2.427, + 2.4262, + 2.432, + 2.4314, + 2.4313, + 2.4262, + 2.4187, + 2.4247, + 2.4318, + 2.4431, + 2.4408, + 2.4372, + 2.4365, + 2.4331, + 2.4375, + 2.4385, + 2.4402, + 2.4442, + 2.4472, + 2.4457, + 2.4354, + 2.4411, + 2.4393, + 2.442, + 2.4397, + 2.4355, + 2.4345, + 2.4316, + 2.4162, + 2.4267, + 2.4355, + 2.4408, + 2.4487, + 2.4603, + 2.4675, + 2.4808, + 2.476, + 2.4817, + 2.4746, + 2.4742, + 2.4709, + 2.4707, + 2.4651, + 2.4657, + 2.4652, + 2.4721, + 2.4724, + 2.4762, + 2.4776, + 2.4691, + 2.4651, + 2.4737, + 2.4652, + 2.4627, + 2.4642, + 2.4733, + 2.4772, + 2.4816, + 2.4779, + 2.4749, + 2.4777, + 2.482, + 2.4757, + 2.4739, + 2.4764, + 2.4786, + 2.475, + 2.4745, + 2.4798, + 2.4825, + 2.4828, + 2.4892, + 2.4822, + 2.4914, + 2.4954, + 2.4963, + 2.4871, + 2.4871, + 2.4924, + 2.4916, + 2.4818, + 2.4738, + 2.4744, + 2.4711, + 2.4809, + 2.4851, + 2.4871, + 2.4868, + 2.4916, + 2.4982, + 2.4924, + 2.484, + 2.4787, + 2.4837, + 2.4769, + 2.4768, + 2.4741, + 2.4666, + 2.459, + 2.4605, + 2.464, + 2.4629, + 2.4573, + 2.4549, + 2.4604, + 2.4623, + 2.46, + 2.4553, + 2.4554, + 2.4554, + 2.4581, + 2.4555, + 2.4527, + 2.4519, + 2.4426, + 2.4404, + 2.4402, + 2.4415, + 2.4431, + 2.446, + 2.444, + 2.4411, + 2.44, + 2.4362, + 2.434, + 2.4303, + 2.4272, + 2.4293, + 2.4323, + 2.4323, + 2.4331, + 2.4338, + 2.435, + 2.4352, + 2.4382, + 2.4455, + 2.4525, + 2.4556, + 2.454, + 2.4546, + 2.4588, + 2.4546, + 2.4545, + 2.4581, + 2.452, + 2.4527, + 2.4481, + 2.4511, + 2.4534, + 2.4529, + 2.4558, + 2.4561, + 2.4534, + 2.4504, + 2.4521, + 2.4591, + 2.4641, + 2.466, + 2.466, + 2.4683, + 2.4647, + 2.4662, + 2.4746, + 2.4773, + 2.4711, + 2.4567, + 2.4588, + 2.4692, + 2.4706, + 2.4651, + 2.4651, + 2.477, + 2.478, + 2.4767, + 2.4736, + 2.4796, + 2.4829, + 2.4792, + 2.4807, + 2.4722, + 2.4768, + 2.4738, + 2.4703, + 2.4644, + 2.465, + 2.47, + 2.467, + 2.4609, + 2.4586, + 2.4631, + 2.4724, + 2.4766, + 2.4814, + 2.4804, + 2.4817, + 2.484, + 2.4885, + 2.4842, + 2.4719, + 2.4702, + 2.4697, + 2.463, + 2.4612, + 2.4596, + 2.4592, + 2.4413, + 2.4467, + 2.4533, + 2.4566, + 2.4617, + 2.4616, + 2.4605, + 2.4581, + 2.456, + 2.4512, + 2.4544, + 2.4581, + 2.4556, + 2.452, + 2.4556, + 2.4501, + 2.452, + 2.4483, + 2.4462, + 2.4429, + 2.434, + 2.4312, + 2.435, + 2.435, + 2.4357, + 2.4411, + 2.4463, + 2.4488, + 2.4506, + 2.4531, + 2.4607, + 2.4616, + 2.4627, + 2.4621, + 2.4685, + 2.4773, + 2.4864, + 2.4931, + 2.4789, + 2.4713, + 2.4763, + 2.5055, + 2.5048, + 2.5122, + 2.5036, + 2.5116, + 2.51, + 2.5079, + 2.5079, + 2.5146, + 2.514, + 2.5178, + 2.5172, + 2.5108, + 2.5124, + 2.502, + 2.464, + 2.4722, + 2.4816, + 2.464, + 2.4631, + 2.41, + 2.4364, + 2.4347, + 2.4382, + 2.453, + 2.4452, + 2.4427, + 2.4436, + 2.4363, + 2.4193, + 2.4034, + 2.4156, + 2.42, + 2.4195, + 2.4107, + 2.4023, + 2.4036, + 2.4088, + 2.412, + 2.4195, + 2.4243, + 2.438, + 2.4349, + 2.4366, + 2.4353, + 2.4303, + 2.4396, + 2.4432, + 2.4461, + 2.4499, + 2.4593, + 2.4583, + 2.4472, + 2.4365, + 2.4255, + 2.4377, + 2.4384, + 2.4483, + 2.4538, + 2.4538, + 2.4544, + 2.4621, + 2.4625, + 2.4688, + 2.4708, + 2.477, + 2.4731, + 2.4705, + 2.4723, + 2.4795, + 2.4768, + 2.4842, + 2.4839, + 2.4854, + 2.499, + 2.4918, + 2.4986, + 2.5031, + 2.5198, + 2.5276, + 2.5333, + 2.5328, + 2.5328, + 2.514, + 2.508, + 2.52, + 2.5233, + 2.5268, + 2.523, + 2.5182, + 2.5078, + 2.506, + 2.5075, + 2.5091, + 2.5104, + 2.4999, + 2.4957, + 2.49, + 2.4878, + 2.4935, + 2.4912, + 2.4843, + 2.4846, + 2.48, + 2.4816, + 2.4832, + 2.483, + 2.4776, + 2.4803, + 2.4791, + 2.4798, + 2.4841, + 2.4877, + 2.4809, + 2.4806, + 2.4863, + 2.501, + 2.5047, + 2.4996, + 2.5015, + 2.508, + 2.5152, + 2.5055, + 2.5106, + 2.5041, + 2.505, + 2.5156, + 2.5035, + 2.4935, + 2.4952, + 2.4613, + 2.45, + 2.45, + 2.4409, + 2.425, + 2.4458, + 2.4395, + 2.4335, + 2.4131, + 2.4, + 2.4093, + 2.4222, + 2.4245, + 2.4221, + 2.4219, + 2.4239, + 2.4253, + 2.4298, + 2.435, + 2.4457, + 2.4554, + 2.442, + 2.4376, + 2.4373, + 2.4398, + 2.431, + 2.4303, + 2.4296, + 2.4286, + 2.4283, + 2.4285, + 2.4356, + 2.4412, + 2.4503, + 2.4543, + 2.4586, + 2.4586, + 2.4553, + 2.4508, + 2.4578, + 2.4632, + 2.4715, + 2.465, + 2.4599, + 2.4558, + 2.4584, + 2.4623, + 2.4667, + 2.468, + 2.4708, + 2.4567, + 2.444, + 2.4371, + 2.4375, + 2.4282, + 2.4348, + 2.4335, + 2.4365, + 2.4364, + 2.4676, + 2.4723, + 2.4812, + 2.4828, + 2.4559, + 2.4556, + 2.4473, + 2.4462, + 2.4497, + 2.4494, + 2.4433, + 2.44, + 2.4348, + 2.4358, + 2.4359, + 2.4296, + 2.4229, + 2.42, + 2.4238, + 2.4232, + 2.4326, + 2.4355, + 2.4384, + 2.4453, + 2.444, + 2.444, + 2.4526, + 2.4418, + 2.4436, + 2.4534, + 2.4615, + 2.4549, + 2.4628, + 2.4625, + 2.4615, + 2.4622, + 2.4658, + 2.4632, + 2.4645, + 2.4648, + 2.4734, + 2.4754, + 2.4827, + 2.4798, + 2.4747, + 2.4789, + 2.48, + 2.4849, + 2.4871, + 2.4781, + 2.4827, + 2.4907, + 2.4908, + 2.4961, + 2.49, + 2.4849, + 2.475, + 2.4721, + 2.485, + 2.4835, + 2.4827, + 2.4864, + 2.4721, + 2.4789, + 2.4787, + 2.4756, + 2.4861, + 2.484, + 2.4858, + 2.4845, + 2.4842, + 2.4816, + 2.4893, + 2.4886, + 2.4854, + 2.4864, + 2.4834, + 2.4756, + 2.4748, + 2.4783, + 2.469, + 2.4765, + 2.4772, + 2.4823, + 2.4767, + 2.483, + 2.4848, + 2.4837, + 2.4814, + 2.4792, + 2.4775, + 2.483, + 2.4782, + 2.4779, + 2.4767, + 2.4762, + 2.4785, + 2.4846, + 2.4922, + 2.4948, + 2.4929, + 2.4919, + 2.4896, + 2.4902, + 2.4844, + 2.488, + 2.4941, + 2.4934, + 2.4962, + 2.4988, + 2.4964, + 2.4969, + 2.4879, + 2.4895, + 2.494, + 2.4943, + 2.4947, + 2.4917, + 2.4958, + 2.4968, + 2.4953, + 2.4956, + 2.4945, + 2.4985, + 2.4903, + 2.4859, + 2.4808, + 2.4797, + 2.4746, + 2.4761, + 2.478, + 2.4835, + 2.4776, + 2.4744, + 2.4726, + 2.4625, + 2.4585, + 2.4618, + 2.4572, + 2.4588, + 2.4654, + 2.4706, + 2.4685, + 2.4701, + 2.4716, + 2.4692, + 2.4671, + 2.4662, + 2.4651, + 2.4523, + 2.4425, + 2.4354, + 2.4446, + 2.4476, + 2.4485, + 2.4465, + 2.4469, + 2.4488, + 2.4454, + 2.4441, + 2.4456, + 2.4489, + 2.449, + 2.4532, + 2.4535, + 2.4556, + 2.4557, + 2.4582, + 2.4556, + 2.4567, + 2.46, + 2.4553, + 2.442, + 2.4355, + 2.4214, + 2.4167, + 2.41, + 2.4093, + 2.4205, + 2.4223, + 2.4144, + 2.4157, + 2.4201, + 2.4154, + 2.4242, + 2.4267, + 2.4295, + 2.4271, + 2.4273, + 2.4302, + 2.4292, + 2.4282, + 2.4234, + 2.4254, + 2.4324, + 2.4305, + 2.4293, + 2.42, + 2.4261, + 2.4305, + 2.4253, + 2.4344, + 2.4309, + 2.4319, + 2.4323, + 2.4416, + 2.4409, + 2.4461, + 2.4563, + 2.4565, + 2.4567, + 2.4662, + 2.4707, + 2.4771, + 2.4764, + 2.4821, + 2.4834, + 2.4721, + 2.47, + 2.4694, + 2.4778, + 2.4719, + 2.4703, + 2.4695, + 2.4675, + 2.4631, + 2.4628, + 2.4619, + 2.46, + 2.4585, + 2.4532, + 2.4452, + 2.4476, + 2.4466, + 2.4418, + 2.4407, + 2.4433, + 2.4499, + 2.4524, + 2.4526, + 2.457, + 2.4554, + 2.4558, + 2.4538, + 2.4534, + 2.4507, + 2.4521, + 2.4537, + 2.4538, + 2.4553, + 2.459, + 2.4595, + 2.4505, + 2.442, + 2.4385, + 2.4395, + 2.4402, + 2.4372, + 2.4365, + 2.437, + 2.435, + 2.427, + 2.4219, + 2.4263, + 2.4319, + 2.4301, + 2.436, + 2.4293, + 2.4275, + 2.4249, + 2.4266, + 2.426, + 2.4233, + 2.4234, + 2.4266, + 2.43, + 2.43, + 2.4315, + 2.4337, + 2.4347, + 2.4348, + 2.439, + 2.4408, + 2.4403, + 2.44, + 2.4349, + 2.4353, + 2.4361, + 2.4368, + 2.443, + 2.4446, + 2.4506, + 2.4549, + 2.4551, + 2.4558, + 2.455, + 2.4526, + 2.4493, + 2.449, + 2.4483, + 2.4488, + 2.4518, + 2.4501, + 2.4522, + 2.4509, + 2.4525, + 2.4525, + 2.4549, + 2.456, + 2.4567, + 2.4562, + 2.4567, + 2.4543, + 2.4488, + 2.4451, + 2.4434, + 2.4438, + 2.4472, + 2.4485, + 2.4482, + 2.446, + 2.4454, + 2.4415, + 2.4411, + 2.4421, + 2.4424, + 2.4391, + 2.4386, + 2.4394, + 2.4441, + 2.4447, + 2.4453, + 2.4504, + 2.4514, + 2.4387, + 2.4369, + 2.4397, + 2.4445, + 2.4453, + 2.4458, + 2.4481, + 2.4475, + 2.4479, + 2.4496, + 2.4481, + 2.4436, + 2.4443, + 2.4443, + 2.4472, + 2.4446, + 2.44, + 2.4381, + 2.4432, + 2.4433, + 2.443, + 2.4395, + 2.4391, + 2.4408, + 2.4422, + 2.44, + 2.4401, + 2.4476, + 2.4404, + 2.4307, + 2.4266, + 2.4279, + 2.417, + 2.4147, + 2.4141, + 2.413, + 2.4203, + 2.4255, + 2.4222, + 2.4203, + 2.4204, + 2.4142, + 2.412, + 2.4029, + 2.4, + 2.4, + 2.4, + 2.3814, + 2.3944, + 2.385, + 2.366, + 2.3727, + 2.3669, + 2.3381, + 2.33, + 2.3452, + 2.3604, + 2.3611, + 2.3623, + 2.3729, + 2.3734, + 2.3818, + 2.3804, + 2.38, + 2.3762, + 2.3751, + 2.3644, + 2.36, + 2.3683, + 2.3687, + 2.3683, + 2.3624, + 2.3623, + 2.3667, + 2.3713, + 2.368, + 2.3746, + 2.3745, + 2.3725, + 2.3715, + 2.3699, + 2.3685, + 2.366, + 2.3612, + 2.363, + 2.3646, + 2.368, + 2.3669, + 2.3672, + 2.3696, + 2.3729, + 2.378, + 2.3807, + 2.3828, + 2.3776, + 2.3724, + 2.3753, + 2.3762, + 2.3759, + 2.3739, + 2.3712, + 2.3649, + 2.3702, + 2.3681, + 2.3691, + 2.3622, + 2.358, + 2.3615, + 2.3622, + 2.3705, + 2.3769, + 2.3747, + 2.3745, + 2.3771, + 2.3818, + 2.3892, + 2.3974, + 2.3979, + 2.4001, + 2.3974, + 2.3954, + 2.3921, + 2.3946, + 2.3921, + 2.3976, + 2.3999, + 2.3969, + 2.395, + 2.3938, + 2.39, + 2.3857, + 2.3932, + 2.392, + 2.3929, + 2.3954, + 2.3955, + 2.3932, + 2.3941, + 2.3978, + 2.3905, + 2.3906, + 2.386, + 2.387, + 2.3905, + 2.3944, + 2.3882, + 2.3877, + 2.3897, + 2.3901, + 2.3884, + 2.3873, + 2.3822, + 2.3863, + 2.388, + 2.3982, + 2.3987, + 2.3999, + 2.4025, + 2.3953, + 2.3916, + 2.39, + 2.3931, + 2.3926, + 2.3977, + 2.3988, + 2.3983, + 2.4092, + 2.4072, + 2.4124, + 2.4117, + 2.4149, + 2.4194, + 2.4219, + 2.4273, + 2.4309, + 2.429, + 2.4251, + 2.4293, + 2.4318, + 2.4316, + 2.4211, + 2.4245, + 2.4279, + 2.4258, + 2.4282, + 2.4165, + 2.4268, + 2.4295, + 2.4306, + 2.4372, + 2.4351, + 2.4392, + 2.4441, + 2.4697, + 2.4838, + 2.4842, + 2.4886, + 2.4913, + 2.4951, + 2.479, + 2.4709, + 2.4788, + 2.4736, + 2.4778, + 2.4816, + 2.4837, + 2.4767, + 2.492, + 2.4983, + 2.4959, + 2.4984, + 2.499, + 2.5035, + 2.5015, + 2.506, + 2.5014, + 2.5026, + 2.5016, + 2.4969, + 2.491, + 2.4882, + 2.4813, + 2.4884, + 2.4972, + 2.5019, + 2.5044, + 2.5018, + 2.5067, + 2.5079, + 2.5012, + 2.5011, + 2.4962, + 2.4965, + 2.4996, + 2.4989, + 2.4965, + 2.4886, + 2.4804, + 2.4767, + 2.4746, + 2.4752, + 2.4806, + 2.4827, + 2.4826, + 2.481, + 2.4755, + 2.4746, + 2.4798, + 2.4638, + 2.4623, + 2.4644, + 2.4632, + 2.46, + 2.4572, + 2.4534, + 2.4551, + 2.4569, + 2.4582, + 2.4515, + 2.4409, + 2.4441, + 2.449, + 2.4539, + 2.4519, + 2.455, + 2.4527, + 2.4555, + 2.4645, + 2.4677, + 2.4679, + 2.4675, + 2.4618, + 2.46, + 2.4599, + 2.4623, + 2.4623, + 2.4609, + 2.4541, + 2.4501, + 2.4575, + 2.454, + 2.4636, + 2.4636, + 2.4677, + 2.4689, + 2.4702, + 2.4711, + 2.4714, + 2.4687, + 2.4703, + 2.4712, + 2.4765, + 2.4804, + 2.4818, + 2.4842, + 2.4884, + 2.4933, + 2.4885, + 2.4861, + 2.4889, + 2.4942, + 2.4926, + 2.4936, + 2.4968, + 2.4971, + 2.4983, + 2.49, + 2.4904, + 2.4958, + 2.5033, + 2.4984, + 2.4914, + 2.4916, + 2.4945, + 2.4911, + 2.4921, + 2.4824, + 2.4825, + 2.4876, + 2.4877, + 2.4874, + 2.4851, + 2.4811, + 2.4813, + 2.4838, + 2.4836, + 2.4921, + 2.4984, + 2.5013, + 2.4965, + 2.5013, + 2.5054, + 2.5022, + 2.5015, + 2.5001, + 2.5006, + 2.5095, + 2.506, + 2.5065, + 2.507, + 2.5076, + 2.503, + 2.5, + 2.4988, + 2.5012, + 2.503, + 2.5036, + 2.4984, + 2.4974, + 2.4974, + 2.4971, + 2.4974, + 2.5056, + 2.5047, + 2.5107, + 2.5082, + 2.5065, + 2.503, + 2.5001, + 2.4997, + 2.4904, + 2.4959, + 2.4959, + 2.4933, + 2.49, + 2.4864, + 2.4775, + 2.4761, + 2.4752, + 2.4762, + 2.4769, + 2.4839, + 2.4885, + 2.4886, + 2.4871, + 2.4866, + 2.4909, + 2.4963, + 2.4995, + 2.494, + 2.4925, + 2.489, + 2.4891, + 2.4843, + 2.4847, + 2.4858, + 2.4871, + 2.485, + 2.4903, + 2.4899, + 2.4914, + 2.4915, + 2.4928, + 2.4945, + 2.497, + 2.4932, + 2.4933, + 2.4916, + 2.4915, + 2.4904, + 2.4897, + 2.4852, + 2.4886, + 2.5013, + 2.5057, + 2.5128, + 2.5136, + 2.511, + 2.5111, + 2.5034, + 2.5039, + 2.5184, + 2.5117, + 2.5173, + 2.5141, + 2.5141, + 2.5209, + 2.5197, + 2.5212, + 2.5238, + 2.5267, + 2.5236, + 2.5201, + 2.52, + 2.5216, + 2.5217, + 2.5277, + 2.5267, + 2.526, + 2.5276, + 2.5275, + 2.5222, + 2.5277, + 2.5242, + 2.5242, + 2.5277, + 2.5257, + 2.5234, + 2.5181, + 2.5185, + 2.5224, + 2.5181, + 2.5153, + 2.51, + 2.5156, + 2.5131, + 2.5096, + 2.5069, + 2.5112, + 2.5131, + 2.5168, + 2.5187, + 2.5187, + 2.5179, + 2.5202, + 2.5114, + 2.5134, + 2.5118, + 2.5117, + 2.5117, + 2.5222, + 2.5262, + 2.5285, + 2.5336, + 2.5343, + 2.5325, + 2.5286, + 2.5284, + 2.5375, + 2.5365, + 2.5371, + 2.5441, + 2.54, + 2.5441, + 2.5422, + 2.5374, + 2.5357, + 2.5302, + 2.5331, + 2.5269, + 2.5242, + 2.5215, + 2.5249, + 2.524, + 2.5245, + 2.523, + 2.5162, + 2.5134, + 2.5189, + 2.5116, + 2.5131, + 2.512, + 2.5013, + 2.504, + 2.493, + 2.489, + 2.4967, + 2.5013, + 2.5025, + 2.4994, + 2.5025, + 2.5001, + 2.4954, + 2.4954, + 2.4869, + 2.483, + 2.4831, + 2.4808, + 2.4724, + 2.4807, + 2.4803, + 2.4813, + 2.4835, + 2.4863, + 2.4887, + 2.4888, + 2.489, + 2.4888, + 2.4887, + 2.4864, + 2.4836, + 2.4796, + 2.4796, + 2.4779, + 2.4621, + 2.465, + 2.4717, + 2.4728, + 2.4684, + 2.4701, + 2.4727, + 2.4783, + 2.4819, + 2.4857, + 2.4852, + 2.483, + 2.485, + 2.4825, + 2.4776, + 2.4786, + 2.4785, + 2.4791, + 2.4733, + 2.4725, + 2.4742, + 2.4725, + 2.4723, + 2.4746, + 2.478, + 2.4837, + 2.4798, + 2.4795, + 2.4799, + 2.48, + 2.4819, + 2.4813, + 2.4818, + 2.479, + 2.4812, + 2.4817, + 2.49, + 2.4896, + 2.4905, + 2.4941, + 2.4958, + 2.499, + 2.4992, + 2.4999, + 2.505, + 2.5082, + 2.5077, + 2.5079, + 2.5024, + 2.5021, + 2.5065, + 2.5103, + 2.5118, + 2.5149, + 2.5168, + 2.5164, + 2.5152, + 2.5168, + 2.5133, + 2.5108, + 2.5059, + 2.5042, + 2.5028, + 2.4992, + 2.5002, + 2.5015, + 2.5058, + 2.5113, + 2.5136, + 2.5096, + 2.512, + 2.5146, + 2.5145, + 2.5134, + 2.514, + 2.5172, + 2.5203, + 2.5172, + 2.5185, + 2.515, + 2.5118, + 2.5069, + 2.4847, + 2.465, + 2.4629, + 2.4661, + 2.4689, + 2.4743, + 2.4648, + 2.4639, + 2.4662, + 2.4515, + 2.4626, + 2.4623, + 2.4625, + 2.4635, + 2.4642, + 2.4693, + 2.4545, + 2.4425, + 2.4371, + 2.4387, + 2.4416, + 2.43, + 2.4333, + 2.43, + 2.4313, + 2.4385, + 2.4363, + 2.432, + 2.4332, + 2.4375, + 2.4472, + 2.444, + 2.4521, + 2.451, + 2.4511, + 2.453, + 2.4436, + 2.4442, + 2.4502, + 2.4494, + 2.4548, + 2.4547, + 2.4533, + 2.4524, + 2.4488, + 2.4528, + 2.4542, + 2.45, + 2.4463, + 2.445, + 2.4497, + 2.444, + 2.448, + 2.4483, + 2.4512, + 2.45, + 2.4396, + 2.4397, + 2.4439, + 2.4434, + 2.4515, + 2.4546, + 2.4549, + 2.4587, + 2.4598, + 2.463, + 2.4723, + 2.4674, + 2.4642, + 2.4624, + 2.4672, + 2.4686, + 2.4707, + 2.469, + 2.464, + 2.4649, + 2.4641, + 2.4637, + 2.4607, + 2.4659, + 2.465, + 2.4513, + 2.448, + 2.4486, + 2.4465, + 2.4517, + 2.4489, + 2.441, + 2.4463, + 2.4449, + 2.4487, + 2.4578, + 2.4526, + 2.4552, + 2.4548, + 2.4561, + 2.4564, + 2.4579, + 2.4595, + 2.4592, + 2.457, + 2.4588, + 2.457, + 2.4601, + 2.4587, + 2.4592, + 2.456, + 2.4562, + 2.456, + 2.4542, + 2.4533, + 2.4532, + 2.4511, + 2.4504, + 2.4517, + 2.4577, + 2.4599, + 2.4565, + 2.4572, + 2.4567, + 2.4588, + 2.4602, + 2.4631, + 2.4625, + 2.4626, + 2.4629, + 2.4624, + 2.4588, + 2.4573, + 2.4572, + 2.4647, + 2.4637, + 2.4633, + 2.4664, + 2.4681, + 2.468, + 2.4683, + 2.4696, + 2.4676, + 2.4691, + 2.4723, + 2.4722, + 2.4722, + 2.4712, + 2.4726, + 2.4729, + 2.4727, + 2.4718, + 2.4726, + 2.4732, + 2.474, + 2.4653, + 2.4694, + 2.4705, + 2.4725, + 2.4742, + 2.4769, + 2.4797, + 2.4784, + 2.479, + 2.4746, + 2.4746, + 2.4715, + 2.4745, + 2.475, + 2.477, + 2.4735, + 2.4779, + 2.4783, + 2.484, + 2.4839, + 2.4843, + 2.4782, + 2.4713, + 2.4729, + 2.4674, + 2.4716, + 2.4779, + 2.4846, + 2.4794, + 2.4805, + 2.4784, + 2.4764, + 2.4766, + 2.4784, + 2.4756, + 2.474, + 2.4731, + 2.4715, + 2.4717, + 2.4719, + 2.4706, + 2.4683, + 2.4674, + 2.4689, + 2.4784, + 2.4779, + 2.4715, + 2.4703, + 2.473, + 2.4757, + 2.4799, + 2.479, + 2.482, + 2.4811, + 2.4818, + 2.4819, + 2.4825, + 2.4863, + 2.487, + 2.4855, + 2.488, + 2.4844, + 2.4867, + 2.4817, + 2.4838, + 2.487, + 2.487, + 2.488, + 2.4895, + 2.4916, + 2.4949, + 2.4969, + 2.4974, + 2.4994, + 2.499, + 2.5013, + 2.503, + 2.5044, + 2.5076, + 2.5107, + 2.5106, + 2.5143, + 2.516, + 2.5156, + 2.5185, + 2.5147, + 2.5138, + 2.5157, + 2.5059, + 2.5, + 2.4818, + 2.4756, + 2.4882, + 2.491, + 2.5028, + 2.5032, + 2.4994, + 2.5004, + 2.4962, + 2.5006, + 2.5104, + 2.5068, + 2.5083, + 2.5054, + 2.5052, + 2.4987, + 2.497, + 2.5015, + 2.4971, + 2.4952, + 2.5042, + 2.5076, + 2.5, + 2.4967, + 2.4977, + 2.4979, + 2.5015, + 2.5021, + 2.5041, + 2.5086, + 2.5097, + 2.5057, + 2.5038, + 2.5016, + 2.5062, + 2.5042, + 2.5046, + 2.5042, + 2.5012, + 2.5015, + 2.5049, + 2.5065, + 2.5094, + 2.5104, + 2.5032, + 2.5016, + 2.506, + 2.5078, + 2.509, + 2.5075, + 2.5052, + 2.504, + 2.505, + 2.5005, + 2.501, + 2.5025, + 2.5006, + 2.4962, + 2.4989, + 2.5002, + 2.5033, + 2.4986, + 2.4994, + 2.4952, + 2.4961, + 2.4934, + 2.4931, + 2.5007, + 2.503, + 2.5045, + 2.5036, + 2.5045, + 2.4977, + 2.4998, + 2.5063, + 2.5111, + 2.5181, + 2.5253, + 2.52, + 2.5206, + 2.52, + 2.5133, + 2.5177, + 2.5245, + 2.5207, + 2.5178, + 2.5171, + 2.5175, + 2.5182, + 2.5237, + 2.5221, + 2.5218, + 2.5237, + 2.5259, + 2.522, + 2.526, + 2.5221, + 2.522, + 2.5259, + 2.5225, + 2.5213, + 2.5188, + 2.513, + 2.5154, + 2.5116, + 2.5129, + 2.5174, + 2.5196, + 2.5188, + 2.5126, + 2.5125, + 2.5064, + 2.507, + 2.5069, + 2.5078, + 2.5081, + 2.507, + 2.5065, + 2.5045, + 2.5033, + 2.5052, + 2.5108, + 2.5128, + 2.5099, + 2.503, + 2.5, + 2.5002, + 2.5025, + 2.5046, + 2.5095, + 2.5054, + 2.5055, + 2.5098, + 2.5106, + 2.5189, + 2.52, + 2.5201, + 2.518, + 2.5171, + 2.5178, + 2.5155, + 2.5173, + 2.5145, + 2.5204, + 2.5258, + 2.5371, + 2.535, + 2.5322, + 2.533, + 2.5048, + 2.45, + 2.46, + 2.44, + 2.4603, + 2.4784, + 2.4829, + 2.475, + 2.4707, + 2.4753, + 2.4807, + 2.4733, + 2.4817, + 2.4822, + 2.48, + 2.488, + 2.4889, + 2.4848, + 2.4852, + 2.4857, + 2.4802, + 2.477, + 2.4711, + 2.4601, + 2.4603, + 2.4641, + 2.4547, + 2.4549, + 2.4629, + 2.4653, + 2.4689, + 2.4677, + 2.468, + 2.4689, + 2.4682, + 2.47, + 2.4671, + 2.4666, + 2.4623, + 2.4638, + 2.4726, + 2.4761, + 2.4737, + 2.4756, + 2.4783, + 2.478, + 2.4821, + 2.4818, + 2.4778, + 2.4773, + 2.4779, + 2.4793, + 2.4807, + 2.4798, + 2.475, + 2.4775, + 2.4719, + 2.4683, + 2.47, + 2.4706, + 2.4747, + 2.4751, + 2.476, + 2.47, + 2.4682, + 2.4703, + 2.4739, + 2.4772, + 2.4794, + 2.474, + 2.4721, + 2.4795, + 2.4837, + 2.4882, + 2.484, + 2.48, + 2.4802, + 2.4768, + 2.4777, + 2.4771, + 2.4783, + 2.4803, + 2.4895, + 2.4959, + 2.4958, + 2.5066, + 2.5077, + 2.5062, + 2.497, + 2.498, + 2.4995, + 2.5099, + 2.5126, + 2.5139, + 2.5128, + 2.5078, + 2.507, + 2.508, + 2.5114, + 2.507, + 2.512, + 2.507, + 2.4966, + 2.4984, + 2.4988, + 2.504, + 2.5041, + 2.5056, + 2.5089, + 2.5065, + 2.5059, + 2.5156, + 2.5179, + 2.5192, + 2.5212, + 2.523, + 2.5266, + 2.5274, + 2.5347, + 2.5313, + 2.5289, + 2.5176, + 2.51, + 2.4852, + 2.4995, + 2.488, + 2.4937, + 2.495, + 2.5062, + 2.507, + 2.5085, + 2.4958, + 2.4947, + 2.49, + 2.49, + 2.4985, + 2.4923, + 2.4919, + 2.49, + 2.4907, + 2.4979, + 2.4962, + 2.4978, + 2.5032, + 2.5067, + 2.5136, + 2.5259, + 2.5266, + 2.5293, + 2.5222, + 2.5238, + 2.5283, + 2.5356, + 2.5352, + 2.5332, + 2.5326, + 2.5296, + 2.5253, + 2.5082, + 2.5174, + 2.5367, + 2.5672, + 2.5615, + 2.5548, + 2.5474, + 2.5383, + 2.5323, + 2.5288, + 2.5261, + 2.5274, + 2.5159, + 2.5046, + 2.494, + 2.4865, + 2.493, + 2.5074, + 2.499, + 2.495, + 2.4942, + 2.4936, + 2.4975, + 2.5049, + 2.5046, + 2.5041, + 2.5036, + 2.502, + 2.4985, + 2.5028, + 2.4925, + 2.4892, + 2.4861, + 2.4977, + 2.4695, + 2.4724, + 2.4821, + 2.4893, + 2.4909, + 2.4904, + 2.4913, + 2.4911, + 2.4948, + 2.4978, + 2.4987, + 2.5016, + 2.5006, + 2.4973, + 2.501, + 2.5031, + 2.4986, + 2.496, + 2.4984, + 2.495, + 2.4983, + 2.5132, + 2.5134, + 2.5151, + 2.5149, + 2.5182, + 2.515, + 2.5146, + 2.5184, + 2.5162, + 2.5084, + 2.5126, + 2.5158, + 2.5176, + 2.5107, + 2.5121, + 2.5112, + 2.5093, + 2.5117, + 2.5093, + 2.5082, + 2.5042, + 2.4943, + 2.4939, + 2.4933, + 2.493, + 2.4846, + 2.4817, + 2.483, + 2.4879, + 2.4888, + 2.4984, + 2.4986, + 2.4988, + 2.4956, + 2.495, + 2.4968, + 2.4916, + 2.4881, + 2.4861, + 2.48, + 2.4772, + 2.477, + 2.48, + 2.4859, + 2.485, + 2.489, + 2.4914, + 2.4942, + 2.5007, + 2.5045, + 2.5069, + 2.5067, + 2.5062, + 2.506, + 2.5087, + 2.5078, + 2.5076, + 2.5075, + 2.5023, + 2.5026, + 2.5123, + 2.5101, + 2.5144, + 2.5138, + 2.5146, + 2.5126, + 2.5107, + 2.5108, + 2.5064, + 2.5036, + 2.503, + 2.5052, + 2.5112, + 2.5029, + 2.4873, + 2.4874, + 2.4916, + 2.4943, + 2.4925, + 2.4966, + 2.4953, + 2.4953, + 2.4907, + 2.4814, + 2.477, + 2.4625, + 2.4567, + 2.4409, + 2.4453, + 2.4541, + 2.4617, + 2.4653, + 2.4791, + 2.4821, + 2.4828, + 2.4854, + 2.4836, + 2.483, + 2.49, + 2.4986, + 2.4999, + 2.5081, + 2.5132, + 2.5191, + 2.5258, + 2.5201, + 2.5281, + 2.562, + 2.5769, + 2.5752, + 2.5528, + 2.5586, + 2.559, + 2.5684, + 2.574, + 2.5739, + 2.5704, + 2.5627, + 2.5607, + 2.5596, + 2.5578, + 2.5506, + 2.5665, + 2.5733, + 2.575, + 2.5685, + 2.5671, + 2.5599, + 2.5654, + 2.5678, + 2.5772, + 2.5823, + 2.5836, + 2.5944, + 2.6001, + 2.605, + 2.5972, + 2.6049, + 2.5929, + 2.5926, + 2.6062, + 2.6065, + 2.598, + 2.6006, + 2.6028, + 2.596, + 2.6011, + 2.5964, + 2.6013, + 2.5972, + 2.5992, + 2.6009, + 2.6005, + 2.6009, + 2.6034, + 2.5999, + 2.5983, + 2.5794, + 2.5773, + 2.5792, + 2.5804, + 2.58, + 2.5781, + 2.5855, + 2.5854, + 2.58, + 2.5808, + 2.5808, + 2.5781, + 2.5766, + 2.5725, + 2.5773, + 2.5752, + 2.5754, + 2.574, + 2.5755, + 2.5731, + 2.5753, + 2.5753, + 2.5853, + 2.5891, + 2.5888, + 2.5949, + 2.597, + 2.5982, + 2.5995, + 2.5969, + 2.5917, + 2.5895, + 2.5936, + 2.5827, + 2.585, + 2.5834, + 2.5832, + 2.5861, + 2.59, + 2.5905, + 2.5845, + 2.58, + 2.5801, + 2.58, + 2.5809, + 2.5818, + 2.5852, + 2.5845, + 2.5873, + 2.5911, + 2.589, + 2.5896, + 2.5886, + 2.5867, + 2.587, + 2.5912, + 2.6023, + 2.6115, + 2.6078, + 2.6083, + 2.6136, + 2.6164, + 2.6192, + 2.6262, + 2.6236, + 2.6203, + 2.6271, + 2.6361, + 2.6345, + 2.6351, + 2.6311, + 2.65, + 2.65, + 2.647, + 2.6526, + 2.6633, + 2.67, + 2.67, + 2.6736, + 2.67, + 2.6656, + 2.6624, + 2.66, + 2.6472, + 2.6396, + 2.6373, + 2.6375, + 2.6378, + 2.6476, + 2.6525, + 2.6438, + 2.6315, + 2.625, + 2.6248, + 2.627, + 2.6279, + 2.6244, + 2.6251, + 2.6289, + 2.6299, + 2.6165, + 2.6154, + 2.62, + 2.6153, + 2.6153, + 2.6109, + 2.6163, + 2.592, + 2.5976, + 2.58, + 2.5757, + 2.5822, + 2.5712, + 2.5627, + 2.5693, + 2.5774, + 2.5762, + 2.576, + 2.5807, + 2.5834, + 2.5852, + 2.5796, + 2.5791, + 2.579, + 2.5757, + 2.573, + 2.5668, + 2.5623, + 2.5553, + 2.5451, + 2.5428, + 2.5564, + 2.5618, + 2.5506, + 2.5509, + 2.5566, + 2.565, + 2.56, + 2.5468, + 2.5458, + 2.5655, + 2.5716, + 2.5827, + 2.5832, + 2.5856, + 2.5857, + 2.592, + 2.5919, + 2.5788, + 2.5815, + 2.587, + 2.5867, + 2.5853, + 2.5824, + 2.5805, + 2.5937, + 2.6022, + 2.6058, + 2.607, + 2.615, + 2.6058, + 2.6049, + 2.6009, + 2.605, + 2.6024, + 2.6028, + 2.5967, + 2.5945, + 2.5969, + 2.5949, + 2.5989, + 2.5998, + 2.6, + 2.5956, + 2.5932, + 2.6049, + 2.6068, + 2.602, + 2.5972, + 2.5935, + 2.5955, + 2.6056, + 2.6051, + 2.6033, + 2.6028, + 2.6069, + 2.6068, + 2.6021, + 2.5896, + 2.592, + 2.5855, + 2.5847, + 2.5844, + 2.589, + 2.5931, + 2.5896, + 2.5917, + 2.5925, + 2.5952, + 2.5967, + 2.5944, + 2.5923, + 2.59, + 2.5981, + 2.5953, + 2.6058, + 2.6055, + 2.6094, + 2.6098, + 2.6106, + 2.6082, + 2.6221, + 2.6363, + 2.6442, + 2.6424, + 2.6429, + 2.645, + 2.6458, + 2.6462, + 2.6399, + 2.642, + 2.6411, + 2.6415, + 2.6435, + 2.6484, + 2.6419, + 2.64, + 2.6262, + 2.6259, + 2.6172, + 2.6135, + 2.6125, + 2.6135, + 2.6174, + 2.6258, + 2.6305, + 2.6305, + 2.6277, + 2.6214, + 2.6236, + 2.6292, + 2.6285, + 2.6286, + 2.6286, + 2.6321, + 2.6356, + 2.6391, + 2.641, + 2.6361, + 2.6289, + 2.6319, + 2.6319, + 2.6385, + 2.6374, + 2.6367, + 2.6396, + 2.6403, + 2.6402, + 2.6401, + 2.6368, + 2.6313, + 2.6281, + 2.6363, + 2.6352, + 2.6363, + 2.6334, + 2.6424, + 2.6482, + 2.6471, + 2.6438, + 2.6381, + 2.6168, + 2.6142, + 2.6254, + 2.6248, + 2.632, + 2.6301, + 2.6322, + 2.6305, + 2.6327, + 2.6277, + 2.6274, + 2.6302, + 2.6266, + 2.628, + 2.6255, + 2.6274, + 2.6418, + 2.6409, + 2.6358, + 2.6343, + 2.6414, + 2.6462, + 2.6482, + 2.644, + 2.6534, + 2.6522, + 2.6609, + 2.6542, + 2.656, + 2.6586, + 2.6663, + 2.6669, + 2.6705, + 2.6681, + 2.6761, + 2.68, + 2.683, + 2.6818, + 2.6711, + 2.682, + 2.6801, + 2.674, + 2.6711, + 2.6686, + 2.667, + 2.6655, + 2.6668, + 2.6653, + 2.6631, + 2.664, + 2.6612, + 2.653, + 2.6541, + 2.6465, + 2.6497, + 2.6522, + 2.6551, + 2.655, + 2.6552, + 2.656, + 2.6561, + 2.6449, + 2.6442, + 2.6471, + 2.6469, + 2.6534, + 2.6546, + 2.655, + 2.6484, + 2.64, + 2.6449, + 2.6423, + 2.6418, + 2.6387, + 2.6392, + 2.6363, + 2.6396, + 2.6451, + 2.6431, + 2.6462, + 2.6478, + 2.6473, + 2.6667, + 2.6539, + 2.6587, + 2.6659, + 2.6664, + 2.6721, + 2.673, + 2.6734, + 2.6889, + 2.6889, + 2.6891, + 2.6809, + 2.6862, + 2.6894, + 2.6813, + 2.6687, + 2.6708, + 2.6611, + 2.6709, + 2.6755, + 2.6986, + 2.7076, + 2.7147, + 2.7162, + 2.7236, + 2.7267, + 2.7223, + 2.72, + 2.7239, + 2.7201, + 2.7144, + 2.712, + 2.7116, + 2.7217, + 2.7216, + 2.718, + 2.708, + 2.706, + 2.7075, + 2.7154, + 2.7186, + 2.7118, + 2.7139, + 2.7043, + 2.7062, + 2.7121, + 2.7063, + 2.7, + 2.7, + 2.7036, + 2.7, + 2.6735, + 2.6727, + 2.6559, + 2.6774, + 2.6727, + 2.6673, + 2.6644, + 2.6678, + 2.6705, + 2.6694, + 2.6716, + 2.6784, + 2.6803, + 2.6821, + 2.6851, + 2.6906, + 2.6921, + 2.6933, + 2.6944, + 2.687, + 2.6783, + 2.676, + 2.6789, + 2.6783, + 2.675, + 2.6724, + 2.681, + 2.6754, + 2.677, + 2.674, + 2.67, + 2.6663, + 2.6578, + 2.646, + 2.6378, + 2.6303, + 2.6214, + 2.6232, + 2.6072, + 2.6342, + 2.637, + 2.6418, + 2.6436, + 2.6503, + 2.6475, + 2.6523, + 2.6469, + 2.6479, + 2.6556, + 2.66, + 2.6755, + 2.6873, + 2.6862, + 2.6824, + 2.6829, + 2.6682, + 2.6604, + 2.643, + 2.55, + 2.3374, + 2.4549, + 2.493, + 2.5207, + 2.5541, + 2.553, + 2.5591, + 2.5614, + 2.5397, + 2.533, + 2.5177, + 2.52, + 2.5207, + 2.5571, + 2.565, + 2.561, + 2.566, + 2.5725, + 2.5874, + 2.5946, + 2.6137, + 2.6236, + 2.6288, + 2.6218, + 2.6336, + 2.6466, + 2.6482, + 2.6426, + 2.6421, + 2.6415, + 2.6401, + 2.6323, + 2.6369, + 2.6272, + 2.6286, + 2.5929, + 2.5809, + 2.597, + 2.5981, + 2.6008, + 2.5904, + 2.5602, + 2.5482, + 2.551, + 2.5263, + 2.5363, + 2.5366, + 2.5411, + 2.5469, + 2.5372, + 2.5305, + 2.5267, + 2.5272, + 2.5317, + 2.5342, + 2.5266, + 2.5211, + 2.5233, + 2.5312, + 2.534, + 2.5319, + 2.5457, + 2.5364, + 2.5262, + 2.4929, + 2.4712, + 2.476, + 2.482, + 2.4862, + 2.4873, + 2.4826, + 2.4783, + 2.4836, + 2.4944, + 2.4924, + 2.4944, + 2.4898, + 2.4909, + 2.4896, + 2.4847, + 2.4774, + 2.47, + 2.4628, + 2.4452, + 2.452, + 2.4518, + 2.4452, + 2.4462, + 2.43, + 2.42, + 2.4307, + 2.4303, + 2.4301, + 2.4426, + 2.4543, + 2.4598, + 2.4605, + 2.4702, + 2.4757, + 2.4707, + 2.4713, + 2.4727, + 2.4566, + 2.4522, + 2.4462, + 2.4502, + 2.4489, + 2.4446, + 2.4505, + 2.4501, + 2.4482, + 2.4473, + 2.4509, + 2.4461, + 2.4467, + 2.4518, + 2.4461, + 2.4558, + 2.461, + 2.454, + 2.4655, + 2.477, + 2.4702, + 2.4701, + 2.4744, + 2.4826, + 2.4894, + 2.4876, + 2.4975, + 2.4953, + 2.4901, + 2.4926, + 2.4954, + 2.4883, + 2.497, + 2.5084, + 2.5095, + 2.5075, + 2.5129, + 2.5193, + 2.5199, + 2.5205, + 2.5231, + 2.52, + 2.5197, + 2.4997, + 2.4892, + 2.4946, + 2.4974, + 2.5021, + 2.5045, + 2.5002, + 2.5017, + 2.5032, + 2.505, + 2.503, + 2.5, + 2.4996, + 2.5046, + 2.5098, + 2.505, + 2.5, + 2.5053, + 2.5054, + 2.5052, + 2.513, + 2.5137, + 2.5158, + 2.5122, + 2.5054, + 2.5079, + 2.5067, + 2.5102, + 2.5081, + 2.5041, + 2.5008, + 2.4991, + 2.4981, + 2.5045, + 2.509, + 2.515, + 2.5141, + 2.5097, + 2.5039, + 2.4929, + 2.4934, + 2.4909, + 2.494, + 2.4965, + 2.4971, + 2.5012, + 2.5022, + 2.496, + 2.5028, + 2.5029, + 2.5027, + 2.5077, + 2.4991, + 2.4984, + 2.5044, + 2.5082, + 2.5084, + 2.5062, + 2.5024, + 2.4984, + 2.4983, + 2.4964, + 2.4926, + 2.488, + 2.4887, + 2.4815, + 2.4822, + 2.4815, + 2.4756, + 2.4716, + 2.4786, + 2.4839, + 2.4828, + 2.4845, + 2.4851, + 2.4865, + 2.4805, + 2.4859, + 2.4778, + 2.4732, + 2.4681, + 2.4652, + 2.4602, + 2.4552, + 2.4561, + 2.4562, + 2.4533, + 2.4407, + 2.4465, + 2.4631, + 2.4602, + 2.4611, + 2.4632, + 2.456, + 2.4598, + 2.4602, + 2.4567, + 2.4479, + 2.4489, + 2.4388, + 2.4316, + 2.4368, + 2.4318, + 2.4397, + 2.444, + 2.4389, + 2.4444, + 2.4439, + 2.4441, + 2.4495, + 2.4431, + 2.4413, + 2.4379, + 2.4355, + 2.4389, + 2.4465, + 2.4566, + 2.4661, + 2.4656, + 2.457, + 2.4581, + 2.4617, + 2.4573, + 2.4581, + 2.4607, + 2.4565, + 2.4436, + 2.4682, + 2.4641, + 2.4602, + 2.4613, + 2.4588, + 2.4463, + 2.442, + 2.4445, + 2.4366, + 2.438, + 2.4459, + 2.4437, + 2.4465, + 2.4599, + 2.4494, + 2.4443, + 2.4491, + 2.4522, + 2.4515, + 2.4461, + 2.4428, + 2.4442, + 2.4472, + 2.4557, + 2.4555, + 2.4558, + 2.4477, + 2.4457, + 2.446, + 2.4385, + 2.4264, + 2.4293, + 2.4323, + 2.4343, + 2.4275, + 2.4127, + 2.4197, + 2.4238, + 2.4228, + 2.4257, + 2.4228, + 2.4146, + 2.4149, + 2.4185, + 2.396, + 2.4116, + 2.411, + 2.4092, + 2.3858, + 2.3976, + 2.4082, + 2.4028, + 2.3884, + 2.3714, + 2.3671, + 2.3926, + 2.3938, + 2.3947, + 2.3891, + 2.3903, + 2.4062, + 2.4073, + 2.4068, + 2.4068, + 2.4083, + 2.4102, + 2.4034, + 2.3977, + 2.3974, + 2.3923, + 2.3857, + 2.3872, + 2.38, + 2.3803, + 2.3814, + 2.3836, + 2.3767, + 2.3651, + 2.367, + 2.3848, + 2.3961, + 2.395, + 2.3965, + 2.4021, + 2.4008, + 2.4077, + 2.4, + 2.3958, + 2.3963, + 2.3945, + 2.3963, + 2.3982, + 2.3968, + 2.3977, + 2.3994, + 2.3952, + 2.3963, + 2.3957, + 2.396, + 2.4026, + 2.4093, + 2.4083, + 2.4059, + 2.4062, + 2.4101, + 2.4123, + 2.4145, + 2.4207, + 2.4232, + 2.4249, + 2.4227, + 2.422, + 2.4233, + 2.4238, + 2.4267, + 2.4281, + 2.4239, + 2.4282, + 2.4298, + 2.4306, + 2.4309, + 2.4337, + 2.4323, + 2.4344 + ], + "name": "OHLC Data", + "open": [ + 2.3777, + 2.3764, + 2.3797, + 2.3783, + 2.3778, + 2.3841, + 2.38, + 2.3748, + 2.3694, + 2.3727, + 2.3658, + 2.3631, + 2.3711, + 2.3718, + 2.3642, + 2.3654, + 2.3678, + 2.3632, + 2.3599, + 2.3655, + 2.3691, + 2.3633, + 2.3828, + 2.3856, + 2.3946, + 2.3905, + 2.389, + 2.3834, + 2.3951, + 2.3996, + 2.4059, + 2.4125, + 2.409, + 2.4077, + 2.4119, + 2.4137, + 2.4209, + 2.4252, + 2.4268, + 2.4304, + 2.4285, + 2.4309, + 2.4218, + 2.4309, + 2.4194, + 2.4176, + 2.4083, + 2.4038, + 2.4057, + 2.4079, + 2.4055, + 2.4014, + 2.4087, + 2.4087, + 2.402, + 2.4035, + 2.407, + 2.4108, + 2.403, + 2.4095, + 2.4095, + 2.4157, + 2.4087, + 2.4109, + 2.4085, + 2.4105, + 2.4156, + 2.4126, + 2.4139, + 2.412, + 2.4115, + 2.4174, + 2.4207, + 2.4139, + 2.4146, + 2.4102, + 2.4181, + 2.4182, + 2.4189, + 2.418, + 2.4226, + 2.4229, + 2.4232, + 2.4217, + 2.4178, + 2.414, + 2.4125, + 2.4199, + 2.4356, + 2.4287, + 2.4325, + 2.4262, + 2.4183, + 2.421, + 2.4189, + 2.4187, + 2.4178, + 2.4186, + 2.4262, + 2.4282, + 2.4284, + 2.4284, + 2.4239, + 2.4215, + 2.4241, + 2.4242, + 2.4267, + 2.4293, + 2.4246, + 2.4325, + 2.4315, + 2.4278, + 2.4283, + 2.4209, + 2.4234, + 2.4206, + 2.4167, + 2.4206, + 2.4174, + 2.417, + 2.4193, + 2.4245, + 2.4299, + 2.4266, + 2.4303, + 2.428, + 2.4246, + 2.4296, + 2.4376, + 2.4384, + 2.437, + 2.4443, + 2.4446, + 2.4388, + 2.436, + 2.4344, + 2.4374, + 2.4456, + 2.4412, + 2.4411, + 2.4431, + 2.446, + 2.4525, + 2.4489, + 2.449, + 2.4549, + 2.4575, + 2.4641, + 2.4562, + 2.4596, + 2.4642, + 2.4505, + 2.448, + 2.4518, + 2.4425, + 2.4382, + 2.4238, + 2.4297, + 2.4237, + 2.4377, + 2.4331, + 2.4385, + 2.4431, + 2.4437, + 2.4467, + 2.4431, + 2.4389, + 2.4382, + 2.4388, + 2.4357, + 2.4347, + 2.4358, + 2.4527, + 2.4511, + 2.4537, + 2.4563, + 2.4589, + 2.4549, + 2.4605, + 2.4628, + 2.4645, + 2.4534, + 2.4449, + 2.4468, + 2.4547, + 2.4562, + 2.4635, + 2.4692, + 2.4786, + 2.4901, + 2.497, + 2.4949, + 2.497, + 2.4913, + 2.5052, + 2.5196, + 2.5236, + 2.5228, + 2.5134, + 2.5091, + 2.5113, + 2.5135, + 2.5046, + 2.5182, + 2.5104, + 2.5052, + 2.503, + 2.505, + 2.4847, + 2.4829, + 2.4836, + 2.4732, + 2.4861, + 2.4807, + 2.4754, + 2.4711, + 2.4802, + 2.4661, + 2.4707, + 2.4734, + 2.4677, + 2.4583, + 2.4491, + 2.4382, + 2.4318, + 2.4411, + 2.4342, + 2.4299, + 2.4306, + 2.4216, + 2.4253, + 2.4218, + 2.4177, + 2.4194, + 2.4246, + 2.4214, + 2.4249, + 2.424, + 2.4258, + 2.4276, + 2.4235, + 2.4226, + 2.4284, + 2.4326, + 2.4341, + 2.4365, + 2.4407, + 2.4543, + 2.4604, + 2.4672, + 2.4625, + 2.4547, + 2.4543, + 2.4591, + 2.4484, + 2.4464, + 2.4381, + 2.4399, + 2.4359, + 2.4448, + 2.4478, + 2.442, + 2.4398, + 2.444, + 2.4405, + 2.4353, + 2.4416, + 2.4407, + 2.4373, + 2.445, + 2.4418, + 2.449, + 2.4577, + 2.4544, + 2.4488, + 2.4521, + 2.4502, + 2.4457, + 2.4477, + 2.4486, + 2.4482, + 2.4432, + 2.445, + 2.4547, + 2.4608, + 2.4599, + 2.4589, + 2.463, + 2.4649, + 2.4625, + 2.4612, + 2.4611, + 2.4608, + 2.4623, + 2.4624, + 2.4659, + 2.4722, + 2.4662, + 2.4667, + 2.4621, + 2.4605, + 2.4582, + 2.4611, + 2.4534, + 2.4563, + 2.4536, + 2.449, + 2.4495, + 2.4528, + 2.4623, + 2.4569, + 2.4513, + 2.4417, + 2.4365, + 2.4353, + 2.4374, + 2.4464, + 2.4463, + 2.4469, + 2.4462, + 2.4372, + 2.4285, + 2.4247, + 2.4308, + 2.4309, + 2.4299, + 2.4322, + 2.4237, + 2.4041, + 2.417, + 2.4169, + 2.4138, + 2.4051, + 2.3991, + 2.3874, + 2.3804, + 2.3964, + 2.3965, + 2.3954, + 2.3962, + 2.3998, + 2.41, + 2.4101, + 2.4114, + 2.4148, + 2.4156, + 2.4108, + 2.4142, + 2.4124, + 2.4112, + 2.4018, + 2.3958, + 2.3981, + 2.3927, + 2.3939, + 2.3893, + 2.399, + 2.3961, + 2.3942, + 2.3981, + 2.4038, + 2.4062, + 2.4036, + 2.4103, + 2.4139, + 2.4144, + 2.4138, + 2.4179, + 2.4149, + 2.4137, + 2.4143, + 2.4147, + 2.4155, + 2.4164, + 2.4092, + 2.41, + 2.4108, + 2.4096, + 2.4129, + 2.4102, + 2.406, + 2.4068, + 2.4093, + 2.4051, + 2.4091, + 2.4049, + 2.4029, + 2.408, + 2.4107, + 2.4034, + 2.4035, + 2.4031, + 2.4022, + 2.4045, + 2.4048, + 2.4048, + 2.4073, + 2.397, + 2.3987, + 2.3921, + 2.3824, + 2.3732, + 2.3871, + 2.3897, + 2.3878, + 2.3995, + 2.4047, + 2.403, + 2.4014, + 2.3984, + 2.3963, + 2.39, + 2.3915, + 2.3855, + 2.3858, + 2.3925, + 2.3921, + 2.3894, + 2.3948, + 2.3989, + 2.396, + 2.3955, + 2.3999, + 2.4065, + 2.4089, + 2.4075, + 2.4052, + 2.4026, + 2.4066, + 2.4019, + 2.4028, + 2.4037, + 2.4095, + 2.4286, + 2.4176, + 2.4203, + 2.4215, + 2.423, + 2.4226, + 2.4215, + 2.4213, + 2.4215, + 2.4217, + 2.4215, + 2.4176, + 2.414, + 2.4227, + 2.4136, + 2.4183, + 2.4153, + 2.4049, + 2.4071, + 2.4097, + 2.413, + 2.4123, + 2.4174, + 2.4191, + 2.4132, + 2.4132, + 2.4067, + 2.4051, + 2.4207, + 2.4151, + 2.4242, + 2.4321, + 2.4294, + 2.4394, + 2.4392, + 2.4484, + 2.4477, + 2.4448, + 2.4363, + 2.4386, + 2.4447, + 2.4347, + 2.4462, + 2.4496, + 2.4465, + 2.4491, + 2.4512, + 2.446, + 2.4441, + 2.4498, + 2.4506, + 2.4453, + 2.4445, + 2.4447, + 2.4432, + 2.4487, + 2.4437, + 2.4509, + 2.4529, + 2.4618, + 2.4604, + 2.459, + 2.4625, + 2.4459, + 2.4499, + 2.4568, + 2.458, + 2.4558, + 2.4437, + 2.4365, + 2.4463, + 2.4414, + 2.4412, + 2.4374, + 2.4309, + 2.4344, + 2.4369, + 2.4348, + 2.4368, + 2.4272, + 2.4247, + 2.4337, + 2.4495, + 2.4459, + 2.4407, + 2.4393, + 2.4418, + 2.4395, + 2.4427, + 2.4419, + 2.4446, + 2.4472, + 2.4582, + 2.4492, + 2.4419, + 2.4432, + 2.4434, + 2.4552, + 2.4431, + 2.4366, + 2.4391, + 2.4365, + 2.4272, + 2.4392, + 2.4419, + 2.4518, + 2.4611, + 2.4676, + 2.481, + 2.4855, + 2.4847, + 2.4838, + 2.4848, + 2.4746, + 2.4817, + 2.4731, + 2.4682, + 2.4695, + 2.4761, + 2.4778, + 2.478, + 2.481, + 2.4782, + 2.4728, + 2.4742, + 2.4766, + 2.4716, + 2.4679, + 2.4738, + 2.4772, + 2.4851, + 2.4844, + 2.4784, + 2.4783, + 2.4851, + 2.4827, + 2.4797, + 2.4781, + 2.4832, + 2.4785, + 2.4763, + 2.4804, + 2.484, + 2.4829, + 2.4924, + 2.4909, + 2.4932, + 2.4959, + 2.4965, + 2.4975, + 2.4885, + 2.4946, + 2.4947, + 2.4975, + 2.4822, + 2.4864, + 2.4767, + 2.4811, + 2.4882, + 2.4885, + 2.4893, + 2.4925, + 2.499, + 2.5022, + 2.4939, + 2.489, + 2.487, + 2.4867, + 2.4804, + 2.48, + 2.4755, + 2.4721, + 2.4638, + 2.4689, + 2.4662, + 2.4642, + 2.4582, + 2.4631, + 2.4673, + 2.4644, + 2.4605, + 2.4561, + 2.4562, + 2.4651, + 2.4602, + 2.4554, + 2.4528, + 2.453, + 2.45, + 2.4414, + 2.4428, + 2.4463, + 2.446, + 2.4494, + 2.4489, + 2.4415, + 2.4453, + 2.4371, + 2.4349, + 2.4326, + 2.4293, + 2.433, + 2.4341, + 2.4348, + 2.4341, + 2.4372, + 2.4352, + 2.4392, + 2.4466, + 2.4525, + 2.456, + 2.4567, + 2.4578, + 2.4643, + 2.459, + 2.458, + 2.4626, + 2.4584, + 2.4568, + 2.4539, + 2.452, + 2.4547, + 2.4542, + 2.4584, + 2.4603, + 2.4585, + 2.4541, + 2.4534, + 2.4591, + 2.4766, + 2.4661, + 2.4704, + 2.4729, + 2.4688, + 2.468, + 2.4769, + 2.4795, + 2.4828, + 2.4776, + 2.4588, + 2.4696, + 2.4761, + 2.4735, + 2.4658, + 2.4785, + 2.4782, + 2.4829, + 2.4787, + 2.4803, + 2.4862, + 2.484, + 2.4817, + 2.4863, + 2.4795, + 2.4831, + 2.4763, + 2.4722, + 2.4691, + 2.4717, + 2.4724, + 2.4686, + 2.4623, + 2.4631, + 2.4736, + 2.4781, + 2.4814, + 2.4826, + 2.4823, + 2.4871, + 2.4885, + 2.49, + 2.4841, + 2.4719, + 2.4718, + 2.4705, + 2.4664, + 2.4621, + 2.4647, + 2.4595, + 2.4502, + 2.4564, + 2.4568, + 2.4629, + 2.4667, + 2.4638, + 2.4636, + 2.4593, + 2.4571, + 2.4545, + 2.4581, + 2.4595, + 2.4576, + 2.4562, + 2.4577, + 2.452, + 2.4545, + 2.4501, + 2.4487, + 2.4428, + 2.4347, + 2.4392, + 2.4415, + 2.4373, + 2.4457, + 2.447, + 2.4517, + 2.453, + 2.4531, + 2.4615, + 2.462, + 2.4673, + 2.4636, + 2.4686, + 2.4788, + 2.4887, + 2.4948, + 2.4941, + 2.4788, + 2.4785, + 2.5188, + 2.5083, + 2.5175, + 2.519, + 2.5169, + 2.5154, + 2.5127, + 2.5096, + 2.5173, + 2.5189, + 2.5182, + 2.5275, + 2.5187, + 2.5124, + 2.5157, + 2.5024, + 2.4737, + 2.4945, + 2.4863, + 2.4719, + 2.4669, + 2.4428, + 2.4428, + 2.4399, + 2.4574, + 2.457, + 2.4482, + 2.4484, + 2.4475, + 2.4382, + 2.4229, + 2.4174, + 2.4258, + 2.4229, + 2.4223, + 2.4109, + 2.4105, + 2.4105, + 2.4123, + 2.4202, + 2.4266, + 2.4385, + 2.4406, + 2.4428, + 2.4461, + 2.4393, + 2.4406, + 2.4532, + 2.4471, + 2.4518, + 2.4593, + 2.4719, + 2.4593, + 2.4512, + 2.4371, + 2.4394, + 2.4498, + 2.4517, + 2.4546, + 2.4645, + 2.4589, + 2.4781, + 2.4632, + 2.4713, + 2.4802, + 2.481, + 2.4782, + 2.4756, + 2.4727, + 2.481, + 2.4824, + 2.4862, + 2.4861, + 2.4894, + 2.503, + 2.4998, + 2.4993, + 2.5068, + 2.5249, + 2.528, + 2.5474, + 2.5409, + 2.5373, + 2.5343, + 2.5154, + 2.523, + 2.5274, + 2.5281, + 2.5351, + 2.5235, + 2.5195, + 2.5109, + 2.5146, + 2.5097, + 2.5132, + 2.5109, + 2.5009, + 2.5024, + 2.4933, + 2.4971, + 2.4948, + 2.4925, + 2.4856, + 2.4852, + 2.4841, + 2.4838, + 2.4849, + 2.4852, + 2.4821, + 2.4913, + 2.4838, + 2.486, + 2.4917, + 2.4908, + 2.4817, + 2.4897, + 2.5038, + 2.5048, + 2.5067, + 2.5036, + 2.5117, + 2.5187, + 2.5171, + 2.5208, + 2.5156, + 2.508, + 2.5169, + 2.5194, + 2.5055, + 2.5032, + 2.4955, + 2.4703, + 2.4676, + 2.4542, + 2.4652, + 2.4497, + 2.4497, + 2.4444, + 2.4351, + 2.4221, + 2.4098, + 2.4262, + 2.4273, + 2.4302, + 2.4221, + 2.4248, + 2.4257, + 2.4298, + 2.4355, + 2.4458, + 2.4588, + 2.4563, + 2.4439, + 2.4393, + 2.4489, + 2.4409, + 2.4357, + 2.4387, + 2.4295, + 2.4387, + 2.4293, + 2.4357, + 2.4429, + 2.4507, + 2.4614, + 2.462, + 2.459, + 2.4616, + 2.4588, + 2.4602, + 2.4632, + 2.4737, + 2.4732, + 2.4718, + 2.4603, + 2.4593, + 2.4657, + 2.4761, + 2.4747, + 2.4716, + 2.473, + 2.4659, + 2.4462, + 2.4419, + 2.438, + 2.4348, + 2.4419, + 2.4393, + 2.4374, + 2.4935, + 2.4743, + 2.4855, + 2.504, + 2.4859, + 2.465, + 2.463, + 2.4518, + 2.4529, + 2.455, + 2.4509, + 2.4501, + 2.4419, + 2.4386, + 2.4472, + 2.4387, + 2.4415, + 2.4278, + 2.4282, + 2.4307, + 2.4394, + 2.45, + 2.4392, + 2.4468, + 2.4505, + 2.444, + 2.4531, + 2.4638, + 2.4438, + 2.4567, + 2.4656, + 2.4682, + 2.4628, + 2.4711, + 2.4649, + 2.4678, + 2.4699, + 2.4684, + 2.4651, + 2.4654, + 2.476, + 2.485, + 2.4845, + 2.4862, + 2.4838, + 2.4825, + 2.484, + 2.4858, + 2.4871, + 2.4901, + 2.4878, + 2.4941, + 2.4994, + 2.4979, + 2.4985, + 2.4908, + 2.489, + 2.4765, + 2.4865, + 2.4856, + 2.4843, + 2.4889, + 2.4864, + 2.4813, + 2.481, + 2.4821, + 2.4888, + 2.4924, + 2.4897, + 2.4873, + 2.4899, + 2.4857, + 2.4905, + 2.4971, + 2.4902, + 2.4882, + 2.4873, + 2.4851, + 2.4768, + 2.4836, + 2.4785, + 2.4795, + 2.4825, + 2.4832, + 2.4824, + 2.4831, + 2.4862, + 2.4884, + 2.4868, + 2.4846, + 2.4794, + 2.4841, + 2.4848, + 2.4785, + 2.4785, + 2.4831, + 2.48, + 2.485, + 2.4925, + 2.4948, + 2.4996, + 2.4942, + 2.494, + 2.4905, + 2.4905, + 2.4901, + 2.4945, + 2.4956, + 2.4963, + 2.4993, + 2.5014, + 2.4994, + 2.4989, + 2.4896, + 2.4953, + 2.4944, + 2.4947, + 2.4985, + 2.4982, + 2.498, + 2.5031, + 2.4964, + 2.4973, + 2.4988, + 2.4999, + 2.4911, + 2.4869, + 2.4822, + 2.4807, + 2.4797, + 2.4791, + 2.4844, + 2.4843, + 2.479, + 2.4746, + 2.4738, + 2.4647, + 2.4618, + 2.465, + 2.4588, + 2.4695, + 2.4709, + 2.472, + 2.4703, + 2.4749, + 2.4749, + 2.4713, + 2.4724, + 2.4711, + 2.4655, + 2.4552, + 2.4442, + 2.445, + 2.4548, + 2.4494, + 2.4566, + 2.4473, + 2.4502, + 2.4492, + 2.4485, + 2.4481, + 2.4557, + 2.4497, + 2.454, + 2.4553, + 2.4568, + 2.4564, + 2.4587, + 2.4587, + 2.457, + 2.4605, + 2.463, + 2.4557, + 2.4435, + 2.4385, + 2.4275, + 2.4172, + 2.4116, + 2.4228, + 2.4281, + 2.4241, + 2.4183, + 2.4201, + 2.4234, + 2.4245, + 2.4292, + 2.4319, + 2.432, + 2.4298, + 2.4348, + 2.434, + 2.4334, + 2.4288, + 2.4258, + 2.4363, + 2.4348, + 2.4333, + 2.4295, + 2.4268, + 2.4314, + 2.4368, + 2.4385, + 2.4355, + 2.4371, + 2.4335, + 2.4423, + 2.4421, + 2.4461, + 2.4568, + 2.4606, + 2.457, + 2.4662, + 2.4713, + 2.4777, + 2.48, + 2.4871, + 2.4847, + 2.4834, + 2.4731, + 2.4731, + 2.4812, + 2.4777, + 2.472, + 2.4702, + 2.4731, + 2.468, + 2.4659, + 2.4638, + 2.464, + 2.4614, + 2.4587, + 2.4536, + 2.453, + 2.4487, + 2.4475, + 2.4426, + 2.4433, + 2.4499, + 2.4548, + 2.4537, + 2.457, + 2.4601, + 2.461, + 2.4589, + 2.4542, + 2.4534, + 2.4546, + 2.4544, + 2.4538, + 2.4589, + 2.461, + 2.461, + 2.4622, + 2.4527, + 2.4497, + 2.4395, + 2.4464, + 2.4405, + 2.4396, + 2.4407, + 2.4394, + 2.4366, + 2.427, + 2.4264, + 2.4327, + 2.4333, + 2.436, + 2.4417, + 2.4294, + 2.4276, + 2.4312, + 2.4286, + 2.4262, + 2.4249, + 2.4268, + 2.4328, + 2.4319, + 2.4355, + 2.4337, + 2.4366, + 2.4349, + 2.4392, + 2.4432, + 2.4439, + 2.4436, + 2.4403, + 2.4353, + 2.4389, + 2.4368, + 2.4454, + 2.4449, + 2.4514, + 2.4555, + 2.4586, + 2.4594, + 2.4563, + 2.4579, + 2.4534, + 2.4534, + 2.4499, + 2.4488, + 2.4518, + 2.4532, + 2.4543, + 2.4535, + 2.453, + 2.453, + 2.457, + 2.456, + 2.4603, + 2.4565, + 2.4621, + 2.4582, + 2.4543, + 2.4494, + 2.4466, + 2.4453, + 2.4511, + 2.4485, + 2.4527, + 2.4485, + 2.4499, + 2.4461, + 2.4434, + 2.4427, + 2.4474, + 2.4425, + 2.4447, + 2.4431, + 2.4441, + 2.4468, + 2.4456, + 2.4517, + 2.4537, + 2.4519, + 2.4393, + 2.44, + 2.4452, + 2.4486, + 2.4463, + 2.4502, + 2.4501, + 2.4504, + 2.4524, + 2.452, + 2.4491, + 2.4448, + 2.4443, + 2.4486, + 2.4501, + 2.4453, + 2.4415, + 2.4455, + 2.4457, + 2.4473, + 2.4435, + 2.4437, + 2.4409, + 2.444, + 2.4423, + 2.4445, + 2.4499, + 2.4483, + 2.4472, + 2.4322, + 2.4353, + 2.4284, + 2.426, + 2.4223, + 2.4168, + 2.4215, + 2.4259, + 2.4301, + 2.4249, + 2.422, + 2.4211, + 2.4143, + 2.4139, + 2.4043, + 2.4036, + 2.4053, + 2.4002, + 2.3997, + 2.3964, + 2.3881, + 2.374, + 2.3874, + 2.37, + 2.3568, + 2.3514, + 2.3623, + 2.3684, + 2.3642, + 2.3743, + 2.375, + 2.3853, + 2.3899, + 2.3833, + 2.3856, + 2.3799, + 2.377, + 2.3666, + 2.3716, + 2.3732, + 2.3694, + 2.3733, + 2.363, + 2.3687, + 2.3734, + 2.3725, + 2.375, + 2.3788, + 2.3753, + 2.3735, + 2.3742, + 2.3701, + 2.3707, + 2.366, + 2.3638, + 2.3665, + 2.3745, + 2.3678, + 2.3701, + 2.3698, + 2.3731, + 2.3796, + 2.3807, + 2.3832, + 2.3848, + 2.3783, + 2.3754, + 2.3769, + 2.3787, + 2.3771, + 2.3754, + 2.3713, + 2.3707, + 2.3741, + 2.3691, + 2.3726, + 2.363, + 2.3653, + 2.3622, + 2.3759, + 2.3769, + 2.379, + 2.3752, + 2.3817, + 2.3818, + 2.3898, + 2.4025, + 2.3979, + 2.4041, + 2.4007, + 2.4028, + 2.3963, + 2.3965, + 2.3972, + 2.3978, + 2.4059, + 2.4025, + 2.3992, + 2.3957, + 2.3965, + 2.3913, + 2.3933, + 2.396, + 2.3939, + 2.3981, + 2.398, + 2.3995, + 2.3941, + 2.4, + 2.3986, + 2.3931, + 2.3905, + 2.389, + 2.3961, + 2.3976, + 2.3954, + 2.3896, + 2.3939, + 2.3918, + 2.3914, + 2.3921, + 2.3885, + 2.3863, + 2.388, + 2.3982, + 2.4013, + 2.4028, + 2.4061, + 2.4031, + 2.3959, + 2.3937, + 2.3947, + 2.3947, + 2.3986, + 2.4018, + 2.399, + 2.411, + 2.414, + 2.4132, + 2.4148, + 2.4168, + 2.4217, + 2.4235, + 2.4307, + 2.4309, + 2.4348, + 2.429, + 2.435, + 2.4356, + 2.4354, + 2.4365, + 2.4327, + 2.4333, + 2.4321, + 2.4297, + 2.4319, + 2.427, + 2.431, + 2.4322, + 2.4391, + 2.4401, + 2.4462, + 2.4452, + 2.4781, + 2.4884, + 2.4965, + 2.4892, + 2.4931, + 2.5004, + 2.4977, + 2.4794, + 2.482, + 2.4813, + 2.4787, + 2.4822, + 2.4897, + 2.484, + 2.5084, + 2.5075, + 2.4993, + 2.5, + 2.5009, + 2.5101, + 2.5112, + 2.5083, + 2.5082, + 2.5039, + 2.5055, + 2.5053, + 2.4994, + 2.4969, + 2.4918, + 2.4898, + 2.4985, + 2.5051, + 2.5066, + 2.5124, + 2.5084, + 2.5128, + 2.5111, + 2.5038, + 2.5023, + 2.5, + 2.5013, + 2.5031, + 2.5004, + 2.4965, + 2.4923, + 2.4838, + 2.4779, + 2.4773, + 2.4838, + 2.4853, + 2.487, + 2.4832, + 2.4815, + 2.4786, + 2.4832, + 2.4817, + 2.4705, + 2.4695, + 2.4657, + 2.4635, + 2.4624, + 2.4591, + 2.4596, + 2.4609, + 2.4615, + 2.4596, + 2.4526, + 2.4441, + 2.4512, + 2.4552, + 2.4541, + 2.4582, + 2.4571, + 2.4559, + 2.4648, + 2.4694, + 2.4718, + 2.4699, + 2.4694, + 2.4648, + 2.4615, + 2.4652, + 2.4639, + 2.4623, + 2.4611, + 2.4546, + 2.4575, + 2.46, + 2.466, + 2.4638, + 2.4682, + 2.4727, + 2.4721, + 2.4728, + 2.4724, + 2.4721, + 2.4725, + 2.4751, + 2.4774, + 2.4807, + 2.4832, + 2.4849, + 2.4895, + 2.4966, + 2.4944, + 2.4935, + 2.4889, + 2.4994, + 2.4953, + 2.496, + 2.4976, + 2.4978, + 2.4986, + 2.4993, + 2.4913, + 2.4983, + 2.5033, + 2.5058, + 2.5001, + 2.4922, + 2.5031, + 2.4958, + 2.4969, + 2.4923, + 2.4836, + 2.4908, + 2.4891, + 2.4882, + 2.4891, + 2.4862, + 2.4813, + 2.4871, + 2.4843, + 2.4921, + 2.4987, + 2.5023, + 2.5039, + 2.5015, + 2.5077, + 2.5092, + 2.5028, + 2.5039, + 2.5021, + 2.5115, + 2.5102, + 2.5097, + 2.5092, + 2.5114, + 2.5081, + 2.5032, + 2.5014, + 2.5048, + 2.503, + 2.5127, + 2.5065, + 2.5034, + 2.5025, + 2.5011, + 2.4975, + 2.5081, + 2.5071, + 2.5134, + 2.5119, + 2.5107, + 2.5071, + 2.5039, + 2.5022, + 2.5006, + 2.4988, + 2.4986, + 2.496, + 2.4956, + 2.4906, + 2.487, + 2.4811, + 2.4763, + 2.479, + 2.4789, + 2.4855, + 2.4897, + 2.4904, + 2.4922, + 2.4879, + 2.4916, + 2.4967, + 2.5003, + 2.5012, + 2.4955, + 2.4946, + 2.4891, + 2.4905, + 2.4851, + 2.4891, + 2.4872, + 2.4912, + 2.4908, + 2.4919, + 2.4925, + 2.495, + 2.4928, + 2.4976, + 2.5014, + 2.4982, + 2.4946, + 2.4941, + 2.492, + 2.4916, + 2.4937, + 2.4905, + 2.4921, + 2.5014, + 2.5059, + 2.5144, + 2.515, + 2.5156, + 2.5124, + 2.513, + 2.5057, + 2.5195, + 2.5204, + 2.5198, + 2.5195, + 2.5183, + 2.521, + 2.5255, + 2.5263, + 2.5267, + 2.5272, + 2.5293, + 2.5245, + 2.5216, + 2.5275, + 2.5239, + 2.5289, + 2.5302, + 2.5278, + 2.5276, + 2.5314, + 2.5275, + 2.529, + 2.5341, + 2.5264, + 2.5335, + 2.5346, + 2.5273, + 2.5233, + 2.5185, + 2.5271, + 2.5229, + 2.5187, + 2.518, + 2.5175, + 2.5165, + 2.5137, + 2.5159, + 2.5128, + 2.5131, + 2.5168, + 2.5209, + 2.5224, + 2.5203, + 2.5246, + 2.5226, + 2.5168, + 2.5146, + 2.5125, + 2.5143, + 2.5242, + 2.5262, + 2.5372, + 2.534, + 2.5367, + 2.5398, + 2.5365, + 2.5286, + 2.54, + 2.5386, + 2.5431, + 2.545, + 2.5483, + 2.5473, + 2.5461, + 2.5432, + 2.5436, + 2.5374, + 2.5348, + 2.5372, + 2.5286, + 2.5244, + 2.5303, + 2.5271, + 2.5249, + 2.5275, + 2.5244, + 2.5184, + 2.5236, + 2.5193, + 2.514, + 2.5151, + 2.5123, + 2.5067, + 2.5046, + 2.4931, + 2.4971, + 2.5049, + 2.5041, + 2.5068, + 2.5049, + 2.5028, + 2.5017, + 2.4959, + 2.4986, + 2.4887, + 2.4832, + 2.4855, + 2.4848, + 2.4859, + 2.4848, + 2.4818, + 2.4879, + 2.4902, + 2.4888, + 2.4893, + 2.4897, + 2.4911, + 2.4928, + 2.4889, + 2.4872, + 2.4842, + 2.4846, + 2.4818, + 2.4785, + 2.4685, + 2.4733, + 2.4757, + 2.4726, + 2.4715, + 2.477, + 2.4783, + 2.4845, + 2.4892, + 2.4912, + 2.4861, + 2.4887, + 2.4883, + 2.4827, + 2.4786, + 2.479, + 2.4832, + 2.4791, + 2.4744, + 2.4771, + 2.4747, + 2.4736, + 2.4746, + 2.481, + 2.484, + 2.4857, + 2.4802, + 2.4824, + 2.4859, + 2.4819, + 2.4854, + 2.4824, + 2.4832, + 2.4818, + 2.4817, + 2.4924, + 2.4901, + 2.4905, + 2.4995, + 2.4958, + 2.4993, + 2.5014, + 2.5011, + 2.5052, + 2.5082, + 2.5151, + 2.5117, + 2.5098, + 2.5057, + 2.5065, + 2.513, + 2.5125, + 2.5165, + 2.5187, + 2.5182, + 2.5169, + 2.5175, + 2.518, + 2.5159, + 2.5121, + 2.5112, + 2.5077, + 2.5032, + 2.5024, + 2.5036, + 2.5074, + 2.5113, + 2.5145, + 2.5162, + 2.512, + 2.5182, + 2.5168, + 2.5144, + 2.517, + 2.5188, + 2.521, + 2.5221, + 2.5185, + 2.5211, + 2.5162, + 2.5149, + 2.508, + 2.4961, + 2.4711, + 2.4745, + 2.4777, + 2.4763, + 2.4791, + 2.4656, + 2.4754, + 2.4708, + 2.4656, + 2.4724, + 2.4636, + 2.4643, + 2.4711, + 2.4706, + 2.4756, + 2.457, + 2.4459, + 2.4392, + 2.4535, + 2.4446, + 2.4336, + 2.4407, + 2.4334, + 2.4422, + 2.4395, + 2.4371, + 2.4333, + 2.4398, + 2.4506, + 2.4534, + 2.4531, + 2.4566, + 2.4536, + 2.4536, + 2.4612, + 2.4447, + 2.4508, + 2.4537, + 2.4574, + 2.455, + 2.4563, + 2.4558, + 2.4537, + 2.4528, + 2.4576, + 2.4542, + 2.4531, + 2.4473, + 2.4515, + 2.451, + 2.4501, + 2.4487, + 2.4543, + 2.4546, + 2.4512, + 2.4412, + 2.4439, + 2.4457, + 2.4524, + 2.4581, + 2.4617, + 2.4589, + 2.4625, + 2.4663, + 2.4739, + 2.4754, + 2.4689, + 2.4647, + 2.47, + 2.4735, + 2.4762, + 2.4732, + 2.4698, + 2.4681, + 2.4666, + 2.468, + 2.4649, + 2.4659, + 2.4718, + 2.4665, + 2.4544, + 2.4521, + 2.4488, + 2.4528, + 2.4539, + 2.4502, + 2.4472, + 2.4486, + 2.4512, + 2.4585, + 2.4592, + 2.4581, + 2.4581, + 2.4617, + 2.4593, + 2.4591, + 2.4624, + 2.4601, + 2.4614, + 2.461, + 2.4603, + 2.4603, + 2.4643, + 2.4603, + 2.4593, + 2.458, + 2.4567, + 2.4581, + 2.4544, + 2.4538, + 2.4534, + 2.4523, + 2.453, + 2.4599, + 2.4606, + 2.4603, + 2.459, + 2.4575, + 2.4615, + 2.4619, + 2.4654, + 2.4639, + 2.4639, + 2.4651, + 2.4635, + 2.4626, + 2.4593, + 2.4579, + 2.4647, + 2.4665, + 2.4638, + 2.4666, + 2.469, + 2.4699, + 2.4684, + 2.4709, + 2.4698, + 2.4694, + 2.4729, + 2.4726, + 2.4735, + 2.4726, + 2.4733, + 2.4736, + 2.4761, + 2.4738, + 2.4748, + 2.4733, + 2.476, + 2.4739, + 2.4701, + 2.471, + 2.4757, + 2.4742, + 2.4774, + 2.4802, + 2.4825, + 2.481, + 2.4851, + 2.4754, + 2.4752, + 2.4775, + 2.4756, + 2.4798, + 2.4782, + 2.4786, + 2.4785, + 2.4846, + 2.4875, + 2.4844, + 2.4866, + 2.4785, + 2.4763, + 2.4742, + 2.4719, + 2.4792, + 2.4847, + 2.4859, + 2.4826, + 2.4828, + 2.4789, + 2.4778, + 2.4784, + 2.4801, + 2.4763, + 2.4752, + 2.4735, + 2.4726, + 2.4732, + 2.4731, + 2.471, + 2.469, + 2.4694, + 2.4785, + 2.4785, + 2.478, + 2.4734, + 2.4735, + 2.4777, + 2.4804, + 2.4808, + 2.4822, + 2.4833, + 2.4829, + 2.4852, + 2.4825, + 2.4867, + 2.4888, + 2.488, + 2.4896, + 2.4893, + 2.4872, + 2.4893, + 2.4859, + 2.4879, + 2.4883, + 2.4883, + 2.4896, + 2.4916, + 2.4964, + 2.4978, + 2.498, + 2.5004, + 2.4998, + 2.5021, + 2.503, + 2.5045, + 2.5082, + 2.5114, + 2.5109, + 2.5144, + 2.5161, + 2.5163, + 2.5191, + 2.5216, + 2.515, + 2.5185, + 2.516, + 2.5062, + 2.5006, + 2.4851, + 2.4909, + 2.4917, + 2.5033, + 2.5058, + 2.5069, + 2.5016, + 2.5022, + 2.5006, + 2.5139, + 2.5106, + 2.512, + 2.5099, + 2.5064, + 2.5056, + 2.5021, + 2.5035, + 2.5037, + 2.4974, + 2.5057, + 2.5077, + 2.5095, + 2.502, + 2.498, + 2.5048, + 2.5016, + 2.5044, + 2.5041, + 2.5104, + 2.5113, + 2.5097, + 2.5077, + 2.5043, + 2.5065, + 2.5064, + 2.5054, + 2.5057, + 2.5045, + 2.5024, + 2.5049, + 2.5067, + 2.5114, + 2.5106, + 2.5115, + 2.5037, + 2.5093, + 2.5078, + 2.5101, + 2.5122, + 2.5091, + 2.5051, + 2.5055, + 2.5053, + 2.5018, + 2.5029, + 2.5035, + 2.5024, + 2.4989, + 2.5023, + 2.5053, + 2.5042, + 2.5, + 2.4998, + 2.4983, + 2.4967, + 2.4939, + 2.5008, + 2.5063, + 2.5062, + 2.5056, + 2.5079, + 2.5045, + 2.5009, + 2.5064, + 2.5112, + 2.5181, + 2.5253, + 2.5301, + 2.5211, + 2.5255, + 2.5203, + 2.5197, + 2.5257, + 2.528, + 2.5255, + 2.5196, + 2.522, + 2.5183, + 2.5252, + 2.5239, + 2.5236, + 2.5237, + 2.5272, + 2.5274, + 2.5266, + 2.5271, + 2.5232, + 2.5275, + 2.5297, + 2.5269, + 2.5237, + 2.5189, + 2.5171, + 2.5175, + 2.5134, + 2.5174, + 2.522, + 2.5214, + 2.5209, + 2.5144, + 2.5139, + 2.5105, + 2.5077, + 2.5096, + 2.5118, + 2.5097, + 2.5074, + 2.5087, + 2.5047, + 2.5053, + 2.5113, + 2.5153, + 2.5162, + 2.5109, + 2.5041, + 2.5003, + 2.5052, + 2.5051, + 2.5103, + 2.5117, + 2.5083, + 2.5105, + 2.5125, + 2.5228, + 2.5216, + 2.5205, + 2.5231, + 2.5181, + 2.5229, + 2.5195, + 2.5192, + 2.5195, + 2.5206, + 2.5268, + 2.5381, + 2.54, + 2.5391, + 2.5391, + 2.5332, + 2.5075, + 2.4835, + 2.4775, + 2.4607, + 2.4799, + 2.485, + 2.4902, + 2.4805, + 2.4799, + 2.4892, + 2.4825, + 2.4834, + 2.4859, + 2.485, + 2.4936, + 2.4894, + 2.4939, + 2.4852, + 2.4912, + 2.4897, + 2.4802, + 2.4818, + 2.4777, + 2.4612, + 2.4661, + 2.4672, + 2.4558, + 2.4632, + 2.4663, + 2.4734, + 2.4696, + 2.4703, + 2.4702, + 2.4748, + 2.4706, + 2.4714, + 2.4682, + 2.4691, + 2.465, + 2.4727, + 2.4793, + 2.4768, + 2.4786, + 2.4785, + 2.4808, + 2.4844, + 2.4824, + 2.4835, + 2.4803, + 2.4836, + 2.4796, + 2.4838, + 2.483, + 2.4809, + 2.4782, + 2.4797, + 2.4743, + 2.4736, + 2.4718, + 2.4803, + 2.4753, + 2.4781, + 2.4784, + 2.4717, + 2.4721, + 2.4766, + 2.4774, + 2.4797, + 2.4808, + 2.4749, + 2.4795, + 2.4843, + 2.4884, + 2.49, + 2.4852, + 2.487, + 2.4812, + 2.4794, + 2.486, + 2.4798, + 2.4889, + 2.4895, + 2.4994, + 2.4966, + 2.5108, + 2.5119, + 2.5145, + 2.5063, + 2.5041, + 2.4999, + 2.5099, + 2.5172, + 2.5173, + 2.5148, + 2.5131, + 2.5099, + 2.5114, + 2.5158, + 2.5116, + 2.5125, + 2.513, + 2.5096, + 2.4986, + 2.4999, + 2.5059, + 2.5069, + 2.5069, + 2.5099, + 2.5144, + 2.5075, + 2.5179, + 2.5228, + 2.523, + 2.5239, + 2.5248, + 2.5277, + 2.5286, + 2.541, + 2.5418, + 2.5358, + 2.5372, + 2.5229, + 2.5108, + 2.5003, + 2.5091, + 2.4939, + 2.4966, + 2.5066, + 2.5119, + 2.5129, + 2.5093, + 2.4983, + 2.5029, + 2.4904, + 2.5003, + 2.5009, + 2.4959, + 2.4935, + 2.4939, + 2.4979, + 2.5013, + 2.5004, + 2.5032, + 2.509, + 2.5136, + 2.5272, + 2.5303, + 2.5343, + 2.5326, + 2.5245, + 2.5295, + 2.5414, + 2.5356, + 2.5474, + 2.5377, + 2.5396, + 2.5349, + 2.5267, + 2.5225, + 2.5385, + 2.5674, + 2.5803, + 2.5621, + 2.5595, + 2.5497, + 2.5444, + 2.5358, + 2.5342, + 2.5346, + 2.5281, + 2.5166, + 2.5059, + 2.504, + 2.4948, + 2.5078, + 2.5128, + 2.5012, + 2.4981, + 2.4951, + 2.4978, + 2.5098, + 2.5122, + 2.5049, + 2.5093, + 2.5078, + 2.502, + 2.5069, + 2.5097, + 2.4928, + 2.4893, + 2.4979, + 2.5001, + 2.4793, + 2.4823, + 2.4927, + 2.4935, + 2.493, + 2.4925, + 2.4915, + 2.4948, + 2.499, + 2.5012, + 2.5019, + 2.5053, + 2.501, + 2.505, + 2.5053, + 2.5045, + 2.4998, + 2.5023, + 2.4998, + 2.4983, + 2.5147, + 2.5171, + 2.5168, + 2.5157, + 2.5203, + 2.5201, + 2.5169, + 2.5203, + 2.5213, + 2.5163, + 2.5181, + 2.5179, + 2.5184, + 2.5188, + 2.5132, + 2.5155, + 2.5128, + 2.5155, + 2.5136, + 2.5096, + 2.5103, + 2.5048, + 2.4958, + 2.4978, + 2.4943, + 2.4969, + 2.4876, + 2.4851, + 2.4891, + 2.4893, + 2.4984, + 2.5028, + 2.5015, + 2.4987, + 2.4998, + 2.4994, + 2.4969, + 2.4915, + 2.4895, + 2.4868, + 2.4851, + 2.4803, + 2.48, + 2.4883, + 2.4873, + 2.489, + 2.4918, + 2.4951, + 2.5016, + 2.5072, + 2.5077, + 2.5136, + 2.5082, + 2.5119, + 2.5103, + 2.5087, + 2.5107, + 2.5097, + 2.5083, + 2.5043, + 2.5123, + 2.5179, + 2.5149, + 2.5162, + 2.5147, + 2.5156, + 2.5134, + 2.5113, + 2.5111, + 2.5065, + 2.5045, + 2.5053, + 2.5114, + 2.5112, + 2.5028, + 2.4874, + 2.4949, + 2.5019, + 2.4989, + 2.4979, + 2.5015, + 2.4953, + 2.4971, + 2.4924, + 2.4819, + 2.4785, + 2.4698, + 2.4596, + 2.4471, + 2.4591, + 2.4621, + 2.4654, + 2.4805, + 2.4851, + 2.4831, + 2.4909, + 2.4893, + 2.484, + 2.4938, + 2.5039, + 2.5008, + 2.5144, + 2.5136, + 2.5204, + 2.5287, + 2.5296, + 2.5285, + 2.5808, + 2.5803, + 2.5814, + 2.577, + 2.5672, + 2.5609, + 2.569, + 2.5762, + 2.582, + 2.5764, + 2.5723, + 2.5638, + 2.5616, + 2.5641, + 2.5632, + 2.5696, + 2.5777, + 2.5765, + 2.5771, + 2.5707, + 2.5697, + 2.5655, + 2.5704, + 2.5842, + 2.5915, + 2.5859, + 2.5959, + 2.601, + 2.6175, + 2.6091, + 2.6114, + 2.6056, + 2.5973, + 2.6129, + 2.6196, + 2.6142, + 2.6011, + 2.6143, + 2.606, + 2.6079, + 2.6026, + 2.6034, + 2.6019, + 2.6033, + 2.6009, + 2.6063, + 2.6065, + 2.6048, + 2.6113, + 2.601, + 2.6048, + 2.5882, + 2.5792, + 2.5893, + 2.5812, + 2.5804, + 2.5932, + 2.5937, + 2.5877, + 2.5808, + 2.5898, + 2.5831, + 2.5809, + 2.5772, + 2.5773, + 2.5814, + 2.5768, + 2.5782, + 2.5773, + 2.5757, + 2.5753, + 2.5812, + 2.5866, + 2.5899, + 2.5922, + 2.5962, + 2.5987, + 2.5993, + 2.5999, + 2.5998, + 2.5978, + 2.5927, + 2.5989, + 2.5976, + 2.5874, + 2.585, + 2.5872, + 2.5864, + 2.5956, + 2.5945, + 2.5919, + 2.5871, + 2.5829, + 2.5827, + 2.582, + 2.5818, + 2.5853, + 2.5899, + 2.5873, + 2.5938, + 2.5913, + 2.5896, + 2.5924, + 2.5886, + 2.587, + 2.5912, + 2.6031, + 2.6143, + 2.6115, + 2.6141, + 2.6136, + 2.6164, + 2.6208, + 2.6308, + 2.6331, + 2.6246, + 2.6285, + 2.639, + 2.6373, + 2.6479, + 2.6382, + 2.6723, + 2.658, + 2.6513, + 2.6615, + 2.6694, + 2.6787, + 2.6751, + 2.6759, + 2.678, + 2.6753, + 2.668, + 2.6647, + 2.6687, + 2.656, + 2.6451, + 2.6435, + 2.6398, + 2.6476, + 2.656, + 2.6678, + 2.6445, + 2.6373, + 2.6265, + 2.6419, + 2.6452, + 2.6306, + 2.6323, + 2.631, + 2.6322, + 2.6301, + 2.6211, + 2.6213, + 2.6202, + 2.6226, + 2.6164, + 2.6269, + 2.6195, + 2.604, + 2.6003, + 2.5905, + 2.5861, + 2.5878, + 2.5732, + 2.5701, + 2.5783, + 2.5783, + 2.5824, + 2.5902, + 2.5859, + 2.5885, + 2.5872, + 2.5804, + 2.5836, + 2.5814, + 2.5775, + 2.5769, + 2.567, + 2.5668, + 2.5602, + 2.5513, + 2.5564, + 2.5663, + 2.5631, + 2.551, + 2.5579, + 2.5734, + 2.5684, + 2.5774, + 2.551, + 2.566, + 2.5716, + 2.5844, + 2.5852, + 2.586, + 2.5961, + 2.5982, + 2.5952, + 2.5922, + 2.5832, + 2.5871, + 2.5932, + 2.5881, + 2.586, + 2.585, + 2.5946, + 2.6025, + 2.6167, + 2.6115, + 2.6196, + 2.6174, + 2.6101, + 2.6055, + 2.605, + 2.6123, + 2.6051, + 2.6046, + 2.5986, + 2.6009, + 2.6, + 2.5992, + 2.6002, + 2.6024, + 2.6084, + 2.596, + 2.6053, + 2.61, + 2.6076, + 2.6039, + 2.6027, + 2.5961, + 2.6074, + 2.6093, + 2.6083, + 2.6039, + 2.6093, + 2.6118, + 2.61, + 2.6023, + 2.5973, + 2.5925, + 2.5899, + 2.5876, + 2.5925, + 2.5991, + 2.5937, + 2.5953, + 2.5926, + 2.5974, + 2.6031, + 2.5985, + 2.5952, + 2.5939, + 2.5989, + 2.6005, + 2.6124, + 2.6099, + 2.6113, + 2.6123, + 2.6218, + 2.625, + 2.6237, + 2.6426, + 2.6482, + 2.6445, + 2.6543, + 2.6515, + 2.65, + 2.6519, + 2.6483, + 2.6427, + 2.6529, + 2.6424, + 2.6465, + 2.6513, + 2.6506, + 2.6465, + 2.6403, + 2.6303, + 2.6282, + 2.6215, + 2.6252, + 2.6135, + 2.6185, + 2.627, + 2.633, + 2.6337, + 2.6332, + 2.6289, + 2.6236, + 2.6302, + 2.6363, + 2.6309, + 2.6286, + 2.6377, + 2.6404, + 2.6447, + 2.6436, + 2.6449, + 2.6367, + 2.6327, + 2.6325, + 2.6388, + 2.6441, + 2.6398, + 2.6405, + 2.6419, + 2.6422, + 2.648, + 2.6449, + 2.6387, + 2.6312, + 2.6381, + 2.6385, + 2.6369, + 2.6388, + 2.6507, + 2.6482, + 2.6495, + 2.6622, + 2.6531, + 2.6386, + 2.6181, + 2.6268, + 2.6287, + 2.6335, + 2.6378, + 2.6322, + 2.6369, + 2.6351, + 2.6338, + 2.6281, + 2.6332, + 2.6302, + 2.6289, + 2.6328, + 2.6275, + 2.6433, + 2.6435, + 2.6472, + 2.6373, + 2.6432, + 2.6481, + 2.6514, + 2.6544, + 2.6581, + 2.6536, + 2.662, + 2.6628, + 2.6583, + 2.6586, + 2.6663, + 2.6725, + 2.6717, + 2.6712, + 2.6777, + 2.6847, + 2.6872, + 2.6838, + 2.6826, + 2.6823, + 2.6865, + 2.6822, + 2.6742, + 2.6761, + 2.6701, + 2.6697, + 2.6675, + 2.669, + 2.6698, + 2.6647, + 2.6724, + 2.6644, + 2.6565, + 2.6566, + 2.6521, + 2.6522, + 2.66, + 2.6572, + 2.6569, + 2.6612, + 2.6575, + 2.6573, + 2.6491, + 2.6471, + 2.6487, + 2.6547, + 2.655, + 2.6558, + 2.655, + 2.6497, + 2.647, + 2.6472, + 2.6452, + 2.6419, + 2.6401, + 2.6413, + 2.6398, + 2.6503, + 2.6532, + 2.6467, + 2.6509, + 2.6527, + 2.6737, + 2.6695, + 2.6605, + 2.6697, + 2.6733, + 2.6756, + 2.6816, + 2.6739, + 2.6895, + 2.6943, + 2.6915, + 2.6983, + 2.6863, + 2.6936, + 2.6943, + 2.6814, + 2.6792, + 2.6716, + 2.6735, + 2.6781, + 2.6986, + 2.7076, + 2.7203, + 2.7193, + 2.724, + 2.7314, + 2.7385, + 2.7274, + 2.7239, + 2.7272, + 2.7232, + 2.7175, + 2.717, + 2.7218, + 2.7249, + 2.7223, + 2.7238, + 2.7091, + 2.7075, + 2.719, + 2.7192, + 2.7204, + 2.7144, + 2.7256, + 2.7112, + 2.7131, + 2.7181, + 2.7066, + 2.7064, + 2.7057, + 2.7039, + 2.6999, + 2.681, + 2.684, + 2.6774, + 2.6832, + 2.6755, + 2.6678, + 2.6691, + 2.6746, + 2.6722, + 2.6782, + 2.682, + 2.6805, + 2.6852, + 2.6869, + 2.6914, + 2.6922, + 2.697, + 2.6993, + 2.6945, + 2.6903, + 2.679, + 2.6843, + 2.6794, + 2.684, + 2.6756, + 2.6825, + 2.6814, + 2.6793, + 2.6793, + 2.6752, + 2.6756, + 2.6662, + 2.6645, + 2.6506, + 2.648, + 2.6327, + 2.6334, + 2.6318, + 2.6346, + 2.6444, + 2.643, + 2.6436, + 2.6608, + 2.6514, + 2.6523, + 2.6616, + 2.6486, + 2.6562, + 2.6624, + 2.676, + 2.6909, + 2.6901, + 2.6917, + 2.6876, + 2.6851, + 2.6687, + 2.6701, + 2.6648, + 2.5854, + 2.4879, + 2.5071, + 2.5215, + 2.59, + 2.5567, + 2.5919, + 2.5623, + 2.5711, + 2.5495, + 2.5367, + 2.5391, + 2.5227, + 2.5634, + 2.5715, + 2.565, + 2.5723, + 2.5725, + 2.5884, + 2.5951, + 2.6234, + 2.6305, + 2.6324, + 2.6307, + 2.6389, + 2.6478, + 2.6494, + 2.653, + 2.6441, + 2.6549, + 2.6439, + 2.6462, + 2.6439, + 2.64, + 2.6336, + 2.633, + 2.5974, + 2.6061, + 2.5998, + 2.6146, + 2.6024, + 2.594, + 2.5672, + 2.5537, + 2.5622, + 2.5413, + 2.5513, + 2.5434, + 2.5472, + 2.5503, + 2.5455, + 2.533, + 2.5293, + 2.5368, + 2.5411, + 2.5348, + 2.5277, + 2.5235, + 2.5373, + 2.5365, + 2.5375, + 2.5515, + 2.5529, + 2.5416, + 2.5286, + 2.5004, + 2.4936, + 2.4859, + 2.4999, + 2.4881, + 2.4944, + 2.4861, + 2.4908, + 2.5024, + 2.496, + 2.4992, + 2.5059, + 2.4914, + 2.495, + 2.4954, + 2.4862, + 2.4801, + 2.4765, + 2.4671, + 2.4527, + 2.4591, + 2.4628, + 2.451, + 2.4518, + 2.4406, + 2.4353, + 2.4393, + 2.431, + 2.4452, + 2.4569, + 2.4655, + 2.4659, + 2.4708, + 2.4787, + 2.4815, + 2.4741, + 2.4784, + 2.4768, + 2.4581, + 2.4566, + 2.4534, + 2.4532, + 2.4552, + 2.4505, + 2.4522, + 2.4515, + 2.4489, + 2.4566, + 2.4515, + 2.4472, + 2.452, + 2.4583, + 2.4558, + 2.4739, + 2.463, + 2.4676, + 2.4854, + 2.4876, + 2.4755, + 2.476, + 2.486, + 2.49, + 2.4927, + 2.4996, + 2.4996, + 2.4964, + 2.4939, + 2.5015, + 2.498, + 2.498, + 2.5097, + 2.5193, + 2.5107, + 2.5146, + 2.5223, + 2.5225, + 2.5304, + 2.5274, + 2.525, + 2.5246, + 2.5198, + 2.5006, + 2.4969, + 2.5045, + 2.5021, + 2.5126, + 2.509, + 2.5018, + 2.5044, + 2.5055, + 2.5055, + 2.5051, + 2.5005, + 2.5048, + 2.5146, + 2.5132, + 2.5056, + 2.5073, + 2.5085, + 2.5074, + 2.5137, + 2.5182, + 2.5205, + 2.5159, + 2.5153, + 2.5095, + 2.5079, + 2.5131, + 2.5099, + 2.5103, + 2.5049, + 2.5021, + 2.5002, + 2.5072, + 2.5141, + 2.516, + 2.5176, + 2.5163, + 2.5104, + 2.5057, + 2.4984, + 2.4998, + 2.494, + 2.5028, + 2.4986, + 2.5018, + 2.504, + 2.5021, + 2.5045, + 2.5039, + 2.5031, + 2.51, + 2.5077, + 2.4994, + 2.5067, + 2.5087, + 2.51, + 2.5117, + 2.5063, + 2.5023, + 2.4996, + 2.5019, + 2.4974, + 2.4928, + 2.4954, + 2.4896, + 2.4838, + 2.4885, + 2.4829, + 2.4762, + 2.4802, + 2.4868, + 2.484, + 2.4888, + 2.4885, + 2.4865, + 2.489, + 2.4859, + 2.4871, + 2.478, + 2.4741, + 2.4727, + 2.4683, + 2.4627, + 2.4569, + 2.4617, + 2.4606, + 2.4564, + 2.4486, + 2.4649, + 2.4631, + 2.4613, + 2.4677, + 2.4642, + 2.4639, + 2.4619, + 2.4636, + 2.4565, + 2.4613, + 2.4577, + 2.4472, + 2.4429, + 2.4368, + 2.4416, + 2.4565, + 2.4445, + 2.4452, + 2.4582, + 2.4456, + 2.4501, + 2.4548, + 2.4478, + 2.4467, + 2.4413, + 2.4414, + 2.4487, + 2.4569, + 2.4699, + 2.4685, + 2.471, + 2.4591, + 2.4689, + 2.4676, + 2.4589, + 2.461, + 2.4643, + 2.4563, + 2.4682, + 2.4718, + 2.4655, + 2.4613, + 2.4663, + 2.4591, + 2.4553, + 2.4447, + 2.4578, + 2.4519, + 2.4517, + 2.458, + 2.4472, + 2.4609, + 2.4623, + 2.4504, + 2.4503, + 2.4614, + 2.4544, + 2.4529, + 2.4484, + 2.4448, + 2.4481, + 2.456, + 2.4624, + 2.4605, + 2.4567, + 2.448, + 2.4469, + 2.4473, + 2.4388, + 2.4306, + 2.4352, + 2.4345, + 2.4361, + 2.4279, + 2.4197, + 2.4298, + 2.4245, + 2.4269, + 2.4264, + 2.4245, + 2.4156, + 2.4253, + 2.4199, + 2.4181, + 2.4175, + 2.4166, + 2.4122, + 2.3999, + 2.41, + 2.4094, + 2.4043, + 2.3947, + 2.3824, + 2.3974, + 2.3938, + 2.4004, + 2.3993, + 2.3945, + 2.4094, + 2.4087, + 2.413, + 2.4069, + 2.4131, + 2.4146, + 2.4102, + 2.4034, + 2.4023, + 2.3996, + 2.3952, + 2.3919, + 2.3894, + 2.3835, + 2.3835, + 2.3903, + 2.3837, + 2.3799, + 2.3755, + 2.3855, + 2.3968, + 2.4013, + 2.397, + 2.4026, + 2.4064, + 2.4086, + 2.4079, + 2.4002, + 2.3987, + 2.4007, + 2.3968, + 2.4052, + 2.399, + 2.398, + 2.405, + 2.3995, + 2.3991, + 2.4014, + 2.3963, + 2.4027, + 2.4101, + 2.4102, + 2.4093, + 2.4068, + 2.4113, + 2.4146, + 2.4145, + 2.4215, + 2.4256, + 2.4249, + 2.4323, + 2.4227, + 2.4238, + 2.4275, + 2.4299, + 2.4314, + 2.4291, + 2.4282, + 2.4313, + 2.4337, + 2.4321, + 2.4341, + 2.4352, + 2.4344 + ], + "x": [ + "2023-12-04T15:00:00", + "2023-12-04T15:03:00", + "2023-12-04T15:06:00", + "2023-12-04T15:09:00", + "2023-12-04T15:12:00", + "2023-12-04T15:15:00", + "2023-12-04T15:18:00", + "2023-12-04T15:21:00", + "2023-12-04T15:24:00", + "2023-12-04T15:27:00", + "2023-12-04T15:30:00", + "2023-12-04T15:33:00", + "2023-12-04T15:36:00", + "2023-12-04T15:39:00", + "2023-12-04T15:42:00", + "2023-12-04T15:45:00", + "2023-12-04T15:48:00", + "2023-12-04T15:51:00", + "2023-12-04T15:54:00", + "2023-12-04T15:57:00", + "2023-12-04T16:00:00", + "2023-12-04T16:03:00", + "2023-12-04T16:06:00", + "2023-12-04T16:09:00", + "2023-12-04T16:12:00", + "2023-12-04T16:15:00", + "2023-12-04T16:18:00", + "2023-12-04T16:21:00", + "2023-12-04T16:24:00", + "2023-12-04T16:27:00", + "2023-12-04T16:30:00", + "2023-12-04T16:33:00", + "2023-12-04T16:36:00", + "2023-12-04T16:39:00", + "2023-12-04T16:42:00", + "2023-12-04T16:45:00", + "2023-12-04T16:48:00", + "2023-12-04T16:51:00", + "2023-12-04T16:54:00", + "2023-12-04T16:57:00", + "2023-12-04T17:00:00", + "2023-12-04T17:03:00", + "2023-12-04T17:06:00", + "2023-12-04T17:09:00", + "2023-12-04T17:12:00", + "2023-12-04T17:15:00", + "2023-12-04T17:18:00", + "2023-12-04T17:21:00", + "2023-12-04T17:24:00", + "2023-12-04T17:27:00", + "2023-12-04T17:30:00", + "2023-12-04T17:33:00", + "2023-12-04T17:36:00", + "2023-12-04T17:39:00", + "2023-12-04T17:42:00", + "2023-12-04T17:45:00", + "2023-12-04T17:48:00", + "2023-12-04T17:51:00", + "2023-12-04T17:54:00", + "2023-12-04T17:57:00", + "2023-12-04T18:00:00", + "2023-12-04T18:03:00", + "2023-12-04T18:06:00", + "2023-12-04T18:09:00", + "2023-12-04T18:12:00", + "2023-12-04T18:15:00", + "2023-12-04T18:18:00", + "2023-12-04T18:21:00", + "2023-12-04T18:24:00", + "2023-12-04T18:27:00", + "2023-12-04T18:30:00", + "2023-12-04T18:33:00", + "2023-12-04T18:36:00", + "2023-12-04T18:39:00", + "2023-12-04T18:42:00", + "2023-12-04T18:45:00", + "2023-12-04T18:48:00", + "2023-12-04T18:51:00", + "2023-12-04T18:54:00", + "2023-12-04T18:57:00", + "2023-12-04T19:00:00", + "2023-12-04T19:03:00", + "2023-12-04T19:06:00", + "2023-12-04T19:09:00", + "2023-12-04T19:12:00", + "2023-12-04T19:15:00", + "2023-12-04T19:18:00", + "2023-12-04T19:21:00", + "2023-12-04T19:24:00", + "2023-12-04T19:27:00", + "2023-12-04T19:30:00", + "2023-12-04T19:33:00", + "2023-12-04T19:36:00", + "2023-12-04T19:39:00", + "2023-12-04T19:42:00", + "2023-12-04T19:45:00", + "2023-12-04T19:48:00", + "2023-12-04T19:51:00", + "2023-12-04T19:54:00", + "2023-12-04T19:57:00", + "2023-12-04T20:00:00", + "2023-12-04T20:03:00", + "2023-12-04T20:06:00", + "2023-12-04T20:09:00", + "2023-12-04T20:12:00", + "2023-12-04T20:15:00", + "2023-12-04T20:18:00", + "2023-12-04T20:21:00", + "2023-12-04T20:24:00", + "2023-12-04T20:27:00", + "2023-12-04T20:30:00", + "2023-12-04T20:33:00", + "2023-12-04T20:36:00", + "2023-12-04T20:39:00", + "2023-12-04T20:42:00", + "2023-12-04T20:45:00", + "2023-12-04T20:48:00", + "2023-12-04T20:51:00", + "2023-12-04T20:54:00", + "2023-12-04T20:57:00", + "2023-12-04T21:00:00", + "2023-12-04T21:03:00", + "2023-12-04T21:06:00", + "2023-12-04T21:09:00", + "2023-12-04T21:12:00", + "2023-12-04T21:15:00", + "2023-12-04T21:18:00", + "2023-12-04T21:21:00", + "2023-12-04T21:24:00", + "2023-12-04T21:27:00", + "2023-12-04T21:30:00", + "2023-12-04T21:33:00", + "2023-12-04T21:36:00", + "2023-12-04T21:39:00", + "2023-12-04T21:42:00", + "2023-12-04T21:45:00", + "2023-12-04T21:48:00", + "2023-12-04T21:51:00", + "2023-12-04T21:54:00", + "2023-12-04T21:57:00", + "2023-12-04T22:00:00", + "2023-12-04T22:03:00", + "2023-12-04T22:06:00", + "2023-12-04T22:09:00", + "2023-12-04T22:12:00", + "2023-12-04T22:15:00", + "2023-12-04T22:18:00", + "2023-12-04T22:21:00", + "2023-12-04T22:24:00", + "2023-12-04T22:27:00", + "2023-12-04T22:30:00", + "2023-12-04T22:33:00", + "2023-12-04T22:36:00", + "2023-12-04T22:39:00", + "2023-12-04T22:42:00", + "2023-12-04T22:45:00", + "2023-12-04T22:48:00", + "2023-12-04T22:51:00", + "2023-12-04T22:54:00", + "2023-12-04T22:57:00", + "2023-12-04T23:00:00", + "2023-12-04T23:03:00", + "2023-12-04T23:06:00", + "2023-12-04T23:09:00", + "2023-12-04T23:12:00", + "2023-12-04T23:15:00", + "2023-12-04T23:18:00", + "2023-12-04T23:21:00", + "2023-12-04T23:24:00", + "2023-12-04T23:27:00", + "2023-12-04T23:30:00", + "2023-12-04T23:33:00", + "2023-12-04T23:36:00", + "2023-12-04T23:39:00", + "2023-12-04T23:42:00", + "2023-12-04T23:45:00", + "2023-12-04T23:48:00", + "2023-12-04T23:51:00", + "2023-12-04T23:54:00", + "2023-12-04T23:57:00", + "2023-12-05T00:00:00", + "2023-12-05T00:03:00", + "2023-12-05T00:06:00", + "2023-12-05T00:09:00", + "2023-12-05T00:12:00", + "2023-12-05T00:15:00", + "2023-12-05T00:18:00", + "2023-12-05T00:21:00", + "2023-12-05T00:24:00", + "2023-12-05T00:27:00", + "2023-12-05T00:30:00", + "2023-12-05T00:33:00", + "2023-12-05T00:36:00", + "2023-12-05T00:39:00", + "2023-12-05T00:42:00", + "2023-12-05T00:45:00", + "2023-12-05T00:48:00", + "2023-12-05T00:51:00", + "2023-12-05T00:54:00", + "2023-12-05T00:57:00", + "2023-12-05T01:00:00", + "2023-12-05T01:03:00", + "2023-12-05T01:06:00", + "2023-12-05T01:09:00", + "2023-12-05T01:12:00", + "2023-12-05T01:15:00", + "2023-12-05T01:18:00", + "2023-12-05T01:21:00", + "2023-12-05T01:24:00", + "2023-12-05T01:27:00", + "2023-12-05T01:30:00", + "2023-12-05T01:33:00", + "2023-12-05T01:36:00", + "2023-12-05T01:39:00", + "2023-12-05T01:42:00", + "2023-12-05T01:45:00", + "2023-12-05T01:48:00", + "2023-12-05T01:51:00", + "2023-12-05T01:54:00", + "2023-12-05T01:57:00", + "2023-12-05T02:00:00", + "2023-12-05T02:03:00", + "2023-12-05T02:06:00", + "2023-12-05T02:09:00", + "2023-12-05T02:12:00", + "2023-12-05T02:15:00", + "2023-12-05T02:18:00", + "2023-12-05T02:21:00", + "2023-12-05T02:24:00", + "2023-12-05T02:27:00", + "2023-12-05T02:30:00", + "2023-12-05T02:33:00", + "2023-12-05T02:36:00", + "2023-12-05T02:39:00", + "2023-12-05T02:42:00", + "2023-12-05T02:45:00", + "2023-12-05T02:48:00", + "2023-12-05T02:51:00", + "2023-12-05T02:54:00", + "2023-12-05T02:57:00", + "2023-12-05T03:00:00", + "2023-12-05T03:03:00", + "2023-12-05T03:06:00", + "2023-12-05T03:09:00", + "2023-12-05T03:12:00", + "2023-12-05T03:15:00", + "2023-12-05T03:18:00", + "2023-12-05T03:21:00", + "2023-12-05T03:24:00", + "2023-12-05T03:27:00", + "2023-12-05T03:30:00", + "2023-12-05T03:33:00", + "2023-12-05T03:36:00", + "2023-12-05T03:39:00", + "2023-12-05T03:42:00", + "2023-12-05T03:45:00", + "2023-12-05T03:48:00", + "2023-12-05T03:51:00", + "2023-12-05T03:54:00", + "2023-12-05T03:57:00", + "2023-12-05T04:00:00", + "2023-12-05T04:03:00", + "2023-12-05T04:06:00", + "2023-12-05T04:09:00", + "2023-12-05T04:12:00", + "2023-12-05T04:15:00", + "2023-12-05T04:18:00", + "2023-12-05T04:21:00", + "2023-12-05T04:24:00", + "2023-12-05T04:27:00", + "2023-12-05T04:30:00", + "2023-12-05T04:33:00", + "2023-12-05T04:36:00", + "2023-12-05T04:39:00", + "2023-12-05T04:42:00", + "2023-12-05T04:45:00", + "2023-12-05T04:48:00", + "2023-12-05T04:51:00", + "2023-12-05T04:54:00", + "2023-12-05T04:57:00", + "2023-12-05T05:00:00", + "2023-12-05T05:03:00", + "2023-12-05T05:06:00", + "2023-12-05T05:09:00", + "2023-12-05T05:12:00", + "2023-12-05T05:15:00", + "2023-12-05T05:18:00", + "2023-12-05T05:21:00", + "2023-12-05T05:24:00", + "2023-12-05T05:27:00", + "2023-12-05T05:30:00", + "2023-12-05T05:33:00", + "2023-12-05T05:36:00", + "2023-12-05T05:39:00", + "2023-12-05T05:42:00", + "2023-12-05T05:45:00", + "2023-12-05T05:48:00", + "2023-12-05T05:51:00", + "2023-12-05T05:54:00", + "2023-12-05T05:57:00", + "2023-12-05T06:00:00", + "2023-12-05T06:03:00", + "2023-12-05T06:06:00", + "2023-12-05T06:09:00", + "2023-12-05T06:12:00", + "2023-12-05T06:15:00", + "2023-12-05T06:18:00", + "2023-12-05T06:21:00", + "2023-12-05T06:24:00", + "2023-12-05T06:27:00", + "2023-12-05T06:30:00", + "2023-12-05T06:33:00", + "2023-12-05T06:36:00", + "2023-12-05T06:39:00", + "2023-12-05T06:42:00", + "2023-12-05T06:45:00", + "2023-12-05T06:48:00", + "2023-12-05T06:51:00", + "2023-12-05T06:54:00", + "2023-12-05T06:57:00", + "2023-12-05T07:00:00", + "2023-12-05T07:03:00", + "2023-12-05T07:06:00", + "2023-12-05T07:09:00", + "2023-12-05T07:12:00", + "2023-12-05T07:15:00", + "2023-12-05T07:18:00", + "2023-12-05T07:21:00", + "2023-12-05T07:24:00", + "2023-12-05T07:27:00", + "2023-12-05T07:30:00", + "2023-12-05T07:33:00", + "2023-12-05T07:36:00", + "2023-12-05T07:39:00", + "2023-12-05T07:42:00", + "2023-12-05T07:45:00", + "2023-12-05T07:48:00", + "2023-12-05T07:51:00", + "2023-12-05T07:54:00", + "2023-12-05T07:57:00", + "2023-12-05T08:00:00", + "2023-12-05T08:03:00", + "2023-12-05T08:06:00", + "2023-12-05T08:09:00", + "2023-12-05T08:12:00", + "2023-12-05T08:15:00", + "2023-12-05T08:18:00", + "2023-12-05T08:21:00", + "2023-12-05T08:24:00", + "2023-12-05T08:27:00", + "2023-12-05T08:30:00", + "2023-12-05T08:33:00", + "2023-12-05T08:36:00", + "2023-12-05T08:39:00", + "2023-12-05T08:42:00", + "2023-12-05T08:45:00", + "2023-12-05T08:48:00", + "2023-12-05T08:51:00", + "2023-12-05T08:54:00", + "2023-12-05T08:57:00", + "2023-12-05T09:00:00", + "2023-12-05T09:03:00", + "2023-12-05T09:06:00", + "2023-12-05T09:09:00", + "2023-12-05T09:12:00", + "2023-12-05T09:15:00", + "2023-12-05T09:18:00", + "2023-12-05T09:21:00", + "2023-12-05T09:24:00", + "2023-12-05T09:27:00", + "2023-12-05T09:30:00", + "2023-12-05T09:33:00", + "2023-12-05T09:36:00", + "2023-12-05T09:39:00", + "2023-12-05T09:42:00", + "2023-12-05T09:45:00", + "2023-12-05T09:48:00", + "2023-12-05T09:51:00", + "2023-12-05T09:54:00", + "2023-12-05T09:57:00", + "2023-12-05T10:00:00", + "2023-12-05T10:03:00", + "2023-12-05T10:06:00", + "2023-12-05T10:09:00", + "2023-12-05T10:12:00", + "2023-12-05T10:15:00", + "2023-12-05T10:18:00", + "2023-12-05T10:21:00", + "2023-12-05T10:24:00", + "2023-12-05T10:27:00", + "2023-12-05T10:30:00", + "2023-12-05T10:33:00", + "2023-12-05T10:36:00", + "2023-12-05T10:39:00", + "2023-12-05T10:42:00", + "2023-12-05T10:45:00", + "2023-12-05T10:48:00", + "2023-12-05T10:51:00", + "2023-12-05T10:54:00", + "2023-12-05T10:57:00", + "2023-12-05T11:00:00", + "2023-12-05T11:03:00", + "2023-12-05T11:06:00", + "2023-12-05T11:09:00", + "2023-12-05T11:12:00", + "2023-12-05T11:15:00", + "2023-12-05T11:18:00", + "2023-12-05T11:21:00", + "2023-12-05T11:24:00", + "2023-12-05T11:27:00", + "2023-12-05T11:30:00", + "2023-12-05T11:33:00", + "2023-12-05T11:36:00", + "2023-12-05T11:39:00", + "2023-12-05T11:42:00", + "2023-12-05T11:45:00", + "2023-12-05T11:48:00", + "2023-12-05T11:51:00", + "2023-12-05T11:54:00", + "2023-12-05T11:57:00", + "2023-12-05T12:00:00", + "2023-12-05T12:03:00", + "2023-12-05T12:06:00", + "2023-12-05T12:09:00", + "2023-12-05T12:12:00", + "2023-12-05T12:15:00", + "2023-12-05T12:18:00", + "2023-12-05T12:21:00", + "2023-12-05T12:24:00", + "2023-12-05T12:27:00", + "2023-12-05T12:30:00", + "2023-12-05T12:33:00", + "2023-12-05T12:36:00", + "2023-12-05T12:39:00", + "2023-12-05T12:42:00", + "2023-12-05T12:45:00", + "2023-12-05T12:48:00", + "2023-12-05T12:51:00", + "2023-12-05T12:54:00", + "2023-12-05T12:57:00", + "2023-12-05T13:00:00", + "2023-12-05T13:03:00", + "2023-12-05T13:06:00", + "2023-12-05T13:09:00", + "2023-12-05T13:12:00", + "2023-12-05T13:15:00", + "2023-12-05T13:18:00", + "2023-12-05T13:21:00", + "2023-12-05T13:24:00", + "2023-12-05T13:27:00", + "2023-12-05T13:30:00", + "2023-12-05T13:33:00", + "2023-12-05T13:36:00", + "2023-12-05T13:39:00", + "2023-12-05T13:42:00", + "2023-12-05T13:45:00", + "2023-12-05T13:48:00", + "2023-12-05T13:51:00", + "2023-12-05T13:54:00", + "2023-12-05T13:57:00", + "2023-12-05T14:00:00", + "2023-12-05T14:03:00", + "2023-12-05T14:06:00", + "2023-12-05T14:09:00", + "2023-12-05T14:12:00", + "2023-12-05T14:15:00", + "2023-12-05T14:18:00", + "2023-12-05T14:21:00", + "2023-12-05T14:24:00", + "2023-12-05T14:27:00", + "2023-12-05T14:30:00", + "2023-12-05T14:33:00", + "2023-12-05T14:36:00", + "2023-12-05T14:39:00", + "2023-12-05T14:42:00", + "2023-12-05T14:45:00", + "2023-12-05T14:48:00", + "2023-12-05T14:51:00", + "2023-12-05T14:54:00", + "2023-12-05T14:57:00", + "2023-12-05T15:00:00", + "2023-12-05T15:03:00", + "2023-12-05T15:06:00", + "2023-12-05T15:09:00", + "2023-12-05T15:12:00", + "2023-12-05T15:15:00", + "2023-12-05T15:18:00", + "2023-12-05T15:21:00", + "2023-12-05T15:24:00", + "2023-12-05T15:27:00", + "2023-12-05T15:30:00", + "2023-12-05T15:33:00", + "2023-12-05T15:36:00", + "2023-12-05T15:39:00", + "2023-12-05T15:42:00", + "2023-12-05T15:45:00", + "2023-12-05T15:48:00", + "2023-12-05T15:51:00", + "2023-12-05T15:54:00", + "2023-12-05T15:57:00", + "2023-12-05T16:00:00", + "2023-12-05T16:03:00", + "2023-12-05T16:06:00", + "2023-12-05T16:09:00", + "2023-12-05T16:12:00", + "2023-12-05T16:15:00", + "2023-12-05T16:18:00", + "2023-12-05T16:21:00", + "2023-12-05T16:24:00", + "2023-12-05T16:27:00", + "2023-12-05T16:30:00", + "2023-12-05T16:33:00", + "2023-12-05T16:36:00", + "2023-12-05T16:39:00", + "2023-12-05T16:42:00", + "2023-12-05T16:45:00", + "2023-12-05T16:48:00", + "2023-12-05T16:51:00", + "2023-12-05T16:54:00", + "2023-12-05T16:57:00", + "2023-12-05T17:00:00", + "2023-12-05T17:03:00", + "2023-12-05T17:06:00", + "2023-12-05T17:09:00", + "2023-12-05T17:12:00", + "2023-12-05T17:15:00", + "2023-12-05T17:18:00", + "2023-12-05T17:21:00", + "2023-12-05T17:24:00", + "2023-12-05T17:27:00", + "2023-12-05T17:30:00", + "2023-12-05T17:33:00", + "2023-12-05T17:36:00", + "2023-12-05T17:39:00", + "2023-12-05T17:42:00", + "2023-12-05T17:45:00", + "2023-12-05T17:48:00", + "2023-12-05T17:51:00", + "2023-12-05T17:54:00", + "2023-12-05T17:57:00", + "2023-12-05T18:00:00", + "2023-12-05T18:03:00", + "2023-12-05T18:06:00", + "2023-12-05T18:09:00", + "2023-12-05T18:12:00", + "2023-12-05T18:15:00", + "2023-12-05T18:18:00", + "2023-12-05T18:21:00", + "2023-12-05T18:24:00", + "2023-12-05T18:27:00", + "2023-12-05T18:30:00", + "2023-12-05T18:33:00", + "2023-12-05T18:36:00", + "2023-12-05T18:39:00", + "2023-12-05T18:42:00", + "2023-12-05T18:45:00", + "2023-12-05T18:48:00", + "2023-12-05T18:51:00", + "2023-12-05T18:54:00", + "2023-12-05T18:57:00", + "2023-12-05T19:00:00", + "2023-12-05T19:03:00", + "2023-12-05T19:06:00", + "2023-12-05T19:09:00", + "2023-12-05T19:12:00", + "2023-12-05T19:15:00", + "2023-12-05T19:18:00", + "2023-12-05T19:21:00", + "2023-12-05T19:24:00", + "2023-12-05T19:27:00", + "2023-12-05T19:30:00", + "2023-12-05T19:33:00", + "2023-12-05T19:36:00", + "2023-12-05T19:39:00", + "2023-12-05T19:42:00", + "2023-12-05T19:45:00", + "2023-12-05T19:48:00", + "2023-12-05T19:51:00", + "2023-12-05T19:54:00", + "2023-12-05T19:57:00", + "2023-12-05T20:00:00", + "2023-12-05T20:03:00", + "2023-12-05T20:06:00", + "2023-12-05T20:09:00", + "2023-12-05T20:12:00", + "2023-12-05T20:15:00", + "2023-12-05T20:18:00", + "2023-12-05T20:21:00", + "2023-12-05T20:24:00", + "2023-12-05T20:27:00", + "2023-12-05T20:30:00", + "2023-12-05T20:33:00", + "2023-12-05T20:36:00", + "2023-12-05T20:39:00", + "2023-12-05T20:42:00", + "2023-12-05T20:45:00", + "2023-12-05T20:48:00", + "2023-12-05T20:51:00", + "2023-12-05T20:54:00", + "2023-12-05T20:57:00", + "2023-12-05T21:00:00", + "2023-12-05T21:03:00", + "2023-12-05T21:06:00", + "2023-12-05T21:09:00", + "2023-12-05T21:12:00", + "2023-12-05T21:15:00", + "2023-12-05T21:18:00", + "2023-12-05T21:21:00", + "2023-12-05T21:24:00", + "2023-12-05T21:27:00", + "2023-12-05T21:30:00", + "2023-12-05T21:33:00", + "2023-12-05T21:36:00", + "2023-12-05T21:39:00", + "2023-12-05T21:42:00", + "2023-12-05T21:45:00", + "2023-12-05T21:48:00", + "2023-12-05T21:51:00", + "2023-12-05T21:54:00", + "2023-12-05T21:57:00", + "2023-12-05T22:00:00", + "2023-12-05T22:03:00", + "2023-12-05T22:06:00", + "2023-12-05T22:09:00", + "2023-12-05T22:12:00", + "2023-12-05T22:15:00", + "2023-12-05T22:18:00", + "2023-12-05T22:21:00", + "2023-12-05T22:24:00", + "2023-12-05T22:27:00", + "2023-12-05T22:30:00", + "2023-12-05T22:33:00", + "2023-12-05T22:36:00", + "2023-12-05T22:39:00", + "2023-12-05T22:42:00", + "2023-12-05T22:45:00", + "2023-12-05T22:48:00", + "2023-12-05T22:51:00", + "2023-12-05T22:54:00", + "2023-12-05T22:57:00", + "2023-12-05T23:00:00", + "2023-12-05T23:03:00", + "2023-12-05T23:06:00", + "2023-12-05T23:09:00", + "2023-12-05T23:12:00", + "2023-12-05T23:15:00", + "2023-12-05T23:18:00", + "2023-12-05T23:21:00", + "2023-12-05T23:24:00", + "2023-12-05T23:27:00", + "2023-12-05T23:30:00", + "2023-12-05T23:33:00", + "2023-12-05T23:36:00", + "2023-12-05T23:39:00", + "2023-12-05T23:42:00", + "2023-12-05T23:45:00", + "2023-12-05T23:48:00", + "2023-12-05T23:51:00", + "2023-12-05T23:54:00", + "2023-12-05T23:57:00", + "2023-12-06T00:00:00", + "2023-12-06T00:03:00", + "2023-12-06T00:06:00", + "2023-12-06T00:09:00", + "2023-12-06T00:12:00", + "2023-12-06T00:15:00", + "2023-12-06T00:18:00", + "2023-12-06T00:21:00", + "2023-12-06T00:24:00", + "2023-12-06T00:27:00", + "2023-12-06T00:30:00", + "2023-12-06T00:33:00", + "2023-12-06T00:36:00", + "2023-12-06T00:39:00", + "2023-12-06T00:42:00", + "2023-12-06T00:45:00", + "2023-12-06T00:48:00", + "2023-12-06T00:51:00", + "2023-12-06T00:54:00", + "2023-12-06T00:57:00", + "2023-12-06T01:00:00", + "2023-12-06T01:03:00", + "2023-12-06T01:06:00", + "2023-12-06T01:09:00", + "2023-12-06T01:12:00", + "2023-12-06T01:15:00", + "2023-12-06T01:18:00", + "2023-12-06T01:21:00", + "2023-12-06T01:24:00", + "2023-12-06T01:27:00", + "2023-12-06T01:30:00", + "2023-12-06T01:33:00", + "2023-12-06T01:36:00", + "2023-12-06T01:39:00", + "2023-12-06T01:42:00", + "2023-12-06T01:45:00", + "2023-12-06T01:48:00", + "2023-12-06T01:51:00", + "2023-12-06T01:54:00", + "2023-12-06T01:57:00", + "2023-12-06T02:00:00", + "2023-12-06T02:03:00", + "2023-12-06T02:06:00", + "2023-12-06T02:09:00", + "2023-12-06T02:12:00", + "2023-12-06T02:15:00", + "2023-12-06T02:18:00", + "2023-12-06T02:21:00", + "2023-12-06T02:24:00", + "2023-12-06T02:27:00", + "2023-12-06T02:30:00", + "2023-12-06T02:33:00", + "2023-12-06T02:36:00", + "2023-12-06T02:39:00", + "2023-12-06T02:42:00", + "2023-12-06T02:45:00", + "2023-12-06T02:48:00", + "2023-12-06T02:51:00", + "2023-12-06T02:54:00", + "2023-12-06T02:57:00", + "2023-12-06T03:00:00", + "2023-12-06T03:03:00", + "2023-12-06T03:06:00", + "2023-12-06T03:09:00", + "2023-12-06T03:12:00", + "2023-12-06T03:15:00", + "2023-12-06T03:18:00", + "2023-12-06T03:21:00", + "2023-12-06T03:24:00", + "2023-12-06T03:27:00", + "2023-12-06T03:30:00", + "2023-12-06T03:33:00", + "2023-12-06T03:36:00", + "2023-12-06T03:39:00", + "2023-12-06T03:42:00", + "2023-12-06T03:45:00", + "2023-12-06T03:48:00", + "2023-12-06T03:51:00", + "2023-12-06T03:54:00", + "2023-12-06T03:57:00", + "2023-12-06T04:00:00", + "2023-12-06T04:03:00", + "2023-12-06T04:06:00", + "2023-12-06T04:09:00", + "2023-12-06T04:12:00", + "2023-12-06T04:15:00", + "2023-12-06T04:18:00", + "2023-12-06T04:21:00", + "2023-12-06T04:24:00", + "2023-12-06T04:27:00", + "2023-12-06T04:30:00", + "2023-12-06T04:33:00", + "2023-12-06T04:36:00", + "2023-12-06T04:39:00", + "2023-12-06T04:42:00", + "2023-12-06T04:45:00", + "2023-12-06T04:48:00", + "2023-12-06T04:51:00", + "2023-12-06T04:54:00", + "2023-12-06T04:57:00", + "2023-12-06T05:00:00", + "2023-12-06T05:03:00", + "2023-12-06T05:06:00", + "2023-12-06T05:09:00", + "2023-12-06T05:12:00", + "2023-12-06T05:15:00", + "2023-12-06T05:18:00", + "2023-12-06T05:21:00", + "2023-12-06T05:24:00", + "2023-12-06T05:27:00", + "2023-12-06T05:30:00", + "2023-12-06T05:33:00", + "2023-12-06T05:36:00", + "2023-12-06T05:39:00", + "2023-12-06T05:42:00", + "2023-12-06T05:45:00", + "2023-12-06T05:48:00", + "2023-12-06T05:51:00", + "2023-12-06T05:54:00", + "2023-12-06T05:57:00", + "2023-12-06T06:00:00", + "2023-12-06T06:03:00", + "2023-12-06T06:06:00", + "2023-12-06T06:09:00", + "2023-12-06T06:12:00", + "2023-12-06T06:15:00", + "2023-12-06T06:18:00", + "2023-12-06T06:21:00", + "2023-12-06T06:24:00", + "2023-12-06T06:27:00", + "2023-12-06T06:30:00", + "2023-12-06T06:33:00", + "2023-12-06T06:36:00", + "2023-12-06T06:39:00", + "2023-12-06T06:42:00", + "2023-12-06T06:45:00", + "2023-12-06T06:48:00", + "2023-12-06T06:51:00", + "2023-12-06T06:54:00", + "2023-12-06T06:57:00", + "2023-12-06T07:00:00", + "2023-12-06T07:03:00", + "2023-12-06T07:06:00", + "2023-12-06T07:09:00", + "2023-12-06T07:12:00", + "2023-12-06T07:15:00", + "2023-12-06T07:18:00", + "2023-12-06T07:21:00", + "2023-12-06T07:24:00", + "2023-12-06T07:27:00", + "2023-12-06T07:30:00", + "2023-12-06T07:33:00", + "2023-12-06T07:36:00", + "2023-12-06T07:39:00", + "2023-12-06T07:42:00", + "2023-12-06T07:45:00", + "2023-12-06T07:48:00", + "2023-12-06T07:51:00", + "2023-12-06T07:54:00", + "2023-12-06T07:57:00", + "2023-12-06T08:00:00", + "2023-12-06T08:03:00", + "2023-12-06T08:06:00", + "2023-12-06T08:09:00", + "2023-12-06T08:12:00", + "2023-12-06T08:15:00", + "2023-12-06T08:18:00", + "2023-12-06T08:21:00", + "2023-12-06T08:24:00", + "2023-12-06T08:27:00", + "2023-12-06T08:30:00", + "2023-12-06T08:33:00", + "2023-12-06T08:36:00", + "2023-12-06T08:39:00", + "2023-12-06T08:42:00", + "2023-12-06T08:45:00", + "2023-12-06T08:48:00", + "2023-12-06T08:51:00", + "2023-12-06T08:54:00", + "2023-12-06T08:57:00", + "2023-12-06T09:00:00", + "2023-12-06T09:03:00", + "2023-12-06T09:06:00", + "2023-12-06T09:09:00", + "2023-12-06T09:12:00", + "2023-12-06T09:15:00", + "2023-12-06T09:18:00", + "2023-12-06T09:21:00", + "2023-12-06T09:24:00", + "2023-12-06T09:27:00", + "2023-12-06T09:30:00", + "2023-12-06T09:33:00", + "2023-12-06T09:36:00", + "2023-12-06T09:39:00", + "2023-12-06T09:42:00", + "2023-12-06T09:45:00", + "2023-12-06T09:48:00", + "2023-12-06T09:51:00", + "2023-12-06T09:54:00", + "2023-12-06T09:57:00", + "2023-12-06T10:00:00", + "2023-12-06T10:03:00", + "2023-12-06T10:06:00", + "2023-12-06T10:09:00", + "2023-12-06T10:12:00", + "2023-12-06T10:15:00", + "2023-12-06T10:18:00", + "2023-12-06T10:21:00", + "2023-12-06T10:24:00", + "2023-12-06T10:27:00", + "2023-12-06T10:30:00", + "2023-12-06T10:33:00", + "2023-12-06T10:36:00", + "2023-12-06T10:39:00", + "2023-12-06T10:42:00", + "2023-12-06T10:45:00", + "2023-12-06T10:48:00", + "2023-12-06T10:51:00", + "2023-12-06T10:54:00", + "2023-12-06T10:57:00", + "2023-12-06T11:00:00", + "2023-12-06T11:03:00", + "2023-12-06T11:06:00", + "2023-12-06T11:09:00", + "2023-12-06T11:12:00", + "2023-12-06T11:15:00", + "2023-12-06T11:18:00", + "2023-12-06T11:21:00", + "2023-12-06T11:24:00", + "2023-12-06T11:27:00", + "2023-12-06T11:30:00", + "2023-12-06T11:33:00", + "2023-12-06T11:36:00", + "2023-12-06T11:39:00", + "2023-12-06T11:42:00", + "2023-12-06T11:45:00", + "2023-12-06T11:48:00", + "2023-12-06T11:51:00", + "2023-12-06T11:54:00", + "2023-12-06T11:57:00", + "2023-12-06T12:00:00", + "2023-12-06T12:03:00", + "2023-12-06T12:06:00", + "2023-12-06T12:09:00", + "2023-12-06T12:12:00", + "2023-12-06T12:15:00", + "2023-12-06T12:18:00", + "2023-12-06T12:21:00", + "2023-12-06T12:24:00", + "2023-12-06T12:27:00", + "2023-12-06T12:30:00", + "2023-12-06T12:33:00", + "2023-12-06T12:36:00", + "2023-12-06T12:39:00", + "2023-12-06T12:42:00", + "2023-12-06T12:45:00", + "2023-12-06T12:48:00", + "2023-12-06T12:51:00", + "2023-12-06T12:54:00", + "2023-12-06T12:57:00", + "2023-12-06T13:00:00", + "2023-12-06T13:03:00", + "2023-12-06T13:06:00", + "2023-12-06T13:09:00", + "2023-12-06T13:12:00", + "2023-12-06T13:15:00", + "2023-12-06T13:18:00", + "2023-12-06T13:21:00", + "2023-12-06T13:24:00", + "2023-12-06T13:27:00", + "2023-12-06T13:30:00", + "2023-12-06T13:33:00", + "2023-12-06T13:36:00", + "2023-12-06T13:39:00", + "2023-12-06T13:42:00", + "2023-12-06T13:45:00", + "2023-12-06T13:48:00", + "2023-12-06T13:51:00", + "2023-12-06T13:54:00", + "2023-12-06T13:57:00", + "2023-12-06T14:00:00", + "2023-12-06T14:03:00", + "2023-12-06T14:06:00", + "2023-12-06T14:09:00", + "2023-12-06T14:12:00", + "2023-12-06T14:15:00", + "2023-12-06T14:18:00", + "2023-12-06T14:21:00", + "2023-12-06T14:24:00", + "2023-12-06T14:27:00", + "2023-12-06T14:30:00", + "2023-12-06T14:33:00", + "2023-12-06T14:36:00", + "2023-12-06T14:39:00", + "2023-12-06T14:42:00", + "2023-12-06T14:45:00", + "2023-12-06T14:48:00", + "2023-12-06T14:51:00", + "2023-12-06T14:54:00", + "2023-12-06T14:57:00", + "2023-12-06T15:00:00", + "2023-12-06T15:03:00", + "2023-12-06T15:06:00", + "2023-12-06T15:09:00", + "2023-12-06T15:12:00", + "2023-12-06T15:15:00", + "2023-12-06T15:18:00", + "2023-12-06T15:21:00", + "2023-12-06T15:24:00", + "2023-12-06T15:27:00", + "2023-12-06T15:30:00", + "2023-12-06T15:33:00", + "2023-12-06T15:36:00", + "2023-12-06T15:39:00", + "2023-12-06T15:42:00", + "2023-12-06T15:45:00", + "2023-12-06T15:48:00", + "2023-12-06T15:51:00", + "2023-12-06T15:54:00", + "2023-12-06T15:57:00", + "2023-12-06T16:00:00", + "2023-12-06T16:03:00", + "2023-12-06T16:06:00", + "2023-12-06T16:09:00", + "2023-12-06T16:12:00", + "2023-12-06T16:15:00", + "2023-12-06T16:18:00", + "2023-12-06T16:21:00", + "2023-12-06T16:24:00", + "2023-12-06T16:27:00", + "2023-12-06T16:30:00", + "2023-12-06T16:33:00", + "2023-12-06T16:36:00", + "2023-12-06T16:39:00", + "2023-12-06T16:42:00", + "2023-12-06T16:45:00", + "2023-12-06T16:48:00", + "2023-12-06T16:51:00", + "2023-12-06T16:54:00", + "2023-12-06T16:57:00", + "2023-12-06T17:00:00", + "2023-12-06T17:03:00", + "2023-12-06T17:06:00", + "2023-12-06T17:09:00", + "2023-12-06T17:12:00", + "2023-12-06T17:15:00", + "2023-12-06T17:18:00", + "2023-12-06T17:21:00", + "2023-12-06T17:24:00", + "2023-12-06T17:27:00", + "2023-12-06T17:30:00", + "2023-12-06T17:33:00", + "2023-12-06T17:36:00", + "2023-12-06T17:39:00", + "2023-12-06T17:42:00", + "2023-12-06T17:45:00", + "2023-12-06T17:48:00", + "2023-12-06T17:51:00", + "2023-12-06T17:54:00", + "2023-12-06T17:57:00", + "2023-12-06T18:00:00", + "2023-12-06T18:03:00", + "2023-12-06T18:06:00", + "2023-12-06T18:09:00", + "2023-12-06T18:12:00", + "2023-12-06T18:15:00", + "2023-12-06T18:18:00", + "2023-12-06T18:21:00", + "2023-12-06T18:24:00", + "2023-12-06T18:27:00", + "2023-12-06T18:30:00", + "2023-12-06T18:33:00", + "2023-12-06T18:36:00", + "2023-12-06T18:39:00", + "2023-12-06T18:42:00", + "2023-12-06T18:45:00", + "2023-12-06T18:48:00", + "2023-12-06T18:51:00", + "2023-12-06T18:54:00", + "2023-12-06T18:57:00", + "2023-12-06T19:00:00", + "2023-12-06T19:03:00", + "2023-12-06T19:06:00", + "2023-12-06T19:09:00", + "2023-12-06T19:12:00", + "2023-12-06T19:15:00", + "2023-12-06T19:18:00", + "2023-12-06T19:21:00", + "2023-12-06T19:24:00", + "2023-12-06T19:27:00", + "2023-12-06T19:30:00", + "2023-12-06T19:33:00", + "2023-12-06T19:36:00", + "2023-12-06T19:39:00", + "2023-12-06T19:42:00", + "2023-12-06T19:45:00", + "2023-12-06T19:48:00", + "2023-12-06T19:51:00", + "2023-12-06T19:54:00", + "2023-12-06T19:57:00", + "2023-12-06T20:00:00", + "2023-12-06T20:03:00", + "2023-12-06T20:06:00", + "2023-12-06T20:09:00", + "2023-12-06T20:12:00", + "2023-12-06T20:15:00", + "2023-12-06T20:18:00", + "2023-12-06T20:21:00", + "2023-12-06T20:24:00", + "2023-12-06T20:27:00", + "2023-12-06T20:30:00", + "2023-12-06T20:33:00", + "2023-12-06T20:36:00", + "2023-12-06T20:39:00", + "2023-12-06T20:42:00", + "2023-12-06T20:45:00", + "2023-12-06T20:48:00", + "2023-12-06T20:51:00", + "2023-12-06T20:54:00", + "2023-12-06T20:57:00", + "2023-12-06T21:00:00", + "2023-12-06T21:03:00", + "2023-12-06T21:06:00", + "2023-12-06T21:09:00", + "2023-12-06T21:12:00", + "2023-12-06T21:15:00", + "2023-12-06T21:18:00", + "2023-12-06T21:21:00", + "2023-12-06T21:24:00", + "2023-12-06T21:27:00", + "2023-12-06T21:30:00", + "2023-12-06T21:33:00", + "2023-12-06T21:36:00", + "2023-12-06T21:39:00", + "2023-12-06T21:42:00", + "2023-12-06T21:45:00", + "2023-12-06T21:48:00", + "2023-12-06T21:51:00", + "2023-12-06T21:54:00", + "2023-12-06T21:57:00", + "2023-12-06T22:00:00", + "2023-12-06T22:03:00", + "2023-12-06T22:06:00", + "2023-12-06T22:09:00", + "2023-12-06T22:12:00", + "2023-12-06T22:15:00", + "2023-12-06T22:18:00", + "2023-12-06T22:21:00", + "2023-12-06T22:24:00", + "2023-12-06T22:27:00", + "2023-12-06T22:30:00", + "2023-12-06T22:33:00", + "2023-12-06T22:36:00", + "2023-12-06T22:39:00", + "2023-12-06T22:42:00", + "2023-12-06T22:45:00", + "2023-12-06T22:48:00", + "2023-12-06T22:51:00", + "2023-12-06T22:54:00", + "2023-12-06T22:57:00", + "2023-12-06T23:00:00", + "2023-12-06T23:03:00", + "2023-12-06T23:06:00", + "2023-12-06T23:09:00", + "2023-12-06T23:12:00", + "2023-12-06T23:15:00", + "2023-12-06T23:18:00", + "2023-12-06T23:21:00", + "2023-12-06T23:24:00", + "2023-12-06T23:27:00", + "2023-12-06T23:30:00", + "2023-12-06T23:33:00", + "2023-12-06T23:36:00", + "2023-12-06T23:39:00", + "2023-12-06T23:42:00", + "2023-12-06T23:45:00", + "2023-12-06T23:48:00", + "2023-12-06T23:51:00", + "2023-12-06T23:54:00", + "2023-12-06T23:57:00", + "2023-12-07T00:00:00", + "2023-12-07T00:03:00", + "2023-12-07T00:06:00", + "2023-12-07T00:09:00", + "2023-12-07T00:12:00", + "2023-12-07T00:15:00", + "2023-12-07T00:18:00", + "2023-12-07T00:21:00", + "2023-12-07T00:24:00", + "2023-12-07T00:27:00", + "2023-12-07T00:30:00", + "2023-12-07T00:33:00", + "2023-12-07T00:36:00", + "2023-12-07T00:39:00", + "2023-12-07T00:42:00", + "2023-12-07T00:45:00", + "2023-12-07T00:48:00", + "2023-12-07T00:51:00", + "2023-12-07T00:54:00", + "2023-12-07T00:57:00", + "2023-12-07T01:00:00", + "2023-12-07T01:03:00", + "2023-12-07T01:06:00", + "2023-12-07T01:09:00", + "2023-12-07T01:12:00", + "2023-12-07T01:15:00", + "2023-12-07T01:18:00", + "2023-12-07T01:21:00", + "2023-12-07T01:24:00", + "2023-12-07T01:27:00", + "2023-12-07T01:30:00", + "2023-12-07T01:33:00", + "2023-12-07T01:36:00", + "2023-12-07T01:39:00", + "2023-12-07T01:42:00", + "2023-12-07T01:45:00", + "2023-12-07T01:48:00", + "2023-12-07T01:51:00", + "2023-12-07T01:54:00", + "2023-12-07T01:57:00", + "2023-12-07T02:00:00", + "2023-12-07T02:03:00", + "2023-12-07T02:06:00", + "2023-12-07T02:09:00", + "2023-12-07T02:12:00", + "2023-12-07T02:15:00", + "2023-12-07T02:18:00", + "2023-12-07T02:21:00", + "2023-12-07T02:24:00", + "2023-12-07T02:27:00", + "2023-12-07T02:30:00", + "2023-12-07T02:33:00", + "2023-12-07T02:36:00", + "2023-12-07T02:39:00", + "2023-12-07T02:42:00", + "2023-12-07T02:45:00", + "2023-12-07T02:48:00", + "2023-12-07T02:51:00", + "2023-12-07T02:54:00", + "2023-12-07T02:57:00", + "2023-12-07T03:00:00", + "2023-12-07T03:03:00", + "2023-12-07T03:06:00", + "2023-12-07T03:09:00", + "2023-12-07T03:12:00", + "2023-12-07T03:15:00", + "2023-12-07T03:18:00", + "2023-12-07T03:21:00", + "2023-12-07T03:24:00", + "2023-12-07T03:27:00", + "2023-12-07T03:30:00", + "2023-12-07T03:33:00", + "2023-12-07T03:36:00", + "2023-12-07T03:39:00", + "2023-12-07T03:42:00", + "2023-12-07T03:45:00", + "2023-12-07T03:48:00", + "2023-12-07T03:51:00", + "2023-12-07T03:54:00", + "2023-12-07T03:57:00", + "2023-12-07T04:00:00", + "2023-12-07T04:03:00", + "2023-12-07T04:06:00", + "2023-12-07T04:09:00", + "2023-12-07T04:12:00", + "2023-12-07T04:15:00", + "2023-12-07T04:18:00", + "2023-12-07T04:21:00", + "2023-12-07T04:24:00", + "2023-12-07T04:27:00", + "2023-12-07T04:30:00", + "2023-12-07T04:33:00", + "2023-12-07T04:36:00", + "2023-12-07T04:39:00", + "2023-12-07T04:42:00", + "2023-12-07T04:45:00", + "2023-12-07T04:48:00", + "2023-12-07T04:51:00", + "2023-12-07T04:54:00", + "2023-12-07T04:57:00", + "2023-12-07T05:00:00", + "2023-12-07T05:03:00", + "2023-12-07T05:06:00", + "2023-12-07T05:09:00", + "2023-12-07T05:12:00", + "2023-12-07T05:15:00", + "2023-12-07T05:18:00", + "2023-12-07T05:21:00", + "2023-12-07T05:24:00", + "2023-12-07T05:27:00", + "2023-12-07T05:30:00", + "2023-12-07T05:33:00", + "2023-12-07T05:36:00", + "2023-12-07T05:39:00", + "2023-12-07T05:42:00", + "2023-12-07T05:45:00", + "2023-12-07T05:48:00", + "2023-12-07T05:51:00", + "2023-12-07T05:54:00", + "2023-12-07T05:57:00", + "2023-12-07T06:00:00", + "2023-12-07T06:03:00", + "2023-12-07T06:06:00", + "2023-12-07T06:09:00", + "2023-12-07T06:12:00", + "2023-12-07T06:15:00", + "2023-12-07T06:18:00", + "2023-12-07T06:21:00", + "2023-12-07T06:24:00", + "2023-12-07T06:27:00", + "2023-12-07T06:30:00", + "2023-12-07T06:33:00", + "2023-12-07T06:36:00", + "2023-12-07T06:39:00", + "2023-12-07T06:42:00", + "2023-12-07T06:45:00", + "2023-12-07T06:48:00", + "2023-12-07T06:51:00", + "2023-12-07T06:54:00", + "2023-12-07T06:57:00", + "2023-12-07T07:00:00", + "2023-12-07T07:03:00", + "2023-12-07T07:06:00", + "2023-12-07T07:09:00", + "2023-12-07T07:12:00", + "2023-12-07T07:15:00", + "2023-12-07T07:18:00", + "2023-12-07T07:21:00", + "2023-12-07T07:24:00", + "2023-12-07T07:27:00", + "2023-12-07T07:30:00", + "2023-12-07T07:33:00", + "2023-12-07T07:36:00", + "2023-12-07T07:39:00", + "2023-12-07T07:42:00", + "2023-12-07T07:45:00", + "2023-12-07T07:48:00", + "2023-12-07T07:51:00", + "2023-12-07T07:54:00", + "2023-12-07T07:57:00", + "2023-12-07T08:00:00", + "2023-12-07T08:03:00", + "2023-12-07T08:06:00", + "2023-12-07T08:09:00", + "2023-12-07T08:12:00", + "2023-12-07T08:15:00", + "2023-12-07T08:18:00", + "2023-12-07T08:21:00", + "2023-12-07T08:24:00", + "2023-12-07T08:27:00", + "2023-12-07T08:30:00", + "2023-12-07T08:33:00", + "2023-12-07T08:36:00", + "2023-12-07T08:39:00", + "2023-12-07T08:42:00", + "2023-12-07T08:45:00", + "2023-12-07T08:48:00", + "2023-12-07T08:51:00", + "2023-12-07T08:54:00", + "2023-12-07T08:57:00", + "2023-12-07T09:00:00", + "2023-12-07T09:03:00", + "2023-12-07T09:06:00", + "2023-12-07T09:09:00", + "2023-12-07T09:12:00", + "2023-12-07T09:15:00", + "2023-12-07T09:18:00", + "2023-12-07T09:21:00", + "2023-12-07T09:24:00", + "2023-12-07T09:27:00", + "2023-12-07T09:30:00", + "2023-12-07T09:33:00", + "2023-12-07T09:36:00", + "2023-12-07T09:39:00", + "2023-12-07T09:42:00", + "2023-12-07T09:45:00", + "2023-12-07T09:48:00", + "2023-12-07T09:51:00", + "2023-12-07T09:54:00", + "2023-12-07T09:57:00", + "2023-12-07T10:00:00", + "2023-12-07T10:03:00", + "2023-12-07T10:06:00", + "2023-12-07T10:09:00", + "2023-12-07T10:12:00", + "2023-12-07T10:15:00", + "2023-12-07T10:18:00", + "2023-12-07T10:21:00", + "2023-12-07T10:24:00", + "2023-12-07T10:27:00", + "2023-12-07T10:30:00", + "2023-12-07T10:33:00", + "2023-12-07T10:36:00", + "2023-12-07T10:39:00", + "2023-12-07T10:42:00", + "2023-12-07T10:45:00", + "2023-12-07T10:48:00", + "2023-12-07T10:51:00", + "2023-12-07T10:54:00", + "2023-12-07T10:57:00", + "2023-12-07T11:00:00", + "2023-12-07T11:03:00", + "2023-12-07T11:06:00", + "2023-12-07T11:09:00", + "2023-12-07T11:12:00", + "2023-12-07T11:15:00", + "2023-12-07T11:18:00", + "2023-12-07T11:21:00", + "2023-12-07T11:24:00", + "2023-12-07T11:27:00", + "2023-12-07T11:30:00", + "2023-12-07T11:33:00", + "2023-12-07T11:36:00", + "2023-12-07T11:39:00", + "2023-12-07T11:42:00", + "2023-12-07T11:45:00", + "2023-12-07T11:48:00", + "2023-12-07T11:51:00", + "2023-12-07T11:54:00", + "2023-12-07T11:57:00", + "2023-12-07T12:00:00", + "2023-12-07T12:03:00", + "2023-12-07T12:06:00", + "2023-12-07T12:09:00", + "2023-12-07T12:12:00", + "2023-12-07T12:15:00", + "2023-12-07T12:18:00", + "2023-12-07T12:21:00", + "2023-12-07T12:24:00", + "2023-12-07T12:27:00", + "2023-12-07T12:30:00", + "2023-12-07T12:33:00", + "2023-12-07T12:36:00", + "2023-12-07T12:39:00", + "2023-12-07T12:42:00", + "2023-12-07T12:45:00", + "2023-12-07T12:48:00", + "2023-12-07T12:51:00", + "2023-12-07T12:54:00", + "2023-12-07T12:57:00", + "2023-12-07T13:00:00", + "2023-12-07T13:03:00", + "2023-12-07T13:06:00", + "2023-12-07T13:09:00", + "2023-12-07T13:12:00", + "2023-12-07T13:15:00", + "2023-12-07T13:18:00", + "2023-12-07T13:21:00", + "2023-12-07T13:24:00", + "2023-12-07T13:27:00", + "2023-12-07T13:30:00", + "2023-12-07T13:33:00", + "2023-12-07T13:36:00", + "2023-12-07T13:39:00", + "2023-12-07T13:42:00", + "2023-12-07T13:45:00", + "2023-12-07T13:48:00", + "2023-12-07T13:51:00", + "2023-12-07T13:54:00", + "2023-12-07T13:57:00", + "2023-12-07T14:00:00", + "2023-12-07T14:03:00", + "2023-12-07T14:06:00", + "2023-12-07T14:09:00", + "2023-12-07T14:12:00", + "2023-12-07T14:15:00", + "2023-12-07T14:18:00", + "2023-12-07T14:21:00", + "2023-12-07T14:24:00", + "2023-12-07T14:27:00", + "2023-12-07T14:30:00", + "2023-12-07T14:33:00", + "2023-12-07T14:36:00", + "2023-12-07T14:39:00", + "2023-12-07T14:42:00", + "2023-12-07T14:45:00", + "2023-12-07T14:48:00", + "2023-12-07T14:51:00", + "2023-12-07T14:54:00", + "2023-12-07T14:57:00", + "2023-12-07T15:00:00", + "2023-12-07T15:03:00", + "2023-12-07T15:06:00", + "2023-12-07T15:09:00", + "2023-12-07T15:12:00", + "2023-12-07T15:15:00", + "2023-12-07T15:18:00", + "2023-12-07T15:21:00", + "2023-12-07T15:24:00", + "2023-12-07T15:27:00", + "2023-12-07T15:30:00", + "2023-12-07T15:33:00", + "2023-12-07T15:36:00", + "2023-12-07T15:39:00", + "2023-12-07T15:42:00", + "2023-12-07T15:45:00", + "2023-12-07T15:48:00", + "2023-12-07T15:51:00", + "2023-12-07T15:54:00", + "2023-12-07T15:57:00", + "2023-12-07T16:00:00", + "2023-12-07T16:03:00", + "2023-12-07T16:06:00", + "2023-12-07T16:09:00", + "2023-12-07T16:12:00", + "2023-12-07T16:15:00", + "2023-12-07T16:18:00", + "2023-12-07T16:21:00", + "2023-12-07T16:24:00", + "2023-12-07T16:27:00", + "2023-12-07T16:30:00", + "2023-12-07T16:33:00", + "2023-12-07T16:36:00", + "2023-12-07T16:39:00", + "2023-12-07T16:42:00", + "2023-12-07T16:45:00", + "2023-12-07T16:48:00", + "2023-12-07T16:51:00", + "2023-12-07T16:54:00", + "2023-12-07T16:57:00", + "2023-12-07T17:00:00", + "2023-12-07T17:03:00", + "2023-12-07T17:06:00", + "2023-12-07T17:09:00", + "2023-12-07T17:12:00", + "2023-12-07T17:15:00", + "2023-12-07T17:18:00", + "2023-12-07T17:21:00", + "2023-12-07T17:24:00", + "2023-12-07T17:27:00", + "2023-12-07T17:30:00", + "2023-12-07T17:33:00", + "2023-12-07T17:36:00", + "2023-12-07T17:39:00", + "2023-12-07T17:42:00", + "2023-12-07T17:45:00", + "2023-12-07T17:48:00", + "2023-12-07T17:51:00", + "2023-12-07T17:54:00", + "2023-12-07T17:57:00", + "2023-12-07T18:00:00", + "2023-12-07T18:03:00", + "2023-12-07T18:06:00", + "2023-12-07T18:09:00", + "2023-12-07T18:12:00", + "2023-12-07T18:15:00", + "2023-12-07T18:18:00", + "2023-12-07T18:21:00", + "2023-12-07T18:24:00", + "2023-12-07T18:27:00", + "2023-12-07T18:30:00", + "2023-12-07T18:33:00", + "2023-12-07T18:36:00", + "2023-12-07T18:39:00", + "2023-12-07T18:42:00", + "2023-12-07T18:45:00", + "2023-12-07T18:48:00", + "2023-12-07T18:51:00", + "2023-12-07T18:54:00", + "2023-12-07T18:57:00", + "2023-12-07T19:00:00", + "2023-12-07T19:03:00", + "2023-12-07T19:06:00", + "2023-12-07T19:09:00", + "2023-12-07T19:12:00", + "2023-12-07T19:15:00", + "2023-12-07T19:18:00", + "2023-12-07T19:21:00", + "2023-12-07T19:24:00", + "2023-12-07T19:27:00", + "2023-12-07T19:30:00", + "2023-12-07T19:33:00", + "2023-12-07T19:36:00", + "2023-12-07T19:39:00", + "2023-12-07T19:42:00", + "2023-12-07T19:45:00", + "2023-12-07T19:48:00", + "2023-12-07T19:51:00", + "2023-12-07T19:54:00", + "2023-12-07T19:57:00", + "2023-12-07T20:00:00", + "2023-12-07T20:03:00", + "2023-12-07T20:06:00", + "2023-12-07T20:09:00", + "2023-12-07T20:12:00", + "2023-12-07T20:15:00", + "2023-12-07T20:18:00", + "2023-12-07T20:21:00", + "2023-12-07T20:24:00", + "2023-12-07T20:27:00", + "2023-12-07T20:30:00", + "2023-12-07T20:33:00", + "2023-12-07T20:36:00", + "2023-12-07T20:39:00", + "2023-12-07T20:42:00", + "2023-12-07T20:45:00", + "2023-12-07T20:48:00", + "2023-12-07T20:51:00", + "2023-12-07T20:54:00", + "2023-12-07T20:57:00", + "2023-12-07T21:00:00", + "2023-12-07T21:03:00", + "2023-12-07T21:06:00", + "2023-12-07T21:09:00", + "2023-12-07T21:12:00", + "2023-12-07T21:15:00", + "2023-12-07T21:18:00", + "2023-12-07T21:21:00", + "2023-12-07T21:24:00", + "2023-12-07T21:27:00", + "2023-12-07T21:30:00", + "2023-12-07T21:33:00", + "2023-12-07T21:36:00", + "2023-12-07T21:39:00", + "2023-12-07T21:42:00", + "2023-12-07T21:45:00", + "2023-12-07T21:48:00", + "2023-12-07T21:51:00", + "2023-12-07T21:54:00", + "2023-12-07T21:57:00", + "2023-12-07T22:00:00", + "2023-12-07T22:03:00", + "2023-12-07T22:06:00", + "2023-12-07T22:09:00", + "2023-12-07T22:12:00", + "2023-12-07T22:15:00", + "2023-12-07T22:18:00", + "2023-12-07T22:21:00", + "2023-12-07T22:24:00", + "2023-12-07T22:27:00", + "2023-12-07T22:30:00", + "2023-12-07T22:33:00", + "2023-12-07T22:36:00", + "2023-12-07T22:39:00", + "2023-12-07T22:42:00", + "2023-12-07T22:45:00", + "2023-12-07T22:48:00", + "2023-12-07T22:51:00", + "2023-12-07T22:54:00", + "2023-12-07T22:57:00", + "2023-12-07T23:00:00", + "2023-12-07T23:03:00", + "2023-12-07T23:06:00", + "2023-12-07T23:09:00", + "2023-12-07T23:12:00", + "2023-12-07T23:15:00", + "2023-12-07T23:18:00", + "2023-12-07T23:21:00", + "2023-12-07T23:24:00", + "2023-12-07T23:27:00", + "2023-12-07T23:30:00", + "2023-12-07T23:33:00", + "2023-12-07T23:36:00", + "2023-12-07T23:39:00", + "2023-12-07T23:42:00", + "2023-12-07T23:45:00", + "2023-12-07T23:48:00", + "2023-12-07T23:51:00", + "2023-12-07T23:54:00", + "2023-12-07T23:57:00", + "2023-12-08T00:00:00", + "2023-12-08T00:03:00", + "2023-12-08T00:06:00", + "2023-12-08T00:09:00", + "2023-12-08T00:12:00", + "2023-12-08T00:15:00", + "2023-12-08T00:18:00", + "2023-12-08T00:21:00", + "2023-12-08T00:24:00", + "2023-12-08T00:27:00", + "2023-12-08T00:30:00", + "2023-12-08T00:33:00", + "2023-12-08T00:36:00", + "2023-12-08T00:39:00", + "2023-12-08T00:42:00", + "2023-12-08T00:45:00", + "2023-12-08T00:48:00", + "2023-12-08T00:51:00", + "2023-12-08T00:54:00", + "2023-12-08T00:57:00", + "2023-12-08T01:00:00", + "2023-12-08T01:03:00", + "2023-12-08T01:06:00", + "2023-12-08T01:09:00", + "2023-12-08T01:12:00", + "2023-12-08T01:15:00", + "2023-12-08T01:18:00", + "2023-12-08T01:21:00", + "2023-12-08T01:24:00", + "2023-12-08T01:27:00", + "2023-12-08T01:30:00", + "2023-12-08T01:33:00", + "2023-12-08T01:36:00", + "2023-12-08T01:39:00", + "2023-12-08T01:42:00", + "2023-12-08T01:45:00", + "2023-12-08T01:48:00", + "2023-12-08T01:51:00", + "2023-12-08T01:54:00", + "2023-12-08T01:57:00", + "2023-12-08T02:00:00", + "2023-12-08T02:03:00", + "2023-12-08T02:06:00", + "2023-12-08T02:09:00", + "2023-12-08T02:12:00", + "2023-12-08T02:15:00", + "2023-12-08T02:18:00", + "2023-12-08T02:21:00", + "2023-12-08T02:24:00", + "2023-12-08T02:27:00", + "2023-12-08T02:30:00", + "2023-12-08T02:33:00", + "2023-12-08T02:36:00", + "2023-12-08T02:39:00", + "2023-12-08T02:42:00", + "2023-12-08T02:45:00", + "2023-12-08T02:48:00", + "2023-12-08T02:51:00", + "2023-12-08T02:54:00", + "2023-12-08T02:57:00", + "2023-12-08T03:00:00", + "2023-12-08T03:03:00", + "2023-12-08T03:06:00", + "2023-12-08T03:09:00", + "2023-12-08T03:12:00", + "2023-12-08T03:15:00", + "2023-12-08T03:18:00", + "2023-12-08T03:21:00", + "2023-12-08T03:24:00", + "2023-12-08T03:27:00", + "2023-12-08T03:30:00", + "2023-12-08T03:33:00", + "2023-12-08T03:36:00", + "2023-12-08T03:39:00", + "2023-12-08T03:42:00", + "2023-12-08T03:45:00", + "2023-12-08T03:48:00", + "2023-12-08T03:51:00", + "2023-12-08T03:54:00", + "2023-12-08T03:57:00", + "2023-12-08T04:00:00", + "2023-12-08T04:03:00", + "2023-12-08T04:06:00", + "2023-12-08T04:09:00", + "2023-12-08T04:12:00", + "2023-12-08T04:15:00", + "2023-12-08T04:18:00", + "2023-12-08T04:21:00", + "2023-12-08T04:24:00", + "2023-12-08T04:27:00", + "2023-12-08T04:30:00", + "2023-12-08T04:33:00", + "2023-12-08T04:36:00", + "2023-12-08T04:39:00", + "2023-12-08T04:42:00", + "2023-12-08T04:45:00", + "2023-12-08T04:48:00", + "2023-12-08T04:51:00", + "2023-12-08T04:54:00", + "2023-12-08T04:57:00", + "2023-12-08T05:00:00", + "2023-12-08T05:03:00", + "2023-12-08T05:06:00", + "2023-12-08T05:09:00", + "2023-12-08T05:12:00", + "2023-12-08T05:15:00", + "2023-12-08T05:18:00", + "2023-12-08T05:21:00", + "2023-12-08T05:24:00", + "2023-12-08T05:27:00", + "2023-12-08T05:30:00", + "2023-12-08T05:33:00", + "2023-12-08T05:36:00", + "2023-12-08T05:39:00", + "2023-12-08T05:42:00", + "2023-12-08T05:45:00", + "2023-12-08T05:48:00", + "2023-12-08T05:51:00", + "2023-12-08T05:54:00", + "2023-12-08T05:57:00", + "2023-12-08T06:00:00", + "2023-12-08T06:03:00", + "2023-12-08T06:06:00", + "2023-12-08T06:09:00", + "2023-12-08T06:12:00", + "2023-12-08T06:15:00", + "2023-12-08T06:18:00", + "2023-12-08T06:21:00", + "2023-12-08T06:24:00", + "2023-12-08T06:27:00", + "2023-12-08T06:30:00", + "2023-12-08T06:33:00", + "2023-12-08T06:36:00", + "2023-12-08T06:39:00", + "2023-12-08T06:42:00", + "2023-12-08T06:45:00", + "2023-12-08T06:48:00", + "2023-12-08T06:51:00", + "2023-12-08T06:54:00", + "2023-12-08T06:57:00", + "2023-12-08T07:00:00", + "2023-12-08T07:03:00", + "2023-12-08T07:06:00", + "2023-12-08T07:09:00", + "2023-12-08T07:12:00", + "2023-12-08T07:15:00", + "2023-12-08T07:18:00", + "2023-12-08T07:21:00", + "2023-12-08T07:24:00", + "2023-12-08T07:27:00", + "2023-12-08T07:30:00", + "2023-12-08T07:33:00", + "2023-12-08T07:36:00", + "2023-12-08T07:39:00", + "2023-12-08T07:42:00", + "2023-12-08T07:45:00", + "2023-12-08T07:48:00", + "2023-12-08T07:51:00", + "2023-12-08T07:54:00", + "2023-12-08T07:57:00", + "2023-12-08T08:00:00", + "2023-12-08T08:03:00", + "2023-12-08T08:06:00", + "2023-12-08T08:09:00", + "2023-12-08T08:12:00", + "2023-12-08T08:15:00", + "2023-12-08T08:18:00", + "2023-12-08T08:21:00", + "2023-12-08T08:24:00", + "2023-12-08T08:27:00", + "2023-12-08T08:30:00", + "2023-12-08T08:33:00", + "2023-12-08T08:36:00", + "2023-12-08T08:39:00", + "2023-12-08T08:42:00", + "2023-12-08T08:45:00", + "2023-12-08T08:48:00", + "2023-12-08T08:51:00", + "2023-12-08T08:54:00", + "2023-12-08T08:57:00", + "2023-12-08T09:00:00", + "2023-12-08T09:03:00", + "2023-12-08T09:06:00", + "2023-12-08T09:09:00", + "2023-12-08T09:12:00", + "2023-12-08T09:15:00", + "2023-12-08T09:18:00", + "2023-12-08T09:21:00", + "2023-12-08T09:24:00", + "2023-12-08T09:27:00", + "2023-12-08T09:30:00", + "2023-12-08T09:33:00", + "2023-12-08T09:36:00", + "2023-12-08T09:39:00", + "2023-12-08T09:42:00", + "2023-12-08T09:45:00", + "2023-12-08T09:48:00", + "2023-12-08T09:51:00", + "2023-12-08T09:54:00", + "2023-12-08T09:57:00", + "2023-12-08T10:00:00", + "2023-12-08T10:03:00", + "2023-12-08T10:06:00", + "2023-12-08T10:09:00", + "2023-12-08T10:12:00", + "2023-12-08T10:15:00", + "2023-12-08T10:18:00", + "2023-12-08T10:21:00", + "2023-12-08T10:24:00", + "2023-12-08T10:27:00", + "2023-12-08T10:30:00", + "2023-12-08T10:33:00", + "2023-12-08T10:36:00", + "2023-12-08T10:39:00", + "2023-12-08T10:42:00", + "2023-12-08T10:45:00", + "2023-12-08T10:48:00", + "2023-12-08T10:51:00", + "2023-12-08T10:54:00", + "2023-12-08T10:57:00", + "2023-12-08T11:00:00", + "2023-12-08T11:03:00", + "2023-12-08T11:06:00", + "2023-12-08T11:09:00", + "2023-12-08T11:12:00", + "2023-12-08T11:15:00", + "2023-12-08T11:18:00", + "2023-12-08T11:21:00", + "2023-12-08T11:24:00", + "2023-12-08T11:27:00", + "2023-12-08T11:30:00", + "2023-12-08T11:33:00", + "2023-12-08T11:36:00", + "2023-12-08T11:39:00", + "2023-12-08T11:42:00", + "2023-12-08T11:45:00", + "2023-12-08T11:48:00", + "2023-12-08T11:51:00", + "2023-12-08T11:54:00", + "2023-12-08T11:57:00", + "2023-12-08T12:00:00", + "2023-12-08T12:03:00", + "2023-12-08T12:06:00", + "2023-12-08T12:09:00", + "2023-12-08T12:12:00", + "2023-12-08T12:15:00", + "2023-12-08T12:18:00", + "2023-12-08T12:21:00", + "2023-12-08T12:24:00", + "2023-12-08T12:27:00", + "2023-12-08T12:30:00", + "2023-12-08T12:33:00", + "2023-12-08T12:36:00", + "2023-12-08T12:39:00", + "2023-12-08T12:42:00", + "2023-12-08T12:45:00", + "2023-12-08T12:48:00", + "2023-12-08T12:51:00", + "2023-12-08T12:54:00", + "2023-12-08T12:57:00", + "2023-12-08T13:00:00", + "2023-12-08T13:03:00", + "2023-12-08T13:06:00", + "2023-12-08T13:09:00", + "2023-12-08T13:12:00", + "2023-12-08T13:15:00", + "2023-12-08T13:18:00", + "2023-12-08T13:21:00", + "2023-12-08T13:24:00", + "2023-12-08T13:27:00", + "2023-12-08T13:30:00", + "2023-12-08T13:33:00", + "2023-12-08T13:36:00", + "2023-12-08T13:39:00", + "2023-12-08T13:42:00", + "2023-12-08T13:45:00", + "2023-12-08T13:48:00", + "2023-12-08T13:51:00", + "2023-12-08T13:54:00", + "2023-12-08T13:57:00", + "2023-12-08T14:00:00", + "2023-12-08T14:03:00", + "2023-12-08T14:06:00", + "2023-12-08T14:09:00", + "2023-12-08T14:12:00", + "2023-12-08T14:15:00", + "2023-12-08T14:18:00", + "2023-12-08T14:21:00", + "2023-12-08T14:24:00", + "2023-12-08T14:27:00", + "2023-12-08T14:30:00", + "2023-12-08T14:33:00", + "2023-12-08T14:36:00", + "2023-12-08T14:39:00", + "2023-12-08T14:42:00", + "2023-12-08T14:45:00", + "2023-12-08T14:48:00", + "2023-12-08T14:51:00", + "2023-12-08T14:54:00", + "2023-12-08T14:57:00", + "2023-12-08T15:00:00", + "2023-12-08T15:03:00", + "2023-12-08T15:06:00", + "2023-12-08T15:09:00", + "2023-12-08T15:12:00", + "2023-12-08T15:15:00", + "2023-12-08T15:18:00", + "2023-12-08T15:21:00", + "2023-12-08T15:24:00", + "2023-12-08T15:27:00", + "2023-12-08T15:30:00", + "2023-12-08T15:33:00", + "2023-12-08T15:36:00", + "2023-12-08T15:39:00", + "2023-12-08T15:42:00", + "2023-12-08T15:45:00", + "2023-12-08T15:48:00", + "2023-12-08T15:51:00", + "2023-12-08T15:54:00", + "2023-12-08T15:57:00", + "2023-12-08T16:00:00", + "2023-12-08T16:03:00", + "2023-12-08T16:06:00", + "2023-12-08T16:09:00", + "2023-12-08T16:12:00", + "2023-12-08T16:15:00", + "2023-12-08T16:18:00", + "2023-12-08T16:21:00", + "2023-12-08T16:24:00", + "2023-12-08T16:27:00", + "2023-12-08T16:30:00", + "2023-12-08T16:33:00", + "2023-12-08T16:36:00", + "2023-12-08T16:39:00", + "2023-12-08T16:42:00", + "2023-12-08T16:45:00", + "2023-12-08T16:48:00", + "2023-12-08T16:51:00", + "2023-12-08T16:54:00", + "2023-12-08T16:57:00", + "2023-12-08T17:00:00", + "2023-12-08T17:03:00", + "2023-12-08T17:06:00", + "2023-12-08T17:09:00", + "2023-12-08T17:12:00", + "2023-12-08T17:15:00", + "2023-12-08T17:18:00", + "2023-12-08T17:21:00", + "2023-12-08T17:24:00", + "2023-12-08T17:27:00", + "2023-12-08T17:30:00", + "2023-12-08T17:33:00", + "2023-12-08T17:36:00", + "2023-12-08T17:39:00", + "2023-12-08T17:42:00", + "2023-12-08T17:45:00", + "2023-12-08T17:48:00", + "2023-12-08T17:51:00", + "2023-12-08T17:54:00", + "2023-12-08T17:57:00", + "2023-12-08T18:00:00", + "2023-12-08T18:03:00", + "2023-12-08T18:06:00", + "2023-12-08T18:09:00", + "2023-12-08T18:12:00", + "2023-12-08T18:15:00", + "2023-12-08T18:18:00", + "2023-12-08T18:21:00", + "2023-12-08T18:24:00", + "2023-12-08T18:27:00", + "2023-12-08T18:30:00", + "2023-12-08T18:33:00", + "2023-12-08T18:36:00", + "2023-12-08T18:39:00", + "2023-12-08T18:42:00", + "2023-12-08T18:45:00", + "2023-12-08T18:48:00", + "2023-12-08T18:51:00", + "2023-12-08T18:54:00", + "2023-12-08T18:57:00", + "2023-12-08T19:00:00", + "2023-12-08T19:03:00", + "2023-12-08T19:06:00", + "2023-12-08T19:09:00", + "2023-12-08T19:12:00", + "2023-12-08T19:15:00", + "2023-12-08T19:18:00", + "2023-12-08T19:21:00", + "2023-12-08T19:24:00", + "2023-12-08T19:27:00", + "2023-12-08T19:30:00", + "2023-12-08T19:33:00", + "2023-12-08T19:36:00", + "2023-12-08T19:39:00", + "2023-12-08T19:42:00", + "2023-12-08T19:45:00", + "2023-12-08T19:48:00", + "2023-12-08T19:51:00", + "2023-12-08T19:54:00", + "2023-12-08T19:57:00", + "2023-12-08T20:00:00", + "2023-12-08T20:03:00", + "2023-12-08T20:06:00", + "2023-12-08T20:09:00", + "2023-12-08T20:12:00", + "2023-12-08T20:15:00", + "2023-12-08T20:18:00", + "2023-12-08T20:21:00", + "2023-12-08T20:24:00", + "2023-12-08T20:27:00", + "2023-12-08T20:30:00", + "2023-12-08T20:33:00", + "2023-12-08T20:36:00", + "2023-12-08T20:39:00", + "2023-12-08T20:42:00", + "2023-12-08T20:45:00", + "2023-12-08T20:48:00", + "2023-12-08T20:51:00", + "2023-12-08T20:54:00", + "2023-12-08T20:57:00", + "2023-12-08T21:00:00", + "2023-12-08T21:03:00", + "2023-12-08T21:06:00", + "2023-12-08T21:09:00", + "2023-12-08T21:12:00", + "2023-12-08T21:15:00", + "2023-12-08T21:18:00", + "2023-12-08T21:21:00", + "2023-12-08T21:24:00", + "2023-12-08T21:27:00", + "2023-12-08T21:30:00", + "2023-12-08T21:33:00", + "2023-12-08T21:36:00", + "2023-12-08T21:39:00", + "2023-12-08T21:42:00", + "2023-12-08T21:45:00", + "2023-12-08T21:48:00", + "2023-12-08T21:51:00", + "2023-12-08T21:54:00", + "2023-12-08T21:57:00", + "2023-12-08T22:00:00", + "2023-12-08T22:03:00", + "2023-12-08T22:06:00", + "2023-12-08T22:09:00", + "2023-12-08T22:12:00", + "2023-12-08T22:15:00", + "2023-12-08T22:18:00", + "2023-12-08T22:21:00", + "2023-12-08T22:24:00", + "2023-12-08T22:27:00", + "2023-12-08T22:30:00", + "2023-12-08T22:33:00", + "2023-12-08T22:36:00", + "2023-12-08T22:39:00", + "2023-12-08T22:42:00", + "2023-12-08T22:45:00", + "2023-12-08T22:48:00", + "2023-12-08T22:51:00", + "2023-12-08T22:54:00", + "2023-12-08T22:57:00", + "2023-12-08T23:00:00", + "2023-12-08T23:03:00", + "2023-12-08T23:06:00", + "2023-12-08T23:09:00", + "2023-12-08T23:12:00", + "2023-12-08T23:15:00", + "2023-12-08T23:18:00", + "2023-12-08T23:21:00", + "2023-12-08T23:24:00", + "2023-12-08T23:27:00", + "2023-12-08T23:30:00", + "2023-12-08T23:33:00", + "2023-12-08T23:36:00", + "2023-12-08T23:39:00", + "2023-12-08T23:42:00", + "2023-12-08T23:45:00", + "2023-12-08T23:48:00", + "2023-12-08T23:51:00", + "2023-12-08T23:54:00", + "2023-12-08T23:57:00", + "2023-12-09T00:00:00", + "2023-12-09T00:03:00", + "2023-12-09T00:06:00", + "2023-12-09T00:09:00", + "2023-12-09T00:12:00", + "2023-12-09T00:15:00", + "2023-12-09T00:18:00", + "2023-12-09T00:21:00", + "2023-12-09T00:24:00", + "2023-12-09T00:27:00", + "2023-12-09T00:30:00", + "2023-12-09T00:33:00", + "2023-12-09T00:36:00", + "2023-12-09T00:39:00", + "2023-12-09T00:42:00", + "2023-12-09T00:45:00", + "2023-12-09T00:48:00", + "2023-12-09T00:51:00", + "2023-12-09T00:54:00", + "2023-12-09T00:57:00", + "2023-12-09T01:00:00", + "2023-12-09T01:03:00", + "2023-12-09T01:06:00", + "2023-12-09T01:09:00", + "2023-12-09T01:12:00", + "2023-12-09T01:15:00", + "2023-12-09T01:18:00", + "2023-12-09T01:21:00", + "2023-12-09T01:24:00", + "2023-12-09T01:27:00", + "2023-12-09T01:30:00", + "2023-12-09T01:33:00", + "2023-12-09T01:36:00", + "2023-12-09T01:39:00", + "2023-12-09T01:42:00", + "2023-12-09T01:45:00", + "2023-12-09T01:48:00", + "2023-12-09T01:51:00", + "2023-12-09T01:54:00", + "2023-12-09T01:57:00", + "2023-12-09T02:00:00", + "2023-12-09T02:03:00", + "2023-12-09T02:06:00", + "2023-12-09T02:09:00", + "2023-12-09T02:12:00", + "2023-12-09T02:15:00", + "2023-12-09T02:18:00", + "2023-12-09T02:21:00", + "2023-12-09T02:24:00", + "2023-12-09T02:27:00", + "2023-12-09T02:30:00", + "2023-12-09T02:33:00", + "2023-12-09T02:36:00", + "2023-12-09T02:39:00", + "2023-12-09T02:42:00", + "2023-12-09T02:45:00", + "2023-12-09T02:48:00", + "2023-12-09T02:51:00", + "2023-12-09T02:54:00", + "2023-12-09T02:57:00", + "2023-12-09T03:00:00", + "2023-12-09T03:03:00", + "2023-12-09T03:06:00", + "2023-12-09T03:09:00", + "2023-12-09T03:12:00", + "2023-12-09T03:15:00", + "2023-12-09T03:18:00", + "2023-12-09T03:21:00", + "2023-12-09T03:24:00", + "2023-12-09T03:27:00", + "2023-12-09T03:30:00", + "2023-12-09T03:33:00", + "2023-12-09T03:36:00", + "2023-12-09T03:39:00", + "2023-12-09T03:42:00", + "2023-12-09T03:45:00", + "2023-12-09T03:48:00", + "2023-12-09T03:51:00", + "2023-12-09T03:54:00", + "2023-12-09T03:57:00", + "2023-12-09T04:00:00", + "2023-12-09T04:03:00", + "2023-12-09T04:06:00", + "2023-12-09T04:09:00", + "2023-12-09T04:12:00", + "2023-12-09T04:15:00", + "2023-12-09T04:18:00", + "2023-12-09T04:21:00", + "2023-12-09T04:24:00", + "2023-12-09T04:27:00", + "2023-12-09T04:30:00", + "2023-12-09T04:33:00", + "2023-12-09T04:36:00", + "2023-12-09T04:39:00", + "2023-12-09T04:42:00", + "2023-12-09T04:45:00", + "2023-12-09T04:48:00", + "2023-12-09T04:51:00", + "2023-12-09T04:54:00", + "2023-12-09T04:57:00", + "2023-12-09T05:00:00", + "2023-12-09T05:03:00", + "2023-12-09T05:06:00", + "2023-12-09T05:09:00", + "2023-12-09T05:12:00", + "2023-12-09T05:15:00", + "2023-12-09T05:18:00", + "2023-12-09T05:21:00", + "2023-12-09T05:24:00", + "2023-12-09T05:27:00", + "2023-12-09T05:30:00", + "2023-12-09T05:33:00", + "2023-12-09T05:36:00", + "2023-12-09T05:39:00", + "2023-12-09T05:42:00", + "2023-12-09T05:45:00", + "2023-12-09T05:48:00", + "2023-12-09T05:51:00", + "2023-12-09T05:54:00", + "2023-12-09T05:57:00", + "2023-12-09T06:00:00", + "2023-12-09T06:03:00", + "2023-12-09T06:06:00", + "2023-12-09T06:09:00", + "2023-12-09T06:12:00", + "2023-12-09T06:15:00", + "2023-12-09T06:18:00", + "2023-12-09T06:21:00", + "2023-12-09T06:24:00", + "2023-12-09T06:27:00", + "2023-12-09T06:30:00", + "2023-12-09T06:33:00", + "2023-12-09T06:36:00", + "2023-12-09T06:39:00", + "2023-12-09T06:42:00", + "2023-12-09T06:45:00", + "2023-12-09T06:48:00", + "2023-12-09T06:51:00", + "2023-12-09T06:54:00", + "2023-12-09T06:57:00", + "2023-12-09T07:00:00", + "2023-12-09T07:03:00", + "2023-12-09T07:06:00", + "2023-12-09T07:09:00", + "2023-12-09T07:12:00", + "2023-12-09T07:15:00", + "2023-12-09T07:18:00", + "2023-12-09T07:21:00", + "2023-12-09T07:24:00", + "2023-12-09T07:27:00", + "2023-12-09T07:30:00", + "2023-12-09T07:33:00", + "2023-12-09T07:36:00", + "2023-12-09T07:39:00", + "2023-12-09T07:42:00", + "2023-12-09T07:45:00", + "2023-12-09T07:48:00", + "2023-12-09T07:51:00", + "2023-12-09T07:54:00", + "2023-12-09T07:57:00", + "2023-12-09T08:00:00", + "2023-12-09T08:03:00", + "2023-12-09T08:06:00", + "2023-12-09T08:09:00", + "2023-12-09T08:12:00", + "2023-12-09T08:15:00", + "2023-12-09T08:18:00", + "2023-12-09T08:21:00", + "2023-12-09T08:24:00", + "2023-12-09T08:27:00", + "2023-12-09T08:30:00", + "2023-12-09T08:33:00", + "2023-12-09T08:36:00", + "2023-12-09T08:39:00", + "2023-12-09T08:42:00", + "2023-12-09T08:45:00", + "2023-12-09T08:48:00", + "2023-12-09T08:51:00", + "2023-12-09T08:54:00", + "2023-12-09T08:57:00", + "2023-12-09T09:00:00", + "2023-12-09T09:03:00", + "2023-12-09T09:06:00", + "2023-12-09T09:09:00", + "2023-12-09T09:12:00", + "2023-12-09T09:15:00", + "2023-12-09T09:18:00", + "2023-12-09T09:21:00", + "2023-12-09T09:24:00", + "2023-12-09T09:27:00", + "2023-12-09T09:30:00", + "2023-12-09T09:33:00", + "2023-12-09T09:36:00", + "2023-12-09T09:39:00", + "2023-12-09T09:42:00", + "2023-12-09T09:45:00", + "2023-12-09T09:48:00", + "2023-12-09T09:51:00", + "2023-12-09T09:54:00", + "2023-12-09T09:57:00", + "2023-12-09T10:00:00", + "2023-12-09T10:03:00", + "2023-12-09T10:06:00", + "2023-12-09T10:09:00", + "2023-12-09T10:12:00", + "2023-12-09T10:15:00", + "2023-12-09T10:18:00", + "2023-12-09T10:21:00", + "2023-12-09T10:24:00", + "2023-12-09T10:27:00", + "2023-12-09T10:30:00", + "2023-12-09T10:33:00", + "2023-12-09T10:36:00", + "2023-12-09T10:39:00", + "2023-12-09T10:42:00", + "2023-12-09T10:45:00", + "2023-12-09T10:48:00", + "2023-12-09T10:51:00", + "2023-12-09T10:54:00", + "2023-12-09T10:57:00", + "2023-12-09T11:00:00", + "2023-12-09T11:03:00", + "2023-12-09T11:06:00", + "2023-12-09T11:09:00", + "2023-12-09T11:12:00", + "2023-12-09T11:15:00", + "2023-12-09T11:18:00", + "2023-12-09T11:21:00", + "2023-12-09T11:24:00", + "2023-12-09T11:27:00", + "2023-12-09T11:30:00", + "2023-12-09T11:33:00", + "2023-12-09T11:36:00", + "2023-12-09T11:39:00", + "2023-12-09T11:42:00", + "2023-12-09T11:45:00", + "2023-12-09T11:48:00", + "2023-12-09T11:51:00", + "2023-12-09T11:54:00", + "2023-12-09T11:57:00", + "2023-12-09T12:00:00", + "2023-12-09T12:03:00", + "2023-12-09T12:06:00", + "2023-12-09T12:09:00", + "2023-12-09T12:12:00", + "2023-12-09T12:15:00", + "2023-12-09T12:18:00", + "2023-12-09T12:21:00", + "2023-12-09T12:24:00", + "2023-12-09T12:27:00", + "2023-12-09T12:30:00", + "2023-12-09T12:33:00", + "2023-12-09T12:36:00", + "2023-12-09T12:39:00", + "2023-12-09T12:42:00", + "2023-12-09T12:45:00", + "2023-12-09T12:48:00", + "2023-12-09T12:51:00", + "2023-12-09T12:54:00", + "2023-12-09T12:57:00", + "2023-12-09T13:00:00", + "2023-12-09T13:03:00", + "2023-12-09T13:06:00", + "2023-12-09T13:09:00", + "2023-12-09T13:12:00", + "2023-12-09T13:15:00", + "2023-12-09T13:18:00", + "2023-12-09T13:21:00", + "2023-12-09T13:24:00", + "2023-12-09T13:27:00", + "2023-12-09T13:30:00", + "2023-12-09T13:33:00", + "2023-12-09T13:36:00", + "2023-12-09T13:39:00", + "2023-12-09T13:42:00", + "2023-12-09T13:45:00", + "2023-12-09T13:48:00", + "2023-12-09T13:51:00", + "2023-12-09T13:54:00", + "2023-12-09T13:57:00", + "2023-12-09T14:00:00", + "2023-12-09T14:03:00", + "2023-12-09T14:06:00", + "2023-12-09T14:09:00", + "2023-12-09T14:12:00", + "2023-12-09T14:15:00", + "2023-12-09T14:18:00", + "2023-12-09T14:21:00", + "2023-12-09T14:24:00", + "2023-12-09T14:27:00", + "2023-12-09T14:30:00", + "2023-12-09T14:33:00", + "2023-12-09T14:36:00", + "2023-12-09T14:39:00", + "2023-12-09T14:42:00", + "2023-12-09T14:45:00", + "2023-12-09T14:48:00", + "2023-12-09T14:51:00", + "2023-12-09T14:54:00", + "2023-12-09T14:57:00", + "2023-12-09T15:00:00", + "2023-12-09T15:03:00", + "2023-12-09T15:06:00", + "2023-12-09T15:09:00", + "2023-12-09T15:12:00", + "2023-12-09T15:15:00", + "2023-12-09T15:18:00", + "2023-12-09T15:21:00", + "2023-12-09T15:24:00", + "2023-12-09T15:27:00", + "2023-12-09T15:30:00", + "2023-12-09T15:33:00", + "2023-12-09T15:36:00", + "2023-12-09T15:39:00", + "2023-12-09T15:42:00", + "2023-12-09T15:45:00", + "2023-12-09T15:48:00", + "2023-12-09T15:51:00", + "2023-12-09T15:54:00", + "2023-12-09T15:57:00", + "2023-12-09T16:00:00", + "2023-12-09T16:03:00", + "2023-12-09T16:06:00", + "2023-12-09T16:09:00", + "2023-12-09T16:12:00", + "2023-12-09T16:15:00", + "2023-12-09T16:18:00", + "2023-12-09T16:21:00", + "2023-12-09T16:24:00", + "2023-12-09T16:27:00", + "2023-12-09T16:30:00", + "2023-12-09T16:33:00", + "2023-12-09T16:36:00", + "2023-12-09T16:39:00", + "2023-12-09T16:42:00", + "2023-12-09T16:45:00", + "2023-12-09T16:48:00", + "2023-12-09T16:51:00", + "2023-12-09T16:54:00", + "2023-12-09T16:57:00", + "2023-12-09T17:00:00", + "2023-12-09T17:03:00", + "2023-12-09T17:06:00", + "2023-12-09T17:09:00", + "2023-12-09T17:12:00", + "2023-12-09T17:15:00", + "2023-12-09T17:18:00", + "2023-12-09T17:21:00", + "2023-12-09T17:24:00", + "2023-12-09T17:27:00", + "2023-12-09T17:30:00", + "2023-12-09T17:33:00", + "2023-12-09T17:36:00", + "2023-12-09T17:39:00", + "2023-12-09T17:42:00", + "2023-12-09T17:45:00", + "2023-12-09T17:48:00", + "2023-12-09T17:51:00", + "2023-12-09T17:54:00", + "2023-12-09T17:57:00", + "2023-12-09T18:00:00", + "2023-12-09T18:03:00", + "2023-12-09T18:06:00", + "2023-12-09T18:09:00", + "2023-12-09T18:12:00", + "2023-12-09T18:15:00", + "2023-12-09T18:18:00", + "2023-12-09T18:21:00", + "2023-12-09T18:24:00", + "2023-12-09T18:27:00", + "2023-12-09T18:30:00", + "2023-12-09T18:33:00", + "2023-12-09T18:36:00", + "2023-12-09T18:39:00", + "2023-12-09T18:42:00", + "2023-12-09T18:45:00", + "2023-12-09T18:48:00", + "2023-12-09T18:51:00", + "2023-12-09T18:54:00", + "2023-12-09T18:57:00", + "2023-12-09T19:00:00", + "2023-12-09T19:03:00", + "2023-12-09T19:06:00", + "2023-12-09T19:09:00", + "2023-12-09T19:12:00", + "2023-12-09T19:15:00", + "2023-12-09T19:18:00", + "2023-12-09T19:21:00", + "2023-12-09T19:24:00", + "2023-12-09T19:27:00", + "2023-12-09T19:30:00", + "2023-12-09T19:33:00", + "2023-12-09T19:36:00", + "2023-12-09T19:39:00", + "2023-12-09T19:42:00", + "2023-12-09T19:45:00", + "2023-12-09T19:48:00", + "2023-12-09T19:51:00", + "2023-12-09T19:54:00", + "2023-12-09T19:57:00", + "2023-12-09T20:00:00", + "2023-12-09T20:03:00", + "2023-12-09T20:06:00", + "2023-12-09T20:09:00", + "2023-12-09T20:12:00", + "2023-12-09T20:15:00", + "2023-12-09T20:18:00", + "2023-12-09T20:21:00", + "2023-12-09T20:24:00", + "2023-12-09T20:27:00", + "2023-12-09T20:30:00", + "2023-12-09T20:33:00", + "2023-12-09T20:36:00", + "2023-12-09T20:39:00", + "2023-12-09T20:42:00", + "2023-12-09T20:45:00", + "2023-12-09T20:48:00", + "2023-12-09T20:51:00", + "2023-12-09T20:54:00", + "2023-12-09T20:57:00", + "2023-12-09T21:00:00", + "2023-12-09T21:03:00", + "2023-12-09T21:06:00", + "2023-12-09T21:09:00", + "2023-12-09T21:12:00", + "2023-12-09T21:15:00", + "2023-12-09T21:18:00", + "2023-12-09T21:21:00", + "2023-12-09T21:24:00", + "2023-12-09T21:27:00", + "2023-12-09T21:30:00", + "2023-12-09T21:33:00", + "2023-12-09T21:36:00", + "2023-12-09T21:39:00", + "2023-12-09T21:42:00", + "2023-12-09T21:45:00", + "2023-12-09T21:48:00", + "2023-12-09T21:51:00", + "2023-12-09T21:54:00", + "2023-12-09T21:57:00", + "2023-12-09T22:00:00", + "2023-12-09T22:03:00", + "2023-12-09T22:06:00", + "2023-12-09T22:09:00", + "2023-12-09T22:12:00", + "2023-12-09T22:15:00", + "2023-12-09T22:18:00", + "2023-12-09T22:21:00", + "2023-12-09T22:24:00", + "2023-12-09T22:27:00", + "2023-12-09T22:30:00", + "2023-12-09T22:33:00", + "2023-12-09T22:36:00", + "2023-12-09T22:39:00", + "2023-12-09T22:42:00", + "2023-12-09T22:45:00", + "2023-12-09T22:48:00", + "2023-12-09T22:51:00", + "2023-12-09T22:54:00", + "2023-12-09T22:57:00", + "2023-12-09T23:00:00", + "2023-12-09T23:03:00", + "2023-12-09T23:06:00", + "2023-12-09T23:09:00", + "2023-12-09T23:12:00", + "2023-12-09T23:15:00", + "2023-12-09T23:18:00", + "2023-12-09T23:21:00", + "2023-12-09T23:24:00", + "2023-12-09T23:27:00", + "2023-12-09T23:30:00", + "2023-12-09T23:33:00", + "2023-12-09T23:36:00", + "2023-12-09T23:39:00", + "2023-12-09T23:42:00", + "2023-12-09T23:45:00", + "2023-12-09T23:48:00", + "2023-12-09T23:51:00", + "2023-12-09T23:54:00", + "2023-12-09T23:57:00", + "2023-12-10T00:00:00", + "2023-12-10T00:03:00", + "2023-12-10T00:06:00", + "2023-12-10T00:09:00", + "2023-12-10T00:12:00", + "2023-12-10T00:15:00", + "2023-12-10T00:18:00", + "2023-12-10T00:21:00", + "2023-12-10T00:24:00", + "2023-12-10T00:27:00", + "2023-12-10T00:30:00", + "2023-12-10T00:33:00", + "2023-12-10T00:36:00", + "2023-12-10T00:39:00", + "2023-12-10T00:42:00", + "2023-12-10T00:45:00", + "2023-12-10T00:48:00", + "2023-12-10T00:51:00", + "2023-12-10T00:54:00", + "2023-12-10T00:57:00", + "2023-12-10T01:00:00", + "2023-12-10T01:03:00", + "2023-12-10T01:06:00", + "2023-12-10T01:09:00", + "2023-12-10T01:12:00", + "2023-12-10T01:15:00", + "2023-12-10T01:18:00", + "2023-12-10T01:21:00", + "2023-12-10T01:24:00", + "2023-12-10T01:27:00", + "2023-12-10T01:30:00", + "2023-12-10T01:33:00", + "2023-12-10T01:36:00", + "2023-12-10T01:39:00", + "2023-12-10T01:42:00", + "2023-12-10T01:45:00", + "2023-12-10T01:48:00", + "2023-12-10T01:51:00", + "2023-12-10T01:54:00", + "2023-12-10T01:57:00", + "2023-12-10T02:00:00", + "2023-12-10T02:03:00", + "2023-12-10T02:06:00", + "2023-12-10T02:09:00", + "2023-12-10T02:12:00", + "2023-12-10T02:15:00", + "2023-12-10T02:18:00", + "2023-12-10T02:21:00", + "2023-12-10T02:24:00", + "2023-12-10T02:27:00", + "2023-12-10T02:30:00", + "2023-12-10T02:33:00", + "2023-12-10T02:36:00", + "2023-12-10T02:39:00", + "2023-12-10T02:42:00", + "2023-12-10T02:45:00", + "2023-12-10T02:48:00", + "2023-12-10T02:51:00", + "2023-12-10T02:54:00", + "2023-12-10T02:57:00", + "2023-12-10T03:00:00", + "2023-12-10T03:03:00", + "2023-12-10T03:06:00", + "2023-12-10T03:09:00", + "2023-12-10T03:12:00", + "2023-12-10T03:15:00", + "2023-12-10T03:18:00", + "2023-12-10T03:21:00", + "2023-12-10T03:24:00", + "2023-12-10T03:27:00", + "2023-12-10T03:30:00", + "2023-12-10T03:33:00", + "2023-12-10T03:36:00", + "2023-12-10T03:39:00", + "2023-12-10T03:42:00", + "2023-12-10T03:45:00", + "2023-12-10T03:48:00", + "2023-12-10T03:51:00", + "2023-12-10T03:54:00", + "2023-12-10T03:57:00", + "2023-12-10T04:00:00", + "2023-12-10T04:03:00", + "2023-12-10T04:06:00", + "2023-12-10T04:09:00", + "2023-12-10T04:12:00", + "2023-12-10T04:15:00", + "2023-12-10T04:18:00", + "2023-12-10T04:21:00", + "2023-12-10T04:24:00", + "2023-12-10T04:27:00", + "2023-12-10T04:30:00", + "2023-12-10T04:33:00", + "2023-12-10T04:36:00", + "2023-12-10T04:39:00", + "2023-12-10T04:42:00", + "2023-12-10T04:45:00", + "2023-12-10T04:48:00", + "2023-12-10T04:51:00", + "2023-12-10T04:54:00", + "2023-12-10T04:57:00", + "2023-12-10T05:00:00", + "2023-12-10T05:03:00", + "2023-12-10T05:06:00", + "2023-12-10T05:09:00", + "2023-12-10T05:12:00", + "2023-12-10T05:15:00", + "2023-12-10T05:18:00", + "2023-12-10T05:21:00", + "2023-12-10T05:24:00", + "2023-12-10T05:27:00", + "2023-12-10T05:30:00", + "2023-12-10T05:33:00", + "2023-12-10T05:36:00", + "2023-12-10T05:39:00", + "2023-12-10T05:42:00", + "2023-12-10T05:45:00", + "2023-12-10T05:48:00", + "2023-12-10T05:51:00", + "2023-12-10T05:54:00", + "2023-12-10T05:57:00", + "2023-12-10T06:00:00", + "2023-12-10T06:03:00", + "2023-12-10T06:06:00", + "2023-12-10T06:09:00", + "2023-12-10T06:12:00", + "2023-12-10T06:15:00", + "2023-12-10T06:18:00", + "2023-12-10T06:21:00", + "2023-12-10T06:24:00", + "2023-12-10T06:27:00", + "2023-12-10T06:30:00", + "2023-12-10T06:33:00", + "2023-12-10T06:36:00", + "2023-12-10T06:39:00", + "2023-12-10T06:42:00", + "2023-12-10T06:45:00", + "2023-12-10T06:48:00", + "2023-12-10T06:51:00", + "2023-12-10T06:54:00", + "2023-12-10T06:57:00", + "2023-12-10T07:00:00", + "2023-12-10T07:03:00", + "2023-12-10T07:06:00", + "2023-12-10T07:09:00", + "2023-12-10T07:12:00", + "2023-12-10T07:15:00", + "2023-12-10T07:18:00", + "2023-12-10T07:21:00", + "2023-12-10T07:24:00", + "2023-12-10T07:27:00", + "2023-12-10T07:30:00", + "2023-12-10T07:33:00", + "2023-12-10T07:36:00", + "2023-12-10T07:39:00", + "2023-12-10T07:42:00", + "2023-12-10T07:45:00", + "2023-12-10T07:48:00", + "2023-12-10T07:51:00", + "2023-12-10T07:54:00", + "2023-12-10T07:57:00", + "2023-12-10T08:00:00", + "2023-12-10T08:03:00", + "2023-12-10T08:06:00", + "2023-12-10T08:09:00", + "2023-12-10T08:12:00", + "2023-12-10T08:15:00", + "2023-12-10T08:18:00", + "2023-12-10T08:21:00", + "2023-12-10T08:24:00", + "2023-12-10T08:27:00", + "2023-12-10T08:30:00", + "2023-12-10T08:33:00", + "2023-12-10T08:36:00", + "2023-12-10T08:39:00", + "2023-12-10T08:42:00", + "2023-12-10T08:45:00", + "2023-12-10T08:48:00", + "2023-12-10T08:51:00", + "2023-12-10T08:54:00", + "2023-12-10T08:57:00", + "2023-12-10T09:00:00", + "2023-12-10T09:03:00", + "2023-12-10T09:06:00", + "2023-12-10T09:09:00", + "2023-12-10T09:12:00", + "2023-12-10T09:15:00", + "2023-12-10T09:18:00", + "2023-12-10T09:21:00", + "2023-12-10T09:24:00", + "2023-12-10T09:27:00", + "2023-12-10T09:30:00", + "2023-12-10T09:33:00", + "2023-12-10T09:36:00", + "2023-12-10T09:39:00", + "2023-12-10T09:42:00", + "2023-12-10T09:45:00", + "2023-12-10T09:48:00", + "2023-12-10T09:51:00", + "2023-12-10T09:54:00", + "2023-12-10T09:57:00", + "2023-12-10T10:00:00", + "2023-12-10T10:03:00", + "2023-12-10T10:06:00", + "2023-12-10T10:09:00", + "2023-12-10T10:12:00", + "2023-12-10T10:15:00", + "2023-12-10T10:18:00", + "2023-12-10T10:21:00", + "2023-12-10T10:24:00", + "2023-12-10T10:27:00", + "2023-12-10T10:30:00", + "2023-12-10T10:33:00", + "2023-12-10T10:36:00", + "2023-12-10T10:39:00", + "2023-12-10T10:42:00", + "2023-12-10T10:45:00", + "2023-12-10T10:48:00", + "2023-12-10T10:51:00", + "2023-12-10T10:54:00", + "2023-12-10T10:57:00", + "2023-12-10T11:00:00", + "2023-12-10T11:03:00", + "2023-12-10T11:06:00", + "2023-12-10T11:09:00", + "2023-12-10T11:12:00", + "2023-12-10T11:15:00", + "2023-12-10T11:18:00", + "2023-12-10T11:21:00", + "2023-12-10T11:24:00", + "2023-12-10T11:27:00", + "2023-12-10T11:30:00", + "2023-12-10T11:33:00", + "2023-12-10T11:36:00", + "2023-12-10T11:39:00", + "2023-12-10T11:42:00", + "2023-12-10T11:45:00", + "2023-12-10T11:48:00", + "2023-12-10T11:51:00", + "2023-12-10T11:54:00", + "2023-12-10T11:57:00", + "2023-12-10T12:00:00", + "2023-12-10T12:03:00", + "2023-12-10T12:06:00", + "2023-12-10T12:09:00", + "2023-12-10T12:12:00", + "2023-12-10T12:15:00", + "2023-12-10T12:18:00", + "2023-12-10T12:21:00", + "2023-12-10T12:24:00", + "2023-12-10T12:27:00", + "2023-12-10T12:30:00", + "2023-12-10T12:33:00", + "2023-12-10T12:36:00", + "2023-12-10T12:39:00", + "2023-12-10T12:42:00", + "2023-12-10T12:45:00", + "2023-12-10T12:48:00", + "2023-12-10T12:51:00", + "2023-12-10T12:54:00", + "2023-12-10T12:57:00", + "2023-12-10T13:00:00", + "2023-12-10T13:03:00", + "2023-12-10T13:06:00", + "2023-12-10T13:09:00", + "2023-12-10T13:12:00", + "2023-12-10T13:15:00", + "2023-12-10T13:18:00", + "2023-12-10T13:21:00", + "2023-12-10T13:24:00", + "2023-12-10T13:27:00", + "2023-12-10T13:30:00", + "2023-12-10T13:33:00", + "2023-12-10T13:36:00", + "2023-12-10T13:39:00", + "2023-12-10T13:42:00", + "2023-12-10T13:45:00", + "2023-12-10T13:48:00", + "2023-12-10T13:51:00", + "2023-12-10T13:54:00", + "2023-12-10T13:57:00", + "2023-12-10T14:00:00", + "2023-12-10T14:03:00", + "2023-12-10T14:06:00", + "2023-12-10T14:09:00", + "2023-12-10T14:12:00", + "2023-12-10T14:15:00", + "2023-12-10T14:18:00", + "2023-12-10T14:21:00", + "2023-12-10T14:24:00", + "2023-12-10T14:27:00", + "2023-12-10T14:30:00", + "2023-12-10T14:33:00", + "2023-12-10T14:36:00", + "2023-12-10T14:39:00", + "2023-12-10T14:42:00", + "2023-12-10T14:45:00", + "2023-12-10T14:48:00", + "2023-12-10T14:51:00", + "2023-12-10T14:54:00", + "2023-12-10T14:57:00", + "2023-12-10T15:00:00", + "2023-12-10T15:03:00", + "2023-12-10T15:06:00", + "2023-12-10T15:09:00", + "2023-12-10T15:12:00", + "2023-12-10T15:15:00", + "2023-12-10T15:18:00", + "2023-12-10T15:21:00", + "2023-12-10T15:24:00", + "2023-12-10T15:27:00", + "2023-12-10T15:30:00", + "2023-12-10T15:33:00", + "2023-12-10T15:36:00", + "2023-12-10T15:39:00", + "2023-12-10T15:42:00", + "2023-12-10T15:45:00", + "2023-12-10T15:48:00", + "2023-12-10T15:51:00", + "2023-12-10T15:54:00", + "2023-12-10T15:57:00", + "2023-12-10T16:00:00", + "2023-12-10T16:03:00", + "2023-12-10T16:06:00", + "2023-12-10T16:09:00", + "2023-12-10T16:12:00", + "2023-12-10T16:15:00", + "2023-12-10T16:18:00", + "2023-12-10T16:21:00", + "2023-12-10T16:24:00", + "2023-12-10T16:27:00", + "2023-12-10T16:30:00", + "2023-12-10T16:33:00", + "2023-12-10T16:36:00", + "2023-12-10T16:39:00", + "2023-12-10T16:42:00", + "2023-12-10T16:45:00", + "2023-12-10T16:48:00", + "2023-12-10T16:51:00", + "2023-12-10T16:54:00", + "2023-12-10T16:57:00", + "2023-12-10T17:00:00", + "2023-12-10T17:03:00", + "2023-12-10T17:06:00", + "2023-12-10T17:09:00", + "2023-12-10T17:12:00", + "2023-12-10T17:15:00", + "2023-12-10T17:18:00", + "2023-12-10T17:21:00", + "2023-12-10T17:24:00", + "2023-12-10T17:27:00", + "2023-12-10T17:30:00", + "2023-12-10T17:33:00", + "2023-12-10T17:36:00", + "2023-12-10T17:39:00", + "2023-12-10T17:42:00", + "2023-12-10T17:45:00", + "2023-12-10T17:48:00", + "2023-12-10T17:51:00", + "2023-12-10T17:54:00", + "2023-12-10T17:57:00", + "2023-12-10T18:00:00", + "2023-12-10T18:03:00", + "2023-12-10T18:06:00", + "2023-12-10T18:09:00", + "2023-12-10T18:12:00", + "2023-12-10T18:15:00", + "2023-12-10T18:18:00", + "2023-12-10T18:21:00", + "2023-12-10T18:24:00", + "2023-12-10T18:27:00", + "2023-12-10T18:30:00", + "2023-12-10T18:33:00", + "2023-12-10T18:36:00", + "2023-12-10T18:39:00", + "2023-12-10T18:42:00", + "2023-12-10T18:45:00", + "2023-12-10T18:48:00", + "2023-12-10T18:51:00", + "2023-12-10T18:54:00", + "2023-12-10T18:57:00", + "2023-12-10T19:00:00", + "2023-12-10T19:03:00", + "2023-12-10T19:06:00", + "2023-12-10T19:09:00", + "2023-12-10T19:12:00", + "2023-12-10T19:15:00", + "2023-12-10T19:18:00", + "2023-12-10T19:21:00", + "2023-12-10T19:24:00", + "2023-12-10T19:27:00", + "2023-12-10T19:30:00", + "2023-12-10T19:33:00", + "2023-12-10T19:36:00", + "2023-12-10T19:39:00", + "2023-12-10T19:42:00", + "2023-12-10T19:45:00", + "2023-12-10T19:48:00", + "2023-12-10T19:51:00", + "2023-12-10T19:54:00", + "2023-12-10T19:57:00", + "2023-12-10T20:00:00", + "2023-12-10T20:03:00", + "2023-12-10T20:06:00", + "2023-12-10T20:09:00", + "2023-12-10T20:12:00", + "2023-12-10T20:15:00", + "2023-12-10T20:18:00", + "2023-12-10T20:21:00", + "2023-12-10T20:24:00", + "2023-12-10T20:27:00", + "2023-12-10T20:30:00", + "2023-12-10T20:33:00", + "2023-12-10T20:36:00", + "2023-12-10T20:39:00", + "2023-12-10T20:42:00", + "2023-12-10T20:45:00", + "2023-12-10T20:48:00", + "2023-12-10T20:51:00", + "2023-12-10T20:54:00", + "2023-12-10T20:57:00", + "2023-12-10T21:00:00", + "2023-12-10T21:03:00", + "2023-12-10T21:06:00", + "2023-12-10T21:09:00", + "2023-12-10T21:12:00", + "2023-12-10T21:15:00", + "2023-12-10T21:18:00", + "2023-12-10T21:21:00", + "2023-12-10T21:24:00", + "2023-12-10T21:27:00", + "2023-12-10T21:30:00", + "2023-12-10T21:33:00", + "2023-12-10T21:36:00", + "2023-12-10T21:39:00", + "2023-12-10T21:42:00", + "2023-12-10T21:45:00", + "2023-12-10T21:48:00", + "2023-12-10T21:51:00", + "2023-12-10T21:54:00", + "2023-12-10T21:57:00", + "2023-12-10T22:00:00", + "2023-12-10T22:03:00", + "2023-12-10T22:06:00", + "2023-12-10T22:09:00", + "2023-12-10T22:12:00", + "2023-12-10T22:15:00", + "2023-12-10T22:18:00", + "2023-12-10T22:21:00", + "2023-12-10T22:24:00", + "2023-12-10T22:27:00", + "2023-12-10T22:30:00", + "2023-12-10T22:33:00", + "2023-12-10T22:36:00", + "2023-12-10T22:39:00", + "2023-12-10T22:42:00", + "2023-12-10T22:45:00", + "2023-12-10T22:48:00", + "2023-12-10T22:51:00", + "2023-12-10T22:54:00", + "2023-12-10T22:57:00", + "2023-12-10T23:00:00", + "2023-12-10T23:03:00", + "2023-12-10T23:06:00", + "2023-12-10T23:09:00", + "2023-12-10T23:12:00", + "2023-12-10T23:15:00", + "2023-12-10T23:18:00", + "2023-12-10T23:21:00", + "2023-12-10T23:24:00", + "2023-12-10T23:27:00", + "2023-12-10T23:30:00", + "2023-12-10T23:33:00", + "2023-12-10T23:36:00", + "2023-12-10T23:39:00", + "2023-12-10T23:42:00", + "2023-12-10T23:45:00", + "2023-12-10T23:48:00", + "2023-12-10T23:51:00", + "2023-12-10T23:54:00", + "2023-12-10T23:57:00", + "2023-12-11T00:00:00", + "2023-12-11T00:03:00", + "2023-12-11T00:06:00", + "2023-12-11T00:09:00", + "2023-12-11T00:12:00", + "2023-12-11T00:15:00", + "2023-12-11T00:18:00", + "2023-12-11T00:21:00", + "2023-12-11T00:24:00", + "2023-12-11T00:27:00", + "2023-12-11T00:30:00", + "2023-12-11T00:33:00", + "2023-12-11T00:36:00", + "2023-12-11T00:39:00", + "2023-12-11T00:42:00", + "2023-12-11T00:45:00", + "2023-12-11T00:48:00", + "2023-12-11T00:51:00", + "2023-12-11T00:54:00", + "2023-12-11T00:57:00", + "2023-12-11T01:00:00", + "2023-12-11T01:03:00", + "2023-12-11T01:06:00", + "2023-12-11T01:09:00", + "2023-12-11T01:12:00", + "2023-12-11T01:15:00", + "2023-12-11T01:18:00", + "2023-12-11T01:21:00", + "2023-12-11T01:24:00", + "2023-12-11T01:27:00", + "2023-12-11T01:30:00", + "2023-12-11T01:33:00", + "2023-12-11T01:36:00", + "2023-12-11T01:39:00", + "2023-12-11T01:42:00", + "2023-12-11T01:45:00", + "2023-12-11T01:48:00", + "2023-12-11T01:51:00", + "2023-12-11T01:54:00", + "2023-12-11T01:57:00", + "2023-12-11T02:00:00", + "2023-12-11T02:03:00", + "2023-12-11T02:06:00", + "2023-12-11T02:09:00", + "2023-12-11T02:12:00", + "2023-12-11T02:15:00", + "2023-12-11T02:18:00", + "2023-12-11T02:21:00", + "2023-12-11T02:24:00", + "2023-12-11T02:27:00", + "2023-12-11T02:30:00", + "2023-12-11T02:33:00", + "2023-12-11T02:36:00", + "2023-12-11T02:39:00", + "2023-12-11T02:42:00", + "2023-12-11T02:45:00", + "2023-12-11T02:48:00", + "2023-12-11T02:51:00", + "2023-12-11T02:54:00", + "2023-12-11T02:57:00", + "2023-12-11T03:00:00", + "2023-12-11T03:03:00", + "2023-12-11T03:06:00", + "2023-12-11T03:09:00", + "2023-12-11T03:12:00", + "2023-12-11T03:15:00", + "2023-12-11T03:18:00", + "2023-12-11T03:21:00", + "2023-12-11T03:24:00", + "2023-12-11T03:27:00", + "2023-12-11T03:30:00", + "2023-12-11T03:33:00", + "2023-12-11T03:36:00", + "2023-12-11T03:39:00", + "2023-12-11T03:42:00", + "2023-12-11T03:45:00", + "2023-12-11T03:48:00", + "2023-12-11T03:51:00", + "2023-12-11T03:54:00", + "2023-12-11T03:57:00", + "2023-12-11T04:00:00", + "2023-12-11T04:03:00", + "2023-12-11T04:06:00", + "2023-12-11T04:09:00", + "2023-12-11T04:12:00", + "2023-12-11T04:15:00", + "2023-12-11T04:18:00", + "2023-12-11T04:21:00", + "2023-12-11T04:24:00", + "2023-12-11T04:27:00", + "2023-12-11T04:30:00", + "2023-12-11T04:33:00", + "2023-12-11T04:36:00", + "2023-12-11T04:39:00", + "2023-12-11T04:42:00", + "2023-12-11T04:45:00", + "2023-12-11T04:48:00", + "2023-12-11T04:51:00", + "2023-12-11T04:54:00", + "2023-12-11T04:57:00", + "2023-12-11T05:00:00", + "2023-12-11T05:03:00", + "2023-12-11T05:06:00", + "2023-12-11T05:09:00", + "2023-12-11T05:12:00", + "2023-12-11T05:15:00", + "2023-12-11T05:18:00", + "2023-12-11T05:21:00", + "2023-12-11T05:24:00", + "2023-12-11T05:27:00", + "2023-12-11T05:30:00", + "2023-12-11T05:33:00", + "2023-12-11T05:36:00", + "2023-12-11T05:39:00", + "2023-12-11T05:42:00", + "2023-12-11T05:45:00", + "2023-12-11T05:48:00", + "2023-12-11T05:51:00", + "2023-12-11T05:54:00", + "2023-12-11T05:57:00", + "2023-12-11T06:00:00", + "2023-12-11T06:03:00", + "2023-12-11T06:06:00", + "2023-12-11T06:09:00", + "2023-12-11T06:12:00", + "2023-12-11T06:15:00", + "2023-12-11T06:18:00", + "2023-12-11T06:21:00", + "2023-12-11T06:24:00", + "2023-12-11T06:27:00", + "2023-12-11T06:30:00", + "2023-12-11T06:33:00", + "2023-12-11T06:36:00", + "2023-12-11T06:39:00", + "2023-12-11T06:42:00", + "2023-12-11T06:45:00", + "2023-12-11T06:48:00", + "2023-12-11T06:51:00", + "2023-12-11T06:54:00", + "2023-12-11T06:57:00", + "2023-12-11T07:00:00", + "2023-12-11T07:03:00", + "2023-12-11T07:06:00", + "2023-12-11T07:09:00", + "2023-12-11T07:12:00", + "2023-12-11T07:15:00", + "2023-12-11T07:18:00", + "2023-12-11T07:21:00", + "2023-12-11T07:24:00", + "2023-12-11T07:27:00", + "2023-12-11T07:30:00", + "2023-12-11T07:33:00", + "2023-12-11T07:36:00", + "2023-12-11T07:39:00", + "2023-12-11T07:42:00", + "2023-12-11T07:45:00", + "2023-12-11T07:48:00", + "2023-12-11T07:51:00", + "2023-12-11T07:54:00", + "2023-12-11T07:57:00", + "2023-12-11T08:00:00", + "2023-12-11T08:03:00", + "2023-12-11T08:06:00", + "2023-12-11T08:09:00", + "2023-12-11T08:12:00", + "2023-12-11T08:15:00", + "2023-12-11T08:18:00", + "2023-12-11T08:21:00", + "2023-12-11T08:24:00", + "2023-12-11T08:27:00", + "2023-12-11T08:30:00", + "2023-12-11T08:33:00", + "2023-12-11T08:36:00", + "2023-12-11T08:39:00", + "2023-12-11T08:42:00", + "2023-12-11T08:45:00", + "2023-12-11T08:48:00", + "2023-12-11T08:51:00", + "2023-12-11T08:54:00", + "2023-12-11T08:57:00", + "2023-12-11T09:00:00", + "2023-12-11T09:03:00", + "2023-12-11T09:06:00", + "2023-12-11T09:09:00", + "2023-12-11T09:12:00", + "2023-12-11T09:15:00", + "2023-12-11T09:18:00", + "2023-12-11T09:21:00", + "2023-12-11T09:24:00", + "2023-12-11T09:27:00", + "2023-12-11T09:30:00", + "2023-12-11T09:33:00", + "2023-12-11T09:36:00", + "2023-12-11T09:39:00", + "2023-12-11T09:42:00", + "2023-12-11T09:45:00", + "2023-12-11T09:48:00", + "2023-12-11T09:51:00", + "2023-12-11T09:54:00", + "2023-12-11T09:57:00", + "2023-12-11T10:00:00", + "2023-12-11T10:03:00", + "2023-12-11T10:06:00", + "2023-12-11T10:09:00", + "2023-12-11T10:12:00", + "2023-12-11T10:15:00", + "2023-12-11T10:18:00", + "2023-12-11T10:21:00", + "2023-12-11T10:24:00", + "2023-12-11T10:27:00", + "2023-12-11T10:30:00", + "2023-12-11T10:33:00", + "2023-12-11T10:36:00", + "2023-12-11T10:39:00", + "2023-12-11T10:42:00", + "2023-12-11T10:45:00", + "2023-12-11T10:48:00", + "2023-12-11T10:51:00", + "2023-12-11T10:54:00", + "2023-12-11T10:57:00", + "2023-12-11T11:00:00", + "2023-12-11T11:03:00", + "2023-12-11T11:06:00", + "2023-12-11T11:09:00", + "2023-12-11T11:12:00", + "2023-12-11T11:15:00", + "2023-12-11T11:18:00", + "2023-12-11T11:21:00", + "2023-12-11T11:24:00", + "2023-12-11T11:27:00", + "2023-12-11T11:30:00", + "2023-12-11T11:33:00", + "2023-12-11T11:36:00", + "2023-12-11T11:39:00", + "2023-12-11T11:42:00", + "2023-12-11T11:45:00", + "2023-12-11T11:48:00", + "2023-12-11T11:51:00", + "2023-12-11T11:54:00", + "2023-12-11T11:57:00", + "2023-12-11T12:00:00", + "2023-12-11T12:03:00", + "2023-12-11T12:06:00", + "2023-12-11T12:09:00", + "2023-12-11T12:12:00", + "2023-12-11T12:15:00", + "2023-12-11T12:18:00", + "2023-12-11T12:21:00", + "2023-12-11T12:24:00", + "2023-12-11T12:27:00", + "2023-12-11T12:30:00", + "2023-12-11T12:33:00", + "2023-12-11T12:36:00", + "2023-12-11T12:39:00", + "2023-12-11T12:42:00", + "2023-12-11T12:45:00", + "2023-12-11T12:48:00", + "2023-12-11T12:51:00", + "2023-12-11T12:54:00", + "2023-12-11T12:57:00", + "2023-12-11T13:00:00", + "2023-12-11T13:03:00", + "2023-12-11T13:06:00", + "2023-12-11T13:09:00", + "2023-12-11T13:12:00", + "2023-12-11T13:15:00", + "2023-12-11T13:18:00", + "2023-12-11T13:21:00", + "2023-12-11T13:24:00", + "2023-12-11T13:27:00", + "2023-12-11T13:30:00", + "2023-12-11T13:33:00", + "2023-12-11T13:36:00", + "2023-12-11T13:39:00", + "2023-12-11T13:42:00", + "2023-12-11T13:45:00", + "2023-12-11T13:48:00", + "2023-12-11T13:51:00", + "2023-12-11T13:54:00", + "2023-12-11T13:57:00", + "2023-12-11T14:00:00", + "2023-12-11T14:03:00", + "2023-12-11T14:06:00", + "2023-12-11T14:09:00", + "2023-12-11T14:12:00", + "2023-12-11T14:15:00", + "2023-12-11T14:18:00", + "2023-12-11T14:21:00", + "2023-12-11T14:24:00", + "2023-12-11T14:27:00", + "2023-12-11T14:30:00", + "2023-12-11T14:33:00", + "2023-12-11T14:36:00", + "2023-12-11T14:39:00", + "2023-12-11T14:42:00", + "2023-12-11T14:45:00", + "2023-12-11T14:48:00", + "2023-12-11T14:51:00", + "2023-12-11T14:54:00", + "2023-12-11T14:57:00", + "2023-12-11T15:00:00", + "2023-12-11T15:03:00", + "2023-12-11T15:06:00", + "2023-12-11T15:09:00", + "2023-12-11T15:12:00", + "2023-12-11T15:15:00", + "2023-12-11T15:18:00", + "2023-12-11T15:21:00", + "2023-12-11T15:24:00", + "2023-12-11T15:27:00", + "2023-12-11T15:30:00", + "2023-12-11T15:33:00", + "2023-12-11T15:36:00", + "2023-12-11T15:39:00", + "2023-12-11T15:42:00", + "2023-12-11T15:45:00", + "2023-12-11T15:48:00", + "2023-12-11T15:51:00", + "2023-12-11T15:54:00", + "2023-12-11T15:57:00", + "2023-12-11T16:00:00", + "2023-12-11T16:03:00", + "2023-12-11T16:06:00", + "2023-12-11T16:09:00", + "2023-12-11T16:12:00", + "2023-12-11T16:15:00", + "2023-12-11T16:18:00", + "2023-12-11T16:21:00", + "2023-12-11T16:24:00", + "2023-12-11T16:27:00", + "2023-12-11T16:30:00", + "2023-12-11T16:33:00", + "2023-12-11T16:36:00", + "2023-12-11T16:39:00", + "2023-12-11T16:42:00", + "2023-12-11T16:45:00", + "2023-12-11T16:48:00", + "2023-12-11T16:51:00", + "2023-12-11T16:54:00", + "2023-12-11T16:57:00", + "2023-12-11T17:00:00", + "2023-12-11T17:03:00", + "2023-12-11T17:06:00", + "2023-12-11T17:09:00", + "2023-12-11T17:12:00", + "2023-12-11T17:15:00", + "2023-12-11T17:18:00", + "2023-12-11T17:21:00", + "2023-12-11T17:24:00", + "2023-12-11T17:27:00", + "2023-12-11T17:30:00", + "2023-12-11T17:33:00", + "2023-12-11T17:36:00", + "2023-12-11T17:39:00", + "2023-12-11T17:42:00", + "2023-12-11T17:45:00", + "2023-12-11T17:48:00", + "2023-12-11T17:51:00", + "2023-12-11T17:54:00", + "2023-12-11T17:57:00", + "2023-12-11T18:00:00", + "2023-12-11T18:03:00", + "2023-12-11T18:06:00", + "2023-12-11T18:09:00", + "2023-12-11T18:12:00", + "2023-12-11T18:15:00", + "2023-12-11T18:18:00", + "2023-12-11T18:21:00", + "2023-12-11T18:24:00", + "2023-12-11T18:27:00", + "2023-12-11T18:30:00", + "2023-12-11T18:33:00", + "2023-12-11T18:36:00", + "2023-12-11T18:39:00", + "2023-12-11T18:42:00", + "2023-12-11T18:45:00", + "2023-12-11T18:48:00", + "2023-12-11T18:51:00", + "2023-12-11T18:54:00", + "2023-12-11T18:57:00", + "2023-12-11T19:00:00", + "2023-12-11T19:03:00", + "2023-12-11T19:06:00", + "2023-12-11T19:09:00", + "2023-12-11T19:12:00", + "2023-12-11T19:15:00", + "2023-12-11T19:18:00", + "2023-12-11T19:21:00", + "2023-12-11T19:24:00", + "2023-12-11T19:27:00", + "2023-12-11T19:30:00", + "2023-12-11T19:33:00", + "2023-12-11T19:36:00", + "2023-12-11T19:39:00", + "2023-12-11T19:42:00", + "2023-12-11T19:45:00", + "2023-12-11T19:48:00", + "2023-12-11T19:51:00", + "2023-12-11T19:54:00", + "2023-12-11T19:57:00", + "2023-12-11T20:00:00", + "2023-12-11T20:03:00", + "2023-12-11T20:06:00", + "2023-12-11T20:09:00", + "2023-12-11T20:12:00", + "2023-12-11T20:15:00", + "2023-12-11T20:18:00", + "2023-12-11T20:21:00", + "2023-12-11T20:24:00", + "2023-12-11T20:27:00", + "2023-12-11T20:30:00", + "2023-12-11T20:33:00", + "2023-12-11T20:36:00", + "2023-12-11T20:39:00", + "2023-12-11T20:42:00", + "2023-12-11T20:45:00", + "2023-12-11T20:48:00", + "2023-12-11T20:51:00", + "2023-12-11T20:54:00", + "2023-12-11T20:57:00", + "2023-12-11T21:00:00", + "2023-12-11T21:03:00", + "2023-12-11T21:06:00", + "2023-12-11T21:09:00", + "2023-12-11T21:12:00", + "2023-12-11T21:15:00", + "2023-12-11T21:18:00", + "2023-12-11T21:21:00", + "2023-12-11T21:24:00", + "2023-12-11T21:27:00", + "2023-12-11T21:30:00", + "2023-12-11T21:33:00", + "2023-12-11T21:36:00", + "2023-12-11T21:39:00", + "2023-12-11T21:42:00", + "2023-12-11T21:45:00", + "2023-12-11T21:48:00", + "2023-12-11T21:51:00", + "2023-12-11T21:54:00", + "2023-12-11T21:57:00" + ], + "type": "candlestick" + }, + { + "marker": { + "color": "red", + "size": 7 + }, + "mode": "markers", + "name": "High Peaks", + "x": [ + "2023-12-04T17:06:00", + "2023-12-04T19:21:00", + "2023-12-04T20:24:00", + "2023-12-04T22:21:00", + "2023-12-04T23:57:00", + "2023-12-05T00:45:00", + "2023-12-05T03:24:00", + "2023-12-05T04:36:00", + "2023-12-05T05:45:00", + "2023-12-05T08:18:00", + "2023-12-05T09:18:00", + "2023-12-05T11:15:00", + "2023-12-05T12:36:00", + "2023-12-05T13:45:00", + "2023-12-05T15:45:00", + "2023-12-05T16:42:00", + "2023-12-05T17:15:00", + "2023-12-05T18:09:00", + "2023-12-05T18:39:00", + "2023-12-05T20:03:00", + "2023-12-05T20:45:00", + "2023-12-05T23:18:00", + "2023-12-06T00:27:00", + "2023-12-06T01:06:00", + "2023-12-06T02:06:00", + "2023-12-06T04:36:00", + "2023-12-06T05:06:00", + "2023-12-06T07:09:00", + "2023-12-06T08:33:00", + "2023-12-06T10:45:00", + "2023-12-06T12:03:00", + "2023-12-06T13:09:00", + "2023-12-06T14:00:00", + "2023-12-06T16:42:00", + "2023-12-06T17:39:00", + "2023-12-06T19:33:00", + "2023-12-06T21:12:00", + "2023-12-06T22:36:00", + "2023-12-07T00:51:00", + "2023-12-07T02:03:00", + "2023-12-07T03:24:00", + "2023-12-07T04:45:00", + "2023-12-07T06:39:00", + "2023-12-07T07:15:00", + "2023-12-07T09:48:00", + "2023-12-07T10:33:00", + "2023-12-07T11:24:00", + "2023-12-07T12:57:00", + "2023-12-07T17:03:00", + "2023-12-07T18:09:00", + "2023-12-07T20:09:00", + "2023-12-07T22:12:00", + "2023-12-08T00:00:00", + "2023-12-08T01:39:00", + "2023-12-08T04:15:00", + "2023-12-08T06:21:00", + "2023-12-08T09:24:00", + "2023-12-08T11:42:00", + "2023-12-08T13:03:00", + "2023-12-08T14:51:00", + "2023-12-08T16:39:00", + "2023-12-08T21:57:00", + "2023-12-09T00:48:00", + "2023-12-09T01:36:00", + "2023-12-09T06:00:00", + "2023-12-09T08:21:00", + "2023-12-09T09:21:00", + "2023-12-09T10:51:00", + "2023-12-09T13:09:00", + "2023-12-09T14:27:00", + "2023-12-09T15:03:00", + "2023-12-09T16:36:00", + "2023-12-09T19:36:00", + "2023-12-09T22:15:00", + "2023-12-10T00:36:00", + "2023-12-10T01:21:00", + "2023-12-10T01:54:00", + "2023-12-10T04:24:00", + "2023-12-10T06:48:00", + "2023-12-10T07:33:00", + "2023-12-10T08:57:00", + "2023-12-10T10:57:00", + "2023-12-10T12:18:00", + "2023-12-10T13:45:00", + "2023-12-10T16:27:00", + "2023-12-10T18:27:00", + "2023-12-10T21:18:00", + "2023-12-10T22:00:00", + "2023-12-10T22:48:00", + "2023-12-11T00:09:00", + "2023-12-11T01:51:00", + "2023-12-11T02:27:00", + "2023-12-11T03:30:00", + "2023-12-11T05:18:00", + "2023-12-11T05:57:00", + "2023-12-11T07:09:00", + "2023-12-11T09:15:00", + "2023-12-11T10:33:00", + "2023-12-11T11:15:00", + "2023-12-11T14:45:00", + "2023-12-11T15:51:00", + "2023-12-11T16:57:00", + "2023-12-11T18:51:00", + "2023-12-11T20:03:00" + ], + "y": [ + 2.4336, + 2.4383, + 2.4346, + 2.468, + 2.4648, + 2.5446, + 2.4708, + 2.4629, + 2.4732, + 2.4165, + 2.4194, + 2.412, + 2.4337, + 2.43, + 2.4709, + 2.4579, + 2.4669, + 2.49, + 2.4865, + 2.4999, + 2.504, + 2.4711, + 2.487, + 2.4888, + 2.4913, + 2.53, + 2.5295, + 2.4755, + 2.5485, + 2.53, + 2.4614, + 2.4884, + 2.5242, + 2.5022, + 2.4977, + 2.5098, + 2.483, + 2.4637, + 2.4911, + 2.4642, + 2.4423, + 2.4627, + 2.4581, + 2.4552, + 2.3902, + 2.3869, + 2.3863, + 2.4069, + 2.5272, + 2.5162, + 2.4728, + 2.5072, + 2.5151, + 2.5045, + 2.5393, + 2.5512, + 2.4918, + 2.5214, + 2.5357, + 2.463, + 2.4777, + 2.4895, + 2.5222, + 2.5148, + 2.5321, + 2.5451, + 2.4969, + 2.4866, + 2.5213, + 2.5511, + 2.5241, + 2.5934, + 2.524, + 2.5185, + 2.605, + 2.583, + 2.6226, + 2.6059, + 2.6922, + 2.6696, + 2.5931, + 2.6222, + 2.6156, + 2.6598, + 2.6648, + 2.692, + 2.7066, + 2.75, + 2.7349, + 2.7035, + 2.6948, + 2.5999, + 2.6627, + 2.5552, + 2.5093, + 2.4843, + 2.5336, + 2.522, + 2.519, + 2.4626, + 2.4785, + 2.4634, + 2.4223, + 2.4144 + ], + "type": "scatter" + }, + { + "marker": { + "color": "green", + "size": 7 + }, + "mode": "markers", + "name": "Low Peaks", + "x": [ + "2023-12-04T15:33:00", + "2023-12-04T17:30:00", + "2023-12-04T19:15:00", + "2023-12-04T20:54:00", + "2023-12-04T21:39:00", + "2023-12-04T22:48:00", + "2023-12-04T23:30:00", + "2023-12-05T00:09:00", + "2023-12-05T02:33:00", + "2023-12-05T04:15:00", + "2023-12-05T07:42:00", + "2023-12-05T08:42:00", + "2023-12-05T11:03:00", + "2023-12-05T11:42:00", + "2023-12-05T13:30:00", + "2023-12-05T14:00:00", + "2023-12-05T14:33:00", + "2023-12-05T16:36:00", + "2023-12-05T17:42:00", + "2023-12-05T18:57:00", + "2023-12-05T20:27:00", + "2023-12-05T22:36:00", + "2023-12-06T00:33:00", + "2023-12-06T01:42:00", + "2023-12-06T02:33:00", + "2023-12-06T03:36:00", + "2023-12-06T05:36:00", + "2023-12-06T06:21:00", + "2023-12-06T07:18:00", + "2023-12-06T09:57:00", + "2023-12-06T11:33:00", + "2023-12-06T12:33:00", + "2023-12-06T13:18:00", + "2023-12-06T13:48:00", + "2023-12-06T14:54:00", + "2023-12-06T16:54:00", + "2023-12-06T18:03:00", + "2023-12-06T20:57:00", + "2023-12-06T21:36:00", + "2023-12-06T22:54:00", + "2023-12-07T01:48:00", + "2023-12-07T03:09:00", + "2023-12-07T03:39:00", + "2023-12-07T06:48:00", + "2023-12-07T09:27:00", + "2023-12-07T10:06:00", + "2023-12-07T10:54:00", + "2023-12-07T12:03:00", + "2023-12-07T14:18:00", + "2023-12-07T16:00:00", + "2023-12-07T16:45:00", + "2023-12-07T17:48:00", + "2023-12-07T19:42:00", + "2023-12-07T20:39:00", + "2023-12-07T22:51:00", + "2023-12-08T01:09:00", + "2023-12-08T01:54:00", + "2023-12-08T05:06:00", + "2023-12-08T07:30:00", + "2023-12-08T08:09:00", + "2023-12-08T08:57:00", + "2023-12-08T10:03:00", + "2023-12-08T12:06:00", + "2023-12-08T13:30:00", + "2023-12-08T14:06:00", + "2023-12-08T15:51:00", + "2023-12-08T17:24:00", + "2023-12-08T21:48:00", + "2023-12-08T22:42:00", + "2023-12-09T01:09:00", + "2023-12-09T02:03:00", + "2023-12-09T04:24:00", + "2023-12-09T07:21:00", + "2023-12-09T08:42:00", + "2023-12-09T09:51:00", + "2023-12-09T11:45:00", + "2023-12-09T13:39:00", + "2023-12-09T14:42:00", + "2023-12-09T15:27:00", + "2023-12-09T16:27:00", + "2023-12-09T17:12:00", + "2023-12-09T18:09:00", + "2023-12-09T20:30:00", + "2023-12-09T21:15:00", + "2023-12-09T23:33:00", + "2023-12-10T00:42:00", + "2023-12-10T01:15:00", + "2023-12-10T03:42:00", + "2023-12-10T05:06:00", + "2023-12-10T07:18:00", + "2023-12-10T08:42:00", + "2023-12-10T09:33:00", + "2023-12-10T10:27:00", + "2023-12-10T11:45:00", + "2023-12-10T12:39:00", + "2023-12-10T14:36:00", + "2023-12-10T16:06:00", + "2023-12-10T16:39:00", + "2023-12-10T20:21:00", + "2023-12-10T21:33:00", + "2023-12-10T23:21:00", + "2023-12-11T01:09:00", + "2023-12-11T02:12:00", + "2023-12-11T02:42:00", + "2023-12-11T04:24:00", + "2023-12-11T05:27:00", + "2023-12-11T06:39:00", + "2023-12-11T07:33:00", + "2023-12-11T09:30:00", + "2023-12-11T11:30:00", + "2023-12-11T12:54:00", + "2023-12-11T13:48:00", + "2023-12-11T14:27:00", + "2023-12-11T15:06:00", + "2023-12-11T16:12:00", + "2023-12-11T16:48:00", + "2023-12-11T18:00:00", + "2023-12-11T18:30:00", + "2023-12-11T19:39:00", + "2023-12-11T20:15:00" + ], + "y": [ + 2.3557, + 2.396, + 2.4093, + 2.4113, + 2.4272, + 2.412, + 2.4305, + 2.4402, + 2.4151, + 2.4313, + 2.3661, + 2.3873, + 2.3721, + 2.3821, + 2.396, + 2.3996, + 2.4256, + 2.4187, + 2.4162, + 2.4627, + 2.4711, + 2.4272, + 2.4567, + 2.4586, + 2.4413, + 2.4312, + 2.41, + 2.4023, + 2.4255, + 2.4776, + 2.4, + 2.4283, + 2.4558, + 2.4282, + 2.42, + 2.4721, + 2.469, + 2.4572, + 2.4354, + 2.4093, + 2.4407, + 2.4219, + 2.4233, + 2.4369, + 2.33, + 2.36, + 2.3612, + 2.358, + 2.3822, + 2.4165, + 2.4709, + 2.4813, + 2.4409, + 2.4501, + 2.4811, + 2.4752, + 2.4843, + 2.5069, + 2.489, + 2.4724, + 2.4621, + 2.4723, + 2.4992, + 2.4515, + 2.43, + 2.4396, + 2.441, + 2.4674, + 2.4674, + 2.4756, + 2.4952, + 2.4931, + 2.5, + 2.44, + 2.4547, + 2.4682, + 2.4966, + 2.4852, + 2.49, + 2.5082, + 2.4865, + 2.4695, + 2.4817, + 2.477, + 2.4409, + 2.5528, + 2.5506, + 2.5725, + 2.58, + 2.6373, + 2.5627, + 2.5428, + 2.5788, + 2.5932, + 2.5844, + 2.6125, + 2.6281, + 2.6142, + 2.6363, + 2.6611, + 2.6559, + 2.6072, + 2.3374, + 2.5177, + 2.5263, + 2.4712, + 2.42, + 2.4446, + 2.4892, + 2.4909, + 2.4716, + 2.4407, + 2.4316, + 2.4355, + 2.4366, + 2.4428, + 2.396, + 2.3671, + 2.3651, + 2.3945 + ], + "type": "scatter" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "shapes": [ + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.6937181818181823, + "y1": 2.6937181818181823, + "yref": "y" + }, + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.5496171428571435, + "y1": 2.5496171428571435, + "yref": "y" + }, + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.464181034482759, + "y1": 2.464181034482759, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.5982588235294113, + "y1": 2.5982588235294113, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.4786431818181813, + "y1": 2.4786431818181813, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.4106101694915254, + "y1": 2.4106101694915254, + "yref": "y" + } + ], + "annotations": [ + { + "showarrow": false, + "text": "High Cluster: 2.69", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.6937181818181823, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "High Cluster: 2.55", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.5496171428571435, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "High Cluster: 2.46", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.464181034482759, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.60", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.5982588235294113, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.48", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.4786431818181813, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.41", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.4106101694915254, + "yanchor": "bottom", + "yref": "y" + } + ], + "xaxis": { + "rangeslider": { + "visible": false + } + }, + "height": 800 + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "analyze_and_plot_candles_with_clusters(candles.iloc[-3500:], prominence_percentage=0.03, distance=10, num_clusters=3)\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:11.421615Z", + "start_time": "2024-04-08T15:18:11.148433Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "def analyze_and_plot_candles_with_clusters(candles, prominence_percentage=1/100, distance=5, num_clusters=None):\n", + " candles['date'] = pd.to_datetime(candles['timestamp'], unit='ms')\n", + " prominence_nominal = calculate_prominence(candles, prominence_percentage)\n", + " close_price = candles['close'].iloc[1]\n", + " high_peaks, _ = find_price_peaks(candles[candles[\"close\"] > close_price].copy(), prominence_nominal, distance)\n", + " _, low_peaks = find_price_peaks(candles[candles[\"close\"] < close_price].copy(), prominence_nominal, distance)\n", + " high_peak_prices = candles['high'].iloc[high_peaks].values\n", + " low_peak_prices = candles['low'].iloc[low_peaks].values\n", + " high_clusters, _ = hierarchical_clustering(high_peak_prices, num_clusters or 3)\n", + " low_clusters, _ = hierarchical_clustering(low_peak_prices, num_clusters or 3)\n", + " plot_price_chart_with_clusters(candles, high_peaks, low_peaks, high_clusters, low_clusters)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:23.929900Z", + "start_time": "2024-04-08T15:18:23.927423Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/r8/2xrqvjjs37g0nbft2yz31b4m0000gn/T/ipykernel_20494/3870981304.py:2: SettingWithCopyWarning:\n", + "\n", + "\n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + "\n" + ] + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "close": [ + 2.6738, + 2.6894, + 2.6943, + 2.6916, + 2.6984, + 2.6864, + 2.6938, + 2.6944, + 2.6815, + 2.6792, + 2.6715, + 2.6735, + 2.6782, + 2.6981, + 2.7078, + 2.7202, + 2.7194, + 2.7248, + 2.7315, + 2.7386, + 2.7274, + 2.7246, + 2.7274, + 2.723, + 2.7179, + 2.7169, + 2.722, + 2.7248, + 2.7223, + 2.7238, + 2.7092, + 2.7075, + 2.7191, + 2.7192, + 2.7205, + 2.7146, + 2.7256, + 2.7112, + 2.7131, + 2.7182, + 2.7067, + 2.7065, + 2.7056, + 2.7036, + 2.7, + 2.6809, + 2.6841, + 2.6772, + 2.6833, + 2.6754, + 2.6678, + 2.6691, + 2.6746, + 2.6721, + 2.6782, + 2.6818, + 2.6806, + 2.6853, + 2.6869, + 2.6913, + 2.6922, + 2.697, + 2.6993, + 2.6945, + 2.6904, + 2.6792, + 2.6842, + 2.6791, + 2.684, + 2.676, + 2.6824, + 2.6813, + 2.6798, + 2.6793, + 2.6752, + 2.6759, + 2.6663, + 2.6645, + 2.6505, + 2.6481, + 2.633, + 2.633, + 2.6311, + 2.6342, + 2.6443, + 2.6428, + 2.6434, + 2.6609, + 2.6513, + 2.6522, + 2.6615, + 2.6484, + 2.6559, + 2.6624, + 2.6764, + 2.6909, + 2.6898, + 2.6912, + 2.6874, + 2.6852, + 2.6687, + 2.6694, + 2.6653, + 2.5812, + 2.4867, + 2.5074, + 2.5222, + 2.5892, + 2.5567, + 2.5925, + 2.5623, + 2.5712, + 2.5495, + 2.5363, + 2.5391, + 2.5231, + 2.5633, + 2.5712, + 2.565, + 2.5719, + 2.5722, + 2.5885, + 2.5951, + 2.6234, + 2.6307, + 2.633, + 2.6316, + 2.6398, + 2.6476, + 2.6492, + 2.6533, + 2.644, + 2.6547, + 2.6439, + 2.6462, + 2.6439, + 2.6403, + 2.6334, + 2.6328, + 2.5976, + 2.6058, + 2.5996, + 2.6147, + 2.6025, + 2.5941, + 2.5672, + 2.5541, + 2.5622, + 2.5419, + 2.5514, + 2.5437, + 2.547, + 2.5503, + 2.5456, + 2.533, + 2.5296, + 2.5369, + 2.5411, + 2.5349, + 2.5278, + 2.5236, + 2.5381, + 2.5364, + 2.5376, + 2.5515, + 2.5526, + 2.5417, + 2.5288, + 2.5008, + 2.4935, + 2.4865, + 2.4999, + 2.4885, + 2.4944, + 2.4862, + 2.4904, + 2.5026, + 2.4961, + 2.4994, + 2.5062, + 2.4914, + 2.4949, + 2.4957, + 2.4861, + 2.4802, + 2.4768, + 2.4668, + 2.4522, + 2.4592, + 2.4626, + 2.4512, + 2.4511, + 2.4406, + 2.4348, + 2.4394, + 2.4312, + 2.4453, + 2.4568, + 2.4655, + 2.466, + 2.4708, + 2.479, + 2.481, + 2.4742, + 2.4783, + 2.4767, + 2.4582, + 2.4566, + 2.4531, + 2.4533, + 2.4555, + 2.4506, + 2.4522, + 2.4517, + 2.449, + 2.4566, + 2.4517, + 2.447, + 2.4521, + 2.4582, + 2.4557, + 2.474, + 2.4631, + 2.4676, + 2.4855, + 2.4869, + 2.4755, + 2.4763, + 2.486, + 2.4899, + 2.4927, + 2.4997, + 2.4996, + 2.4966, + 2.4938, + 2.5015, + 2.498, + 2.4981, + 2.5097, + 2.5193, + 2.5108, + 2.5147, + 2.5222, + 2.5224, + 2.5303, + 2.5274, + 2.5254, + 2.5245, + 2.52, + 2.5005, + 2.4968, + 2.5045, + 2.5021, + 2.5126, + 2.5089, + 2.5018, + 2.5044, + 2.5058, + 2.5056, + 2.5051, + 2.5002, + 2.5047, + 2.5145, + 2.5134, + 2.5057, + 2.5074, + 2.5084, + 2.5075, + 2.5136, + 2.5182, + 2.5204, + 2.5158, + 2.5153, + 2.5094, + 2.5079, + 2.5131, + 2.5102, + 2.5106, + 2.5048, + 2.5021, + 2.5002, + 2.5072, + 2.5144, + 2.516, + 2.5177, + 2.5162, + 2.5097, + 2.5058, + 2.4982, + 2.5001, + 2.4941, + 2.5026, + 2.4987, + 2.5017, + 2.504, + 2.5022, + 2.5045, + 2.5039, + 2.503, + 2.5099, + 2.5077, + 2.4992, + 2.5066, + 2.5088, + 2.5102, + 2.5117, + 2.5063, + 2.5024, + 2.4997, + 2.5018, + 2.4975, + 2.4932, + 2.4952, + 2.4899, + 2.4832, + 2.4884, + 2.4829, + 2.4764, + 2.4797, + 2.4865, + 2.484, + 2.4888, + 2.4887, + 2.4864, + 2.4885, + 2.486, + 2.4872, + 2.4779, + 2.474, + 2.4728, + 2.4682, + 2.4631, + 2.4572, + 2.4617, + 2.4604, + 2.4566, + 2.4486, + 2.4649, + 2.4631, + 2.4612, + 2.4688, + 2.4648, + 2.4633, + 2.4623, + 2.464, + 2.4567, + 2.4608, + 2.4581, + 2.4474, + 2.4437, + 2.4372, + 2.4415, + 2.4564, + 2.444, + 2.4457, + 2.458, + 2.4452, + 2.4502, + 2.4543, + 2.4472, + 2.4462, + 2.4417, + 2.4414, + 2.4489, + 2.4572, + 2.4699, + 2.4687, + 2.471, + 2.4591, + 2.4691, + 2.4673, + 2.4588, + 2.4607, + 2.4643, + 2.4566, + 2.4685, + 2.4719, + 2.4657, + 2.4612, + 2.4664, + 2.4594, + 2.4553, + 2.4449, + 2.4585, + 2.4517, + 2.4516, + 2.4579, + 2.4473, + 2.4608, + 2.4626, + 2.4506, + 2.4501, + 2.4614, + 2.4544, + 2.4529, + 2.4484, + 2.4447, + 2.4482, + 2.4562, + 2.4618, + 2.4605, + 2.4568, + 2.4479, + 2.4475, + 2.4475, + 2.4388, + 2.431, + 2.4349, + 2.4345, + 2.4363, + 2.4282, + 2.4193, + 2.4297, + 2.4241, + 2.427, + 2.4264, + 2.4247, + 2.4152, + 2.4252, + 2.4199, + 2.4184, + 2.4175, + 2.4173, + 2.4122, + 2.3996, + 2.4104, + 2.4094, + 2.4044, + 2.3947, + 2.3822, + 2.3975, + 2.3935, + 2.4006, + 2.3993, + 2.3947, + 2.4093, + 2.4086, + 2.4131, + 2.4068, + 2.4131, + 2.4146, + 2.4106, + 2.4036, + 2.4021, + 2.4003, + 2.3948, + 2.3925, + 2.3894, + 2.3835, + 2.3829, + 2.3904, + 2.3837, + 2.3798, + 2.3751, + 2.3855, + 2.3964, + 2.4012, + 2.3971, + 2.4024, + 2.4065, + 2.4087, + 2.4078, + 2.4004, + 2.3996, + 2.3999, + 2.3968, + 2.4053, + 2.3994, + 2.3978, + 2.4049, + 2.3996, + 2.3991, + 2.4013, + 2.3964, + 2.4027, + 2.4101, + 2.41, + 2.4093, + 2.4069, + 2.4113, + 2.4146, + 2.4146, + 2.422, + 2.4257, + 2.4249, + 2.4321, + 2.4227, + 2.4234, + 2.428, + 2.4299, + 2.4314, + 2.429, + 2.4281, + 2.4315, + 2.4331, + 2.4322, + 2.4343, + 2.4355, + 2.4343, + 2.4347 + ], + "high": [ + 2.6826, + 2.6907, + 2.7, + 2.6962, + 2.7022, + 2.6983, + 2.7066, + 2.6976, + 2.6965, + 2.6823, + 2.6833, + 2.6745, + 2.6786, + 2.7018, + 2.7099, + 2.7253, + 2.7246, + 2.731, + 2.7418, + 2.741, + 2.75, + 2.7354, + 2.7333, + 2.7298, + 2.7277, + 2.7197, + 2.7232, + 2.728, + 2.7291, + 2.7238, + 2.7263, + 2.7131, + 2.722, + 2.7202, + 2.7242, + 2.7209, + 2.7349, + 2.7265, + 2.7151, + 2.721, + 2.7206, + 2.7131, + 2.7066, + 2.7118, + 2.7056, + 2.6999, + 2.689, + 2.684, + 2.6878, + 2.6832, + 2.68, + 2.6729, + 2.6785, + 2.6785, + 2.6816, + 2.6822, + 2.6845, + 2.6871, + 2.687, + 2.6941, + 2.6973, + 2.6989, + 2.6993, + 2.7035, + 2.6954, + 2.6922, + 2.6844, + 2.6856, + 2.6861, + 2.6862, + 2.6869, + 2.6867, + 2.686, + 2.6826, + 2.6809, + 2.6763, + 2.6795, + 2.6672, + 2.666, + 2.6534, + 2.6488, + 2.638, + 2.6426, + 2.6375, + 2.6466, + 2.6466, + 2.6488, + 2.6627, + 2.6642, + 2.6551, + 2.6639, + 2.6628, + 2.6577, + 2.666, + 2.6771, + 2.6935, + 2.6944, + 2.6948, + 2.692, + 2.6904, + 2.6867, + 2.6784, + 2.6768, + 2.6763, + 2.5959, + 2.5175, + 2.5393, + 2.5901, + 2.5929, + 2.5999, + 2.5942, + 2.5873, + 2.5773, + 2.5667, + 2.5449, + 2.5524, + 2.5635, + 2.589, + 2.5766, + 2.5748, + 2.5867, + 2.5906, + 2.5964, + 2.6268, + 2.6316, + 2.6446, + 2.6436, + 2.6419, + 2.6492, + 2.6554, + 2.6627, + 2.6535, + 2.6562, + 2.6576, + 2.6504, + 2.6487, + 2.6495, + 2.6466, + 2.6392, + 2.6375, + 2.6149, + 2.6079, + 2.6198, + 2.6159, + 2.6134, + 2.5952, + 2.575, + 2.5662, + 2.5624, + 2.5558, + 2.552, + 2.552, + 2.5562, + 2.5525, + 2.5456, + 2.536, + 2.5388, + 2.5432, + 2.5434, + 2.5348, + 2.5318, + 2.5393, + 2.5389, + 2.5418, + 2.5547, + 2.555, + 2.5552, + 2.5416, + 2.5304, + 2.5026, + 2.5011, + 2.5016, + 2.502, + 2.4988, + 2.4953, + 2.4979, + 2.5056, + 2.5049, + 2.5046, + 2.5093, + 2.5073, + 2.5025, + 2.4996, + 2.4962, + 2.4864, + 2.4873, + 2.4767, + 2.47, + 2.4729, + 2.4631, + 2.4643, + 2.4557, + 2.4532, + 2.4425, + 2.4397, + 2.4416, + 2.4474, + 2.4568, + 2.4671, + 2.4675, + 2.4713, + 2.4795, + 2.4819, + 2.4843, + 2.4785, + 2.4801, + 2.4772, + 2.4621, + 2.4665, + 2.4575, + 2.4579, + 2.4562, + 2.4572, + 2.4555, + 2.4537, + 2.4618, + 2.4579, + 2.4515, + 2.4522, + 2.4628, + 2.4598, + 2.4743, + 2.4781, + 2.474, + 2.4867, + 2.4894, + 2.4952, + 2.4873, + 2.4876, + 2.4902, + 2.5, + 2.5003, + 2.5016, + 2.5004, + 2.4993, + 2.5027, + 2.5077, + 2.5004, + 2.5119, + 2.5205, + 2.5234, + 2.5257, + 2.524, + 2.5279, + 2.5316, + 2.5336, + 2.5331, + 2.5281, + 2.5277, + 2.52, + 2.5006, + 2.5053, + 2.5068, + 2.513, + 2.5133, + 2.5107, + 2.5055, + 2.5062, + 2.5109, + 2.5082, + 2.5086, + 2.5069, + 2.5145, + 2.5179, + 2.5132, + 2.5075, + 2.5103, + 2.5098, + 2.5167, + 2.5188, + 2.5218, + 2.522, + 2.5177, + 2.5166, + 2.5118, + 2.5132, + 2.5156, + 2.511, + 2.5103, + 2.5053, + 2.507, + 2.5084, + 2.5144, + 2.5168, + 2.518, + 2.519, + 2.5167, + 2.5156, + 2.506, + 2.5034, + 2.5012, + 2.5033, + 2.5047, + 2.5058, + 2.5055, + 2.5073, + 2.5051, + 2.5051, + 2.5052, + 2.5108, + 2.5122, + 2.5095, + 2.5066, + 2.5093, + 2.5108, + 2.5137, + 2.5126, + 2.5071, + 2.5039, + 2.5029, + 2.5038, + 2.4992, + 2.497, + 2.4955, + 2.4898, + 2.4895, + 2.4911, + 2.4861, + 2.4815, + 2.4869, + 2.4921, + 2.4888, + 2.4888, + 2.4904, + 2.4897, + 2.489, + 2.4877, + 2.4871, + 2.4801, + 2.4741, + 2.4764, + 2.4687, + 2.465, + 2.4664, + 2.467, + 2.4639, + 2.4564, + 2.4653, + 2.476, + 2.4664, + 2.474, + 2.4739, + 2.4648, + 2.4682, + 2.4648, + 2.4659, + 2.4619, + 2.4616, + 2.4597, + 2.4479, + 2.4555, + 2.4452, + 2.4566, + 2.458, + 2.4473, + 2.4626, + 2.4596, + 2.4556, + 2.4586, + 2.4553, + 2.4506, + 2.4514, + 2.4479, + 2.4498, + 2.4573, + 2.4723, + 2.4735, + 2.4728, + 2.4712, + 2.4714, + 2.4725, + 2.4683, + 2.4635, + 2.4697, + 2.4652, + 2.4733, + 2.4771, + 2.4785, + 2.4713, + 2.4677, + 2.4672, + 2.4661, + 2.4556, + 2.4597, + 2.4578, + 2.4549, + 2.4579, + 2.4591, + 2.4608, + 2.463, + 2.4628, + 2.453, + 2.4617, + 2.4625, + 2.4576, + 2.4559, + 2.4495, + 2.451, + 2.4582, + 2.4634, + 2.4625, + 2.4622, + 2.458, + 2.4511, + 2.4526, + 2.4492, + 2.442, + 2.4398, + 2.4372, + 2.4402, + 2.4362, + 2.4305, + 2.4299, + 2.4352, + 2.4296, + 2.4316, + 2.4273, + 2.4251, + 2.4259, + 2.4255, + 2.4205, + 2.425, + 2.4221, + 2.4208, + 2.4131, + 2.4129, + 2.4157, + 2.4095, + 2.408, + 2.3957, + 2.3975, + 2.4051, + 2.4026, + 2.4033, + 2.401, + 2.41, + 2.4132, + 2.4223, + 2.415, + 2.4152, + 2.417, + 2.4153, + 2.4112, + 2.4052, + 2.4033, + 2.4041, + 2.3952, + 2.3944, + 2.39, + 2.3889, + 2.3922, + 2.3926, + 2.3859, + 2.3839, + 2.3888, + 2.3974, + 2.4033, + 2.4017, + 2.4054, + 2.409, + 2.4093, + 2.4144, + 2.4092, + 2.4019, + 2.405, + 2.4032, + 2.4053, + 2.4065, + 2.4007, + 2.4053, + 2.4083, + 2.4006, + 2.4021, + 2.4027, + 2.4033, + 2.4108, + 2.4137, + 2.4148, + 2.4104, + 2.4115, + 2.4197, + 2.4164, + 2.4221, + 2.4257, + 2.4256, + 2.4375, + 2.4323, + 2.4267, + 2.4312, + 2.43, + 2.4318, + 2.4338, + 2.4306, + 2.4349, + 2.4349, + 2.4351, + 2.435, + 2.4365, + 2.4379, + 2.4347 + ], + "low": [ + 2.673, + 2.6734, + 2.6889, + 2.6889, + 2.6891, + 2.6809, + 2.6862, + 2.6894, + 2.6813, + 2.6687, + 2.6708, + 2.6611, + 2.6709, + 2.6755, + 2.6986, + 2.7076, + 2.7147, + 2.7162, + 2.7236, + 2.7267, + 2.7223, + 2.72, + 2.7239, + 2.7201, + 2.7144, + 2.712, + 2.7116, + 2.7217, + 2.7216, + 2.718, + 2.708, + 2.706, + 2.7075, + 2.7154, + 2.7186, + 2.7118, + 2.7139, + 2.7043, + 2.7062, + 2.7121, + 2.7063, + 2.7, + 2.7, + 2.7036, + 2.7, + 2.6735, + 2.6727, + 2.6559, + 2.6774, + 2.6727, + 2.6673, + 2.6644, + 2.6678, + 2.6705, + 2.6694, + 2.6716, + 2.6784, + 2.6803, + 2.6821, + 2.6851, + 2.6906, + 2.6921, + 2.6933, + 2.6944, + 2.687, + 2.6783, + 2.676, + 2.6789, + 2.6783, + 2.675, + 2.6724, + 2.681, + 2.6754, + 2.677, + 2.674, + 2.67, + 2.6663, + 2.6578, + 2.646, + 2.6378, + 2.6303, + 2.6214, + 2.6232, + 2.6072, + 2.6342, + 2.637, + 2.6418, + 2.6436, + 2.6503, + 2.6475, + 2.6523, + 2.6469, + 2.6479, + 2.6556, + 2.66, + 2.6755, + 2.6873, + 2.6862, + 2.6824, + 2.6829, + 2.6682, + 2.6604, + 2.643, + 2.55, + 2.3374, + 2.4549, + 2.493, + 2.5207, + 2.5541, + 2.553, + 2.5591, + 2.5614, + 2.5397, + 2.533, + 2.5177, + 2.52, + 2.5207, + 2.5571, + 2.565, + 2.561, + 2.566, + 2.5725, + 2.5874, + 2.5946, + 2.6137, + 2.6236, + 2.6288, + 2.6218, + 2.6336, + 2.6466, + 2.6482, + 2.6426, + 2.6421, + 2.6415, + 2.6401, + 2.6323, + 2.6369, + 2.6272, + 2.6286, + 2.5929, + 2.5809, + 2.597, + 2.5981, + 2.6008, + 2.5904, + 2.5602, + 2.5482, + 2.551, + 2.5263, + 2.5363, + 2.5366, + 2.5411, + 2.5469, + 2.5372, + 2.5305, + 2.5267, + 2.5272, + 2.5317, + 2.5342, + 2.5266, + 2.5211, + 2.5233, + 2.5312, + 2.534, + 2.5319, + 2.5457, + 2.5364, + 2.5262, + 2.4929, + 2.4712, + 2.476, + 2.482, + 2.4862, + 2.4873, + 2.4826, + 2.4783, + 2.4836, + 2.4944, + 2.4924, + 2.4944, + 2.4898, + 2.4909, + 2.4896, + 2.4847, + 2.4774, + 2.47, + 2.4628, + 2.4452, + 2.452, + 2.4518, + 2.4452, + 2.4462, + 2.43, + 2.42, + 2.4307, + 2.4303, + 2.4301, + 2.4426, + 2.4543, + 2.4598, + 2.4605, + 2.4702, + 2.4757, + 2.4707, + 2.4713, + 2.4727, + 2.4566, + 2.4522, + 2.4462, + 2.4502, + 2.4489, + 2.4446, + 2.4505, + 2.4501, + 2.4482, + 2.4473, + 2.4509, + 2.4461, + 2.4467, + 2.4518, + 2.4461, + 2.4558, + 2.461, + 2.454, + 2.4655, + 2.477, + 2.4702, + 2.4701, + 2.4744, + 2.4826, + 2.4894, + 2.4876, + 2.4975, + 2.4953, + 2.4901, + 2.4926, + 2.4954, + 2.4883, + 2.497, + 2.5084, + 2.5095, + 2.5075, + 2.5129, + 2.5193, + 2.5199, + 2.5205, + 2.5231, + 2.52, + 2.5197, + 2.4997, + 2.4892, + 2.4946, + 2.4974, + 2.5021, + 2.5045, + 2.5002, + 2.5017, + 2.5032, + 2.505, + 2.503, + 2.5, + 2.4996, + 2.5046, + 2.5098, + 2.505, + 2.5, + 2.5053, + 2.5054, + 2.5052, + 2.513, + 2.5137, + 2.5158, + 2.5122, + 2.5054, + 2.5079, + 2.5067, + 2.5102, + 2.5081, + 2.5041, + 2.5008, + 2.4991, + 2.4981, + 2.5045, + 2.509, + 2.515, + 2.5141, + 2.5097, + 2.5039, + 2.4929, + 2.4934, + 2.4909, + 2.494, + 2.4965, + 2.4971, + 2.5012, + 2.5022, + 2.496, + 2.5028, + 2.5029, + 2.5027, + 2.5077, + 2.4991, + 2.4984, + 2.5044, + 2.5082, + 2.5084, + 2.5062, + 2.5024, + 2.4984, + 2.4983, + 2.4964, + 2.4926, + 2.488, + 2.4887, + 2.4815, + 2.4822, + 2.4815, + 2.4756, + 2.4716, + 2.4786, + 2.4839, + 2.4828, + 2.4845, + 2.4851, + 2.4865, + 2.4805, + 2.4859, + 2.4778, + 2.4732, + 2.4681, + 2.4652, + 2.4602, + 2.4552, + 2.4561, + 2.4562, + 2.4533, + 2.4407, + 2.4465, + 2.4631, + 2.4602, + 2.4611, + 2.4632, + 2.456, + 2.4598, + 2.4602, + 2.4567, + 2.4479, + 2.4489, + 2.4388, + 2.4316, + 2.4368, + 2.4318, + 2.4397, + 2.444, + 2.4389, + 2.4444, + 2.4439, + 2.4441, + 2.4495, + 2.4431, + 2.4413, + 2.4379, + 2.4355, + 2.4389, + 2.4465, + 2.4566, + 2.4661, + 2.4656, + 2.457, + 2.4581, + 2.4617, + 2.4573, + 2.4581, + 2.4607, + 2.4565, + 2.4436, + 2.4682, + 2.4641, + 2.4602, + 2.4613, + 2.4588, + 2.4463, + 2.442, + 2.4445, + 2.4366, + 2.438, + 2.4459, + 2.4437, + 2.4465, + 2.4599, + 2.4494, + 2.4443, + 2.4491, + 2.4522, + 2.4515, + 2.4461, + 2.4428, + 2.4442, + 2.4472, + 2.4557, + 2.4555, + 2.4558, + 2.4477, + 2.4457, + 2.446, + 2.4385, + 2.4264, + 2.4293, + 2.4323, + 2.4343, + 2.4275, + 2.4127, + 2.4197, + 2.4238, + 2.4228, + 2.4257, + 2.4228, + 2.4146, + 2.4149, + 2.4185, + 2.396, + 2.4116, + 2.411, + 2.4092, + 2.3858, + 2.3976, + 2.4082, + 2.4028, + 2.3884, + 2.3714, + 2.3671, + 2.3926, + 2.3938, + 2.3947, + 2.3891, + 2.3903, + 2.4062, + 2.4073, + 2.4068, + 2.4068, + 2.4083, + 2.4102, + 2.4034, + 2.3977, + 2.3974, + 2.3923, + 2.3857, + 2.3872, + 2.38, + 2.3803, + 2.3814, + 2.3836, + 2.3767, + 2.3651, + 2.367, + 2.3848, + 2.3961, + 2.395, + 2.3965, + 2.4021, + 2.4008, + 2.4077, + 2.4, + 2.3958, + 2.3963, + 2.3945, + 2.3963, + 2.3982, + 2.3968, + 2.3977, + 2.3994, + 2.3952, + 2.3963, + 2.3957, + 2.396, + 2.4026, + 2.4093, + 2.4083, + 2.4059, + 2.4062, + 2.4101, + 2.4123, + 2.4145, + 2.4207, + 2.4232, + 2.4249, + 2.4227, + 2.422, + 2.4233, + 2.4238, + 2.4267, + 2.4281, + 2.4239, + 2.4282, + 2.4298, + 2.4306, + 2.4309, + 2.4337, + 2.4323, + 2.4344 + ], + "name": "OHLC Data", + "open": [ + 2.6816, + 2.6739, + 2.6895, + 2.6943, + 2.6915, + 2.6983, + 2.6863, + 2.6936, + 2.6943, + 2.6814, + 2.6792, + 2.6716, + 2.6735, + 2.6781, + 2.6986, + 2.7076, + 2.7203, + 2.7193, + 2.724, + 2.7314, + 2.7385, + 2.7274, + 2.7239, + 2.7272, + 2.7232, + 2.7175, + 2.717, + 2.7218, + 2.7249, + 2.7223, + 2.7238, + 2.7091, + 2.7075, + 2.719, + 2.7192, + 2.7204, + 2.7144, + 2.7256, + 2.7112, + 2.7131, + 2.7181, + 2.7066, + 2.7064, + 2.7057, + 2.7039, + 2.6999, + 2.681, + 2.684, + 2.6774, + 2.6832, + 2.6755, + 2.6678, + 2.6691, + 2.6746, + 2.6722, + 2.6782, + 2.682, + 2.6805, + 2.6852, + 2.6869, + 2.6914, + 2.6922, + 2.697, + 2.6993, + 2.6945, + 2.6903, + 2.679, + 2.6843, + 2.6794, + 2.684, + 2.6756, + 2.6825, + 2.6814, + 2.6793, + 2.6793, + 2.6752, + 2.6756, + 2.6662, + 2.6645, + 2.6506, + 2.648, + 2.6327, + 2.6334, + 2.6318, + 2.6346, + 2.6444, + 2.643, + 2.6436, + 2.6608, + 2.6514, + 2.6523, + 2.6616, + 2.6486, + 2.6562, + 2.6624, + 2.676, + 2.6909, + 2.6901, + 2.6917, + 2.6876, + 2.6851, + 2.6687, + 2.6701, + 2.6648, + 2.5854, + 2.4879, + 2.5071, + 2.5215, + 2.59, + 2.5567, + 2.5919, + 2.5623, + 2.5711, + 2.5495, + 2.5367, + 2.5391, + 2.5227, + 2.5634, + 2.5715, + 2.565, + 2.5723, + 2.5725, + 2.5884, + 2.5951, + 2.6234, + 2.6305, + 2.6324, + 2.6307, + 2.6389, + 2.6478, + 2.6494, + 2.653, + 2.6441, + 2.6549, + 2.6439, + 2.6462, + 2.6439, + 2.64, + 2.6336, + 2.633, + 2.5974, + 2.6061, + 2.5998, + 2.6146, + 2.6024, + 2.594, + 2.5672, + 2.5537, + 2.5622, + 2.5413, + 2.5513, + 2.5434, + 2.5472, + 2.5503, + 2.5455, + 2.533, + 2.5293, + 2.5368, + 2.5411, + 2.5348, + 2.5277, + 2.5235, + 2.5373, + 2.5365, + 2.5375, + 2.5515, + 2.5529, + 2.5416, + 2.5286, + 2.5004, + 2.4936, + 2.4859, + 2.4999, + 2.4881, + 2.4944, + 2.4861, + 2.4908, + 2.5024, + 2.496, + 2.4992, + 2.5059, + 2.4914, + 2.495, + 2.4954, + 2.4862, + 2.4801, + 2.4765, + 2.4671, + 2.4527, + 2.4591, + 2.4628, + 2.451, + 2.4518, + 2.4406, + 2.4353, + 2.4393, + 2.431, + 2.4452, + 2.4569, + 2.4655, + 2.4659, + 2.4708, + 2.4787, + 2.4815, + 2.4741, + 2.4784, + 2.4768, + 2.4581, + 2.4566, + 2.4534, + 2.4532, + 2.4552, + 2.4505, + 2.4522, + 2.4515, + 2.4489, + 2.4566, + 2.4515, + 2.4472, + 2.452, + 2.4583, + 2.4558, + 2.4739, + 2.463, + 2.4676, + 2.4854, + 2.4876, + 2.4755, + 2.476, + 2.486, + 2.49, + 2.4927, + 2.4996, + 2.4996, + 2.4964, + 2.4939, + 2.5015, + 2.498, + 2.498, + 2.5097, + 2.5193, + 2.5107, + 2.5146, + 2.5223, + 2.5225, + 2.5304, + 2.5274, + 2.525, + 2.5246, + 2.5198, + 2.5006, + 2.4969, + 2.5045, + 2.5021, + 2.5126, + 2.509, + 2.5018, + 2.5044, + 2.5055, + 2.5055, + 2.5051, + 2.5005, + 2.5048, + 2.5146, + 2.5132, + 2.5056, + 2.5073, + 2.5085, + 2.5074, + 2.5137, + 2.5182, + 2.5205, + 2.5159, + 2.5153, + 2.5095, + 2.5079, + 2.5131, + 2.5099, + 2.5103, + 2.5049, + 2.5021, + 2.5002, + 2.5072, + 2.5141, + 2.516, + 2.5176, + 2.5163, + 2.5104, + 2.5057, + 2.4984, + 2.4998, + 2.494, + 2.5028, + 2.4986, + 2.5018, + 2.504, + 2.5021, + 2.5045, + 2.5039, + 2.5031, + 2.51, + 2.5077, + 2.4994, + 2.5067, + 2.5087, + 2.51, + 2.5117, + 2.5063, + 2.5023, + 2.4996, + 2.5019, + 2.4974, + 2.4928, + 2.4954, + 2.4896, + 2.4838, + 2.4885, + 2.4829, + 2.4762, + 2.4802, + 2.4868, + 2.484, + 2.4888, + 2.4885, + 2.4865, + 2.489, + 2.4859, + 2.4871, + 2.478, + 2.4741, + 2.4727, + 2.4683, + 2.4627, + 2.4569, + 2.4617, + 2.4606, + 2.4564, + 2.4486, + 2.4649, + 2.4631, + 2.4613, + 2.4677, + 2.4642, + 2.4639, + 2.4619, + 2.4636, + 2.4565, + 2.4613, + 2.4577, + 2.4472, + 2.4429, + 2.4368, + 2.4416, + 2.4565, + 2.4445, + 2.4452, + 2.4582, + 2.4456, + 2.4501, + 2.4548, + 2.4478, + 2.4467, + 2.4413, + 2.4414, + 2.4487, + 2.4569, + 2.4699, + 2.4685, + 2.471, + 2.4591, + 2.4689, + 2.4676, + 2.4589, + 2.461, + 2.4643, + 2.4563, + 2.4682, + 2.4718, + 2.4655, + 2.4613, + 2.4663, + 2.4591, + 2.4553, + 2.4447, + 2.4578, + 2.4519, + 2.4517, + 2.458, + 2.4472, + 2.4609, + 2.4623, + 2.4504, + 2.4503, + 2.4614, + 2.4544, + 2.4529, + 2.4484, + 2.4448, + 2.4481, + 2.456, + 2.4624, + 2.4605, + 2.4567, + 2.448, + 2.4469, + 2.4473, + 2.4388, + 2.4306, + 2.4352, + 2.4345, + 2.4361, + 2.4279, + 2.4197, + 2.4298, + 2.4245, + 2.4269, + 2.4264, + 2.4245, + 2.4156, + 2.4253, + 2.4199, + 2.4181, + 2.4175, + 2.4166, + 2.4122, + 2.3999, + 2.41, + 2.4094, + 2.4043, + 2.3947, + 2.3824, + 2.3974, + 2.3938, + 2.4004, + 2.3993, + 2.3945, + 2.4094, + 2.4087, + 2.413, + 2.4069, + 2.4131, + 2.4146, + 2.4102, + 2.4034, + 2.4023, + 2.3996, + 2.3952, + 2.3919, + 2.3894, + 2.3835, + 2.3835, + 2.3903, + 2.3837, + 2.3799, + 2.3755, + 2.3855, + 2.3968, + 2.4013, + 2.397, + 2.4026, + 2.4064, + 2.4086, + 2.4079, + 2.4002, + 2.3987, + 2.4007, + 2.3968, + 2.4052, + 2.399, + 2.398, + 2.405, + 2.3995, + 2.3991, + 2.4014, + 2.3963, + 2.4027, + 2.4101, + 2.4102, + 2.4093, + 2.4068, + 2.4113, + 2.4146, + 2.4145, + 2.4215, + 2.4256, + 2.4249, + 2.4323, + 2.4227, + 2.4238, + 2.4275, + 2.4299, + 2.4314, + 2.4291, + 2.4282, + 2.4313, + 2.4337, + 2.4321, + 2.4341, + 2.4352, + 2.4344 + ], + "x": [ + "2023-12-10T21:00:00", + "2023-12-10T21:03:00", + "2023-12-10T21:06:00", + "2023-12-10T21:09:00", + "2023-12-10T21:12:00", + "2023-12-10T21:15:00", + "2023-12-10T21:18:00", + "2023-12-10T21:21:00", + "2023-12-10T21:24:00", + "2023-12-10T21:27:00", + "2023-12-10T21:30:00", + "2023-12-10T21:33:00", + "2023-12-10T21:36:00", + "2023-12-10T21:39:00", + "2023-12-10T21:42:00", + "2023-12-10T21:45:00", + "2023-12-10T21:48:00", + "2023-12-10T21:51:00", + "2023-12-10T21:54:00", + "2023-12-10T21:57:00", + "2023-12-10T22:00:00", + "2023-12-10T22:03:00", + "2023-12-10T22:06:00", + "2023-12-10T22:09:00", + "2023-12-10T22:12:00", + "2023-12-10T22:15:00", + "2023-12-10T22:18:00", + "2023-12-10T22:21:00", + "2023-12-10T22:24:00", + "2023-12-10T22:27:00", + "2023-12-10T22:30:00", + "2023-12-10T22:33:00", + "2023-12-10T22:36:00", + "2023-12-10T22:39:00", + "2023-12-10T22:42:00", + "2023-12-10T22:45:00", + "2023-12-10T22:48:00", + "2023-12-10T22:51:00", + "2023-12-10T22:54:00", + "2023-12-10T22:57:00", + "2023-12-10T23:00:00", + "2023-12-10T23:03:00", + "2023-12-10T23:06:00", + "2023-12-10T23:09:00", + "2023-12-10T23:12:00", + "2023-12-10T23:15:00", + "2023-12-10T23:18:00", + "2023-12-10T23:21:00", + "2023-12-10T23:24:00", + "2023-12-10T23:27:00", + "2023-12-10T23:30:00", + "2023-12-10T23:33:00", + "2023-12-10T23:36:00", + "2023-12-10T23:39:00", + "2023-12-10T23:42:00", + "2023-12-10T23:45:00", + "2023-12-10T23:48:00", + "2023-12-10T23:51:00", + "2023-12-10T23:54:00", + "2023-12-10T23:57:00", + "2023-12-11T00:00:00", + "2023-12-11T00:03:00", + "2023-12-11T00:06:00", + "2023-12-11T00:09:00", + "2023-12-11T00:12:00", + "2023-12-11T00:15:00", + "2023-12-11T00:18:00", + "2023-12-11T00:21:00", + "2023-12-11T00:24:00", + "2023-12-11T00:27:00", + "2023-12-11T00:30:00", + "2023-12-11T00:33:00", + "2023-12-11T00:36:00", + "2023-12-11T00:39:00", + "2023-12-11T00:42:00", + "2023-12-11T00:45:00", + "2023-12-11T00:48:00", + "2023-12-11T00:51:00", + "2023-12-11T00:54:00", + "2023-12-11T00:57:00", + "2023-12-11T01:00:00", + "2023-12-11T01:03:00", + "2023-12-11T01:06:00", + "2023-12-11T01:09:00", + "2023-12-11T01:12:00", + "2023-12-11T01:15:00", + "2023-12-11T01:18:00", + "2023-12-11T01:21:00", + "2023-12-11T01:24:00", + "2023-12-11T01:27:00", + "2023-12-11T01:30:00", + "2023-12-11T01:33:00", + "2023-12-11T01:36:00", + "2023-12-11T01:39:00", + "2023-12-11T01:42:00", + "2023-12-11T01:45:00", + "2023-12-11T01:48:00", + "2023-12-11T01:51:00", + "2023-12-11T01:54:00", + "2023-12-11T01:57:00", + "2023-12-11T02:00:00", + "2023-12-11T02:03:00", + "2023-12-11T02:06:00", + "2023-12-11T02:09:00", + "2023-12-11T02:12:00", + "2023-12-11T02:15:00", + "2023-12-11T02:18:00", + "2023-12-11T02:21:00", + "2023-12-11T02:24:00", + "2023-12-11T02:27:00", + "2023-12-11T02:30:00", + "2023-12-11T02:33:00", + "2023-12-11T02:36:00", + "2023-12-11T02:39:00", + "2023-12-11T02:42:00", + "2023-12-11T02:45:00", + "2023-12-11T02:48:00", + "2023-12-11T02:51:00", + "2023-12-11T02:54:00", + "2023-12-11T02:57:00", + "2023-12-11T03:00:00", + "2023-12-11T03:03:00", + "2023-12-11T03:06:00", + "2023-12-11T03:09:00", + "2023-12-11T03:12:00", + "2023-12-11T03:15:00", + "2023-12-11T03:18:00", + "2023-12-11T03:21:00", + "2023-12-11T03:24:00", + "2023-12-11T03:27:00", + "2023-12-11T03:30:00", + "2023-12-11T03:33:00", + "2023-12-11T03:36:00", + "2023-12-11T03:39:00", + "2023-12-11T03:42:00", + "2023-12-11T03:45:00", + "2023-12-11T03:48:00", + "2023-12-11T03:51:00", + "2023-12-11T03:54:00", + "2023-12-11T03:57:00", + "2023-12-11T04:00:00", + "2023-12-11T04:03:00", + "2023-12-11T04:06:00", + "2023-12-11T04:09:00", + "2023-12-11T04:12:00", + "2023-12-11T04:15:00", + "2023-12-11T04:18:00", + "2023-12-11T04:21:00", + "2023-12-11T04:24:00", + "2023-12-11T04:27:00", + "2023-12-11T04:30:00", + "2023-12-11T04:33:00", + "2023-12-11T04:36:00", + "2023-12-11T04:39:00", + "2023-12-11T04:42:00", + "2023-12-11T04:45:00", + "2023-12-11T04:48:00", + "2023-12-11T04:51:00", + "2023-12-11T04:54:00", + "2023-12-11T04:57:00", + "2023-12-11T05:00:00", + "2023-12-11T05:03:00", + "2023-12-11T05:06:00", + "2023-12-11T05:09:00", + "2023-12-11T05:12:00", + "2023-12-11T05:15:00", + "2023-12-11T05:18:00", + "2023-12-11T05:21:00", + "2023-12-11T05:24:00", + "2023-12-11T05:27:00", + "2023-12-11T05:30:00", + "2023-12-11T05:33:00", + "2023-12-11T05:36:00", + "2023-12-11T05:39:00", + "2023-12-11T05:42:00", + "2023-12-11T05:45:00", + "2023-12-11T05:48:00", + "2023-12-11T05:51:00", + "2023-12-11T05:54:00", + "2023-12-11T05:57:00", + "2023-12-11T06:00:00", + "2023-12-11T06:03:00", + "2023-12-11T06:06:00", + "2023-12-11T06:09:00", + "2023-12-11T06:12:00", + "2023-12-11T06:15:00", + "2023-12-11T06:18:00", + "2023-12-11T06:21:00", + "2023-12-11T06:24:00", + "2023-12-11T06:27:00", + "2023-12-11T06:30:00", + "2023-12-11T06:33:00", + "2023-12-11T06:36:00", + "2023-12-11T06:39:00", + "2023-12-11T06:42:00", + "2023-12-11T06:45:00", + "2023-12-11T06:48:00", + "2023-12-11T06:51:00", + "2023-12-11T06:54:00", + "2023-12-11T06:57:00", + "2023-12-11T07:00:00", + "2023-12-11T07:03:00", + "2023-12-11T07:06:00", + "2023-12-11T07:09:00", + "2023-12-11T07:12:00", + "2023-12-11T07:15:00", + "2023-12-11T07:18:00", + "2023-12-11T07:21:00", + "2023-12-11T07:24:00", + "2023-12-11T07:27:00", + "2023-12-11T07:30:00", + "2023-12-11T07:33:00", + "2023-12-11T07:36:00", + "2023-12-11T07:39:00", + "2023-12-11T07:42:00", + "2023-12-11T07:45:00", + "2023-12-11T07:48:00", + "2023-12-11T07:51:00", + "2023-12-11T07:54:00", + "2023-12-11T07:57:00", + "2023-12-11T08:00:00", + "2023-12-11T08:03:00", + "2023-12-11T08:06:00", + "2023-12-11T08:09:00", + "2023-12-11T08:12:00", + "2023-12-11T08:15:00", + "2023-12-11T08:18:00", + "2023-12-11T08:21:00", + "2023-12-11T08:24:00", + "2023-12-11T08:27:00", + "2023-12-11T08:30:00", + "2023-12-11T08:33:00", + "2023-12-11T08:36:00", + "2023-12-11T08:39:00", + "2023-12-11T08:42:00", + "2023-12-11T08:45:00", + "2023-12-11T08:48:00", + "2023-12-11T08:51:00", + "2023-12-11T08:54:00", + "2023-12-11T08:57:00", + "2023-12-11T09:00:00", + "2023-12-11T09:03:00", + "2023-12-11T09:06:00", + "2023-12-11T09:09:00", + "2023-12-11T09:12:00", + "2023-12-11T09:15:00", + "2023-12-11T09:18:00", + "2023-12-11T09:21:00", + "2023-12-11T09:24:00", + "2023-12-11T09:27:00", + "2023-12-11T09:30:00", + "2023-12-11T09:33:00", + "2023-12-11T09:36:00", + "2023-12-11T09:39:00", + "2023-12-11T09:42:00", + "2023-12-11T09:45:00", + "2023-12-11T09:48:00", + "2023-12-11T09:51:00", + "2023-12-11T09:54:00", + "2023-12-11T09:57:00", + "2023-12-11T10:00:00", + "2023-12-11T10:03:00", + "2023-12-11T10:06:00", + "2023-12-11T10:09:00", + "2023-12-11T10:12:00", + "2023-12-11T10:15:00", + "2023-12-11T10:18:00", + "2023-12-11T10:21:00", + "2023-12-11T10:24:00", + "2023-12-11T10:27:00", + "2023-12-11T10:30:00", + "2023-12-11T10:33:00", + "2023-12-11T10:36:00", + "2023-12-11T10:39:00", + "2023-12-11T10:42:00", + "2023-12-11T10:45:00", + "2023-12-11T10:48:00", + "2023-12-11T10:51:00", + "2023-12-11T10:54:00", + "2023-12-11T10:57:00", + "2023-12-11T11:00:00", + "2023-12-11T11:03:00", + "2023-12-11T11:06:00", + "2023-12-11T11:09:00", + "2023-12-11T11:12:00", + "2023-12-11T11:15:00", + "2023-12-11T11:18:00", + "2023-12-11T11:21:00", + "2023-12-11T11:24:00", + "2023-12-11T11:27:00", + "2023-12-11T11:30:00", + "2023-12-11T11:33:00", + "2023-12-11T11:36:00", + "2023-12-11T11:39:00", + "2023-12-11T11:42:00", + "2023-12-11T11:45:00", + "2023-12-11T11:48:00", + "2023-12-11T11:51:00", + "2023-12-11T11:54:00", + "2023-12-11T11:57:00", + "2023-12-11T12:00:00", + "2023-12-11T12:03:00", + "2023-12-11T12:06:00", + "2023-12-11T12:09:00", + "2023-12-11T12:12:00", + "2023-12-11T12:15:00", + "2023-12-11T12:18:00", + "2023-12-11T12:21:00", + "2023-12-11T12:24:00", + "2023-12-11T12:27:00", + "2023-12-11T12:30:00", + "2023-12-11T12:33:00", + "2023-12-11T12:36:00", + "2023-12-11T12:39:00", + "2023-12-11T12:42:00", + "2023-12-11T12:45:00", + "2023-12-11T12:48:00", + "2023-12-11T12:51:00", + "2023-12-11T12:54:00", + "2023-12-11T12:57:00", + "2023-12-11T13:00:00", + "2023-12-11T13:03:00", + "2023-12-11T13:06:00", + "2023-12-11T13:09:00", + "2023-12-11T13:12:00", + "2023-12-11T13:15:00", + "2023-12-11T13:18:00", + "2023-12-11T13:21:00", + "2023-12-11T13:24:00", + "2023-12-11T13:27:00", + "2023-12-11T13:30:00", + "2023-12-11T13:33:00", + "2023-12-11T13:36:00", + "2023-12-11T13:39:00", + "2023-12-11T13:42:00", + "2023-12-11T13:45:00", + "2023-12-11T13:48:00", + "2023-12-11T13:51:00", + "2023-12-11T13:54:00", + "2023-12-11T13:57:00", + "2023-12-11T14:00:00", + "2023-12-11T14:03:00", + "2023-12-11T14:06:00", + "2023-12-11T14:09:00", + "2023-12-11T14:12:00", + "2023-12-11T14:15:00", + "2023-12-11T14:18:00", + "2023-12-11T14:21:00", + "2023-12-11T14:24:00", + "2023-12-11T14:27:00", + "2023-12-11T14:30:00", + "2023-12-11T14:33:00", + "2023-12-11T14:36:00", + "2023-12-11T14:39:00", + "2023-12-11T14:42:00", + "2023-12-11T14:45:00", + "2023-12-11T14:48:00", + "2023-12-11T14:51:00", + "2023-12-11T14:54:00", + "2023-12-11T14:57:00", + "2023-12-11T15:00:00", + "2023-12-11T15:03:00", + "2023-12-11T15:06:00", + "2023-12-11T15:09:00", + "2023-12-11T15:12:00", + "2023-12-11T15:15:00", + "2023-12-11T15:18:00", + "2023-12-11T15:21:00", + "2023-12-11T15:24:00", + "2023-12-11T15:27:00", + "2023-12-11T15:30:00", + "2023-12-11T15:33:00", + "2023-12-11T15:36:00", + "2023-12-11T15:39:00", + "2023-12-11T15:42:00", + "2023-12-11T15:45:00", + "2023-12-11T15:48:00", + "2023-12-11T15:51:00", + "2023-12-11T15:54:00", + "2023-12-11T15:57:00", + "2023-12-11T16:00:00", + "2023-12-11T16:03:00", + "2023-12-11T16:06:00", + "2023-12-11T16:09:00", + "2023-12-11T16:12:00", + "2023-12-11T16:15:00", + "2023-12-11T16:18:00", + "2023-12-11T16:21:00", + "2023-12-11T16:24:00", + "2023-12-11T16:27:00", + "2023-12-11T16:30:00", + "2023-12-11T16:33:00", + "2023-12-11T16:36:00", + "2023-12-11T16:39:00", + "2023-12-11T16:42:00", + "2023-12-11T16:45:00", + "2023-12-11T16:48:00", + "2023-12-11T16:51:00", + "2023-12-11T16:54:00", + "2023-12-11T16:57:00", + "2023-12-11T17:00:00", + "2023-12-11T17:03:00", + "2023-12-11T17:06:00", + "2023-12-11T17:09:00", + "2023-12-11T17:12:00", + "2023-12-11T17:15:00", + "2023-12-11T17:18:00", + "2023-12-11T17:21:00", + "2023-12-11T17:24:00", + "2023-12-11T17:27:00", + "2023-12-11T17:30:00", + "2023-12-11T17:33:00", + "2023-12-11T17:36:00", + "2023-12-11T17:39:00", + "2023-12-11T17:42:00", + "2023-12-11T17:45:00", + "2023-12-11T17:48:00", + "2023-12-11T17:51:00", + "2023-12-11T17:54:00", + "2023-12-11T17:57:00", + "2023-12-11T18:00:00", + "2023-12-11T18:03:00", + "2023-12-11T18:06:00", + "2023-12-11T18:09:00", + "2023-12-11T18:12:00", + "2023-12-11T18:15:00", + "2023-12-11T18:18:00", + "2023-12-11T18:21:00", + "2023-12-11T18:24:00", + "2023-12-11T18:27:00", + "2023-12-11T18:30:00", + "2023-12-11T18:33:00", + "2023-12-11T18:36:00", + "2023-12-11T18:39:00", + "2023-12-11T18:42:00", + "2023-12-11T18:45:00", + "2023-12-11T18:48:00", + "2023-12-11T18:51:00", + "2023-12-11T18:54:00", + "2023-12-11T18:57:00", + "2023-12-11T19:00:00", + "2023-12-11T19:03:00", + "2023-12-11T19:06:00", + "2023-12-11T19:09:00", + "2023-12-11T19:12:00", + "2023-12-11T19:15:00", + "2023-12-11T19:18:00", + "2023-12-11T19:21:00", + "2023-12-11T19:24:00", + "2023-12-11T19:27:00", + "2023-12-11T19:30:00", + "2023-12-11T19:33:00", + "2023-12-11T19:36:00", + "2023-12-11T19:39:00", + "2023-12-11T19:42:00", + "2023-12-11T19:45:00", + "2023-12-11T19:48:00", + "2023-12-11T19:51:00", + "2023-12-11T19:54:00", + "2023-12-11T19:57:00", + "2023-12-11T20:00:00", + "2023-12-11T20:03:00", + "2023-12-11T20:06:00", + "2023-12-11T20:09:00", + "2023-12-11T20:12:00", + "2023-12-11T20:15:00", + "2023-12-11T20:18:00", + "2023-12-11T20:21:00", + "2023-12-11T20:24:00", + "2023-12-11T20:27:00", + "2023-12-11T20:30:00", + "2023-12-11T20:33:00", + "2023-12-11T20:36:00", + "2023-12-11T20:39:00", + "2023-12-11T20:42:00", + "2023-12-11T20:45:00", + "2023-12-11T20:48:00", + "2023-12-11T20:51:00", + "2023-12-11T20:54:00", + "2023-12-11T20:57:00", + "2023-12-11T21:00:00", + "2023-12-11T21:03:00", + "2023-12-11T21:06:00", + "2023-12-11T21:09:00", + "2023-12-11T21:12:00", + "2023-12-11T21:15:00", + "2023-12-11T21:18:00", + "2023-12-11T21:21:00", + "2023-12-11T21:24:00", + "2023-12-11T21:27:00", + "2023-12-11T21:30:00", + "2023-12-11T21:33:00", + "2023-12-11T21:36:00", + "2023-12-11T21:39:00", + "2023-12-11T21:42:00", + "2023-12-11T21:45:00", + "2023-12-11T21:48:00", + "2023-12-11T21:51:00", + "2023-12-11T21:54:00", + "2023-12-11T21:57:00" + ], + "type": "candlestick" + }, + { + "marker": { + "color": "red", + "size": 7 + }, + "mode": "markers", + "name": "High Peaks", + "x": [ + "2023-12-10T21:36:00", + "2023-12-10T22:00:00", + "2023-12-10T22:24:00", + "2023-12-10T22:45:00", + "2023-12-10T23:03:00" + ], + "y": [ + 2.6786, + 2.75, + 2.7291, + 2.7209, + 2.7131 + ], + "type": "scatter" + }, + { + "marker": { + "color": "green", + "size": 7 + }, + "mode": "markers", + "name": "Low Peaks", + "x": [ + "2023-12-10T21:27:00", + "2023-12-10T22:18:00", + "2023-12-10T22:57:00", + "2023-12-10T23:21:00", + "2023-12-10T23:51:00", + "2023-12-11T00:21:00", + "2023-12-11T01:00:00", + "2023-12-11T01:24:00", + "2023-12-11T01:39:00", + "2023-12-11T02:03:00", + "2023-12-11T02:24:00", + "2023-12-11T02:39:00", + "2023-12-11T03:06:00", + "2023-12-11T03:24:00", + "2023-12-11T04:00:00", + "2023-12-11T04:18:00", + "2023-12-11T05:12:00", + "2023-12-11T05:30:00", + "2023-12-11T05:48:00", + "2023-12-11T06:30:00", + "2023-12-11T07:09:00", + "2023-12-11T07:24:00", + "2023-12-11T07:42:00", + "2023-12-11T08:18:00", + "2023-12-11T08:42:00", + "2023-12-11T09:09:00", + "2023-12-11T09:27:00", + "2023-12-11T09:45:00", + "2023-12-11T10:33:00", + "2023-12-11T10:54:00", + "2023-12-11T11:27:00", + "2023-12-11T11:45:00", + "2023-12-11T12:06:00", + "2023-12-11T12:21:00", + "2023-12-11T12:45:00", + "2023-12-11T13:03:00", + "2023-12-11T13:24:00", + "2023-12-11T13:51:00", + "2023-12-11T14:12:00", + "2023-12-11T14:27:00", + "2023-12-11T14:57:00", + "2023-12-11T15:12:00", + "2023-12-11T15:51:00", + "2023-12-11T16:09:00", + "2023-12-11T17:18:00", + "2023-12-11T17:54:00", + "2023-12-11T18:12:00", + "2023-12-11T19:15:00" + ], + "y": [ + 2.6687, + 2.7116, + 2.7121, + 2.6559, + 2.6803, + 2.6789, + 2.6303, + 2.6503, + 2.6556, + 2.6604, + 2.5541, + 2.533, + 2.5874, + 2.6336, + 2.5809, + 2.5482, + 2.5319, + 2.476, + 2.4836, + 2.4452, + 2.4707, + 2.4462, + 2.4482, + 2.4702, + 2.4901, + 2.5193, + 2.4997, + 2.5002, + 2.5158, + 2.5041, + 2.4934, + 2.5022, + 2.4984, + 2.5024, + 2.4822, + 2.4828, + 2.4732, + 2.4465, + 2.4602, + 2.4316, + 2.4431, + 2.4465, + 2.4641, + 2.4445, + 2.4264, + 2.4149, + 2.3858, + 2.3923 + ], + "type": "scatter" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "shapes": [ + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.721033333333333, + "y1": 2.721033333333333, + "yref": "y" + }, + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.75, + "y1": 2.75, + "yref": "y" + }, + { + "line": { + "color": "orange", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.6786, + "y1": 2.6786, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.6670636363636366, + "y1": 2.6670636363636366, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.4309333333333334, + "y1": 2.4309333333333334, + "yref": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 2.504964, + "y1": 2.504964, + "yref": "y" + } + ], + "annotations": [ + { + "showarrow": false, + "text": "High Cluster: 2.72", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.721033333333333, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "High Cluster: 2.75", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.75, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "High Cluster: 2.68", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.6786, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.67", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.6670636363636366, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.43", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.4309333333333334, + "yanchor": "bottom", + "yref": "y" + }, + { + "showarrow": false, + "text": "Low Cluster: 2.50", + "x": 1, + "xanchor": "right", + "xref": "x domain", + "y": 2.504964, + "yanchor": "bottom", + "yref": "y" + } + ], + "xaxis": { + "rangeslider": { + "visible": false + } + }, + "height": 800 + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "analyze_and_plot_candles_with_clusters(candles.iloc[-500:], num_clusters=3)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-04-08T15:18:24.610029Z", + "start_time": "2024-04-08T15:18:24.441693Z" + } + } + }, + { + "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 +} diff --git a/quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb b/quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb new file mode 100644 index 0000000..6d32473 --- /dev/null +++ b/quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb @@ -0,0 +1,591 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "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)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "from decimal import Decimal\n", + "\n", + "# Market configuration\n", + "exchange = \"binance_perpetual\"\n", + "trading_pair = \"WLD-USDT\"\n", + "interval = \"3m\"\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 = Decimal(\"0.015\")\n", + "take_profit = Decimal(\"0.03\")\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\")" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder\n", + "from hummingbot.smart_components.strategy_frameworks.data_types import (\n", + " TripleBarrierConf\n", + ")\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", + " # for directional strategies we don't need spreads since we are going to use market orders to enter\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", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's inpect the order levels\n", + "order_levels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "import sys\n", + "from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig\n", + "from quants_lab.controllers.supertrend import SuperTrend, SuperTrendConfig\n", + "\n", + "# Controller configuration\n", + "length = 100\n", + "multiplier = 3.0\n", + "percentage_threshold = 0.01\n", + "\n", + "# Creating the instance of the configuration and the controller\n", + "config = SuperTrendConfig(\n", + " exchange=exchange,\n", + " trading_pair=trading_pair,\n", + " order_levels=order_levels,\n", + " candles_config=[\n", + " CandlesConfig(connector=exchange, trading_pair=trading_pair, interval=interval, max_records=sys.maxsize),\n", + " ],\n", + " leverage=leverage,\n", + " length=length,\n", + " multiplier=multiplier,\n", + " percentage_threshold=percentage_threshold,\n", + ")\n", + "controller = SuperTrend(config=config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from quants_lab.strategy.strategy_analysis import StrategyAnalysis\n", + "\n", + "from hummingbot.smart_components.strategy_frameworks.directional_trading.directional_trading_backtesting_engine import \\\n", + " DirectionalTradingBacktestingEngine\n", + "\n", + "# Creating the backtesting engine and loading the historical data\n", + "engine = DirectionalTradingBacktestingEngine(controller=controller)\n", + "engine.load_controller_data(\"../../../data/candles\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's see what is inside the candles of the controller\n", + "engine.controller.candles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "engine.controller.candles[0].candles_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's understand what is inside the processed data since this is what we are going to use when generating the signal ;)\n", + "engine.controller.get_processed_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's run the backtesting\n", + "\n", + "backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd,\n", + " trade_cost=trade_cost,\n", + " start=start, end=end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Let's see what is inside the backtesting results\n", + "backtesting_results.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# now let's analyze each of the dataframes\n", + "\n", + "# 1. The processed data: this is the data that we are going to use to generate the signal\n", + "backtesting_results[\"processed_data\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# 2. The executors dataframe: this is the dataframe that contains the information of the orders that were executed\n", + "backtesting_results[\"executors_df\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# 3. The results dataframe: this is the dataframe that contains the information of the pnl of the strategy\n", + "backtesting_results[\"results\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Now let's analyze the results using the StrategyAnalysis class\n", + "strategy_analysis = StrategyAnalysis(\n", + " positions=backtesting_results[\"executors_df\"],\n", + " candles_df=backtesting_results[\"processed_data\"],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# let's visualize the PNL over time of the strategy\n", + "strategy_analysis.pnl_over_time()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "strategy_analysis.create_base_figure(volume=False, positions=False, trade_pnl=True)\n", + "fig = strategy_analysis.figure()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Now let's see how we can add the SuperTrend to the plot\n", + "\n", + "import plotly.graph_objects as go\n", + "\n", + "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", + "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", + "# Add the SuperTrend line\n", + "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", + " mode='markers',\n", + " name='SuperTrend Short',\n", + " line=dict(color=\"red\")),\n", + " row=1, col=1)\n", + "\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# To see the trades we will need to select a lower timeframe due the restrictions and speed of the plotly library\n", + "start_time = \"2023-11-03\"\n", + "end_time = \"2023-11-05\"\n", + "\n", + "processed_data_filtered = backtesting_results[\"processed_data\"][\n", + " (backtesting_results[\"processed_data\"][\"timestamp\"] >= start_time) &\n", + " (backtesting_results[\"processed_data\"][\"timestamp\"] <= end_time)\n", + "]\n", + "\n", + "executors_filtered = backtesting_results[\"executors_df\"][\n", + " (backtesting_results[\"executors_df\"][\"timestamp\"] >= start_time) &\n", + " (backtesting_results[\"executors_df\"][\"timestamp\"] <= end_time)\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "executors_filtered" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "\n", + "strategy_analysis = StrategyAnalysis(\n", + " positions=executors_filtered,\n", + " candles_df=processed_data_filtered,\n", + ")\n", + "\n", + "strategy_analysis.create_base_figure(volume=False, positions=True, trade_pnl=True)\n", + "fig = strategy_analysis.figure()\n", + "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", + "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", + "# Add the SuperTrend line\n", + "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", + " mode='markers',\n", + " name='SuperTrend Short',\n", + " line=dict(color=\"red\")),\n", + " row=1, col=1)\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Scatter of PNL per Trade\n", + "This bar chart illustrates the PNL for each individual trade. Positive PNLs are shown in green and negative PNLs in red, providing a clear view of profitable vs. unprofitable trades.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "executors_df = backtesting_results[\"executors_df\"]\n", + "\n", + "fig = px.scatter(executors_df, x=\"timestamp\", y='net_pnl_quote', title='PNL per Trade',\n", + " color='profitable', color_continuous_scale=['red', 'green'])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Scatter Plot of Volume vs. PNL\n", + "This scatter plot explores the relationship between the trade volume and the PNL for each trade. It can reveal if larger volumes are associated with higher profits or losses.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig = px.scatter(executors_df, x='volume', y='net_pnl_quote', title='Trade Volume vs. PNL')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Histogram of PNL Distribution\n", + "The histogram displays the distribution of PNL values across all trades. It helps in understanding the frequency and range of profit and loss outcomes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "fig = px.histogram(executors_df, x='net_pnl_quote', title='PNL Distribution')\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# Conclusion\n", + "We can see that the indicator has potential to bring good signals to trade and might be interesting to see how we can design a market maker that shifts the mid price based on this indicator.\n", + "A lot of the short signals are wrong but if we zoom in into the loss signals we can see that the losses are not that big and the wins are bigger and if we had implemented the trailing stop feature probably a lot of them are going to be profits." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# Next steps\n", + "- Filter only the loss signals and understand what you can do to prevent them\n", + "- Try different configuration values for the indicator\n", + "- Test in multiple markets, pick mature markets like BTC-USDT or ETH-USDT and also volatile markets like DOGE-USDT or SHIB-USDT" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb b/quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb new file mode 100644 index 0000000..c09c9b7 --- /dev/null +++ b/quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb @@ -0,0 +1,183 @@ +{ + "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": [ + "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 import SuperTrend, SuperTrendConfig\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", + " interval = \"3m\"\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 = SuperTrendConfig(\n", + " exchange=exchange,\n", + " trading_pair=trading_pair,\n", + " strategy_name='supertrend',\n", + " candles_config=[\n", + " CandlesConfig(connector=exchange, trading_pair=trading_pair,\n", + " interval=interval, 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 = SuperTrend(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 + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "import optuna\n", + "\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", + "\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 + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Not let's run the optimization!\n", + "\n", + "n_trials = 200\n", + "study.optimize(objective, n_trials=n_trials)" + ], + "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 +} diff --git a/quants_lab/research_notebooks/supertrend/01_strategy_design_supertrend.ipynb b/quants_lab/research_notebooks/supertrend/01_strategy_design_supertrend.ipynb index b081dd4..aedbd2c 100644 --- a/quants_lab/research_notebooks/supertrend/01_strategy_design_supertrend.ipynb +++ b/quants_lab/research_notebooks/supertrend/01_strategy_design_supertrend.ipynb @@ -9,6 +9,15 @@ "collapsed": false } }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } + }, { "cell_type": "code", "execution_count": 1, From f6562b1f5d2cf5a196af1c8751081d345130c9f5 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:54:32 -0300 Subject: [PATCH 003/139] (feat) default dark theme --- .streamlit/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.streamlit/config.toml b/.streamlit/config.toml index 9a427db..f6e4364 100644 --- a/.streamlit/config.toml +++ b/.streamlit/config.toml @@ -1,5 +1,5 @@ [theme] -base="light" +base="dark" font="monospace" [server] port=8501 From 02836dd68820d0c1697bd3165fe4ff8edee5a7fc Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:54:47 -0300 Subject: [PATCH 004/139] (feat) add page to generate dman maker v2 configs --- pages/dman_maker_v2/app.py | 468 +++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 pages/dman_maker_v2/app.py diff --git a/pages/dman_maker_v2/app.py b/pages/dman_maker_v2/app.py new file mode 100644 index 0000000..e427a17 --- /dev/null +++ b/pages/dman_maker_v2/app.py @@ -0,0 +1,468 @@ +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.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page +from hummingbot.smart_components.utils.distributions import Distributions + + +def normalize(values): + total = sum(values) + return [Decimal(val / total) for val in values] + + +def distribution_inputs(column, dist_type_name, levels=3): + if dist_type_name == "Spread": + dist_type = column.selectbox( + f"Type of {dist_type_name} Distribution", + ("Manual", "GeoCustom", "Geometric", "Fibonacci", "Logarithmic", "Arithmetic"), + 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.1, + 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") + 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 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): + if dist_type == "Manual": + return manual_values + elif dist_type == "Linear": + return Distributions.linear(n_levels, start, start + ts_ap) + 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) + + +# Initialize the Streamlit page +initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") + +# Page content +st.text("This tool will let you create a config for D-Man Maker V2 and upload it to the BackendAPI.") +st.write("---") + +c1, c2, c3, c4, c5, c6, c7, c8, c9 = st.columns(9) + +with c1: + connector = st.text_input("Connector", value="binance_perpetual") +with c2: + trading_pair = st.text_input("Trading Pair", value="WLD-USDT") +with c3: + leverage = st.number_input("Leverage", value=20) +with c4: + total_amount_quote = st.number_input("Total amount of quote", value=1000) +with c5: + position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) +with c6: + cooldown_time = st.number_input("Cooldown Time", value=60) +with c7: + executor_refresh_time = st.number_input("Refresh Time (minutes)", value=60) +with c8: + top_executor_refresh_time = st.number_input("Top Refresh Time (seconds)", value=60) +with c9: + executor_activation_bounds = st.number_input("Activation Bounds (%)", value=0.1) + +# Executors configuration +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=2) +with col_sell: + st.header("Sell Order Settings") + sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) + +col_buy_spreads, col_buy_amounts, col_sell_spreads, col_sell_amounts = st.columns(4) + +# Inputs for buy orders +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) +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) +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) +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) + +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 = normalize( + 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 = normalize( + 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)) + +all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions) +buy_order_amounts_quote = [Decimal(amount * total_amount_quote) for amount in + all_orders_amount_normalized[:buy_order_levels]] +sell_order_amounts_quote = [Decimal(amount * total_amount_quote) for amount in + all_orders_amount_normalized[buy_order_levels:]] + +# Initialize your figure with a dark theme +fig = make_subplots(specs=[[{"secondary_y": True}]]) +fig.update_layout( + template="plotly_dark", + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + title="Market Maker Order Distribution", + xaxis_title="Spread (%)", + yaxis_title="Amount (Quote)", + legend_title="Order Type", + font=dict(color='white', size=12) # Consistent font color and size +) + +# Define colors for buy and sell orders +colors = { + 'buy': '#32CD32', # Green for buy orders + 'sell': '#FF6347' # Tomato red for sell orders +} + +# Add traces for buy and sell orders +# Buy orders on the negative side of x-axis +fig.add_trace(go.Bar( + x=[-dist for dist in buy_spread_distributions], + y=buy_order_amounts_quote, + name='Buy Orders', + marker_color=colors['buy'], + width=[0.2] * buy_order_levels # Adjust the width of the bars as needed +), secondary_y=False) + +# Sell orders on the positive side of x-axis +fig.add_trace(go.Bar( + x=sell_spread_distributions, + y=sell_order_amounts_quote, + name='Sell Orders', + marker_color=colors['sell'], + width=[0.2] * buy_order_levels # Adjust the width of the bars as needed + +), secondary_y=False) + +# Annotations can be added for each bar to display the value on top +for i, value in enumerate(buy_order_amounts_quote): + fig.add_annotation( + x=-buy_spread_distributions[i], + y=value + 10, # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['buy'], size=10) + ) + +for i, value in enumerate(sell_order_amounts_quote): + fig.add_annotation( + x=sell_spread_distributions[i], + y=value + 10, # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['sell'], size=10) + ) + +# Optional: Add horizontal line or extra annotations if needed +# e.g., for average, threshold, or specific markers + +# Update the layout to make it responsive and visually appealing +fig.update_layout( + height=600, + width=800, + margin=dict(l=20, r=20, t=50, b=20) +) + +# Display the figure in Streamlit +st.plotly_chart(fig, use_container_width=True) + +# Layout in columns +col_dca_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2]) + +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)] +with col_dca_quote: + executor_level = st.selectbox("Executor Level", buy_executor_levels + sell_executor_levels) + side, level = executor_level.split("_") + if side == "BUY": + dca_amount = buy_order_amounts_quote[int(level)] + else: + dca_amount = sell_order_amounts_quote[int(level)] + st.write(f"DCA Amount: {dca_amount:.2f}") + +with col_tp_sl: + 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) + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=3.0, step=0.1) + +with col_levels: + n_levels = st.number_input("Number of Levels", min_value=1, value=5) + tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=5.0, step=0.1) + time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=60 * 6) + +# Spread and Amount Distributions +spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs( + col_spread_dist, "Spread", n_levels) +amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs( + col_amount_dist, "Amount", n_levels) + +spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, + spread_step, spread_ratio, manual_spreads) +amount_distribution = normalize( + get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, + manual_amounts)) +dca_order_amounts = [Decimal(amount_dist * dca_amount) for amount_dist in amount_distribution] +dca_spreads = [Decimal(spread - spread_distribution[0]) for spread in spread_distribution] + +break_even_values = [] +take_profit_values = [] +for level in range(n_levels): + dca_spreads_normalized = [Decimal(spread) + Decimal(0.01) for spread in dca_spreads[:level + 1]] + amounts = dca_order_amounts[:level + 1] + break_even = (sum([spread * amount for spread, amount in zip(dca_spreads_normalized, amounts)]) / sum( + amounts)) - Decimal(0.01) + break_even_values.append(break_even) + take_profit_values.append(break_even - Decimal(ts_ap)) + +accumulated_amount = [sum(dca_order_amounts[:i + 1]) for i in range(len(dca_order_amounts))] + + +def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): + unrealized_pnl = [] + for i in range(len(spreads)): + distance = abs(spreads[i] - break_even_values[i]) + pnl = accumulated_amount[i] * distance / 100 # PNL calculation + unrealized_pnl.append(pnl) + return unrealized_pnl + + +# Calculate unrealized PNL +cum_unrealized_pnl = calculate_unrealized_pnl(dca_spreads, break_even_values, accumulated_amount) + +tech_colors = { + 'spread': '#00BFFF', # Deep Sky Blue + 'break_even': '#FFD700', # Gold + 'take_profit': '#32CD32', # Green + 'order_amount': '#1E90FF', # Dodger Blue + 'cum_amount': '#4682B4', # Steel Blue + 'stop_loss': '#FF0000', # Red +} + +# Create Plotly figure with secondary y-axis and a dark theme +fig = make_subplots(specs=[[{"secondary_y": True}]]) +fig.update_layout(template="plotly_dark") + +# Update the Scatter Plots and Horizontal Lines +fig.add_trace(go.Scatter(x=list(range(len(dca_spreads))), y=dca_spreads, name='Spread (%)', mode='lines+markers', + line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) +fig.add_trace( + go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', mode='lines+markers', + line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) +fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', + mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), secondary_y=False) + +# Add the new Bar Plot for Cumulative Unrealized PNL +fig.add_trace(go.Bar( + x=list(range(len(cum_unrealized_pnl))), + y=cum_unrealized_pnl, + text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], + textposition='auto', + textfont=dict(color='white', size=12), + name='Cum Unrealized PNL', + marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed +), secondary_y=True) + +fig.add_trace(go.Bar( + x=list(range(len(dca_order_amounts))), + y=dca_order_amounts, + text=[f"{amt:.2f}" for amt in dca_order_amounts], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Order Amount', + marker=dict(color=tech_colors['order_amount'], opacity=0.5), +), secondary_y=True) + +# Modify the Bar Plot for Accumulated Amount +fig.add_trace(go.Bar( + x=list(range(len(accumulated_amount))), + y=accumulated_amount, + text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Cum Amount', + marker=dict(color=tech_colors['cum_amount'], opacity=0.5), +), secondary_y=True) + +# Add Horizontal Lines for Last Breakeven Price and Stop Loss Level +last_break_even = break_even_values[-1] +stop_loss_value = last_break_even + Decimal(sl) +# Horizontal Lines for Last Breakeven and Stop Loss +fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", + annotation_position="top left", line_color=tech_colors['break_even']) +fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", + annotation_position="bottom right", line_color=tech_colors['stop_loss']) + +# Update Annotations for Spread and Break Even +for i, (spread, be_value, tp_value) in enumerate(zip(dca_spreads, break_even_values, take_profit_values)): + fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, + font=dict(color=tech_colors['spread'])) + fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, + font=dict(color=tech_colors['break_even'])) + fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, + font=dict(color=tech_colors['take_profit'])) +# Update Layout with a Dark Theme +fig.update_layout( + title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", + xaxis_title="Order Level", + yaxis_title="Spread (%)", + yaxis2_title="Amount (Quote)", + height=800, + width=1800, + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + font=dict(color='white') # Font color +) + +# Calculate metrics +dca_max_loss = dca_amount * Decimal(sl / 100) +profit_per_level = [cum_amount * Decimal(ts_ap / 100) for cum_amount in accumulated_amount] +loots_to_recover = [dca_max_loss / profit for profit in profit_per_level] + +# Define a consistent annotation size and maximum value for the secondary y-axis +circle_text = "●" # Unicode character for a circle +max_secondary_value = max(max(accumulated_amount), max(dca_order_amounts), + max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data + +# Determine an appropriate y-offset for annotations +y_offset_secondary = max_secondary_value * Decimal( + 0.1) # Adjusts the height relative to the maximum value on the secondary y-axis + +# Add annotations to the Plotly figure for the secondary y-axis +for i, loot in enumerate(loots_to_recover): + fig.add_annotation( + x=i, + y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset + text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines + showarrow=False, + font=dict(size=16, color='purple'), + xanchor="center", # Centers the text above the x coordinate + yanchor="bottom", # Anchors the text at its bottom to avoid overlapping + align="center", + yref="y2" # Reference the secondary y-axis + ) +# Add Max Loss Metric as an Annotation +dca_max_loss_annotation_text = f"DCA Max Loss (Quote): {dca_max_loss:.2f}" +fig.add_annotation( + x=max(len(dca_spreads), len(break_even_values)) - 1, # Positioning the annotation to the right + text=dca_max_loss_annotation_text, + showarrow=False, + font=dict(size=20, color='white'), + bgcolor='red', # Red background for emphasis + xanchor="left", + yanchor="top", + yref="y2" # Reference the secondary y-axis +) + +st.write("---") + +# Display in Streamlit +st.plotly_chart(fig) +c1, c2, c3 = st.columns([2, 2, 1]) +with c1: + config_base = st.text_input("Config Base", value=f"{connector}-{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": "dman_maker_v2", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector, + "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_quote, + "sell_amounts_pct": sell_order_amounts_quote, + "executor_refresh_time": executor_refresh_time * 60, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "stop_loss": sl / 100, + "take_profit": tp / 100, + "time_limit": time_limit * 60, + "take_profit_order_type": 2, + "trailing_stop": { + "activation_price": ts_ap / 100, + "trailing_delta": ts_delta / 100}, + "dca_amounts": dca_order_amounts, + "dca_spreads": [spread / 100 for spread in dca_spreads], + "top_executor_refresh_time": top_executor_refresh_time, + "executor_activation_bounds": [executor_activation_bounds / 100], + } +yaml_config = yaml.dump(config, default_flow_style=False) + +with c3: + download_config = st.download_button( + label="Download YAML", + data=yaml_config, + file_name='config.yaml', + mime='text/yaml' + ) + upload_config_to_backend = st.button("Upload Config to BackendAPI") + + +if upload_config_to_backend: + backend_api_client = BackendAPIClient.get_instance() + backend_api_client.add_controller_config(config) + st.success("Config uploaded successfully!") \ No newline at end of file From da027c75e50500c64dd8dba4a11321b52e4d615a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:55:09 -0300 Subject: [PATCH 005/139] (feat) refactor bot orchestration page --- pages/bot_orchestration/app.py | 163 ++++++--------------------------- 1 file changed, 28 insertions(+), 135 deletions(-) diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index 00f9499..a74f3c3 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -1,99 +1,15 @@ -from types import SimpleNamespace - -from commlib.exceptions import RPCClientTimeoutError - -import constants import streamlit as st -from streamlit_elements import elements, mui, lazy, sync, event -import time +from streamlit_elements import elements, mui -from docker_manager import DockerManager -from hbotrc import BotCommands - -from ui_components.bot_performance_card import BotPerformanceCard +from ui_components.bot_performance_card_v2 import BotPerformanceCardV2 from ui_components.dashboard import Dashboard -from ui_components.exited_bot_card import ExitedBotCard -from ui_components.launch_bot_card import LaunchBotCard -from ui_components.launch_broker_card import LaunchBrokerCard +from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page -CARD_WIDTH = 6 +# Constants for UI layout +CARD_WIDTH = 12 CARD_HEIGHT = 3 -NUM_CARD_COLS = 2 - -initialize_st_page(title="Instances", icon="🦅", initial_sidebar_state="collapsed") - -if "is_broker_running" not in st.session_state: - st.session_state.is_broker_running = False - -if "active_bots" not in st.session_state: - st.session_state.active_bots = {} - -if "exited_bots" not in st.session_state: - st.session_state.exited_bots = {} - -if "new_bot_name" not in st.session_state: - st.session_state.new_bot_name = "" - -if "selected_strategy" not in st.session_state: - st.session_state.selected_strategy = None - -if "editor_tabs" not in st.session_state: - st.session_state.editor_tabs = {} - - -def update_containers_info(docker_manager): - active_containers = docker_manager.get_active_containers() - st.session_state.is_broker_running = "hummingbot-broker" in active_containers - if st.session_state.is_broker_running: - try: - active_hbot_containers = [container for container in active_containers if - "hummingbot-" in container and "broker" not in container - and "master_bot_conf" not in container] - previous_active_bots = list(st.session_state.active_bots) - - # Remove bots that are no longer active - for bot in previous_active_bots: - if bot not in active_hbot_containers: - del st.session_state.active_bots[bot] - - # Add new bots - for bot in active_hbot_containers: - if bot not in previous_active_bots: - st.session_state.active_bots[bot] = { - "bot_name": bot, - "broker_client": BotCommands(host='localhost', port=1883, username='admin', password='password', - bot_id=bot) - } - - # Update bot info - for bot in list(st.session_state.active_bots): - try: - broker_client = st.session_state.active_bots[bot]["broker_client"] - status = broker_client.status() - history = broker_client.history() - is_running = "No strategy is currently running" not in status.msg - st.session_state.active_bots[bot]["is_running"] = is_running - st.session_state.active_bots[bot]["status"] = status.msg - st.session_state.active_bots[bot]["trades"] = history.trades - st.session_state.active_bots[bot]["selected_strategy"] = None - except RPCClientTimeoutError: - st.error(f"RPCClientTimeoutError: Could not connect to {bot}. Please review the connection.") - del st.session_state.active_bots[bot] - except RuntimeError: - st.rerun() - st.session_state.active_bots = dict( - sorted(st.session_state.active_bots.items(), key=lambda x: x[1]['is_running'], reverse=True)) - else: - st.session_state.active_bots = {} - - -docker_manager = DockerManager() -if not docker_manager.is_docker_running(): - st.warning("Docker is not running. Please start Docker and refresh the page.") - st.stop() -update_containers_info(docker_manager) -exited_containers = [container for container in docker_manager.get_exited_containers() if "broker" not in container] +NUM_CARD_COLS = 1 def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_HEIGHT, card_height: int = CARD_WIDTH): @@ -102,55 +18,32 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int return sorted(x_y, key=lambda x: (x[1], x[0])) -if "create_containers_board" not in st.session_state: - board = Dashboard() - create_containers_board = SimpleNamespace( - dashboard=board, - launch_bot=LaunchBotCard(board, 0, 0, 8, 1.5), - launch_broker=LaunchBrokerCard(board, 8, 0, 4, 1.5) - ) - st.session_state.create_containers_board = create_containers_board - -else: - create_containers_board = st.session_state.create_containers_board +initialize_st_page(title="Instances", icon="🦅", initial_sidebar_state="collapsed") +api_client = BackendAPIClient.get_instance(host="localhost", port=8000) -with elements("create_bot"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - with create_containers_board.dashboard(): - create_containers_board.launch_bot() - create_containers_board.launch_broker() +if not api_client.is_docker_running(): + st.warning("Docker is not running. Please start Docker and refresh the page.") + st.stop() -with elements("active_instances_board"): - with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): - mui.Typography("🦅 Active Instances", variant="h5") - if st.session_state.is_broker_running: - quantity_of_active_bots = len(st.session_state.active_bots) - if quantity_of_active_bots > 0: - # TODO: Make layout configurable - grid_positions = get_grid_positions(n_cards=quantity_of_active_bots, cols=NUM_CARD_COLS, - card_width=CARD_WIDTH, card_height=CARD_HEIGHT) +active_bots_response = api_client.get_active_bots_status() +if active_bots_response.get("status") == "success": + with elements("active_instances_board"): + with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): + mui.Typography("🦅 Active Instances", variant="h5") + active_bots = active_bots_response.get("data") + if active_bots: + positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) active_instances_board = Dashboard() - for (bot, config), (x, y) in zip(st.session_state.active_bots.items(), grid_positions): - st.session_state.active_bots[bot]["bot_performance_card"] = BotPerformanceCard(active_instances_board, - x, y, - CARD_WIDTH, CARD_HEIGHT) - with active_instances_board(): - for bot, config in st.session_state.active_bots.items(): - st.session_state.active_bots[bot]["bot_performance_card"](config) + for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): + card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) + with active_instances_board(): + card(bot_info) else: mui.Alert("No active bots found. Please create a new bot.", severity="info", sx={"margin": "1rem"}) - else: - mui.Alert("Please start Hummingbot Broker to control your bots.", severity="warning", sx={"margin": "1rem"}) -with elements("stopped_instances_board"): - grid_positions = get_grid_positions(n_cards=len(exited_containers), cols=NUM_CARD_COLS, card_width=CARD_WIDTH, card_height=CARD_HEIGHT) - exited_instances_board = Dashboard() - for exited_instance, (x, y) in zip(exited_containers, grid_positions): - st.session_state.exited_bots[exited_instance] = ExitedBotCard(exited_instances_board, x, y, - CARD_WIDTH, 1) - with mui.Paper(style={"padding": "2rem"}, variant="outlined"): - mui.Typography("💤 Stopped Instances", variant="h5") - with exited_instances_board(): - for bot, card in st.session_state.exited_bots.items(): - card(bot) + + + + + From 29e1353986d13987e35de25a200d4f17b4409b25 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:55:17 -0300 Subject: [PATCH 006/139] (feat) change layout of pages --- main.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index cebdbb8..7061e04 100644 --- a/main.py +++ b/main.py @@ -11,16 +11,18 @@ def main_page(): [ Page("main.py", "Hummingbot Dashboard", "📊"), Section("Bot Orchestration", "🐙"), - Page("pages/master_conf/app.py", "Credentials", "🗝️"), Page("pages/bot_orchestration/app.py", "Instances", "🦅"), - Page("pages/file_manager/app.py", "File Explorer", "🗂"), + Page("pages/launch_bot/app.py", "Deploy", "🚀"), + Section("Config Generator", "🎛️"), Page("pages/position_builder/app.py", "Position Builder", "🔭"), + Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), + # Page("pages/master_conf/app.py", "Credentials", "🗝️"), + # Page("pages/file_manager/app.py", "File Explorer", "🗂"), Section("Backtest Manager", "⚙️"), Page("pages/backtest_get_data/app.py", "Get Data", "💾"), Page("pages/backtest_create/create.py", "Create", "⚔️"), Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), - Page("pages/launch_bot/app.py", "Deploy", "🙌"), Section("Community Pages", "👨‍👩‍👧‍👦"), Page("pages/strategy_performance/app.py", "Strategy Performance", "🚀"), Page("pages/db_inspector/app.py", "DB Inspector", "🔍"), From 830b0487a1b29dbc4611b78c6c71fc67673ff489 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:55:25 -0300 Subject: [PATCH 007/139] (feat) create bot performance card v2 --- ui_components/bot_performance_card_v2.py | 114 +++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 ui_components/bot_performance_card_v2.py diff --git a/ui_components/bot_performance_card_v2.py b/ui_components/bot_performance_card_v2.py new file mode 100644 index 0000000..aaf2546 --- /dev/null +++ b/ui_components/bot_performance_card_v2.py @@ -0,0 +1,114 @@ +from streamlit_elements import mui, lazy +from ui_components.dashboard import Dashboard +import streamlit as st +import time +import pandas as pd +import datetime + +from utils.backend_api_client import BackendAPIClient + +TRADES_TO_SHOW = 5 +WIDE_COL_WIDTH = 150 +MEDIUM_COL_WIDTH = 140 +backend_api_client = BackendAPIClient.get_instance() + + +def stop_bot(bot_name): + backend_api_client.stop_bot(bot_name) + backend_api_client.stop_container(bot_name) + backend_api_client.remove_container(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): + def __init__(self, board, x, y, w, h, **item_props): + super().__init__(board, x, y, w, h, **item_props) + + def __call__(self, bot_config: dict): + bot_name = bot_config["bot_name"] + status = bot_config.get("status", {"running_status": "not available"}) + is_running = status["running_status"] == "running" + global_performance = status.get("global_performance") + with mui.Card(key=self._key, + sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, + elevation=2): + color = "green" if is_running else "grey" + mui.CardHeader( + title=bot_name, + subheader=status["running_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}): + # Balances Table + mui.Typography("Balances", variant="h6") + + balances = status.get("total_balances", {}) + if balances: + rows = [(exchange, symbol, round(float(value), 2)) for exchange, inner_dict in balances.items() for symbol, value + in inner_dict.items()] + df_balances = pd.DataFrame(rows, columns=["Exchange", "Currency", "Amount"]).reset_index().rename(columns={"index": "id"}) + + balances_rows = df_balances.to_dict(orient='records') + balances_cols = [{'field': col, 'headerName': col} for col in df_balances.columns] + + for column in balances_cols: + # Customize width for 'exchange' column + if column['field'] == 'Exchange': + column['width'] = WIDE_COL_WIDTH + mui.DataGrid( + rows=balances_rows, + columns=balances_cols, + autoHeight=True, + density="compact", + disableColumnSelector=True, + hideFooter=True, + initialState={"columns": {"columnVisibilityModel": {"id": False}}}) + else: + mui.Typography(str(balances), sx={"fontSize": "0.75rem"}) + mui.Divider(sx={"margin": "1rem 0"}) + # Controllers Table + mui.Typography("Controllers", variant="h6", sx={"marginTop": 2}) + controllers = status.get("controllers") + if controllers: + controllers_list = [] + for controller, inner_dict in controllers.items(): + controllers_list.append({ + "Controller ID": controller, + "Realized PNL ($)": inner_dict.get("realized_pnl_quote", 0), + "Unrealized PNL ($)": inner_dict.get("unrealized_pnl_quote", 0), + "GLOBAL PNL ($)": inner_dict.get("global_pnl_quote", 0), + # "global_pnl_pct": inner_dict.get("global_pnl_pct", 0), + "Volume ($)": inner_dict.get("total_volume_traded", 0), + }) + + df_controllers = pd.DataFrame(controllers_list).reset_index().rename(columns={"index": "id"}) + controllers_rows = df_controllers.to_dict(orient='records') + controllers_cols = [{'field': col, 'headerName': col} for col in df_controllers.columns] + for column in controllers_cols: + # Customize width for 'exchange' column + column['width'] = WIDE_COL_WIDTH + mui.DataGrid( + rows=controllers_rows, + columns=controllers_cols, + autoHeight=True, + density="compact", + disableColumnSelector=True, + hideFooter=True, + initialState={"columns": {"columnVisibilityModel": {"id": False}}}) + else: + mui.Typography(str(controllers), sx={"fontSize": "0.75rem"}) + mui.Divider(sx={"margin": "1rem 0"}) + mui.Typography("Global Performance", variant="h6") + if global_performance: + global_pnl_quote = global_performance.get("global_pnl_quote", 0) + global_pnl_pct = global_performance.get("global_pnl_pct", 0) + total_volume_traded = global_performance.get("total_volume_traded", 0) + mui.Typography(f" Global PnL (Quote): {global_pnl_quote} | Global PnL %: {global_pnl_pct} | Total Volume Traded: {total_volume_traded}") + else: + mui.Typography("No global performance data available", sx={"fontSize": "0.75rem"}) From 4d6247aaa41be7107a9ca8e37e556c8cd06456d7 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:59:17 -0300 Subject: [PATCH 008/139] (feat) add README --- pages/dman_maker_v2/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 pages/dman_maker_v2/README.md diff --git a/pages/dman_maker_v2/README.md b/pages/dman_maker_v2/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/dman_maker_v2/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file From 31dcc2391e13163dbbfb63335a9f1b0c84b75e20 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:59:32 -0300 Subject: [PATCH 009/139] (feat) improve file name downloaded --- pages/dman_maker_v2/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/dman_maker_v2/app.py b/pages/dman_maker_v2/app.py index e427a17..283013b 100644 --- a/pages/dman_maker_v2/app.py +++ b/pages/dman_maker_v2/app.py @@ -456,7 +456,7 @@ with c3: download_config = st.download_button( label="Download YAML", data=yaml_config, - file_name='config.yaml', + file_name=f'{id.lower()}.yml', mime='text/yaml' ) upload_config_to_backend = st.button("Upload Config to BackendAPI") From c6a5550cadf4876b66f57bd820dcaee4ad5ef7da Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:59:38 -0300 Subject: [PATCH 010/139] (feat) add backend api client --- utils/backend_api_client.py | 161 ++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 utils/backend_api_client.py diff --git a/utils/backend_api_client.py b/utils/backend_api_client.py new file mode 100644 index 0000000..8cb7daf --- /dev/null +++ b/utils/backend_api_client.py @@ -0,0 +1,161 @@ +import requests + + +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) + return response.json() + + 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 response.json() # Handle errors or no data 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_credentials(self): + """Get available credentials.""" + url = f"{self.base_url}/list-credentials" + 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() From 33a7f78c58531d5c9d472908899c05a6ed3e5dab Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 19:59:46 -0300 Subject: [PATCH 011/139] (feat) refactor launch strategy card --- ui_components/launch_strategy_v2.py | 163 ++++++++++++++++++---------- 1 file changed, 105 insertions(+), 58 deletions(-) diff --git a/ui_components/launch_strategy_v2.py b/ui_components/launch_strategy_v2.py index b329ed6..1cf7724 100644 --- a/ui_components/launch_strategy_v2.py +++ b/ui_components/launch_strategy_v2.py @@ -1,36 +1,39 @@ import json -import os import time -from docker_manager import DockerManager import streamlit as st from hummingbot.core.data_type.common import PositionMode, OrderType, TradeType from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder from streamlit_elements import mui, lazy -import constants -from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ - get_yml_files_from_directory +from utils.backend_api_client import BackendAPIClient from .dashboard import Dashboard class LaunchStrategyV2(Dashboard.Item): DEFAULT_ROWS = [] - DEFAULT_COLUMNS = DEFAULT_COLUMNS = [ - {"field": 'id', "headerName": 'ID', "width": 180}, - {"field": 'strategy_name', "headerName": 'Strategy Name', "width": 180, "editable": False, }, - {"field": 'exchange', "headerName": 'Exchange', "width": 180, "editable": True, }, - {"field": 'trading_pair', "headerName": 'Trading_pair', "width": 180, "editable": True, }, + 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._controllers_available = get_python_files_from_directory(constants.CONTROLLERS_PATH) - self._controller_selected = self._controllers_available[0] - self._controller_configs_available = get_yml_files_from_directory("hummingbot_files/controller_configs") + self._backend_api_client = BackendAPIClient.get_instance() + self._controller_configs_available = self._backend_api_client.get_all_controllers_config() self._controller_config_selected = None self._bot_name = None self._image_name = "hummingbot/hummingbot:latest" - self._base_bot_config = "master_bot_conf" + self._credentials = "master_account" def _set_bot_name(self, event): self._bot_name = event.target.value @@ -38,29 +41,41 @@ class LaunchStrategyV2(Dashboard.Item): def _set_image_name(self, event): self._image_name = event.target.value - def _set_base_bot_config(self, event): - self._base_bot_config = event.target.value + def _set_credentials(self, event): + self._credentials = event.target.value def _set_controller(self, event): self._controller_selected = event.target.value def _handle_row_selection(self, params, _): - self._controller_config_selected = 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: - bot_name = f"hummingbot-{self._bot_name}" - extra_environment_variables = ["-e", "CONFIG_FILE_NAME=strategy_v2_launcher.py", - "-e", f"controller_configs={','.join(self._controller_config_selected)}"] - DockerManager().create_hummingbot_instance(instance_name=bot_name, - base_conf_folder=f"{constants.HUMMINGBOT_TEMPLATES}/{self._base_bot_config}/.", - target_conf_folder=f"{constants.BOTS_FOLDER}/{bot_name}/.", - controllers_folder=constants.CONTROLLERS_PATH, - controllers_config_folder=constants.CONTROLLERS_CONFIG_PATH, - extra_environment_variables=extra_environment_variables, - image=self._image_name, - ) - with st.spinner('Starting Master Configs instance... This process may take a few seconds'): + 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_generic_with_cash_out.py", + "time_to_cash_out": None, + } + } + + self._backend_api_client.add_script_config(script_config) + deploy_config = { + "instance_name": bot_name, + "script": "v2_generic_with_cash_out.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 " @@ -79,52 +94,55 @@ class LaunchStrategyV2(Dashboard.Item): "The new instance will contain the credentials configured in the following base instance:", severity="info") with mui.Grid(item=True, xs=4): - master_configs = [conf.split("/")[-2] for conf in - get_directories_from_directory(constants.HUMMINGBOT_TEMPLATES) if - "bot_conf" in conf] + available_credentials = self._backend_api_client.get_available_credentials() with mui.FormControl(variant="standard", sx={"width": "100%"}): - mui.FormHelperText("Base Configs") - with mui.Select(label="Base Configs", defaultValue=master_configs[0], - variant="standard", onChange=lazy(self._set_base_bot_config)): - for master_config in master_configs: + mui.FormHelperText("Credentials") + with mui.Select(label="Credentials", defaultValue=available_credentials[0], + 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): - mui.TextField(label="Hummingbot Image", - defaultValue="hummingbot/hummingbot:latest", - variant="outlined", - placeholder="hummingbot-[name]", - onChange=lazy(self._set_image_name), - sx={"width": "100%"}) + 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="hummingbot/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%"}): + 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: + data.append({"id": config["id"], "controller_name": config["controller_name"], + "controller_type": config["controller_type"], + "connector_name": config["connector_name"], + "trading_pair": config["trading_pair"], + "total_amount_quote": config["total_amount_quote"], + "max_loss_quote": config["total_amount_quote"] * config["stop_loss"], + "stop_loss": config["stop_loss"], + "take_profit": config["take_profit"], + "trailing_stop": str(config["trailing_stop"]["activation_price"]) + " / " + + str(config["trailing_stop"]["trailing_delta"]), + "time_limit": config["time_limit"]}) - with mui.Grid(item=True, xs=8): - try: - encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode) - data = [] - for config in self._controller_configs_available: - decoded_config = encoder_decoder.yaml_load(config) - data.append({"id": config.split("/")[-1], "strategy_name": decoded_config["strategy_name"], - "exchange": decoded_config["exchange"], "trading_pair": decoded_config["trading_pair"]}) - except json.JSONDecodeError: - data = self.DEFAULT_ROWS - + 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("Data grid") - + mui.Typography("Controllers Config") with mui.Box(sx={"flex": 1, "minHeight": 3}): mui.DataGrid( columns=self.DEFAULT_COLUMNS, @@ -135,4 +153,33 @@ class LaunchStrategyV2(Dashboard.Item): disableSelectionOnClick=True, onSelectionModelChange=self._handle_row_selection, ) + # with mui.Grid(item=True, xs=8): + # try: + # encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode) + # data = [] + # for config in self._controller_configs_available: + # decoded_config = encoder_decoder.yaml_load(config) + # data.append({"id": config.split("/")[-1], "strategy_name": decoded_config["strategy_name"], + # "exchange": decoded_config["exchange"], "trading_pair": decoded_config["trading_pair"]}) + # except json.JSONDecodeError: + # data = self.DEFAULT_ROWS + # 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("Data grid") + # + # 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, + # ) + # From f649916ec7291a4f16f3389155c99d02bb60046e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 12 Apr 2024 20:37:53 -0300 Subject: [PATCH 012/139] (feat) refactor environment --- Dockerfile | 7 ------- environment_conda.yml | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 865eb80..d117555 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/environment_conda.yml b/environment_conda.yml index c66e00c..d31bbbb 100644 --- a/environment_conda.yml +++ b/environment_conda.yml @@ -11,6 +11,7 @@ dependencies: - hummingbot - streamlit - watchdog + - python-dotenv - plotly - pycoingecko - glom From bf2077d0b6fe6b0cb9230e03056a726dca9cd53d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 16 Apr 2024 12:19:29 -0300 Subject: [PATCH 013/139] (feat) add broker host and port as env configuirable variables --- CONFIG.py | 8 ++++++++ pages/bot_orchestration/app.py | 3 ++- pages/dman_maker_v2/app.py | 7 ++++--- ui_components/bot_performance_card_v2.py | 7 ++++--- ui_components/launch_strategy_v2.py | 16 +++++++--------- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CONFIG.py b/CONFIG.py index 4cf953f..bf3ab9e 100644 --- a/CONFIG.py +++ b/CONFIG.py @@ -1,3 +1,7 @@ +import os + +from dotenv import 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 +16,7 @@ CERTIFIED_EXCHANGES = ["ascendex", "binance", "bybit", "gate.io", "hitbtc", "huo CERTIFIED_STRATEGIES = ["xemm", "cross exchange market making", "pmm", "pure market making"] AUTH_SYSTEM_ENABLED = False +load_dotenv() + +BACKEND_API_HOST = os.getenv("BACKEND_API_HOST", "localhost") +BACKEND_API_PORT = os.getenv("BACKEND_API_PORT", 8000) \ No newline at end of file diff --git a/pages/bot_orchestration/app.py b/pages/bot_orchestration/app.py index a74f3c3..a7289da 100644 --- a/pages/bot_orchestration/app.py +++ b/pages/bot_orchestration/app.py @@ -1,6 +1,7 @@ import streamlit as st from streamlit_elements import elements, mui +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from ui_components.bot_performance_card_v2 import BotPerformanceCardV2 from ui_components.dashboard import Dashboard from utils.backend_api_client import BackendAPIClient @@ -19,7 +20,7 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int initialize_st_page(title="Instances", icon="🦅", initial_sidebar_state="collapsed") -api_client = BackendAPIClient.get_instance(host="localhost", port=8000) +api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) if not api_client.is_docker_running(): diff --git a/pages/dman_maker_v2/app.py b/pages/dman_maker_v2/app.py index 283013b..78bf932 100644 --- a/pages/dman_maker_v2/app.py +++ b/pages/dman_maker_v2/app.py @@ -5,6 +5,7 @@ import plotly.graph_objects as go from decimal import Decimal import yaml +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page from hummingbot.smart_components.utils.distributions import Distributions @@ -430,8 +431,8 @@ config = { "connector_name": connector, "trading_pair": trading_pair, "total_amount_quote": total_amount_quote, - "buy_spreads": buy_spread_distributions, - "sell_spreads": sell_spread_distributions, + "buy_spreads": [spread / 100 for spread in buy_spread_distributions], + "sell_spreads": [spread / 100 for spread in sell_spread_distributions], "buy_amounts_pct": buy_order_amounts_quote, "sell_amounts_pct": sell_order_amounts_quote, "executor_refresh_time": executor_refresh_time * 60, @@ -463,6 +464,6 @@ with c3: if upload_config_to_backend: - backend_api_client = BackendAPIClient.get_instance() + 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!") \ No newline at end of file diff --git a/ui_components/bot_performance_card_v2.py b/ui_components/bot_performance_card_v2.py index aaf2546..27901f3 100644 --- a/ui_components/bot_performance_card_v2.py +++ b/ui_components/bot_performance_card_v2.py @@ -1,16 +1,17 @@ from streamlit_elements import mui, lazy + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from ui_components.dashboard import Dashboard import streamlit as st import time import pandas as pd -import datetime from utils.backend_api_client import BackendAPIClient TRADES_TO_SHOW = 5 -WIDE_COL_WIDTH = 150 +WIDE_COL_WIDTH = 180 MEDIUM_COL_WIDTH = 140 -backend_api_client = BackendAPIClient.get_instance() +backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) def stop_bot(bot_name): diff --git a/ui_components/launch_strategy_v2.py b/ui_components/launch_strategy_v2.py index 1cf7724..b261041 100644 --- a/ui_components/launch_strategy_v2.py +++ b/ui_components/launch_strategy_v2.py @@ -1,11 +1,9 @@ -import json import time import streamlit as st -from hummingbot.core.data_type.common import PositionMode, OrderType, TradeType -from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder from streamlit_elements import mui, lazy +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from .dashboard import Dashboard @@ -28,21 +26,21 @@ class LaunchStrategyV2(Dashboard.Item): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._backend_api_client = BackendAPIClient.get_instance() + 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 = "hummingbot/hummingbot:latest" + 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, event): - self._image_name = event.target.value + def _set_image_name(self, _, childs): + self._image_name = childs.props.value - def _set_credentials(self, event): - self._credentials = event.target.value + def _set_credentials(self, _, childs): + self._credentials = childs.props.value def _set_controller(self, event): self._controller_selected = event.target.value From 6995e74440f4796c1d3cb3bfd1665f02d3edaa7f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 16 Apr 2024 12:19:42 -0300 Subject: [PATCH 014/139] (feat) remove docker manager --- pages/launch_bot/app.py | 8 -------- pages/master_conf/app.py | 5 ----- 2 files changed, 13 deletions(-) diff --git a/pages/launch_bot/app.py b/pages/launch_bot/app.py index dd2e43c..70b661b 100644 --- a/pages/launch_bot/app.py +++ b/pages/launch_bot/app.py @@ -3,8 +3,6 @@ from types import SimpleNamespace import streamlit as st from streamlit_elements import elements, mui -from docker_manager import DockerManager - from ui_components.dashboard import Dashboard from ui_components.launch_strategy_v2 import LaunchStrategyV2 from utils.st_utils import initialize_st_page @@ -16,12 +14,6 @@ NUM_CARD_COLS = 2 initialize_st_page(title="Launch Bot", icon="🙌", initial_sidebar_state="collapsed") -docker_manager = DockerManager() -if not docker_manager.is_docker_running(): - st.warning("Docker is not running. Please start Docker and refresh the page.") - st.stop() - - def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_HEIGHT, card_height: int = CARD_WIDTH): rows = n_cards // cols + 1 x_y = [(x * card_width, y * card_height) for x in range(cols) for y in range(rows)] diff --git a/pages/master_conf/app.py b/pages/master_conf/app.py index 75727f4..c5b1758 100644 --- a/pages/master_conf/app.py +++ b/pages/master_conf/app.py @@ -15,11 +15,6 @@ from utils.st_utils import initialize_st_page initialize_st_page(title="Credentials", icon="🗝️", initial_sidebar_state="collapsed") -docker_manager = DockerManager() -if not docker_manager.is_docker_running(): - st.warning("Docker is not running. Please start Docker and refresh the page.") - st.stop() - if "mc_board" not in st.session_state: board = Dashboard() mc_board = SimpleNamespace( From 74c86fbbe46332ea5dff3175d4be67e642fec07e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 16 Apr 2024 12:20:27 -0300 Subject: [PATCH 015/139] (feat) refactor main menu --- main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 7061e04..b65f27f 100644 --- a/main.py +++ b/main.py @@ -18,11 +18,11 @@ def main_page(): Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), # Page("pages/master_conf/app.py", "Credentials", "🗝️"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), - Section("Backtest Manager", "⚙️"), - Page("pages/backtest_get_data/app.py", "Get Data", "💾"), - Page("pages/backtest_create/create.py", "Create", "⚔️"), - Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), - Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), + # Section("Backtest Manager", "⚙️"), + # Page("pages/backtest_get_data/app.py", "Get Data", "💾"), + # Page("pages/backtest_create/create.py", "Create", "⚔️"), + # Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), + # Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), Section("Community Pages", "👨‍👩‍👧‍👦"), Page("pages/strategy_performance/app.py", "Strategy Performance", "🚀"), Page("pages/db_inspector/app.py", "DB Inspector", "🔍"), From c921c61d8dbda330d773a3da29e14e14aa2d2015 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:48:56 -0300 Subject: [PATCH 016/139] (feat) update dman_v2 --- pages/dman_maker_v2/app.py | 79 +++----------------------------------- 1 file changed, 6 insertions(+), 73 deletions(-) diff --git a/pages/dman_maker_v2/app.py b/pages/dman_maker_v2/app.py index 78bf932..d3203ad 100644 --- a/pages/dman_maker_v2/app.py +++ b/pages/dman_maker_v2/app.py @@ -1,4 +1,3 @@ -from math import exp import streamlit as st from plotly.subplots import make_subplots import plotly.graph_objects as go @@ -8,73 +7,7 @@ import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page -from hummingbot.smart_components.utils.distributions import Distributions - - -def normalize(values): - total = sum(values) - return [Decimal(val / total) for val in values] - - -def distribution_inputs(column, dist_type_name, levels=3): - if dist_type_name == "Spread": - dist_type = column.selectbox( - f"Type of {dist_type_name} Distribution", - ("Manual", "GeoCustom", "Geometric", "Fibonacci", "Logarithmic", "Arithmetic"), - 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.1, - 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") - 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 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): - if dist_type == "Manual": - return manual_values - elif dist_type == "Linear": - return Distributions.linear(n_levels, start, start + ts_ap) - 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) - +from utils.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") @@ -142,9 +75,9 @@ sell_amount_distributions = normalize( sell_amount_step, sell_amount_ratio, sell_manual_amounts)) all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions) -buy_order_amounts_quote = [Decimal(amount * total_amount_quote) for amount in +buy_order_amounts_quote = [amount * total_amount_quote for amount in all_orders_amount_normalized[:buy_order_levels]] -sell_order_amounts_quote = [Decimal(amount * total_amount_quote) for amount in +sell_order_amounts_quote = [amount * total_amount_quote for amount in all_orders_amount_normalized[buy_order_levels:]] # Initialize your figure with a dark theme @@ -417,7 +350,7 @@ st.write("---") st.plotly_chart(fig) c1, c2, c3 = st.columns([2, 2, 1]) with c1: - config_base = st.text_input("Config Base", value=f"{connector}-{trading_pair.split('-')[0]}") + config_base = st.text_input("Config Base", value=f"dman_maker_v2-{connector}-{trading_pair.split('-')[0]}") with c2: config_tag = st.text_input("Config Tag", value="1.1") @@ -431,8 +364,8 @@ config = { "connector_name": connector, "trading_pair": trading_pair, "total_amount_quote": total_amount_quote, - "buy_spreads": [spread / 100 for spread in buy_spread_distributions], - "sell_spreads": [spread / 100 for spread in sell_spread_distributions], + "buy_spreads": [Decimal(spread / 100) for spread in buy_spread_distributions], + "sell_spreads": [Decimal(spread / 100) for spread in sell_spread_distributions], "buy_amounts_pct": buy_order_amounts_quote, "sell_amounts_pct": sell_order_amounts_quote, "executor_refresh_time": executor_refresh_time * 60, From 89c8872706e80c51583770f3dc538b8fe58dfde1 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:52:46 -0300 Subject: [PATCH 017/139] (feat) refactor inputs --- pages/dman_maker_v2/app.py | 2 +- pages/position_builder/app.py | 61 +---------------------------- ui_components/st_inputs.py | 72 +++++++++++++++++++++++++++++++++++ utils/st_utils.py | 1 + 4 files changed, 75 insertions(+), 61 deletions(-) create mode 100644 ui_components/st_inputs.py diff --git a/pages/dman_maker_v2/app.py b/pages/dman_maker_v2/app.py index d3203ad..013c829 100644 --- a/pages/dman_maker_v2/app.py +++ b/pages/dman_maker_v2/app.py @@ -7,7 +7,7 @@ import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page -from utils.st_inputs import normalize, distribution_inputs, get_distribution +from ui_components.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") diff --git a/pages/position_builder/app.py b/pages/position_builder/app.py index 7667caa..475c6f8 100644 --- a/pages/position_builder/app.py +++ b/pages/position_builder/app.py @@ -1,4 +1,3 @@ -from math import exp import streamlit as st from plotly.subplots import make_subplots import plotly.graph_objects as go @@ -6,7 +5,7 @@ from decimal import Decimal import yaml from utils.st_utils import initialize_st_page -from hummingbot.smart_components.utils.distributions import Distributions +from ui_components.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="Position Generator", icon="🔭", initial_sidebar_state="collapsed") @@ -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,62 +36,10 @@ with col_levels: n_levels = st.number_input("Number of Levels", min_value=1, value=5) -def distribution_inputs(column, dist_type_name): - if dist_type_name == "Spread": - dist_type = column.selectbox( - f"Type of {dist_type_name} Distribution", - ("GeoCustom", "Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"), - key=f"{dist_type_name.lower()}_dist_type", - # Set the default value - ) - else: - dist_type = column.selectbox( - f"Type of {dist_type_name} Distribution", - ("Geometric", "Fibonacci", "Manual", "Logarithmic", "Arithmetic"), - key=f"{dist_type_name.lower()}_dist_type", - # Set the default value - ) - base, scaling_factor, step, ratio, manual_values = None, None, None, None, None - - if dist_type != "Manual": - start = column.number_input(f"{dist_type_name} Start Value", value=1.0, key=f"{dist_type_name.lower()}_start") - if dist_type == "Logarithmic": - base = column.number_input(f"{dist_type_name} Log Base", value=exp(1), key=f"{dist_type_name.lower()}_base") - scaling_factor = column.number_input(f"{dist_type_name} Scaling Factor", value=2.0, key=f"{dist_type_name.lower()}_scaling") - elif dist_type == "Arithmetic": - step = column.number_input(f"{dist_type_name} Step", value=0.1, key=f"{dist_type_name.lower()}_step") - elif dist_type == "Geometric": - ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio") - elif dist_type == "GeoCustom": - ratio = column.number_input(f"{dist_type_name} Ratio", value=2.0, key=f"{dist_type_name.lower()}_ratio") - else: - manual_values = [column.number_input(f"{dist_type_name} for level {i+1}", value=1.0, key=f"{dist_type_name.lower()}_{i}") for i in range(n_levels)] - start = None # As start is not relevant for Manual type - - return dist_type, start, base, scaling_factor, step, ratio, manual_values - - # Spread and Amount Distributions spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs(col_spread_dist, "Spread") amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs(col_amount_dist, "Amount") - -def get_distribution(dist_type, n_levels, start, base=None, scaling_factor=None, step=None, ratio=None, manual_values=None): - if dist_type == "Manual": - return manual_values - elif dist_type == "Linear": - return Distributions.linear(n_levels, start, start + tp) - elif dist_type == "Fibonacci": - return Distributions.fibonacci(n_levels, start) - elif dist_type == "Logarithmic": - return Distributions.logarithmic(n_levels, base, scaling_factor, start) - elif dist_type == "Arithmetic": - return Distributions.arithmetic(n_levels, start, step) - elif dist_type == "Geometric": - return Distributions.geometric(n_levels, start, ratio) - elif dist_type == "GeoCustom": - return [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio) - spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads) amount_distribution = normalize(get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts)) order_amounts = [Decimal(amount_dist * total_amount_quote) for amount_dist in amount_distribution] diff --git a/ui_components/st_inputs.py b/ui_components/st_inputs.py new file mode 100644 index 0000000..5dda44f --- /dev/null +++ b/ui_components/st_inputs.py @@ -0,0 +1,72 @@ +from _decimal import Decimal +from math import exp + +from hummingbot.smart_components.utils.distributions import Distributions + + +def normalize(values): + total = sum(values) + return [Decimal(val / total) for val in values] + + +def distribution_inputs(column, dist_type_name, levels=3): + 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.1, + 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: + 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 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): + if dist_type == "Manual": + return manual_values + elif dist_type == "Linear": + return Distributions.linear(n_levels, start, step) + 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) diff --git a/utils/st_utils.py b/utils/st_utils.py index a89dcf5..40c7c14 100644 --- a/utils/st_utils.py +++ b/utils/st_utils.py @@ -1,4 +1,5 @@ import os.path + import pandas as pd from pathlib import Path import inspect From bb71831d5ade24ce392fbd8eeae1182e2c81c404 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:52:53 -0300 Subject: [PATCH 018/139] (feat) add xemm config generator --- pages/xemm_controller/app.py | 145 +++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 pages/xemm_controller/app.py diff --git a/pages/xemm_controller/app.py b/pages/xemm_controller/app.py new file mode 100644 index 0000000..b39cd61 --- /dev/null +++ b/pages/xemm_controller/app.py @@ -0,0 +1,145 @@ +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 CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page +from hummingbot.smart_components.utils.distributions import Distributions + + +# Initialize the Streamlit page +initialize_st_page(title="XEMM Multiple Levels", icon="⚡️", initial_sidebar_state="collapsed") + +# 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!") \ No newline at end of file From 870b50779ba19fb1d92fbd6e6fae03a64116057d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:53:07 -0300 Subject: [PATCH 019/139] (feat) add pmm simple config generator --- pages/pmm_simple/app.py | 239 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 pages/pmm_simple/app.py diff --git a/pages/pmm_simple/app.py b/pages/pmm_simple/app.py new file mode 100644 index 0000000..218b63b --- /dev/null +++ b/pages/pmm_simple/app.py @@ -0,0 +1,239 @@ +import pandas as pd +import streamlit as st +from hummingbot.connector.connector_base import OrderType +from plotly.subplots import make_subplots +import plotly.graph_objects as go +from decimal import Decimal +import yaml + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page +from ui_components.st_inputs import normalize, distribution_inputs, get_distribution + +# Initialize the Streamlit page +initialize_st_page(title="PMM Simple", icon="👨‍🏫", initial_sidebar_state="collapsed") + +# Page content +st.text("This tool will let you create a config for PMM Simple and upload it to the BackendAPI.") +st.write("---") + +c1, c2, c3, c4, c5, c6, c7 = st.columns(7) + +with c1: + connector = st.text_input("Connector", value="binance_perpetual") +with c2: + trading_pair = st.text_input("Trading Pair", value="WLD-USDT") +with c3: + total_amount_quote = st.number_input("Total amount of quote", value=1000) +with c4: + leverage = st.number_input("Leverage", value=20) + position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) +with c5: + executor_refresh_time = st.number_input("Refresh Time (minutes)", value=3) + cooldown_time = st.number_input("Cooldown Time (minutes)", value=3) +with c6: + 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 c7: + 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) + + + + +# Executors configuration +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=2) +with col_sell: + st.header("Sell Order Settings") + sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) + +col_buy_spreads, col_buy_amounts, col_sell_spreads, col_sell_amounts = st.columns(4) + +# Inputs for buy orders +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) +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) +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) +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) + +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 = normalize( + 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 = normalize( + 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)) + +all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions) +buy_order_amounts_quote = [amount * total_amount_quote for amount in + all_orders_amount_normalized[:buy_order_levels]] +sell_order_amounts_quote = [amount * total_amount_quote for amount in + all_orders_amount_normalized[buy_order_levels:]] + +# Initialize your figure with a dark theme +fig = make_subplots(specs=[[{"secondary_y": True}]]) +fig.update_layout( + template="plotly_dark", + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + title="Market Maker Order Distribution", + xaxis_title="Spread (%)", + yaxis_title="Amount (Quote)", + legend_title="Order Type", + font=dict(color='white', size=12) # Consistent font color and size +) + +# Define colors for buy and sell orders +colors = { + 'buy': '#32CD32', # Green for buy orders + 'sell': '#FF6347' # Tomato red for sell orders +} + +# Add traces for buy and sell orders +# Buy orders on the negative side of x-axis +fig.add_trace(go.Bar( + x=[-dist for dist in buy_spread_distributions], + y=buy_order_amounts_quote, + name='Buy Orders', + marker_color=colors['buy'], + width=[0.2] * buy_order_levels # Adjust the width of the bars as needed +), secondary_y=False) + +# Sell orders on the positive side of x-axis +fig.add_trace(go.Bar( + x=sell_spread_distributions, + y=sell_order_amounts_quote, + name='Sell Orders', + marker_color=colors['sell'], + width=[0.2] * buy_order_levels # Adjust the width of the bars as needed + +), secondary_y=False) + +# Annotations can be added for each bar to display the value on top +for i, value in enumerate(buy_order_amounts_quote): + fig.add_annotation( + x=-buy_spread_distributions[i], + y=value + 10, # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['buy'], size=10) + ) + +for i, value in enumerate(sell_order_amounts_quote): + fig.add_annotation( + x=sell_spread_distributions[i], + y=value + 10, # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['sell'], size=10) + ) + +# Optional: Add horizontal line or extra annotations if needed +# e.g., for average, threshold, or specific markers + +# Update the layout to make it responsive and visually appealing +fig.update_layout( + height=600, + width=800, + margin=dict(l=20, r=20, t=50, b=20) +) + +# Display the figure in Streamlit +st.plotly_chart(fig, use_container_width=True) + +# Create DataFrame for Buy Orders +buy_orders_df = pd.DataFrame({ + "Level": range(1, buy_order_levels + 1), + "Spread (%)": [-dist for dist in buy_spread_distributions], + "Amount (Quote)": buy_order_amounts_quote, + "Take Profit ($)": [float(amount) * (tp / 100) for amount in buy_order_amounts_quote], + "Stop Loss ($)": [float(amount) * (sl / 100) for amount in buy_order_amounts_quote], + "Min Trailing Stop ($)": [float(amount) * ((ts_ap - ts_delta) / 100) for amount in buy_order_amounts_quote], + "TP/SL Ratio": [tp / sl] * buy_order_levels, + "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * buy_order_levels, +}) + +# Create DataFrame for Sell Orders +sell_orders_df = pd.DataFrame({ + "Level": range(1, sell_order_levels + 1), + "Spread (%)": [dist for dist in sell_spread_distributions], + "Amount (Quote)": sell_order_amounts_quote, + "Take Profit ($)": [float(amount) * (tp / 100) for amount in sell_order_amounts_quote], + "Stop Loss ($)": [float(amount) * (sl / 100) for amount in sell_order_amounts_quote], + "Min Trailing Stop ($)": [float(amount) * ((ts_ap - ts_delta) / 100) for amount in sell_order_amounts_quote], + "TP/SL Ratio": [tp / sl] * buy_order_levels, + "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * buy_order_levels, +}) + +# Display the DataFrames in Streamlit +st.write("Buy Orders:") +st.dataframe(buy_orders_df) +st.write("Sell Orders:") +st.dataframe(sell_orders_df) + +c1, c2, c3 = st.columns([2, 2, 1]) +with c1: + config_base = st.text_input("Config Base", value=f"pmm_simple-{connector}-{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": "pmm_simple", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "buy_spreads": [Decimal(spread / 100) for spread in buy_spread_distributions], + "sell_spreads": [Decimal(spread / 100) for spread in sell_spread_distributions], + "buy_amounts_pct": buy_order_amounts_quote, + "sell_amounts_pct": sell_order_amounts_quote, + "executor_refresh_time": executor_refresh_time * 60, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "stop_loss": sl / 100, + "take_profit": tp / 100, + "time_limit": time_limit * 60, + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap / 100, + "trailing_delta": ts_delta / 100}, + "top_executor_refresh_time": None, + } +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!") \ No newline at end of file From 67722ffeac15a44c469c026125e30a73f0085290 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:53:25 -0300 Subject: [PATCH 020/139] (feat) make launch stategy compatible wiht generic controllers --- ui_components/launch_strategy_v2.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ui_components/launch_strategy_v2.py b/ui_components/launch_strategy_v2.py index b261041..12ca115 100644 --- a/ui_components/launch_strategy_v2.py +++ b/ui_components/launch_strategy_v2.py @@ -120,17 +120,24 @@ class LaunchStrategyV2(Dashboard.Item): 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": config["connector_name"], - "trading_pair": config["trading_pair"], - "total_amount_quote": config["total_amount_quote"], - "max_loss_quote": config["total_amount_quote"] * config["stop_loss"], - "stop_loss": config["stop_loss"], - "take_profit": config["take_profit"], - "trailing_stop": str(config["trailing_stop"]["activation_price"]) + " / " + - str(config["trailing_stop"]["trailing_delta"]), - "time_limit": config["time_limit"]}) + "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") From 27d480b7e436aaa11968b3bc3497a464c3e77c2a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:53:36 -0300 Subject: [PATCH 021/139] (feat) add extra pages --- main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index b65f27f..0438e29 100644 --- a/main.py +++ b/main.py @@ -14,8 +14,11 @@ def main_page(): Page("pages/bot_orchestration/app.py", "Instances", "🦅"), Page("pages/launch_bot/app.py", "Deploy", "🚀"), Section("Config Generator", "🎛️"), - Page("pages/position_builder/app.py", "Position Builder", "🔭"), + Page("pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), + Page("pages/bollinger_v1/app.py", "Bollinger V1", "📈"), + Page("pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), + Page("pages/position_builder/app.py", "Position Builder", "🔭"), # Page("pages/master_conf/app.py", "Credentials", "🗝️"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), # Section("Backtest Manager", "⚙️"), From 9bba5b09459823a48c78a46b6e3d98288b5db5ad Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:53:46 -0300 Subject: [PATCH 022/139] (feat) add bollinger controller and candles support --- pages/bollinger_v1/app.py | 148 ++++++++++++++++++++++++++++++++++++ utils/backend_api_client.py | 12 +++ 2 files changed, 160 insertions(+) create mode 100644 pages/bollinger_v1/app.py diff --git a/pages/bollinger_v1/app.py b/pages/bollinger_v1/app.py new file mode 100644 index 0000000..1870e3e --- /dev/null +++ b/pages/bollinger_v1/app.py @@ -0,0 +1,148 @@ +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import pandas_ta as ta + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page + +# Initialize the Streamlit page +initialize_st_page(title="Bollinger 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_candles(connector_name, trading_pair, interval, max_records) + +@st.cache_data +def add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): + # Add Bollinger Bands + df.ta.bbands(length=bb_length, std=bb_std, append=True) + + # Generate signal + long_condition = df[f"BBP_{bb_length}_{bb_std}"] < bb_long_threshold + short_condition = df[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold + + # 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 Bollinger V1 and visualize the strategy.") +st.write("---") + +# Inputs for Bollinger Band configuration +st.write("## Candles Configuration") +c1, c2, c3, c4 = st.columns(4) +with c1: + connector_name = st.text_input("Connector Name", value="binance") +with c2: + trading_pair = st.text_input("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("## Bollinger Bands Configuration") +c1, c2, c3, c4 = st.columns(4) +with c1: + bb_length = st.number_input("Bollinger Bands Length", min_value=20, max_value=200, value=100) +with c2: + bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) +with c3: + bb_long_threshold = st.number_input("Long Threshold", value=0.0) +with c4: + bb_short_threshold = st.number_input("Short Threshold", value=1.0) + + +# Load candle data +candle_data = get_candles(connector_name=connector_name, trading_pair=trading_pair, interval=interval, max_records=max_records) +df = pd.DataFrame(candle_data) +df.index = pd.to_datetime(df['timestamp'], unit='ms') +candles_processed = add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold) +# Create a dynamic variable naming convention based on bb_length and bb_std +bb_lower = f'BBL_{bb_length}_{bb_std}' +bb_middle = f'BBM_{bb_length}_{bb_std}' +bb_upper = f'BBU_{bb_length}_{bb_std}' + + +# 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 Bollinger Bands', '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[bb_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[bb_middle], 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[bb_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="Bollinger Bands 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) diff --git a/utils/backend_api_client.py b/utils/backend_api_client.py index 8cb7daf..f06e948 100644 --- a/utils/backend_api_client.py +++ b/utils/backend_api_client.py @@ -159,3 +159,15 @@ class BackendAPIClient: } response = requests.post(url, json=config) return response.json() + + def get_candles(self, connector: str, trading_pair: str, interval: str, max_records: int): + """Get candles data.""" + url = f"{self.base_url}/candles" + payload = { + "connector": connector, + "trading_pair": trading_pair, + "interval": interval, + "max_records": max_records + } + response = requests.post(url, json=payload) + return response.json() From 5e752fbca4a365f8ae1a864ecc2f0ea6c8df52c8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 20:53:52 -0300 Subject: [PATCH 023/139] (feat) add readmes --- pages/bollinger_v1/README.md | 19 +++++++++++++++++++ pages/pmm_simple/README.md | 19 +++++++++++++++++++ pages/xemm_controller/README.md | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 pages/bollinger_v1/README.md create mode 100644 pages/pmm_simple/README.md create mode 100644 pages/xemm_controller/README.md diff --git a/pages/bollinger_v1/README.md b/pages/bollinger_v1/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/bollinger_v1/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/pmm_simple/README.md b/pages/pmm_simple/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/pmm_simple/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/xemm_controller/README.md b/pages/xemm_controller/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/xemm_controller/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file From 0ef7a7a0ebb1a086d2f7ce21c47b0e2bb6eec088 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 21:35:11 -0300 Subject: [PATCH 024/139] (feat) add position config --- pages/bollinger_v1/app.py | 86 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/pages/bollinger_v1/app.py b/pages/bollinger_v1/app.py index 1870e3e..6a13dc4 100644 --- a/pages/bollinger_v1/app.py +++ b/pages/bollinger_v1/app.py @@ -2,6 +2,8 @@ import streamlit as st import pandas as pd import plotly.graph_objects as go import pandas_ta as ta +import yaml +from hummingbot.connector.connector_base import OrderType from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient @@ -39,13 +41,35 @@ st.write("---") st.write("## Candles Configuration") c1, c2, c3, c4 = st.columns(4) with c1: - connector_name = st.text_input("Connector Name", value="binance") + 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) +with c4: + bb_short_threshold = st.number_input("Short Threshold", value=1.0) + +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("## Bollinger Bands Configuration") c1, c2, c3, c4 = st.columns(4) @@ -55,12 +79,11 @@ with c2: bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) with c3: bb_long_threshold = st.number_input("Long Threshold", value=0.0) -with c4: - bb_short_threshold = st.number_input("Short Threshold", value=1.0) + # Load candle data -candle_data = get_candles(connector_name=connector_name, trading_pair=trading_pair, interval=interval, max_records=max_records) +candle_data = get_candles(connector_name=candles_connector, trading_pair=candles_trading_pair, interval=interval, max_records=max_records) df = pd.DataFrame(candle_data) df.index = pd.to_datetime(df['timestamp'], unit='ms') candles_processed = add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold) @@ -146,3 +169,58 @@ fig.update_yaxes( # 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, + "bb_length": bb_length, + "bb_std": bb_std, + "bb_long_threshold": bb_long_threshold, + "bb_short_threshold": bb_short_threshold +} + +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!") \ No newline at end of file From 4a8b7877187b9d95149e7c0928eb802dc734d741 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 17 Apr 2024 21:36:47 -0300 Subject: [PATCH 025/139] (feat) add macd bb --- main.py | 1 + pages/macd_bb_v1/README.md | 19 +++++ pages/macd_bb_v1/app.py | 147 +++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 pages/macd_bb_v1/README.md create mode 100644 pages/macd_bb_v1/app.py diff --git a/main.py b/main.py index 0438e29..c8e1109 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ def main_page(): Page("pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), Page("pages/bollinger_v1/app.py", "Bollinger V1", "📈"), + Page("pages/macd_bb_v1/app.py", "MACD_BB V1", "📉"), Page("pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), Page("pages/position_builder/app.py", "Position Builder", "🔭"), # Page("pages/master_conf/app.py", "Credentials", "🗝️"), diff --git a/pages/macd_bb_v1/README.md b/pages/macd_bb_v1/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/macd_bb_v1/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/macd_bb_v1/app.py b/pages/macd_bb_v1/app.py new file mode 100644 index 0000000..97c9bc0 --- /dev/null +++ b/pages/macd_bb_v1/app.py @@ -0,0 +1,147 @@ +import streamlit as st +import pandas as pd +import pandas_ta as ta +import plotly.graph_objects as go +import yaml +from hummingbot.connector.connector_base import OrderType +from plotly.subplots import make_subplots + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page + +# Initialize the Streamlit page +initialize_st_page(title="MACD_BB V1", 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_candles(connector_name, trading_pair, interval, max_records) + +@st.cache_data +def add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal): + # Bollinger Bands + df.ta.bbands(length=bb_length, std=bb_std, append=True) + # MACD + df.ta.macd(fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True) + + # Decision Logic + bbp = df[f"BBP_{bb_length}_{bb_std}"] + macdh = df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] + macd = df[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"] + + long_condition = (bbp < bb_long_threshold) & (macdh > 0) & (macd < 0) + short_condition = (bbp > bb_short_threshold) & (macdh < 0) & (macd > 0) + + df["signal"] = 0 + df.loc[long_condition, "signal"] = 1 + df.loc[short_condition, "signal"] = -1 + + return df + +st.write("## Configuration") +c1, c2, c3, c4 = st.columns(4) +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) +with c4: + bb_length = st.number_input("BB Length", min_value=2, value=100) + bb_std = st.number_input("BB Std Dev", min_value=0.1, value=2.0, step=0.1) + bb_long_threshold = st.number_input("BB Long Threshold", value=0.0) + bb_short_threshold = st.number_input("BB Short Threshold", value=1.0) + +# 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='ms') +df = add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal) + +# 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 with Bollinger Bands', '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) + +fig.add_trace(go.Scatter(x=df.index, y=df[f"BBL_{bb_length}_{bb_std}"], line=dict(color='blue'), name='Lower Band'), row=1, col=1) +fig.add_trace(go.Scatter(x=df.index, y=df[f"BBM_{bb_length}_{bb_std}"], line=dict(color='red'), name='Middle Band'), row=1, col=1) +fig.add_trace(go.Scatter(x=df.index, y=df[f"BBU_{bb_length}_{bb_std}"], line=dict(color='green'), name='Upper Band'), 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=20, 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=20, 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, + "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, +} \ No newline at end of file From 4f5f7abb14b601786fbe270e4417b0e86d0e349d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 19 Apr 2024 18:59:44 -0300 Subject: [PATCH 026/139] (feat) improve bollinger page --- pages/bollinger_v1/app.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pages/bollinger_v1/app.py b/pages/bollinger_v1/app.py index 6a13dc4..c31d4ad 100644 --- a/pages/bollinger_v1/app.py +++ b/pages/bollinger_v1/app.py @@ -50,8 +50,7 @@ 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) -with c4: - bb_short_threshold = st.number_input("Short Threshold", value=1.0) + st.write("## Positions Configuration") c1, c2, c3, c4 = st.columns(4) @@ -79,8 +78,8 @@ with c2: bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) with c3: bb_long_threshold = st.number_input("Long Threshold", value=0.0) - - +with c4: + bb_short_threshold = st.number_input("Short Threshold", value=1.0) # Load candle data candle_data = get_candles(connector_name=candles_connector, trading_pair=candles_trading_pair, interval=interval, max_records=max_records) From 91eb255b7cbd0c936ae5b72de5973f9cbdc77ef8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 19 Apr 2024 18:59:51 -0300 Subject: [PATCH 027/139] (feat) improve macd pmm simple pages --- pages/macd_bb_v1/app.py | 19 ++++++++++++++++++- pages/pmm_simple/app.py | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pages/macd_bb_v1/app.py b/pages/macd_bb_v1/app.py index 97c9bc0..56c4ba9 100644 --- a/pages/macd_bb_v1/app.py +++ b/pages/macd_bb_v1/app.py @@ -144,4 +144,21 @@ config = { "macd_fast": macd_fast, "macd_slow": macd_slow, "macd_signal": macd_signal, -} \ No newline at end of file +} + +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!") diff --git a/pages/pmm_simple/app.py b/pages/pmm_simple/app.py index 218b63b..dcd7a8b 100644 --- a/pages/pmm_simple/app.py +++ b/pages/pmm_simple/app.py @@ -221,6 +221,7 @@ config = { "trailing_delta": ts_delta / 100}, "top_executor_refresh_time": None, } + yaml_config = yaml.dump(config, default_flow_style=False) with c3: From 1bc32648954830f55f08c5a4ec82152f3e8f42bf Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 30 Apr 2024 19:58:41 -0300 Subject: [PATCH 028/139] (feat) add readmes --- .../README.md | 0 pages/dman_v5/README.md | 19 +++++++++++++++++++ pages/trend_follower_v1/README.md | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+) rename pages/{backtest_get_data => data_download_candles}/README.md (100%) create mode 100644 pages/dman_v5/README.md create mode 100644 pages/trend_follower_v1/README.md diff --git a/pages/backtest_get_data/README.md b/pages/data_download_candles/README.md similarity index 100% rename from pages/backtest_get_data/README.md rename to pages/data_download_candles/README.md diff --git a/pages/dman_v5/README.md b/pages/dman_v5/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/dman_v5/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/pages/trend_follower_v1/README.md b/pages/trend_follower_v1/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/pages/trend_follower_v1/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file From 4e82a6eed5dd41930f997d657a3e8a176e6ef629 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 30 Apr 2024 19:58:47 -0300 Subject: [PATCH 029/139] (feat) upgrade backend api client --- utils/backend_api_client.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/backend_api_client.py b/utils/backend_api_client.py index f06e948..f13e73e 100644 --- a/utils/backend_api_client.py +++ b/utils/backend_api_client.py @@ -160,9 +160,9 @@ class BackendAPIClient: response = requests.post(url, json=config) return response.json() - def get_candles(self, connector: str, trading_pair: str, interval: str, max_records: int): + def get_real_time_candles(self, connector: str, trading_pair: str, interval: str, max_records: int): """Get candles data.""" - url = f"{self.base_url}/candles" + url = f"{self.base_url}/real-time-candles" payload = { "connector": connector, "trading_pair": trading_pair, @@ -171,3 +171,16 @@ class BackendAPIClient: } 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() From 9da66ae830315b49e20f98a88f14b00ed99a8778 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 30 Apr 2024 19:58:52 -0300 Subject: [PATCH 030/139] (feat) add pages to main --- main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index c8e1109..5d61dbf 100644 --- a/main.py +++ b/main.py @@ -17,13 +17,17 @@ def main_page(): Page("pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), Page("pages/bollinger_v1/app.py", "Bollinger V1", "📈"), - Page("pages/macd_bb_v1/app.py", "MACD_BB V1", "📉"), + Page("pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), + Page("pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), + Page("pages/macd_bb_v1/app.py", "MACD_BB V1", "📊"), + Page("pages/dman_v5/app.py", "D-Man V5", "📊"), Page("pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), Page("pages/position_builder/app.py", "Position Builder", "🔭"), + Page("pages/dynamic_position_builder/app.py", "Dynamic Position Builder", "🔭"), # Page("pages/master_conf/app.py", "Credentials", "🗝️"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), - # Section("Backtest Manager", "⚙️"), - # Page("pages/backtest_get_data/app.py", "Get Data", "💾"), + Section("Data", "💾"), + Page("pages/data_download_candles/app.py", "Download Candles", "💹"), # Page("pages/backtest_create/create.py", "Create", "⚔️"), # Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), # Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), From c371a58ae92152495ccb7dbbaba21237b3da632f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 30 Apr 2024 19:59:06 -0300 Subject: [PATCH 031/139] (feat) adapt controllers config page --- pages/bollinger_v1/app.py | 2 +- pages/macd_bb_v1/app.py | 2 +- pages/trend_follower_v1/app.py | 219 +++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 pages/trend_follower_v1/app.py diff --git a/pages/bollinger_v1/app.py b/pages/bollinger_v1/app.py index c31d4ad..900f51b 100644 --- a/pages/bollinger_v1/app.py +++ b/pages/bollinger_v1/app.py @@ -16,7 +16,7 @@ initialize_st_page(title="Bollinger V1", icon="📈", initial_sidebar_state="exp @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_candles(connector_name, trading_pair, interval, max_records) + return backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records) @st.cache_data def add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): diff --git a/pages/macd_bb_v1/app.py b/pages/macd_bb_v1/app.py index 56c4ba9..f102748 100644 --- a/pages/macd_bb_v1/app.py +++ b/pages/macd_bb_v1/app.py @@ -16,7 +16,7 @@ initialize_st_page(title="MACD_BB V1", icon="📊", initial_sidebar_state="expan @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_candles(connector_name, trading_pair, interval, max_records) + return backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records) @st.cache_data def add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal): diff --git a/pages/trend_follower_v1/app.py b/pages/trend_follower_v1/app.py new file mode 100644 index 0000000..96bc340 --- /dev/null +++ b/pages/trend_follower_v1/app.py @@ -0,0 +1,219 @@ +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import pandas_ta as ta +import yaml +from hummingbot.connector.connector_base import OrderType + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page + +# Initialize the Streamlit page +initialize_st_page(title="Trend Follower 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, sma_fast, sma_slow, entry_threshold): + # Add indicators + df.ta.sma(close='close', length=sma_fast, append=True) + df.ta.sma(close='close', length=sma_slow, append=True) + df = df.dropna() + cross_up = ta.cross(df[f"SMA_{sma_fast}"], df[f"SMA_{sma_slow}"], above=True) + cross_down = ta.cross(df[f"SMA_{sma_fast}"], df[f"SMA_{sma_slow}"], above=False) + sma_fast = df[f"SMA_{sma_fast}"] + sma_slow = df[f"SMA_{sma_slow}"] + + # Generate signal + long_condition = (cross_up == 1) & (df["close"] * (1 - entry_threshold) < sma_fast) + short_condition = (cross_down == 1) & (df["close"] * (1 + entry_threshold) > sma_fast) + + 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 Trend Follower V1 and visualize the strategy.") +st.write("---") + +# Inputs for Bollinger Band 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("## Bollinger Bands Configuration") +c1, c2, c3 = st.columns(3) +with c1: + sma_fast = st.number_input("Fast SMA Length", min_value=10, max_value=100, value=20) +with c2: + sma_slow = st.number_input("Slow SMA Length", min_value=20, max_value=500, value=200) +with c3: + entry_threshold = st.number_input("Entry Threshold (%)", min_value=0.0, max_value=100.0, value=1.0, step=0.1) / 100 + +# 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='ms') +candles_processed = add_indicators(df, sma_fast, sma_slow, entry_threshold) + + +# 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 Moving Averages', '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) + +# Moving Averages +fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed[f"SMA_{sma_fast}"], line=dict(color='blue'), name=f'Fast SMA ({sma_fast})'), row=1, col=1) +fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed[f"SMA_{sma_slow}"], line=dict(color='red'), name=f'Slow SMA ({sma_slow})'), 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=20, 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=20, 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="Bollinger Bands 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!") \ No newline at end of file From 37a608f4e7db015e33747715818ff846d2cd1fd7 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 30 Apr 2024 19:59:17 -0300 Subject: [PATCH 032/139] (feat) refactor get data page --- pages/backtest_get_data/app.py | 70 ------------------------------ pages/data_download_candles/app.py | 70 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 70 deletions(-) delete mode 100644 pages/backtest_get_data/app.py create mode 100644 pages/data_download_candles/app.py diff --git a/pages/backtest_get_data/app.py b/pages/backtest_get_data/app.py deleted file mode 100644 index 4239b60..0000000 --- a/pages/backtest_get_data/app.py +++ /dev/null @@ -1,70 +0,0 @@ -import time -from subprocess import CalledProcessError - -import streamlit as st - -import constants -from utils import os_utils -from docker_manager import DockerManager - -from utils.st_utils import initialize_st_page - - -initialize_st_page(title="Get Data", icon="💾", initial_sidebar_state="collapsed") - -# Start content here -docker_manager = DockerManager() - -c1, c2, c3 = st.columns([2, 2, 0.5]) -with c1: - exchange = st.selectbox("Exchange", ["binance_perpetual", "binance"], index=0) - trading_pairs = st.text_input("Trading Pairs (separated with commas)", value="BTC-USDT,ETH-USDT") -with c2: - intervals = st.multiselect("Intervals", options=["1s", "1m", "3m", "5m", "15m", "1h", "4h", "1d"], default=["1m", "3m", "1h"]) - days_to_download = st.number_input("Days to Download", value=30, min_value=1, max_value=365, step=1) -with c3: - get_data_button = st.button("Download Candles!") - clean_container_folder_button = st.button("Clean Candles Folder") - -if clean_container_folder_button: - st.warning("Cleaning Candles Data folder...", icon="⚠️") - st.write("---") - os_utils.remove_files_from_directory(constants.CANDLES_DATA_PATH) - st.write("### Container folder cleaned.") - st.write("---") - -if get_data_button: - candles_container_config = { - "EXCHANGE": exchange, - "TRADING_PAIRS": trading_pairs, - "INTERVALS": ",".join(intervals), - "DAYS_TO_DOWNLOAD": days_to_download, - } - time.sleep(0.5) - docker_manager.create_download_candles_container(candles_config=candles_container_config, - yml_path=constants.DOWNLOAD_CANDLES_CONFIG_YML) - st.info("Downloading candles with a Docker container in the background. " - "When this process is ready you will see the candles inside data/candles", icon="🕓") - -st.write("---") -st.write("## ⚙️Containers Management") -try: - active_containers = docker_manager.get_active_containers() - c1, c2 = st.columns([0.9, 0.1]) - with c1: - if "backtest_get_data" in active_containers: - st.success("Hummingbot Candles Downloader is running") - st.write("Exited Containers:") - st.warning(docker_manager.get_exited_containers()) - with c2: - if "backtest_get_data" in active_containers: - stop_containers_button = st.button("Stop Candles Downloader") - if stop_containers_button: - docker_manager.stop_container("backtest_get_data") - clean_exited_containers_button = st.button("Clean Containers") - if clean_exited_containers_button: - docker_manager.clean_exited_containers() - -except CalledProcessError as error: - st.write("### Docker is not running. Please start docker in your machine.") - diff --git a/pages/data_download_candles/app.py b/pages/data_download_candles/app.py new file mode 100644 index 0000000..89ad95b --- /dev/null +++ b/pages/data_download_candles/app.py @@ -0,0 +1,70 @@ +import streamlit as st +from datetime import datetime, time +import pandas as pd +import plotly.graph_objects as go + +from utils.backend_api_client import BackendAPIClient +from utils.st_utils import initialize_st_page + +# Initialize Streamlit page +initialize_st_page(title="Download Candles", icon="💾", initial_sidebar_state="collapsed") +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='ms') + + # 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', + ) From 24617500d15619445386ce1b443675e7a239883f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 9 May 2024 10:32:21 -0300 Subject: [PATCH 033/139] (feat) refactor file structure --- Makefile | 6 +- {ui_components => backend}/__init__.py | 0 environment_conda.yml | 2 +- .../README.md => frontend/__init__.py | 0 frontend/components/__init__.py | 0 .../components}/bot_performance_card.py | 2 +- .../components}/bot_performance_card_v2.py | 6 +- .../components}/bots_file_explorer.py | 2 +- .../components}/card.py | 2 +- .../components}/controllers_file_explorer.py | 4 +- .../components}/dashboard.py | 0 .../components}/datagrid.py | 0 .../directional_strategy_creation_card.py | 0 .../components}/editor.py | 0 .../components}/exited_bot_card.py | 7 +- .../components}/file_explorer_base.py | 0 .../components}/launch_bot_card.py | 0 .../components}/launch_broker_card.py | 0 .../components}/launch_master_bot_card.py | 0 .../components}/launch_strategy_v2.py | 0 .../components}/master_conf_file_explorer.py | 2 +- .../components}/media_player.py | 0 .../components}/optimization_creation_card.py | 0 .../components}/optimization_run_card.py | 0 .../optimizations_file_explorer.py | 2 +- .../components}/st_inputs.py | 0 frontend/pages/__init__.py | 0 .../pages}/backtest_analyze/README.md | 0 frontend/pages/backtest_analyze/__init__.py | 0 .../pages}/backtest_analyze/analyze.py | 0 .../pages}/backtest_create/README.md | 0 frontend/pages/backtest_create/__init__.py | 0 .../pages}/backtest_create/create.py | 8 +- .../pages}/backtest_optimize/README.md | 0 frontend/pages/backtest_optimize/__init__.py | 0 .../pages}/backtest_optimize/optimize.py | 10 +- .../pages}/bollinger_v1/README.md | 0 frontend/pages/bollinger_v1/__init__.py | 0 {pages => frontend/pages}/bollinger_v1/app.py | 0 .../pages}/bot_orchestration/README.md | 0 frontend/pages/bot_orchestration/__init__.py | 0 .../pages}/bot_orchestration/app.py | 4 +- .../pages}/data_download_candles/README.md | 0 .../pages/data_download_candles/__init__.py | 0 .../pages}/data_download_candles/app.py | 0 .../pages}/db_inspector/README.md | 0 frontend/pages/db_inspector/__init__.py | 0 {pages => frontend/pages}/db_inspector/app.py | 0 .../pages}/dman_maker_v2/README.md | 0 frontend/pages/dman_maker_v2/__init__.py | 0 .../pages}/dman_maker_v2/app.py | 2 +- {pages => frontend/pages}/dman_v5/README.md | 0 frontend/pages/dman_v5/__init__.py | 0 frontend/pages/dman_v5/app.py | 149 ++++++++ .../pages/dynamic_position_builder/README.md | 0 .../dynamic_position_builder/__init__.py | 0 .../pages/dynamic_position_builder/app.py | 220 +++++++++++ .../pages}/file_manager/README.md | 0 frontend/pages/file_manager/__init__.py | 0 {pages => frontend/pages}/file_manager/app.py | 6 +- .../pages/kalman_filter_v1}/README.md | 0 frontend/pages/kalman_filter_v1/__init__.py | 0 frontend/pages/kalman_filter_v1/app.py | 226 ++++++++++++ .../pages}/launch_bot/README.md | 0 frontend/pages/launch_bot/__init__.py | 0 {pages => frontend/pages}/launch_bot/app.py | 4 +- .../pages/macd_bb_v1}/README.md | 0 frontend/pages/macd_bb_v1/__init__.py | 0 {pages => frontend/pages}/macd_bb_v1/app.py | 0 .../pages}/master_conf/README.md | 0 frontend/pages/master_conf/__init__.py | 0 {pages => frontend/pages}/master_conf/app.py | 12 +- .../pages/pmm_simple}/README.md | 0 frontend/pages/pmm_simple/__init__.py | 0 {pages => frontend/pages}/pmm_simple/app.py | 7 +- frontend/pages/position_builder/README.md | 0 frontend/pages/position_builder/__init__.py | 0 .../pages}/position_builder/app.py | 6 +- .../pages}/reference_data/7_📋_Data.py | 0 frontend/pages/reference_data/__init__.py | 0 .../pages}/strategy_performance/README.md | 0 .../pages/strategy_performance/__init__.py | 0 .../pages}/strategy_performance/app.py | 0 .../pages}/token_spreads/README.md | 0 frontend/pages/token_spreads/__init__.py | 0 .../pages}/token_spreads/app.py | 0 .../pages/trend_follower_v1}/README.md | 0 frontend/pages/trend_follower_v1/__init__.py | 0 .../pages}/trend_follower_v1/app.py | 0 .../pages}/tvl_vs_mcap/README.md | 0 frontend/pages/tvl_vs_mcap/__init__.py | 0 {pages => frontend/pages}/tvl_vs_mcap/app.py | 0 frontend/pages/xemm_controller/README.md | 19 + frontend/pages/xemm_controller/__init__.py | 0 .../pages}/xemm_controller/app.py | 0 helpers/add_authemail.py | 27 -- helpers/edit_authadmin_password.py | 26 -- hummingbot_files/bots/.gitignore | 1 - .../conf/.password_verification | 1 - .../bots/data_downloader/conf/conf_client.yml | 199 ---------- .../conf/conf_fee_overrides.yml | 340 ----------------- .../data_downloader/conf/hummingbot_logs.yml | 83 ----- .../bots/data_downloader/conf_client.yml | 194 ---------- .../scripts/download_candles.py | 61 ---- .../compose_files/broker-compose.yml | 43 --- .../compose_files/data-downloader-compose.yml | 23 -- .../controller_configs/.gitignore | 1 - .../data_downloader_config.yml | 4 - .../conf/.password_verification | 1 - .../master_bot_conf/conf/conf_client.yml | 199 ---------- .../conf/conf_fee_overrides.yml | 340 ----------------- .../master_bot_conf/conf/hummingbot_logs.yml | 83 ----- .../scripts/download_candles.py | 61 ---- .../scripts/download_order_book_and_trades.py | 99 ----- .../master_bot_conf/scripts/fixed_grid.py | 341 ------------------ .../scripts/simple_arbitrage_example.py | 193 ---------- .../scripts/simple_pmm_example.py | 77 ---- .../scripts/simple_rsi_example.py | 259 ------------- .../scripts/simple_vwap_example.py | 184 ---------- .../scripts/simple_xemm_example.py | 204 ----------- .../scripts/strategy_v2_launcher.py | 110 ------ .../v2_directional-trading_macd_bb_v1.py | 91 ----- .../scripts/v2_market-making_dman_composed.py | 145 -------- ...v2_market-making_dman_v1_multiple_pairs.py | 133 ------- ...v2_market-making_dman_v2_multiple_pairs.py | 139 ------- ...v2_market-making_dman_v3_multiple_pairs.py | 141 -------- main.py | 40 +- .../01_strategy_design_dman_maker.ipynb | 48 +-- .../strategy/directional_strategy_base.py | 94 ----- utils/hummingbot_processes.py | 4 +- 130 files changed, 701 insertions(+), 3996 deletions(-) rename {ui_components => backend}/__init__.py (100%) rename pages/position_builder/README.md => frontend/__init__.py (100%) create mode 100644 frontend/components/__init__.py rename {ui_components => frontend/components}/bot_performance_card.py (99%) rename {ui_components => frontend/components}/bot_performance_card_v2.py (98%) rename {ui_components => frontend/components}/bots_file_explorer.py (95%) rename {ui_components => frontend/components}/card.py (95%) rename {ui_components => frontend/components}/controllers_file_explorer.py (90%) rename {ui_components => frontend/components}/dashboard.py (100%) rename {ui_components => frontend/components}/datagrid.py (100%) rename {ui_components => frontend/components}/directional_strategy_creation_card.py (100%) rename {ui_components => frontend/components}/editor.py (100%) rename {ui_components => frontend/components}/exited_bot_card.py (89%) rename {ui_components => frontend/components}/file_explorer_base.py (100%) rename {ui_components => frontend/components}/launch_bot_card.py (100%) rename {ui_components => frontend/components}/launch_broker_card.py (100%) rename {ui_components => frontend/components}/launch_master_bot_card.py (100%) rename {ui_components => frontend/components}/launch_strategy_v2.py (100%) rename {ui_components => frontend/components}/master_conf_file_explorer.py (97%) rename {ui_components => frontend/components}/media_player.py (100%) rename {ui_components => frontend/components}/optimization_creation_card.py (100%) rename {ui_components => frontend/components}/optimization_run_card.py (100%) rename {ui_components => frontend/components}/optimizations_file_explorer.py (92%) rename {ui_components => frontend/components}/st_inputs.py (100%) create mode 100644 frontend/pages/__init__.py rename {pages => frontend/pages}/backtest_analyze/README.md (100%) create mode 100644 frontend/pages/backtest_analyze/__init__.py rename {pages => frontend/pages}/backtest_analyze/analyze.py (100%) rename {pages => frontend/pages}/backtest_create/README.md (100%) create mode 100644 frontend/pages/backtest_create/__init__.py rename {pages => frontend/pages}/backtest_create/create.py (84%) rename {pages => frontend/pages}/backtest_optimize/README.md (100%) create mode 100644 frontend/pages/backtest_optimize/__init__.py rename {pages => frontend/pages}/backtest_optimize/optimize.py (85%) rename {pages => frontend/pages}/bollinger_v1/README.md (100%) create mode 100644 frontend/pages/bollinger_v1/__init__.py rename {pages => frontend/pages}/bollinger_v1/app.py (100%) rename {pages => frontend/pages}/bot_orchestration/README.md (100%) create mode 100644 frontend/pages/bot_orchestration/__init__.py rename {pages => frontend/pages}/bot_orchestration/app.py (93%) rename {pages => frontend/pages}/data_download_candles/README.md (100%) create mode 100644 frontend/pages/data_download_candles/__init__.py rename {pages => frontend/pages}/data_download_candles/app.py (100%) rename {pages => frontend/pages}/db_inspector/README.md (100%) create mode 100644 frontend/pages/db_inspector/__init__.py rename {pages => frontend/pages}/db_inspector/app.py (100%) rename {pages => frontend/pages}/dman_maker_v2/README.md (100%) create mode 100644 frontend/pages/dman_maker_v2/__init__.py rename {pages => frontend/pages}/dman_maker_v2/app.py (99%) rename {pages => frontend/pages}/dman_v5/README.md (100%) create mode 100644 frontend/pages/dman_v5/__init__.py create mode 100644 frontend/pages/dman_v5/app.py create mode 100644 frontend/pages/dynamic_position_builder/README.md create mode 100644 frontend/pages/dynamic_position_builder/__init__.py create mode 100644 frontend/pages/dynamic_position_builder/app.py rename {pages => frontend/pages}/file_manager/README.md (100%) create mode 100644 frontend/pages/file_manager/__init__.py rename {pages => frontend/pages}/file_manager/app.py (87%) rename {pages/macd_bb_v1 => frontend/pages/kalman_filter_v1}/README.md (100%) create mode 100644 frontend/pages/kalman_filter_v1/__init__.py create mode 100644 frontend/pages/kalman_filter_v1/app.py rename {pages => frontend/pages}/launch_bot/README.md (100%) create mode 100644 frontend/pages/launch_bot/__init__.py rename {pages => frontend/pages}/launch_bot/app.py (90%) rename {pages/pmm_simple => frontend/pages/macd_bb_v1}/README.md (100%) create mode 100644 frontend/pages/macd_bb_v1/__init__.py rename {pages => frontend/pages}/macd_bb_v1/app.py (100%) rename {pages => frontend/pages}/master_conf/README.md (100%) create mode 100644 frontend/pages/master_conf/__init__.py rename {pages => frontend/pages}/master_conf/app.py (80%) rename {pages/trend_follower_v1 => frontend/pages/pmm_simple}/README.md (100%) create mode 100644 frontend/pages/pmm_simple/__init__.py rename {pages => frontend/pages}/pmm_simple/app.py (97%) create mode 100644 frontend/pages/position_builder/README.md create mode 100644 frontend/pages/position_builder/__init__.py rename {pages => frontend/pages}/position_builder/app.py (98%) rename {pages => frontend/pages}/reference_data/7_📋_Data.py (100%) create mode 100644 frontend/pages/reference_data/__init__.py rename {pages => frontend/pages}/strategy_performance/README.md (100%) create mode 100644 frontend/pages/strategy_performance/__init__.py rename {pages => frontend/pages}/strategy_performance/app.py (100%) rename {pages => frontend/pages}/token_spreads/README.md (100%) create mode 100644 frontend/pages/token_spreads/__init__.py rename {pages => frontend/pages}/token_spreads/app.py (100%) rename {pages/xemm_controller => frontend/pages/trend_follower_v1}/README.md (100%) create mode 100644 frontend/pages/trend_follower_v1/__init__.py rename {pages => frontend/pages}/trend_follower_v1/app.py (100%) rename {pages => frontend/pages}/tvl_vs_mcap/README.md (100%) create mode 100644 frontend/pages/tvl_vs_mcap/__init__.py rename {pages => frontend/pages}/tvl_vs_mcap/app.py (100%) create mode 100644 frontend/pages/xemm_controller/README.md create mode 100644 frontend/pages/xemm_controller/__init__.py rename {pages => frontend/pages}/xemm_controller/app.py (100%) delete mode 100755 helpers/add_authemail.py delete mode 100644 helpers/edit_authadmin_password.py delete mode 100644 hummingbot_files/bots/.gitignore delete mode 100644 hummingbot_files/bots/data_downloader/conf/.password_verification delete mode 100644 hummingbot_files/bots/data_downloader/conf/conf_client.yml delete mode 100644 hummingbot_files/bots/data_downloader/conf/conf_fee_overrides.yml delete mode 100755 hummingbot_files/bots/data_downloader/conf/hummingbot_logs.yml delete mode 100644 hummingbot_files/bots/data_downloader/conf_client.yml delete mode 100644 hummingbot_files/bots/data_downloader/scripts/download_candles.py delete mode 100644 hummingbot_files/compose_files/broker-compose.yml delete mode 100644 hummingbot_files/compose_files/data-downloader-compose.yml delete mode 100644 hummingbot_files/controller_configs/.gitignore delete mode 100644 hummingbot_files/scripts_configs/data_downloader_config.yml delete mode 100644 hummingbot_files/templates/master_bot_conf/conf/.password_verification delete mode 100644 hummingbot_files/templates/master_bot_conf/conf/conf_client.yml delete mode 100644 hummingbot_files/templates/master_bot_conf/conf/conf_fee_overrides.yml delete mode 100755 hummingbot_files/templates/master_bot_conf/conf/hummingbot_logs.yml delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/download_candles.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/download_order_book_and_trades.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/fixed_grid.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/simple_arbitrage_example.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/simple_pmm_example.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/simple_rsi_example.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/simple_vwap_example.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/simple_xemm_example.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/strategy_v2_launcher.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/v2_directional-trading_macd_bb_v1.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_composed.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v1_multiple_pairs.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v2_multiple_pairs.py delete mode 100644 hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v3_multiple_pairs.py delete mode 100644 quants_lab/strategy/directional_strategy_base.py diff --git a/Makefile b/Makefile index 14be7c3..20683ea 100644 --- a/Makefile +++ b/Makefile @@ -6,14 +6,14 @@ 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 \ No newline at end of file diff --git a/ui_components/__init__.py b/backend/__init__.py similarity index 100% rename from ui_components/__init__.py rename to backend/__init__.py diff --git a/environment_conda.yml b/environment_conda.yml index d31bbbb..6cabb9a 100644 --- a/environment_conda.yml +++ b/environment_conda.yml @@ -8,7 +8,7 @@ dependencies: - pydantic=1.9.* - pip - pip: - - hummingbot + - /Users/dardonacci/Documents/work/hummingbot/dist/hummingbot-20240429-cp310-cp310-macosx_11_0_arm64.whl - streamlit - watchdog - python-dotenv diff --git a/pages/position_builder/README.md b/frontend/__init__.py similarity index 100% rename from pages/position_builder/README.md rename to frontend/__init__.py diff --git a/frontend/components/__init__.py b/frontend/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui_components/bot_performance_card.py b/frontend/components/bot_performance_card.py similarity index 99% rename from ui_components/bot_performance_card.py rename to frontend/components/bot_performance_card.py index 9941428..83ebb6d 100644 --- a/ui_components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -1,6 +1,6 @@ from docker_manager import DockerManager from streamlit_elements import mui, lazy -from ui_components.dashboard import Dashboard +from frontend.components.dashboard import Dashboard import streamlit as st import time from utils.os_utils import get_python_files_from_directory, get_yml_files_from_directory diff --git a/ui_components/bot_performance_card_v2.py b/frontend/components/bot_performance_card_v2.py similarity index 98% rename from ui_components/bot_performance_card_v2.py rename to frontend/components/bot_performance_card_v2.py index 27901f3..6466d9d 100644 --- a/ui_components/bot_performance_card_v2.py +++ b/frontend/components/bot_performance_card_v2.py @@ -1,9 +1,7 @@ -from streamlit_elements import mui, lazy +from streamlit_elements import mui from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from ui_components.dashboard import Dashboard -import streamlit as st -import time +from frontend.components.dashboard import Dashboard import pandas as pd from utils.backend_api_client import BackendAPIClient diff --git a/ui_components/bots_file_explorer.py b/frontend/components/bots_file_explorer.py similarity index 95% rename from ui_components/bots_file_explorer.py rename to frontend/components/bots_file_explorer.py index 6b9da2e..9816122 100644 --- a/ui_components/bots_file_explorer.py +++ b/frontend/components/bots_file_explorer.py @@ -1,7 +1,7 @@ from streamlit_elements import mui import constants -from ui_components.file_explorer_base import FileExplorerBase +from frontend.components.file_explorer_base import FileExplorerBase from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ get_yml_files_from_directory, get_log_files_from_directory diff --git a/ui_components/card.py b/frontend/components/card.py similarity index 95% rename from ui_components/card.py rename to frontend/components/card.py index 10b9a90..557a290 100644 --- a/ui_components/card.py +++ b/frontend/components/card.py @@ -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): diff --git a/ui_components/controllers_file_explorer.py b/frontend/components/controllers_file_explorer.py similarity index 90% rename from ui_components/controllers_file_explorer.py rename to frontend/components/controllers_file_explorer.py index 3940c77..6fe4d88 100644 --- a/ui_components/controllers_file_explorer.py +++ b/frontend/components/controllers_file_explorer.py @@ -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 frontend.components.file_explorer_base import FileExplorerBase +from utils.os_utils import load_controllers class ControllersFileExplorer(FileExplorerBase): diff --git a/ui_components/dashboard.py b/frontend/components/dashboard.py similarity index 100% rename from ui_components/dashboard.py rename to frontend/components/dashboard.py diff --git a/ui_components/datagrid.py b/frontend/components/datagrid.py similarity index 100% rename from ui_components/datagrid.py rename to frontend/components/datagrid.py diff --git a/ui_components/directional_strategy_creation_card.py b/frontend/components/directional_strategy_creation_card.py similarity index 100% rename from ui_components/directional_strategy_creation_card.py rename to frontend/components/directional_strategy_creation_card.py diff --git a/ui_components/editor.py b/frontend/components/editor.py similarity index 100% rename from ui_components/editor.py rename to frontend/components/editor.py diff --git a/ui_components/exited_bot_card.py b/frontend/components/exited_bot_card.py similarity index 89% rename from ui_components/exited_bot_card.py rename to frontend/components/exited_bot_card.py index 6d4c253..eb020f3 100644 --- a/ui_components/exited_bot_card.py +++ b/frontend/components/exited_bot_card.py @@ -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 class ExitedBotCard(Dashboard.Item): diff --git a/ui_components/file_explorer_base.py b/frontend/components/file_explorer_base.py similarity index 100% rename from ui_components/file_explorer_base.py rename to frontend/components/file_explorer_base.py diff --git a/ui_components/launch_bot_card.py b/frontend/components/launch_bot_card.py similarity index 100% rename from ui_components/launch_bot_card.py rename to frontend/components/launch_bot_card.py diff --git a/ui_components/launch_broker_card.py b/frontend/components/launch_broker_card.py similarity index 100% rename from ui_components/launch_broker_card.py rename to frontend/components/launch_broker_card.py diff --git a/ui_components/launch_master_bot_card.py b/frontend/components/launch_master_bot_card.py similarity index 100% rename from ui_components/launch_master_bot_card.py rename to frontend/components/launch_master_bot_card.py diff --git a/ui_components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py similarity index 100% rename from ui_components/launch_strategy_v2.py rename to frontend/components/launch_strategy_v2.py diff --git a/ui_components/master_conf_file_explorer.py b/frontend/components/master_conf_file_explorer.py similarity index 97% rename from ui_components/master_conf_file_explorer.py rename to frontend/components/master_conf_file_explorer.py index e395f48..a4cf8eb 100644 --- a/ui_components/master_conf_file_explorer.py +++ b/frontend/components/master_conf_file_explorer.py @@ -1,7 +1,7 @@ from streamlit_elements import mui import constants -from ui_components.file_explorer_base import FileExplorerBase +from frontend.components.file_explorer_base import FileExplorerBase from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ get_yml_files_from_directory, get_log_files_from_directory diff --git a/ui_components/media_player.py b/frontend/components/media_player.py similarity index 100% rename from ui_components/media_player.py rename to frontend/components/media_player.py diff --git a/ui_components/optimization_creation_card.py b/frontend/components/optimization_creation_card.py similarity index 100% rename from ui_components/optimization_creation_card.py rename to frontend/components/optimization_creation_card.py diff --git a/ui_components/optimization_run_card.py b/frontend/components/optimization_run_card.py similarity index 100% rename from ui_components/optimization_run_card.py rename to frontend/components/optimization_run_card.py diff --git a/ui_components/optimizations_file_explorer.py b/frontend/components/optimizations_file_explorer.py similarity index 92% rename from ui_components/optimizations_file_explorer.py rename to frontend/components/optimizations_file_explorer.py index c0354ab..c207d62 100644 --- a/ui_components/optimizations_file_explorer.py +++ b/frontend/components/optimizations_file_explorer.py @@ -1,7 +1,7 @@ from streamlit_elements import mui import constants -from ui_components.file_explorer_base import FileExplorerBase +from frontend.components.file_explorer_base import FileExplorerBase from utils.os_utils import get_python_files_from_directory diff --git a/ui_components/st_inputs.py b/frontend/components/st_inputs.py similarity index 100% rename from ui_components/st_inputs.py rename to frontend/components/st_inputs.py diff --git a/frontend/pages/__init__.py b/frontend/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/backtest_analyze/README.md b/frontend/pages/backtest_analyze/README.md similarity index 100% rename from pages/backtest_analyze/README.md rename to frontend/pages/backtest_analyze/README.md diff --git a/frontend/pages/backtest_analyze/__init__.py b/frontend/pages/backtest_analyze/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/backtest_analyze/analyze.py b/frontend/pages/backtest_analyze/analyze.py similarity index 100% rename from pages/backtest_analyze/analyze.py rename to frontend/pages/backtest_analyze/analyze.py diff --git a/pages/backtest_create/README.md b/frontend/pages/backtest_create/README.md similarity index 100% rename from pages/backtest_create/README.md rename to frontend/pages/backtest_create/README.md diff --git a/frontend/pages/backtest_create/__init__.py b/frontend/pages/backtest_create/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/backtest_create/create.py b/frontend/pages/backtest_create/create.py similarity index 84% rename from pages/backtest_create/create.py rename to frontend/pages/backtest_create/create.py index 4e6322a..754ee4a 100644 --- a/pages/backtest_create/create.py +++ b/frontend/pages/backtest_create/create.py @@ -3,10 +3,10 @@ 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 utils.st_utils import initialize_st_page diff --git a/pages/backtest_optimize/README.md b/frontend/pages/backtest_optimize/README.md similarity index 100% rename from pages/backtest_optimize/README.md rename to frontend/pages/backtest_optimize/README.md diff --git a/frontend/pages/backtest_optimize/__init__.py b/frontend/pages/backtest_optimize/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/backtest_optimize/optimize.py b/frontend/pages/backtest_optimize/optimize.py similarity index 85% rename from pages/backtest_optimize/optimize.py rename to frontend/pages/backtest_optimize/optimize.py index f601d75..861b862 100644 --- a/pages/backtest_optimize/optimize.py +++ b/frontend/pages/backtest_optimize/optimize.py @@ -5,11 +5,11 @@ 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 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 utils import os_utils from utils.st_utils import initialize_st_page diff --git a/pages/bollinger_v1/README.md b/frontend/pages/bollinger_v1/README.md similarity index 100% rename from pages/bollinger_v1/README.md rename to frontend/pages/bollinger_v1/README.md diff --git a/frontend/pages/bollinger_v1/__init__.py b/frontend/pages/bollinger_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/bollinger_v1/app.py b/frontend/pages/bollinger_v1/app.py similarity index 100% rename from pages/bollinger_v1/app.py rename to frontend/pages/bollinger_v1/app.py diff --git a/pages/bot_orchestration/README.md b/frontend/pages/bot_orchestration/README.md similarity index 100% rename from pages/bot_orchestration/README.md rename to frontend/pages/bot_orchestration/README.md diff --git a/frontend/pages/bot_orchestration/__init__.py b/frontend/pages/bot_orchestration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/bot_orchestration/app.py b/frontend/pages/bot_orchestration/app.py similarity index 93% rename from pages/bot_orchestration/app.py rename to frontend/pages/bot_orchestration/app.py index a7289da..177243d 100644 --- a/pages/bot_orchestration/app.py +++ b/frontend/pages/bot_orchestration/app.py @@ -2,8 +2,8 @@ import streamlit as st from streamlit_elements import elements, mui from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from ui_components.bot_performance_card_v2 import BotPerformanceCardV2 -from ui_components.dashboard import Dashboard +from frontend.components.bot_performance_card_v2 import BotPerformanceCardV2 +from frontend.components.dashboard import Dashboard from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page diff --git a/pages/data_download_candles/README.md b/frontend/pages/data_download_candles/README.md similarity index 100% rename from pages/data_download_candles/README.md rename to frontend/pages/data_download_candles/README.md diff --git a/frontend/pages/data_download_candles/__init__.py b/frontend/pages/data_download_candles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/data_download_candles/app.py b/frontend/pages/data_download_candles/app.py similarity index 100% rename from pages/data_download_candles/app.py rename to frontend/pages/data_download_candles/app.py diff --git a/pages/db_inspector/README.md b/frontend/pages/db_inspector/README.md similarity index 100% rename from pages/db_inspector/README.md rename to frontend/pages/db_inspector/README.md diff --git a/frontend/pages/db_inspector/__init__.py b/frontend/pages/db_inspector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/db_inspector/app.py b/frontend/pages/db_inspector/app.py similarity index 100% rename from pages/db_inspector/app.py rename to frontend/pages/db_inspector/app.py diff --git a/pages/dman_maker_v2/README.md b/frontend/pages/dman_maker_v2/README.md similarity index 100% rename from pages/dman_maker_v2/README.md rename to frontend/pages/dman_maker_v2/README.md diff --git a/frontend/pages/dman_maker_v2/__init__.py b/frontend/pages/dman_maker_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/dman_maker_v2/app.py b/frontend/pages/dman_maker_v2/app.py similarity index 99% rename from pages/dman_maker_v2/app.py rename to frontend/pages/dman_maker_v2/app.py index 013c829..86a7af1 100644 --- a/pages/dman_maker_v2/app.py +++ b/frontend/pages/dman_maker_v2/app.py @@ -7,7 +7,7 @@ import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page -from ui_components.st_inputs import normalize, distribution_inputs, get_distribution +from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") diff --git a/pages/dman_v5/README.md b/frontend/pages/dman_v5/README.md similarity index 100% rename from pages/dman_v5/README.md rename to frontend/pages/dman_v5/README.md diff --git a/frontend/pages/dman_v5/__init__.py b/frontend/pages/dman_v5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/dman_v5/app.py b/frontend/pages/dman_v5/app.py new file mode 100644 index 0000000..2cdfecc --- /dev/null +++ b/frontend/pages/dman_v5/app.py @@ -0,0 +1,149 @@ +import streamlit as st +import pandas as pd +import pandas_ta as ta +import plotly.graph_objects as go +import yaml +from hummingbot.connector.connector_base import OrderType +from plotly.subplots import make_subplots + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.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='ms') +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!") diff --git a/frontend/pages/dynamic_position_builder/README.md b/frontend/pages/dynamic_position_builder/README.md new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/dynamic_position_builder/__init__.py b/frontend/pages/dynamic_position_builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/dynamic_position_builder/app.py b/frontend/pages/dynamic_position_builder/app.py new file mode 100644 index 0000000..3130f07 --- /dev/null +++ b/frontend/pages/dynamic_position_builder/app.py @@ -0,0 +1,220 @@ +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 frontend.components.st_inputs import normalize, distribution_inputs, get_distribution + +# Initialize the Streamlit page +initialize_st_page(title="Dynamic Spread Generator", icon="🚀", initial_sidebar_state="collapsed") + + +# Page content +st.text("This tool will help you analyze and generate a position config.") +st.write("---") + +# Layout in columns +col_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2]) + +def convert_to_yaml(spreads, order_amounts): + data = { + 'dca_spreads': [float(spread)/100 for spread in spreads], + 'dca_amounts': [float(amount) for amount in order_amounts] + } + return yaml.dump(data, default_flow_style=False) + + +with col_quote: + total_amount_quote = st.number_input("Total amount of quote", value=1000) + +with col_tp_sl: + tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=2.0, step=0.1) + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=8.0, step=0.1) + +with col_levels: + n_levels = st.number_input("Number of Levels", min_value=1, value=5) + + +# Spread and Amount Distributions +spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs( + col_spread_dist, "Spread", n_levels) +amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs( + col_amount_dist, "Amount", n_levels) + +# Assuming get_distribution function returns a list of Decimal values for spreads and amounts +spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, + spread_step, spread_ratio, manual_spreads) +amount_distribution = normalize( + get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, + amount_ratio, manual_amounts)) + +order_amounts = [Decimal(amount_dist * total_amount_quote) for amount_dist in amount_distribution] + +# Initialize lists to store values per level +take_profit_values = [] +dynamic_spreads = [] +break_even_values = [] +spread_adjustment_factor = Decimal(0.01) # Spread adjustment factor + +for level in range(n_levels): + if level > 0: + # Recompute spread for current level based on the previous level's break-even price + previous_break_even = break_even_values[level - 1] + new_spread = previous_break_even + Decimal(spread_distribution[level]) # Dynamic spread adjustment + dynamic_spreads.append(new_spread) + total_amount = sum(order_amounts[:level + 1]) + break_even_values.append(Decimal(sum([spread * amount for spread, amount in zip(dynamic_spreads, order_amounts[:level + 1])]) / total_amount)) + else: + dynamic_spreads = [Decimal(spread_distribution[0])] # Initialize with the first spread value + break_even_values = [Decimal(spread_distribution[0])] + +accumulated_amount = [sum(order_amounts[:i + 1]) for i in range(len(order_amounts))] + + +def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): + unrealized_pnl = [] + for i in range(len(spreads)): + distance = abs(spreads[i] - break_even_values[i]) + pnl = accumulated_amount[i] * distance / 100 # PNL calculation + unrealized_pnl.append(pnl) + return unrealized_pnl + +# Calculate unrealized PNL +cum_unrealized_pnl = calculate_unrealized_pnl(dynamic_spreads, break_even_values, accumulated_amount) + + +tech_colors = { + 'spread': '#00BFFF', # Deep Sky Blue + 'break_even': '#FFD700', # Gold + 'take_profit': '#32CD32', # Green + 'order_amount': '#1E90FF', # Dodger Blue + 'cum_amount': '#4682B4', # Steel Blue + 'stop_loss': '#FF0000', # Red +} + +# Create Plotly figure with secondary y-axis and a dark theme +fig = make_subplots(specs=[[{"secondary_y": True}]]) +fig.update_layout(template="plotly_dark") + +# Update the Scatter Plots and Horizontal Lines +fig.add_trace(go.Scatter(x=list(range(len(dynamic_spreads))), y=dynamic_spreads, name='Spread (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) +fig.add_trace(go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) +fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), secondary_y=False) + +# Add the new Bar Plot for Cumulative Unrealized PNL +fig.add_trace(go.Bar( + x=list(range(len(cum_unrealized_pnl))), + y=cum_unrealized_pnl, + text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], + textposition='auto', + textfont=dict(color='white', size=12), + name='Cum Unrealized PNL', + marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed +), secondary_y=True) + +fig.add_trace(go.Bar( + x=list(range(len(order_amounts))), + y=order_amounts, + text=[f"{amt:.2f}" for amt in order_amounts], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Order Amount', + marker=dict(color=tech_colors['order_amount'], opacity=0.5), +), secondary_y=True) + +# Modify the Bar Plot for Accumulated Amount +fig.add_trace(go.Bar( + x=list(range(len(accumulated_amount))), + y=accumulated_amount, + text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Cum Amount', + marker=dict(color=tech_colors['cum_amount'], opacity=0.5), +), secondary_y=True) + + +# Add Horizontal Lines for Last Breakeven Price and Stop Loss Level +last_break_even = break_even_values[-1] +stop_loss_value = last_break_even + Decimal(sl) +# Horizontal Lines for Last Breakeven and Stop Loss +fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", annotation_position="top left", line_color=tech_colors['break_even']) +fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", annotation_position="bottom right", line_color=tech_colors['stop_loss']) + +# Update Annotations for Spread and Break Even +for i, (spread, be_value, tp_value) in enumerate(zip(dynamic_spreads, break_even_values, take_profit_values)): + fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['spread'])) + fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, font=dict(color=tech_colors['break_even'])) + fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['take_profit'])) +# Update Layout with a Dark Theme +fig.update_layout( + title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", + xaxis_title="Order Level", + yaxis_title="Spread (%)", + yaxis2_title="Amount (Quote)", + height=800, + width=1800, + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + font=dict(color='white') # Font color +) + +# Calculate metrics +max_loss = total_amount_quote * Decimal(sl / 100) +profit_per_level = [cum_amount * Decimal(tp / 100) for cum_amount in accumulated_amount] +loots_to_recover = [max_loss / profit for profit in profit_per_level] + +# Define a consistent annotation size and maximum value for the secondary y-axis +circle_text = "●" # Unicode character for a circle +max_secondary_value = max(max(accumulated_amount), max(order_amounts), max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data + +# Determine an appropriate y-offset for annotations +y_offset_secondary = max_secondary_value * Decimal(0.1) # Adjusts the height relative to the maximum value on the secondary y-axis + +# Add annotations to the Plotly figure for the secondary y-axis +for i, loot in enumerate(loots_to_recover): + fig.add_annotation( + x=i, + y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset + text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines + showarrow=False, + font=dict(size=16, color='purple'), + xanchor="center", # Centers the text above the x coordinate + yanchor="bottom", # Anchors the text at its bottom to avoid overlapping + align="center", + yref="y2" # Reference the secondary y-axis + ) +# Add Max Loss Metric as an Annotation +max_loss_annotation_text = f"Max Loss (Quote): {max_loss:.2f}" +fig.add_annotation( + x=max(len(dynamic_spreads), len(break_even_values)) - 1, # Positioning the annotation to the right + text=max_loss_annotation_text, + showarrow=False, + font=dict(size=20, color='white'), + bgcolor='red', # Red background for emphasis + xanchor="left", + yanchor="top", + yref="y2" # Reference the secondary y-axis +) + +st.write("---") + +# Display in Streamlit +st.plotly_chart(fig) +# Export Button +if st.button('Export as YAML'): + yaml_data = convert_to_yaml(spread_distribution, order_amounts) + st.download_button( + label="Download YAML", + data=yaml_data, + file_name='config.yaml', + mime='text/yaml' + ) diff --git a/pages/file_manager/README.md b/frontend/pages/file_manager/README.md similarity index 100% rename from pages/file_manager/README.md rename to frontend/pages/file_manager/README.md diff --git a/frontend/pages/file_manager/__init__.py b/frontend/pages/file_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/file_manager/app.py b/frontend/pages/file_manager/app.py similarity index 87% rename from pages/file_manager/app.py rename to frontend/pages/file_manager/app.py index 059f472..1c731cc 100644 --- a/pages/file_manager/app.py +++ b/frontend/pages/file_manager/app.py @@ -2,9 +2,9 @@ from types import SimpleNamespace import streamlit as st from streamlit_elements import elements, mui -from ui_components.bots_file_explorer import BotsFileExplorer -from ui_components.dashboard import Dashboard -from ui_components.editor import Editor +from frontend.components.bots_file_explorer import BotsFileExplorer +from frontend.components.dashboard import Dashboard +from frontend.components.editor import Editor from utils.st_utils import initialize_st_page diff --git a/pages/macd_bb_v1/README.md b/frontend/pages/kalman_filter_v1/README.md similarity index 100% rename from pages/macd_bb_v1/README.md rename to frontend/pages/kalman_filter_v1/README.md diff --git a/frontend/pages/kalman_filter_v1/__init__.py b/frontend/pages/kalman_filter_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/kalman_filter_v1/app.py b/frontend/pages/kalman_filter_v1/app.py new file mode 100644 index 0000000..3e34269 --- /dev/null +++ b/frontend/pages/kalman_filter_v1/app.py @@ -0,0 +1,226 @@ +import streamlit as st +import pandas as pd +import plotly.graph_objects as go +import pandas_ta as ta +import yaml +from hummingbot.connector.connector_base import OrderType +from pykalman import KalmanFilter + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from utils.backend_api_client import BackendAPIClient +from utils.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='ms') +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!") \ No newline at end of file diff --git a/pages/launch_bot/README.md b/frontend/pages/launch_bot/README.md similarity index 100% rename from pages/launch_bot/README.md rename to frontend/pages/launch_bot/README.md diff --git a/frontend/pages/launch_bot/__init__.py b/frontend/pages/launch_bot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/launch_bot/app.py b/frontend/pages/launch_bot/app.py similarity index 90% rename from pages/launch_bot/app.py rename to frontend/pages/launch_bot/app.py index 70b661b..99dd0a7 100644 --- a/pages/launch_bot/app.py +++ b/frontend/pages/launch_bot/app.py @@ -3,8 +3,8 @@ from types import SimpleNamespace import streamlit as st from streamlit_elements import elements, mui -from ui_components.dashboard import Dashboard -from ui_components.launch_strategy_v2 import LaunchStrategyV2 +from frontend.components.dashboard import Dashboard +from frontend.components.launch_strategy_v2 import LaunchStrategyV2 from utils.st_utils import initialize_st_page CARD_WIDTH = 6 diff --git a/pages/pmm_simple/README.md b/frontend/pages/macd_bb_v1/README.md similarity index 100% rename from pages/pmm_simple/README.md rename to frontend/pages/macd_bb_v1/README.md diff --git a/frontend/pages/macd_bb_v1/__init__.py b/frontend/pages/macd_bb_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/macd_bb_v1/app.py b/frontend/pages/macd_bb_v1/app.py similarity index 100% rename from pages/macd_bb_v1/app.py rename to frontend/pages/macd_bb_v1/app.py diff --git a/pages/master_conf/README.md b/frontend/pages/master_conf/README.md similarity index 100% rename from pages/master_conf/README.md rename to frontend/pages/master_conf/README.md diff --git a/frontend/pages/master_conf/__init__.py b/frontend/pages/master_conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/master_conf/app.py b/frontend/pages/master_conf/app.py similarity index 80% rename from pages/master_conf/app.py rename to frontend/pages/master_conf/app.py index c5b1758..c92ae53 100644 --- a/pages/master_conf/app.py +++ b/frontend/pages/master_conf/app.py @@ -1,15 +1,11 @@ -import glob -import os from types import SimpleNamespace import streamlit as st -from docker_manager import DockerManager from streamlit_elements import elements, mui -import constants -from ui_components.dashboard import Dashboard -from ui_components.editor import Editor -from ui_components.launch_master_bot_card import LaunchMasterBotCard -from ui_components.master_conf_file_explorer import MasterConfFileExplorer +from frontend.components.dashboard import Dashboard +from frontend.components.editor import Editor +from frontend.components.launch_master_bot_card import LaunchMasterBotCard +from frontend.components.master_conf_file_explorer import MasterConfFileExplorer from utils.st_utils import initialize_st_page diff --git a/pages/trend_follower_v1/README.md b/frontend/pages/pmm_simple/README.md similarity index 100% rename from pages/trend_follower_v1/README.md rename to frontend/pages/pmm_simple/README.md diff --git a/frontend/pages/pmm_simple/__init__.py b/frontend/pages/pmm_simple/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/pmm_simple/app.py b/frontend/pages/pmm_simple/app.py similarity index 97% rename from pages/pmm_simple/app.py rename to frontend/pages/pmm_simple/app.py index dcd7a8b..6b0bd03 100644 --- a/pages/pmm_simple/app.py +++ b/frontend/pages/pmm_simple/app.py @@ -9,7 +9,7 @@ import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from utils.backend_api_client import BackendAPIClient from utils.st_utils import initialize_st_page -from ui_components.st_inputs import normalize, distribution_inputs, get_distribution +from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="PMM Simple", icon="👨‍🏫", initial_sidebar_state="collapsed") @@ -178,8 +178,8 @@ sell_orders_df = pd.DataFrame({ "Take Profit ($)": [float(amount) * (tp / 100) for amount in sell_order_amounts_quote], "Stop Loss ($)": [float(amount) * (sl / 100) for amount in sell_order_amounts_quote], "Min Trailing Stop ($)": [float(amount) * ((ts_ap - ts_delta) / 100) for amount in sell_order_amounts_quote], - "TP/SL Ratio": [tp / sl] * buy_order_levels, - "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * buy_order_levels, + "TP/SL Ratio": [tp / sl] * sell_order_levels, + "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * sell_order_levels, }) # Display the DataFrames in Streamlit @@ -219,7 +219,6 @@ config = { "trailing_stop": { "activation_price": ts_ap / 100, "trailing_delta": ts_delta / 100}, - "top_executor_refresh_time": None, } yaml_config = yaml.dump(config, default_flow_style=False) diff --git a/frontend/pages/position_builder/README.md b/frontend/pages/position_builder/README.md new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/position_builder/__init__.py b/frontend/pages/position_builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/position_builder/app.py b/frontend/pages/position_builder/app.py similarity index 98% rename from pages/position_builder/app.py rename to frontend/pages/position_builder/app.py index 475c6f8..146719c 100644 --- a/pages/position_builder/app.py +++ b/frontend/pages/position_builder/app.py @@ -5,7 +5,7 @@ from decimal import Decimal import yaml from utils.st_utils import initialize_st_page -from ui_components.st_inputs import normalize, distribution_inputs, get_distribution +from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution # Initialize the Streamlit page initialize_st_page(title="Position Generator", icon="🔭", initial_sidebar_state="collapsed") @@ -37,8 +37,8 @@ with col_levels: # 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") +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)) diff --git a/pages/reference_data/7_📋_Data.py b/frontend/pages/reference_data/7_📋_Data.py similarity index 100% rename from pages/reference_data/7_📋_Data.py rename to frontend/pages/reference_data/7_📋_Data.py diff --git a/frontend/pages/reference_data/__init__.py b/frontend/pages/reference_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/strategy_performance/README.md b/frontend/pages/strategy_performance/README.md similarity index 100% rename from pages/strategy_performance/README.md rename to frontend/pages/strategy_performance/README.md diff --git a/frontend/pages/strategy_performance/__init__.py b/frontend/pages/strategy_performance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/strategy_performance/app.py b/frontend/pages/strategy_performance/app.py similarity index 100% rename from pages/strategy_performance/app.py rename to frontend/pages/strategy_performance/app.py diff --git a/pages/token_spreads/README.md b/frontend/pages/token_spreads/README.md similarity index 100% rename from pages/token_spreads/README.md rename to frontend/pages/token_spreads/README.md diff --git a/frontend/pages/token_spreads/__init__.py b/frontend/pages/token_spreads/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/token_spreads/app.py b/frontend/pages/token_spreads/app.py similarity index 100% rename from pages/token_spreads/app.py rename to frontend/pages/token_spreads/app.py diff --git a/pages/xemm_controller/README.md b/frontend/pages/trend_follower_v1/README.md similarity index 100% rename from pages/xemm_controller/README.md rename to frontend/pages/trend_follower_v1/README.md diff --git a/frontend/pages/trend_follower_v1/__init__.py b/frontend/pages/trend_follower_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/trend_follower_v1/app.py b/frontend/pages/trend_follower_v1/app.py similarity index 100% rename from pages/trend_follower_v1/app.py rename to frontend/pages/trend_follower_v1/app.py diff --git a/pages/tvl_vs_mcap/README.md b/frontend/pages/tvl_vs_mcap/README.md similarity index 100% rename from pages/tvl_vs_mcap/README.md rename to frontend/pages/tvl_vs_mcap/README.md diff --git a/frontend/pages/tvl_vs_mcap/__init__.py b/frontend/pages/tvl_vs_mcap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/tvl_vs_mcap/app.py b/frontend/pages/tvl_vs_mcap/app.py similarity index 100% rename from pages/tvl_vs_mcap/app.py rename to frontend/pages/tvl_vs_mcap/app.py diff --git a/frontend/pages/xemm_controller/README.md b/frontend/pages/xemm_controller/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/frontend/pages/xemm_controller/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/frontend/pages/xemm_controller/__init__.py b/frontend/pages/xemm_controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/xemm_controller/app.py b/frontend/pages/xemm_controller/app.py similarity index 100% rename from pages/xemm_controller/app.py rename to frontend/pages/xemm_controller/app.py diff --git a/helpers/add_authemail.py b/helpers/add_authemail.py deleted file mode 100755 index a7c6897..0000000 --- a/helpers/add_authemail.py +++ /dev/null @@ -1,27 +0,0 @@ -import streamlit_authenticator as stauth -from ruamel.yaml import YAML -import os - -yaml = YAML(typ='safe', pure=True) - -# enter email address -new_email = input("Enter dashboard email >> ") - -# if user enter no email address, exit setup! -if len(new_email) == 0: - print("\nNo email added, please try again!\n") - exit() - -# load the YAML file -yaml_file = "../credentials.yml" -with open(yaml_file, "r") as file: - data = yaml.load(file) - -# append the email address to credentials.yml -data["preauthorized"]["emails"].append(new_email) - -# write the updated data back to the file -with open(yaml_file, "w") as file: - yaml.dump(data, file) - -print("Email has been successfully added!") diff --git a/helpers/edit_authadmin_password.py b/helpers/edit_authadmin_password.py deleted file mode 100644 index 35113e2..0000000 --- a/helpers/edit_authadmin_password.py +++ /dev/null @@ -1,26 +0,0 @@ -import streamlit_authenticator as stauth -from ruamel.yaml import YAML -import os - -yaml = YAML(typ='safe', pure=True) - -# enter admin password or use default t3st01 -new_password = input("Enter dashboard password >> ") -new_password = new_password or "t3st01" - -# extract the hash password from the List -hash_password = stauth.Hasher([new_password]).generate()[0] - -# load the YAML file -yaml_file = "../credentials.yml" -with open(yaml_file, "r") as file: - data = yaml.load(file) - -# update the admin password on credentials.yml -data["credentials"]["usernames"]["admin"]["password"] = hash_password - -# write the updated data back to the file -with open(yaml_file, "w") as file: - yaml.dump(data, file) - -print("Admin password has been updated! ") diff --git a/hummingbot_files/bots/.gitignore b/hummingbot_files/bots/.gitignore deleted file mode 100644 index 359db62..0000000 --- a/hummingbot_files/bots/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/hummingbot* \ No newline at end of file diff --git a/hummingbot_files/bots/data_downloader/conf/.password_verification b/hummingbot_files/bots/data_downloader/conf/.password_verification deleted file mode 100644 index b8c7618..0000000 --- a/hummingbot_files/bots/data_downloader/conf/.password_verification +++ /dev/null @@ -1 +0,0 @@ -7b2263727970746f223a207b22636970686572223a20226165732d3132382d637472222c2022636970686572706172616d73223a207b226976223a20223864336365306436393461623131396334363135663935366464653839363063227d2c202263697068657274657874223a20223836333266323430613563306131623665353664222c20226b6466223a202270626b646632222c20226b6466706172616d73223a207b2263223a20313030303030302c2022646b6c656e223a2033322c2022707266223a2022686d61632d736861323536222c202273616c74223a20226566373330376531636464373964376132303338323534656139343433663930227d2c20226d6163223a202266393439383534613530633138363633386363353962336133363665633962353333386633613964373266636635343066313034333361353431636232306438227d2c202276657273696f6e223a20337d \ No newline at end of file diff --git a/hummingbot_files/bots/data_downloader/conf/conf_client.yml b/hummingbot_files/bots/data_downloader/conf/conf_client.yml deleted file mode 100644 index ab2880c..0000000 --- a/hummingbot_files/bots/data_downloader/conf/conf_client.yml +++ /dev/null @@ -1,199 +0,0 @@ -#################################### -### client_config_map config ### -#################################### - -instance_id: 039758736d451914503a45ff596e168902d62557 - -log_level: INFO - -debug_console: false - -strategy_report_interval: 900.0 - -logger_override_whitelist: -- hummingbot.strategy.arbitrage -- hummingbot.strategy.cross_exchange_market_making -- conf - -log_file_path: /home/hummingbot/logs - -kill_switch_mode: {} - -# What to auto-fill in the prompt after each import command (start/config) -autofill_import: disabled - -telegram_mode: {} - -# MQTT Bridge configuration. -mqtt_bridge: - mqtt_host: localhost - mqtt_port: 1883 - mqtt_username: '' - mqtt_password: '' - mqtt_namespace: hbot - mqtt_ssl: false - mqtt_logger: true - mqtt_notifier: true - mqtt_commands: true - mqtt_events: true - mqtt_external_events: true - mqtt_autostart: true - -# Error log sharing -send_error_logs: true - -# Can store the previous strategy ran for quick retrieval. -previous_strategy: null - -# Advanced database options, currently supports SQLAlchemy's included dialects -# Reference: https://docs.sqlalchemy.org/en/13/dialects/ -# To use an instance of SQLite DB the required configuration is -# db_engine: sqlite -# To use a DBMS the required configuration is -# db_host: 127.0.0.1 -# db_port: 3306 -# db_username: username -# db_password: password -# db_name: dbname -db_mode: - db_engine: sqlite - -pmm_script_mode: {} - -# Balance Limit Configurations -# e.g. Setting USDT and BTC limits on Binance. -# balance_asset_limit: -# binance: -# BTC: 0.1 -# USDT: 1000 -balance_asset_limit: - bybit_testnet: {} - lbank: {} - binance_us: {} - crypto_com: {} - ascend_ex_paper_trade: {} - hotbit: {} - gate_io_paper_trade: {} - bitmex_testnet: {} - ndax_testnet: {} - huobi: {} - probit_kr: {} - altmarkets: {} - hitbtc: {} - foxbit: {} - ascend_ex: {} - binance: {} - okx: {} - ciex: {} - bitmex: {} - bitfinex: {} - probit: {} - kraken: {} - kucoin: {} - bitmart: {} - bybit: {} - bittrex: {} - btc_markets: {} - mock_paper_exchange: {} - kucoin_paper_trade: {} - ndax: {} - loopring: {} - mexc: {} - whitebit: {} - coinbase_pro: {} - binance_paper_trade: {} - gate_io: {} - -# Fixed gas price (in Gwei) for Ethereum transactions -manual_gas_price: 50.0 - -# Gateway API Configurations -# default host to only use localhost -# Port need to match the final installation port for Gateway -gateway: - gateway_api_host: localhost - gateway_api_port: '15888' - -certs_path: /home/hummingbot/certs - -# Whether to enable aggregated order and trade data collection -anonymized_metrics_mode: - anonymized_metrics_interval_min: 15.0 - -# Command Shortcuts -# Define abbreviations for often used commands -# or batch grouped commands together -command_shortcuts: -- command: spreads - help: Set bid and ask spread - arguments: - - Bid Spread - - Ask Spread - output: - - config bid_spread $1 - - config ask_spread $2 - -# A source for rate oracle, currently ascend_ex, binance, coin_gecko, coin_cap, kucoin, gate_io -rate_oracle_source: - name: binance - -# A universal token which to display tokens values in, e.g. USD,EUR,BTC -global_token: - global_token_name: USD - global_token_symbol: $ - -# Percentage of API rate limits (on any exchange and any end point) allocated to this bot instance. -# Enter 50 to indicate 50%. E.g. if the API rate limit is 100 calls per second, and you allocate -# 50% to this setting, the bot will have a maximum (limit) of 50 calls per second -rate_limits_share_pct: 100.0 - -commands_timeout: - create_command_timeout: 10.0 - other_commands_timeout: 30.0 - -# Tabulate table format style (https://github.com/astanin/python-tabulate#table-format) -tables_format: psql - -paper_trade: - paper_trade_exchanges: - - binance - - kucoin - - ascend_ex - - gate_io - paper_trade_account_balance: - BTC: 1.0 - USDT: 1000.0 - ONE: 1000.0 - USDQ: 1000.0 - TUSD: 1000.0 - ETH: 10.0 - WETH: 10.0 - USDC: 1000.0 - DAI: 1000.0 - -color: - top_pane: '#000000' - bottom_pane: '#000000' - output_pane: '#262626' - input_pane: '#1C1C1C' - logs_pane: '#121212' - terminal_primary: '#5FFFD7' - primary_label: '#5FFFD7' - secondary_label: '#FFFFFF' - success_label: '#5FFFD7' - warning_label: '#FFFF00' - info_label: '#5FD7FF' - error_label: '#FF0000' - gold_label: '#FFD700' - silver_label: '#C0C0C0' - bronze_label: '#CD7F32' - -# The tick size is the frequency with which the clock notifies the time iterators by calling the -# c_tick() method, that means for example that if the tick size is 1, the logic of the strategy -# will run every second. -tick_size: 1.0 - -market_data_collection: - market_data_collection_enabled: true - market_data_collection_interval: 60 - market_data_collection_depth: 20 diff --git a/hummingbot_files/bots/data_downloader/conf/conf_fee_overrides.yml b/hummingbot_files/bots/data_downloader/conf/conf_fee_overrides.yml deleted file mode 100644 index f295650..0000000 --- a/hummingbot_files/bots/data_downloader/conf/conf_fee_overrides.yml +++ /dev/null @@ -1,340 +0,0 @@ -######################################## -### Fee overrides configurations ### -######################################## - -# For more detailed information: https://docs.hummingbot.io -template_version: 14 - -# Example of the fields that can be specified to override the `TradeFeeFactory` default settings. -# If the field is missing or the value is left blank, the default value will be used. -# The percentage values are specified as 0.1 for 0.1%. -# -# [exchange name]_percent_fee_token: -# [exchange name]_maker_percent_fee: -# [exchange name]_taker_percent_fee: -# [exchange name]_buy_percent_fee_deducted_from_returns: # if False, the buy fee is added to the order costs -# [exchange name]_maker_fixed_fees: # a list of lists of token-fee pairs (e.g. [["ETH", 1]]) -# [exchange name]_taker_fixed_fees: # a list of lists of token-fee pairs (e.g. [["ETH", 1]]) - -binance_percent_fee_token: # BNB -binance_maker_percent_fee: # 0.75 -binance_taker_percent_fee: # 0.75 -binance_buy_percent_fee_deducted_from_returns: # True - -# List of supported Exchanges for which the user's data_downloader/conf_fee_override.yml -# will work. This file currently needs to be in sync with hummingbot list of -# supported exchanges -altmarkets_buy_percent_fee_deducted_from_returns: -altmarkets_maker_fixed_fees: -altmarkets_maker_percent_fee: -altmarkets_percent_fee_token: -altmarkets_taker_fixed_fees: -altmarkets_taker_percent_fee: -ascend_ex_buy_percent_fee_deducted_from_returns: -ascend_ex_maker_fixed_fees: -ascend_ex_maker_percent_fee: -ascend_ex_percent_fee_token: -ascend_ex_taker_fixed_fees: -ascend_ex_taker_percent_fee: -binance_maker_fixed_fees: -binance_perpetual_buy_percent_fee_deducted_from_returns: -binance_perpetual_maker_fixed_fees: -binance_perpetual_maker_percent_fee: -binance_perpetual_percent_fee_token: -binance_perpetual_taker_fixed_fees: -binance_perpetual_taker_percent_fee: -binance_perpetual_testnet_buy_percent_fee_deducted_from_returns: -binance_perpetual_testnet_maker_fixed_fees: -binance_perpetual_testnet_maker_percent_fee: -binance_perpetual_testnet_percent_fee_token: -binance_perpetual_testnet_taker_fixed_fees: -binance_perpetual_testnet_taker_percent_fee: -binance_taker_fixed_fees: -binance_us_buy_percent_fee_deducted_from_returns: -binance_us_maker_fixed_fees: -binance_us_maker_percent_fee: -binance_us_percent_fee_token: -binance_us_taker_fixed_fees: -binance_us_taker_percent_fee: -bitfinex_buy_percent_fee_deducted_from_returns: -bitfinex_maker_fixed_fees: -bitfinex_maker_percent_fee: -bitfinex_percent_fee_token: -bitfinex_taker_fixed_fees: -bitfinex_taker_percent_fee: -bitmart_buy_percent_fee_deducted_from_returns: -bitmart_maker_fixed_fees: -bitmart_maker_percent_fee: -bitmart_percent_fee_token: -bitmart_taker_fixed_fees: -bitmart_taker_percent_fee: -bittrex_buy_percent_fee_deducted_from_returns: -bittrex_maker_fixed_fees: -bittrex_maker_percent_fee: -bittrex_percent_fee_token: -bittrex_taker_fixed_fees: -bittrex_taker_percent_fee: -btc_markets_percent_fee_token: -btc_markets_maker_percent_fee: -btc_markets_taker_percent_fee: -btc_markets_buy_percent_fee_deducted_from_returns: -bybit_perpetual_buy_percent_fee_deducted_from_returns: -bybit_perpetual_maker_fixed_fees: -bybit_perpetual_maker_percent_fee: -bybit_perpetual_percent_fee_token: -bybit_perpetual_taker_fixed_fees: -bybit_perpetual_taker_percent_fee: -bybit_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bybit_perpetual_testnet_maker_fixed_fees: -bybit_perpetual_testnet_maker_percent_fee: -bybit_perpetual_testnet_percent_fee_token: -bybit_perpetual_testnet_taker_fixed_fees: -bybit_perpetual_testnet_taker_percent_fee: -coinbase_pro_buy_percent_fee_deducted_from_returns: -coinbase_pro_maker_fixed_fees: -coinbase_pro_maker_percent_fee: -coinbase_pro_percent_fee_token: -coinbase_pro_taker_fixed_fees: -coinbase_pro_taker_percent_fee: -crypto_com_buy_percent_fee_deducted_from_returns: -crypto_com_maker_fixed_fees: -crypto_com_maker_percent_fee: -crypto_com_percent_fee_token: -crypto_com_taker_fixed_fees: -crypto_com_taker_percent_fee: -dydx_perpetual_buy_percent_fee_deducted_from_returns: -dydx_perpetual_maker_fixed_fees: -dydx_perpetual_maker_percent_fee: -dydx_perpetual_percent_fee_token: -dydx_perpetual_taker_fixed_fees: -dydx_perpetual_taker_percent_fee: -gate_io_buy_percent_fee_deducted_from_returns: -gate_io_maker_fixed_fees: -gate_io_maker_percent_fee: -gate_io_percent_fee_token: -gate_io_taker_fixed_fees: -gate_io_taker_percent_fee: -hitbtc_buy_percent_fee_deducted_from_returns: -hitbtc_maker_fixed_fees: -hitbtc_maker_percent_fee: -hitbtc_percent_fee_token: -hitbtc_taker_fixed_fees: -hitbtc_taker_percent_fee: -huobi_buy_percent_fee_deducted_from_returns: -huobi_maker_fixed_fees: -huobi_maker_percent_fee: -huobi_percent_fee_token: -huobi_taker_fixed_fees: -huobi_taker_percent_fee: -kraken_buy_percent_fee_deducted_from_returns: -kraken_maker_fixed_fees: -kraken_maker_percent_fee: -kraken_percent_fee_token: -kraken_taker_fixed_fees: -kraken_taker_percent_fee: -kucoin_buy_percent_fee_deducted_from_returns: -kucoin_maker_fixed_fees: -kucoin_maker_percent_fee: -kucoin_percent_fee_token: -kucoin_taker_fixed_fees: -kucoin_taker_percent_fee: -loopring_buy_percent_fee_deducted_from_returns: -loopring_maker_fixed_fees: -loopring_maker_percent_fee: -loopring_percent_fee_token: -loopring_taker_fixed_fees: -loopring_taker_percent_fee: -mexc_buy_percent_fee_deducted_from_returns: -mexc_maker_fixed_fees: -mexc_maker_percent_fee: -mexc_percent_fee_token: -mexc_taker_fixed_fees: -mexc_taker_percent_fee: -ndax_buy_percent_fee_deducted_from_returns: -ndax_maker_fixed_fees: -ndax_maker_percent_fee: -ndax_percent_fee_token: -ndax_taker_fixed_fees: -ndax_taker_percent_fee: -ndax_testnet_buy_percent_fee_deducted_from_returns: -ndax_testnet_maker_fixed_fees: -ndax_testnet_maker_percent_fee: -ndax_testnet_percent_fee_token: -ndax_testnet_taker_fixed_fees: -ndax_testnet_taker_percent_fee: -okx_buy_percent_fee_deducted_from_returns: -okx_maker_fixed_fees: -okx_maker_percent_fee: -okx_percent_fee_token: -okx_taker_fixed_fees: -okx_taker_percent_fee: -probit_buy_percent_fee_deducted_from_returns: -probit_kr_buy_percent_fee_deducted_from_returns: -probit_kr_maker_fixed_fees: -probit_kr_maker_percent_fee: -probit_kr_percent_fee_token: -probit_kr_taker_fixed_fees: -probit_kr_taker_percent_fee: -probit_maker_fixed_fees: -probit_maker_percent_fee: -probit_percent_fee_token: -probit_taker_fixed_fees: -probit_taker_percent_fee: -bitmex_perpetual_percent_fee_token: -bitmex_perpetual_maker_percent_fee: -bitmex_perpetual_taker_percent_fee: -bitmex_perpetual_buy_percent_fee_deducted_from_returns: -bitmex_perpetual_maker_fixed_fees: -bitmex_perpetual_taker_fixed_fees: -bitmex_perpetual_testnet_percent_fee_token: -bitmex_perpetual_testnet_maker_percent_fee: -bitmex_perpetual_testnet_taker_percent_fee: -bitmex_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bitmex_perpetual_testnet_maker_fixed_fees: -bitmex_perpetual_testnet_taker_fixed_fees: -kucoin_perpetual_percent_fee_token: -kucoin_perpetual_maker_percent_fee: -kucoin_perpetual_taker_percent_fee: -kucoin_perpetual_buy_percent_fee_deducted_from_returns: -kucoin_perpetual_maker_fixed_fees: -kucoin_perpetual_taker_fixed_fees: -kucoin_perpetual_testnet_percent_fee_token: -kucoin_perpetual_testnet_maker_percent_fee: -kucoin_perpetual_testnet_taker_percent_fee: -kucoin_perpetual_testnet_buy_percent_fee_deducted_from_returns: -kucoin_perpetual_testnet_maker_fixed_fees: -kucoin_perpetual_testnet_taker_fixed_fees: -gate_io_perpetual_percent_fee_token: -gate_io_perpetual_maker_percent_fee: -gate_io_perpetual_taker_percent_fee: -gate_io_perpetual_buy_percent_fee_deducted_from_returns: -gate_io_perpetual_maker_fixed_fees: -gate_io_perpetual_taker_fixed_fees: -phemex_perpetual_percent_fee_token: -phemex_perpetual_maker_percent_fee: -phemex_perpetual_taker_percent_fee: -phemex_perpetual_buy_percent_fee_deducted_from_returns: -phemex_perpetual_maker_fixed_fees: -phemex_perpetual_taker_fixed_fees: -phemex_perpetual_testnet_percent_fee_token: -phemex_perpetual_testnet_maker_percent_fee: -phemex_perpetual_testnet_taker_percent_fee: -phemex_perpetual_testnet_buy_percent_fee_deducted_from_returns: -phemex_perpetual_testnet_maker_fixed_fees: -phemex_perpetual_testnet_taker_fixed_fees: -bitget_perpetual_percent_fee_token: -bitget_perpetual_maker_percent_fee: -bitget_perpetual_taker_percent_fee: -bitget_perpetual_buy_percent_fee_deducted_from_returns: -bitget_perpetual_maker_fixed_fees: -bitget_perpetual_taker_fixed_fees: -bit_com_perpetual_percent_fee_token: -bit_com_perpetual_maker_percent_fee: -bit_com_perpetual_taker_percent_fee: -bit_com_perpetual_buy_percent_fee_deducted_from_returns: -bit_com_perpetual_maker_fixed_fees: -bit_com_perpetual_taker_fixed_fees: -bit_com_perpetual_testnet_percent_fee_token: -bit_com_perpetual_testnet_maker_percent_fee: -bit_com_perpetual_testnet_taker_percent_fee: -bit_com_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bit_com_perpetual_testnet_maker_fixed_fees: -bit_com_perpetual_testnet_taker_fixed_fees: -whitebit_percent_fee_token: -whitebit_maker_percent_fee: -whitebit_taker_percent_fee: -whitebit_buy_percent_fee_deducted_from_returns: -whitebit_maker_fixed_fees: -whitebit_taker_fixed_fees: -bitmex_percent_fee_token: -bitmex_maker_percent_fee: -bitmex_taker_percent_fee: -bitmex_buy_percent_fee_deducted_from_returns: -bitmex_maker_fixed_fees: -bitmex_taker_fixed_fees: -bitmex_testnet_percent_fee_token: -bitmex_testnet_maker_percent_fee: -bitmex_testnet_taker_percent_fee: -bitmex_testnet_buy_percent_fee_deducted_from_returns: -bitmex_testnet_maker_fixed_fees: -bitmex_testnet_taker_fixed_fees: -ciex_percent_fee_token: -ciex_maker_percent_fee: -ciex_taker_percent_fee: -ciex_buy_percent_fee_deducted_from_returns: -ciex_maker_fixed_fees: -ciex_taker_fixed_fees: -foxbit_percent_fee_token: -foxbit_maker_percent_fee: -foxbit_taker_percent_fee: -foxbit_buy_percent_fee_deducted_from_returns: -foxbit_maker_fixed_fees: -foxbit_taker_fixed_fees: -lbank_percent_fee_token: -lbank_maker_percent_fee: -lbank_taker_percent_fee: -lbank_buy_percent_fee_deducted_from_returns: -lbank_maker_fixed_fees: -lbank_taker_fixed_fees: -bybit_percent_fee_token: -bybit_maker_percent_fee: -bybit_taker_percent_fee: -bybit_buy_percent_fee_deducted_from_returns: -bybit_maker_fixed_fees: -bybit_taker_fixed_fees: -bybit_testnet_percent_fee_token: -bybit_testnet_maker_percent_fee: -bybit_testnet_taker_percent_fee: -bybit_testnet_buy_percent_fee_deducted_from_returns: -bybit_testnet_maker_fixed_fees: -bybit_testnet_taker_fixed_fees: -hotbit_percent_fee_token: -hotbit_maker_percent_fee: -hotbit_taker_percent_fee: -hotbit_buy_percent_fee_deducted_from_returns: -hotbit_maker_fixed_fees: -hotbit_taker_fixed_fees: -btc_markets_maker_fixed_fees: -btc_markets_taker_fixed_fees: -polkadex_percent_fee_token: -polkadex_maker_percent_fee: -polkadex_taker_percent_fee: -polkadex_buy_percent_fee_deducted_from_returns: -polkadex_maker_fixed_fees: -polkadex_taker_fixed_fees: -woo_x_percent_fee_token: -woo_x_maker_percent_fee: -woo_x_taker_percent_fee: -woo_x_buy_percent_fee_deducted_from_returns: -woo_x_maker_fixed_fees: -woo_x_taker_fixed_fees: -woo_x_testnet_percent_fee_token: -woo_x_testnet_maker_percent_fee: -woo_x_testnet_taker_percent_fee: -woo_x_testnet_buy_percent_fee_deducted_from_returns: -woo_x_testnet_maker_fixed_fees: -woo_x_testnet_taker_fixed_fees: -vertex_percent_fee_token: -vertex_maker_percent_fee: -vertex_taker_percent_fee: -vertex_buy_percent_fee_deducted_from_returns: -vertex_maker_fixed_fees: -vertex_taker_fixed_fees: -vertex_testnet_percent_fee_token: -vertex_testnet_maker_percent_fee: -vertex_testnet_taker_percent_fee: -vertex_testnet_buy_percent_fee_deducted_from_returns: -vertex_testnet_maker_fixed_fees: -vertex_testnet_taker_fixed_fees: -injective_v2_percent_fee_token: -injective_v2_maker_percent_fee: -injective_v2_taker_percent_fee: -injective_v2_buy_percent_fee_deducted_from_returns: -injective_v2_maker_fixed_fees: -injective_v2_taker_fixed_fees: -injective_v2_perpetual_percent_fee_token: -injective_v2_perpetual_maker_percent_fee: -injective_v2_perpetual_taker_percent_fee: -injective_v2_perpetual_buy_percent_fee_deducted_from_returns: -injective_v2_perpetual_maker_fixed_fees: -injective_v2_perpetual_taker_fixed_fees: diff --git a/hummingbot_files/bots/data_downloader/conf/hummingbot_logs.yml b/hummingbot_files/bots/data_downloader/conf/hummingbot_logs.yml deleted file mode 100755 index 8e65271..0000000 --- a/hummingbot_files/bots/data_downloader/conf/hummingbot_logs.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -version: 1 -template_version: 12 - -formatters: - simple: - format: "%(asctime)s - %(process)d - %(name)s - %(levelname)s - %(message)s" - -handlers: - console: - class: hummingbot.logger.cli_handler.CLIHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - console_warning: - class: hummingbot.logger.cli_handler.CLIHandler - level: WARNING - formatter: simple - stream: ext://sys.stdout - console_info: - class: hummingbot.logger.cli_handler.CLIHandler - level: INFO - formatter: simple - stream: ext://sys.stdout - file_handler: - class: logging.handlers.TimedRotatingFileHandler - level: DEBUG - formatter: simple - filename: $PROJECT_DIR/logs/logs_$STRATEGY_FILE_PATH.log - encoding: utf8 - when: "D" - interval: 1 - backupCount: 7 - "null": - class: logging.NullHandler - level: DEBUG - -loggers: - hummingbot.core.utils.eth_gas_station_lookup: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.logger.log_server_client: - level: WARNING - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.logger.reporting_proxy_handler: - level: WARNING - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.strategy: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.connector: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.client: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.core.event.event_reporter: - level: EVENT_LOG - propagate: false - handlers: [file_handler] - mqtt: false - conf: - level: NETWORK - handlers: ["null"] - propagate: false - mqtt: false - -root: - level: INFO - handlers: [console, file_handler] - mqtt: true diff --git a/hummingbot_files/bots/data_downloader/conf_client.yml b/hummingbot_files/bots/data_downloader/conf_client.yml deleted file mode 100644 index 5bc6b61..0000000 --- a/hummingbot_files/bots/data_downloader/conf_client.yml +++ /dev/null @@ -1,194 +0,0 @@ -#################################### -### client_config_map config ### -#################################### - -instance_id: e90c0d6f2b1e2d54fa0c0a69612b07174320963b - -log_level: INFO - -debug_console: false - -strategy_report_interval: 900.0 - -logger_override_whitelist: -- hummingbot.strategy.arbitrage -- hummingbot.strategy.cross_exchange_market_making -- conf - -log_file_path: /home/hummingbot/logs - -kill_switch_mode: {} - -# What to auto-fill in the prompt after each import command (start/config) -autofill_import: disabled - -telegram_mode: {} - -# MQTT Bridge configuration. -mqtt_bridge: - mqtt_host: localhost - mqtt_port: 1883 - mqtt_username: '' - mqtt_password: '' - mqtt_namespace: hbot - mqtt_ssl: false - mqtt_logger: true - mqtt_notifier: true - mqtt_commands: true - mqtt_events: true - mqtt_external_events: true - mqtt_autostart: true - -# Error log sharing -send_error_logs: true - -# Can store the previous strategy ran for quick retrieval. -previous_strategy: null - -# Advanced database options, currently supports SQLAlchemy's included dialects -# Reference: https://docs.sqlalchemy.org/en/13/dialects/ -# To use an instance of SQLite DB the required configuration is -# db_engine: sqlite -# To use a DBMS the required configuration is -# db_host: 127.0.0.1 -# db_port: 3306 -# db_username: username -# db_password: password -# db_name: dbname -db_mode: - db_engine: sqlite - -pmm_script_mode: {} - -# Balance Limit Configurations -# e.g. Setting USDT and BTC limits on Binance. -# balance_asset_limit: -# binance: -# BTC: 0.1 -# USDT: 1000 -balance_asset_limit: - kucoin: {} - ciex: {} - ascend_ex_paper_trade: {} - crypto_com: {} - mock_paper_exchange: {} - btc_markets: {} - bitmart: {} - hitbtc: {} - loopring: {} - mexc: {} - polkadex: {} - bybit: {} - foxbit: {} - gate_io_paper_trade: {} - kucoin_paper_trade: {} - altmarkets: {} - ascend_ex: {} - bittrex: {} - probit_kr: {} - binance: {} - bybit_testnet: {} - okx: {} - bitmex: {} - binance_us: {} - probit: {} - gate_io: {} - lbank: {} - whitebit: {} - bitmex_testnet: {} - kraken: {} - huobi: {} - binance_paper_trade: {} - ndax_testnet: {} - coinbase_pro: {} - ndax: {} - bitfinex: {} - -# Fixed gas price (in Gwei) for Ethereum transactions -manual_gas_price: 50.0 - -# Gateway API Configurations -# default host to only use localhost -# Port need to match the final installation port for Gateway -gateway: - gateway_api_host: localhost - gateway_api_port: '15888' - -certs_path: /home/hummingbot/certs - -# Whether to enable aggregated order and trade data collection -anonymized_metrics_mode: - anonymized_metrics_interval_min: 15.0 - -# Command Shortcuts -# Define abbreviations for often used commands -# or batch grouped commands together -command_shortcuts: -- command: spreads - help: Set bid and ask spread - arguments: - - Bid Spread - - Ask Spread - output: - - config bid_spread $1 - - config ask_spread $2 - -# A source for rate oracle, currently ascend_ex, binance, coin_gecko, kucoin, gate_io -rate_oracle_source: - name: binance - -# A universal token which to display tokens values in, e.g. USD,EUR,BTC -global_token: - global_token_name: USD - global_token_symbol: $ - -# Percentage of API rate limits (on any exchange and any end point) allocated to this bot instance. -# Enter 50 to indicate 50%. E.g. if the API rate limit is 100 calls per second, and you allocate -# 50% to this setting, the bot will have a maximum (limit) of 50 calls per second -rate_limits_share_pct: 100.0 - -commands_timeout: - create_command_timeout: 10.0 - other_commands_timeout: 30.0 - -# Tabulate table format style (https://github.com/astanin/python-tabulate#table-format) -tables_format: psql - -paper_trade: - paper_trade_exchanges: - - binance - - kucoin - - ascend_ex - - gate_io - paper_trade_account_balance: - BTC: 1.0 - USDT: 1000.0 - ONE: 1000.0 - USDQ: 1000.0 - TUSD: 1000.0 - ETH: 10.0 - WETH: 10.0 - USDC: 1000.0 - DAI: 1000.0 - -color: - top_pane: '#000000' - bottom_pane: '#000000' - output_pane: '#262626' - input_pane: '#1C1C1C' - logs_pane: '#121212' - terminal_primary: '#5FFFD7' - primary_label: '#5FFFD7' - secondary_label: '#FFFFFF' - success_label: '#5FFFD7' - warning_label: '#FFFF00' - info_label: '#5FD7FF' - error_label: '#FF0000' - gold_label: '#FFD700' - silver_label: '#C0C0C0' - bronze_label: '#CD7F32' - -# The tick size is the frequency with which the clock notifies the time iterators by calling the -# c_tick() method, that means for example that if the tick size is 1, the logic of the strategy -# will run every second. -tick_size: 1.0 diff --git a/hummingbot_files/bots/data_downloader/scripts/download_candles.py b/hummingbot_files/bots/data_downloader/scripts/download_candles.py deleted file mode 100644 index bfb5d10..0000000 --- a/hummingbot_files/bots/data_downloader/scripts/download_candles.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -from typing import Dict - -from hummingbot import data_path -from hummingbot.client.hummingbot_application import HummingbotApplication -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig, CandlesFactory -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DownloadCandles(ScriptStrategyBase): - """ - This script provides an example of how to use the Candles Feed to download and store historical data. - It downloads 3-minute candles for 3 Binance trading pairs ["APE-USDT", "BTC-USDT", "BNB-USDT"] and stores them in - CSV files in the /data directory. The script stops after it has downloaded 50,000 max_records records for each pair. - Is important to notice that the component will fail if all the candles are not available since the idea of it is to - use it in production based on candles needed to compute technical indicators. - """ - exchange = os.getenv("EXCHANGE", "binance_perpetual") - trading_pairs = os.getenv("TRADING_PAIRS", "DODO-BUSD,LTC-USDT").split(",") - intervals = os.getenv("INTERVALS", "1m,3m,5m,1h").split(",") - days_to_download = int(os.getenv("DAYS_TO_DOWNLOAD", "3")) - # we can initialize any trading pair since we only need the candles - markets = {"binance_paper_trade": {"BTC-USDT"}} - - @staticmethod - 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])) - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - combinations = [(trading_pair, interval) for trading_pair in self.trading_pairs for interval in self.intervals] - - self.candles = {f"{combinations[0]}_{combinations[1]}": {} for combinations in combinations} - # we need to initialize the candles for each trading pair - for combination in combinations: - - candle = CandlesFactory.get_candle(CandlesConfig(connector=self.exchange, trading_pair=combination[0], interval=combination[1], max_records=self.get_max_records(self.days_to_download, combination[1]))) - candle.start() - # we are storing the candles object and the csv path to save the candles - self.candles[f"{combination[0]}_{combination[1]}"]["candles"] = candle - self.candles[f"{combination[0]}_{combination[1]}"][ - "csv_path"] = data_path() + f"/candles_{self.exchange}_{combination[0]}_{combination[1]}.csv" - - def on_tick(self): - for trading_pair, candles_info in self.candles.items(): - if not candles_info["candles"].ready: - self.logger().info(f"Candles not ready yet for {trading_pair}! Missing {candles_info['candles']._candles.maxlen - len(candles_info['candles']._candles)}") - pass - else: - df = candles_info["candles"].candles_df - df.to_csv(candles_info["csv_path"], index=False) - if all(candles_info["candles"].ready for candles_info in self.candles.values()): - HummingbotApplication.main_application().stop() - - def on_stop(self): - for candles_info in self.candles.values(): - candles_info["candles"].stop() diff --git a/hummingbot_files/compose_files/broker-compose.yml b/hummingbot_files/compose_files/broker-compose.yml deleted file mode 100644 index e4aad77..0000000 --- a/hummingbot_files/compose_files/broker-compose.yml +++ /dev/null @@ -1,43 +0,0 @@ -version: '3.9' - -services: - emqx: - container_name: hummingbot-broker - image: emqx:5 - restart: unless-stopped - environment: - - EMQX_NAME=emqx - - EMQX_HOST=node1.emqx.local - - EMQX_CLUSTER__DISCOVERY_STRATEGY=static - - EMQX_CLUSTER__STATIC__SEEDS=[emqx@node1.emqx.local] - - EMQX_LOADED_PLUGINS="emqx_recon,emqx_retainer,emqx_management,emqx_dashboard" - volumes: - - emqx-data:/opt/emqx/data - - emqx-log:/opt/emqx/log - - emqx-etc:/opt/emqx/etc - ports: - - "1883:1883" # mqtt:tcp - - "8883:8883" # mqtt:tcp:ssl - - "8083:8083" # mqtt:ws - - "8084:8084" # mqtt:ws:ssl - - "8081:8081" # http:management - - "18083:18083" # http:dashboard - - "61613:61613" # web-stomp gateway - networks: - emqx-bridge: - aliases: - - node1.emqx.local - healthcheck: - test: [ "CMD", "/opt/emqx/bin/emqx_ctl", "status" ] - interval: 5s - timeout: 25s - retries: 5 - -networks: - emqx-bridge: - driver: bridge - -volumes: - emqx-data: { } - emqx-log: { } - emqx-etc: { } \ No newline at end of file diff --git a/hummingbot_files/compose_files/data-downloader-compose.yml b/hummingbot_files/compose_files/data-downloader-compose.yml deleted file mode 100644 index 468220b..0000000 --- a/hummingbot_files/compose_files/data-downloader-compose.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: "3.9" -services: - bot: - container_name: candles_downloader - image: hummingbot/hummingbot:development - volumes: - - "../../data/candles:/home/hummingbot/data" - - "../bots/data_downloader/conf:/home/hummingbot/conf" - - "../bots/data_downloader/conf/connectors:/home/hummingbot/conf/connectors" - - "../bots/data_downloader/scripts:/home/hummingbot/scripts" - environment: - - CONFIG_PASSWORD=a - - CONFIG_FILE_NAME=download_candles.py - env_file: - - ../scripts_configs/data_downloader_config.yml - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: 5 - tty: true - stdin_open: true - network_mode: host diff --git a/hummingbot_files/controller_configs/.gitignore b/hummingbot_files/controller_configs/.gitignore deleted file mode 100644 index 42780ec..0000000 --- a/hummingbot_files/controller_configs/.gitignore +++ /dev/null @@ -1 +0,0 @@ - * \ No newline at end of file diff --git a/hummingbot_files/scripts_configs/data_downloader_config.yml b/hummingbot_files/scripts_configs/data_downloader_config.yml deleted file mode 100644 index 7fefb6e..0000000 --- a/hummingbot_files/scripts_configs/data_downloader_config.yml +++ /dev/null @@ -1,4 +0,0 @@ -DAYS_TO_DOWNLOAD: 30 -EXCHANGE: binance_perpetual -INTERVALS: 1m,3m,1h -TRADING_PAIRS: BTC-USDT,ETH-USDT diff --git a/hummingbot_files/templates/master_bot_conf/conf/.password_verification b/hummingbot_files/templates/master_bot_conf/conf/.password_verification deleted file mode 100644 index b8c7618..0000000 --- a/hummingbot_files/templates/master_bot_conf/conf/.password_verification +++ /dev/null @@ -1 +0,0 @@ -7b2263727970746f223a207b22636970686572223a20226165732d3132382d637472222c2022636970686572706172616d73223a207b226976223a20223864336365306436393461623131396334363135663935366464653839363063227d2c202263697068657274657874223a20223836333266323430613563306131623665353664222c20226b6466223a202270626b646632222c20226b6466706172616d73223a207b2263223a20313030303030302c2022646b6c656e223a2033322c2022707266223a2022686d61632d736861323536222c202273616c74223a20226566373330376531636464373964376132303338323534656139343433663930227d2c20226d6163223a202266393439383534613530633138363633386363353962336133363665633962353333386633613964373266636635343066313034333361353431636232306438227d2c202276657273696f6e223a20337d \ No newline at end of file diff --git a/hummingbot_files/templates/master_bot_conf/conf/conf_client.yml b/hummingbot_files/templates/master_bot_conf/conf/conf_client.yml deleted file mode 100644 index 9e2b6e1..0000000 --- a/hummingbot_files/templates/master_bot_conf/conf/conf_client.yml +++ /dev/null @@ -1,199 +0,0 @@ -#################################### -### client_config_map config ### -#################################### - -instance_id: hummingbot-master_bot_conf - -log_level: INFO - -debug_console: false - -strategy_report_interval: 900.0 - -logger_override_whitelist: -- hummingbot.strategy.arbitrage -- hummingbot.strategy.cross_exchange_market_making -- conf - -log_file_path: /home/hummingbot/logs - -kill_switch_mode: {} - -# What to auto-fill in the prompt after each import command (start/config) -autofill_import: disabled - -telegram_mode: {} - -# MQTT Bridge configuration. -mqtt_bridge: - mqtt_host: localhost - mqtt_port: 1883 - mqtt_username: '' - mqtt_password: '' - mqtt_namespace: hbot - mqtt_ssl: false - mqtt_logger: true - mqtt_notifier: true - mqtt_commands: true - mqtt_events: true - mqtt_external_events: true - mqtt_autostart: true - -# Error log sharing -send_error_logs: true - -# Can store the previous strategy ran for quick retrieval. -previous_strategy: null - -# Advanced database options, currently supports SQLAlchemy's included dialects -# Reference: https://docs.sqlalchemy.org/en/13/dialects/ -# To use an instance of SQLite DB the required configuration is -# db_engine: sqlite -# To use a DBMS the required configuration is -# db_host: 127.0.0.1 -# db_port: 3306 -# db_username: username -# db_password: password -# db_name: dbname -db_mode: - db_engine: sqlite - -pmm_script_mode: {} - -# Balance Limit Configurations -# e.g. Setting USDT and BTC limits on Binance. -# balance_asset_limit: -# binance: -# BTC: 0.1 -# USDT: 1000 -balance_asset_limit: - altmarkets: {} - ascend_ex: {} - ascend_ex_paper_trade: {} - binance: {} - binance_paper_trade: {} - binance_us: {} - bitfinex: {} - bitmart: {} - bitmex: {} - bitmex_testnet: {} - bittrex: {} - btc_markets: {} - bybit: {} - bybit_testnet: {} - ciex: {} - coinbase_pro: {} - crypto_com: {} - foxbit: {} - gate_io: {} - gate_io_paper_trade: {} - hitbtc: {} - hotbit: {} - huobi: {} - kraken: {} - kucoin: {} - kucoin_paper_trade: {} - lbank: {} - loopring: {} - mexc: {} - mock_paper_exchange: {} - ndax: {} - ndax_testnet: {} - okx: {} - probit: {} - probit_kr: {} - whitebit: {} - -# Fixed gas price (in Gwei) for Ethereum transactions -manual_gas_price: 50.0 - -# Gateway API Configurations -# default host to only use localhost -# Port need to match the final installation port for Gateway -gateway: - gateway_api_host: localhost - gateway_api_port: '15888' - -certs_path: /home/hummingbot/certs - -# Whether to enable aggregated order and trade data collection -anonymized_metrics_mode: - anonymized_metrics_interval_min: 15.0 - -# Command Shortcuts -# Define abbreviations for often used commands -# or batch grouped commands together -command_shortcuts: -- command: spreads - help: Set bid and ask spread - arguments: - - Bid Spread - - Ask Spread - output: - - config bid_spread $1 - - config ask_spread $2 - -# A source for rate oracle, currently ascend_ex, binance, coin_gecko, coin_cap, kucoin, gate_io -rate_oracle_source: - name: binance - -# A universal token which to display tokens values in, e.g. USD,EUR,BTC -global_token: - global_token_name: USD - global_token_symbol: $ - -# Percentage of API rate limits (on any exchange and any end point) allocated to this bot instance. -# Enter 50 to indicate 50%. E.g. if the API rate limit is 100 calls per second, and you allocate -# 50% to this setting, the bot will have a maximum (limit) of 50 calls per second -rate_limits_share_pct: 100.0 - -commands_timeout: - create_command_timeout: 10.0 - other_commands_timeout: 30.0 - -# Tabulate table format style (https://github.com/astanin/python-tabulate#table-format) -tables_format: psql - -paper_trade: - paper_trade_exchanges: - - binance - - kucoin - - ascend_ex - - gate_io - paper_trade_account_balance: - BTC: 1.0 - DAI: 1000.0 - ETH: 10.0 - ONE: 1000.0 - TUSD: 1000.0 - USDC: 1000.0 - USDQ: 1000.0 - USDT: 1000.0 - WETH: 10.0 - -color: - top_pane: '#000000' - bottom_pane: '#000000' - output_pane: '#262626' - input_pane: '#1C1C1C' - logs_pane: '#121212' - terminal_primary: '#5FFFD7' - primary_label: '#5FFFD7' - secondary_label: '#FFFFFF' - success_label: '#5FFFD7' - warning_label: '#FFFF00' - info_label: '#5FD7FF' - error_label: '#FF0000' - gold_label: '#FFD700' - silver_label: '#C0C0C0' - bronze_label: '#CD7F32' - -# The tick size is the frequency with which the clock notifies the time iterators by calling the -# c_tick() method, that means for example that if the tick size is 1, the logic of the strategy -# will run every second. -tick_size: 1.0 - -market_data_collection: - market_data_collection_enabled: true - market_data_collection_interval: 60 - market_data_collection_depth: 20 diff --git a/hummingbot_files/templates/master_bot_conf/conf/conf_fee_overrides.yml b/hummingbot_files/templates/master_bot_conf/conf/conf_fee_overrides.yml deleted file mode 100644 index 8648bb5..0000000 --- a/hummingbot_files/templates/master_bot_conf/conf/conf_fee_overrides.yml +++ /dev/null @@ -1,340 +0,0 @@ -######################################## -### Fee overrides configurations ### -######################################## - -# For more detailed information: https://docs.hummingbot.io -template_version: 14 - -# Example of the fields that can be specified to override the `TradeFeeFactory` default settings. -# If the field is missing or the value is left blank, the default value will be used. -# The percentage values are specified as 0.1 for 0.1%. -# -# [exchange name]_percent_fee_token: -# [exchange name]_maker_percent_fee: -# [exchange name]_taker_percent_fee: -# [exchange name]_buy_percent_fee_deducted_from_returns: # if False, the buy fee is added to the order costs -# [exchange name]_maker_fixed_fees: # a list of lists of token-fee pairs (e.g. [["ETH", 1]]) -# [exchange name]_taker_fixed_fees: # a list of lists of token-fee pairs (e.g. [["ETH", 1]]) - -binance_percent_fee_token: # BNB -binance_maker_percent_fee: # 0.75 -binance_taker_percent_fee: # 0.75 -binance_buy_percent_fee_deducted_from_returns: # True - -# List of supported Exchanges for which the user's data_downloader/conf_fee_override.yml -# will work. This file currently needs to be in sync with hummingbot list of -# supported exchanges -altmarkets_buy_percent_fee_deducted_from_returns: -altmarkets_maker_fixed_fees: -altmarkets_maker_percent_fee: -altmarkets_percent_fee_token: -altmarkets_taker_fixed_fees: -altmarkets_taker_percent_fee: -ascend_ex_buy_percent_fee_deducted_from_returns: -ascend_ex_maker_fixed_fees: -ascend_ex_maker_percent_fee: -ascend_ex_percent_fee_token: -ascend_ex_taker_fixed_fees: -ascend_ex_taker_percent_fee: -binance_maker_fixed_fees: -binance_perpetual_buy_percent_fee_deducted_from_returns: -binance_perpetual_maker_fixed_fees: -binance_perpetual_maker_percent_fee: -binance_perpetual_percent_fee_token: -binance_perpetual_taker_fixed_fees: -binance_perpetual_taker_percent_fee: -binance_perpetual_testnet_buy_percent_fee_deducted_from_returns: -binance_perpetual_testnet_maker_fixed_fees: -binance_perpetual_testnet_maker_percent_fee: -binance_perpetual_testnet_percent_fee_token: -binance_perpetual_testnet_taker_fixed_fees: -binance_perpetual_testnet_taker_percent_fee: -binance_taker_fixed_fees: -binance_us_buy_percent_fee_deducted_from_returns: -binance_us_maker_fixed_fees: -binance_us_maker_percent_fee: -binance_us_percent_fee_token: -binance_us_taker_fixed_fees: -binance_us_taker_percent_fee: -bitfinex_buy_percent_fee_deducted_from_returns: -bitfinex_maker_fixed_fees: -bitfinex_maker_percent_fee: -bitfinex_percent_fee_token: -bitfinex_taker_fixed_fees: -bitfinex_taker_percent_fee: -bitmart_buy_percent_fee_deducted_from_returns: -bitmart_maker_fixed_fees: -bitmart_maker_percent_fee: -bitmart_percent_fee_token: -bitmart_taker_fixed_fees: -bitmart_taker_percent_fee: -bittrex_buy_percent_fee_deducted_from_returns: -bittrex_maker_fixed_fees: -bittrex_maker_percent_fee: -bittrex_percent_fee_token: -bittrex_taker_fixed_fees: -bittrex_taker_percent_fee: -btc_markets_percent_fee_token: -btc_markets_maker_percent_fee: -btc_markets_taker_percent_fee: -btc_markets_buy_percent_fee_deducted_from_returns: -bybit_perpetual_buy_percent_fee_deducted_from_returns: -bybit_perpetual_maker_fixed_fees: -bybit_perpetual_maker_percent_fee: -bybit_perpetual_percent_fee_token: -bybit_perpetual_taker_fixed_fees: -bybit_perpetual_taker_percent_fee: -bybit_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bybit_perpetual_testnet_maker_fixed_fees: -bybit_perpetual_testnet_maker_percent_fee: -bybit_perpetual_testnet_percent_fee_token: -bybit_perpetual_testnet_taker_fixed_fees: -bybit_perpetual_testnet_taker_percent_fee: -coinbase_pro_buy_percent_fee_deducted_from_returns: -coinbase_pro_maker_fixed_fees: -coinbase_pro_maker_percent_fee: -coinbase_pro_percent_fee_token: -coinbase_pro_taker_fixed_fees: -coinbase_pro_taker_percent_fee: -crypto_com_buy_percent_fee_deducted_from_returns: -crypto_com_maker_fixed_fees: -crypto_com_maker_percent_fee: -crypto_com_percent_fee_token: -crypto_com_taker_fixed_fees: -crypto_com_taker_percent_fee: -dydx_perpetual_buy_percent_fee_deducted_from_returns: -dydx_perpetual_maker_fixed_fees: -dydx_perpetual_maker_percent_fee: -dydx_perpetual_percent_fee_token: -dydx_perpetual_taker_fixed_fees: -dydx_perpetual_taker_percent_fee: -gate_io_buy_percent_fee_deducted_from_returns: -gate_io_maker_fixed_fees: -gate_io_maker_percent_fee: -gate_io_percent_fee_token: -gate_io_taker_fixed_fees: -gate_io_taker_percent_fee: -hitbtc_buy_percent_fee_deducted_from_returns: -hitbtc_maker_fixed_fees: -hitbtc_maker_percent_fee: -hitbtc_percent_fee_token: -hitbtc_taker_fixed_fees: -hitbtc_taker_percent_fee: -huobi_buy_percent_fee_deducted_from_returns: -huobi_maker_fixed_fees: -huobi_maker_percent_fee: -huobi_percent_fee_token: -huobi_taker_fixed_fees: -huobi_taker_percent_fee: -kraken_buy_percent_fee_deducted_from_returns: -kraken_maker_fixed_fees: -kraken_maker_percent_fee: -kraken_percent_fee_token: -kraken_taker_fixed_fees: -kraken_taker_percent_fee: -kucoin_buy_percent_fee_deducted_from_returns: -kucoin_maker_fixed_fees: -kucoin_maker_percent_fee: -kucoin_percent_fee_token: -kucoin_taker_fixed_fees: -kucoin_taker_percent_fee: -loopring_buy_percent_fee_deducted_from_returns: -loopring_maker_fixed_fees: -loopring_maker_percent_fee: -loopring_percent_fee_token: -loopring_taker_fixed_fees: -loopring_taker_percent_fee: -mexc_buy_percent_fee_deducted_from_returns: -mexc_maker_fixed_fees: -mexc_maker_percent_fee: -mexc_percent_fee_token: -mexc_taker_fixed_fees: -mexc_taker_percent_fee: -ndax_buy_percent_fee_deducted_from_returns: -ndax_maker_fixed_fees: -ndax_maker_percent_fee: -ndax_percent_fee_token: -ndax_taker_fixed_fees: -ndax_taker_percent_fee: -ndax_testnet_buy_percent_fee_deducted_from_returns: -ndax_testnet_maker_fixed_fees: -ndax_testnet_maker_percent_fee: -ndax_testnet_percent_fee_token: -ndax_testnet_taker_fixed_fees: -ndax_testnet_taker_percent_fee: -okx_buy_percent_fee_deducted_from_returns: -okx_maker_fixed_fees: -okx_maker_percent_fee: -okx_percent_fee_token: -okx_taker_fixed_fees: -okx_taker_percent_fee: -probit_buy_percent_fee_deducted_from_returns: -probit_kr_buy_percent_fee_deducted_from_returns: -probit_kr_maker_fixed_fees: -probit_kr_maker_percent_fee: -probit_kr_percent_fee_token: -probit_kr_taker_fixed_fees: -probit_kr_taker_percent_fee: -probit_maker_fixed_fees: -probit_maker_percent_fee: -probit_percent_fee_token: -probit_taker_fixed_fees: -probit_taker_percent_fee: -bitmex_perpetual_percent_fee_token: -bitmex_perpetual_maker_percent_fee: -bitmex_perpetual_taker_percent_fee: -bitmex_perpetual_buy_percent_fee_deducted_from_returns: -bitmex_perpetual_maker_fixed_fees: -bitmex_perpetual_taker_fixed_fees: -bitmex_perpetual_testnet_percent_fee_token: -bitmex_perpetual_testnet_maker_percent_fee: -bitmex_perpetual_testnet_taker_percent_fee: -bitmex_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bitmex_perpetual_testnet_maker_fixed_fees: -bitmex_perpetual_testnet_taker_fixed_fees: -kucoin_perpetual_percent_fee_token: -kucoin_perpetual_maker_percent_fee: -kucoin_perpetual_taker_percent_fee: -kucoin_perpetual_buy_percent_fee_deducted_from_returns: -kucoin_perpetual_maker_fixed_fees: -kucoin_perpetual_taker_fixed_fees: -kucoin_perpetual_testnet_percent_fee_token: -kucoin_perpetual_testnet_maker_percent_fee: -kucoin_perpetual_testnet_taker_percent_fee: -kucoin_perpetual_testnet_buy_percent_fee_deducted_from_returns: -kucoin_perpetual_testnet_maker_fixed_fees: -kucoin_perpetual_testnet_taker_fixed_fees: -gate_io_perpetual_percent_fee_token: -gate_io_perpetual_maker_percent_fee: -gate_io_perpetual_taker_percent_fee: -gate_io_perpetual_buy_percent_fee_deducted_from_returns: -gate_io_perpetual_maker_fixed_fees: -gate_io_perpetual_taker_fixed_fees: -phemex_perpetual_percent_fee_token: -phemex_perpetual_maker_percent_fee: -phemex_perpetual_taker_percent_fee: -phemex_perpetual_buy_percent_fee_deducted_from_returns: -phemex_perpetual_maker_fixed_fees: -phemex_perpetual_taker_fixed_fees: -phemex_perpetual_testnet_percent_fee_token: -phemex_perpetual_testnet_maker_percent_fee: -phemex_perpetual_testnet_taker_percent_fee: -phemex_perpetual_testnet_buy_percent_fee_deducted_from_returns: -phemex_perpetual_testnet_maker_fixed_fees: -phemex_perpetual_testnet_taker_fixed_fees: -bitget_perpetual_percent_fee_token: -bitget_perpetual_maker_percent_fee: -bitget_perpetual_taker_percent_fee: -bitget_perpetual_buy_percent_fee_deducted_from_returns: -bitget_perpetual_maker_fixed_fees: -bitget_perpetual_taker_fixed_fees: -bit_com_perpetual_percent_fee_token: -bit_com_perpetual_maker_percent_fee: -bit_com_perpetual_taker_percent_fee: -bit_com_perpetual_buy_percent_fee_deducted_from_returns: -bit_com_perpetual_maker_fixed_fees: -bit_com_perpetual_taker_fixed_fees: -bit_com_perpetual_testnet_percent_fee_token: -bit_com_perpetual_testnet_maker_percent_fee: -bit_com_perpetual_testnet_taker_percent_fee: -bit_com_perpetual_testnet_buy_percent_fee_deducted_from_returns: -bit_com_perpetual_testnet_maker_fixed_fees: -bit_com_perpetual_testnet_taker_fixed_fees: -whitebit_percent_fee_token: -whitebit_maker_percent_fee: -whitebit_taker_percent_fee: -whitebit_buy_percent_fee_deducted_from_returns: -whitebit_maker_fixed_fees: -whitebit_taker_fixed_fees: -bitmex_percent_fee_token: -bitmex_maker_percent_fee: -bitmex_taker_percent_fee: -bitmex_buy_percent_fee_deducted_from_returns: -bitmex_maker_fixed_fees: -bitmex_taker_fixed_fees: -bitmex_testnet_percent_fee_token: -bitmex_testnet_maker_percent_fee: -bitmex_testnet_taker_percent_fee: -bitmex_testnet_buy_percent_fee_deducted_from_returns: -bitmex_testnet_maker_fixed_fees: -bitmex_testnet_taker_fixed_fees: -ciex_percent_fee_token: -ciex_maker_percent_fee: -ciex_taker_percent_fee: -ciex_buy_percent_fee_deducted_from_returns: -ciex_maker_fixed_fees: -ciex_taker_fixed_fees: -foxbit_percent_fee_token: -foxbit_maker_percent_fee: -foxbit_taker_percent_fee: -foxbit_buy_percent_fee_deducted_from_returns: -foxbit_maker_fixed_fees: -foxbit_taker_fixed_fees: -lbank_percent_fee_token: -lbank_maker_percent_fee: -lbank_taker_percent_fee: -lbank_buy_percent_fee_deducted_from_returns: -lbank_maker_fixed_fees: -lbank_taker_fixed_fees: -bybit_percent_fee_token: -bybit_maker_percent_fee: -bybit_taker_percent_fee: -bybit_buy_percent_fee_deducted_from_returns: -bybit_maker_fixed_fees: -bybit_taker_fixed_fees: -bybit_testnet_percent_fee_token: -bybit_testnet_maker_percent_fee: -bybit_testnet_taker_percent_fee: -bybit_testnet_buy_percent_fee_deducted_from_returns: -bybit_testnet_maker_fixed_fees: -bybit_testnet_taker_fixed_fees: -hotbit_percent_fee_token: -hotbit_maker_percent_fee: -hotbit_taker_percent_fee: -hotbit_buy_percent_fee_deducted_from_returns: -hotbit_maker_fixed_fees: -hotbit_taker_fixed_fees: -btc_markets_maker_fixed_fees: -btc_markets_taker_fixed_fees: -polkadex_percent_fee_token: -polkadex_maker_percent_fee: -polkadex_taker_percent_fee: -polkadex_buy_percent_fee_deducted_from_returns: -polkadex_maker_fixed_fees: -polkadex_taker_fixed_fees: -vertex_percent_fee_token: -vertex_maker_percent_fee: -vertex_taker_percent_fee: -vertex_buy_percent_fee_deducted_from_returns: -vertex_maker_fixed_fees: -vertex_taker_fixed_fees: -vertex_testnet_percent_fee_token: -vertex_testnet_maker_percent_fee: -vertex_testnet_taker_percent_fee: -vertex_testnet_buy_percent_fee_deducted_from_returns: -vertex_testnet_maker_fixed_fees: -vertex_testnet_taker_fixed_fees: -woo_x_percent_fee_token: -woo_x_maker_percent_fee: -woo_x_taker_percent_fee: -woo_x_buy_percent_fee_deducted_from_returns: -woo_x_maker_fixed_fees: -woo_x_taker_fixed_fees: -woo_x_testnet_percent_fee_token: -woo_x_testnet_maker_percent_fee: -woo_x_testnet_taker_percent_fee: -woo_x_testnet_buy_percent_fee_deducted_from_returns: -woo_x_testnet_maker_fixed_fees: -woo_x_testnet_taker_fixed_fees: -injective_v2_percent_fee_token: -injective_v2_maker_percent_fee: -injective_v2_taker_percent_fee: -injective_v2_buy_percent_fee_deducted_from_returns: -injective_v2_maker_fixed_fees: -injective_v2_taker_fixed_fees: -injective_v2_perpetual_percent_fee_token: -injective_v2_perpetual_maker_percent_fee: -injective_v2_perpetual_taker_percent_fee: -injective_v2_perpetual_buy_percent_fee_deducted_from_returns: -injective_v2_perpetual_maker_fixed_fees: -injective_v2_perpetual_taker_fixed_fees: diff --git a/hummingbot_files/templates/master_bot_conf/conf/hummingbot_logs.yml b/hummingbot_files/templates/master_bot_conf/conf/hummingbot_logs.yml deleted file mode 100755 index 8e65271..0000000 --- a/hummingbot_files/templates/master_bot_conf/conf/hummingbot_logs.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -version: 1 -template_version: 12 - -formatters: - simple: - format: "%(asctime)s - %(process)d - %(name)s - %(levelname)s - %(message)s" - -handlers: - console: - class: hummingbot.logger.cli_handler.CLIHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - console_warning: - class: hummingbot.logger.cli_handler.CLIHandler - level: WARNING - formatter: simple - stream: ext://sys.stdout - console_info: - class: hummingbot.logger.cli_handler.CLIHandler - level: INFO - formatter: simple - stream: ext://sys.stdout - file_handler: - class: logging.handlers.TimedRotatingFileHandler - level: DEBUG - formatter: simple - filename: $PROJECT_DIR/logs/logs_$STRATEGY_FILE_PATH.log - encoding: utf8 - when: "D" - interval: 1 - backupCount: 7 - "null": - class: logging.NullHandler - level: DEBUG - -loggers: - hummingbot.core.utils.eth_gas_station_lookup: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.logger.log_server_client: - level: WARNING - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.logger.reporting_proxy_handler: - level: WARNING - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.strategy: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.connector: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.client: - level: NETWORK - propagate: false - handlers: [console, file_handler] - mqtt: true - hummingbot.core.event.event_reporter: - level: EVENT_LOG - propagate: false - handlers: [file_handler] - mqtt: false - conf: - level: NETWORK - handlers: ["null"] - propagate: false - mqtt: false - -root: - level: INFO - handlers: [console, file_handler] - mqtt: true diff --git a/hummingbot_files/templates/master_bot_conf/scripts/download_candles.py b/hummingbot_files/templates/master_bot_conf/scripts/download_candles.py deleted file mode 100644 index 4e56cc2..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/download_candles.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -from typing import Dict - -from hummingbot import data_path -from hummingbot.client.hummingbot_application import HummingbotApplication -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig, CandlesFactory -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DownloadCandles(ScriptStrategyBase): - """ - This script provides an example of how to use the Candles Feed to download and store historical data. - It downloads 3-minute candles for 3 Binance trading pairs ["APE-USDT", "BTC-USDT", "BNB-USDT"] and stores them in - CSV files in the /data directory. The script stops after it has downloaded 50,000 max_records records for each pair. - Is important to notice that the component will fail if all the candles are not available since the idea of it is to - use it in production based on candles needed to compute technical indicators. - """ - exchange = os.getenv("EXCHANGE", "binance_perpetual") - trading_pairs = os.getenv("TRADING_PAIRS", "DODO-BUSD,LTC-USDT").split(",") - intervals = os.getenv("INTERVALS", "1m,3m,5m,1h").split(",") - days_to_download = int(os.getenv("DAYS_TO_DOWNLOAD", "3")) - # we can initialize any trading pair since we only need the candles - markets = {"binance_paper_trade": {"BTC-USDT"}} - - @staticmethod - def get_max_records(days_to_download: int, interval: str) -> int: - conversion = {"m": 1, "h": 60, "d": 1440} - unit = interval[-1] - quantity = int(interval[:-1]) - return int(days_to_download * 24 * 60 / (quantity * conversion[unit])) - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - combinations = [(trading_pair, interval) for trading_pair in self.trading_pairs for interval in self.intervals] - - self.candles = {f"{combinations[0]}_{combinations[1]}": {} for combinations in combinations} - # we need to initialize the candles for each trading pair - for combination in combinations: - - candle = CandlesFactory.get_candle(CandlesConfig(connector=self.exchange, trading_pair=combination[0], interval=combination[1], max_records=self.get_max_records(self.days_to_download, combination[1]))) - candle.start() - # we are storing the candles object and the csv path to save the candles - self.candles[f"{combination[0]}_{combination[1]}"]["candles"] = candle - self.candles[f"{combination[0]}_{combination[1]}"][ - "csv_path"] = data_path() + f"/candles_{self.exchange}_{combination[0]}_{combination[1]}.csv" - - def on_tick(self): - for trading_pair, candles_info in self.candles.items(): - if not candles_info["candles"].is_ready: - self.logger().info(f"Candles not ready yet for {trading_pair}! Missing {candles_info['candles']._candles.maxlen - len(candles_info['candles']._candles)}") - pass - else: - df = candles_info["candles"].candles_df - df.to_csv(candles_info["csv_path"], index=False) - if all(candles_info["candles"].is_ready for candles_info in self.candles.values()): - HummingbotApplication.main_application().stop() - - def on_stop(self): - for candles_info in self.candles.values(): - candles_info["candles"].stop() diff --git a/hummingbot_files/templates/master_bot_conf/scripts/download_order_book_and_trades.py b/hummingbot_files/templates/master_bot_conf/scripts/download_order_book_and_trades.py deleted file mode 100644 index d9c754f..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/download_order_book_and_trades.py +++ /dev/null @@ -1,99 +0,0 @@ -import json -import os -from datetime import datetime -from typing import Dict - -from hummingbot import data_path -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.core.event.event_forwarder import SourceInfoEventForwarder -from hummingbot.core.event.events import OrderBookEvent, OrderBookTradeEvent -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DownloadTradesAndOrderBookSnapshots(ScriptStrategyBase): - exchange = os.getenv("EXCHANGE", "binance_paper_trade") - trading_pairs = os.getenv("TRADING_PAIRS", "ETH-USDT,BTC-USDT") - depth = int(os.getenv("DEPTH", 50)) - trading_pairs = [pair for pair in trading_pairs.split(",")] - last_dump_timestamp = 0 - time_between_csv_dumps = 10 - - ob_temp_storage = {trading_pair: [] for trading_pair in trading_pairs} - trades_temp_storage = {trading_pair: [] for trading_pair in trading_pairs} - current_date = None - ob_file_paths = {} - trades_file_paths = {} - markets = {exchange: set(trading_pairs)} - subscribed_to_order_book_trade_event: bool = False - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - self.create_order_book_and_trade_files() - self.order_book_trade_event = SourceInfoEventForwarder(self._process_public_trade) - - def on_tick(self): - if not self.subscribed_to_order_book_trade_event: - self.subscribe_to_order_book_trade_event() - self.check_and_replace_files() - for trading_pair in self.trading_pairs: - order_book_data = self.get_order_book_dict(self.exchange, trading_pair, self.depth) - self.ob_temp_storage[trading_pair].append(order_book_data) - if self.last_dump_timestamp < self.current_timestamp: - self.dump_and_clean_temp_storage() - - def get_order_book_dict(self, exchange: str, trading_pair: str, depth: int = 50): - order_book = self.connectors[exchange].get_order_book(trading_pair) - snapshot = order_book.snapshot - return { - "ts": self.current_timestamp, - "bids": snapshot[0].loc[:(depth - 1), ["price", "amount"]].values.tolist(), - "asks": snapshot[1].loc[:(depth - 1), ["price", "amount"]].values.tolist(), - } - - def dump_and_clean_temp_storage(self): - for trading_pair, order_book_info in self.ob_temp_storage.items(): - file = self.ob_file_paths[trading_pair] - json_strings = [json.dumps(obj) for obj in order_book_info] - json_data = '\n'.join(json_strings) - file.write(json_data) - self.ob_temp_storage[trading_pair] = [] - for trading_pair, trades_info in self.trades_temp_storage.items(): - file = self.trades_file_paths[trading_pair] - json_strings = [json.dumps(obj) for obj in trades_info] - json_data = '\n'.join(json_strings) - file.write(json_data) - self.trades_temp_storage[trading_pair] = [] - self.last_dump_timestamp = self.current_timestamp + self.time_between_csv_dumps - - def check_and_replace_files(self): - current_date = datetime.now().strftime("%Y-%m-%d") - if current_date != self.current_date: - for file in self.ob_file_paths.values(): - file.close() - self.create_order_book_and_trade_files() - - def create_order_book_and_trade_files(self): - self.current_date = datetime.now().strftime("%Y-%m-%d") - self.ob_file_paths = {trading_pair: self.get_file(self.exchange, trading_pair, "order_book_snapshots", self.current_date) for - trading_pair in self.trading_pairs} - self.trades_file_paths = {trading_pair: self.get_file(self.exchange, trading_pair, "trades", self.current_date) for - trading_pair in self.trading_pairs} - - @staticmethod - def get_file(exchange: str, trading_pair: str, source_type: str, current_date: str): - file_path = data_path() + f"/{exchange}_{trading_pair}_{source_type}_{current_date}.txt" - return open(file_path, "a") - - def _process_public_trade(self, event_tag: int, market: ConnectorBase, event: OrderBookTradeEvent): - self.trades_temp_storage[event.trading_pair].append({ - "ts": event.timestamp, - "price": event.price, - "q_base": event.amount, - "side": event.type.name.lower(), - }) - - def subscribe_to_order_book_trade_event(self): - for market in self.connectors.values(): - for order_book in market.order_books.values(): - order_book.add_listener(OrderBookEvent.TradeEvent, self.order_book_trade_event) - self.subscribed_to_order_book_trade_event = True diff --git a/hummingbot_files/templates/master_bot_conf/scripts/fixed_grid.py b/hummingbot_files/templates/master_bot_conf/scripts/fixed_grid.py deleted file mode 100644 index 09cfc4a..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/fixed_grid.py +++ /dev/null @@ -1,341 +0,0 @@ -import logging -from decimal import Decimal -from typing import Dict, List - -import numpy as np -import pandas as pd - -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.core.data_type.common import OrderType, PriceType, TradeType -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.events import BuyOrderCompletedEvent, OrderFilledEvent, SellOrderCompletedEvent -from hummingbot.core.utils import map_df_to_str -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class FixedGrid(ScriptStrategyBase): - # Parameters to modify ----------------------------------------- - trading_pair = "ENJ-USDT" - exchange = "ascend_ex" - n_levels = 8 - grid_price_ceiling = Decimal(0.33) - grid_price_floor = Decimal(0.3) - order_amount = Decimal(18.0) - # Optional ---------------------- - spread_scale_factor = Decimal(1.0) - amount_scale_factor = Decimal(1.0) - rebalance_order_type = "limit" - rebalance_order_spread = Decimal(0.02) - rebalance_order_refresh_time = 60.0 - grid_orders_refresh_time = 3600000.0 - price_source = PriceType.MidPrice - # ---------------------------------------------------------------- - - markets = {exchange: {trading_pair}} - create_timestamp = 0 - price_levels = [] - base_inv_levels = [] - quote_inv_levels = [] - order_amount_levels = [] - quote_inv_levels_current_price = [] - current_level = -100 - grid_spread = (grid_price_ceiling - grid_price_floor) / (n_levels - 1) - inv_correct = True - rebalance_order_amount = Decimal(0.0) - rebalance_order_buy = True - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - - self.minimum_spread = (self.grid_price_ceiling - self.grid_price_floor) / (1 + 2 * sum([pow(self.spread_scale_factor, n) for n in range(1, int(self.n_levels / 2))])) - self.price_levels.append(self.grid_price_floor) - for i in range(2, int(self.n_levels / 2) + 1): - price = self.grid_price_floor + self.minimum_spread * sum([pow(self.spread_scale_factor, int(self.n_levels / 2) - n) for n in range(1, i)]) - self.price_levels.append(price) - for i in range(1, int(self.n_levels / 2) + 1): - self.order_amount_levels.append(self.order_amount * pow(self.amount_scale_factor, int(self.n_levels / 2) - i)) - - for i in range(int(self.n_levels / 2) + 1, self.n_levels + 1): - price = self.price_levels[int(self.n_levels / 2) - 1] + self.minimum_spread * sum([pow(self.spread_scale_factor, n) for n in range(0, i - int(self.n_levels / 2))]) - self.price_levels.append(price) - self.order_amount_levels.append(self.order_amount * pow(self.amount_scale_factor, i - int(self.n_levels / 2) - 1)) - - for i in range(1, self.n_levels + 1): - self.base_inv_levels.append(sum(self.order_amount_levels[i:self.n_levels])) - self.quote_inv_levels.append(sum([self.price_levels[n] * self.order_amount_levels[n] for n in range(0, i - 1)])) - for i in range(self.n_levels): - self.quote_inv_levels_current_price.append(self.quote_inv_levels[i] / self.price_levels[i]) - - def on_tick(self): - proposal = None - if self.create_timestamp <= self.current_timestamp: - # If grid level not yet set, find it. - if self.current_level == -100: - price = self.connectors[self.exchange].get_price_by_type(self.trading_pair, self.price_source) - # Find level closest to market - min_diff = 1e8 - for i in range(self.n_levels): - if min(min_diff, abs(self.price_levels[i] - price)) < min_diff: - min_diff = abs(self.price_levels[i] - price) - self.current_level = i - - msg = (f"Current price {price}, Initial level {self.current_level+1}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) - - if price > self.grid_price_ceiling: - msg = ("WARNING: Current price is above grid ceiling") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - elif price < self.grid_price_floor: - msg = ("WARNING: Current price is below grid floor") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - - market, trading_pair, base_asset, quote_asset = self.get_market_trading_pair_tuples()[0] - base_balance = float(market.get_balance(base_asset)) - quote_balance = float(market.get_balance(quote_asset) / self.price_levels[self.current_level]) - - if base_balance < self.base_inv_levels[self.current_level]: - self.inv_correct = False - msg = (f"WARNING: Insuffient {base_asset} balance for grid bot. Will attempt to rebalance") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - if base_balance + quote_balance < self.base_inv_levels[self.current_level] + self.quote_inv_levels_current_price[self.current_level]: - msg = (f"WARNING: Insuffient {base_asset} and {quote_asset} balance for grid bot. Unable to rebalance." - f"Please add funds or change grid parameters") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - return - else: - # Calculate additional base required with 5% tolerance - base_required = (Decimal(self.base_inv_levels[self.current_level]) - Decimal(base_balance)) * Decimal(1.05) - self.rebalance_order_buy = True - self.rebalance_order_amount = Decimal(base_required) - elif quote_balance < self.quote_inv_levels_current_price[self.current_level]: - self.inv_correct = False - msg = (f"WARNING: Insuffient {quote_asset} balance for grid bot. Will attempt to rebalance") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - if base_balance + quote_balance < self.base_inv_levels[self.current_level] + self.quote_inv_levels_current_price[self.current_level]: - msg = (f"WARNING: Insuffient {base_asset} and {quote_asset} balance for grid bot. Unable to rebalance." - f"Please add funds or change grid parameters") - self.log_with_clock(logging.WARNING, msg) - self.notify_hb_app_with_timestamp(msg) - return - else: - # Calculate additional quote required with 5% tolerance - quote_required = (Decimal(self.quote_inv_levels_current_price[self.current_level]) - Decimal(quote_balance)) * Decimal(1.05) - self.rebalance_order_buy = False - self.rebalance_order_amount = Decimal(quote_required) - else: - self.inv_correct = True - - if self.inv_correct is True: - # Create proposals for Grid - proposal = self.create_grid_proposal() - else: - # Create rebalance proposal - proposal = self.create_rebalance_proposal() - - self.cancel_active_orders() - if proposal is not None: - self.execute_orders_proposal(proposal) - - def create_grid_proposal(self) -> List[OrderCandidate]: - buys = [] - sells = [] - - # Proposal will be created according to grid price levels - for i in range(self.current_level): - price = self.price_levels[i] - size = self.order_amount_levels[i] - if size > 0: - buy_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.BUY, amount=size, price=price) - buys.append(buy_order) - - for i in range(self.current_level + 1, self.n_levels): - price = self.price_levels[i] - size = self.order_amount_levels[i] - if size > 0: - sell_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.SELL, amount=size, price=price) - sells.append(sell_order) - - return buys + sells - - def create_rebalance_proposal(self): - buys = [] - sells = [] - - # Proposal will be created according to start order spread. - if self.rebalance_order_buy is True: - ref_price = self.connectors[self.exchange].get_price_by_type(self.trading_pair, self.price_source) - price = ref_price * (Decimal("100") - self.rebalance_order_spread) / Decimal("100") - size = self.rebalance_order_amount - - msg = (f"Placing buy order to rebalance; amount: {size}, price: {price}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) - if size > 0: - if self.rebalance_order_type == "limit": - buy_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.BUY, amount=size, price=price) - elif self.rebalance_order_type == "market": - buy_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.MARKET, - order_side=TradeType.BUY, amount=size, price=price) - buys.append(buy_order) - - if self.rebalance_order_buy is False: - ref_price = self.connectors[self.exchange].get_price_by_type(self.trading_pair, self.price_source) - price = ref_price * (Decimal("100") + self.rebalance_order_spread) / Decimal("100") - size = self.rebalance_order_amount - msg = (f"Placing sell order to rebalance; amount: {size}, price: {price}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) - if size > 0: - if self.rebalance_order_type == "limit": - sell_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.SELL, amount=size, price=price) - elif self.rebalance_order_type == "market": - sell_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.MARKET, - order_side=TradeType.SELL, amount=size, price=price) - sells.append(sell_order) - - return buys + sells - - def did_fill_order(self, event: OrderFilledEvent): - msg = (f"{event.trade_type.name} {round(event.amount, 2)} {event.trading_pair} {self.exchange} at {round(event.price, 2)}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) - - def did_complete_buy_order(self, event: BuyOrderCompletedEvent): - if self.inv_correct is False: - self.create_timestamp = self.current_timestamp + float(1.0) - - if self.inv_correct is True: - # Set the new level - self.current_level -= 1 - # Add sell order above current level - price = self.price_levels[self.current_level + 1] - size = self.order_amount_levels[self.current_level + 1] - proposal = [OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.SELL, amount=size, price=price)] - self.execute_orders_proposal(proposal) - - def did_complete_sell_order(self, event: SellOrderCompletedEvent): - if self.inv_correct is False: - self.create_timestamp = self.current_timestamp + float(1.0) - - if self.inv_correct is True: - # Set the new level - self.current_level += 1 - # Add buy order above current level - price = self.price_levels[self.current_level - 1] - size = self.order_amount_levels[self.current_level - 1] - proposal = [OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.BUY, amount=size, price=price)] - self.execute_orders_proposal(proposal) - - def execute_orders_proposal(self, proposal: List[OrderCandidate]) -> None: - for order in proposal: - self.place_order(connector_name=self.exchange, order=order) - if self.inv_correct is False: - next_cycle = self.current_timestamp + self.rebalance_order_refresh_time - if self.create_timestamp <= self.current_timestamp: - self.create_timestamp = next_cycle - else: - next_cycle = self.current_timestamp + self.grid_orders_refresh_time - if self.create_timestamp <= self.current_timestamp: - self.create_timestamp = next_cycle - - def place_order(self, connector_name: str, order: OrderCandidate): - if order.order_side == TradeType.SELL: - self.sell(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - elif order.order_side == TradeType.BUY: - self.buy(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - - def grid_assets_df(self) -> pd.DataFrame: - market, trading_pair, base_asset, quote_asset = self.get_market_trading_pair_tuples()[0] - price = self.connectors[self.exchange].get_price_by_type(self.trading_pair, self.price_source) - base_balance = float(market.get_balance(base_asset)) - quote_balance = float(market.get_balance(quote_asset)) - available_base_balance = float(market.get_available_balance(base_asset)) - available_quote_balance = float(market.get_available_balance(quote_asset)) - base_value = base_balance * float(price) - total_in_quote = base_value + quote_balance - base_ratio = base_value / total_in_quote if total_in_quote > 0 else 0 - quote_ratio = quote_balance / total_in_quote if total_in_quote > 0 else 0 - data = [ - ["", base_asset, quote_asset], - ["Total Balance", round(base_balance, 4), round(quote_balance, 4)], - ["Available Balance", round(available_base_balance, 4), round(available_quote_balance, 4)], - [f"Current Value ({quote_asset})", round(base_value, 4), round(quote_balance, 4)] - ] - data.append(["Current %", f"{base_ratio:.1%}", f"{quote_ratio:.1%}"]) - df = pd.DataFrame(data=data) - return df - - def grid_status_data_frame(self) -> pd.DataFrame: - grid_data = [] - grid_columns = ["Parameter", "Value"] - - market, trading_pair, base_asset, quote_asset = self.get_market_trading_pair_tuples()[0] - base_balance = float(market.get_balance(base_asset)) - quote_balance = float(market.get_balance(quote_asset) / self.price_levels[self.current_level]) - - grid_data.append(["Grid spread", round(self.grid_spread, 4)]) - grid_data.append(["Current grid level", self.current_level + 1]) - grid_data.append([f"{base_asset} required", round(self.base_inv_levels[self.current_level], 4)]) - grid_data.append([f"{quote_asset} required in {base_asset}", round(self.quote_inv_levels_current_price[self.current_level], 4)]) - grid_data.append([f"{base_asset} balance", round(base_balance, 4)]) - grid_data.append([f"{quote_asset} balance in {base_asset}", round(quote_balance, 4)]) - grid_data.append(["Correct inventory balance", self.inv_correct]) - - return pd.DataFrame(data=grid_data, columns=grid_columns).replace(np.nan, '', regex=True) - - def format_status(self) -> str: - """ - Displays the status of the fixed grid strategy - Returns status of the current strategy on user balances and current active orders. - """ - if not self.ready_to_trade: - return "Market connectors are not ready." - - lines = [] - warning_lines = [] - warning_lines.extend(self.network_warning(self.get_market_trading_pair_tuples())) - - balance_df = self.get_balance_df() - lines.extend(["", " Balances:"] + [" " + line for line in balance_df.to_string(index=False).split("\n")]) - - grid_df = map_df_to_str(self.grid_status_data_frame()) - lines.extend(["", " Grid:"] + [" " + line for line in grid_df.to_string(index=False).split("\n")]) - - assets_df = map_df_to_str(self.grid_assets_df()) - - first_col_length = max(*assets_df[0].apply(len)) - df_lines = assets_df.to_string(index=False, header=False, - formatters={0: ("{:<" + str(first_col_length) + "}").format}).split("\n") - lines.extend(["", " Assets:"] + [" " + line for line in df_lines]) - - try: - df = self.active_orders_df() - lines.extend(["", " Orders:"] + [" " + line for line in df.to_string(index=False).split("\n")]) - except ValueError: - lines.extend(["", " No active maker orders."]) - - warning_lines.extend(self.balance_warning(self.get_market_trading_pair_tuples())) - if len(warning_lines) > 0: - lines.extend(["", "*** WARNINGS ***"] + warning_lines) - return "\n".join(lines) - - def cancel_active_orders(self): - """ - Cancels active orders - """ - for order in self.get_active_orders(connector_name=self.exchange): - self.cancel(self.exchange, order.trading_pair, order.client_order_id) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/simple_arbitrage_example.py b/hummingbot_files/templates/master_bot_conf/scripts/simple_arbitrage_example.py deleted file mode 100644 index fb478ab..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/simple_arbitrage_example.py +++ /dev/null @@ -1,193 +0,0 @@ -import logging -from decimal import Decimal -from typing import Any, Dict - -import pandas as pd - -from hummingbot.core.data_type.common import OrderType, TradeType -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.events import OrderFilledEvent -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class SimpleArbitrage(ScriptStrategyBase): - """ - BotCamp Cohort: Sept 2022 - Design Template: https://hummingbot-foundation.notion.site/Simple-Arbitrage-51b2af6e54b6493dab12e5d537798c07 - Video: TBD - Description: - A simplified version of Hummingbot arbitrage strategy, this bot checks the Volume Weighted Average Price for - bid and ask in two exchanges and if it finds a profitable opportunity, it will trade the tokens. - """ - order_amount = Decimal("0.01") # in base asset - min_profitability = Decimal("0.002") # in percentage - base = "ETH" - quote = "USDT" - trading_pair = f"{base}-{quote}" - exchange_A = "binance_paper_trade" - exchange_B = "kucoin_paper_trade" - - markets = {exchange_A: {trading_pair}, - exchange_B: {trading_pair}} - - def on_tick(self): - vwap_prices = self.get_vwap_prices_for_amount(self.order_amount) - proposal = self.check_profitability_and_create_proposal(vwap_prices) - if len(proposal) > 0: - proposal_adjusted: Dict[str, OrderCandidate] = self.adjust_proposal_to_budget(proposal) - self.place_orders(proposal_adjusted) - - def get_vwap_prices_for_amount(self, amount: Decimal): - bid_ex_a = self.connectors[self.exchange_A].get_vwap_for_volume(self.trading_pair, False, amount) - ask_ex_a = self.connectors[self.exchange_A].get_vwap_for_volume(self.trading_pair, True, amount) - bid_ex_b = self.connectors[self.exchange_B].get_vwap_for_volume(self.trading_pair, False, amount) - ask_ex_b = self.connectors[self.exchange_B].get_vwap_for_volume(self.trading_pair, True, amount) - vwap_prices = { - self.exchange_A: { - "bid": bid_ex_a.result_price, - "ask": ask_ex_a.result_price - }, - self.exchange_B: { - "bid": bid_ex_b.result_price, - "ask": ask_ex_b.result_price - } - } - return vwap_prices - - def get_fees_percentages(self, vwap_prices: Dict[str, Any]) -> Dict: - # We assume that the fee percentage for buying or selling is the same - a_fee = self.connectors[self.exchange_A].get_fee( - base_currency=self.base, - quote_currency=self.quote, - order_type=OrderType.MARKET, - order_side=TradeType.BUY, - amount=self.order_amount, - price=vwap_prices[self.exchange_A]["ask"], - is_maker=False - ).percent - - b_fee = self.connectors[self.exchange_B].get_fee( - base_currency=self.base, - quote_currency=self.quote, - order_type=OrderType.MARKET, - order_side=TradeType.BUY, - amount=self.order_amount, - price=vwap_prices[self.exchange_B]["ask"], - is_maker=False - ).percent - - return { - self.exchange_A: a_fee, - self.exchange_B: b_fee - } - - def get_profitability_analysis(self, vwap_prices: Dict[str, Any]) -> Dict: - fees = self.get_fees_percentages(vwap_prices) - buy_a_sell_b_quote = vwap_prices[self.exchange_A]["ask"] * (1 - fees[self.exchange_A]) * self.order_amount - \ - vwap_prices[self.exchange_B]["bid"] * (1 + fees[self.exchange_B]) * self.order_amount - buy_a_sell_b_base = buy_a_sell_b_quote / ( - (vwap_prices[self.exchange_A]["ask"] + vwap_prices[self.exchange_B]["bid"]) / 2) - - buy_b_sell_a_quote = vwap_prices[self.exchange_B]["ask"] * (1 - fees[self.exchange_B]) * self.order_amount - \ - vwap_prices[self.exchange_A]["bid"] * (1 + fees[self.exchange_A]) * self.order_amount - - buy_b_sell_a_base = buy_b_sell_a_quote / ( - (vwap_prices[self.exchange_B]["ask"] + vwap_prices[self.exchange_A]["bid"]) / 2) - - return { - "buy_a_sell_b": - { - "quote_diff": buy_a_sell_b_quote, - "base_diff": buy_a_sell_b_base, - "profitability_pct": buy_a_sell_b_base / self.order_amount - }, - "buy_b_sell_a": - { - "quote_diff": buy_b_sell_a_quote, - "base_diff": buy_b_sell_a_base, - "profitability_pct": buy_b_sell_a_base / self.order_amount - }, - } - - def check_profitability_and_create_proposal(self, vwap_prices: Dict[str, Any]) -> Dict: - proposal = {} - profitability_analysis = self.get_profitability_analysis(vwap_prices) - if profitability_analysis["buy_a_sell_b"]["profitability_pct"] > self.min_profitability: - # This means that the ask of the first exchange is lower than the bid of the second one - proposal[self.exchange_A] = OrderCandidate(trading_pair=self.trading_pair, is_maker=False, - order_type=OrderType.MARKET, - order_side=TradeType.BUY, amount=self.order_amount, - price=vwap_prices[self.exchange_A]["ask"]) - proposal[self.exchange_B] = OrderCandidate(trading_pair=self.trading_pair, is_maker=False, - order_type=OrderType.MARKET, - order_side=TradeType.SELL, amount=Decimal(self.order_amount), - price=vwap_prices[self.exchange_B]["bid"]) - elif profitability_analysis["buy_b_sell_a"]["profitability_pct"] > self.min_profitability: - # This means that the ask of the second exchange is lower than the bid of the first one - proposal[self.exchange_B] = OrderCandidate(trading_pair=self.trading_pair, is_maker=False, - order_type=OrderType.MARKET, - order_side=TradeType.BUY, amount=self.order_amount, - price=vwap_prices[self.exchange_B]["ask"]) - proposal[self.exchange_A] = OrderCandidate(trading_pair=self.trading_pair, is_maker=False, - order_type=OrderType.MARKET, - order_side=TradeType.SELL, amount=Decimal(self.order_amount), - price=vwap_prices[self.exchange_A]["bid"]) - - return proposal - - def adjust_proposal_to_budget(self, proposal: Dict[str, OrderCandidate]) -> Dict[str, OrderCandidate]: - for connector, order in proposal.items(): - proposal[connector] = self.connectors[connector].budget_checker.adjust_candidate(order, all_or_none=True) - return proposal - - def place_orders(self, proposal: Dict[str, OrderCandidate]) -> None: - for connector, order in proposal.items(): - self.place_order(connector_name=connector, order=order) - - def place_order(self, connector_name: str, order: OrderCandidate): - if order.order_side == TradeType.SELL: - self.sell(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - elif order.order_side == TradeType.BUY: - self.buy(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - - def format_status(self) -> str: - """ - Returns status of the current strategy on user balances and current active orders. This function is called - when status command is issued. Override this function to create custom status display output. - """ - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - warning_lines = [] - warning_lines.extend(self.network_warning(self.get_market_trading_pair_tuples())) - - balance_df = self.get_balance_df() - lines.extend(["", " Balances:"] + [" " + line for line in balance_df.to_string(index=False).split("\n")]) - - vwap_prices = self.get_vwap_prices_for_amount(self.order_amount) - lines.extend(["", " VWAP Prices for amount"] + [" " + line for line in - pd.DataFrame(vwap_prices).to_string().split("\n")]) - profitability_analysis = self.get_profitability_analysis(vwap_prices) - lines.extend(["", " Profitability (%)"] + [ - f" Buy A: {self.exchange_A} --> Sell B: {self.exchange_B}"] + [ - f" Quote Diff: {profitability_analysis['buy_a_sell_b']['quote_diff']:.7f}"] + [ - f" Base Diff: {profitability_analysis['buy_a_sell_b']['base_diff']:.7f}"] + [ - f" Percentage: {profitability_analysis['buy_a_sell_b']['profitability_pct'] * 100:.4f} %"] + [ - f" Buy B: {self.exchange_B} --> Sell A: {self.exchange_A}"] + [ - f" Quote Diff: {profitability_analysis['buy_b_sell_a']['quote_diff']:.7f}"] + [ - f" Base Diff: {profitability_analysis['buy_b_sell_a']['base_diff']:.7f}"] + [ - f" Percentage: {profitability_analysis['buy_b_sell_a']['profitability_pct'] * 100:.4f} %" - ]) - - warning_lines.extend(self.balance_warning(self.get_market_trading_pair_tuples())) - if len(warning_lines) > 0: - lines.extend(["", "*** WARNINGS ***"] + warning_lines) - return "\n".join(lines) - - def did_fill_order(self, event: OrderFilledEvent): - msg = ( - f"{event.trade_type.name} {round(event.amount, 2)} {event.trading_pair} at {round(event.price, 2)}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) \ No newline at end of file diff --git a/hummingbot_files/templates/master_bot_conf/scripts/simple_pmm_example.py b/hummingbot_files/templates/master_bot_conf/scripts/simple_pmm_example.py deleted file mode 100644 index 8370819..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/simple_pmm_example.py +++ /dev/null @@ -1,77 +0,0 @@ -import logging -from decimal import Decimal -from typing import List - -from hummingbot.core.data_type.common import OrderType, PriceType, TradeType -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.events import OrderFilledEvent -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class SimplePMM(ScriptStrategyBase): - """ - BotCamp Cohort: Sept 2022 - Design Template: https://hummingbot-foundation.notion.site/Simple-PMM-63cc765486dd42228d3da0b32537fc92 - Video: - - Description: - The bot will place two orders around the price_source (mid price or last traded price) in a trading_pair on - exchange, with a distance defined by the ask_spread and bid_spread. Every order_refresh_time in seconds, - the bot will cancel and replace the orders. - """ - bid_spread = 0.0001 - ask_spread = 0.0001 - order_refresh_time = 15 - order_amount = 0.01 - create_timestamp = 0 - trading_pair = "ETH-USDT" - exchange = "binance_paper_trade" - # Here you can use for example the LastTrade price to use in your strategy - price_source = PriceType.MidPrice - - markets = {exchange: {trading_pair}} - - def on_tick(self): - if self.create_timestamp <= self.current_timestamp: - self.cancel_all_orders() - proposal: List[OrderCandidate] = self.create_proposal() - proposal_adjusted: List[OrderCandidate] = self.adjust_proposal_to_budget(proposal) - self.place_orders(proposal_adjusted) - self.create_timestamp = self.order_refresh_time + self.current_timestamp - - def create_proposal(self) -> List[OrderCandidate]: - ref_price = self.connectors[self.exchange].get_price_by_type(self.trading_pair, self.price_source) - buy_price = ref_price * Decimal(1 - self.bid_spread) - sell_price = ref_price * Decimal(1 + self.ask_spread) - - buy_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.BUY, amount=Decimal(self.order_amount), price=buy_price) - - sell_order = OrderCandidate(trading_pair=self.trading_pair, is_maker=True, order_type=OrderType.LIMIT, - order_side=TradeType.SELL, amount=Decimal(self.order_amount), price=sell_price) - - return [buy_order, sell_order] - - def adjust_proposal_to_budget(self, proposal: List[OrderCandidate]) -> List[OrderCandidate]: - proposal_adjusted = self.connectors[self.exchange].budget_checker.adjust_candidates(proposal, all_or_none=True) - return proposal_adjusted - - def place_orders(self, proposal: List[OrderCandidate]) -> None: - for order in proposal: - self.place_order(connector_name=self.exchange, order=order) - - def place_order(self, connector_name: str, order: OrderCandidate): - if order.order_side == TradeType.SELL: - self.sell(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - elif order.order_side == TradeType.BUY: - self.buy(connector_name=connector_name, trading_pair=order.trading_pair, amount=order.amount, - order_type=order.order_type, price=order.price) - - def cancel_all_orders(self): - for order in self.get_active_orders(connector_name=self.exchange): - self.cancel(self.exchange, order.trading_pair, order.client_order_id) - - def did_fill_order(self, event: OrderFilledEvent): - msg = (f"{event.trade_type.name} {round(event.amount, 2)} {event.trading_pair} {self.exchange} at {round(event.price, 2)}") - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/simple_rsi_example.py b/hummingbot_files/templates/master_bot_conf/scripts/simple_rsi_example.py deleted file mode 100644 index 7f8fa59..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/simple_rsi_example.py +++ /dev/null @@ -1,259 +0,0 @@ -import math -import os -from decimal import Decimal -from typing import Optional - -import pandas as pd - -from hummingbot.client.hummingbot_application import HummingbotApplication -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.connector.utils import combine_to_hb_trading_pair -from hummingbot.core.data_type.common import OrderType, TradeType -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.event_forwarder import SourceInfoEventForwarder -from hummingbot.core.event.events import OrderBookEvent, OrderBookTradeEvent, OrderFilledEvent -from hummingbot.core.rate_oracle.rate_oracle import RateOracle -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class SimpleRSIScript(ScriptStrategyBase): - """ - The strategy is to buy on overbought signal and sell on oversold. - """ - connector_name = os.getenv("CONNECTOR_NAME", "binance_paper_trade") - base = os.getenv("BASE", "BTC") - quote = os.getenv("QUOTE", "USDT") - timeframe = os.getenv("TIMEFRAME", "1s") - - position_amount_usd = Decimal(os.getenv("POSITION_AMOUNT_USD", "50")) - - rsi_length = int(os.getenv("RSI_LENGTH", "14")) - - # If true - uses Exponential Moving Average, if false - Simple Moving Average. - rsi_is_ema = os.getenv("RSI_IS_EMA", 'True').lower() in ('true', '1', 't') - - buy_rsi = int(os.getenv("BUY_RSI", "30")) - sell_rsi = int(os.getenv("SELL_RSI", "70")) - - # It depends on a timeframe. Make sure you have enough trades to calculate rsi_length number of candlesticks. - trade_count_limit = int(os.getenv("TRADE_COUNT_LIMIT", "100000")) - - trading_pair = combine_to_hb_trading_pair(base, quote) - markets = {connector_name: {trading_pair}} - - subscribed_to_order_book_trade_event: bool = False - position: Optional[OrderFilledEvent] = None - - _trades: 'list[OrderBookTradeEvent]' = [] - _cumulative_price_change_pct = Decimal(0) - _filling_position: bool = False - - def on_tick(self): - """ - On every tick calculate OHLCV candlesticks, calculate RSI, react on overbought or oversold signal with creating, - adjusting and sending an order. - """ - if not self.subscribed_to_order_book_trade_event: - # Set pandas resample rule for a timeframe - self._set_resample_rule(self.timeframe) - self.subscribe_to_order_book_trade_event() - elif len(self._trades) > 0: - df = self.calculate_candlesticks() - df = self.calculate_rsi(df, self.rsi_length, self.rsi_is_ema) - should_open_position = self.should_open_position(df) - should_close_position = self.should_close_position(df) - if should_open_position or should_close_position: - order_side = TradeType.BUY if should_open_position else TradeType.SELL - order_candidate = self.create_order_candidate(order_side) - # Adjust OrderCandidate - order_adjusted = self.connectors[self.connector_name].budget_checker.adjust_candidate(order_candidate, all_or_none=False) - if math.isclose(order_adjusted.amount, Decimal("0"), rel_tol=1E-5): - self.logger().info(f"Order adjusted: {order_adjusted.amount}, too low to place an order") - else: - self.send_order(order_adjusted) - else: - self._rsi = df.iloc[-1]['rsi'] - self.logger().info(f"RSI is {self._rsi:.0f}") - - def _set_resample_rule(self, timeframe): - """ - Convert timeframe to pandas resample rule value. - https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html - """ - timeframe_to_rule = { - "1s": "1S", - "10s": "10S", - "30s": "30S", - "1m": "1T", - "15m": "15T" - } - if timeframe not in timeframe_to_rule.keys(): - self.logger().error(f"{timeframe} timeframe is not mapped to resample rule.") - HummingbotApplication.main_application().stop() - self._resample_rule = timeframe_to_rule[timeframe] - - def should_open_position(self, df: pd.DataFrame) -> bool: - """ - If overbought and not in the position. - """ - rsi: float = df.iloc[-1]['rsi'] - rsi_is_calculated = pd.notna(rsi) - time_to_buy = rsi_is_calculated and rsi <= self.buy_rsi - can_buy = self.position is None and not self._filling_position - return can_buy and time_to_buy - - def should_close_position(self, df: pd.DataFrame) -> bool: - """ - If oversold and in the position. - """ - rsi: float = df.iloc[-1]['rsi'] - rsi_is_calculated = pd.notna(rsi) - time_to_sell = rsi_is_calculated and rsi >= self.sell_rsi - can_sell = self.position is not None and not self._filling_position - return can_sell and time_to_sell - - def create_order_candidate(self, order_side: bool) -> OrderCandidate: - """ - Create and quantize order candidate. - """ - connector: ConnectorBase = self.connectors[self.connector_name] - is_buy = order_side == TradeType.BUY - price = connector.get_price(self.trading_pair, is_buy) - if is_buy: - conversion_rate = RateOracle.get_instance().get_pair_rate(self.trading_pair) - amount = self.position_amount_usd / conversion_rate - else: - amount = self.position.amount - - amount = connector.quantize_order_amount(self.trading_pair, amount) - price = connector.quantize_order_price(self.trading_pair, price) - return OrderCandidate( - trading_pair=self.trading_pair, - is_maker = False, - order_type = OrderType.LIMIT, - order_side = order_side, - amount = amount, - price = price) - - def send_order(self, order: OrderCandidate): - """ - Send order to the exchange, indicate that position is filling, and send log message with a trade. - """ - is_buy = order.order_side == TradeType.BUY - place_order = self.buy if is_buy else self.sell - place_order( - connector_name=self.connector_name, - trading_pair=self.trading_pair, - amount=order.amount, - order_type=order.order_type, - price=order.price - ) - self._filling_position = True - if is_buy: - msg = f"RSI is below {self.buy_rsi:.2f}, buying {order.amount:.5f} {self.base} with limit order at {order.price:.2f} ." - else: - msg = (f"RSI is above {self.sell_rsi:.2f}, selling {self.position.amount:.5f} {self.base}" - f" with limit order at ~ {order.price:.2f}, entry price was {self.position.price:.2f}.") - self.notify_hb_app_with_timestamp(msg) - self.logger().info(msg) - - def calculate_candlesticks(self) -> pd.DataFrame: - """ - Convert raw trades to OHLCV dataframe. - """ - df = pd.DataFrame(self._trades) - df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s') - df["timestamp"] = pd.to_datetime(df["timestamp"]) - df.drop(columns=[df.columns[0]], axis=1, inplace=True) - df = df.set_index('timestamp') - df = df.resample(self._resample_rule).agg({ - 'price': ['first', 'max', 'min', 'last'], - 'amount': 'sum', - }) - df.columns = df.columns.to_flat_index().map(lambda x: x[1]) - df.rename(columns={'first': 'open', 'max': 'high', 'min': 'low', 'last': 'close', 'sum': 'volume'}, inplace=True) - return df - - def did_fill_order(self, event: OrderFilledEvent): - """ - Indicate that position is filled, save position properties on enter, calculate cumulative price change on exit. - """ - if event.trade_type == TradeType.BUY: - self.position = event - self._filling_position = False - elif event.trade_type == TradeType.SELL: - delta_price = (event.price - self.position.price) / self.position.price - self._cumulative_price_change_pct += delta_price - self.position = None - self._filling_position = False - else: - self.logger().warn(f"Unsupported order type filled: {event.trade_type}") - - @staticmethod - def calculate_rsi(df: pd.DataFrame, length: int = 14, is_ema: bool = True): - """ - Calculate relative strength index and add it to the dataframe. - """ - close_delta = df['close'].diff() - up = close_delta.clip(lower=0) - down = close_delta.clip(upper=0).abs() - - if is_ema: - # Exponential Moving Average - ma_up = up.ewm(com = length - 1, adjust=True, min_periods = length).mean() - ma_down = down.ewm(com = length - 1, adjust=True, min_periods = length).mean() - else: - # Simple Moving Average - ma_up = up.rolling(window = length, adjust=False).mean() - ma_down = down.rolling(window = length, adjust=False).mean() - - rs = ma_up / ma_down - df["rsi"] = 100 - (100 / (1 + rs)) - return df - - def subscribe_to_order_book_trade_event(self): - """ - Subscribe to raw trade event. - """ - self.order_book_trade_event = SourceInfoEventForwarder(self._process_public_trade) - for market in self.connectors.values(): - for order_book in market.order_books.values(): - order_book.add_listener(OrderBookEvent.TradeEvent, self.order_book_trade_event) - self.subscribed_to_order_book_trade_event = True - - def _process_public_trade(self, event_tag: int, market: ConnectorBase, event: OrderBookTradeEvent): - """ - Add new trade to list, remove old trade event, if count greater than trade_count_limit. - """ - if len(self._trades) >= self.trade_count_limit: - self._trades.pop(0) - self._trades.append(event) - - def format_status(self) -> str: - """ - Returns status of the current strategy on user balances and current active orders. This function is called - when status command is issued. Override this function to create custom status display output. - """ - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - warning_lines = [] - warning_lines.extend(self.network_warning(self.get_market_trading_pair_tuples())) - - balance_df = self.get_balance_df() - lines.extend(["", " Balances:"] + [" " + line for line in balance_df.to_string(index=False).split("\n")]) - - try: - df = self.active_orders_df() - lines.extend(["", " Orders:"] + [" " + line for line in df.to_string(index=False).split("\n")]) - except ValueError: - lines.extend(["", " No active maker orders."]) - - # Strategy specific info - lines.extend(["", " Current RSI:"] + [" " + f"{self._rsi:.0f}"]) - lines.extend(["", " Simple RSI strategy total price change with all trades:"] + [" " + f"{self._cumulative_price_change_pct:.5f}" + " %"]) - - warning_lines.extend(self.balance_warning(self.get_market_trading_pair_tuples())) - if len(warning_lines) > 0: - lines.extend(["", "*** WARNINGS ***"] + warning_lines) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/simple_vwap_example.py b/hummingbot_files/templates/master_bot_conf/scripts/simple_vwap_example.py deleted file mode 100644 index 89dcf08..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/simple_vwap_example.py +++ /dev/null @@ -1,184 +0,0 @@ -import logging -import math -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.utils import split_hb_trading_pair -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.events import OrderFilledEvent, OrderType, TradeType -from hummingbot.core.rate_oracle.rate_oracle import RateOracle -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class VWAPExample(ScriptStrategyBase): - """ - BotCamp Cohort: Sept 2022 - Design Template: https://hummingbot-foundation.notion.site/Simple-VWAP-Example-d43a929cc5bd45c6b1a72f63e6635618 - Video: - - Description: - This example lets you create one VWAP in a market using a percentage of the sum volume of the order book - until a spread from the mid price. - This example demonstrates: - - How to get the account balance - - How to get the bids and asks of a market - - How to code a "utility" strategy - """ - last_ordered_ts = 0 - vwap: Dict = {"connector_name": "binance_paper_trade", "trading_pair": "ETH-USDT", "is_buy": True, - "total_volume_usd": 100000, "price_spread": 0.001, "volume_perc": 0.001, "order_delay_time": 10} - markets = {vwap["connector_name"]: {vwap["trading_pair"]}} - - def on_tick(self): - """ - Every order delay time the strategy will buy or sell the base asset. It will compute the cumulative order book - volume until the spread and buy a percentage of that. - The input of the strategy is in USD, but we will use the rate oracle to get a target base that will be static. - - Use the Rate Oracle to get a conversion rate - - Create proposal (a list of order candidates) - - Check the account balance and adjust the proposal accordingly (lower order amount if needed) - - Lastly, execute the proposal on the exchange - """ - if self.last_ordered_ts < (self.current_timestamp - self.vwap["order_delay_time"]): - if self.vwap.get("status") is None: - self.init_vwap_stats() - elif self.vwap.get("status") == "ACTIVE": - vwap_order: OrderCandidate = self.create_order() - vwap_order_adjusted = self.vwap["connector"].budget_checker.adjust_candidate(vwap_order, - all_or_none=False) - if math.isclose(vwap_order_adjusted.amount, Decimal("0"), rel_tol=1E-5): - self.logger().info(f"Order adjusted: {vwap_order_adjusted.amount}, too low to place an order") - else: - self.place_order( - connector_name=self.vwap["connector_name"], - trading_pair=self.vwap["trading_pair"], - is_buy=self.vwap["is_buy"], - amount=vwap_order_adjusted.amount, - order_type=vwap_order_adjusted.order_type) - self.last_ordered_ts = self.current_timestamp - - def init_vwap_stats(self): - # General parameters - vwap = self.vwap.copy() - vwap["connector"] = self.connectors[vwap["connector_name"]] - vwap["delta"] = 0 - vwap["trades"] = [] - vwap["status"] = "ACTIVE" - vwap["trade_type"] = TradeType.BUY if self.vwap["is_buy"] else TradeType.SELL - base_asset, quote_asset = split_hb_trading_pair(vwap["trading_pair"]) - - # USD conversion to quote and base asset - conversion_base_asset = f"{base_asset}-USD" - conversion_quote_asset = f"{quote_asset}-USD" - base_conversion_rate = RateOracle.get_instance().get_pair_rate(conversion_base_asset) - quote_conversion_rate = RateOracle.get_instance().get_pair_rate(conversion_quote_asset) - vwap["start_price"] = vwap["connector"].get_price(vwap["trading_pair"], vwap["is_buy"]) - vwap["target_base_volume"] = vwap["total_volume_usd"] / base_conversion_rate - vwap["ideal_quote_volume"] = vwap["total_volume_usd"] / quote_conversion_rate - - # Compute market order scenario - orderbook_query = vwap["connector"].get_quote_volume_for_base_amount(vwap["trading_pair"], vwap["is_buy"], - vwap["target_base_volume"]) - vwap["market_order_base_volume"] = orderbook_query.query_volume - vwap["market_order_quote_volume"] = orderbook_query.result_volume - vwap["volume_remaining"] = vwap["target_base_volume"] - vwap["real_quote_volume"] = Decimal(0) - self.vwap = vwap - - def create_order(self) -> OrderCandidate: - """ - Retrieves the cumulative volume of the order book until the price spread is reached, then takes a percentage - of that to use as order amount. - """ - # Compute the new price using the max spread allowed - mid_price = float(self.vwap["connector"].get_mid_price(self.vwap["trading_pair"])) - price_multiplier = 1 + self.vwap["price_spread"] if self.vwap["is_buy"] else 1 - self.vwap["price_spread"] - price_affected_by_spread = mid_price * price_multiplier - - # Query the cumulative volume until the price affected by spread - orderbook_query = self.vwap["connector"].get_volume_for_price( - trading_pair=self.vwap["trading_pair"], - is_buy=self.vwap["is_buy"], - price=price_affected_by_spread) - volume_for_price = orderbook_query.result_volume - - # Check if the volume available is higher than the remaining - amount = min(volume_for_price * Decimal(self.vwap["volume_perc"]), Decimal(self.vwap["volume_remaining"])) - - # Quantize the order amount and price - amount = self.vwap["connector"].quantize_order_amount(self.vwap["trading_pair"], amount) - price = self.vwap["connector"].quantize_order_price(self.vwap["trading_pair"], - Decimal(price_affected_by_spread)) - # Create the Order Candidate - vwap_order = OrderCandidate( - trading_pair=self.vwap["trading_pair"], - is_maker=False, - order_type=OrderType.MARKET, - order_side=self.vwap["trade_type"], - amount=amount, - price=price) - return vwap_order - - def place_order(self, - connector_name: str, - trading_pair: str, - is_buy: bool, - amount: Decimal, - order_type: OrderType, - price=Decimal("NaN"), - ): - if is_buy: - self.buy(connector_name, trading_pair, amount, order_type, price) - else: - self.sell(connector_name, trading_pair, amount, order_type, price) - - def did_fill_order(self, event: OrderFilledEvent): - """ - Listens to fill order event to log it and notify the Hummingbot application. - If you set up Telegram bot, you will get notification there as well. - """ - if event.trading_pair == self.vwap["trading_pair"] and event.trade_type == self.vwap["trade_type"]: - self.vwap["volume_remaining"] -= event.amount - self.vwap["delta"] = (self.vwap["target_base_volume"] - self.vwap["volume_remaining"]) / self.vwap[ - "target_base_volume"] - self.vwap["real_quote_volume"] += event.price * event.amount - self.vwap["trades"].append(event) - if math.isclose(self.vwap["delta"], 1, rel_tol=1e-5): - self.vwap["status"] = "COMPLETE" - msg = (f"({event.trading_pair}) {event.trade_type.name} order (price: {round(event.price, 2)}) of " - f"{round(event.amount, 2)} " - f"{split_hb_trading_pair(event.trading_pair)[0]} is filled.") - - self.log_with_clock(logging.INFO, msg) - self.notify_hb_app_with_timestamp(msg) - - def format_status(self) -> str: - """ - Returns status of the current strategy on user balances and current active orders. This function is called - when status command is issued. Override this function to create custom status display output. - """ - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - warning_lines = [] - warning_lines.extend(self.network_warning(self.get_market_trading_pair_tuples())) - - balance_df = self.get_balance_df() - lines.extend(["", " Balances:"] + [" " + line for line in balance_df.to_string(index=False).split("\n")]) - - try: - df = self.active_orders_df() - lines.extend(["", " Orders:"] + [" " + line for line in df.to_string(index=False).split("\n")]) - except ValueError: - lines.extend(["", " No active maker orders."]) - lines.extend(["", "VWAP Info:"] + [" " + key + ": " + value - for key, value in self.vwap.items() - if type(value) == str]) - - lines.extend(["", "VWAP Stats:"] + [" " + key + ": " + str(round(value, 4)) - for key, value in self.vwap.items() - if type(value) in [int, float, Decimal]]) - - warning_lines.extend(self.balance_warning(self.get_market_trading_pair_tuples())) - if len(warning_lines) > 0: - lines.extend(["", "*** WARNINGS ***"] + warning_lines) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/simple_xemm_example.py b/hummingbot_files/templates/master_bot_conf/scripts/simple_xemm_example.py deleted file mode 100644 index 3c8244b..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/simple_xemm_example.py +++ /dev/null @@ -1,204 +0,0 @@ -from decimal import Decimal - -import pandas as pd - -from hummingbot.core.data_type.common import OrderType, TradeType -from hummingbot.core.data_type.order_candidate import OrderCandidate -from hummingbot.core.event.events import OrderFilledEvent -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class SimpleXEMM(ScriptStrategyBase): - """ - BotCamp Cohort: Sept 2022 - Design Template: https://hummingbot-foundation.notion.site/Simple-XEMM-Example-f08cf7546ea94a44b389672fd21bb9ad - Video: https://www.loom.com/share/ca08fe7bc3d14ba68ae704305ac78a3a - Description: - A simplified version of Hummingbot cross-exchange market making strategy, this bot makes a market on - the maker pair and hedges any filled trades in the taker pair. If the spread (difference between maker order price - and taker hedge price) dips below min_spread, the bot refreshes the order - """ - - maker_exchange = "kucoin_paper_trade" - maker_pair = "ETH-USDT" - taker_exchange = "gate_io_paper_trade" - taker_pair = "ETH-USDT" - - order_amount = 0.1 # amount for each order - spread_bps = 10 # bot places maker orders at this spread to taker price - min_spread_bps = 0 # bot refreshes order if spread is lower than min-spread - slippage_buffer_spread_bps = 100 # buffer applied to limit taker hedging trades on taker exchange - max_order_age = 120 # bot refreshes orders after this age - - markets = {maker_exchange: {maker_pair}, taker_exchange: {taker_pair}} - - buy_order_placed = False - sell_order_placed = False - - def on_tick(self): - taker_buy_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - taker_sell_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, False, self.order_amount) - - if not self.buy_order_placed: - maker_buy_price = taker_sell_result.result_price * Decimal(1 - self.spread_bps / 10000) - buy_order_amount = min(self.order_amount, self.buy_hedging_budget()) - buy_order = OrderCandidate(trading_pair=self.maker_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal(buy_order_amount), price=maker_buy_price) - buy_order_adjusted = self.connectors[self.maker_exchange].budget_checker.adjust_candidate(buy_order, all_or_none=False) - self.buy(self.maker_exchange, self.maker_pair, buy_order_adjusted.amount, buy_order_adjusted.order_type, buy_order_adjusted.price) - self.buy_order_placed = True - - if not self.sell_order_placed: - maker_sell_price = taker_buy_result.result_price * Decimal(1 + self.spread_bps / 10000) - sell_order_amount = min(self.order_amount, self.sell_hedging_budget()) - sell_order = OrderCandidate(trading_pair=self.maker_pair, is_maker=True, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal(sell_order_amount), price=maker_sell_price) - sell_order_adjusted = self.connectors[self.maker_exchange].budget_checker.adjust_candidate(sell_order, all_or_none=False) - self.sell(self.maker_exchange, self.maker_pair, sell_order_adjusted.amount, sell_order_adjusted.order_type, sell_order_adjusted.price) - self.sell_order_placed = True - - for order in self.get_active_orders(connector_name=self.maker_exchange): - cancel_timestamp = order.creation_timestamp / 1000000 + self.max_order_age - if order.is_buy: - buy_cancel_threshold = taker_sell_result.result_price * Decimal(1 - self.min_spread_bps / 10000) - if order.price > buy_cancel_threshold or cancel_timestamp < self.current_timestamp: - self.logger().info(f"Cancelling buy order: {order.client_order_id}") - self.cancel(self.maker_exchange, order.trading_pair, order.client_order_id) - self.buy_order_placed = False - else: - sell_cancel_threshold = taker_buy_result.result_price * Decimal(1 + self.min_spread_bps / 10000) - if order.price < sell_cancel_threshold or cancel_timestamp < self.current_timestamp: - self.logger().info(f"Cancelling sell order: {order.client_order_id}") - self.cancel(self.maker_exchange, order.trading_pair, order.client_order_id) - self.sell_order_placed = False - return - - def buy_hedging_budget(self) -> Decimal: - balance = self.connectors[self.taker_exchange].get_available_balance("ETH") - return balance - - def sell_hedging_budget(self) -> Decimal: - balance = self.connectors[self.taker_exchange].get_available_balance("USDT") - taker_buy_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - return balance / taker_buy_result.result_price - - def is_active_maker_order(self, event: OrderFilledEvent): - """ - Helper function that checks if order is an active order on the maker exchange - """ - for order in self.get_active_orders(connector_name=self.maker_exchange): - if order.client_order_id == event.order_id: - return True - return False - - def did_fill_order(self, event: OrderFilledEvent): - - mid_price = self.connectors[self.maker_exchange].get_mid_price(self.maker_pair) - if event.trade_type == TradeType.BUY and self.is_active_maker_order(event): - taker_sell_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, False, self.order_amount) - sell_price_with_slippage = taker_sell_result.result_price * Decimal(1 - self.slippage_buffer_spread_bps / 10000) - self.logger().info(f"Filled maker buy order with price: {event.price}") - sell_spread_bps = (taker_sell_result.result_price - event.price) / mid_price * 10000 - self.logger().info(f"Sending taker sell order at price: {taker_sell_result.result_price} spread: {int(sell_spread_bps)} bps") - sell_order = OrderCandidate(trading_pair=self.taker_pair, is_maker=False, order_type=OrderType.LIMIT, order_side=TradeType.SELL, amount=Decimal(event.amount), price=sell_price_with_slippage) - sell_order_adjusted = self.connectors[self.taker_exchange].budget_checker.adjust_candidate(sell_order, all_or_none=False) - self.sell(self.taker_exchange, self.taker_pair, sell_order_adjusted.amount, sell_order_adjusted.order_type, sell_order_adjusted.price) - self.buy_order_placed = False - else: - if event.trade_type == TradeType.SELL and self.is_active_maker_order(event): - taker_buy_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - buy_price_with_slippage = taker_buy_result.result_price * Decimal(1 + self.slippage_buffer_spread_bps / 10000) - buy_spread_bps = (event.price - taker_buy_result.result_price) / mid_price * 10000 - self.logger().info(f"Filled maker sell order at price: {event.price}") - self.logger().info(f"Sending taker buy order: {taker_buy_result.result_price} spread: {int(buy_spread_bps)}") - buy_order = OrderCandidate(trading_pair=self.taker_pair, is_maker=False, order_type=OrderType.LIMIT, order_side=TradeType.BUY, amount=Decimal(event.amount), price=buy_price_with_slippage) - buy_order_adjusted = self.connectors[self.taker_exchange].budget_checker.adjust_candidate(buy_order, all_or_none=False) - self.buy(self.taker_exchange, self.taker_pair, buy_order_adjusted.amount, buy_order_adjusted.order_type, buy_order_adjusted.price) - self.sell_order_placed = False - - def exchanges_df(self) -> pd.DataFrame: - """ - Return a custom data frame of prices on maker vs taker exchanges for display purposes - """ - mid_price = self.connectors[self.maker_exchange].get_mid_price(self.maker_pair) - maker_buy_result = self.connectors[self.maker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - maker_sell_result = self.connectors[self.maker_exchange].get_price_for_volume(self.taker_pair, False, self.order_amount) - taker_buy_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - taker_sell_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, False, self.order_amount) - maker_buy_spread_bps = (maker_buy_result.result_price - taker_buy_result.result_price) / mid_price * 10000 - maker_sell_spread_bps = (taker_sell_result.result_price - maker_sell_result.result_price) / mid_price * 10000 - columns = ["Exchange", "Market", "Mid Price", "Buy Price", "Sell Price", "Buy Spread", "Sell Spread"] - data = [] - data.append([ - self.maker_exchange, - self.maker_pair, - float(self.connectors[self.maker_exchange].get_mid_price(self.maker_pair)), - float(maker_buy_result.result_price), - float(maker_sell_result.result_price), - int(maker_buy_spread_bps), - int(maker_sell_spread_bps) - ]) - data.append([ - self.taker_exchange, - self.taker_pair, - float(self.connectors[self.taker_exchange].get_mid_price(self.maker_pair)), - float(taker_buy_result.result_price), - float(taker_sell_result.result_price), - int(-maker_buy_spread_bps), - int(-maker_sell_spread_bps) - ]) - df = pd.DataFrame(data=data, columns=columns) - return df - - def active_orders_df(self) -> pd.DataFrame: - """ - Returns a custom data frame of all active maker orders for display purposes - """ - columns = ["Exchange", "Market", "Side", "Price", "Amount", "Spread Mid", "Spread Cancel", "Age"] - data = [] - mid_price = self.connectors[self.maker_exchange].get_mid_price(self.maker_pair) - taker_buy_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, True, self.order_amount) - taker_sell_result = self.connectors[self.taker_exchange].get_price_for_volume(self.taker_pair, False, self.order_amount) - buy_cancel_threshold = taker_sell_result.result_price * Decimal(1 - self.min_spread_bps / 10000) - sell_cancel_threshold = taker_buy_result.result_price * Decimal(1 + self.min_spread_bps / 10000) - for connector_name, connector in self.connectors.items(): - for order in self.get_active_orders(connector_name): - age_txt = "n/a" if order.age() <= 0. else pd.Timestamp(order.age(), unit='s').strftime('%H:%M:%S') - spread_mid_bps = (mid_price - order.price) / mid_price * 10000 if order.is_buy else (order.price - mid_price) / mid_price * 10000 - spread_cancel_bps = (buy_cancel_threshold - order.price) / buy_cancel_threshold * 10000 if order.is_buy else (order.price - sell_cancel_threshold) / sell_cancel_threshold * 10000 - data.append([ - self.maker_exchange, - order.trading_pair, - "buy" if order.is_buy else "sell", - float(order.price), - float(order.quantity), - int(spread_mid_bps), - int(spread_cancel_bps), - age_txt - ]) - if not data: - raise ValueError - df = pd.DataFrame(data=data, columns=columns) - df.sort_values(by=["Market", "Side"], inplace=True) - return df - - def format_status(self) -> str: - """ - Returns status of the current strategy on user balances and current active orders. This function is called - when status command is issued. Override this function to create custom status display output. - """ - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - - balance_df = self.get_balance_df() - lines.extend(["", " Balances:"] + [" " + line for line in balance_df.to_string(index=False).split("\n")]) - - exchanges_df = self.exchanges_df() - lines.extend(["", " Exchanges:"] + [" " + line for line in exchanges_df.to_string(index=False).split("\n")]) - - try: - orders_df = self.active_orders_df() - lines.extend(["", " Active Orders:"] + [" " + line for line in orders_df.to_string(index=False).split("\n")]) - except ValueError: - lines.extend(["", " No active maker orders."]) - - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/strategy_v2_launcher.py b/hummingbot_files/templates/master_bot_conf/scripts/strategy_v2_launcher.py deleted file mode 100644 index 392972b..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/strategy_v2_launcher.py +++ /dev/null @@ -1,110 +0,0 @@ -import inspect -import os -import importlib.util - -from hummingbot.core.data_type.common import OrderType, PositionMode, TradeType, PositionSide, PositionAction -from hummingbot.smart_components.strategy_frameworks.data_types import ( - ExecutorHandlerStatus, -) -from hummingbot.smart_components.strategy_frameworks.directional_trading import DirectionalTradingControllerBase, \ - DirectionalTradingControllerConfigBase, DirectionalTradingExecutorHandler -from hummingbot.smart_components.utils.config_encoder_decoder import ConfigEncoderDecoder -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -def load_controllers(path): - controllers = {} - for filename in os.listdir(path): - if filename.endswith('.py') and "__init__" not in filename: - module_name = filename[:-3] # strip the .py to get the module name - controllers[module_name] = {"module": module_name} - file_path = os.path.join(path, filename) - spec = importlib.util.spec_from_file_location(module_name, file_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - for name, cls in inspect.getmembers(module, inspect.isclass): - if issubclass(cls, DirectionalTradingControllerBase) and cls is not DirectionalTradingControllerBase: - controllers[module_name]["class"] = cls - if issubclass(cls, DirectionalTradingControllerConfigBase) and cls is not DirectionalTradingControllerConfigBase: - controllers[module_name]["config"] = cls - return controllers - - -def initialize_controller_from_config(encoder_decoder: ConfigEncoderDecoder, - all_controllers_info: dict, - controller_config_file: str): - config = encoder_decoder.yaml_load(f"conf/controllers_config/{controller_config_file}") - controller_info = all_controllers_info[config["strategy_name"]] - config_instance = controller_info["config"](**config) - controller_class = controller_info["class"](config_instance) - return controller_class - - -class StrategyV2Launcher(ScriptStrategyBase): - controller_configs = os.getenv("controller_configs", "bollinger_8044.yml,bollinger_8546.yml,bollinger_8883.yml") - controllers = {} - markets = {} - executor_handlers = {} - encoder_decoder = ConfigEncoderDecoder(TradeType, PositionMode, OrderType) - controllers_info = load_controllers("hummingbot/smart_components/controllers") - - for controller_config in controller_configs.split(","): - controller = initialize_controller_from_config(encoder_decoder, controllers_info, controller_config) - markets = controller.update_strategy_markets_dict(markets) - controllers[controller_config] = controller - - def __init__(self, connectors): - super().__init__(connectors) - for controller_config, controller in self.controllers.items(): - self.executor_handlers[controller_config] = DirectionalTradingExecutorHandler(strategy=self, controller=controller) - - def on_stop(self): - for connector in self.connectors.keys(): - if self.is_perpetual(connector): - self.close_open_positions(connector) - for executor_handler in self.executor_handlers.values(): - executor_handler.stop() - - @staticmethod - def is_perpetual(exchange): - """ - Checks if the exchange is a perpetual market. - """ - return "perpetual" in exchange - - def close_open_positions(self, exchange): - connector = self.connectors[exchange] - for trading_pair, position in connector.account_positions.items(): - if position.position_side == PositionSide.LONG: - self.sell(connector_name=exchange, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - elif position.position_side == PositionSide.SHORT: - self.buy(connector_name=exchange, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - for executor_handler in self.executor_handlers.values(): - if executor_handler.status == ExecutorHandlerStatus.NOT_STARTED: - executor_handler.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - for controller_config, executor_handler in self.executor_handlers.items(): - lines.extend(["\n------------------------------------------------------------------------------------------"]) - lines.extend([f"Strategy: {executor_handler.controller.config.strategy_name} | Config: {controller_config}", - executor_handler.to_format_status()]) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/v2_directional-trading_macd_bb_v1.py b/hummingbot_files/templates/master_bot_conf/scripts/v2_directional-trading_macd_bb_v1.py deleted file mode 100644 index 2e72ccd..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/v2_directional-trading_macd_bb_v1.py +++ /dev/null @@ -1,91 +0,0 @@ -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.connector_base import ConnectorBase, TradeType -from hummingbot.core.data_type.common import OrderType -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig -from hummingbot.smart_components.controllers.macd_bb_v1 import MACDBBV1, MACDBBV1Config -from hummingbot.smart_components.strategy_frameworks.data_types import ( - ExecutorHandlerStatus, - OrderLevel, - TripleBarrierConf, -) -from hummingbot.smart_components.strategy_frameworks.directional_trading.directional_trading_executor_handler import ( - DirectionalTradingExecutorHandler, -) -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class MarketMakingDmanComposed(ScriptStrategyBase): - trading_pairs = ["HBAR-USDT", "CYBER-USDT", "ETH-USDT", "LPT-USDT", "UNFI-USDT"] - leverage_by_trading_pair = { - "HBAR-USDT": 25, - "CYBER-USDT": 20, - "ETH-USDT": 100, - "LPT-USDT": 10, - "UNFI-USDT": 20, - } - triple_barrier_conf = TripleBarrierConf( - stop_loss=Decimal("0.01"), take_profit=Decimal("0.03"), - time_limit=60 * 60 * 6, - trailing_stop_activation_price_delta=Decimal("0.008"), - trailing_stop_trailing_delta=Decimal("0.004"), - open_order_type=OrderType.MARKET - ) - - order_levels = [ - OrderLevel(level=0, side=TradeType.BUY, order_amount_usd=Decimal("15"), - spread_factor=Decimal(0.5), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf), - OrderLevel(level=0, side=TradeType.SELL, order_amount_usd=Decimal("15"), - spread_factor=Decimal(0.5), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf), - ] - controllers = {} - markets = {} - executor_handlers = {} - - for trading_pair in trading_pairs: - config = MACDBBV1Config( - exchange="binance_perpetual", - trading_pair=trading_pair, - order_levels=order_levels, - candles_config=[ - CandlesConfig(connector="binance_perpetual", trading_pair=trading_pair, interval="3m", max_records=100), - ], - leverage=leverage_by_trading_pair[trading_pair], - macd_fast=21, macd_slow=42, macd_signal=9, - bb_length=100, bb_std=2.0, bb_long_threshold=0.3, bb_short_threshold=0.7, - ) - controller = MACDBBV1(config=config) - markets = controller.update_strategy_markets_dict(markets) - controllers[trading_pair] = controller - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - for trading_pair, controller in self.controllers.items(): - self.executor_handlers[trading_pair] = DirectionalTradingExecutorHandler(strategy=self, controller=controller) - - def on_stop(self): - for executor_handler in self.executor_handlers.values(): - executor_handler.stop() - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - for executor_handler in self.executor_handlers.values(): - if executor_handler.status == ExecutorHandlerStatus.NOT_STARTED: - executor_handler.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - for trading_pair, executor_handler in self.executor_handlers.items(): - if executor_handler.controller.all_candles_ready: - lines.extend( - [f"Strategy: {executor_handler.controller.config.strategy_name} | Trading Pair: {trading_pair}", - executor_handler.to_format_status()]) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_composed.py b/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_composed.py deleted file mode 100644 index c539897..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_composed.py +++ /dev/null @@ -1,145 +0,0 @@ -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.connector_base import ConnectorBase, TradeType -from hummingbot.core.data_type.common import OrderType, PositionAction, PositionSide -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig -from hummingbot.smart_components.controllers.dman_v1 import DManV1, DManV1Config -from hummingbot.smart_components.controllers.dman_v2 import DManV2, DManV2Config -from hummingbot.smart_components.strategy_frameworks.data_types import ( - ExecutorHandlerStatus, - OrderLevel, - TripleBarrierConf, -) -from hummingbot.smart_components.strategy_frameworks.market_making.market_making_executor_handler import ( - MarketMakingExecutorHandler, -) -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class MarketMakingDmanComposed(ScriptStrategyBase): - trading_pair = "HBAR-USDT" - triple_barrier_conf_top = TripleBarrierConf( - stop_loss=Decimal("0.03"), take_profit=Decimal("0.02"), - time_limit=60 * 60 * 1, - trailing_stop_activation_price_delta=Decimal("0.002"), - trailing_stop_trailing_delta=Decimal("0.0005") - ) - triple_barrier_conf_bottom = TripleBarrierConf( - stop_loss=Decimal("0.03"), take_profit=Decimal("0.02"), - time_limit=60 * 60 * 3, - trailing_stop_activation_price_delta=Decimal("0.005"), - trailing_stop_trailing_delta=Decimal("0.001") - ) - - config_v1 = DManV1Config( - exchange="binance_perpetual", - trading_pair=trading_pair, - order_levels=[ - OrderLevel(level=0, side=TradeType.BUY, order_amount_usd=Decimal("15"), - spread_factor=Decimal(1.0), order_refresh_time=60 * 30, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_top), - OrderLevel(level=1, side=TradeType.BUY, order_amount_usd=Decimal("50"), - spread_factor=Decimal(5.0), order_refresh_time=60 * 30, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=2, side=TradeType.BUY, order_amount_usd=Decimal("50"), - spread_factor=Decimal(8.0), order_refresh_time=60 * 15, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=0, side=TradeType.SELL, order_amount_usd=Decimal("15"), - spread_factor=Decimal(1.0), order_refresh_time=60 * 30, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_top), - OrderLevel(level=1, side=TradeType.SELL, order_amount_usd=Decimal("50"), - spread_factor=Decimal(5.0), order_refresh_time=60 * 30, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=2, side=TradeType.SELL, order_amount_usd=Decimal("50"), - spread_factor=Decimal(8.0), order_refresh_time=60 * 15, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - ], - candles_config=[ - CandlesConfig(connector="binance_perpetual", trading_pair=trading_pair, interval="3m", max_records=1000), - ], - leverage=25, - natr_length=21 - ) - config_v2 = DManV2Config( - exchange="binance_perpetual", - trading_pair=trading_pair, - order_levels=[ - OrderLevel(level=0, side=TradeType.BUY, order_amount_usd=Decimal(15), - spread_factor=Decimal(1.0), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_top), - OrderLevel(level=1, side=TradeType.BUY, order_amount_usd=Decimal(30), - spread_factor=Decimal(2.0), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=2, side=TradeType.BUY, order_amount_usd=Decimal(50), - spread_factor=Decimal(3.0), order_refresh_time=60 * 15, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=0, side=TradeType.SELL, order_amount_usd=Decimal(15), - spread_factor=Decimal(1.0), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_top), - OrderLevel(level=1, side=TradeType.SELL, order_amount_usd=Decimal(30), - spread_factor=Decimal(2.0), order_refresh_time=60 * 5, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - OrderLevel(level=2, side=TradeType.SELL, order_amount_usd=Decimal(50), - spread_factor=Decimal(3.0), order_refresh_time=60 * 15, - cooldown_time=15, triple_barrier_conf=triple_barrier_conf_bottom), - ], - candles_config=[ - CandlesConfig(connector="binance_perpetual", trading_pair=trading_pair, interval="3m", max_records=1000), - ], - leverage=25, - natr_length=21, macd_fast=12, macd_slow=26, macd_signal=9 - ) - dman_v1 = DManV1(config=config_v1) - dman_v2 = DManV2(config=config_v2) - - empty_markets = {} - markets = dman_v1.update_strategy_markets_dict(empty_markets) - markets = dman_v2.update_strategy_markets_dict(markets) - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - self.dman_v1_executor = MarketMakingExecutorHandler(strategy=self, controller=self.dman_v1) - self.dman_v2_executor = MarketMakingExecutorHandler(strategy=self, controller=self.dman_v2) - - def on_stop(self): - self.close_open_positions() - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - if self.dman_v1_executor.status == ExecutorHandlerStatus.NOT_STARTED: - self.dman_v1_executor.start() - if self.dman_v2_executor.status == ExecutorHandlerStatus.NOT_STARTED: - self.dman_v2_executor.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - lines.extend(["DMAN V1", self.dman_v1_executor.to_format_status()]) - lines.extend(["\n-----------------------------------------\n"]) - lines.extend(["DMAN V2", self.dman_v2_executor.to_format_status()]) - return "\n".join(lines) - - def close_open_positions(self): - # we are going to close all the open positions when the bot stops - for connector_name, connector in self.connectors.items(): - for trading_pair, position in connector.account_positions.items(): - if trading_pair in self.markets[connector_name]: - if position.position_side == PositionSide.LONG: - self.sell(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - elif position.position_side == PositionSide.SHORT: - self.buy(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v1_multiple_pairs.py b/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v1_multiple_pairs.py deleted file mode 100644 index 24189cf..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v1_multiple_pairs.py +++ /dev/null @@ -1,133 +0,0 @@ -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.core.data_type.common import OrderType, PositionAction, PositionSide -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig -from hummingbot.smart_components.controllers.dman_v1 import DManV1, DManV1Config -from hummingbot.smart_components.strategy_frameworks.data_types import ExecutorHandlerStatus, TripleBarrierConf -from hummingbot.smart_components.strategy_frameworks.market_making.market_making_executor_handler import ( - MarketMakingExecutorHandler, -) -from hummingbot.smart_components.utils.distributions import Distributions -from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DManV1MultiplePairs(ScriptStrategyBase): - # Account configuration - exchange = "binance_perpetual" - trading_pairs = ["ETH-USDT"] - leverage = 20 - - # Candles configuration - candles_exchange = "binance_perpetual" - candles_interval = "3m" - candles_max_records = 300 - - # Orders configuration - order_amount = Decimal("25") - n_levels = 5 - start_spread = 0.0006 - step_between_orders = 0.009 - order_refresh_time = 60 * 15 # 15 minutes - cooldown_time = 5 - - # Triple barrier configuration - stop_loss = Decimal("0.2") - take_profit = Decimal("0.06") - time_limit = 60 * 60 * 12 - trailing_stop_activation_price_delta = Decimal(str(step_between_orders / 2)) - trailing_stop_trailing_delta = Decimal(str(step_between_orders / 3)) - - # Advanced configurations - natr_length = 100 - - # Applying the configuration - order_level_builder = OrderLevelBuilder(n_levels=n_levels) - order_levels = order_level_builder.build_order_levels( - amounts=order_amount, - spreads=Distributions.arithmetic(n_levels=n_levels, start=start_spread, step=step_between_orders), - triple_barrier_confs=TripleBarrierConf( - stop_loss=stop_loss, take_profit=take_profit, time_limit=time_limit, - trailing_stop_activation_price_delta=trailing_stop_activation_price_delta, - trailing_stop_trailing_delta=trailing_stop_trailing_delta), - order_refresh_time=order_refresh_time, - cooldown_time=cooldown_time, - ) - controllers = {} - markets = {} - executor_handlers = {} - - for trading_pair in trading_pairs: - config = DManV1Config( - exchange=exchange, - trading_pair=trading_pair, - order_levels=order_levels, - candles_config=[ - CandlesConfig(connector=candles_exchange, trading_pair=trading_pair, - interval=candles_interval, max_records=candles_max_records), - ], - leverage=leverage, - natr_length=natr_length, - ) - controller = DManV1(config=config) - markets = controller.update_strategy_markets_dict(markets) - controllers[trading_pair] = controller - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - for trading_pair, controller in self.controllers.items(): - self.executor_handlers[trading_pair] = MarketMakingExecutorHandler(strategy=self, controller=controller) - - @property - def is_perpetual(self): - """ - Checks if the exchange is a perpetual market. - """ - return "perpetual" in self.exchange - - def on_stop(self): - if self.is_perpetual: - self.close_open_positions() - for executor_handler in self.executor_handlers.values(): - executor_handler.stop() - - def close_open_positions(self): - # we are going to close all the open positions when the bot stops - for connector_name, connector in self.connectors.items(): - for trading_pair, position in connector.account_positions.items(): - if trading_pair in self.markets[connector_name]: - if position.position_side == PositionSide.LONG: - self.sell(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - elif position.position_side == PositionSide.SHORT: - self.buy(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - for executor_handler in self.executor_handlers.values(): - if executor_handler.status == ExecutorHandlerStatus.NOT_STARTED: - executor_handler.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - for trading_pair, executor_handler in self.executor_handlers.items(): - lines.extend( - [f"Strategy: {executor_handler.controller.config.strategy_name} | Trading Pair: {trading_pair}", - executor_handler.to_format_status()]) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v2_multiple_pairs.py b/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v2_multiple_pairs.py deleted file mode 100644 index a3d7af2..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v2_multiple_pairs.py +++ /dev/null @@ -1,139 +0,0 @@ -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.core.data_type.common import OrderType, PositionAction, PositionSide -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig -from hummingbot.smart_components.controllers.dman_v2 import DManV2, DManV2Config -from hummingbot.smart_components.strategy_frameworks.data_types import ExecutorHandlerStatus, TripleBarrierConf -from hummingbot.smart_components.strategy_frameworks.market_making.market_making_executor_handler import ( - MarketMakingExecutorHandler, -) -from hummingbot.smart_components.utils.distributions import Distributions -from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DManV2MultiplePairs(ScriptStrategyBase): - # Account configuration - exchange = "binance_perpetual" - trading_pairs = ["ETH-USDT"] - leverage = 20 - - # Candles configuration - candles_exchange = "binance_perpetual" - candles_interval = "3m" - candles_max_records = 300 - - # Orders configuration - order_amount = Decimal("25") - n_levels = 5 - start_spread = 0.0006 - step_between_orders = 0.009 - order_refresh_time = 60 * 15 # 15 minutes - cooldown_time = 5 - - # Triple barrier configuration - stop_loss = Decimal("0.2") - take_profit = Decimal("0.06") - time_limit = 60 * 60 * 12 - trailing_stop_activation_price_delta = Decimal(str(step_between_orders / 2)) - trailing_stop_trailing_delta = Decimal(str(step_between_orders / 3)) - - # Advanced configurations - macd_fast = 12 - macd_slow = 26 - macd_signal = 9 - natr_length = 100 - - # Applying the configuration - order_level_builder = OrderLevelBuilder(n_levels=n_levels) - order_levels = order_level_builder.build_order_levels( - amounts=order_amount, - spreads=Distributions.arithmetic(n_levels=n_levels, start=start_spread, step=step_between_orders), - triple_barrier_confs=TripleBarrierConf( - stop_loss=stop_loss, take_profit=take_profit, time_limit=time_limit, - trailing_stop_activation_price_delta=trailing_stop_activation_price_delta, - trailing_stop_trailing_delta=trailing_stop_trailing_delta), - order_refresh_time=order_refresh_time, - cooldown_time=cooldown_time, - ) - controllers = {} - markets = {} - executor_handlers = {} - - for trading_pair in trading_pairs: - config = DManV2Config( - exchange=exchange, - trading_pair=trading_pair, - order_levels=order_levels, - candles_config=[ - CandlesConfig(connector=candles_exchange, trading_pair=trading_pair, - interval=candles_interval, max_records=candles_max_records), - ], - leverage=leverage, - macd_fast=macd_fast, - macd_slow=macd_slow, - macd_signal=macd_signal, - natr_length=natr_length, - ) - controller = DManV2(config=config) - markets = controller.update_strategy_markets_dict(markets) - controllers[trading_pair] = controller - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - for trading_pair, controller in self.controllers.items(): - self.executor_handlers[trading_pair] = MarketMakingExecutorHandler(strategy=self, controller=controller) - - @property - def is_perpetual(self): - """ - Checks if the exchange is a perpetual market. - """ - return "perpetual" in self.exchange - - def on_stop(self): - if self.is_perpetual: - self.close_open_positions() - for executor_handler in self.executor_handlers.values(): - executor_handler.stop() - - def close_open_positions(self): - # we are going to close all the open positions when the bot stops - for connector_name, connector in self.connectors.items(): - for trading_pair, position in connector.account_positions.items(): - if trading_pair in self.markets[connector_name]: - if position.position_side == PositionSide.LONG: - self.sell(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - elif position.position_side == PositionSide.SHORT: - self.buy(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - for executor_handler in self.executor_handlers.values(): - if executor_handler.status == ExecutorHandlerStatus.NOT_STARTED: - executor_handler.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - for trading_pair, executor_handler in self.executor_handlers.items(): - lines.extend( - [f"Strategy: {executor_handler.controller.config.strategy_name} | Trading Pair: {trading_pair}", - executor_handler.to_format_status()]) - return "\n".join(lines) diff --git a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v3_multiple_pairs.py b/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v3_multiple_pairs.py deleted file mode 100644 index dd58f6c..0000000 --- a/hummingbot_files/templates/master_bot_conf/scripts/v2_market-making_dman_v3_multiple_pairs.py +++ /dev/null @@ -1,141 +0,0 @@ -from decimal import Decimal -from typing import Dict - -from hummingbot.connector.connector_base import ConnectorBase -from hummingbot.core.data_type.common import OrderType, PositionAction, PositionSide -from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig -from hummingbot.smart_components.controllers.dman_v3 import DManV3, DManV3Config -from hummingbot.smart_components.strategy_frameworks.data_types import ExecutorHandlerStatus, TripleBarrierConf -from hummingbot.smart_components.strategy_frameworks.market_making.market_making_executor_handler import ( - MarketMakingExecutorHandler, -) -from hummingbot.smart_components.utils.distributions import Distributions -from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder -from hummingbot.strategy.script_strategy_base import ScriptStrategyBase - - -class DManV3MultiplePairs(ScriptStrategyBase): - # Account configuration - exchange = "binance_perpetual" - trading_pairs = ["ETH-USDT"] - leverage = 20 - - # Candles configuration - candles_exchange = "binance_perpetual" - candles_interval = "1h" - candles_max_records = 300 - bollinger_band_length = 200 - bollinger_band_std = 3.0 - - # Orders configuration - order_amount = Decimal("25") - n_levels = 5 - start_spread = 0.5 # percentage of the bollinger band (0.5 means that the order will be between the bollinger mid-price and the upper band) - step_between_orders = 0.3 # percentage of the bollinger band (0.1 means that the next order will be 10% of the bollinger band away from the previous order) - - # Triple barrier configuration - stop_loss = Decimal("0.01") - take_profit = Decimal("0.03") - time_limit = 60 * 60 * 6 - trailing_stop_activation_price_delta = Decimal("0.008") - trailing_stop_trailing_delta = Decimal("0.004") - - # Advanced configurations - side_filter = True - dynamic_spread_factor = True - dynamic_target_spread = False - smart_activation = False - activation_threshold = Decimal("0.001") - - # Applying the configuration - order_level_builder = OrderLevelBuilder(n_levels=n_levels) - order_levels = order_level_builder.build_order_levels( - amounts=order_amount, - spreads=Distributions.arithmetic(n_levels=n_levels, start=start_spread, step=step_between_orders), - triple_barrier_confs=TripleBarrierConf( - stop_loss=stop_loss, take_profit=take_profit, time_limit=time_limit, - trailing_stop_activation_price_delta=trailing_stop_activation_price_delta, - trailing_stop_trailing_delta=trailing_stop_trailing_delta), - ) - controllers = {} - markets = {} - executor_handlers = {} - - for trading_pair in trading_pairs: - config = DManV3Config( - exchange=exchange, - trading_pair=trading_pair, - order_levels=order_levels, - candles_config=[ - CandlesConfig(connector=candles_exchange, trading_pair=trading_pair, - interval=candles_interval, max_records=candles_max_records), - ], - bb_length=bollinger_band_length, - bb_std=bollinger_band_std, - side_filter=side_filter, - dynamic_spread_factor=dynamic_spread_factor, - dynamic_target_spread=dynamic_target_spread, - smart_activation=smart_activation, - activation_threshold=activation_threshold, - leverage=leverage, - ) - controller = DManV3(config=config) - markets = controller.update_strategy_markets_dict(markets) - controllers[trading_pair] = controller - - def __init__(self, connectors: Dict[str, ConnectorBase]): - super().__init__(connectors) - for trading_pair, controller in self.controllers.items(): - self.executor_handlers[trading_pair] = MarketMakingExecutorHandler(strategy=self, controller=controller) - - @property - def is_perpetual(self): - """ - Checks if the exchange is a perpetual market. - """ - return "perpetual" in self.exchange - - def on_stop(self): - if self.is_perpetual: - self.close_open_positions() - for executor_handler in self.executor_handlers.values(): - executor_handler.stop() - - def close_open_positions(self): - # we are going to close all the open positions when the bot stops - for connector_name, connector in self.connectors.items(): - for trading_pair, position in connector.account_positions.items(): - if trading_pair in self.markets[connector_name]: - if position.position_side == PositionSide.LONG: - self.sell(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - elif position.position_side == PositionSide.SHORT: - self.buy(connector_name=connector_name, - trading_pair=position.trading_pair, - amount=abs(position.amount), - order_type=OrderType.MARKET, - price=connector.get_mid_price(position.trading_pair), - position_action=PositionAction.CLOSE) - - def on_tick(self): - """ - This shows you how you can start meta controllers. You can run more than one at the same time and based on the - market conditions, you can orchestrate from this script when to stop or start them. - """ - for executor_handler in self.executor_handlers.values(): - if executor_handler.status == ExecutorHandlerStatus.NOT_STARTED: - executor_handler.start() - - def format_status(self) -> str: - if not self.ready_to_trade: - return "Market connectors are not ready." - lines = [] - for trading_pair, executor_handler in self.executor_handlers.items(): - lines.extend( - [f"Strategy: {executor_handler.controller.config.strategy_name} | Trading Pair: {trading_pair}", - executor_handler.to_format_status()]) - return "\n".join(lines) diff --git a/main.py b/main.py index 5d61dbf..728a8ae 100644 --- a/main.py +++ b/main.py @@ -11,31 +11,31 @@ def main_page(): [ Page("main.py", "Hummingbot Dashboard", "📊"), Section("Bot Orchestration", "🐙"), - Page("pages/bot_orchestration/app.py", "Instances", "🦅"), - Page("pages/launch_bot/app.py", "Deploy", "🚀"), + Page("frontend/pages/bot_orchestration/app.py", "Instances", "🦅"), + Page("frontend/pages/launch_bot/app.py", "Deploy", "🚀"), Section("Config Generator", "🎛️"), - Page("pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), - Page("pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), - Page("pages/bollinger_v1/app.py", "Bollinger V1", "📈"), - Page("pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), - Page("pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), - Page("pages/macd_bb_v1/app.py", "MACD_BB V1", "📊"), - Page("pages/dman_v5/app.py", "D-Man V5", "📊"), - Page("pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), - Page("pages/position_builder/app.py", "Position Builder", "🔭"), - Page("pages/dynamic_position_builder/app.py", "Dynamic Position Builder", "🔭"), + Page("frontend/pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), + Page("frontend/pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), + Page("frontend/pages/bollinger_v1/app.py", "Bollinger V1", "📈"), + Page("frontend/pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), + Page("frontend/pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), + Page("frontend/pages/macd_bb_v1/app.py", "MACD_BB V1", "📊"), + Page("frontend/pages/dman_v5/app.py", "D-Man V5", "📊"), + Page("frontend/pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), + Page("frontend/pages/position_builder/app.py", "Position Builder", "🔭"), + Page("frontend/pages/dynamic_position_builder/app.py", "Dynamic Position Builder", "🔭"), # Page("pages/master_conf/app.py", "Credentials", "🗝️"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), Section("Data", "💾"), - Page("pages/data_download_candles/app.py", "Download Candles", "💹"), + Page("frontend/pages/data_download_candles/app.py", "Download Candles", "💹"), # Page("pages/backtest_create/create.py", "Create", "⚔️"), # Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), # Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), Section("Community Pages", "👨‍👩‍👧‍👦"), - Page("pages/strategy_performance/app.py", "Strategy Performance", "🚀"), - Page("pages/db_inspector/app.py", "DB Inspector", "🔍"), - Page("pages/token_spreads/app.py", "Token Spreads", "🧙"), - Page("pages/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"), + Page("frontend/pages/strategy_performance/app.py", "Strategy Performance", "🚀"), + Page("frontend/pages/db_inspector/app.py", "DB Inspector", "🔍"), + Page("frontend/pages/token_spreads/app.py", "Token Spreads", "🧙"), + Page("frontend/pages/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"), ] ) @@ -96,11 +96,9 @@ def main_page(): st.header("Feedback and Issues") - st.write( - "Please give us feedback in the **#dashboard** channel of the [Hummingbot Discord](https://discord.gg/hummingbot)! 🙏") + st.write("Please give us feedback in the **#dashboard** channel of the [Hummingbot Discord](https://discord.gg/hummingbot)! 🙏") - st.write( - "If you encounter any bugs or have suggestions for improvement, please create an issue in the [Hummingbot Dashboard Github](https://github.com/hummingbot/dashboard).") + st.write("If you encounter any bugs or have suggestions for improvement, please create an issue in the [Hummingbot Dashboard Github](https://github.com/hummingbot/dashboard).") config = read_yaml_file("credentials.yml") diff --git a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb index 6c5a876..befef71 100644 --- a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb +++ b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb @@ -11,18 +11,18 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 10080/10080 [00:09<00:00, 1117.51it/s]\n", + "100%|██████████| 10080/10080 [00:13<00:00, 772.37it/s]\n", "Unclosed client session\n", - "client_session: \n", + "client_session: \n", "Unclosed connector\n", - "connections: ['[(, 28635.105190041)]']\n", - "connector: \n" + "connections: ['[(, 127427.978703458)]']\n", + "connector: \n" ] } ], @@ -32,8 +32,8 @@ "from utils.hummingbot_processes import aget_candles_list\n", "\n", "exchange = \"binance_perpetual\"\n", - "symbols = [\"MATIC-USDT\"]\n", - "intervals = [\"1m\"]\n", + "symbols = [\"XRP-USDT\"]\n", + "intervals = [\"3m\"]\n", "max_records = 10080\n", "\n", "candles_config = [CandlesConfig(connector=exchange,\n", @@ -46,14 +46,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:31:26.786920Z", - "start_time": "2024-04-11T21:31:17.744594Z" + "end_time": "2024-04-18T22:02:52.165106Z", + "start_time": "2024-04-18T22:02:39.073506Z" } } }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "outputs": [], "source": [ "import pandas as pd\n", @@ -91,14 +91,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:09:39.343126Z", - "start_time": "2024-04-11T21:09:39.339565Z" + "end_time": "2024-04-18T22:02:54.638744Z", + "start_time": "2024-04-18T22:02:54.635152Z" } } }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 14, "outputs": [], "source": [ "window_length = 240 # Replace with your desired\n", @@ -108,19 +108,19 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:15:49.256567Z", - "start_time": "2024-04-11T21:15:48.820292Z" + "end_time": "2024-04-18T22:02:56.718508Z", + "start_time": "2024-04-18T22:02:56.321691Z" } } }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "outputs": [ { "data": { "text/plain": "
", - "image/png": "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07x5c9Wq5fjP3YWOY0bnzp2VlZWl5cuXa+3atfr111/1/vvvl7slsmnTppfcV1ngaNOmzQXbHD58WEePHtU777xjD0JlX2XhuaCgoNJ1L1myRK+99prCwsL0r3/9q1y/ycnJ0YgRIxQeHm5/7un222+XJB07dsyhrb+/f7mweP77s3//fjVo0EABAQEO7Zo1a+bwes+ePTIMQ88880y583zuuecczjMtLU1Hjx5VixYt1LZtWz3++OP6/vvvL3kNAMAVeEYKAKrBgQMHdOzYsXJ/NJ6rTp06WrNmjVatWqUvv/xSS5Ys0bx589SzZ08tW7asUtNkm3muqbIu9KHBJSUl1TZ194WOY7jxEzyuvvpqxcXFXbKdq96TskD9wAMPVPhckSSH6fUv5Ny6ExIS1KpVK91555164403lJqaKunse9u7d28dOXJETz75pFq1aqXAwEAdPHhQI0aMKBfuXdkPyvY9btw4JSQkVNim7Ofotttu0969e/X5559r2bJl+tvf/qapU6dq1qxZevDBB11WEwBUhCAFANXggw8+kKQL/mFYplatWurVq5d69eqlKVOmaNKkSXrqqae0atUqxcXFXTDUOOv8W6IMw9CePXsc/iC/6qqrdPTo0XLb7t+/X9dee639tZnaoqOjtXz5ch0/ftxhVOrHH3+0r3eF6Ohoff/99yotLXUYlXL1cS5X2XX84YcfLtimXr16Cg4OVklJSaUCXGX169dPt99+uyZNmqQ//vGPCgwM1LZt27Rr1y699957Gj58uL3t5cyGFx0drVWrVtmn5C+zZ88eh3Zl18JqtVbqPMPDwzVy5EiNHDlSRUVFuu222zRhwgSCFIAqx619AFDFVq5cqeeff15NmzbV0KFDL9juyJEj5Za1b99eklRcXCxJCgwMlKQKg40z3n//fYfntj766CPl5uaqT58+9mXXXXed1q1bZ/9QX0lauHBhuWnSzdTWt29flZSU6C9/+YvD8qlTp8pisTgc/3L07dtXeXl5mjdvnn3ZmTNn9OabbyooKMh+q5q71atXT7fddpveffdd5eTkOKwrG3WrXbu2Bg4cqI8//rjCwHX48GGnj//kk0/qt99+01//+lf7sc49dtn3b7zxhtPHSEhIkM1msx9DOjv6NGPGDId29evXV/fu3fX2228rNze33H7OPc/ffvvNYV1QUJCaNWtm/3kBgKrEiBQAuNDixYv1448/6syZM8rPz9fKlSuVlZWl6OhoffHFFxf9ENa0tDStWbNG/fr1U3R0tAoKCvTWW2+pYcOG6tq1q6SzoSYsLEyzZs1ScHCwAgMD1blz50o9h1OR8PBwde3aVSNHjlR+fr6mTZumZs2aOUwI8OCDD+qjjz7SHXfcod///vfau3ev5syZ4zD5g9na+vfvrx49euipp57Szz//rBtuuEHLli3T559/rkcffbTcvp01evRovf322xoxYoQ2bdqkJk2a6KOPPtI333yjadOmXfSZteo2ffp0de3aVTfeeKNGjx6tpk2b6ueff9aXX36pLVu2SJJeeuklrVq1Sp07d9aoUaMUExOjI0eOaPPmzVq+fHmFYbwy+vTpozZt2mjKlClKSUlRq1atdN1112ncuHE6ePCgQkJC9PHHH1/ymbSLGTBggDp16qTHHntMe/bsUatWrfTFF1/Yaz53RHPGjBnq2rWr2rZtq1GjRunaa69Vfn6+srOzdeDAAW3dulWSFBMTo+7du6tjx44KDw/Xxo0b9dFHH2nMmDFO1wkAleau6QIB4EpSNv152Zevr68RGRlp9O7d23jjjTccptkuc/705ytWrDDuvvtuIyoqyvD19TWioqKM+++/39i1a5fDdp9//rkRExNj+Pj4OEwbffvttxvXX399hfVdaPrz//u//zPGjx9v1K9f36hTp47Rr1+/ctNvG4ZhvP7668Y111xj+Pn5GbfeequxcePGcvu8WG3nT39uGIZx/PhxY+zYsUZUVJRhtVqN5s2bG6+++qrDdN+GYVQ4NbdhXHha9vPl5+cbI0eONK6++mrD19fXaNu2bYVTtJud/vxSbcumOH/11VcvuO78On744QfjnnvuMcLCwgx/f3+jZcuWxjPPPFPufFJSUoxGjRoZVqvViIyMNHr16mW88847l1V3RkaGQ007duww4uLijKCgIOPqq682Ro0aZZ92/ty6ExMTjcDAwHL7O79/G4ZhHD582BgyZIgRHBxshIaGGiNGjDC++eYbQ5Ixd+5ch7Z79+41hg8fbkRGRhpWq9W45pprjDvvvNP46KOP7G1eeOEFo1OnTkZYWJhRp04do1WrVsaLL75onD59+pLXAgAul8Uw3PikLgAAqNE+++wz3XPPPfr666916623urscAKg0ghQAAKgW//73vx1m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" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1sAAAIjCAYAAAD1OgEdAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAABPnklEQVR4nO3dfXyP9f////trvHZiNjMnO3Eyc5KTnBXRclrGnCSkIiujRR9tFdKJdxEqSjnPO/V+v6MTSjoRisxJVOS0IZWzZBXbRMwm87Id3z/89vr1spnt5XXstZfdrpeLS47jeB7H8TheD8e4d5y8LIZhGAIAAAAAuJSXuwsAAAAAgGsRYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwBKqQkTJshisZTIvjp37qzOnTvbp7/66itZLBZ99NFHJbL/IUOGqE6dOiWyL2dlZmbqwQcfVGhoqCwWi0aOHGnavn799VdZLBYtWLDAtH0AAMxH2AKAErBgwQJZLBb7L19fX4WHhysmJkazZ8/WmTNnXLKfo0ePasKECUpOTnbJ9lypNNdWFJMnT9aCBQs0YsQIvfvuu7r//vsvO7ZOnToO/a5evbo6dOigTz/9tAQrLr5L6/b391ebNm30zjvvuLs0APBIFsMwDHcXAQDXugULFmjo0KGaNGmSIiMjZbPZlJqaqq+++kpJSUmqXbu2li1bpubNm9vXuXDhgi5cuCBfX98i72f79u266aabNH/+fA0ZMqTI650/f16S5O3tLenila1bb71VS5Ys0V133VXk7Thbm81mU25urnx8fFyyLzPcfPPNKl++vL755psrjq1Tp44qV66sxx9/XNLFoPnGG2/ol19+0euvv67/+7//K3R9wzCUnZ0tq9WqcuXKuaT+ori07mPHjum///2v9u/frzfffFPDhg0rsVoA4FpQ3t0FAEBZ0qNHD7Vu3do+PXbsWK1bt06333677rjjDv3000/y8/OTJJUvX17ly5v7Y/rs2bOqUKGCPWS5i9Vqdev+iyI9PV1NmjQp8vgaNWrovvvus08PHjxY9evX14wZMy4bti5cuKDc3Fx5e3sXK2S70qV1DxkyRHXr1tWMGTMIWwBQTNxGCABudtttt2ncuHE6cuSI3nvvPfv8gp7ZSkpKUvv27RUUFKSKFSuqYcOG+te//iXp4tWom266SZI0dOhQ+61gec/9dO7cWU2bNtWOHTvUsWNHVahQwb7upc9s5cnJydG//vUvhYaGyt/fX3fccYd+++03hzF16tQp8CraP7d5pdoKemYrKytLjz/+uGrVqiUfHx81bNhQr776qi69IcNisSgxMVFLly5V06ZN5ePjo+uvv16rVq0q+AO/RHp6uuLj4xUSEiJfX1+1aNFCb7/9tn153vNrhw8f1ueff26v/ddffy3S9vOEhoaqcePGOnz4sKT//7msV199VTNnzlS9evXk4+OjH3/88bLPbP3888+65557VK1aNfn5+alhw4Z65plnHMb88ccfeuCBBxQSEmL/LN56661i1fpP1apVU6NGjXTo0CGH+V9//bXuvvtu1a5dWz4+PqpVq5ZGjRqlv//+22HckCFDVLFiRf3xxx/q27evKlasqGrVqmnMmDHKyclxGHvixAndf//9CgwMVFBQkOLi4rRr167LfhZ33XWXgoOD5evrq9atW2vZsmUOY2w2myZOnKgGDRrI19dXVapUUfv27ZWUlOT05wEAxcGVLQAoBe6//37961//0urVqy979WDv3r26/fbb1bx5c02aNEk+Pj46ePCgvv32W0lS48aNNWnSJI0fP17Dhw9Xhw4dJEm33HKLfRsnTpxQjx49NHDgQN13330KCQkptK4XX3xRFotFTz31lNLT0zVz5kxFR0crOTnZfgWuKIpS2z8ZhqE77rhD69evV3x8vFq2bKkvv/xSTzzxhP744w/NmDHDYfw333yjTz75RA8//LACAgI0e/Zs9e/fXykpKapSpcpl6/r777/VuXNnHTx4UImJiYqMjNSSJUs0ZMgQnTp1So899pgaN26sd999V6NGjVLNmjXtt9hVq1atyMcvXfyH/2+//Zavnvnz5+vcuXMaPny4fHx8FBwcrNzc3Hzr7969Wx06dJDVatXw4cNVp04dHTp0SMuXL9eLL74oSUpLS9PNN99sD6DVqlXTypUrFR8fr4yMDKde6nHhwgX9/vvvqly5ssP8JUuW6OzZsxoxYoSqVKmirVu3as6cOfr999+1ZMkSh7E5OTmKiYlR27Zt9eqrr2rNmjWaNm2a6tWrpxEjRkiScnNz1bt3b23dulUjRoxQo0aN9NlnnykuLi5fTXv37lW7du1Uo0YNPf300/L399eHH36ovn376uOPP1a/fv0kXfwfFlOmTNGDDz6oNm3aKCMjQ9u3b9fOnTvVtWvXYn8WAFBsBgDAdPPnzzckGdu2bbvsmEqVKhk33HCDffq5554z/vljesaMGYYk4/jx45fdxrZt2wxJxvz58/Mt69SpkyHJmDdvXoHLOnXqZJ9ev369IcmoUaOGkZGRYZ//4YcfGpKMWbNm2edFREQYcXFxV9xmYbXFxcUZERER9umlS5cakowXXnjBYdxdd91lWCwW4+DBg/Z5kgxvb2+Hebt27TIkGXPmzMm3r3+aOXOmIcl477337PPOnz9vREVFGRUrVnQ49oiICKNXr16Fbu+fY7t162YcP37cOH78uLFr1y5j4MCBhiTjkUceMQzDMA4fPmxIMgIDA4309HSH9fOW/fOz6tixoxEQEGAcOXLEYWxubq799/Hx8UZYWJjx559/OowZOHCgUalSJePs2bPFqnvPnj3G/fffb0gyEhISHMYWtK0pU6YYFovFoca4uDhDkjFp0iSHsTfccIPRqlUr+/THH39sSDJmzpxpn5eTk2Pcdttt+T6LLl26GM2aNTPOnTvn8DnccsstRoMGDezzWrRoUeSeAYAZuI0QAEqJihUrFvpWwqCgIEnSZ599VuCVj6Lw8fHR0KFDizx+8ODBCggIsE/fddddCgsL0xdffOHU/ovqiy++ULly5fToo486zH/88cdlGIZWrlzpMD86Olr16tWzTzdv3lyBgYH65Zdfrrif0NBQ3XvvvfZ5VqtVjz76qDIzM7Vhwwanj2H16tWqVq2aqlWrphYtWmjJkiW6//779fLLLzuM69+//xWvkh0/flwbN27UAw88oNq1azssy7vV1DAMffzxx+rdu7cMw9Cff/5p/xUTE6PTp09r586dxaq7WbNmevfddzV06FC98sorDuP+eWUzKytLf/75p2655RYZhqHvv/8+33YvfU6tQ4cODv1ZtWqVrFarw5VdLy8vJSQkOKx38uRJrVu3Tvfcc4/OnDljP8YTJ04oJiZGBw4c0B9//CHp4jmzd+9eHThw4IrHDQBmIGwBQCmRmZnpEGwuNWDAALVr104PPvigQkJCNHDgQH344YfFCl41atQo1sswGjRo4DBtsVhUv379Yj+vVFxHjhxReHh4vs+jcePG9uX/dGkAkaTKlSvrr7/+uuJ+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HHXr1k3bt29XamqqKW/TBAAz8cwWAAAoFR555BH9/fffioqKUnZ2tj755BNt2rRJkydPJmgB8Ehc2QIAAKXCokWLNG3aNB08eFDnzp1T/fr1NWLECF5oAcBjEbYAAAAAwAR8zxYAAAAAmICwBQAAAAAm4AUZRZCbm6ujR48qICDA4UsVAQAAAJQthmHozJkzCg8Pl5dX4deuCFtFcPToUdWqVcvdZQAAAAAoJX777TfVrFmz0DGErSIICAiQdPEDDQwMdMk2bTabVq9erW7duslqtbpkm3A9+uQ56JVnoE+eg155BvrkOeiVZyhKnzIyMlSrVi17RigMYasI8m4dDAwMdGnYqlChggIDAznhSjH65DnolWegT56DXnkG+uQ56JVnKE6fivJ4ES/IAAAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMEF5dxeAktW7t3PrLV/u2joAAACAax1XtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAEzg1rC1ceNG9e7dW+Hh4bJYLFq6dKl9mc1m01NPPaVmzZrJ399f4eHhGjx4sI4ePeqwjZMnTyo2NlaBgYEKCgpSfHy8MjMzHcbs3r1bHTp0kK+vr2rVqqWpU6eWxOEBAAAAKMPcGraysrLUokULzZ07N9+ys2fPaufOnRo3bpx27typTz75RPv27dMdd9zhMC42NlZ79+5VUlKSVqxYoY0bN2r48OH25RkZGerWrZsiIiK0Y8cOvfLKK5owYYLefPNN048PAAAAQNlV3p0779Gjh3r06FHgskqVKikpKclh3muvvaY2bdooJSVFtWvX1k8//aRVq1Zp27Ztat26tSRpzpw56tmzp1599VWFh4dr4cKFOn/+vN566y15e3vr+uuvV3JysqZPn+4QygAAAADAldwatorr9OnTslgsCgoKkiRt3rxZQUFB9qAlSdHR0fLy8tKWLVvUr18/bd68WR07dpS3t7d9TExMjF5++WX99ddfqly5cr79ZGdnKzs72z6dkZEh6eKtjTabzSXHkrcdV22vqKxW59Yr4TJLDXf1CcVHrzwDffIc9Moz0CfPQa88Q1H6VJweekzYOnfunJ566inde++9CgwMlCSlpqaqevXqDuPKly+v4OBgpaam2sdERkY6jAkJCbEvKyhsTZkyRRMnTsw3f/Xq1apQoYJLjifPpVfvzBYX59x6X3zh2jo8TUn3Cc6jV56BPnkOeuUZ6JPnoFeeobA+nT17tsjb8YiwZbPZdM8998gwDL3++uum72/s2LEaPXq0fTojI0O1atVSt27d7EHvatlsNiUlJalr166yOnu5yQkDBji33uLFrq3DU7irTyg+euUZ6JPnoFeegT55DnrlGYrSp7y73oqi1IetvKB15MgRrVu3ziHshIaGKj093WH8hQsXdPLkSYWGhtrHpKWlOYzJm84bcykfHx/5+Pjkm2+1Wl1+cpixzcI4e+W6rP9MKOk+wXn0yjPQJ89BrzwDffIc9MozFNan4vSvVH/PVl7QOnDggNasWaMqVao4LI+KitKpU6e0Y8cO+7x169YpNzdXbdu2tY/ZuHGjw72VSUlJatiwYYG3EAIAAACAK7g1bGVmZio5OVnJycmSpMOHDys5OVkpKSmy2Wy66667tH37di1cuFA5OTlKTU1Vamqqzp8/L0lq3LixunfvrmHDhmnr1q369ttvlZiYqIEDByo8PFySNGjQIHl7eys+Pl579+7V4sWLNWvWLIfbBAEAAADA1dx6G+H27dt166232qfzAlBcXJwmTJigZcuWSZJatmzpsN769evVuXNnSdLChQuVmJioLl26yMvLS/3799fs2bPtYytVqqTVq1crISFBrVq1UtWqVTV+/Hhe+w4AAADAVG4NW507d5ZhGJddXtiyPMHBwVq0aFGhY5o3b66vv/662PUBAAAAgLNK9TNbAAAAAOCpCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJijv7gJwbevd27n1li93bR0AAABASePKFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJigvLsLAFypd2/n1lu+3LV1AAAAAIQtlErOhiYAAACgtOA2QgAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwARuDVsbN25U7969FR4eLovFoqVLlzosNwxD48ePV1hYmPz8/BQdHa0DBw44jDl58qRiY2MVGBiooKAgxcfHKzMz02HM7t271aFDB/n6+qpWrVqaOnWq2YcGAAAAoIxza9jKyspSixYtNHfu3AKXT506VbNnz9a8efO0ZcsW+fv7KyYmRufOnbOPiY2N1d69e5WUlKQVK1Zo48aNGj58uH15RkaGunXrpoiICO3YsUOvvPKKJkyYoDfffNP04wMAAABQdpV358579OihHj16FLjMMAzNnDlTzz77rPr06SNJeueddxQSEqKlS5dq4MCB+umnn7Rq1Spt27ZNrVu3liTNmTNHPXv21Kuvvqrw8HAtXLhQ58+f11tvvSVvb29df/31Sk5O1vTp0x1CGQAAAAC4klvDVmEOHz6s1NRURUdH2+dVqlRJbdu21ebNmzVw4EBt3rxZQUFB9qAlSdHR0fLy8tKWLVvUr18/bd68WR07dpS3t7d9TExMjF5++WX99ddfqly5cr59Z2dnKzs72z6dkZEhSbLZbLLZbC45vrztuGp7RWW1Orees2U6u7+Sdrnjc1efUHz0yjPQJ89BrzwDffIc9MozFKVPxelhqQ1bqampkqSQkBCH+SEhIfZlqampql69usPy8uXLKzg42GFMZGRkvm3kLSsobE2ZMkUTJ07MN3/16tWqUKGCk0dUsKSkJJdu70ri4pxb74svSnZ/Je1Kx1fSfYLz6JVnoE+eg155BvrkOeiVZyisT2fPni3ydkpt2HKnsWPHavTo0fbpjIwM1apVS926dVNg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MDVp0sRhXuPGjfXxxx9LkkJDQyVJaWlpDle20tLS1LJlS/uY9PR0h21cuHBBJ0+etK9/KR8fH/n4+OSbb7VaXX5ymLHNwnDl2jk2m7XAsFXStwPys/nKSvqcgnPok+egV56BPnkOeuUZCutTcfrn1NsIR40aJavVqpSUFIfb6gYMGKBVq1Y5s8kCtWvXLt/tf/v371dERISkiy/LCA0N1dq1a+3LMzIytGXLFkVFRUmSoqKidOrUKe3YscM+Zt26dcrNzVXbtm1dVisAAAAA/JNTV7ZWr16tL7/8UjVr1nSY36BBAx05csQlhUkXQ90tt9yiyZMn65577tHWrVv15ptv6s0335QkWSwWjRw5Ui+88IIaNGigyMhIjRs3TuHh4erbt6+ki1fCunfvrmHDhmnevHmy2WxKTEzUwIEDPfpNhL17u7sCAAAAAIVxKmxlZWUV+KKIkydPFnj7nbNuuukmffrppxo7dqwmTZqkyMhIzZw5U7GxsfYxTz75pLKysjR8+HCdOnVK7du316pVq+wv15CkhQsXKjExUV26dJGXl5f69++v2bNnu6xOAAAAALiUU2GrQ4cOeuedd/T8889LuniFKTc3V1OnTtWtt97q0gJvv/123X777ZddbrFYNGnSJE2aNOmyY4KDg7Vo0SKX1gUAAAAAhXEqbE2dOlVdunTR9u3bdf78eT355JPau3evTp48qW+//dbVNQIAAACAx3HqBRlNmzbV/v371b59e/Xp00dZWVm688479f3336tevXqurhEAAAAAPE6xr2zZbDZ1795d8+bN0zPPPGNGTQAAAADg8Yp9ZctqtWr37t1m1AIAAAAA1wynbiO877779L///c/VtQAAAADANcOpF2RcuHBBb731ltasWaNWrVrJ39/fYfn06dNdUhwAAAAAeKpiha1ffvlFderU0Q8//KAbb7xRkrR//36HMRaLxXXVAQAAAICHKlbYatCggY4dO6b169dLkgYMGKDZs2crJCTElOIAAAAAwFMV65ktwzAcpleuXKmsrCyXFgQAAAAA1wKnXpCR59LwBQAAAAC4qFhhy2Kx5Hsmi2e0AAAAACC/Yj2zZRiGhgwZIh8fH0nSuXPn9H//93/53kb4ySefuK5CAHa9ezu/7vLlrqsDAAAAV1assBUXF+cwfd9997m0GAAAAAC4VhQrbM2fP9+sOgAAAADgmnJVL8gAAAAAABSMsAUAAAAAJiBsAQAAAIAJCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACYr1PVsAHPXu7e4KAAAAUFpxZQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATEDYAgAAAAATeFTYeumll2SxWDRy5Ej7vHPnzikhIUFVqlRRxYoV1b9/f6WlpTmsl5KSol69eqlChQqqXr26nnjiCV24cKGEqwcAAABQlnhM2Nq2bZveeOMNNW/e3GH+qFGjtHz5ci1ZskQbNmzQ0aNHdeedd9qX5+TkqFevXjp//rw2bdqkt99+WwsWLND48eNL+hAAAAAAlCEeEbYyMzMVGxur//znP6pcubJ9/unTp/W///1P06dP12233aZWrVpp/vz52rRpk7777jtJ0urVq/Xjjz/qvffeU8uWLdWjRw89//zzmjt3rs6fP++uQwIAAABwjSvv7gKKIiEhQb169VJ0dLReeOEF+/wdO3bIZrMpOjraPq9Ro0aqXbu2Nm/erJtvvlmbN29Ws2bNFBISYh8TExOjESNGaO/evbrhhhvy7S87O1vZ2dn26YyMDEmSzWaTzWZzyTHlbcfZ7VmtLikDV2C12hz+68lc9Ee31Lracwolgz55DnrlGeiT56BXnqEofSpOD0t92Prggw+0c+dObdu2Ld+y1NRUeXt7KygoyGF+SEiIUlNT7WP+GbTyluctK8iUKVM0ceLEfPNXr16tChUqOHMYl5WUlOTUenFxLi0DVzBokHN9Kk2++MLdFZQMZ88plCz65DnolWegT56DXnmGwvp09uzZIm+nVIet3377TY899piSkpLk6+tbYvsdO3asRo8ebZ/OyMhQrVq11K1bNwUGBrpkHzabTUlJSeratausTlymGjDAJWXgCqxWmwYNStKiRV1ls3n25cTFi91dgbmu9pxCyaBPnoNeeQb65DnolWcoSp/y7norilIdtnbs2KH09HTdeOON9nk5OTnauHGjXnvtNX355Zc6f/68Tp065XB1Ky0tTaGhoZKk0NBQbd261WG7eW8rzBtzKR8fH/n4+OSbb7VaXX5yOLtNrkCXLJvN6vFhq6z8XDfjPIXr0SfPQa88A33yHPTKMxTWp+L0r1S/IKNLly7as2ePkpOT7b9at26t2NhY+++tVqvWrl1rX2ffvn1KSUlRVFSUJCkqKkp79uxRenq6fUxSUpICAwPVpEmTEj8mAAAAAGVDqb6yFRAQoKZNmzrM8/f3V5UqVezz4+PjNXr0aAUHByswMFCPPPKIoqKidPPNN0uSunXrpiZNmuj+++/X1KlTlZqaqmeffVYJCQkFXr0CAAAAAFco1WGrKGbMmCEvLy/1799f2dnZiomJ0b///W/78nLlymnFihUaMWKEoqKi5O/vr7i4OE2aNMmNVQMAAAC41nlc2Prqq68cpn19fTV37lzNnTv3sutEREToi7LyKjYAAAAApUKpfmYLAAAAADwVYQsAAAAATEDYAgAAAAATELYAAAAAwASELQAAAAAwAWELAAAAAExA2AIAAAAAE3jc92wBcE7v3s6tt3y5a+sAAAAoK7iyBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJiBsAQAAAIAJCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJiBsAQAAAIAJCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJiBsAQAAAIAJCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJiBsAQAAAIAJCFsAAAAAYALCFgAAAACYgLAFAAAAACYgbAEAAACACQhbAAAAAGACwhYAAAAAmICwBQAAAAAmIGwBAAAAgAkIWwAAAABgAsIWAAAAAJiAsAUAAAAAJijVYWvKlCm66aabFBAQoOrVq6tv377at2+fw5hz584pISFBVapUUcWKFdW/f3+lpaU5jElJSVGvXr1UoUIFVa9eXU888YQuXLhQkocCAAAAoIwp1WFrw4YNSkhI0HfffaekpCTZbDZ169ZNWVlZ9jGjRo3S8uXLtWTJEm3YsEFHjx7VnXfeaV+ek5OjXr166fz589q0aZPefvttLViwQOPHj3fHIQEAAAAoI8q7u4DCrFq1ymF6wYIFql69unbs2KGOHTvq9OnT+t///qdFixbptttukyTNnz9fjRs31nfffaebb75Zq1ev1o8//qg1a9YoJCRELVu21PPPP6+nnnpKEyZMkLe3d779ZmdnKzs72z6dkZEhSbLZbLLZbC45trztOLs9q9UlZeAKrFabw3/LIhf9kTfd1Z5TKBn0yXPQK89AnzwHvfIMRelTcXpoMQzDuOqqSsjBgwfVoEED7dmzR02bNtW6devUpUsX/fXXXwoKCrKPi4iI0MiRIzVq1CiNHz9ey5YtU3Jysn354cOHVbduXe3cuVM33HBDvv1MmDBBEydOzDd/0aJFqlChghmHBgAAAMADnD17VoMGDdLp06cVGBhY6NhSfWXrn3JzczVy5Ei1a9dOTZs2lSSlpqbK29vbIWhJUkhIiFJTU+1jQkJC8i3PW1aQsWPHavTo0fbpjIwM1apVS926dbviB1pUNptNSUlJ6tq1q6xOXKYaMMAlZeAKrFabBg1K0qJFXWWzlc3LiYsXu7uCornacwolgz55DnrlGeiT56BXnqEofcq7660oPCZsJSQk6IcfftA333xj+r58fHzk4+OTb77VanX5yeHsNrkCXbJsNmuZDVue9veBGecpXI8+eQ565Rnok+egV56hsD4Vp3+l+gUZeRITE7VixQqtX79eNWvWtM8PDQ3V+fPnderUKYfxaWlpCg0NtY+59O2EedN5YwAAAADA1Up12DIMQ4mJifr000+1bt06RUZGOixv1aqVrFar1q5da5+3b98+paSkKCoqSpIUFRWlPXv2KD093T4mKSlJgYGBatKkSckcCAAAAIAyp1TfRpiQkKBFixbps88+U0BAgP0Zq0qVKsnPz0+VKlVSfHy8Ro8e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Uw899JAeffTRfMtq16592fW8vLxUv359SVLjxo116NAhjRgxQu+++64kafPmzYqNjdXEiRMVExOjSpUq6YMPPtC0adMctmO1Wh2mLRaLcnNzC6350vrLlSunHTt2ONzCKEkVK1Ys8nYAAOYibAEASh1/f397qHFG8+bN9fvvv2v//v0FXt268cYb9eOPP17VPiTp6aefVr169TRq1CjdeOON2rRpkyIiIvTMM8/Yxxw5cqRY26xbt66sVqu2bdtmD36nT5/W/v371bFjR0nSDTfcoJycHKWnp6tDhw5XdQwAAPPwggwAwDWnU6dO6tixo/r376+kpCQdPnxYK1eu1KpVqyRdfGPgpk2blJiYqOTkZB04cECfffZZsV6QIUm1atVSv379NH78eEkXn/1KSUnRBx98oEOHDmn27Nn69NNPi7XNgIAAxcXF6YknntD69eu1d+9excfHy8vLSxaLRZJ03XXXKTY2VoMHD9Ynn3yiw4cPa+vWrZoyZYo+//zzYu0PAGAewhYA4Jr08ccf66abbtK9996rJk2a6Mknn1ROTo6ki1e+NmzYoP3796tDhw664YYbNH78eIeXUBTVqFGj9Pnnn2vr1q264447NGrUKCUmJqply5batGmTxo0bV+xtTp8+XVFRUbr99tsVHR2tdu3aqXHjxg4v7pg/f74GDx6sxx9/XA0bNlTfvn0droYBANzPYhiG4e4iAADA5WVlZalGjRqaNm2a4uPj3V0OAKCIeGYLAIBS5vvvv9fPP/+sNm3a6PTp05o0aZIkqU+fPm6uDABQHIQtAABKoVdffVX79u2Tt7e3WrVqpa+//lpVq1Z1d1kAgGLgNkIAAAAAMAEvyAAAAAAAExC2AAAAAMAEhC0AAAAAMAFhCwAAAABMQNgCAAAAABMQtgAAAADABIQtAAAAADABYQsAAAAATPD/AKj1Ta49MNZKAAAAAElFTkSuQmCC" }, "metadata": {}, "output_type": "display_data" @@ -141,19 +141,19 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:15:49.347509Z", - "start_time": "2024-04-11T21:15:49.262675Z" + "end_time": "2024-04-18T22:03:04.360281Z", + "start_time": "2024-04-18T22:03:04.258530Z" } } }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 16, "outputs": [ { "data": { "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAABIQAAAJwCAYAAAD4AboDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXgT1foH8G+aLlAKZZG9ZRFwQ3BBUMAquIDiUgy44IJcl6sCCqLwA+/iLsqOV/EqXkVxQcFQrooKKmCviqCCF/WqgCBQKCBLS6F0SfL74zhknzkzmaSZzPfzPH2aTM9MTttMZuad97zH4fP5fCAiIiIiIiIiIttIq+sOEBERERERERFRYjEgRERERERERERkMwwIERERERERERHZDANCREREREREREQ2w4AQEREREREREZHNMCBERERERERERGQzDAgREREREREREdkMA0JERERERERERDbDgBARERERERERkc0wIERERES6jBgxAh06dKjrbpDFPPTQQ3A4HHXdDSIiIvoDA0JEREQ2Mm/ePDgcjmNf9erVwwknnIDRo0dj9+7ddd29qEL7nZ6ejrZt22LEiBEoKSmp6+6Z7vPPP8dVV12Fli1bIisrCx06dMAdd9yBbdu21XXXgnTo0CHo/xLta968eXXdVSIiIgrh8Pl8vrruBBERESXGvHnz8Kc//QmPPPIIOnbsiKNHj+I///kP5s+fj/bt2+P7779Hdna26jZqamrg9XqRlZWVoF5H7vfq1asxb948dOjQAd9//z3q1auXsP7E0z/+8Q+MGTMGxx9/PEaMGIHWrVvjf//7H1588UUAwNKlS9GnT5867qVQVFSEioqKY8+XLl2KN998EzNnzsRxxx13bHmfPn3Qrl071NbWpsz/iYiIyOrS67oDRERElHiXXnopzjrrLADAbbfdhmbNmmHGjBlYsmQJhg0bFnGdw4cPo0GDBsjIyEhkV4OE9vu4447DU089hX//+9+45ppr6qxfZvn8888xduxYnHvuufjwww+DgnN33XUX+vbti6FDh+KHH35AkyZNEtYv5X8favDgwUHPS0tL8eabb2Lw4MERhxWmp/PUk4iIKFlwyBgRERHhggsuAABs2bIFgKgTlJOTg82bN2PQoEFo2LAhbrjhhmM/C73Y93q9mD17Nrp164Z69eqhefPmuOSSS/D1118HtXvttdfQo0cP1K9fH02bNsV1112H7du3G+53QUEBAGDz5s3HllVXV+Pvf/87evTogdzcXDRo0AAFBQVYsWJF0Lpbt26Fw+HAtGnT8MILL6BTp07IyspCz549sXbt2rDXWrhwIU455RTUq1cPp556KhYvXhz1bzFr1ix07doV9erVQ8uWLXHHHXfgwIEDmr/Po48+CofDgVdeeSUsU6tTp06YMmUKdu3aheeffx4AMG3aNDgcDvz2229h25o0aRIyMzODXverr77CJZdcgtzcXGRnZ+P888/H559/HrSeUuvnxx9/xPXXX48mTZrg3HPP1ey7lkg1hBwOB0aPHn3sb1u/fn307t0bGzZsAAA8//zz6Ny5M+rVq4d+/fph69atYduV+Z2IiIgoHANCREREdCyg0qxZs2PLamtrMXDgQLRo0QLTpk3DkCFDoq5/6623YuzYscjPz8dTTz2FiRMnol69eli9evWxNo8//jiGDx+OLl26YMaMGRg7diw++eQTnHfeeTh48KChfisBgsBsmfLycrz44ovo168fnnrqKTz00EPYu3cvBg4ciPXr14dt44033sDUqVNxxx134LHHHsPWrVvhcrlQU1NzrM3777+Pa6+9FhkZGZg8eTJcLhduvfVWfPPNN2Hbu+OOOzB+/Hj07dsXs2fPxp/+9Ce8/vrrGDhwYNA2Qx05cgSffPIJCgoK0LFjx4htrr32WmRlZeG9994DAFxzzTVwOBx4++23w9q+/fbbGDBgwLG/zaefforzzjsP5eXlePDBB/HEE0/g4MGDuOCCC7BmzZqw9a+++mocOXIETzzxBG6//fao/Y5VcXEx7rvvPtx888146KGH8L///Q+XX345nn32WTz99NMYOXIkxo8fjy+//BK33HJL0Lp6fyciIiIK4CMiIiLbePnll30AfB9//LFv7969vu3bt/sWLFjga9asma9+/fq+HTt2+Hw+n+/mm2/2AfBNnDgxbBs333yzr3379seef/rppz4AvnvuuSesrdfr9fl8Pt/WrVt9TqfT9/jjjwf9fMOGDb709PSw5TL9XrRoka958+a+rKws3/bt24+1ra2t9VVVVQWtf+DAAV/Lli19t9xyy7FlW7Zs8QHwNWvWzLd///5jy5csWeID4Hv33XePLevWrZsvLy/Pd+jQoWPLVq5c6QMQ9LcoLi72AfC9/vrrQa//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v/wl4lAyQAwb+/jjj9GwYUMMGjQo7Of9+vXDl19+iUcffRTPPPMMKioq0KpVK5x99tlhGUdWkYq/ExERUaI4fEYqJhIRERERTj/9dDRv3hzLly+v664QERER6cIaQkREREQaampqUFtbG7Rs5cqV+O6779CvX7+66RQRERFRDJghRERERKRh69atuOiii3DjjTeiTZs2+Omnn/DPf/4Tubm5+P7779GsWbO67iIRERGRLqwhRERERKShSZMm6NGjB1588UXs3bsXDRo0wGWXXYYnn3ySwSAiIiKyJGYIERERERERERHZDGsIERERERERERHZDANCREREREREREQ2Y7saQl6vFzt37kTDhg3hcDjqujtERERERERERKbw+Xw4dOgQ2rRpg7Q09Rwg2wWEdu7cifz8/LruBhERERERERFRXGzfvh15eXmqbWwXEGrYsCEA8cdp1KhRHffGuJqaGixbtgwDBgxARkZGXXeHKClwvyAKx/2CKBz3C6Jw3C+IwllxvygvL0d+fv6x2Ica2wWElGFijRo1snxAKDs7G40aNbLMG5Mo3rhfEIXjfkEUjvsFUTjuF0ThrLxfyJTIYVFpIiIiIiIiIiKbYUCIiIiIiIiIiMhmGBAiIiIiIiIiIrIZBoSIiIiIiIiIiGyGASEiIiIiIiIiIpthQIiIiIiIiIiIyGYYECIiIiIiIiIishkGhIiIiIiIiIiIbIYBISIiIiIiIiIim2FAiIiIiIiIiIjIZhgQIiIiIiIiIiKyGQaEiIiIiIiIiIhshgEhIiIiIiIiIiKbYUCIiIiIiIiIiMhmGBAiIiIiIiIiIrIZBoSIiIiIiIiIiGyGASEiIiIiIhsrKwPOPhvIzBRf55wjlhERUWpLr+sOEBERERFR3ejcGdi8OXjZV18BjRsDnToBmzbVSbeIiCgBmCFERERERGRDkYJBgTZvFm2IiCg1MSBERERERGQzZWXqwSDF5s0cPkZElKoYECIiIiIispnLLotPWyIisg4GhIiIiIiIbGbbtvi0JSIi62BAiIiIiIjIZtq1i09bIiKyDgaEiIiIiIhs5v3349OWiIisgwEhIiIiIiKbyc0V08pr6dRJtCUiotTDgBARERERkQ1t2qQeFOrUSbQhIqLUxIAQEREREZFNbdoEHDwYvOzss8UyBoOIiFJbel13gIiIiIiI6k7okLDV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w4fry5Yt01977TW9qKhIf/rpp1P6eQlChAceeEBv0aKF/t577+mrVq3S33zzTb1Ro0b6Y489tq8MtQsiF5g2bZp+xx136GVlZToA/e233054P1Xt4PPPP9c1TdPHjRunf//99/qdd96p5+fn60uXLg38OxCFDEJZRq9evfRrr7123/NoNKq3bdtWf/DBB9NYK4IIhk2bNukA9E8++UTXdV2vqKjQ8/Pz9TfffHNfmR9++EEHoH/55Ze6rrMBIBwO6+Xl5fvK/Pvf/9abNGmi7927V9d1XR8zZox++OGHJ1zrwgsv1E8//fSgPxJBeGbnzp36gQceqM+cOVM/6aST9hmEqF0Qucitt96q9+3b1/b9WCyml5aW6g899NC+1yoqKvTCwkL9tdde03Vd17///nsdgL5w4cJ9ZT744AM9FArp69ev13Vd15966im9WbNm+9oJv/bBBx+s+iMRhG8GDRqkX3nllQmvRSIRffjw4bquU7sgchOzQSiV7eCCCy7QBw0alFCf4447Tv/zn/+s9DP6gULGsoiamhosXrwYAwcO3PdaOBzGwIED8eWXX6axZgQRDJWVlQCA5s2bAwAWL16M2trahDZwyCGHoGPHjvvawJdffokjjzwSrVu33lfm9NNPx44dO/Ddd9/tK2M8By9D7YjIZK699loMGjQo6d6ldkHkIu+88w569OiB888/H/vttx+6deuGZ599dt/7q1atQnl5ecI93bRpUxx33HEJ7aKkpAQ9evTYV2bgwIEIh8OYP3/+vjInnngiCgoK9pU5/fTT8dNPP2H79u1Bf0yCkKJPnz6YNWsWfv75ZwDAN998g88++wxnnHEGAGoXBAGkth1kw9yKDEJZxJYtWxCNRhMm9ADQunVrlJeXp6lWBBEMsVgMN9xwA0444QQcccQRAIDy8nIUFBSgpKQkoayxDZSXl1u2Ef6eU5kdO3aguro6iI9DEL6YPHkyvvrqKzz44INJ71G7IHKRlStX4t///jcOPPBATJ8+HX/5y19w/fXX47///S+A+H3tNGcqLy/Hfvvtl/B+Xl4emjdvLtV2CCJTuO2223DRRRfhkEMOQX5+Prp164YbbrgBw4cPB0DtgiCA1LYDuzKZ1E7y0l0BgiAIK6699losW7YMn332WbqrQhBpZe3atRg1ahRmzpyJBg0apLs6BJERxGIx9OjRA//8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" + "image/png": "iVBORw0KGgoAAAANSUhEUgAABH8AAAJwCAYAAAAQrc1tAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC5IklEQVR4nOzdeXxU1f3/8fdkskBYgqwJJCiCS1WsiqCoUVDAoiI04F632tZWbUFUqv1al7oWFaE/16rVuuBGI7gLbogLgihVu1iIIIsEQUogBLJM5vfH7ZBtlnvvnDtbXs/HIw8yM2fuPQm5M3fe95zP8QWDwaAAAAAAAACQkbKS3QEAAAAAAAB4h/AHAAAAAAAggxH+AAAAAAAAZDDCHwAAAAAAgAxG+AMAAAAAAJDBCH8AAAAAAAAyGOEPAAAAAABABiP8AQAAAAAAyGCEPwAAAAAAABmM8AcAAER0wQUXaK+99kp2N5BmbrjhBvl8vmR3AwAA/A/hDwAAGeqxxx6Tz+fb/dWhQwftu+++uuyyy7Rx48Zkdy+i1v3Ozs5Wv379dMEFF2j9+vXJ7p5xH3zwgX784x+rT58+ysvL01577aWLL75Ya9asSXbXWthrr71a/L9E+nrssceS3VUAANCKLxgMBpPdCQAAYN5jjz2mCy+8UH/4wx80YMAA7dq1S++//76eeOIJ7bnnnvryyy+Vn58fdRv19fVqbGxUXl5egnodvt+LFy/WY489pr322ktffvmlOnTokLD+eOn//b//p8mTJ2vvvffWBRdcoKKiIv3rX//Sww8/LEl69dVXddRRRyW5l5a5c+equrp69+1XX31VTz/9tO6++2717Nlz9/1HHXWU+vfvr4aGhoz5fwIAIN1lJ7sDAADAW2PHjtXhhx8uSfrZz36mHj16aMaMGZo3b57OOuussM/ZsWOHOnXqpJycnER2tYXW/e7Zs6f++Mc/6sUXX9Tpp5+etH6Z8sEHH2jKlCk65phj9Prrr7cI4n71q1/p6KOP1qRJk/SPf/xDe+yxR8L6Ffq/b23ChAktbldWVurpp5/WhAkTwk4NzM7mNBMAgFTBtC8AANqZ448/XpK0atUqSVZdn86dO6uiokInnXSSunTponPOOWf3Y60/2Dc2NmrWrFkaPHiwOnTooF69eulHP/qRPvnkkxbtnnzySQ0ZMkQdO3ZU9+7ddeaZZ2rt2rWu+11aWipJqqio2H1fXV2drrvuOg0ZMkQFBQXq1KmTSktL9c4777R47urVq+Xz+XTnnXfqz3/+swYOHKi8vDwNHTpUS5cubbOv559/XgcccIA6dOiggw46SC+88ELE38XMmTN14IEHqkOHDurTp48uvvhi/fe//43589x0003y+Xz661//2mYE1sCBAzV9+nRt2LBBDz74oCTpzjvvlM/n0zfffNNmW9dcc41yc3Nb7Pfjjz/Wj370IxUUFCg/P1/HHXecPvjggxbPC9Xm+ec//6mzzz5be+yxh4455piYfY8lXM0fn8+nyy67bPfvtmPHjho+fLi++OILSdKDDz6oQYMGqUOHDhoxYoRWr17dZrt2fiYAANAW4Q8AAO1MKDzp0aPH7vsaGhp04oknqnfv3rrzzjs1ceLEiM+/6KKLNGXKFJWUlOiPf/yjrr76anXo0EGLFy/e3eaWW27Reeedp3322UczZszQlClT9NZbb+nYY4/V1q1bXfU7FAY0HwWzbds2PfzwwxoxYoT++Mc/6oYbbtCmTZt04oknavny5W22MXv2bN1xxx26+OKLdfPNN2v16tUqKytTfX397javvPKKzjjjDOXk5Oi2225TWVmZLrroIi1btqzN9i6++GJdddVVOvroozVr1ixdeOGFeuqpp3TiiSe22GZrNTU1euutt1RaWqoBAwaEbXPGGWcoLy9PL7/8siTp9NNPl8/n03PPPdem7XPPPacxY8bs/t28/fbbOvbYY7Vt2zZdf/31uvXWW7V161Ydf/zxWrJkSZvnn3baaaqpqdGtt96qn//85xH7Ha9Fixbpiiuu0Pnnn68bbrhB//rXv3TKKafo3nvv1Z/+9Cddcskluuqqq/TRRx/ppz/9aYvnOv2ZAABAM0EAAJCRHn300aCk4JtvvhnctGlTcO3atcFnnnkm2KNHj2DHjh2D69atCwaDweD5558flBS8+uqr22zj/PPPD+655567b7/99ttBScHf/OY3bdo2NjYGg8FgcPXq1UG/3x+85ZZbWjz+xRdfBLOzs9vcb6ffc+bMCfbq1SuYl5cXXLt27e62DQ0Nwdra2hbP/+9//xvs06dP8Kc//enu+1atWhWUFOzRo0dwy5Ytu++fN29eUFLwpZde2n3f4MGDg8XFxcHt27fvvu/dd98NSmrxu1i0aFFQUvCpp55qsf/XX3897P3NLV++PCgpOHny5Ki/i4MPPjjYvXv33beHDx8eHDJkSIs2S5YsCUoKPv7448Fg0Pp/2GeffYInnnji7v+TYDAYrKmpCQ4YMCA4evTo3fddf/31QUnBs846K2o/wrnjjjuCkoKrVq1q81hou81JCubl5bVo/+CDDwYlBQsLC4Pbtm3bff8111zTYttOfiYAANAWI38AAMhwo0aNUq9evVRSUqIzzzxTnTt31gsvvKB+/fq1aPerX/0q5rb+9re/yefz6frrr2/zWGiaT3l5uRobG3X66adr8+bNu78KCwu1zz77tJmSZaffkyZNUqdOnfTiiy+quLh4dxu/36/c3FxJ1hSsLVu2qKGhQYcffrg+/fTTNts844wzWowcCk0l+/rrryVJ3377rb744gudd9556ty58+52xx13nAYPHtxiW88//7wKCgo0evToFj/nkCFD1Llz56g/5/bt2yVJXbp0ifo76NKli7Zt29ai/8uWLWsx9e3ZZ59VXl6exo8fL0lavny5VqxYobPPPlvff//97n7t2LFDJ5xwgt577z01Nja22M8vf/nLqP0w5YQTTmgxde6II46QJE2cOLHF7yJ0f+j/xc3PBAAAmlCJDwCADHfvvfdq3333VXZ2tvr06aP99ttPWVktr/9kZ2e3CFUiqaioUN++fdW9e/eIbVasWKFgMKh99tkn7ON2i0iH+l1VVaW//OUveu+998KuOvbXv/5Vd911l/7973+3mGoVbjpV//79W9wOBUGhWjmhejqDBg1q89xBgwa1CJRWrFihqqoq9e7dO2z/v/vuu4g/WyjoCIVAkWzfvr1FKHLaaadp6tSpevbZZ/W73/1OwWBQzz//vMaOHauuXbvu7pcknX/++RG3W1VV1SIEizT1zLTWv/+CggJJUklJSdj7Q/8vbn4mAADQhPAHAIAMN2zYsN2rZkWSl5fXJhByq7GxUT6fT6+99pr8fn+bx5uPqImmeb8nTJigY445Rmeffba++uqr3dt48skndcEFF2jChAm66qqr1Lt3b/n9ft12220tRseEhOuPJAWDQbs/3m6NjY3q3bu3nnrqqbCP9+rVK+JzBw0apOzsbH3++ecR29TW1uqrr75q8X/Xt29flZaW6rnnntPvfvc7LV68WGvWrNEf//jHFv2SpDvuuEOHHHJI2G23/j/o2LFjxH6YFOn3H+v/xc3PBAAAmhD+AAAA2wYOHKg33nhDW7ZsiTj6Z+DAgQoGgxowYID23XdfI/sNBTojR47UPffco6uvvlqSNGfOHO29994qLy9vsbpUuGlpduy5556SpJUrV7Z5rPV9AwcO1Jtvvqmjjz7acXjSqVMnjRw5Um+//ba++eab3ftt7rnnnlNtba1OOeWUFvefccYZuuSSS/TVV1/p2WefVX5+vsaNG9eiX5LUtWtXjRo1ylG/UlUm/kwAACQSNX8AAIBtEydOVDAY1I033tjmsdAojbKyMvn9ft14441tRtQEg0F9//33rvY9YsQIDRs2TDNnztSuXbskNY0Yab6fjz/+WB999JGrffTt21cHHXSQHn/8cVVXV+++f+HChbuXJA85/fTTFQgEdNNNN7XZTkNDQ8xVza699loFg0FdcMEF2rlzZ4vHVq1apWnTpqmoqEgXX3xxi8cmTpwov9+vp59+Ws8//7xOOeUUderUaffjQ4YM0cCBA3XnnXe2+BlCNm3aFLVfqSgTfyYAABKJkT8AAMC2kSNH6txzz9Wf/vQnrVixQj/60Y/U2NioRYsWaeTIkbrssss0cOBA3Xzzzbrmmmu0evVqTZgwQV26dNGqVav0wgsv6Be/+IWuvPJKV/u/6qqrdNppp+mxxx7TL3/5S51yyikqLy/Xj3/8Y5188slatWqVHnjgAR1wwAFhQwI7br31Vo0fP15HH320LrzwQv33v//VPffco4MOOqjFNo877jhdfPHFuu2227R8+XKNGTNGOTk5WrFihZ5//nnNmjVLkyZNirifY489VnfeeaemTp2qgw8+WBdccIGKior073//Ww899JAaGxv16quvtqlj07t3b40cOVIzZszQ9u3bdcYZZ7R4PCsrSw8//LDGjh2rAw88UBdeeKH69eun9evX65133lHXrl310ksvufrdJEsm/kwAACQS4Q8AAHDk0Ucf1cEHH6xHHnlEV111lQoKCnT44YfrqKOO2t3m6quv1r777qu777579yihkpISjRkzRqeeeqrrfZeVle0eAfLzn/9cF1xwgSorK/Xggw/qjTfe0AEHHKAnn3xSzz//vN59911X+xg3bpyefvpp3XDDDbr66qu1zz776LHHHtNf//pX/eMf/2jR9oEHHtCQIUP04IMP6ne/+52ys7O111576Sc/+YmOPvromPu6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" }, "metadata": {}, "output_type": "display_data" @@ -161,7 +161,7 @@ ], "source": [ "plt.figure(figsize=(14, 7))\n", - "plt.plot(range_analysis_df['Start_Index'], range_analysis_df['Price_Range'] * 100, marker='o', linestyle='-', color='blue')\n", + "plt.plot(range_analysis_df['Start_Index'], range_analysis_df['Price_Range_Pct'] * 100, marker='o', linestyle='-', color='blue')\n", "plt.title('Price Range Over Time')\n", "plt.xlabel('Index')\n", "plt.ylabel('Price Range')\n", @@ -171,8 +171,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-11T21:15:35.060071Z", - "start_time": "2024-04-11T21:15:34.953227Z" + "end_time": "2024-04-18T22:03:34.608164Z", + "start_time": "2024-04-18T22:03:34.504796Z" } } }, diff --git a/quants_lab/strategy/directional_strategy_base.py b/quants_lab/strategy/directional_strategy_base.py deleted file mode 100644 index 224e5dc..0000000 --- a/quants_lab/strategy/directional_strategy_base.py +++ /dev/null @@ -1,94 +0,0 @@ -import os -from datetime import datetime -from typing import Optional, TypeVar, Generic -import pandas as pd -from pydantic import BaseModel - -from quants_lab.labeling.triple_barrier_method import triple_barrier_method - -ConfigType = TypeVar("ConfigType", bound=BaseModel) - - -class DirectionalStrategyBase(Generic[ConfigType]): - # TODO: - # * Add a data structure to request candles from CSV files as part of the config - # * Evaluate to move the get data outside the backtesting to optimize the performance. - def __init__(self, config: ConfigType): - self.config = config - - def get_data(self, start: Optional[str] = None, end: Optional[str] = None): - df = self.get_raw_data() - return self.filter_df_by_time(df, start, end) - - def get_raw_data(self): - raise NotImplemented - - def preprocessing(self, df): - raise NotImplemented - - def predict(self, df): - raise NotImplemented - - @staticmethod - def get_candles(exchange: str, trading_pair: str, interval: str) -> pd.DataFrame: - """ - Get a dataframe of market data from the database. - :param exchange: Exchange name - :param trading_pair: Trading pair - :param interval: Interval of the data - :return: Dataframe of market data - """ - script_dir = os.path.dirname(os.path.abspath(__file__)) - data_dir = os.path.join(script_dir, "../../data/candles") - filename = f"candles_{exchange}_{trading_pair.upper()}_{interval}.csv" - file_path = os.path.join(data_dir, filename) - if not os.path.exists(file_path): - raise FileNotFoundError(f"File '{file_path}' does not exist.") - df = pd.read_csv(file_path) - df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms") - return df - - @staticmethod - def filter_df_by_time(df, start: Optional[str] = None, end: Optional[str] = None): - if start is not None: - start_condition = df["timestamp"] >= datetime.strptime(start, "%Y-%m-%d") - else: - start_condition = pd.Series([True]*len(df)) - if end is not None: - end_condition = df["timestamp"] <= datetime.strptime(end, "%Y-%m-%d") - else: - end_condition = pd.Series([True]*len(df)) - return df[start_condition & end_condition] - - def run_backtesting(self, - take_profit_multiplier, stop_loss_multiplier, time_limit, - std_span, order_amount=100, leverage=20, initial_portfolio=1000, - taker_fee=0.0003, maker_fee=0.00012, - start: Optional[str] = None, end: Optional[str] = None): - df = self.get_data(start=start, end=end) - df = self.preprocessing(df) - df = self.predict(df) - df = triple_barrier_method( - df=df, - std_span=std_span, - tp=take_profit_multiplier, - sl=stop_loss_multiplier, - tl=time_limit, - trade_cost=taker_fee * 2, - max_executors=1, - ) - - first_row = df.iloc[0].tolist() - first_row.extend([0, 0, 0, 0, 0, initial_portfolio]) - active_signals = df[df["active_signal"] == 1].copy() - active_signals.loc[:, "amount"] = order_amount - active_signals.loc[:, "margin_used"] = order_amount / leverage - active_signals.loc[:, "fee_pct"] = active_signals["close_type"].apply( - lambda x: maker_fee + taker_fee if x == "tp" else taker_fee * 2) - active_signals.loc[:, "fee_usd"] = active_signals["fee_pct"] * active_signals["amount"] - active_signals.loc[:, "ret_usd"] = active_signals.apply(lambda x: (x["ret"] - x["fee_pct"]) * x["amount"], - axis=1) - active_signals.loc[:, "current_portfolio"] = initial_portfolio + active_signals["ret_usd"].cumsum() - active_signals.loc[:, "current_portfolio"].fillna(method='ffill', inplace=True) - positions = pd.concat([pd.DataFrame([first_row], columns=active_signals.columns), active_signals]) - return df, positions.reset_index(drop=True) diff --git a/utils/hummingbot_processes.py b/utils/hummingbot_processes.py index 7a8e501..4ceb001 100644 --- a/utils/hummingbot_processes.py +++ b/utils/hummingbot_processes.py @@ -18,7 +18,7 @@ async def aget_candles(connector_name: str, trading_pair: str, interval: str, ma candles.start() pbar = tqdm(total=candles._candles.maxlen) - while not candles.is_ready: + while not candles.ready: await asyncio.sleep(1) awaited_records = candles._candles.maxlen - len(candles._candles) pbar.update(candles._candles.maxlen - awaited_records - pbar.n) @@ -32,7 +32,7 @@ async def aget_candles(connector_name: str, trading_pair: str, interval: str, ma async def adownload_candles(connector_name: str, trading_pair: str, interval: str, max_records: int, download_path: str): candles = CandlesFactory.get_candle(CandlesConfig(connector_name, trading_pair, interval, max_records)) candles.start() - while not candles.is_ready: + while not candles.ready: print(f"Candles not ready yet! Missing {candles._candles.maxlen - len(candles._candles)}") await asyncio.sleep(1) df = candles.candles_df From 5890f55c464fa6c7d70def7f84fbb8852be53bdc Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:42:16 -0400 Subject: [PATCH 034/139] (feat) name make file --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 20683ea..830c46c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .ONESHELL: .PHONY: run -.PHONY: conda_remove -.PHONY: conda_create +.PHONY: uninstall +.PHONY: install run: streamlit run main.py From 502c2b7d7c33224f6c4a59b33d873c9066c7ae49 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:42:53 -0400 Subject: [PATCH 035/139] (feat) display only fundamental pages --- main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 728a8ae..940c349 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ from st_pages import Page, Section, show_pages from streamlit_authenticator import Authenticate from CONFIG import AUTH_SYSTEM_ENABLED -from utils.os_utils import read_yaml_file, dump_dict_to_yaml +from backend.utils.os_utils import read_yaml_file, dump_dict_to_yaml def main_page(): @@ -17,10 +17,10 @@ def main_page(): Page("frontend/pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), Page("frontend/pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), Page("frontend/pages/bollinger_v1/app.py", "Bollinger V1", "📈"), - Page("frontend/pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), - Page("frontend/pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), + # Page("frontend/pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), + # Page("frontend/pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), Page("frontend/pages/macd_bb_v1/app.py", "MACD_BB V1", "📊"), - Page("frontend/pages/dman_v5/app.py", "D-Man V5", "📊"), + # Page("frontend/pages/dman_v5/app.py", "D-Man V5", "📊"), Page("frontend/pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), Page("frontend/pages/position_builder/app.py", "Position Builder", "🔭"), Page("frontend/pages/dynamic_position_builder/app.py", "Dynamic Position Builder", "🔭"), @@ -115,7 +115,7 @@ if "authenticator" not in st.session_state: if not AUTH_SYSTEM_ENABLED: main_page() elif st.session_state["authentication_status"]: - config["credentials"] = st.session_state.authenticator.credentials + config["credentials"] = st.session_state.authenticator_handler.credentials dump_dict_to_yaml(config, "credentials.yml") with st.sidebar: st.write(f'Welcome {st.session_state["name"]}!') From 6ed1264e3a3caf6c7bb19ad96e91a82479d2b16e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:43:29 -0400 Subject: [PATCH 036/139] (feat) refactor performance components --- frontend/components/bot_performance_card.py | 237 ++++++------------ .../components/bot_performance_card_v2.py | 113 --------- 2 files changed, 80 insertions(+), 270 deletions(-) delete mode 100644 frontend/components/bot_performance_card_v2.py diff --git a/frontend/components/bot_performance_card.py b/frontend/components/bot_performance_card.py index 83ebb6d..bcc4c11 100644 --- a/frontend/components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -1,78 +1,58 @@ -from docker_manager import DockerManager -from streamlit_elements import mui, lazy +from streamlit_elements import mui + +from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from frontend.components.dashboard import Dashboard -import streamlit as st -import time -from utils.os_utils import get_python_files_from_directory, get_yml_files_from_directory -from utils.status_parser import StatusParser import pandas as pd -import datetime + +from backend.services.backend_api_client import BackendAPIClient TRADES_TO_SHOW = 5 WIDE_COL_WIDTH = 180 -MEDIUM_COL_WIDTH = 150 +MEDIUM_COL_WIDTH = 140 +backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) -def time_ago(ts): - now_utc = datetime.datetime.now(datetime.timezone.utc) - seconds_since_epoch_utc = now_utc.timestamp() - delta = round(seconds_since_epoch_utc - ts / 1000) - if delta < 60: - return f"{delta}s ago" - if delta < 3600: - return f"{delta // 60}m ago" - else: - return f"{delta // 3600}h ago" -class BotPerformanceCard(Dashboard.Item): +def stop_bot(bot_name): + backend_api_client.stop_bot(bot_name) + backend_api_client.stop_container(bot_name) + backend_api_client.remove_container(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): def __init__(self, board, x, y, w, h, **item_props): super().__init__(board, x, y, w, h, **item_props) - @staticmethod - def set_strategy(_, childs, bot_name): - st.session_state.active_bots[bot_name]["selected_strategy"] = childs.props.value - - @staticmethod - def start_strategy(bot_name, broker_client): - selected_strategy = st.session_state.active_bots[bot_name]["selected_strategy"] - if selected_strategy.endswith(".py"): - broker_client.start(script=selected_strategy) - elif selected_strategy.endswith(".yml"): - broker_client.import_strategy(strategy=selected_strategy.replace(".yml", "")) - time.sleep(0.5) - broker_client.start() - def __call__(self, bot_config: dict): bot_name = bot_config["bot_name"] - scripts_directory = f"./hummingbot_files/bots/{bot_config['bot_name']}" - strategies_directory = f"{scripts_directory}/conf/strategies" - scripts = [file.split("/")[-1] for file in get_python_files_from_directory(scripts_directory)] - strategies = [file.split("/")[-1] for file in get_yml_files_from_directory(strategies_directory)] - if bot_config["selected_strategy"] is None: - st.session_state.active_bots[bot_name]["selected_strategy"] = "" - + status = bot_config.get("status", {"running_status": "not available"}) + is_running = status["running_status"] == "running" + global_performance = status.get("global_performance") with mui.Card(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, elevation=2): - color = "green" if bot_config["is_running"] else "grey" - subheader_message = "Running " + st.session_state.active_bots[bot_name]["selected_strategy"] if bot_config["is_running"] else "Not running" + color = "green" if is_running else "grey" mui.CardHeader( - title=bot_config["bot_name"], - subheader=subheader_message, + title=bot_name, + subheader=status["running_status"], avatar=mui.Avatar("🤖", sx={"bgcolor": color}), - action=mui.IconButton(mui.icon.Stop, onClick=lambda: bot_config["broker_client"].stop()) if bot_config[ - "is_running"] else mui.IconButton(mui.icon.BuildCircle), - className=self._draggable_class, - ) - if bot_config["is_running"]: + 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}): # Balances Table mui.Typography("Balances", variant="h6") - # # Convert list of dictionaries to DataFrame - balances = StatusParser(bot_config["status"], type="balances").parse() - if balances != "No balances": - df_balances = pd.DataFrame(balances) + balances = status.get("total_balances", {}) + if balances: + rows = [(exchange, symbol, round(float(value), 2)) for exchange, inner_dict in balances.items() for symbol, value + in inner_dict.items()] + df_balances = pd.DataFrame(rows, columns=["Exchange", "Currency", "Amount"]).reset_index().rename(columns={"index": "id"}) + balances_rows = df_balances.to_dict(orient='records') balances_cols = [{'field': col, 'headerName': col} for col in df_balances.columns] @@ -80,111 +60,54 @@ class BotPerformanceCard(Dashboard.Item): # Customize width for 'exchange' column if column['field'] == 'Exchange': column['width'] = WIDE_COL_WIDTH - mui.DataGrid(rows=balances_rows, - columns=balances_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) + mui.DataGrid( + rows=balances_rows, + columns=balances_cols, + autoHeight=True, + density="compact", + disableColumnSelector=True, + hideFooter=True, + initialState={"columns": {"columnVisibilityModel": {"id": False}}}) else: mui.Typography(str(balances), sx={"fontSize": "0.75rem"}) + mui.Divider(sx={"margin": "1rem 0"}) + # Controllers Table + mui.Typography("Controllers", variant="h6", sx={"marginTop": 2}) + controllers = status.get("controllers") + if controllers: + controllers_list = [] + for controller, inner_dict in controllers.items(): + controllers_list.append({ + "Controller ID": controller, + "Realized PNL ($)": inner_dict.get("realized_pnl_quote", 0), + "Unrealized PNL ($)": inner_dict.get("unrealized_pnl_quote", 0), + "GLOBAL PNL ($)": inner_dict.get("global_pnl_quote", 0), + # "global_pnl_pct": inner_dict.get("global_pnl_pct", 0), + "Volume ($)": inner_dict.get("total_volume_traded", 0), + }) - # Active Orders Table - mui.Typography("Active Orders", variant="h6", sx={"marginTop": 2}) - - # Convert list of dictionaries to DataFrame - try: - orders = StatusParser(bot_config["status"], type="orders").parse() - if orders not in ["No active maker orders", "Market connectors are not ready"]: - df_orders = pd.DataFrame(orders) - orders_rows = df_orders.to_dict(orient='records') - orders_cols = [{'field': col, 'headerName': col} for col in df_orders.columns] - - for column in orders_cols: - # Customize width for 'exchange' column - if column['field'] == 'Exchange': - column['width'] = WIDE_COL_WIDTH - # Customize width for column - if column['field'] == 'Price': - column['width'] = MEDIUM_COL_WIDTH - - mui.DataGrid(rows=orders_rows, - columns=orders_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) - else: - mui.Typography(str(orders), sx={"fontSize": "0.75rem"}) - except ValueError as e: - mui.Typography(str(bot_config["status"]), sx={"fontSize": "0.75rem"}) - - # Trades Table - mui.Typography("Recent Trades", variant="h6", sx={"marginTop": 2}) - df_trades = pd.DataFrame(bot_config["trades"]) - if not df_trades.empty: - # Add 'id' column to the dataframe by concatenating 'trade_id' and 'trade_timestamp' - df_trades['id'] = df_trades.get('trade_id', '0').astype(str) + df_trades['trade_timestamp'].astype(str) - - # Convert timestamp col to datetime - df_trades['trade_timestamp'] = df_trades['trade_timestamp'].astype(int) - - # Show last X trades - df_trades = df_trades.sort_values(by='trade_timestamp', ascending=False) - df_trades = df_trades.head(TRADES_TO_SHOW) - df_trades['time_ago'] = df_trades['trade_timestamp'].apply(time_ago) - - trades_rows = df_trades.to_dict(orient='records') - trades_cols = [{'field': col, 'headerName': col} for col in df_trades.columns] - - for column in trades_cols: - # Customize width for 'market' column - if column['field'] == 'market': - column['width'] = WIDE_COL_WIDTH - if column['field'] == 'trade_timestamp': - column['width'] = MEDIUM_COL_WIDTH - - mui.DataGrid(rows=trades_rows, - columns=trades_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False, "trade_id": False, "trade_timestamp": False, "base_asset": False, "quote_asset": False, "raw_json": False}}}) + df_controllers = pd.DataFrame(controllers_list).reset_index().rename(columns={"index": "id"}) + controllers_rows = df_controllers.to_dict(orient='records') + controllers_cols = [{'field': col, 'headerName': col} for col in df_controllers.columns] + for column in controllers_cols: + # Customize width for 'exchange' column + column['width'] = WIDE_COL_WIDTH + mui.DataGrid( + rows=controllers_rows, + columns=controllers_cols, + autoHeight=True, + density="compact", + disableColumnSelector=True, + hideFooter=True, + initialState={"columns": {"columnVisibilityModel": {"id": False}}}) else: - mui.Typography("No trades yet", sx={"fontSize": "0.75rem"}) - else: - with mui.CardContent(sx={"flex": 1}): - with mui.Grid(container=True, spacing=2): - with mui.Grid(item=True, xs=12): - mui.Typography("Select a strategy config file (.yml) or script (.py) to run:") - with mui.Grid(item=True, xs=8): - with mui.Select(onChange=lazy(lambda x, y: self.set_strategy(x, y, bot_name)), - sx={"width": "100%"}): - for strategy in strategies: - mui.MenuItem(strategy, value=strategy, divider=True, sx={"fontWeight": "bold"}) - for script in scripts: - mui.MenuItem(script, value=script) - with mui.Grid(item=True, xs=4): - with mui.Button(onClick=lambda x: self.start_strategy(bot_name, bot_config["broker_client"]), - variant="outlined", - color="success", - sx={"width": "100%", "height": "100%"}): - mui.icon.PlayCircle() - mui.Typography("Start") - with mui.CardActions(): - with mui.Grid(container=True, spacing=2): - with mui.Grid(item=True, xs=6): - with mui.Button(onClick=lambda: DockerManager().stop_container(bot_name), - variant="outlined", - color="error", - sx={"width": "100%", "height": "100%"}): - mui.icon.DeleteForever() - mui.Typography("Stop Instance") - with mui.Grid(item=True, xs=6): - mui.TextField(InputProps={"readOnly": True}, - label="Attach to instance", - value="docker attach " + bot_name, - sx={"width": "100%"}) + mui.Typography(str(controllers), sx={"fontSize": "0.75rem"}) + mui.Divider(sx={"margin": "1rem 0"}) + mui.Typography("Global Performance", variant="h6") + if global_performance: + global_pnl_quote = global_performance.get("global_pnl_quote", 0) + global_pnl_pct = global_performance.get("global_pnl_pct", 0) + total_volume_traded = global_performance.get("total_volume_traded", 0) + mui.Typography(f" Global PnL (Quote): {global_pnl_quote} | Global PnL %: {global_pnl_pct} | Total Volume Traded: {total_volume_traded}") + else: + mui.Typography("No global performance data available", sx={"fontSize": "0.75rem"}) diff --git a/frontend/components/bot_performance_card_v2.py b/frontend/components/bot_performance_card_v2.py deleted file mode 100644 index 6466d9d..0000000 --- a/frontend/components/bot_performance_card_v2.py +++ /dev/null @@ -1,113 +0,0 @@ -from streamlit_elements import mui - -from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from frontend.components.dashboard import Dashboard -import pandas as pd - -from utils.backend_api_client import BackendAPIClient - -TRADES_TO_SHOW = 5 -WIDE_COL_WIDTH = 180 -MEDIUM_COL_WIDTH = 140 -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) - backend_api_client.stop_container(bot_name) - backend_api_client.remove_container(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): - def __init__(self, board, x, y, w, h, **item_props): - super().__init__(board, x, y, w, h, **item_props) - - def __call__(self, bot_config: dict): - bot_name = bot_config["bot_name"] - status = bot_config.get("status", {"running_status": "not available"}) - is_running = status["running_status"] == "running" - global_performance = status.get("global_performance") - with mui.Card(key=self._key, - sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, - elevation=2): - color = "green" if is_running else "grey" - mui.CardHeader( - title=bot_name, - subheader=status["running_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}): - # Balances Table - mui.Typography("Balances", variant="h6") - - balances = status.get("total_balances", {}) - if balances: - rows = [(exchange, symbol, round(float(value), 2)) for exchange, inner_dict in balances.items() for symbol, value - in inner_dict.items()] - df_balances = pd.DataFrame(rows, columns=["Exchange", "Currency", "Amount"]).reset_index().rename(columns={"index": "id"}) - - balances_rows = df_balances.to_dict(orient='records') - balances_cols = [{'field': col, 'headerName': col} for col in df_balances.columns] - - for column in balances_cols: - # Customize width for 'exchange' column - if column['field'] == 'Exchange': - column['width'] = WIDE_COL_WIDTH - mui.DataGrid( - rows=balances_rows, - columns=balances_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) - else: - mui.Typography(str(balances), sx={"fontSize": "0.75rem"}) - mui.Divider(sx={"margin": "1rem 0"}) - # Controllers Table - mui.Typography("Controllers", variant="h6", sx={"marginTop": 2}) - controllers = status.get("controllers") - if controllers: - controllers_list = [] - for controller, inner_dict in controllers.items(): - controllers_list.append({ - "Controller ID": controller, - "Realized PNL ($)": inner_dict.get("realized_pnl_quote", 0), - "Unrealized PNL ($)": inner_dict.get("unrealized_pnl_quote", 0), - "GLOBAL PNL ($)": inner_dict.get("global_pnl_quote", 0), - # "global_pnl_pct": inner_dict.get("global_pnl_pct", 0), - "Volume ($)": inner_dict.get("total_volume_traded", 0), - }) - - df_controllers = pd.DataFrame(controllers_list).reset_index().rename(columns={"index": "id"}) - controllers_rows = df_controllers.to_dict(orient='records') - controllers_cols = [{'field': col, 'headerName': col} for col in df_controllers.columns] - for column in controllers_cols: - # Customize width for 'exchange' column - column['width'] = WIDE_COL_WIDTH - mui.DataGrid( - rows=controllers_rows, - columns=controllers_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) - else: - mui.Typography(str(controllers), sx={"fontSize": "0.75rem"}) - mui.Divider(sx={"margin": "1rem 0"}) - mui.Typography("Global Performance", variant="h6") - if global_performance: - global_pnl_quote = global_performance.get("global_pnl_quote", 0) - global_pnl_pct = global_performance.get("global_pnl_pct", 0) - total_volume_traded = global_performance.get("total_volume_traded", 0) - mui.Typography(f" Global PnL (Quote): {global_pnl_quote} | Global PnL %: {global_pnl_pct} | Total Volume Traded: {total_volume_traded}") - else: - mui.Typography("No global performance data available", sx={"fontSize": "0.75rem"}) From 41ab88cbb7a1de700436edd8d47258212e013438 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:47:00 -0400 Subject: [PATCH 037/139] (feat) refactor file explorers --- frontend/components/bots_file_explorer.py | 4 +-- .../components/controllers_file_explorer.py | 2 +- frontend/components/file_explorer_base.py | 9 +++--- .../components/master_conf_file_explorer.py | 32 ------------------- 4 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 frontend/components/master_conf_file_explorer.py diff --git a/frontend/components/bots_file_explorer.py b/frontend/components/bots_file_explorer.py index 9816122..a91cd0c 100644 --- a/frontend/components/bots_file_explorer.py +++ b/frontend/components/bots_file_explorer.py @@ -1,9 +1,9 @@ from streamlit_elements import mui import constants -from frontend.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): diff --git a/frontend/components/controllers_file_explorer.py b/frontend/components/controllers_file_explorer.py index 6fe4d88..b38bebb 100644 --- a/frontend/components/controllers_file_explorer.py +++ b/frontend/components/controllers_file_explorer.py @@ -1,8 +1,8 @@ from streamlit_elements import mui import constants +from backend.utils.os_utils import load_controllers from frontend.components.file_explorer_base import FileExplorerBase -from utils.os_utils import load_controllers class ControllersFileExplorer(FileExplorerBase): diff --git a/frontend/components/file_explorer_base.py b/frontend/components/file_explorer_base.py index 22d1b85..4d64318 100644 --- a/frontend/components/file_explorer_base.py +++ b/frontend/components/file_explorer_base.py @@ -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: diff --git a/frontend/components/master_conf_file_explorer.py b/frontend/components/master_conf_file_explorer.py deleted file mode 100644 index a4cf8eb..0000000 --- a/frontend/components/master_conf_file_explorer.py +++ /dev/null @@ -1,32 +0,0 @@ -from streamlit_elements import mui - -import constants -from frontend.components.file_explorer_base import FileExplorerBase -from utils.os_utils import get_directories_from_directory, get_python_files_from_directory, \ - get_yml_files_from_directory, get_log_files_from_directory - - -class MasterConfFileExplorer(FileExplorerBase): - def add_tree_view(self): - directory = constants.HUMMINGBOT_TEMPLATES - configs = [conf.split("/")[-2] for conf in get_directories_from_directory(directory) if "master_bot_conf" in conf] - with mui.lab.TreeView(defaultExpandIcon=mui.icon.ChevronRight, defaultCollapseIcon=mui.icon.ExpandMore, - onNodeSelect=lambda event, node_id: self.set_selected_file(event, node_id), - defaultExpanded=["master_bot_conf"]): - for conf in configs: - with mui.lab.TreeItem(nodeId=conf, label=f"🤖{conf}"): - with mui.lab.TreeItem(nodeId=f"scripts_{conf}", label="🐍Scripts"): - for file in get_python_files_from_directory(f"{directory}/{conf}/scripts"): - mui.lab.TreeItem(nodeId=file, label=f"📄{file.split('/')[-1]}") - with mui.lab.TreeItem(nodeId=f"strategies_{conf}", label="📜Strategies"): - for file in get_yml_files_from_directory(f"{directory}/{conf}/conf/strategies"): - mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") - with mui.lab.TreeItem(nodeId=f"configs_{conf}", label="🔧Client Config"): - for file in get_yml_files_from_directory(f"{directory}/{conf}/conf"): - mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") - with mui.lab.TreeItem(nodeId=f"keys_{conf}", label="🔑Keys"): - for file in get_yml_files_from_directory(f"{directory}/{conf}/conf/connectors"): - mui.lab.TreeItem(nodeId=file, label=f"🔑 {file.split('/')[-1]}") - with mui.lab.TreeItem(nodeId=f"logs_{conf}", label="🗄️Logs"): - for file in get_log_files_from_directory(f"{directory}/{conf}/logs"): - mui.lab.TreeItem(nodeId=file, label=f"📄 {file.split('/')[-1]}") From 3898ed6d79ba26ed88d89d6b901624653aa3423c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:47:19 -0400 Subject: [PATCH 038/139] (feat) refactor components to create bots --- .../directional_strategy_creation_card.py | 47 ------------------- frontend/components/exited_bot_card.py | 2 +- frontend/components/launch_bot_card.py | 2 +- frontend/components/launch_strategy_v2.py | 2 +- 4 files changed, 3 insertions(+), 50 deletions(-) delete mode 100644 frontend/components/directional_strategy_creation_card.py diff --git a/frontend/components/directional_strategy_creation_card.py b/frontend/components/directional_strategy_creation_card.py deleted file mode 100644 index 9aa733d..0000000 --- a/frontend/components/directional_strategy_creation_card.py +++ /dev/null @@ -1,47 +0,0 @@ -from streamlit_elements import mui, lazy - -import constants -from utils.file_templates import directional_trading_controller_template -from utils.os_utils import save_file -from .dashboard import Dashboard - - -class DirectionalStrategyCreationCard(Dashboard.Item): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._strategy_name = "CustomStrategy" - self._strategy_type = "directional" - - def _set_strategy_name(self, event): - self._strategy_name = event.target.value - - def _set_strategy_type(self, _, childs): - self._strategy_type = childs.props.value - - def _create_strategy(self): - if self._strategy_type == "directional": - strategy_code = directional_trading_controller_template(self._strategy_name) - save_file(name=f"{self._strategy_name.lower()}.py", content=strategy_code, - path=constants.CONTROLLERS_PATH) - - 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.icon.NoteAdd() - mui.Typography("Create new strategy", variant="h6") - - with mui.Grid(container=True, spacing=2, sx={"padding": "10px"}): - with mui.Grid(item=True, xs=5): - with mui.FormControl(variant="standard", sx={"width": "100%"}): - mui.FormHelperText("Template name") - with mui.Select(label="Select strategy type", defaultValue="directional", - variant="standard", onChange=lazy(self._set_strategy_type)): - mui.MenuItem("Directional", value="directional") - with mui.Grid(item=True, xs=5): - with mui.FormControl(variant="standard", sx={"width": "100%"}): - mui.TextField(defaultValue="CustomStrategy", label="Strategy Name", - variant="standard", onChange=lazy(self._set_strategy_name)) - with mui.Grid(item=True, xs=2): - with mui.Button(variant="contained", onClick=self._create_strategy): - mui.icon.Add() - mui.Typography("Create", variant="body1") diff --git a/frontend/components/exited_bot_card.py b/frontend/components/exited_bot_card.py index eb020f3..df40ae7 100644 --- a/frontend/components/exited_bot_card.py +++ b/frontend/components/exited_bot_card.py @@ -2,7 +2,7 @@ from docker_manager import DockerManager from streamlit_elements import mui from frontend.components.dashboard import Dashboard -from utils import os_utils +from backend.utils import os_utils class ExitedBotCard(Dashboard.Item): diff --git a/frontend/components/launch_bot_card.py b/frontend/components/launch_bot_card.py index 6cf9f7f..c435518 100644 --- a/frontend/components/launch_bot_card.py +++ b/frontend/components/launch_bot_card.py @@ -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 diff --git a/frontend/components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py index 12ca115..1de6430 100644 --- a/frontend/components/launch_strategy_v2.py +++ b/frontend/components/launch_strategy_v2.py @@ -4,7 +4,7 @@ import streamlit as st from streamlit_elements import mui, lazy from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient +from backend.services.backend_api_client import BackendAPIClient from .dashboard import Dashboard From 66191ee670df2cf6631ad2d03620da75bb3442ef Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:47:34 -0400 Subject: [PATCH 039/139] (feat) refactor optimization cards --- frontend/components/optimization_creation_card.py | 4 ++-- frontend/components/optimization_run_card.py | 3 +-- frontend/components/optimizations_file_explorer.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/components/optimization_creation_card.py b/frontend/components/optimization_creation_card.py index ee9a9b4..78765ac 100644 --- a/frontend/components/optimization_creation_card.py +++ b/frontend/components/optimization_creation_card.py @@ -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 diff --git a/frontend/components/optimization_run_card.py b/frontend/components/optimization_run_card.py index 91aa086..531cec3 100644 --- a/frontend/components/optimization_run_card.py +++ b/frontend/components/optimization_run_card.py @@ -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 diff --git a/frontend/components/optimizations_file_explorer.py b/frontend/components/optimizations_file_explorer.py index c207d62..901b93a 100644 --- a/frontend/components/optimizations_file_explorer.py +++ b/frontend/components/optimizations_file_explorer.py @@ -1,8 +1,8 @@ from streamlit_elements import mui import constants +from backend.utils.os_utils import get_python_files_from_directory from frontend.components.file_explorer_base import FileExplorerBase -from utils.os_utils import get_python_files_from_directory class OptimizationsStrategiesFileExplorer(FileExplorerBase): From 6ad575b794f5b6759b55eefdb8ebf02cc0de3386 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:47:51 -0400 Subject: [PATCH 040/139] (feat) refactor editors and inputs --- frontend/components/editor.py | 2 +- frontend/components/st_inputs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/components/editor.py b/frontend/components/editor.py index df65be6..ea4a454 100644 --- a/frontend/components/editor.py +++ b/frontend/components/editor.py @@ -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 diff --git a/frontend/components/st_inputs.py b/frontend/components/st_inputs.py index 5dda44f..9a63f5b 100644 --- a/frontend/components/st_inputs.py +++ b/frontend/components/st_inputs.py @@ -1,7 +1,7 @@ from _decimal import Decimal from math import exp -from hummingbot.smart_components.utils.distributions import Distributions +from hummingbot.strategy_v2.utils.distributions import Distributions def normalize(values): From afb6f87a96537989b98aadd9abcbce065c11b80e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:48:02 -0400 Subject: [PATCH 041/139] (feat) refactor backtesting pages --- frontend/pages/backtest_analyze/analyze.py | 17 ++++++++--------- frontend/pages/backtest_create/create.py | 4 +--- frontend/pages/backtest_optimize/optimize.py | 5 ++--- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/frontend/pages/backtest_analyze/analyze.py b/frontend/pages/backtest_analyze/analyze.py index 2a55573..3c755a3 100644 --- a/frontend/pages/backtest_analyze/analyze.py +++ b/frontend/pages/backtest_analyze/analyze.py @@ -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,12 +10,11 @@ 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.data_viz.graphs import BacktestingGraphs +from frontend.data_viz.strategy_analysis import StrategyAnalysis +from frontend.st_utils import initialize_st_page initialize_st_page(title="Analyze", icon="🔬", initial_sidebar_state="collapsed") diff --git a/frontend/pages/backtest_create/create.py b/frontend/pages/backtest_create/create.py index 754ee4a..9f2dee9 100644 --- a/frontend/pages/backtest_create/create.py +++ b/frontend/pages/backtest_create/create.py @@ -7,9 +7,7 @@ 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 utils.st_utils import initialize_st_page - +from frontend.st_utils import initialize_st_page initialize_st_page(title="Create", icon="️⚔️", initial_sidebar_state="collapsed") diff --git a/frontend/pages/backtest_optimize/optimize.py b/frontend/pages/backtest_optimize/optimize.py index 861b862..520e392 100644 --- a/frontend/pages/backtest_optimize/optimize.py +++ b/frontend/pages/backtest_optimize/optimize.py @@ -10,9 +10,8 @@ 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 utils import os_utils - -from utils.st_utils import initialize_st_page +from backend.utils import os_utils +from frontend.st_utils import initialize_st_page initialize_st_page(title="Optimize", icon="🧪", initial_sidebar_state="collapsed") From d90a69c9249a342c80dc3960778f6faeaa3b9e54 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:48:16 -0400 Subject: [PATCH 042/139] (feat) refactor orchestration pages --- frontend/pages/bot_orchestration/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/pages/bot_orchestration/app.py b/frontend/pages/bot_orchestration/app.py index 177243d..836d3b1 100644 --- a/frontend/pages/bot_orchestration/app.py +++ b/frontend/pages/bot_orchestration/app.py @@ -2,10 +2,10 @@ import streamlit as st from streamlit_elements import elements, mui from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from frontend.components.bot_performance_card_v2 import BotPerformanceCardV2 +from frontend.components.bot_performance_card import BotPerformanceCardV2 from frontend.components.dashboard import Dashboard -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page # Constants for UI layout CARD_WIDTH = 12 From f99afde49bcde90fc4dd054b9432efb2c4ae484c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:49:53 -0400 Subject: [PATCH 043/139] (feat) initialize controller configs pages --- frontend/pages/bollinger_v1/app.py | 36 +++- frontend/pages/dman_maker_v2/app.py | 4 +- frontend/pages/dman_v5/app.py | 6 +- frontend/pages/kalman_filter_v1/app.py | 5 +- frontend/pages/macd_bb_v1/app.py | 6 +- frontend/pages/pmm_simple/app.py | 246 ++---------------------- frontend/pages/trend_follower_v1/app.py | 4 +- frontend/pages/xemm_controller/app.py | 9 +- 8 files changed, 65 insertions(+), 251 deletions(-) diff --git a/frontend/pages/bollinger_v1/app.py b/frontend/pages/bollinger_v1/app.py index 900f51b..a733491 100644 --- a/frontend/pages/bollinger_v1/app.py +++ b/frontend/pages/bollinger_v1/app.py @@ -1,13 +1,15 @@ +from datetime import datetime + import streamlit as st import pandas as pd import plotly.graph_objects as go -import pandas_ta as ta import yaml +import pandas_ta as ta # noqa: F401 from hummingbot.connector.connector_base import OrderType from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page # Initialize the Streamlit page initialize_st_page(title="Bollinger V1", icon="📈", initial_sidebar_state="expanded") @@ -207,6 +209,7 @@ config = { "bb_short_threshold": bb_short_threshold } + yaml_config = yaml.dump(config, default_flow_style=False) with c3: @@ -222,4 +225,29 @@ with c3: 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!") \ No newline at end of file + st.success("Config uploaded successfully!") + +st.write("---") +st.write("### Backtesting") +c1, c2, c3, c4, c5 = st.columns(5) +with c1: + start_datetime = st.date_input("Start Date", datetime(2024, 5, 1)) +with c2: + end_datetime = st.date_input("End Date", datetime(2024, 5, 1)) +with c3: + backtesting_resolution = st.selectbox("Backtesting Resolution", options=["1m", "3m", "5m", "15m", "30m"], index=1) +with c4: + trade_cost = st.number_input("Trade Cost", min_value=0.0, value=0.0006, step=0.0001) +with c5: + run_backtesting = st.button("Run Backtesting") + +if run_backtesting: + backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) + backtesting_results = backend_api_client.run_backtesting( + start_time=int(start_datetime.timestamp()) * 1000, + end_time=(end_datetime.timestamp()) * 1000, + backtesting_resolution=backtesting_resolution, + trade_cost=trade_cost, + config=yaml_config, + ) + st.write(backtesting_results) \ No newline at end of file diff --git a/frontend/pages/dman_maker_v2/app.py b/frontend/pages/dman_maker_v2/app.py index 86a7af1..d171170 100644 --- a/frontend/pages/dman_maker_v2/app.py +++ b/frontend/pages/dman_maker_v2/app.py @@ -5,9 +5,9 @@ from decimal import Decimal import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient 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="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") diff --git a/frontend/pages/dman_v5/app.py b/frontend/pages/dman_v5/app.py index 2cdfecc..c706077 100644 --- a/frontend/pages/dman_v5/app.py +++ b/frontend/pages/dman_v5/app.py @@ -1,14 +1,12 @@ import streamlit as st import pandas as pd -import pandas_ta as ta import plotly.graph_objects as go import yaml -from hummingbot.connector.connector_base import OrderType from plotly.subplots import make_subplots from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +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") diff --git a/frontend/pages/kalman_filter_v1/app.py b/frontend/pages/kalman_filter_v1/app.py index 3e34269..47d2b64 100644 --- a/frontend/pages/kalman_filter_v1/app.py +++ b/frontend/pages/kalman_filter_v1/app.py @@ -1,14 +1,13 @@ import streamlit as st import pandas as pd import plotly.graph_objects as go -import pandas_ta as ta import yaml from hummingbot.connector.connector_base import OrderType from pykalman import KalmanFilter from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +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") diff --git a/frontend/pages/macd_bb_v1/app.py b/frontend/pages/macd_bb_v1/app.py index f102748..797011d 100644 --- a/frontend/pages/macd_bb_v1/app.py +++ b/frontend/pages/macd_bb_v1/app.py @@ -1,14 +1,12 @@ import streamlit as st import pandas as pd -import pandas_ta as ta import plotly.graph_objects as go import yaml -from hummingbot.connector.connector_base import OrderType from plotly.subplots import make_subplots from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page # Initialize the Streamlit page initialize_st_page(title="MACD_BB V1", icon="📊", initial_sidebar_state="expanded") diff --git a/frontend/pages/pmm_simple/app.py b/frontend/pages/pmm_simple/app.py index 6b0bd03..3b6ad19 100644 --- a/frontend/pages/pmm_simple/app.py +++ b/frontend/pages/pmm_simple/app.py @@ -1,239 +1,35 @@ -import pandas as pd import streamlit as st -from hummingbot.connector.connector_base import OrderType -from plotly.subplots import make_subplots -import plotly.graph_objects as go -from decimal import Decimal -import yaml - +from frontend.components.st_inputs import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page -from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution + +# Import submodules +from frontend.pages.pmm_simple.user_inputs import user_inputs +from frontend.pages.pmm_simple.order_calculation import calculate_orders +from frontend.data_viz.visualization import visualize_orders +from frontend.pages.pmm_simple.config_handling import handle_config +from frontend.components.backtesting import backtesting_section # Initialize the Streamlit page initialize_st_page(title="PMM Simple", icon="👨‍🏫", initial_sidebar_state="collapsed") +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 and upload it to the BackendAPI.") st.write("---") -c1, c2, c3, c4, c5, c6, c7 = st.columns(7) +# Get user inputs +inputs = user_inputs() -with c1: - connector = st.text_input("Connector", value="binance_perpetual") -with c2: - trading_pair = st.text_input("Trading Pair", value="WLD-USDT") -with c3: - total_amount_quote = st.number_input("Total amount of quote", value=1000) -with c4: - leverage = st.number_input("Leverage", value=20) - position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) -with c5: - executor_refresh_time = st.number_input("Refresh Time (minutes)", value=3) - cooldown_time = st.number_input("Cooldown Time (minutes)", value=3) -with c6: - 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 c7: - 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) +# Calculate orders based on inputs +order_data = calculate_orders(inputs) +# Visualize orders +visualize_orders(order_data) +# Handle configuration +handle_config(inputs, order_data, backend_api_client) - -# Executors configuration -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=2) -with col_sell: - st.header("Sell Order Settings") - sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) - -col_buy_spreads, col_buy_amounts, col_sell_spreads, col_sell_amounts = st.columns(4) - -# Inputs for buy orders -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) -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) -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) -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) - -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 = normalize( - 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 = normalize( - 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)) - -all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions) -buy_order_amounts_quote = [amount * total_amount_quote for amount in - all_orders_amount_normalized[:buy_order_levels]] -sell_order_amounts_quote = [amount * total_amount_quote for amount in - all_orders_amount_normalized[buy_order_levels:]] - -# Initialize your figure with a dark theme -fig = make_subplots(specs=[[{"secondary_y": True}]]) -fig.update_layout( - template="plotly_dark", - plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background - paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper - title="Market Maker Order Distribution", - xaxis_title="Spread (%)", - yaxis_title="Amount (Quote)", - legend_title="Order Type", - font=dict(color='white', size=12) # Consistent font color and size -) - -# Define colors for buy and sell orders -colors = { - 'buy': '#32CD32', # Green for buy orders - 'sell': '#FF6347' # Tomato red for sell orders -} - -# Add traces for buy and sell orders -# Buy orders on the negative side of x-axis -fig.add_trace(go.Bar( - x=[-dist for dist in buy_spread_distributions], - y=buy_order_amounts_quote, - name='Buy Orders', - marker_color=colors['buy'], - width=[0.2] * buy_order_levels # Adjust the width of the bars as needed -), secondary_y=False) - -# Sell orders on the positive side of x-axis -fig.add_trace(go.Bar( - x=sell_spread_distributions, - y=sell_order_amounts_quote, - name='Sell Orders', - marker_color=colors['sell'], - width=[0.2] * buy_order_levels # Adjust the width of the bars as needed - -), secondary_y=False) - -# Annotations can be added for each bar to display the value on top -for i, value in enumerate(buy_order_amounts_quote): - fig.add_annotation( - x=-buy_spread_distributions[i], - y=value + 10, # Offset the text slightly above the bar - text=str(round(value, 2)), - showarrow=False, - font=dict(color=colors['buy'], size=10) - ) - -for i, value in enumerate(sell_order_amounts_quote): - fig.add_annotation( - x=sell_spread_distributions[i], - y=value + 10, # Offset the text slightly above the bar - text=str(round(value, 2)), - showarrow=False, - font=dict(color=colors['sell'], size=10) - ) - -# Optional: Add horizontal line or extra annotations if needed -# e.g., for average, threshold, or specific markers - -# Update the layout to make it responsive and visually appealing -fig.update_layout( - height=600, - width=800, - margin=dict(l=20, r=20, t=50, b=20) -) - -# Display the figure in Streamlit -st.plotly_chart(fig, use_container_width=True) - -# Create DataFrame for Buy Orders -buy_orders_df = pd.DataFrame({ - "Level": range(1, buy_order_levels + 1), - "Spread (%)": [-dist for dist in buy_spread_distributions], - "Amount (Quote)": buy_order_amounts_quote, - "Take Profit ($)": [float(amount) * (tp / 100) for amount in buy_order_amounts_quote], - "Stop Loss ($)": [float(amount) * (sl / 100) for amount in buy_order_amounts_quote], - "Min Trailing Stop ($)": [float(amount) * ((ts_ap - ts_delta) / 100) for amount in buy_order_amounts_quote], - "TP/SL Ratio": [tp / sl] * buy_order_levels, - "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * buy_order_levels, -}) - -# Create DataFrame for Sell Orders -sell_orders_df = pd.DataFrame({ - "Level": range(1, sell_order_levels + 1), - "Spread (%)": [dist for dist in sell_spread_distributions], - "Amount (Quote)": sell_order_amounts_quote, - "Take Profit ($)": [float(amount) * (tp / 100) for amount in sell_order_amounts_quote], - "Stop Loss ($)": [float(amount) * (sl / 100) for amount in sell_order_amounts_quote], - "Min Trailing Stop ($)": [float(amount) * ((ts_ap - ts_delta) / 100) for amount in sell_order_amounts_quote], - "TP/SL Ratio": [tp / sl] * sell_order_levels, - "TS/SL Ratio": [(ts_ap - ts_delta) / sl] * sell_order_levels, -}) - -# Display the DataFrames in Streamlit -st.write("Buy Orders:") -st.dataframe(buy_orders_df) -st.write("Sell Orders:") -st.dataframe(sell_orders_df) - -c1, c2, c3 = st.columns([2, 2, 1]) -with c1: - config_base = st.text_input("Config Base", value=f"pmm_simple-{connector}-{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": "pmm_simple", - "controller_type": "market_making", - "manual_kill_switch": None, - "candles_config": [], - "connector_name": connector, - "trading_pair": trading_pair, - "total_amount_quote": total_amount_quote, - "buy_spreads": [Decimal(spread / 100) for spread in buy_spread_distributions], - "sell_spreads": [Decimal(spread / 100) for spread in sell_spread_distributions], - "buy_amounts_pct": buy_order_amounts_quote, - "sell_amounts_pct": sell_order_amounts_quote, - "executor_refresh_time": executor_refresh_time * 60, - "cooldown_time": cooldown_time, - "leverage": leverage, - "position_mode": position_mode, - "stop_loss": sl / 100, - "take_profit": tp / 100, - "time_limit": time_limit * 60, - "take_profit_order_type": take_profit_order_type.value, - "trailing_stop": { - "activation_price": ts_ap / 100, - "trailing_delta": ts_delta / 100}, - } - -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!") \ No newline at end of file +st.write("---") +st.write("### Backtesting") +backtesting_section(inputs, backend_api_client) diff --git a/frontend/pages/trend_follower_v1/app.py b/frontend/pages/trend_follower_v1/app.py index 96bc340..de89149 100644 --- a/frontend/pages/trend_follower_v1/app.py +++ b/frontend/pages/trend_follower_v1/app.py @@ -6,8 +6,8 @@ import yaml from hummingbot.connector.connector_base import OrderType from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +from backend.services.backend_api_client import BackendAPIClient +from frontend.st_utils import initialize_st_page # Initialize the Streamlit page initialize_st_page(title="Trend Follower V1", icon="📈", initial_sidebar_state="expanded") diff --git a/frontend/pages/xemm_controller/app.py b/frontend/pages/xemm_controller/app.py index b39cd61..33c2a39 100644 --- a/frontend/pages/xemm_controller/app.py +++ b/frontend/pages/xemm_controller/app.py @@ -1,15 +1,10 @@ -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 CONFIG import BACKEND_API_HOST, BACKEND_API_PORT -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page -from hummingbot.smart_components.utils.distributions import Distributions - +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="⚡️", initial_sidebar_state="collapsed") From 0dfad99a093811700e8b1559efef102f918999fa Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:50:10 -0400 Subject: [PATCH 044/139] (feat) refactor data download candles --- frontend/pages/data_download_candles/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/pages/data_download_candles/app.py b/frontend/pages/data_download_candles/app.py index 89ad95b..6cf6018 100644 --- a/frontend/pages/data_download_candles/app.py +++ b/frontend/pages/data_download_candles/app.py @@ -3,8 +3,8 @@ from datetime import datetime, time import pandas as pd import plotly.graph_objects as go -from utils.backend_api_client import BackendAPIClient -from utils.st_utils import initialize_st_page +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="💾", initial_sidebar_state="collapsed") From 877a728748f14396911768a4cb8442ec86717191 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:50:33 -0400 Subject: [PATCH 045/139] (feat) refactor base performance pages --- frontend/pages/db_inspector/app.py | 3 +-- frontend/pages/reference_data/7_📋_Data.py | 14 +++++++------- frontend/pages/strategy_performance/app.py | 7 +++---- frontend/pages/token_spreads/app.py | 12 +++++------- frontend/pages/tvl_vs_mcap/app.py | 3 +-- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/frontend/pages/db_inspector/app.py b/frontend/pages/db_inspector/app.py index bde870f..a1a6d61 100644 --- a/frontend/pages/db_inspector/app.py +++ b/frontend/pages/db_inspector/app.py @@ -3,8 +3,7 @@ import streamlit as st import sqlite3 import pandas as pd -from utils.st_utils import initialize_st_page - +from frontend.st_utils import initialize_st_page initialize_st_page(title="DB Inspector", icon="🔍") diff --git a/frontend/pages/reference_data/7_📋_Data.py b/frontend/pages/reference_data/7_📋_Data.py index c75547e..f1a3829 100644 --- a/frontend/pages/reference_data/7_📋_Data.py +++ b/frontend/pages/reference_data/7_📋_Data.py @@ -1,29 +1,29 @@ import streamlit as st 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 @st.cache_data def get_all_coins_df(): - return CoinGeckoUtils().get_all_coins_df() + return CoinGeckoClient().get_all_coins_df() @st.cache_data def get_all_exchanges_df(): - return CoinGeckoUtils().get_all_exchanges_df() + return CoinGeckoClient().get_all_exchanges_df() @st.cache_data def get_miner_stats_df(): - return MinerUtils().get_miner_stats_df() + return MinerClient().get_miner_stats_df() @st.cache_data def get_coin_tickers_by_id_list(coins_id: list): - return CoinGeckoUtils().get_coin_tickers_by_id_list(coins_id) + return CoinGeckoClient().get_coin_tickers_by_id_list(coins_id) initialize_st_page(title="Crypto Data", icon=":bar_chart:") diff --git a/frontend/pages/strategy_performance/app.py b/frontend/pages/strategy_performance/app.py index d7ea0c0..7d0e3ae 100644 --- a/frontend/pages/strategy_performance/app.py +++ b/frontend/pages/strategy_performance/app.py @@ -2,11 +2,10 @@ import os import pandas as pd import streamlit as st import math -from utils.os_utils import get_databases -from utils.database_manager import DatabaseManager -from utils.graphs import PerformanceGraphs -from utils.st_utils import initialize_st_page, download_csv_button, style_metric_cards, db_error_message +from backend.utils.os_utils import get_databases +from frontend.data_viz.graphs import PerformanceGraphs +from frontend.st_utils import initialize_st_page, style_metric_cards initialize_st_page(title="Strategy Performance", icon="🚀") style_metric_cards() diff --git a/frontend/pages/token_spreads/app.py b/frontend/pages/token_spreads/app.py index 1fa8eb0..56a30f6 100644 --- a/frontend/pages/token_spreads/app.py +++ b/frontend/pages/token_spreads/app.py @@ -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(): diff --git a/frontend/pages/tvl_vs_mcap/app.py b/frontend/pages/tvl_vs_mcap/app.py index e553e46..48e276f 100644 --- a/frontend/pages/tvl_vs_mcap/app.py +++ b/frontend/pages/tvl_vs_mcap/app.py @@ -4,8 +4,7 @@ import pandas as pd import plotly.express as px from defillama import DefiLlama -from utils.st_utils import initialize_st_page - +from frontend.st_utils import initialize_st_page initialize_st_page(title="TVL vs Market Cap", icon="🦉") From 17c8fddc9a5a97714260c88a5150c1e9e4607b7c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:51:10 -0400 Subject: [PATCH 046/139] (feat) refactor utility pages --- .../pages/dynamic_position_builder/README.md | 0 .../dynamic_position_builder/__init__.py | 0 .../pages/dynamic_position_builder/app.py | 220 ------------------ frontend/pages/file_manager/app.py | 3 +- frontend/pages/position_builder/app.py | 2 +- 5 files changed, 2 insertions(+), 223 deletions(-) delete mode 100644 frontend/pages/dynamic_position_builder/README.md delete mode 100644 frontend/pages/dynamic_position_builder/__init__.py delete mode 100644 frontend/pages/dynamic_position_builder/app.py diff --git a/frontend/pages/dynamic_position_builder/README.md b/frontend/pages/dynamic_position_builder/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/pages/dynamic_position_builder/__init__.py b/frontend/pages/dynamic_position_builder/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/pages/dynamic_position_builder/app.py b/frontend/pages/dynamic_position_builder/app.py deleted file mode 100644 index 3130f07..0000000 --- a/frontend/pages/dynamic_position_builder/app.py +++ /dev/null @@ -1,220 +0,0 @@ -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 frontend.components.st_inputs import normalize, distribution_inputs, get_distribution - -# Initialize the Streamlit page -initialize_st_page(title="Dynamic Spread Generator", icon="🚀", initial_sidebar_state="collapsed") - - -# Page content -st.text("This tool will help you analyze and generate a position config.") -st.write("---") - -# Layout in columns -col_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2]) - -def convert_to_yaml(spreads, order_amounts): - data = { - 'dca_spreads': [float(spread)/100 for spread in spreads], - 'dca_amounts': [float(amount) for amount in order_amounts] - } - return yaml.dump(data, default_flow_style=False) - - -with col_quote: - total_amount_quote = st.number_input("Total amount of quote", value=1000) - -with col_tp_sl: - tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=2.0, step=0.1) - sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=8.0, step=0.1) - -with col_levels: - n_levels = st.number_input("Number of Levels", min_value=1, value=5) - - -# Spread and Amount Distributions -spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs( - col_spread_dist, "Spread", n_levels) -amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs( - col_amount_dist, "Amount", n_levels) - -# Assuming get_distribution function returns a list of Decimal values for spreads and amounts -spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, - spread_step, spread_ratio, manual_spreads) -amount_distribution = normalize( - get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, - amount_ratio, manual_amounts)) - -order_amounts = [Decimal(amount_dist * total_amount_quote) for amount_dist in amount_distribution] - -# Initialize lists to store values per level -take_profit_values = [] -dynamic_spreads = [] -break_even_values = [] -spread_adjustment_factor = Decimal(0.01) # Spread adjustment factor - -for level in range(n_levels): - if level > 0: - # Recompute spread for current level based on the previous level's break-even price - previous_break_even = break_even_values[level - 1] - new_spread = previous_break_even + Decimal(spread_distribution[level]) # Dynamic spread adjustment - dynamic_spreads.append(new_spread) - total_amount = sum(order_amounts[:level + 1]) - break_even_values.append(Decimal(sum([spread * amount for spread, amount in zip(dynamic_spreads, order_amounts[:level + 1])]) / total_amount)) - else: - dynamic_spreads = [Decimal(spread_distribution[0])] # Initialize with the first spread value - break_even_values = [Decimal(spread_distribution[0])] - -accumulated_amount = [sum(order_amounts[:i + 1]) for i in range(len(order_amounts))] - - -def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): - unrealized_pnl = [] - for i in range(len(spreads)): - distance = abs(spreads[i] - break_even_values[i]) - pnl = accumulated_amount[i] * distance / 100 # PNL calculation - unrealized_pnl.append(pnl) - return unrealized_pnl - -# Calculate unrealized PNL -cum_unrealized_pnl = calculate_unrealized_pnl(dynamic_spreads, break_even_values, accumulated_amount) - - -tech_colors = { - 'spread': '#00BFFF', # Deep Sky Blue - 'break_even': '#FFD700', # Gold - 'take_profit': '#32CD32', # Green - 'order_amount': '#1E90FF', # Dodger Blue - 'cum_amount': '#4682B4', # Steel Blue - 'stop_loss': '#FF0000', # Red -} - -# Create Plotly figure with secondary y-axis and a dark theme -fig = make_subplots(specs=[[{"secondary_y": True}]]) -fig.update_layout(template="plotly_dark") - -# Update the Scatter Plots and Horizontal Lines -fig.add_trace(go.Scatter(x=list(range(len(dynamic_spreads))), y=dynamic_spreads, name='Spread (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) -fig.add_trace(go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) -fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), secondary_y=False) - -# Add the new Bar Plot for Cumulative Unrealized PNL -fig.add_trace(go.Bar( - x=list(range(len(cum_unrealized_pnl))), - y=cum_unrealized_pnl, - text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], - textposition='auto', - textfont=dict(color='white', size=12), - name='Cum Unrealized PNL', - marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed -), secondary_y=True) - -fig.add_trace(go.Bar( - x=list(range(len(order_amounts))), - y=order_amounts, - text=[f"{amt:.2f}" for amt in order_amounts], # List comprehension to format text labels - textposition='auto', - textfont=dict( - color='white', - size=12 - ), - name='Order Amount', - marker=dict(color=tech_colors['order_amount'], opacity=0.5), -), secondary_y=True) - -# Modify the Bar Plot for Accumulated Amount -fig.add_trace(go.Bar( - x=list(range(len(accumulated_amount))), - y=accumulated_amount, - text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels - textposition='auto', - textfont=dict( - color='white', - size=12 - ), - name='Cum Amount', - marker=dict(color=tech_colors['cum_amount'], opacity=0.5), -), secondary_y=True) - - -# Add Horizontal Lines for Last Breakeven Price and Stop Loss Level -last_break_even = break_even_values[-1] -stop_loss_value = last_break_even + Decimal(sl) -# Horizontal Lines for Last Breakeven and Stop Loss -fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", annotation_position="top left", line_color=tech_colors['break_even']) -fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", annotation_position="bottom right", line_color=tech_colors['stop_loss']) - -# Update Annotations for Spread and Break Even -for i, (spread, be_value, tp_value) in enumerate(zip(dynamic_spreads, break_even_values, take_profit_values)): - fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['spread'])) - fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, font=dict(color=tech_colors['break_even'])) - fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['take_profit'])) -# Update Layout with a Dark Theme -fig.update_layout( - title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", - xaxis_title="Order Level", - yaxis_title="Spread (%)", - yaxis2_title="Amount (Quote)", - height=800, - width=1800, - plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background - paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper - font=dict(color='white') # Font color -) - -# Calculate metrics -max_loss = total_amount_quote * Decimal(sl / 100) -profit_per_level = [cum_amount * Decimal(tp / 100) for cum_amount in accumulated_amount] -loots_to_recover = [max_loss / profit for profit in profit_per_level] - -# Define a consistent annotation size and maximum value for the secondary y-axis -circle_text = "●" # Unicode character for a circle -max_secondary_value = max(max(accumulated_amount), max(order_amounts), max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data - -# Determine an appropriate y-offset for annotations -y_offset_secondary = max_secondary_value * Decimal(0.1) # Adjusts the height relative to the maximum value on the secondary y-axis - -# Add annotations to the Plotly figure for the secondary y-axis -for i, loot in enumerate(loots_to_recover): - fig.add_annotation( - x=i, - y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset - text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines - showarrow=False, - font=dict(size=16, color='purple'), - xanchor="center", # Centers the text above the x coordinate - yanchor="bottom", # Anchors the text at its bottom to avoid overlapping - align="center", - yref="y2" # Reference the secondary y-axis - ) -# Add Max Loss Metric as an Annotation -max_loss_annotation_text = f"Max Loss (Quote): {max_loss:.2f}" -fig.add_annotation( - x=max(len(dynamic_spreads), len(break_even_values)) - 1, # Positioning the annotation to the right - text=max_loss_annotation_text, - showarrow=False, - font=dict(size=20, color='white'), - bgcolor='red', # Red background for emphasis - xanchor="left", - yanchor="top", - yref="y2" # Reference the secondary y-axis -) - -st.write("---") - -# Display in Streamlit -st.plotly_chart(fig) -# Export Button -if st.button('Export as YAML'): - yaml_data = convert_to_yaml(spread_distribution, order_amounts) - st.download_button( - label="Download YAML", - data=yaml_data, - file_name='config.yaml', - mime='text/yaml' - ) diff --git a/frontend/pages/file_manager/app.py b/frontend/pages/file_manager/app.py index 1c731cc..70b2e62 100644 --- a/frontend/pages/file_manager/app.py +++ b/frontend/pages/file_manager/app.py @@ -5,8 +5,7 @@ from streamlit_elements import elements, mui from frontend.components.bots_file_explorer import BotsFileExplorer from frontend.components.dashboard import Dashboard from frontend.components.editor import Editor -from utils.st_utils import initialize_st_page - +from frontend.st_utils import initialize_st_page initialize_st_page(title="Strategy Configs", icon="🗂️", initial_sidebar_state="collapsed") diff --git a/frontend/pages/position_builder/app.py b/frontend/pages/position_builder/app.py index 146719c..b7f3a2b 100644 --- a/frontend/pages/position_builder/app.py +++ b/frontend/pages/position_builder/app.py @@ -4,8 +4,8 @@ import plotly.graph_objects as go from decimal import Decimal import yaml -from utils.st_utils import initialize_st_page 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") From 3e9cf2e7d937af560d1da9129e26dbfa1789a466 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:52:04 -0400 Subject: [PATCH 047/139] (feat) refactor launch pages --- frontend/pages/launch_bot/app.py | 2 +- frontend/pages/master_conf/app.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/pages/launch_bot/app.py b/frontend/pages/launch_bot/app.py index 99dd0a7..c2ea54c 100644 --- a/frontend/pages/launch_bot/app.py +++ b/frontend/pages/launch_bot/app.py @@ -5,7 +5,7 @@ from streamlit_elements import elements, mui from frontend.components.dashboard import Dashboard from frontend.components.launch_strategy_v2 import LaunchStrategyV2 -from utils.st_utils import initialize_st_page +from frontend.st_utils import initialize_st_page CARD_WIDTH = 6 CARD_HEIGHT = 3 diff --git a/frontend/pages/master_conf/app.py b/frontend/pages/master_conf/app.py index c92ae53..5d59277 100644 --- a/frontend/pages/master_conf/app.py +++ b/frontend/pages/master_conf/app.py @@ -6,8 +6,7 @@ from frontend.components.dashboard import Dashboard from frontend.components.editor import Editor from frontend.components.launch_master_bot_card import LaunchMasterBotCard from frontend.components.master_conf_file_explorer import MasterConfFileExplorer -from utils.st_utils import initialize_st_page - +from frontend.st_utils import initialize_st_page initialize_st_page(title="Credentials", icon="🗝️", initial_sidebar_state="collapsed") From a1665403200876e2a039d6da102ceb0d03d6c834 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:52:41 -0400 Subject: [PATCH 048/139] (feat) remove labeling, controllers and strategy from quants lab --- quants_lab/controllers/__init__.py | 0 quants_lab/controllers/bollinger_v1.py | 59 ----- quants_lab/controllers/dman_v1.py | 101 -------- quants_lab/controllers/dman_v2.py | 112 --------- quants_lab/controllers/dman_v3.py | 125 ---------- quants_lab/controllers/macd_bb_v1.py | 72 ------ quants_lab/controllers/supertrend.py | 52 ---- .../controllers/supertrend_multitimeframe.py | 89 ------- quants_lab/controllers/trend_follower_v1.py | 66 ----- quants_lab/labeling/__init__.py | 0 quants_lab/labeling/triple_barrier_method.py | 70 ------ quants_lab/strategy/__init__.py | 0 quants_lab/strategy/strategy_analysis.py | 234 ------------------ 13 files changed, 980 deletions(-) delete mode 100644 quants_lab/controllers/__init__.py delete mode 100644 quants_lab/controllers/bollinger_v1.py delete mode 100644 quants_lab/controllers/dman_v1.py delete mode 100644 quants_lab/controllers/dman_v2.py delete mode 100644 quants_lab/controllers/dman_v3.py delete mode 100644 quants_lab/controllers/macd_bb_v1.py delete mode 100644 quants_lab/controllers/supertrend.py delete mode 100644 quants_lab/controllers/supertrend_multitimeframe.py delete mode 100644 quants_lab/controllers/trend_follower_v1.py delete mode 100644 quants_lab/labeling/__init__.py delete mode 100644 quants_lab/labeling/triple_barrier_method.py delete mode 100644 quants_lab/strategy/__init__.py delete mode 100644 quants_lab/strategy/strategy_analysis.py diff --git a/quants_lab/controllers/__init__.py b/quants_lab/controllers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/quants_lab/controllers/bollinger_v1.py b/quants_lab/controllers/bollinger_v1.py deleted file mode 100644 index 30c1372..0000000 --- a/quants_lab/controllers/bollinger_v1.py +++ /dev/null @@ -1,59 +0,0 @@ -import time - -import pandas as pd -import pandas_ta as ta -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 import DirectionalTradingControllerConfigBase, \ - DirectionalTradingControllerBase -from pydantic import Field - - -class BollingerV1Conf(DirectionalTradingControllerConfigBase): - strategy_name = "bollinger_v1" - bb_length: int = Field(default=100, ge=100, le=400) - bb_std: float = Field(default=2.0, ge=2.0, le=3.0) - bb_long_threshold: float = Field(default=0.0, ge=-1.0, le=0.2) - bb_short_threshold: float = Field(default=1.0, ge=0.8, le=2.0) - - -class BollingerV1(DirectionalTradingControllerBase): - - def __init__(self, config: BollingerV1Conf): - 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. - """ - 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 - - def get_processed_data(self) -> pd.DataFrame: - df = self.candles[0].candles_df - - # Add indicators - df.ta.bbands(length=self.config.bb_length, std=self.config.bb_std, append=True) - - # Generate signal - long_condition = df[f"BBP_{self.config.bb_length}_{self.config.bb_std}"] < self.config.bb_long_threshold - short_condition = df[f"BBP_{self.config.bb_length}_{self.config.bb_std}"] > self.config.bb_short_threshold - - # Generate signal - df["signal"] = 0 - df.loc[long_condition, "signal"] = 1 - df.loc[short_condition, "signal"] = -1 - return df - - def extra_columns_to_show(self): - return [f"BBP_{self.config.bb_length}_{self.config.bb_std}"] \ No newline at end of file diff --git a/quants_lab/controllers/dman_v1.py b/quants_lab/controllers/dman_v1.py deleted file mode 100644 index 782e055..0000000 --- a/quants_lab/controllers/dman_v1.py +++ /dev/null @@ -1,101 +0,0 @@ -import time -from decimal import Decimal - -import pandas_ta as ta # noqa: F401 - -from hummingbot.core.data_type.common import TradeType -from hummingbot.smart_components.executors.position_executor.data_types import PositionConfig, TrailingStop -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.market_making.market_making_controller_base import ( - MarketMakingControllerBase, - MarketMakingControllerConfigBase, -) - - -class DManV1Config(MarketMakingControllerConfigBase): - strategy_name: str = "dman_v1" - natr_length: int = 14 - - -class DManV1(MarketMakingControllerBase): - """ - Directional Market Making Strategy making use of NATR indicator to make spreads dynamic. - """ - - def __init__(self, config: DManV1Config): - super().__init__(config) - self.config = config - - def refresh_order_condition(self, executor: PositionExecutor, order_level: OrderLevel) -> bool: - """ - Checks if the order needs to be refreshed. - You can reimplement this method to add more conditions. - """ - if executor.position_config.timestamp + order_level.order_refresh_time > time.time(): - return False - return True - - 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. - """ - 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 - - def get_processed_data(self): - """ - Gets the price and spread multiplier from the last candlestick. - """ - candles_df = self.candles[0].candles_df - natr = ta.natr(candles_df["high"], candles_df["low"], candles_df["close"], length=self.config.natr_length) / 100 - - candles_df["spread_multiplier"] = natr - candles_df["price_multiplier"] = 0.0 - return candles_df - - def get_position_config(self, order_level: OrderLevel) -> PositionConfig: - """ - Creates a PositionConfig object from an OrderLevel object. - Here you can use technical indicators to determine the parameters of the position config. - """ - close_price = self.get_close_price(self.config.trading_pair) - price_multiplier, spread_multiplier = self.get_price_and_spread_multiplier() - - price_adjusted = close_price * (1 + price_multiplier) - side_multiplier = -1 if order_level.side == TradeType.BUY else 1 - order_price = price_adjusted * (1 + order_level.spread_factor * spread_multiplier * side_multiplier) - amount = order_level.order_amount_usd / order_price - - if order_level.triple_barrier_conf.trailing_stop_trailing_delta and order_level.triple_barrier_conf.trailing_stop_trailing_delta: - trailing_stop = TrailingStop( - activation_price_delta=order_level.triple_barrier_conf.trailing_stop_activation_price_delta, - trailing_delta=order_level.triple_barrier_conf.trailing_stop_trailing_delta, - ) - else: - trailing_stop = None - position_config = PositionConfig( - timestamp=time.time(), - trading_pair=self.config.trading_pair, - exchange=self.config.exchange, - side=order_level.side, - amount=amount, - take_profit=order_level.triple_barrier_conf.take_profit, - stop_loss=order_level.triple_barrier_conf.stop_loss, - time_limit=order_level.triple_barrier_conf.time_limit, - entry_price=Decimal(order_price), - open_order_type=order_level.triple_barrier_conf.open_order_type, - take_profit_order_type=order_level.triple_barrier_conf.take_profit_order_type, - trailing_stop=trailing_stop, - leverage=self.config.leverage - ) - return position_config diff --git a/quants_lab/controllers/dman_v2.py b/quants_lab/controllers/dman_v2.py deleted file mode 100644 index 402d479..0000000 --- a/quants_lab/controllers/dman_v2.py +++ /dev/null @@ -1,112 +0,0 @@ -import time -from decimal import Decimal - -import pandas_ta as ta # noqa: F401 - -from hummingbot.core.data_type.common import TradeType -from hummingbot.smart_components.executors.position_executor.data_types import PositionConfig, TrailingStop -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.market_making.market_making_controller_base import ( - MarketMakingControllerBase, - MarketMakingControllerConfigBase, -) - - -class DManV2Config(MarketMakingControllerConfigBase): - strategy_name: str = "dman_v2" - macd_fast: int = 12 - macd_slow: int = 26 - macd_signal: int = 9 - natr_length: int = 14 - - -class DManV2(MarketMakingControllerBase): - """ - Directional Market Making Strategy making use of NATR indicator to make spreads dynamic and shift the mid price. - """ - - def __init__(self, config: DManV2Config): - super().__init__(config) - self.config = config - - def refresh_order_condition(self, executor: PositionExecutor, order_level: OrderLevel) -> bool: - """ - Checks if the order needs to be refreshed. - You can reimplement this method to add more conditions. - """ - if executor.position_config.timestamp + order_level.order_refresh_time > time.time(): - return False - return True - - 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. - """ - 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 - - def get_processed_data(self): - """ - Gets the price and spread multiplier from the last candlestick. - """ - candles_df = self.candles[0].candles_df - natr = ta.natr(candles_df["high"], candles_df["low"], candles_df["close"], length=self.config.natr_length) / 100 - - macd_output = ta.macd(candles_df["close"], fast=self.config.macd_fast, slow=self.config.macd_slow, signal=self.config.macd_signal) - macd = macd_output[f"MACD_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.macd_signal}"] - macdh = macd_output[f"MACDh_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.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 - - candles_df["spread_multiplier"] = natr - candles_df["price_multiplier"] = price_multiplier - return candles_df - - def get_position_config(self, order_level: OrderLevel) -> PositionConfig: - """ - Creates a PositionConfig object from an OrderLevel object. - Here you can use technical indicators to determine the parameters of the position config. - """ - close_price = self.get_close_price(self.config.trading_pair) - price_multiplier, spread_multiplier = self.get_price_and_spread_multiplier() - - price_adjusted = close_price * (1 + price_multiplier) - side_multiplier = -1 if order_level.side == TradeType.BUY else 1 - order_price = price_adjusted * (1 + order_level.spread_factor * spread_multiplier * side_multiplier) - amount = order_level.order_amount_usd / order_price - if order_level.triple_barrier_conf.trailing_stop_trailing_delta and order_level.triple_barrier_conf.trailing_stop_trailing_delta: - trailing_stop = TrailingStop( - activation_price_delta=order_level.triple_barrier_conf.trailing_stop_activation_price_delta, - trailing_delta=order_level.triple_barrier_conf.trailing_stop_trailing_delta, - ) - else: - trailing_stop = None - position_config = PositionConfig( - timestamp=time.time(), - trading_pair=self.config.trading_pair, - exchange=self.config.exchange, - side=order_level.side, - amount=amount, - take_profit=order_level.triple_barrier_conf.take_profit, - stop_loss=order_level.triple_barrier_conf.stop_loss, - time_limit=order_level.triple_barrier_conf.time_limit, - entry_price=Decimal(order_price), - open_order_type=order_level.triple_barrier_conf.open_order_type, - take_profit_order_type=order_level.triple_barrier_conf.take_profit_order_type, - trailing_stop=trailing_stop, - leverage=self.config.leverage - ) - return position_config diff --git a/quants_lab/controllers/dman_v3.py b/quants_lab/controllers/dman_v3.py deleted file mode 100644 index caea6f2..0000000 --- a/quants_lab/controllers/dman_v3.py +++ /dev/null @@ -1,125 +0,0 @@ -import time -from decimal import Decimal - -import pandas_ta as ta # noqa: F401 - -from hummingbot.core.data_type.common import TradeType -from hummingbot.smart_components.executors.position_executor.data_types import PositionConfig, TrailingStop -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.market_making.market_making_controller_base import ( - MarketMakingControllerBase, - MarketMakingControllerConfigBase, -) - - -class DManV3Config(MarketMakingControllerConfigBase): - strategy_name: str = "dman_v3" - bb_length: int = 100 - bb_std: float = 2.0 - side_filter: bool = False - smart_activation: bool = False - activation_threshold: Decimal = Decimal("0.001") - dynamic_spread_factor: bool = True - dynamic_target_spread: bool = False - - -class DManV3(MarketMakingControllerBase): - """ - Mean reversion strategy with Grid execution making use of Bollinger Bands indicator to make spreads dynamic - and shift the mid price. - """ - - def __init__(self, config: DManV3Config): - super().__init__(config) - self.config = config - - def refresh_order_condition(self, executor: PositionExecutor, order_level: OrderLevel) -> bool: - """ - Checks if the order needs to be refreshed. - You can reimplement this method to add more conditions. - """ - if executor.position_config.timestamp + order_level.order_refresh_time > time.time(): - return False - return True - - 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. - """ - 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 - - def get_processed_data(self): - """ - Gets the price and spread multiplier from the last candlestick. - """ - candles_df = self.candles[0].candles_df - bbp = ta.bbands(candles_df["close"], length=self.config.bb_length, std=self.config.bb_std) - - candles_df["price_multiplier"] = bbp[f"BBM_{self.config.bb_length}_{self.config.bb_std}"] - candles_df["spread_multiplier"] = bbp[f"BBB_{self.config.bb_length}_{self.config.bb_std}"] / 200 - return candles_df - - def get_position_config(self, order_level: OrderLevel) -> PositionConfig: - """ - Creates a PositionConfig object from an OrderLevel object. - Here you can use technical indicators to determine the parameters of the position config. - """ - close_price = self.get_close_price(self.config.trading_pair) - - bollinger_mid_price, spread_multiplier = self.get_price_and_spread_multiplier() - if not self.config.dynamic_spread_factor: - spread_multiplier = 1 - side_multiplier = -1 if order_level.side == TradeType.BUY else 1 - order_spread_multiplier = order_level.spread_factor * spread_multiplier * side_multiplier - order_price = bollinger_mid_price * (1 + order_spread_multiplier) - amount = order_level.order_amount_usd / order_price - - # Avoid placing the order from the opposite side - side_filter_condition = self.config.side_filter and ( - (bollinger_mid_price > close_price and side_multiplier == 1) or - (bollinger_mid_price < close_price and side_multiplier == -1)) - if side_filter_condition: - return - - # Smart activation of orders - smart_activation_condition = self.config.smart_activation and ( - side_multiplier == 1 and (close_price < order_price * (1 + self.config.activation_threshold)) or - (side_multiplier == -1 and (close_price > order_price * (1 - self.config.activation_threshold)))) - if smart_activation_condition: - return - - target_spread = spread_multiplier if self.config.dynamic_target_spread else 1 - if order_level.triple_barrier_conf.trailing_stop_trailing_delta and order_level.triple_barrier_conf.trailing_stop_trailing_delta: - trailing_stop = TrailingStop( - activation_price_delta=order_level.triple_barrier_conf.trailing_stop_activation_price_delta * target_spread, - trailing_delta=order_level.triple_barrier_conf.trailing_stop_trailing_delta * target_spread, - ) - else: - trailing_stop = None - position_config = PositionConfig( - timestamp=time.time(), - trading_pair=self.config.trading_pair, - exchange=self.config.exchange, - side=order_level.side, - amount=amount, - take_profit=order_level.triple_barrier_conf.take_profit * target_spread, - stop_loss=order_level.triple_barrier_conf.stop_loss * target_spread, - time_limit=order_level.triple_barrier_conf.time_limit, - entry_price=Decimal(order_price), - open_order_type=order_level.triple_barrier_conf.open_order_type, - take_profit_order_type=order_level.triple_barrier_conf.take_profit_order_type, - trailing_stop=trailing_stop, - leverage=self.config.leverage - ) - return position_config diff --git a/quants_lab/controllers/macd_bb_v1.py b/quants_lab/controllers/macd_bb_v1.py deleted file mode 100644 index 834362f..0000000 --- a/quants_lab/controllers/macd_bb_v1.py +++ /dev/null @@ -1,72 +0,0 @@ -import time -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 ( - DirectionalTradingControllerBase, - DirectionalTradingControllerConfigBase, -) - - -class MACDBBV1Config(DirectionalTradingControllerConfigBase): - strategy_name: str = "macd_bb_v1" - bb_length: int = Field(default=24, ge=100, le=200) - bb_std: float = Field(default=2.0, ge=2.0, le=3.0) - bb_long_threshold: float = Field(default=0.0, ge=-1.0, le=0.2) - bb_short_threshold: float = Field(default=1.0, ge=0.8, le=2.0) - macd_fast: int = Field(default=21, ge=12, le=60) - macd_slow: int = Field(default=42, ge=26, le=200) - macd_signal: int = Field(default=9, ge=8, le=20) - - -class MACDBBV1(DirectionalTradingControllerBase): - """ - Directional Market Making Strategy making use of NATR indicator to make spreads dynamic. - """ - - def __init__(self, config: MACDBBV1Config): - 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. - """ - 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 - - def get_processed_data(self) -> pd.DataFrame: - df = self.candles[0].candles_df - - # Add indicators - df.ta.bbands(length=self.config.bb_length, std=self.config.bb_std, append=True) - df.ta.macd(fast=self.config.macd_fast, slow=self.config.macd_slow, signal=self.config.macd_signal, append=True) - bbp = df[f"BBP_{self.config.bb_length}_{self.config.bb_std}"] - macdh = df[f"MACDh_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.macd_signal}"] - macd = df[f"MACD_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.macd_signal}"] - - # Generate signal - long_condition = (bbp < self.config.bb_long_threshold) & (macdh > 0) & (macd < 0) - short_condition = (bbp > self.config.bb_short_threshold) & (macdh < 0) & (macd > 0) - df["signal"] = 0 - df.loc[long_condition, "signal"] = 1 - df.loc[short_condition, "signal"] = -1 - return df - - def extra_columns_to_show(self): - return [f"BBP_{self.config.bb_length}_{self.config.bb_std}", - f"MACDh_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.macd_signal}", - f"MACD_{self.config.macd_fast}_{self.config.macd_slow}_{self.config.macd_signal}"] diff --git a/quants_lab/controllers/supertrend.py b/quants_lab/controllers/supertrend.py deleted file mode 100644 index 6da96c4..0000000 --- a/quants_lab/controllers/supertrend.py +++ /dev/null @@ -1,52 +0,0 @@ -import time - -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 SuperTrendConfig(DirectionalTradingControllerConfigBase): - strategy_name: str = "supertrend" - 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 SuperTrend(DirectionalTradingControllerBase): - def __init__(self, config: SuperTrendConfig): - 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 - - def get_processed_data(self) -> pd.DataFrame: - df = self.candles[0].candles_df - df.ta.supertrend(length=self.config.length, multiplier=self.config.multiplier, append=True) - df["percentage_distance"] = abs(df["close"] - df[f"SUPERT_{self.config.length}_{self.config.multiplier}"]) / df["close"] - - # Generate long and short conditions - long_condition = (df[f"SUPERTd_{self.config.length}_{self.config.multiplier}"] == 1) & (df["percentage_distance"] < self.config.percentage_threshold) - short_condition = (df[f"SUPERTd_{self.config.length}_{self.config.multiplier}"] == -1) & (df["percentage_distance"] < self.config.percentage_threshold) - - # Choose side - df['signal'] = 0 - df.loc[long_condition, 'signal'] = 1 - df.loc[short_condition, 'signal'] = -1 - return df diff --git a/quants_lab/controllers/supertrend_multitimeframe.py b/quants_lab/controllers/supertrend_multitimeframe.py deleted file mode 100644 index 2e389d3..0000000 --- a/quants_lab/controllers/supertrend_multitimeframe.py +++ /dev/null @@ -1,89 +0,0 @@ -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 diff --git a/quants_lab/controllers/trend_follower_v1.py b/quants_lab/controllers/trend_follower_v1.py deleted file mode 100644 index 167de8f..0000000 --- a/quants_lab/controllers/trend_follower_v1.py +++ /dev/null @@ -1,66 +0,0 @@ -import time -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 ( - DirectionalTradingControllerBase, - DirectionalTradingControllerConfigBase, -) - - -class TrendFollowerV1Config(DirectionalTradingControllerConfigBase): - strategy_name: str = "trend_follower_v1" - sma_fast: int = Field(default=20, ge=10, le=150) - sma_slow: int = Field(default=100, ge=50, le=400) - bb_length: int = Field(default=100, ge=100, le=200) - bb_std: float = Field(default=2.0, ge=2.0, le=3.0) - bb_threshold: float = Field(default=0.2, ge=0.1, le=0.5) - - -class TrendFollowerV1(DirectionalTradingControllerBase): - - def __init__(self, config: TrendFollowerV1Config): - 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 - - def get_processed_data(self) -> pd.DataFrame: - df = self.candles[0].candles_df - df.ta.sma(length=self.config.sma_fast, append=True) - df.ta.sma(length=self.config.sma_slow, append=True) - df.ta.bbands(length=self.config.bb_length, std=2.0, append=True) - - - # Generate long and short conditions - bbp = df[f"BBP_{self.config.bb_length}_2.0"] - inside_bounds_condition = (bbp < 0.5 + self.config.bb_threshold) & (bbp > 0.5 - self.config.bb_threshold) - - long_cond = (df[f'SMA_{self.config.sma_fast}'] > df[f'SMA_{self.config.sma_slow}']) - short_cond = (df[f'SMA_{self.config.sma_fast}'] < df[f'SMA_{self.config.sma_slow}']) - - # Choose side - df['signal'] = 0 - df.loc[long_cond & inside_bounds_condition, 'signal'] = 1 - df.loc[short_cond & inside_bounds_condition, 'signal'] = -1 - return df - - def extra_columns_to_show(self): - return [f"BBP_{self.config.bb_length}_{self.config.bb_std}", - f"SMA_{self.config.sma_fast}", - f"SMA_{self.config.sma_slow}"] diff --git a/quants_lab/labeling/__init__.py b/quants_lab/labeling/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/quants_lab/labeling/triple_barrier_method.py b/quants_lab/labeling/triple_barrier_method.py deleted file mode 100644 index 0650d56..0000000 --- a/quants_lab/labeling/triple_barrier_method.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Optional - -import numpy as np -import pandas as pd - - -def triple_barrier_method(df, tp=1.0, sl=1.0, tl=5, std_span: Optional[int] = 100, trade_cost=0.0006, max_executors: int = 1): - df.index = pd.to_datetime(df.timestamp, unit="ms") - if std_span: - df["target"] = df["close"].rolling(std_span).std() / df["close"] - else: - df["target"] = 1 / 100 - df["tl"] = df.index + pd.Timedelta(seconds=tl) - df.dropna(subset="target", inplace=True) - - df = apply_tp_sl_on_tl(df, tp=tp, sl=sl) - - df = get_bins(df, trade_cost) - - df['tp'] = df['close'] * (1 + df['target'] * tp * df["side"]) - df['sl'] = df['close'] * (1 - df['target'] * sl * df["side"]) - - df = add_active_signals(df, max_executors) - return df - - -def add_active_signals(df, max_executors): - close_times = [pd.Timestamp.min] * max_executors - df["active_signal"] = 0 - for index, row in df[(df["side"] != 0)].iterrows(): - for close_time in close_times: - if row["timestamp"] > close_time: - df.loc[df.index == index, "active_signal"] = 1 - close_times.remove(close_time) - close_times.append(row["close_time"]) - break - return df - - -def get_bins(df, trade_cost): - # 1) prices aligned with events - px = df.index.union(df['tl'].values).drop_duplicates() - px = df.close.reindex(px, method='ffill') - - # 2) create out object - df['ret'] = (px.loc[df['close_time'].values].values / px.loc[df.index] - 1) * df['side'] - df['real_class'] = np.sign(df['ret'] - trade_cost) - return df - - -def apply_tp_sl_on_tl(df: pd.DataFrame, tp: float, sl: float): - events = df[df["side"] != 0].copy() - if tp > 0: - take_profit = tp * events['target'] - else: - take_profit = pd.Series(index=df.index) # NaNs - if sl > 0: - stop_loss = - sl * events['target'] - else: - stop_loss = pd.Series(index=df.index) # NaNs - - for loc, tl in events['tl'].fillna(df.index[-1]).items(): - df0 = df.close[loc:tl] # path prices - df0 = (df0 / df.close[loc] - 1) * events.at[loc, 'side'] # path returns - df.loc[loc, 'stop_loss_time'] = df0[df0 < stop_loss[loc]].index.min() # earliest stop loss. - df.loc[loc, 'take_profit_time'] = df0[df0 > take_profit[loc]].index.min() # earliest profit taking. - df["close_time"] = df[["tl", "take_profit_time", "stop_loss_time"]].dropna(how='all').min(axis=1) - df['close_type'] = df[['take_profit_time', 'stop_loss_time', 'tl']].dropna(how='all').idxmin(axis=1) - df['close_type'].replace({'take_profit_time': 'tp', 'stop_loss_time': 'sl'}, inplace=True) - return df diff --git a/quants_lab/strategy/__init__.py b/quants_lab/strategy/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/quants_lab/strategy/strategy_analysis.py b/quants_lab/strategy/strategy_analysis.py deleted file mode 100644 index bd4ca0b..0000000 --- a/quants_lab/strategy/strategy_analysis.py +++ /dev/null @@ -1,234 +0,0 @@ -from typing import Optional - -import pandas as pd -from plotly.subplots import make_subplots -import pandas_ta as ta # noqa: F401 -import plotly.graph_objs as go -import numpy as np - - -class StrategyAnalysis: - def __init__(self, positions: pd.DataFrame, candles_df: Optional[pd.DataFrame] = None): - self.candles_df = candles_df - self.positions = positions - self.candles_df["timestamp"] = pd.to_datetime(self.candles_df["timestamp"], unit="ms") - self.positions["timestamp"] = pd.to_datetime(self.positions["timestamp"], unit="ms") - self.positions["close_time"] = pd.to_datetime(self.positions["close_time"], unit="ms") - self.base_figure = None - - def create_base_figure(self, candlestick=True, volume=True, positions=False, trade_pnl=False, extra_rows=0): - rows, heights = self.get_n_rows_and_heights(extra_rows + trade_pnl, volume) - self.rows = rows - specs = [[{"secondary_y": True}]] * rows - 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(row=1, col=1) - if volume: - self.add_volume() - if positions: - self.add_positions() - if trade_pnl: - self.add_trade_pnl(row=rows) - self.update_layout(volume) - - def add_positions(self): - # Add long and short positions - active_signals = self.positions.copy() - active_signals.loc[active_signals["signal"] == -1, "symbol"] = "triangle-down" - active_signals.loc[active_signals["signal"] == 1, "symbol"] = "triangle-up" - active_signals.loc[active_signals["profitable"] == 1, "color"] = "lightgreen" - active_signals.loc[active_signals["profitable"] == -1, "color"] = "red" - self.base_figure.add_trace(go.Scatter(x=active_signals.loc[(active_signals["side"] != 0), "timestamp"], - y=active_signals.loc[active_signals["side"] != 0, "close"], - name="Entry Price: $", - mode="markers", - marker_color=active_signals.loc[(active_signals["side"] != 0), "color"], - marker_symbol=active_signals.loc[(active_signals["side"] != 0), "symbol"], - marker_size=20, - marker_line={"color": "black", "width": 0.7}), - row=1, col=1) - - for index, row in active_signals.iterrows(): - self.base_figure.add_shape(type="rect", - fillcolor="green", - opacity=0.5, - x0=row.timestamp, - y0=row.close, - x1=row.close_time, - y1=row.take_profit_price, - line=dict(color="green"), - row=1, col=1) - # Add SL - self.base_figure.add_shape(type="rect", - fillcolor="red", - opacity=0.5, - x0=row.timestamp, - y0=row.close, - x1=row.close_time, - y1=row.stop_loss_price, - line=dict(color="red"), - row=1, col=1) - - def get_n_rows_and_heights(self, extra_rows, volume=True): - rows = 1 + extra_rows + volume - row_heights = [0.5] * (extra_rows) - if volume: - row_heights.insert(0, 0.2) - row_heights.insert(0, 0.8) - return rows, row_heights - - def figure(self): - return self.base_figure - - def add_candles_graph(self, row, col, name_suffix='', timeframe_suffix=''): - self.base_figure.add_trace( - go.Candlestick( - 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=row, col=col, - ) - - def add_volume(self): - self.base_figure.add_trace( - go.Bar( - x=self.candles_df["timestamp"], - y=self.candles_df["volume"], - name="Volume", - opacity=0.5, - marker=dict(color="lightgreen") - ), - row=2, col=1, - ) - - def add_trade_pnl(self, row=2): - self.base_figure.add_trace( - go.Scatter( - x=self.positions["timestamp"], - y=self.positions["net_pnl_quote"].cumsum(), - name="Cumulative Trade PnL", - mode="lines", - line=dict(color="chocolate", width=2)), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text="Cum Trade PnL", row=row, col=1) - - def update_layout(self, volume=True): - self.base_figure.update_layout( - title={ - "text": "Backtesting Analysis", - "y": 0.95, - "x": 0.5, - "xanchor": "center", - "yanchor": "top" - }, - legend=dict( - orientation="h", - yanchor="bottom", - y=-0.2, - xanchor="right", - x=1 - ), - height=1500, - xaxis_rangeslider_visible=False, - hovermode="x unified" - ) - self.base_figure.update_yaxes(title_text="Price", row=1, col=1) - if volume: - self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) - self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) - - def initial_portfolio(self): - return self.positions["inventory"].dropna().values[0] - - def final_portfolio(self): - return self.positions["inventory"].dropna().values[-1] - - def net_profit_usd(self): - return self.final_portfolio() - self.initial_portfolio() - - def net_profit_pct(self): - return self.net_profit_usd() / self.initial_portfolio() - - def returns(self): - return self.positions["net_pnl_quote"] / self.initial_portfolio() - - def total_positions(self): - return self.positions.shape[0] - 1 - - def win_signals(self): - return self.positions.loc[(self.positions["profitable"] > 0) & (self.positions["side"] != 0)] - - def loss_signals(self): - return self.positions.loc[(self.positions["profitable"] < 0) & (self.positions["side"] != 0)] - - def accuracy(self): - return self.win_signals().shape[0] / self.total_positions() - - def max_drawdown_usd(self): - cumulative_returns = self.positions["net_pnl_quote"].cumsum() - peak = np.maximum.accumulate(cumulative_returns) - drawdown = (cumulative_returns - peak) - max_draw_down = np.min(drawdown) - return max_draw_down - - def max_drawdown_pct(self): - return self.max_drawdown_usd() / self.initial_portfolio() - - def sharpe_ratio(self): - returns = self.returns() - return returns.mean() / returns.std() - - def profit_factor(self): - 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 - - def duration_in_minutes(self): - return (self.positions["timestamp"].iloc[-1] - self.positions["timestamp"].iloc[0]).total_seconds() / 60 - - def avg_trading_time_in_minutes(self): - time_diff_minutes = (self.positions["close_time"] - self.positions["timestamp"]).dt.total_seconds() / 60 - return time_diff_minutes.mean() - - def start_date(self): - return pd.to_datetime(self.candles_df.timestamp.min(), unit="ms") - - def end_date(self): - return pd.to_datetime(self.candles_df.timestamp.max(), unit="ms") - - def avg_profit(self): - return self.positions.net_pnl_quote.mean() - - def text_report(self): - return f""" -Strategy Performance Report: - - Net Profit: {self.net_profit_usd():,.2f} USD ({self.net_profit_pct() * 100:,.2f}%) - - Total Positions: {self.total_positions()} - - Win Signals: {self.win_signals().shape[0]} - - Loss Signals: {self.loss_signals().shape[0]} - - Accuracy: {self.accuracy():,.2f}% - - Profit Factor: {self.profit_factor():,.2f} - - Max Drawdown: {self.max_drawdown_usd():,.2f} USD | {self.max_drawdown_pct() * 100:,.2f}% - - Sharpe Ratio: {self.sharpe_ratio():,.2f} - - Duration: {self.duration_in_minutes() / 60:,.2f} Hours - - Average Trade Duration: {self.avg_trading_time_in_minutes():,.2f} minutes - """ - - def pnl_over_time(self): - fig = go.Figure() - fig.add_trace(go.Scatter(name="PnL Over Time", - x=self.positions.index, - y=self.positions.net_pnl_quote.cumsum())) - # Update layout with the required attributes - fig.update_layout( - title="PnL Over Time", - xaxis_title="N° Position", - yaxis=dict(title="Net PnL USD", side="left", showgrid=False), - ) - return fig From c7ecf7da49794888991d0f98d85ae3016bdc51bd Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 12:53:13 -0400 Subject: [PATCH 049/139] (feat) remove base utils page --- utils/__init__.py | 0 utils/backend_api_client.py | 186 ---------- utils/coingecko_utils.py | 54 --- utils/data_manipulation.py | 319 ----------------- utils/database_manager.py | 203 ----------- utils/file_templates.py | 169 --------- utils/graphs.py | 585 ------------------------------- utils/hummingbot_processes.py | 41 --- utils/miner_utils.py | 62 ---- utils/optuna_database_manager.py | 299 ---------------- utils/os_utils.py | 164 --------- utils/st_utils.py | 79 ----- utils/status_parser.py | 116 ------ 13 files changed, 2277 deletions(-) delete mode 100644 utils/__init__.py delete mode 100644 utils/backend_api_client.py delete mode 100644 utils/coingecko_utils.py delete mode 100644 utils/data_manipulation.py delete mode 100644 utils/database_manager.py delete mode 100644 utils/file_templates.py delete mode 100644 utils/graphs.py delete mode 100644 utils/hummingbot_processes.py delete mode 100644 utils/miner_utils.py delete mode 100644 utils/optuna_database_manager.py delete mode 100644 utils/os_utils.py delete mode 100644 utils/st_utils.py delete mode 100644 utils/status_parser.py diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/backend_api_client.py b/utils/backend_api_client.py deleted file mode 100644 index f13e73e..0000000 --- a/utils/backend_api_client.py +++ /dev/null @@ -1,186 +0,0 @@ -import requests - - -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) - return response.json() - - 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 response.json() # Handle errors or no data 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_credentials(self): - """Get available credentials.""" - url = f"{self.base_url}/list-credentials" - 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() diff --git a/utils/coingecko_utils.py b/utils/coingecko_utils.py deleted file mode 100644 index 919793b..0000000 --- a/utils/coingecko_utils.py +++ /dev/null @@ -1,54 +0,0 @@ -import time - -from pycoingecko import CoinGeckoAPI -import pandas as pd -import re - - -class CoinGeckoUtils: - def __init__(self): - self.connector = CoinGeckoAPI() - - def get_all_coins_df(self): - coin_list = self.connector.get_coins_list() - return pd.DataFrame(coin_list) - - def get_all_coins_markets_df(self): - coin_list = self.connector.get_coins_markets(vs_currency="USD") - return pd.DataFrame(coin_list) - - def get_coin_tickers_by_id(self, coin_id: str): - coin_tickers = self.connector.get_coin_ticker_by_id(id=coin_id) - coin_tickers_df = pd.DataFrame(coin_tickers["tickers"]) - coin_tickers_df["token_id"] = coin_id - return coin_tickers_df - - def get_coin_tickers_by_id_list(self, coins_id: list): - dfs = [] - for coin_id in coins_id: - df = self.get_coin_tickers_by_id(coin_id) - dfs.append(df) - time.sleep(1) - - coin_tickers_df = pd.concat(dfs) - coin_tickers_df["exchange"] = coin_tickers_df["market"].apply( - lambda x: re.sub("Exchange", "", x["name"])) - coin_tickers_df.drop(columns="market", inplace=True) - coin_tickers_df["trading_pair"] = coin_tickers_df.base + "-" + coin_tickers_df.target - return coin_tickers_df - - def get_all_exchanges_df(self): - exchanges_list = self.connector.get_exchanges_list() - return pd.DataFrame(exchanges_list) - - def get_exchanges_markets_info_by_id_list(self, exchanges_id: list): - dfs = [] - for exchange_id in exchanges_id: - df = pd.DataFrame(self.connector.get_exchanges_by_id(exchange_id)["tickers"]) - dfs.append(df) - exchanges_spreads_df = pd.concat(dfs) - exchanges_spreads_df["exchange"] = exchanges_spreads_df["market"].apply( - lambda x: re.sub("Exchange", "", x["name"])) - exchanges_spreads_df.drop(columns="market", inplace=True) - exchanges_spreads_df["trading_pair"] = exchanges_spreads_df.base + "-" + exchanges_spreads_df.target - return exchanges_spreads_df diff --git a/utils/data_manipulation.py b/utils/data_manipulation.py deleted file mode 100644 index 4060556..0000000 --- a/utils/data_manipulation.py +++ /dev/null @@ -1,319 +0,0 @@ -import datetime -from dataclasses import dataclass -import pandas as pd -import numpy as np - - -@dataclass -class StrategyData: - orders: pd.DataFrame - order_status: pd.DataFrame - trade_fill: pd.DataFrame - market_data: pd.DataFrame = None - position_executor: pd.DataFrame = None - - @property - def strategy_summary(self): - if self.trade_fill is not None: - return self.get_strategy_summary() - else: - return None - - def get_strategy_summary(self): - columns_dict = {"strategy": "Strategy", - "market": "Exchange", - "symbol": "Trading Pair", - "order_id_count": "# Trades", - "total_positions": "# Positions", - "volume_sum": "Volume", - "TAKE_PROFIT": "# TP", - "STOP_LOSS": "# SL", - "TRAILING_STOP": "# TSL", - "TIME_LIMIT": "# TL", - "net_realized_pnl_full_series": "PnL Over Time", - "net_realized_pnl_last": "Realized PnL"} - - def full_series(series): - return list(series) - - # Get trade fill data - trade_fill_data = self.trade_fill.copy() - trade_fill_data["volume"] = trade_fill_data["amount"] * trade_fill_data["price"] - grouped_trade_fill = trade_fill_data.groupby(["strategy", "market", "symbol"] - ).agg({"order_id": "count", - "volume": "sum", - "net_realized_pnl": [full_series, - "last"]}).reset_index() - grouped_trade_fill.columns = [f"{col[0]}_{col[1]}" if len(col[1]) > 0 else col[0] for col in grouped_trade_fill.columns] - - # Get position executor data - if self.position_executor is not None: - position_executor_data = self.position_executor.copy() - grouped_executors = position_executor_data.groupby(["exchange", "trading_pair", "controller_name", "close_type"]).agg(metric_count=("close_type", "count")).reset_index() - index_cols = ["exchange", "trading_pair", "controller_name"] - pivot_executors = pd.pivot_table(grouped_executors, values="metric_count", index=index_cols, columns="close_type").reset_index() - result_cols = ["TAKE_PROFIT", "STOP_LOSS", "TRAILING_STOP", "TIME_LIMIT"] - pivot_executors = pivot_executors.reindex(columns=index_cols + result_cols, fill_value=0) - pivot_executors["total_positions"] = pivot_executors[result_cols].sum(axis=1) - strategy_summary = grouped_trade_fill.merge(pivot_executors, left_on=["market", "symbol"], - right_on=["exchange", "trading_pair"], - how="left") - strategy_summary.drop(columns=["exchange", "trading_pair"], inplace=True) - else: - strategy_summary = grouped_trade_fill.copy() - strategy_summary["TAKE_PROFIT"] = np.nan - strategy_summary["STOP_LOSS"] = np.nan - strategy_summary["TRAILING_STOP"] = np.nan - strategy_summary["TIME_LIMIT"] = np.nan - strategy_summary["total_positions"] = np.nan - - strategy_summary.rename(columns=columns_dict, inplace=True) - strategy_summary.sort_values(["Realized PnL"], ascending=True, inplace=True) - strategy_summary["Explore"] = False - sorted_cols = ["Explore", "Strategy", "Exchange", "Trading Pair", "# Trades", "Volume", "# Positions", - "# TP", "# SL", "# TSL", "# TL", "PnL Over Time", "Realized PnL"] - strategy_summary = strategy_summary.reindex(columns=sorted_cols, fill_value=0) - return strategy_summary - - def get_single_market_strategy_data(self, exchange: str, trading_pair: str): - orders = self.orders[(self.orders["market"] == exchange) & (self.orders["symbol"] == trading_pair)].copy() - trade_fill = self.trade_fill[self.trade_fill["order_id"].isin(orders["id"])].copy() - order_status = self.order_status[self.order_status["order_id"].isin(orders["id"])].copy() - if self.market_data is not None: - market_data = self.market_data[(self.market_data["exchange"] == exchange) & - (self.market_data["trading_pair"] == trading_pair)].copy() - else: - market_data = None - if self.position_executor is not None: - position_executor = self.position_executor[(self.position_executor["exchange"] == exchange) & - (self.position_executor["trading_pair"] == trading_pair)].copy() - else: - position_executor = None - return SingleMarketStrategyData( - exchange=exchange, - trading_pair=trading_pair, - orders=orders, - order_status=order_status, - trade_fill=trade_fill, - market_data=market_data, - position_executor=position_executor - ) - - @property - def exchanges(self): - return self.trade_fill["market"].unique() - - @property - def trading_pairs(self): - return self.trade_fill["symbol"].unique() - - @property - def start_time(self): - return self.orders["creation_timestamp"].min() - - @property - def end_time(self): - return self.orders["last_update_timestamp"].max() - - @property - def duration_seconds(self): - return (self.end_time - self.start_time).total_seconds() - - @property - def buys(self): - return self.trade_fill[self.trade_fill["trade_type"] == "BUY"] - - @property - def sells(self): - return self.trade_fill[self.trade_fill["trade_type"] == "SELL"] - - @property - def total_buy_trades(self): - return self.buys["amount"].count() - - @property - def total_sell_trades(self): - return self.sells["amount"].count() - - @property - def total_orders(self): - return self.total_buy_trades + self.total_sell_trades - - -@dataclass -class SingleMarketStrategyData: - exchange: str - trading_pair: str - orders: pd.DataFrame - order_status: pd.DataFrame - trade_fill: pd.DataFrame - market_data: pd.DataFrame = None - position_executor: pd.DataFrame = None - - def get_filtered_strategy_data(self, start_date: datetime.datetime, end_date: datetime.datetime): - orders = self.orders[ - (self.orders["creation_timestamp"] >= start_date) & (self.orders["creation_timestamp"] <= end_date)].copy() - trade_fill = self.trade_fill[self.trade_fill["order_id"].isin(orders["id"])].copy() - order_status = self.order_status[self.order_status["order_id"].isin(orders["id"])].copy() - if self.market_data is not None: - market_data = self.market_data[ - (self.market_data.index >= start_date) & (self.market_data.index <= end_date)].copy() - else: - market_data = None - if self.position_executor is not None: - position_executor = self.position_executor[(self.position_executor.datetime >= start_date) & - (self.position_executor.datetime <= end_date)].copy() - else: - position_executor = None - return SingleMarketStrategyData( - exchange=self.exchange, - trading_pair=self.trading_pair, - orders=orders, - order_status=order_status, - trade_fill=trade_fill, - market_data=market_data, - position_executor=position_executor - ) - - def get_market_data_resampled(self, interval): - data_resampled = self.market_data.resample(interval).agg({ - "mid_price": "ohlc", - "best_bid": "last", - "best_ask": "last", - }) - data_resampled.columns = data_resampled.columns.droplevel(0) - return data_resampled - - @property - def base_asset(self): - return self.trading_pair.split("-")[0] - - @property - def quote_asset(self): - return self.trading_pair.split("-")[1] - - @property - def start_time(self): - return self.orders["creation_timestamp"].min() - - @property - def end_time(self): - return self.orders["last_update_timestamp"].max() - - @property - def duration_seconds(self): - return (self.end_time - self.start_time).total_seconds() - - @property - def start_price(self): - return self.trade_fill["price"].iat[0] - - @property - def end_price(self): - return self.trade_fill["price"].iat[-1] - - @property - def buys(self): - return self.trade_fill[self.trade_fill["trade_type"] == "BUY"] - - @property - def sells(self): - return self.trade_fill[self.trade_fill["trade_type"] == "SELL"] - - @property - def total_buy_amount(self): - return self.buys["amount"].sum() - - @property - def total_sell_amount(self): - return self.sells["amount"].sum() - - @property - def total_buy_trades(self): - return self.buys["amount"].count() - - @property - def total_sell_trades(self): - return self.sells["amount"].count() - - @property - def total_orders(self): - return self.total_buy_trades + self.total_sell_trades - - @property - def average_buy_price(self): - if self.total_buy_amount != 0: - average_price = (self.buys["price"] * self.buys["amount"]).sum() / self.total_buy_amount - return np.nan_to_num(average_price, nan=0) - else: - return 0 - - @property - def average_sell_price(self): - if self.total_sell_amount != 0: - average_price = (self.sells["price"] * self.sells["amount"]).sum() / self.total_sell_amount - return np.nan_to_num(average_price, nan=0) - else: - return 0 - - @property - def price_change(self): - return (self.end_price - self.start_price) / self.start_price - - @property - def trade_pnl_quote(self): - buy_volume = self.buys["amount"].sum() * self.average_buy_price - sell_volume = self.sells["amount"].sum() * self.average_sell_price - inventory_change_volume = self.inventory_change_base_asset * self.end_price - return sell_volume - buy_volume + inventory_change_volume - - @property - def cum_fees_in_quote(self): - return self.trade_fill["trade_fee_in_quote"].sum() - - @property - def net_pnl_quote(self): - return self.trade_pnl_quote - self.cum_fees_in_quote - - @property - def inventory_change_base_asset(self): - return self.total_buy_amount - self.total_sell_amount - - @property - def accuracy(self): - total_wins = (self.trade_fill["net_realized_pnl"] >= 0).sum() - total_losses = (self.trade_fill["net_realized_pnl"] < 0).sum() - return total_wins / (total_wins + total_losses) - - @property - def profit_factor(self): - total_profit = self.trade_fill.loc[self.trade_fill["realized_pnl"] >= 0, "realized_pnl"].sum() - total_loss = self.trade_fill.loc[self.trade_fill["realized_pnl"] < 0, "realized_pnl"].sum() - return total_profit / -total_loss - - @property - def properties_table(self): - properties_dict = {"Base Asset": self.base_asset, - "Quote Asset": self.quote_asset, - # "Start Time": self.start_time, - # "End Time": self.end_time, - "Exchange": self.exchange, - "Trading pair": self.trading_pair, - "Duration (seconds)": self.duration_seconds, - "Start Price": self.start_price, - "End Price": self.end_price, - "Total Buy Amount": self.total_buy_amount, - "Total Sell Amount": self.total_sell_amount, - "Total Buy Trades": self.total_buy_trades, - "Total Sell Trades": self.total_sell_trades, - "Total Orders": self.total_orders, - "Average Buy Price": self.average_buy_price, - "Average Sell Price": self.average_sell_price, - "Price Change": self.price_change, - "Trade PnL Quote": self.trade_pnl_quote, - "Cum Fees in Quote": self.cum_fees_in_quote, - "Net PnL Quote": self.net_pnl_quote, - "Inventory Change (base asset)": self.inventory_change_base_asset} - properties_table = pd.DataFrame([properties_dict]).transpose().reset_index() - properties_table.columns = ["Metric", "Value"] - return properties_table diff --git a/utils/database_manager.py b/utils/database_manager.py deleted file mode 100644 index 43dc57c..0000000 --- a/utils/database_manager.py +++ /dev/null @@ -1,203 +0,0 @@ -import os -import streamlit as st - -import pandas as pd -from sqlalchemy import create_engine, text -from sqlalchemy.orm import sessionmaker - -from utils.data_manipulation import StrategyData - - -class DatabaseManager: - def __init__(self, db_name: str, executors_path: str = "data"): - self.db_name = db_name - # TODO: Create db path for all types of db - self.db_path = f'sqlite:///{os.path.join(db_name)}' - self.engine = create_engine(self.db_path, connect_args={'check_same_thread': False}) - self.session_maker = sessionmaker(bind=self.engine) - - def get_strategy_data(self, config_file_path=None, start_date=None, end_date=None): - def load_data(table_loader): - try: - return table_loader() - except Exception as e: - return None # Return None to indicate failure - - # Use load_data to load tables - orders = load_data(self.get_orders) - trade_fills = load_data(self.get_trade_fills) - order_status = load_data(self.get_order_status) - market_data = load_data(self.get_market_data) - position_executor = load_data(self.get_position_executor_data) - - strategy_data = StrategyData(orders, order_status, trade_fills, market_data, position_executor) - return strategy_data - - @staticmethod - def _get_table_status(table_loader): - try: - data = table_loader() - return "Correct" if len(data) > 0 else f"Error - No records matched" - except Exception as e: - return f"Error - {str(e)}" - - @property - def status(self): - status = {"db_name": self.db_name, - "trade_fill": self._get_table_status(self.get_trade_fills), - "orders": self._get_table_status(self.get_orders), - "order_status": self._get_table_status(self.get_order_status), - "market_data": self._get_table_status(self.get_market_data), - "position_executor": self._get_table_status(self.get_position_executor_data), - } - return status - - @property - def config_files(self): - return self.get_config_files() - - @property - def configs(self): - return {config_file: self.get_exchanges_trading_pairs_by_config_file(config_file) for config_file in self.config_files} - - def get_config_files(self): - with self.session_maker() as session: - query = 'SELECT DISTINCT config_file_path FROM TradeFill' - config_files = pd.read_sql_query(text(query), session.connection()) - return config_files['config_file_path'].tolist() - - def get_exchanges_trading_pairs_by_config_file(self, config_file_path): - with self.session_maker() as session: - query = f"SELECT DISTINCT market, symbol FROM TradeFill WHERE config_file_path = '{config_file_path}'" - exchanges_trading_pairs = pd.read_sql_query(text(query), session.connection()) - exchanges_trading_pairs["market"] = exchanges_trading_pairs["market"].apply( - lambda x: x.lower().replace("_papertrade", "")) - exchanges_trading_pairs = exchanges_trading_pairs.groupby("market")["symbol"].apply(list).to_dict() - return exchanges_trading_pairs - - @staticmethod - def _get_orders_query(config_file_path=None, start_date=None, end_date=None): - query = "SELECT * FROM 'Order'" - conditions = [] - if config_file_path: - conditions.append(f"config_file_path = '{config_file_path}'") - if start_date: - conditions.append(f"created_at >= '{start_date}'") - if end_date: - conditions.append(f"created_at <= '{end_date}'") - if conditions: - query += f" WHERE {' AND '.join(conditions)}" - return query - - @staticmethod - def _get_order_status_query(order_ids=None, start_date=None, end_date=None): - query = "SELECT * FROM OrderStatus" - conditions = [] - if order_ids: - order_ids_string = ",".join(f"'{order_id}'" for order_id in order_ids) - conditions.append(f"order_id IN ({order_ids_string})") - if start_date: - conditions.append(f"created_at >= '{start_date}'") - if end_date: - conditions.append(f"created_at <= '{end_date}'") - if conditions: - query += f" WHERE {' AND '.join(conditions)}" - return query - - @staticmethod - def _get_trade_fills_query(config_file_path=None, start_date=None, end_date=None): - query = "SELECT * FROM TradeFill" - conditions = [] - if config_file_path: - conditions.append(f"config_file_path = '{config_file_path}'") - if start_date: - conditions.append(f"created_at >= '{start_date}'") - if end_date: - conditions.append(f"created_at <= '{end_date}'") - if conditions: - query += f" WHERE {' AND '.join(conditions)}" - return query - - @staticmethod - def _get_market_data_query(start_date=None, end_date=None): - query = "SELECT * FROM MarketData" - conditions = [] - if start_date: - conditions.append(f"timestamp >= '{start_date * 1e6}'") - if end_date: - conditions.append(f"timestamp <= '{end_date * 1e6}'") - if conditions: - query += f" WHERE {' AND '.join(conditions)}" - return query - - @staticmethod - def _get_position_executor_query(start_date=None, end_date=None): - query = "SELECT * FROM PositionExecutors" - conditions = [] - if start_date: - conditions.append(f"timestamp >= '{start_date}'") - if end_date: - conditions.append(f"timestamp <= '{end_date}'") - if conditions: - query += f" WHERE {' AND '.join(conditions)}" - return query - - def get_orders(self, config_file_path=None, start_date=None, end_date=None): - with self.session_maker() as session: - query = self._get_orders_query(config_file_path, start_date, end_date) - orders = pd.read_sql_query(text(query), session.connection()) - orders["market"] = orders["market"] - orders["amount"] = orders["amount"] / 1e6 - orders["price"] = orders["price"] / 1e6 - orders['creation_timestamp'] = pd.to_datetime(orders['creation_timestamp'], unit="ms") - orders['last_update_timestamp'] = pd.to_datetime(orders['last_update_timestamp'], unit="ms") - return orders - - def get_trade_fills(self, config_file_path=None, start_date=None, end_date=None): - groupers = ["config_file_path", "market", "symbol"] - float_cols = ["amount", "price", "trade_fee_in_quote"] - with self.session_maker() as session: - query = self._get_trade_fills_query(config_file_path, start_date, end_date) - trade_fills = pd.read_sql_query(text(query), session.connection()) - trade_fills[float_cols] = trade_fills[float_cols] / 1e6 - trade_fills["cum_fees_in_quote"] = trade_fills.groupby(groupers)["trade_fee_in_quote"].cumsum() - trade_fills["net_amount"] = trade_fills['amount'] * trade_fills['trade_type'].apply(lambda x: 1 if x == 'BUY' else -1) - trade_fills["net_amount_quote"] = trade_fills['net_amount'] * trade_fills['price'] - trade_fills["cum_net_amount"] = trade_fills.groupby(groupers)["net_amount"].cumsum() - trade_fills["unrealized_trade_pnl"] = -1 * trade_fills.groupby(groupers)["net_amount_quote"].cumsum() - trade_fills["inventory_cost"] = trade_fills["cum_net_amount"] * trade_fills["price"] - trade_fills["realized_trade_pnl"] = trade_fills["unrealized_trade_pnl"] + trade_fills["inventory_cost"] - trade_fills["net_realized_pnl"] = trade_fills["realized_trade_pnl"] - trade_fills["cum_fees_in_quote"] - trade_fills["realized_pnl"] = trade_fills.groupby(groupers)["net_realized_pnl"].diff() - trade_fills["gross_pnl"] = trade_fills.groupby(groupers)["realized_trade_pnl"].diff() - trade_fills["trade_fee"] = trade_fills.groupby(groupers)["cum_fees_in_quote"].diff() - trade_fills["timestamp"] = pd.to_datetime(trade_fills["timestamp"], unit="ms") - trade_fills["market"] = trade_fills["market"] - trade_fills["quote_volume"] = trade_fills["price"] * trade_fills["amount"] - return trade_fills - - def get_order_status(self, order_ids=None, start_date=None, end_date=None): - with self.session_maker() as session: - query = self._get_order_status_query(order_ids, start_date, end_date) - order_status = pd.read_sql_query(text(query), session.connection()) - return order_status - - def get_market_data(self, start_date=None, end_date=None): - with self.session_maker() as session: - query = self._get_market_data_query(start_date, end_date) - market_data = pd.read_sql_query(text(query), session.connection()) - market_data["timestamp"] = pd.to_datetime(market_data["timestamp"] / 1e6, unit="ms") - market_data.set_index("timestamp", inplace=True) - market_data["mid_price"] = market_data["mid_price"] / 1e6 - market_data["best_bid"] = market_data["best_bid"] / 1e6 - market_data["best_ask"] = market_data["best_ask"] / 1e6 - return market_data - - def get_position_executor_data(self, start_date=None, end_date=None) -> pd.DataFrame: - with self.session_maker() as session: - query = self._get_position_executor_query(start_date, end_date) - position_executor = pd.read_sql_query(text(query), session.connection()) - position_executor.set_index("timestamp", inplace=True) - position_executor["datetime"] = pd.to_datetime(position_executor.index, unit="s") - position_executor["level"] = position_executor["order_level"].apply(lambda x: x.split("_")[1]) - return position_executor diff --git a/utils/file_templates.py b/utils/file_templates.py deleted file mode 100644 index 3ad48a8..0000000 --- a/utils/file_templates.py +++ /dev/null @@ -1,169 +0,0 @@ -from typing import Dict - - -def directional_trading_controller_template(strategy_cls_name: str) -> str: - strategy_config_cls_name = f"{strategy_cls_name}Config" - sma_config_text = "{self.config.sma_length}" - return f"""import time -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 ( - DirectionalTradingControllerBase, - DirectionalTradingControllerConfigBase, -) - -class {strategy_config_cls_name}(DirectionalTradingControllerConfigBase): - strategy_name: str = "{strategy_cls_name.lower()}" - sma_length: int = Field(default=20, ge=10, le=200) - # ... Add more fields here - - -class {strategy_cls_name}(DirectionalTradingControllerBase): - - def __init__(self, config: {strategy_config_cls_name}): - 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 - - def get_processed_data(self) -> pd.DataFrame: - df = self.candles[0].candles_df - df.ta.sma(length=self.config.sma_length, append=True) - # ... Add more indicators here - # ... Check https://github.com/twopirllc/pandas-ta#indicators-by-category for more indicators - # ... Use help(ta.indicator_name) to get more info - - # Generate long and short conditions - long_cond = (df['close'] > df[f'SMA_{sma_config_text}']) - short_cond = (df['close'] < df[f'SMA_{sma_config_text}']) - - # Choose side - df['signal'] = 0 - df.loc[long_cond, 'signal'] = 1 - df.loc[short_cond, 'signal'] = -1 - return df -""" - - -def get_optuna_suggest_str(field_name: str, properties: Dict): - if field_name == "candles_config": - return f"""{field_name}=[ - CandlesConfig(connector=exchange, trading_pair=trading_pair, - interval="1h", max_records=1000000) - ]""" - if field_name == "strategy_name": - return f"{field_name}='{properties.get('default', '_')}'" - if field_name in ["order_levels", "trading_pair", "exchange"]: - return f"{field_name}={field_name}" - if field_name == "position_mode": - return f"{field_name}=PositionMode.HEDGE" - if field_name == "leverage": - return f"{field_name}=10" - - if properties["type"] == "number": - optuna_trial_str = f"trial.suggest_float('{field_name}', {properties.get('minimum', '_')}, {properties.get('maximum', '_')}, step=0.01)" - elif properties["type"] == "integer": - optuna_trial_str = f"trial.suggest_int('{field_name}', {properties.get('minimum', '_')}, {properties.get('maximum', '_')})" - elif properties["type"] == "string": - optuna_trial_str = f"trial.suggest_categorical('{field_name}', ['{properties.get('default', '_')}',])" - else: - raise Exception(f"Unknown type {properties['type']} for field {field_name}") - return f"{field_name}={optuna_trial_str}" - - -def strategy_optimization_template(strategy_info: dict): - strategy_cls = strategy_info["class"] - strategy_config = strategy_info["config"] - strategy_module = strategy_info["module"] - field_schema = strategy_config.schema()["properties"] - fields_str = [get_optuna_suggest_str(field_name, properties) for field_name, properties in field_schema.items()] - fields_str = "".join([f" {field_str},\n" for field_str in fields_str]) - return f"""import traceback -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 optuna import TrialPruned - -from quants_lab.controllers.{strategy_module} import {strategy_cls.__name__}, {strategy_config.__name__} - - -def objective(trial): - try: - # General configuration for the backtesting - exchange = "binance_perpetual" - trading_pair = "BTC-USDT" - start = "2023-01-01" - end = "2024-01-01" - initial_portfolio_usd = 1000.0 - trade_cost = 0.0006 - - # The definition of order levels is not so necessary for directional strategies now but let's you customize the - # amounts for going long or short, the cooldown time between orders and the triple barrier configuration - stop_loss = trial.suggest_float('stop_loss', 0.01, 0.02, step=0.01) - take_profit = trial.suggest_float('take_profit', 0.01, 0.05, step=0.01) - time_limit = trial.suggest_int('time_limit', 60 * 60 * 12, 60 * 60 * 24) - - triple_barrier_conf = TripleBarrierConf( - stop_loss=Decimal(stop_loss), take_profit=Decimal(take_profit), - time_limit=time_limit, - trailing_stop_activation_price_delta=Decimal("0.008"), # It's not working yet with the backtesting engine - trailing_stop_trailing_delta=Decimal("0.004"), - ) - - order_levels = [ - OrderLevel(level=0, side=TradeType.BUY, order_amount_usd=Decimal(50), - cooldown_time=15, triple_barrier_conf=triple_barrier_conf), - OrderLevel(level=0, side=TradeType.SELL, order_amount_usd=Decimal(50), - cooldown_time=15, triple_barrier_conf=triple_barrier_conf), - ] - config = {strategy_config.__name__}( -{fields_str} - ) - controller = {strategy_cls.__name__}(config=config) - engine = DirectionalTradingBacktestingEngine(controller=controller) - engine.load_controller_data("./data/candles") - backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd, trade_cost=trade_cost, - start=start, end=end) - - strategy_analysis = backtesting_results["results"] - encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode) - - trial.set_user_attr("net_pnl_quote", strategy_analysis["net_pnl_quote"]) - trial.set_user_attr("net_pnl_pct", strategy_analysis["net_pnl"]) - trial.set_user_attr("max_drawdown_usd", strategy_analysis["max_drawdown_usd"]) - trial.set_user_attr("max_drawdown_pct", strategy_analysis["max_drawdown_pct"]) - trial.set_user_attr("sharpe_ratio", strategy_analysis["sharpe_ratio"]) - trial.set_user_attr("accuracy", strategy_analysis["accuracy"]) - trial.set_user_attr("total_positions", strategy_analysis["total_positions"]) - trial.set_user_attr("profit_factor", strategy_analysis["profit_factor"]) - trial.set_user_attr("duration_in_hours", strategy_analysis["duration_minutes"] / 60) - trial.set_user_attr("avg_trading_time_in_hours", strategy_analysis["avg_trading_time_minutes"] / 60) - trial.set_user_attr("win_signals", strategy_analysis["win_signals"]) - trial.set_user_attr("loss_signals", strategy_analysis["loss_signals"]) - trial.set_user_attr("config", encoder_decoder.encode(config.dict())) - return strategy_analysis["net_pnl"] - except Exception as e: - traceback.print_exc() - raise TrialPruned() - """ diff --git a/utils/graphs.py b/utils/graphs.py deleted file mode 100644 index 608e1e0..0000000 --- a/utils/graphs.py +++ /dev/null @@ -1,585 +0,0 @@ -import pandas as pd -from plotly.subplots import make_subplots -import plotly.express as px -import pandas_ta as ta # noqa: F401 -import streamlit as st -from typing import Union - -from utils.data_manipulation import StrategyData, SingleMarketStrategyData -from quants_lab.strategy.strategy_analysis import StrategyAnalysis -import plotly.graph_objs as go - -BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" -BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" -FEE_COLOR = "rgba(51, 0, 51, 0.9)" -MIN_INTERVAL_RESOLUTION = "1m" - - -class CandlesGraph: - def __init__(self, candles_df: pd.DataFrame, line_mode=False, show_volume=True, extra_rows=1): - self.candles_df = candles_df - self.show_volume = show_volume - self.line_mode = line_mode - rows, heights = self.get_n_rows_and_heights(extra_rows) - self.rows = rows - specs = [[{"secondary_y": True}]] * rows - self.base_figure = make_subplots(rows=rows, cols=1, shared_xaxes=True, vertical_spacing=0.005, - row_heights=heights, specs=specs) - self.min_time = candles_df.reset_index().timestamp.min() - self.max_time = candles_df.reset_index().timestamp.max() - self.add_candles_graph() - if self.show_volume: - self.add_volume() - self.update_layout() - - def get_n_rows_and_heights(self, extra_rows): - rows = 1 + extra_rows + self.show_volume - row_heights = [0.4] * (extra_rows) - if self.show_volume: - row_heights.insert(0, 0.05) - row_heights.insert(0, 0.8) - return rows, row_heights - - def figure(self): - return self.base_figure - - def add_candles_graph(self): - if self.line_mode: - self.base_figure.add_trace( - go.Scatter(x=self.candles_df.index, - y=self.candles_df['close'], - name="Close", - mode='lines', - line=dict(color='blue')), - row=1, col=1, - ) - else: - hover_text = [] - for i in range(len(self.candles_df)): - hover_text.append( - f"Open: {self.candles_df['open'][i]}
" - f"High: {self.candles_df['high'][i]}
" - f"Low: {self.candles_df['low'][i]}
" - f"Close: {self.candles_df['close'][i]}
" - ) - self.base_figure.add_trace( - go.Candlestick( - x=self.candles_df.index, - open=self.candles_df['open'], - high=self.candles_df['high'], - low=self.candles_df['low'], - close=self.candles_df['close'], - name="OHLC", - hoverinfo="text", - hovertext=hover_text - ), - row=1, col=1, - ) - - def add_buy_trades(self, orders_data: pd.DataFrame): - self.base_figure.add_trace( - go.Scatter( - x=orders_data['timestamp'], - y=orders_data['price'], - name='Buy Orders', - mode='markers', - marker=dict( - symbol='triangle-up', - color='green', - size=12, - line=dict(color='black', width=1), - opacity=0.7, - ), - hoverinfo="text", - hovertext=orders_data["price"].apply(lambda x: f"Buy Order: {x}
")), - row=1, col=1, - ) - - def add_sell_trades(self, orders_data: pd.DataFrame): - self.base_figure.add_trace( - go.Scatter( - x=orders_data['timestamp'], - y=orders_data['price'], - name='Sell Orders', - mode='markers', - marker=dict(symbol='triangle-down', - color='red', - size=12, - line=dict(color='black', width=1), - opacity=0.7,), - hoverinfo="text", - hovertext=orders_data["price"].apply(lambda x: f"Sell Order: {x}
")), - row=1, col=1, - ) - - def add_bollinger_bands(self, length=20, std=2.0, row=1): - df = self.candles_df.copy() - if len(df) < length: - st.warning("Not enough data to calculate Bollinger Bands") - return - df.ta.bbands(length=length, std=std, append=True) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBU_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=row, col=1, - ) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBM_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=1, col=1, - ) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBL_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=1, col=1, - ) - - def add_volume(self): - self.base_figure.add_trace( - go.Bar( - x=self.candles_df.index, - y=self.candles_df['volume'], - name="Volume", - opacity=0.5, - marker=dict(color='lightgreen'), - - ), - row=2, col=1, - ) - - def add_ema(self, length=20, row=1): - df = self.candles_df.copy() - if len(df) < length: - st.warning("Not enough data to calculate EMA") - return - df.ta.ema(length=length, append=True) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'EMA_{length}'], - name='EMA', - mode='lines', - line=dict(color='yellow', width=1)), - row=row, col=1, - ) - - def add_quote_inventory_change(self, strategy_data: StrategyData, row=3): - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill.inventory_cost, - name="Quote Inventory", - mode="lines", - line=dict(shape="hv"), - ), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text='Quote Inventory Change', row=row, col=1) - - def add_pnl(self, strategy_data: SingleMarketStrategyData, row=4): - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=[max(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], - name="Cum Profit", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.1), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(0, 255, 0, 0.5)" - ), - row=row, col=1 - ) - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=[min(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], - name="Cum Loss", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.3), - # marker=dict(symbol="arrow"), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(255, 0, 0, 0.5)", - ), - row=row, col=1 - ) - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill["cum_fees_in_quote"].apply(lambda x: round(x, 4)), - name="Cum Fees", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.1)", dash="dash", width=0.1), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(51, 0, 51, 0.5)" - ), - row=row, col=1 - ) - self.base_figure.add_trace(go.Scatter(name="Net Realized Profit", - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill["net_realized_pnl"], - mode="lines", - line=dict(shape="hv")), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text='PNL', row=row, col=1) - - def add_positions(self, position_executor_data: pd.DataFrame, row=1): - position_executor_data["close_datetime"] = pd.to_datetime(position_executor_data["close_timestamp"], unit="s") - i = 1 - for index, rown in position_executor_data.iterrows(): - i += 1 - self.base_figure.add_trace(go.Scatter(name=f"Position {index}", - x=[rown.datetime, rown.close_datetime], - y=[rown.entry_price, rown.close_price], - mode="lines", - line=dict(color="lightgreen" if rown.net_pnl_quote > 0 else "red"), - hoverinfo="text", - hovertext=f"Position N°: {i}
" - f"Datetime: {rown.datetime}
" - f"Close datetime: {rown.close_datetime}
" - f"Side: {rown.side}
" - f"Entry price: {rown.entry_price}
" - f"Close price: {rown.close_price}
" - f"Close type: {rown.close_type}
" - f"Stop Loss: {100 * rown.sl:.2f}%
" - f"Take Profit: {100 * rown.tp:.2f}%
" - f"Time Limit: {100 * rown.tl:.2f}
" - f"Open Order Type: {rown.open_order_type}
" - f"Leverage: {rown.leverage}
" - f"Controller name: {rown.controller_name}
", - showlegend=False), - row=row, col=1) - - def update_layout(self): - self.base_figure.update_layout( - title={ - 'text': "Market activity", - 'y': 0.99, - 'x': 0.5, - 'xanchor': 'center', - 'yanchor': 'top' - }, - legend=dict( - orientation="h", - x=0.5, - y=1.04, - xanchor="center", - yanchor="bottom" - ), - height=1000, - xaxis=dict(rangeslider_visible=False, - range=[self.min_time, self.max_time]), - yaxis=dict(range=[self.candles_df.low.min(), self.candles_df.high.max()]), - hovermode='x unified' - ) - self.base_figure.update_yaxes(title_text="Price", row=1, col=1) - if self.show_volume: - self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) - self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) - - -class BacktestingGraphs: - def __init__(self, study_df: pd.DataFrame): - self.study_df = study_df - - def pnl_vs_maxdrawdown(self): - fig = go.Figure() - fig.add_trace(go.Scatter(name="Pnl vs Max Drawdown", - x=-100 * self.study_df["max_drawdown_pct"], - y=100 * self.study_df["net_pnl_pct"], - mode="markers", - text=None, - hovertext=self.study_df["hover_text"])) - fig.update_layout( - title="PnL vs Max Drawdown", - xaxis_title="Max Drawdown [%]", - yaxis_title="Net Profit [%]", - height=800 - ) - fig.data[0].text = [] - return fig - - @staticmethod - def get_trial_metrics(strategy_analysis: StrategyAnalysis, - add_volume: bool = True, - add_positions: bool = True, - add_pnl: bool = True): - """Isolated method because it needs to be called from analyze and simulate pages""" - metrics_container = st.container() - with metrics_container: - col1, col2 = st.columns(2) - with col1: - st.subheader("🏦 Market") - with col2: - st.subheader("📋 General stats") - col1, col2, col3, col4 = st.columns(4) - with col1: - st.metric("Exchange", st.session_state["strategy_params"]["exchange"]) - with col2: - st.metric("Trading Pair", st.session_state["strategy_params"]["trading_pair"]) - with col3: - st.metric("Start date", strategy_analysis.start_date().strftime("%Y-%m-%d %H:%M")) - st.metric("End date", strategy_analysis.end_date().strftime("%Y-%m-%d %H:%M")) - with col4: - st.metric("Duration (hours)", f"{strategy_analysis.duration_in_minutes() / 60:.2f}") - st.metric("Price change", st.session_state["strategy_params"]["trading_pair"]) - st.subheader("📈 Performance") - col1, col2, col3, col4, col5, col6, col7, col8 = st.columns(8) - with col1: - st.metric("Net PnL USD", - f"{strategy_analysis.net_profit_usd():.2f}", - delta=f"{100 * strategy_analysis.net_profit_pct():.2f}%", - help="The overall profit or loss achieved.") - with col2: - st.metric("Total positions", - f"{strategy_analysis.total_positions()}", - help="The total number of closed trades, winning and losing.") - with col3: - st.metric("Accuracy", - f"{100 * (len(strategy_analysis.win_signals()) / strategy_analysis.total_positions()):.2f} %", - help="The percentage of winning trades, the number of winning trades divided by the" - " total number of closed trades") - with col4: - st.metric("Profit factor", - f"{strategy_analysis.profit_factor():.2f}", - help="The amount of money the strategy made for every unit of money it lost, " - "gross profits divided by gross losses.") - with col5: - st.metric("Max Drawdown", - f"{strategy_analysis.max_drawdown_usd():.2f}", - delta=f"{100 * strategy_analysis.max_drawdown_pct():.2f}%", - help="The greatest loss drawdown, i.e., the greatest possible loss the strategy had compared " - "to its highest profits") - with col6: - st.metric("Avg Profit", - f"{strategy_analysis.avg_profit():.2f}", - help="The sum of money gained or lost by the average trade, Net Profit divided by " - "the overall number of closed trades.") - with col7: - st.metric("Avg Minutes", - f"{strategy_analysis.avg_trading_time_in_minutes():.2f}", - help="The average number of minutes that elapsed during trades for all closed trades.") - with col8: - st.metric("Sharpe Ratio", - f"{strategy_analysis.sharpe_ratio():.2f}", - help="The Sharpe ratio is a measure that quantifies the risk-adjusted return of an investment" - " or portfolio. It compares the excess return earned above a risk-free rate per unit of" - " risk taken.") - - st.plotly_chart(strategy_analysis.pnl_over_time(), use_container_width=True) - strategy_analysis.create_base_figure(volume=add_volume, positions=add_positions, trade_pnl=add_pnl) - st.plotly_chart(strategy_analysis.figure(), use_container_width=True) - return metrics_container - - -class PerformanceGraphs: - BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" - BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" - FEE_COLOR = "rgba(51, 0, 51, 0.9)" - - def __init__(self, strategy_data: Union[StrategyData, SingleMarketStrategyData]): - self.strategy_data = strategy_data - - @property - def has_summary_table(self): - if isinstance(self.strategy_data, StrategyData): - return self.strategy_data.strategy_summary is not None - else: - return False - - @property - def has_position_executor_summary(self): - if isinstance(self.strategy_data, StrategyData): - return self.strategy_data.position_executor is not None - else: - return False - - def strategy_summary_table(self): - summary = st.data_editor(self.strategy_data.strategy_summary, - column_config={"PnL Over Time": st.column_config.LineChartColumn("PnL Over Time", - y_min=0, - y_max=5000), - "Explore": st.column_config.CheckboxColumn(required=True) - }, - use_container_width=True, - hide_index=True - ) - selected_rows = summary[summary.Explore] - if len(selected_rows) > 0: - return selected_rows - else: - return None - - def summary_chart(self): - fig = px.bar(self.strategy_data.strategy_summary, x="Trading Pair", y="Realized PnL", color="Exchange") - fig.update_traces(width=min(1.0, 0.1 * len(self.strategy_data.strategy_summary))) - return fig - - def pnl_over_time(self): - df = self.strategy_data.trade_fill.copy() - df.reset_index(drop=True, inplace=True) - df_above = df[df['net_realized_pnl'] >= 0] - df_below = df[df['net_realized_pnl'] < 0] - - fig = go.Figure() - fig.add_trace(go.Bar(name="Cum Realized PnL", - x=df_above.index, - y=df_above["net_realized_pnl"], - marker_color=BULLISH_COLOR, - # hoverdq - showlegend=False)) - fig.add_trace(go.Bar(name="Cum Realized PnL", - x=df_below.index, - y=df_below["net_realized_pnl"], - marker_color=BEARISH_COLOR, - showlegend=False)) - fig.update_layout(title=dict( - text='Cummulative PnL', # Your title text - x=0.43, - y=0.95, - ), - plot_bgcolor='rgba(0,0,0,0)', - paper_bgcolor='rgba(0,0,0,0)') - return fig - - def intraday_performance(self): - df = self.strategy_data.trade_fill.copy() - - def hr2angle(hr): - return (hr * 15) % 360 - - def hr_str(hr): - # Normalize hr to be between 1 and 12 - hr_string = str(((hr - 1) % 12) + 1) - suffix = ' AM' if (hr % 24) < 12 else ' PM' - return hr_string + suffix - - df["hour"] = df["timestamp"].dt.hour - realized_pnl_per_hour = df.groupby("hour")[["realized_pnl", "quote_volume"]].sum().reset_index() - fig = go.Figure() - fig.add_trace(go.Barpolar( - name="Profits", - r=realized_pnl_per_hour["quote_volume"], - theta=realized_pnl_per_hour["hour"] * 15, - marker=dict( - color=realized_pnl_per_hour["realized_pnl"], - colorscale="RdYlGn", - cmin=-(abs(realized_pnl_per_hour["realized_pnl"]).max()), - cmid=0.0, - cmax=(abs(realized_pnl_per_hour["realized_pnl"]).max()), - colorbar=dict( - title='Realized PnL', - x=0, - y=-0.5, - xanchor='left', - yanchor='bottom', - orientation='h' - ) - ))) - fig.update_layout( - polar=dict( - radialaxis=dict( - visible=True, - showline=False, - ), - angularaxis=dict( - rotation=90, - direction="clockwise", - tickvals=[hr2angle(hr) for hr in range(24)], - ticktext=[hr_str(hr) for hr in range(24)], - ), - bgcolor='rgba(255, 255, 255, 0)', - - ), - legend=dict( - orientation="h", - x=0.5, - y=1.08, - xanchor="center", - yanchor="bottom" - ), - title=dict( - text='Intraday Performance', - x=0.5, - y=0.93, - xanchor="center", - yanchor="bottom" - ), - ) - return fig - - def returns_histogram(self): - df = self.strategy_data.trade_fill.copy() - fig = go.Figure() - fig.add_trace(go.Histogram(name="Losses", - x=df.loc[df["realized_pnl"] < 0, "realized_pnl"], - marker_color=BEARISH_COLOR)) - fig.add_trace(go.Histogram(name="Profits", - x=df.loc[df["realized_pnl"] > 0, "realized_pnl"], - marker_color=BULLISH_COLOR)) - fig.update_layout( - title=dict( - text='Returns Distribution', - x=0.5, - xanchor="center", - ), - legend=dict( - orientation="h", - yanchor="bottom", - y=1.02, - xanchor="center", - x=.48 - )) - return fig - - def position_executor_summary_sunburst(self): - if self.strategy_data.position_executor is not None: - df = self.strategy_data.position_executor.copy() - grouped_df = df.groupby(["trading_pair", "side", "close_type"]).size().reset_index(name="count") - - fig = px.sunburst(grouped_df, - path=['trading_pair', 'side', 'close_type'], - values="count", - color_continuous_scale='RdBu', - color_continuous_midpoint=0) - - fig.update_layout( - title=dict( - text='Position Executor Summary', - x=0.5, - xanchor="center", - ), - legend=dict( - orientation="h", - yanchor="bottom", - y=1.02, - xanchor="center", - x=.48 - ) - ) - return fig - else: - return None - - def candles_graph(self, candles: pd.DataFrame, interval="5m", show_volume=False, extra_rows=2): - line_mode = interval == MIN_INTERVAL_RESOLUTION - cg = CandlesGraph(candles, show_volume=show_volume, line_mode=line_mode, extra_rows=extra_rows) - cg.add_buy_trades(self.strategy_data.buys) - cg.add_sell_trades(self.strategy_data.sells) - cg.add_pnl(self.strategy_data, row=2) - cg.add_quote_inventory_change(self.strategy_data, row=3) - if self.strategy_data.position_executor is not None: - cg.add_positions(self.strategy_data.position_executor, row=1) - return cg.figure() diff --git a/utils/hummingbot_processes.py b/utils/hummingbot_processes.py deleted file mode 100644 index 4ceb001..0000000 --- a/utils/hummingbot_processes.py +++ /dev/null @@ -1,41 +0,0 @@ -import asyncio -from typing import List - -from tqdm import tqdm - -from hummingbot.data_feed.candles_feed.candles_factory import CandlesFactory, CandlesConfig - - -async def aget_candles_list(candles_configs: List[CandlesConfig]): - tasks = [aget_candles(config.connector, config.trading_pair, config.interval, config.max_records) for config in candles_configs] - return await asyncio.gather(*tasks) - - -async def aget_candles(connector_name: str, trading_pair: str, interval: str, max_records: int): - candles = CandlesFactory.get_candle(CandlesConfig(connector=connector_name, - trading_pair=trading_pair, - interval=interval, max_records=max_records)) - candles.start() - - pbar = tqdm(total=candles._candles.maxlen) - while not candles.ready: - await asyncio.sleep(1) - awaited_records = candles._candles.maxlen - len(candles._candles) - pbar.update(candles._candles.maxlen - awaited_records - pbar.n) - - pbar.close() - df = candles.candles_df - candles.stop() - return df - - -async def adownload_candles(connector_name: str, trading_pair: str, interval: str, max_records: int, download_path: str): - candles = CandlesFactory.get_candle(CandlesConfig(connector_name, trading_pair, interval, max_records)) - candles.start() - while not candles.ready: - print(f"Candles not ready yet! Missing {candles._candles.maxlen - len(candles._candles)}") - await asyncio.sleep(1) - df = candles.candles_df - df.to_csv(download_path, index=False) - candles.stop() - diff --git a/utils/miner_utils.py b/utils/miner_utils.py deleted file mode 100644 index ca70299..0000000 --- a/utils/miner_utils.py +++ /dev/null @@ -1,62 +0,0 @@ -import pandas as pd -import requests -from glom import * - - -class MinerUtils: - MARKETS_ENDPOINT = "https://api.hummingbot.io/bounty/markets" - - @staticmethod - def reward_splitter(base, reward_dict): - tmp = {"rewards_HBOT": 0, "rewards_STABLE": 0, "rewards_base": 0, } - if "HBOT" in reward_dict: - tmp["rewards_HBOT"] += reward_dict["HBOT"] - if "USDC" in reward_dict: - tmp["rewards_STABLE"] += reward_dict["USDC"] - if "USDT" in reward_dict: - tmp["rewards_STABLE"] += reward_dict["USDT"] - if base in reward_dict: - tmp["rewards_base"] += reward_dict[base] - - return pd.Series(tmp, dtype=float) - - @staticmethod - def exchange_coingecko_id(exchange: str): - converter = { - "kucoin": "kucoin", - "binance": "binance", - "gateio": "gate", - "ascendex": "bitmax" - } - return converter.get(exchange, None) - - def get_miner_stats_df(self): - miner_data = requests.get(self.MARKETS_ENDPOINT).json() - spec = { - 'market_id': ('markets', ['market_id']), - 'trading_pair': ('markets', ['trading_pair']), - 'exchange': ('markets', ['exchange_name']), - 'base': ('markets', ['base_asset']), - 'quote': ('markets', ['quote_asset']), - 'start_timestamp': ('markets', [("active_bounty_periods", ['start_timestamp'])]), - 'end_timestamp': ('markets', [("active_bounty_periods", ['end_timestamp'])]), - 'budget': ('markets', [("active_bounty_periods", ['budget'])]), - 'spread_max': ('markets', [("active_bounty_periods", ['spread_max'])]), - 'payout_asset': ('markets', [("active_bounty_periods", ['payout_asset'])]), - 'return': ('markets', ['return']), - 'last_snapshot_ts': ('markets', ['last_snapshot_ts']), - 'hourly_payout_usd': ('markets', ['hourly_payout_usd']), - 'bots': ('markets', ['bots']), - 'last_hour_bots': ('markets', ['last_hour_bots']), - 'filled_24h_volume': ('markets', ['filled_24h_volume']), - # 'weekly_reward_in_usd': ('markets', ['weekly_reward_in_usd']), - # 'weekly_reward': ('markets', ['weekly_reward']), - 'market_24h_usd_volume': ('markets', ['market_24h_usd_volume']) - } - - r = glom(miner_data, spec) - df = pd.DataFrame(r) - # df = pd.concat([df, df.apply(lambda x: self.reward_splitter(x.base, x.weekly_reward), axis=1)], axis=1) - df["trading_pair"] = df.apply(lambda x: x.base + "-" + x.quote, axis=1) - df["exchange_coingecko_id"] = df.apply(lambda x: self.exchange_coingecko_id(x.exchange), axis=1) - return df diff --git a/utils/optuna_database_manager.py b/utils/optuna_database_manager.py deleted file mode 100644 index a124153..0000000 --- a/utils/optuna_database_manager.py +++ /dev/null @@ -1,299 +0,0 @@ -import os -import json -from typing import Optional - -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]): - db_root_path = db_root_path or "data/backtesting" - self.db_name = db_name - self.db_path = f'sqlite:///{os.path.join(db_root_path, db_name)}' - self.engine = create_engine(self.db_path, connect_args={'check_same_thread': False}) - self.session_maker = sessionmaker(bind=self.engine) - - @property - def status(self): - try: - with self.session_maker() as session: - query = 'SELECT * FROM trials WHERE state = "COMPLETE"' - completed_trials = pd.read_sql_query(text(query), session.connection()) - if len(completed_trials) > 0: - # TODO: improve error handling, think what to do with other cases - return "OK" - else: - return "No records found in the trials table with completed state" - except Exception as e: - return f"Error: {str(e)}" - - @property - def tables(self): - return self._get_tables() - - def _get_tables(self): - try: - with self.session_maker() as session: - query = "SELECT name FROM sqlite_master WHERE type='table';" - tables = pd.read_sql_query(text(query), session.connection()) - return tables["name"].tolist() - except Exception as e: - return f"Error: {str(e)}" - - @property - def trials(self): - return self._get_trials_table() - - def _get_trials_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trials"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def studies(self): - return self._get_studies_table() - - def _get_studies_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM studies"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_params(self): - return self._get_trial_params_table() - - def _get_trial_params_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_params"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_values(self): - return self._get_trial_values_table() - - def _get_trial_values_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_values"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_system_attributes(self): - return self._get_trial_system_attributes_table() - - def _get_trial_system_attributes_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_system_attributes"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_system_attributes(self): - return self._get_trial_system_attributes_table() - - def _get_trial_system_attributes_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_system_attributes"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def version_info(self): - return self._get_version_info_table() - - def _get_version_info_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM version_info"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def study_directions(self): - return self._get_study_directions_table() - - def _get_study_directions_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM study_directions"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def study_user_attributes(self): - return self._get_study_user_attributes_table() - - def _get_study_user_attributes_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM study_user_attributes"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def study_system_attributes(self): - return self._get_study_system_attributes_table() - - def _get_study_system_attributes_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM study_system_attributes"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_user_attributes(self): - return self._get_trial_user_attributes_table() - - def _get_trial_user_attributes_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_user_attributes"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_intermediate_values(self): - return self._get_trial_intermediate_values_table() - - def _get_trial_intermediate_values_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_intermediate_values"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def trial_heartbeats(self): - return self._get_trial_heartbeats_table() - - def _get_trial_heartbeats_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM trial_heartbeats"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def alembic_version(self): - return self._get_alembic_version_table() - - def _get_alembic_version_table(self): - try: - with self.session_maker() as session: - df = pd.read_sql_query(text("SELECT * FROM alembic_version"), session.connection()) - return df - except Exception as e: - return f"Error: {str(e)}" - - @property - def merged_df(self): - return self._get_merged_df() - - @staticmethod - def _add_hovertext(x): - summary_label = (f"Trial ID: {x['trial_id']}
" - f"Study: {x['study_name']}
" - f"--------------------
" - f"Accuracy: {100 * x['accuracy']:.2f} %
" - f"Avg Trading Time in Hours: {x['avg_trading_time_in_hours']:.2f}
" - f"Duration in Hours: {x['duration_in_hours']:.2f}
" - f"Loss Signals: {x['loss_signals']}
" - f"Max Drawdown [%]: {100 * x['max_drawdown_pct']:.2f} %
" - f"Max Drawdown [USD]: $ {x['max_drawdown_usd']:.2f}
" - f"Net Profit [%]: {100 * x['net_pnl_pct']:.2f} %
" - f"Net Profit [$]: $ {x['net_pnl_quote']:.2f}
" - f"Profit Factor: {x['profit_factor']:.2f}
" - f"Sharpe Ratio: {x['sharpe_ratio']:.4f}
" - f"Total Positions: {x['total_positions']}
" - f"Win Signals: {x['win_signals']}
" - f"Trial value: {x['value']}
" - f"Direction: {x['direction']}
" - ) - return summary_label - - def _get_merged_df(self): - float_cols = ["accuracy", "avg_trading_time_in_hours", "duration_in_hours", "max_drawdown_pct", "max_drawdown_usd", - "net_pnl_pct", "net_pnl_quote", "profit_factor", "sharpe_ratio", "value"] - int_cols = ["loss_signals", "total_positions", "win_signals"] - merged_df = self.trials\ - .merge(self.studies, on="study_id")\ - .merge(pd.pivot(self.trial_user_attributes, index="trial_id", columns="key", values="value_json"), - on="trial_id")\ - .merge(self.trial_values, on="trial_id")\ - .merge(self.study_directions, on="study_id") - merged_df[float_cols] = merged_df[float_cols].astype("float") - merged_df[int_cols] = merged_df[int_cols].astype("int64") - merged_df["hover_text"] = merged_df.apply(self._add_hovertext, axis=1) - return merged_df - - def load_studies(self): - df = self.merged_df - study_name_col = 'study_name' - trial_id_col = 'trial_id' - nested_dict = {} - for _, row in df.iterrows(): - study_name = row[study_name_col] - trial_id = row[trial_id_col] - data_dict = row.drop([study_name_col, trial_id_col]).to_dict() - if study_name not in nested_dict: - nested_dict[study_name] = {} - nested_dict[study_name][trial_id] = data_dict - return nested_dict - - def load_params(self): - trial_id_col = 'trial_id' - param_name_col = 'param_name' - param_value_col = 'param_value' - distribution_json_col = 'distribution_json' - nested_dict = {} - for _, row in self.trial_params.iterrows(): - trial_id = row[trial_id_col] - param_name = row[param_name_col] - param_value = row[param_value_col] - distribution_json = row[distribution_json_col] - - if trial_id not in nested_dict: - nested_dict[trial_id] = {} - - dist_json = json.loads(distribution_json) - default_step = None - default_low = None - default_high = None - default_log = None - - nested_dict[trial_id][param_name] = { - 'param_name': param_name, - 'param_value': param_value, - 'step': dist_json["attributes"].get("step", default_step), - 'low': dist_json["attributes"].get("low", default_low), - 'high': dist_json["attributes"].get("high", default_high), - 'log': dist_json["attributes"].get("log", default_log), - } - return nested_dict diff --git a/utils/os_utils.py b/utils/os_utils.py deleted file mode 100644 index e0ff6c3..0000000 --- a/utils/os_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -import glob -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 -from hummingbot.smart_components.strategy_frameworks.market_making import MarketMakingControllerBase, \ - MarketMakingControllerConfigBase - - -def remove_files_from_directory(directory: str): - for file in os.listdir(directory): - os.remove(f"{directory}/{file}") - - -def remove_file(file_path: str): - os.remove(file_path) - - -def remove_directory(directory: str): - process = subprocess.Popen(f"rm -rf {directory}", shell=True) - process.wait() - - -def dump_dict_to_yaml(data_dict, filename): - with open(filename, 'w') as file: - yaml.dump(data_dict, file) - - -def read_yaml_file(file_path): - with open(file_path, 'r') as file: - data = yaml.safe_load(file) - return data - - -def directory_exists(directory: str): - return os.path.exists(directory) - - -def save_file(name: str, content: str, path: str): - complete_file_path = os.path.join(path, name) - os.makedirs(path, exist_ok=True) - with open(complete_file_path, "w") as file: - file.write(content) - - -def load_file(path: str) -> str: - try: - with open(path, 'r') as file: - contents = file.read() - return contents - except FileNotFoundError: - print(f"File '{path}' not found.") - return "" - except IOError: - print(f"Error reading file '{path}'.") - return "" - - -def get_directories_from_directory(directory: str) -> list: - directories = glob.glob(directory + "/**/") - return directories - - -def get_python_files_from_directory(directory: str) -> list: - py_files = glob.glob(directory + "/**/*.py", recursive=True) - py_files = [path for path in py_files if not path.endswith("__init__.py")] - return py_files - - -def get_log_files_from_directory(directory: str) -> list: - log_files = glob.glob(directory + "/**/*.log*", recursive=True) - return log_files - - -def get_yml_files_from_directory(directory: str) -> list: - yml = glob.glob(directory + "/**/*.yml", recursive=True) - return yml - - -def load_controllers(path): - controllers = {} - for filename in os.listdir(path): - if filename.endswith('.py') and "__init__" not in filename: - module_name = filename[:-3] # strip the .py to get the module name - controllers[module_name] = {"module": module_name} - file_path = os.path.join(path, filename) - spec = importlib.util.spec_from_file_location(module_name, file_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - for name, cls in inspect.getmembers(module, inspect.isclass): - if issubclass(cls, DirectionalTradingControllerBase) and cls is not DirectionalTradingControllerBase: - controllers[module_name]["class"] = cls - controllers[module_name]["type"] = "directional_trading" - if issubclass(cls, DirectionalTradingControllerConfigBase) and cls is not DirectionalTradingControllerConfigBase: - controllers[module_name]["config"] = cls - if issubclass(cls, MarketMakingControllerBase) and cls is not MarketMakingControllerBase: - controllers[module_name]["class"] = cls - controllers[module_name]["type"] = "market_making" - if issubclass(cls, MarketMakingControllerConfigBase) and cls is not MarketMakingControllerConfigBase: - controllers[module_name]["config"] = cls - return controllers - - -def get_bots_data_paths(): - root_directory = "hummingbot_files/bots" - bots_data_paths = {"General / Uploaded data": "data"} - reserved_word = "hummingbot-" - # Walk through the directory tree - for dirpath, dirnames, filenames in os.walk(root_directory): - for dirname in dirnames: - if dirname == "data": - parent_folder = os.path.basename(dirpath) - if parent_folder.startswith(reserved_word): - bots_data_paths[parent_folder] = os.path.join(dirpath, dirname) - if "dashboard" in bots_data_paths: - del bots_data_paths["dashboard"] - data_sources = {key: value for key, value in bots_data_paths.items() if value is not None} - return data_sources - - -def get_databases(): - databases = {} - bots_data_paths = get_bots_data_paths() - for source_name, source_path in bots_data_paths.items(): - sqlite_files = {} - for db_name in os.listdir(source_path): - if db_name.endswith(".sqlite"): - sqlite_files[db_name] = os.path.join(source_path, db_name) - databases[source_name] = sqlite_files - if len(databases) > 0: - return {key: value for key, value in databases.items() if value} - else: - return None - - -def get_function_from_file(file_path: str, function_name: str): - # Create a module specification from the file path and load it - spec = importlib.util.spec_from_file_location("module.name", file_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - # Get the function from the module - function = getattr(module, function_name) - return function - - -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 diff --git a/utils/st_utils.py b/utils/st_utils.py deleted file mode 100644 index 40c7c14..0000000 --- a/utils/st_utils.py +++ /dev/null @@ -1,79 +0,0 @@ -import os.path - -import pandas as pd -from pathlib import Path -import inspect - -import streamlit as st -from st_pages import add_page_title - -from utils.database_manager import DatabaseManager - -def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_state="collapsed"): - st.set_page_config( - page_title=title, - page_icon=icon, - layout=layout, - initial_sidebar_state=initial_sidebar_state - ) - caller_frame = inspect.currentframe().f_back - - add_page_title(layout=layout, initial_sidebar_state=initial_sidebar_state) - - current_directory = Path(os.path.dirname(inspect.getframeinfo(caller_frame).filename)) - readme_path = current_directory / "README.md" - with st.expander("About This Page"): - st.write(readme_path.read_text()) - - -def download_csv_button(df: pd.DataFrame, filename: str, key: str): - csv = df.to_csv(index=False).encode('utf-8') - return st.download_button( - label="Download CSV", - data=csv, - file_name=f"{filename}.csv", - mime="text/csv", - key=key - ) - - -def style_metric_cards( - background_color: str = "rgba(255, 255, 255, 0)", - border_size_px: int = 1, - border_color: str = "rgba(255, 255, 255, 0.3)", - border_radius_px: int = 5, - border_left_color: str = "rgba(255, 255, 255, 0.5)", - box_shadow: bool = True, -): - - box_shadow_str = ( - "box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important;" - if box_shadow - else "box-shadow: none !important;" - ) - st.markdown( - f""" - - """, - unsafe_allow_html=True, - ) - - -def db_error_message(db: DatabaseManager, error_message: str): - container = st.container() - with container: - st.warning(error_message) - with st.expander("DB Status"): - status_df = pd.DataFrame([db.status]).transpose().reset_index() - status_df.columns = ["Attribute", "Value"] - st.table(status_df) - return container diff --git a/utils/status_parser.py b/utils/status_parser.py deleted file mode 100644 index 0b9679b..0000000 --- a/utils/status_parser.py +++ /dev/null @@ -1,116 +0,0 @@ -class StatusParser: - def __init__(self, input_str, type='orders'): - self.lines = input_str.split("\n") - - if type == 'orders': - if "No active maker orders" in input_str: - self.parser = self - if "Market connectors are not ready" in input_str: - self.parser = self - elif all(keyword in input_str for keyword in ['Orders:','Exchange', 'Market', 'Side', 'Price', 'Amount', 'Age']): - self.parser = OrdersParser(self.lines, ['Exchange', 'Market', 'Side', 'Price', 'Amount', 'Age']) - elif all(keyword in input_str for keyword in ['Orders:','Level', 'Amount (Orig)', 'Amount (Adj)']): - self.parser = OrdersParser(self.lines, ['Level', 'Type', 'Price', 'Spread', 'Amount (Orig)', 'Amount (Adj)', 'Age']) - else: - raise ValueError("No matching string for type 'order'") - elif type == 'balances': - self.parser = BalancesParser(self.lines) - # if all(keyword in input_str for keyword in ['Balances:']): - else: - raise ValueError(f"Unsupported type: {type}") - - def parse(self): - return self.parser._parse() - - def _parse(self): - if "No active maker orders" in self.lines: - return "No active maker orders" - if "Market connectors are not ready" in "\n".join(self.lines): - return "Market connectors are not ready" - raise NotImplementedError - -class OrdersParser: - def __init__(self, lines, columns): - self.lines = lines - self.columns = columns - - def _parse(self): - if "No active maker orders" in "\n".join(self.lines): - return "No active maker orders" - - if "Market connectors are not ready" in "\n".join(self.lines): - return "Market connectors are not ready" - - orders = [] - for i, line in enumerate(self.lines): - if "Orders:" in line: - start_idx = i + 1 - break - - lines = self.lines[start_idx + 1:] - for line in lines: - - # Ignore warning lines - # if line.startswith("***"): - # break - - # Break when there's a blank line - if not line.strip(): - break - - parts = line.split() - if len(parts) < len(self.columns): - continue - - # Create the orders dictionary based on provided columns - order = {} - for idx, col in enumerate(self.columns): - order[col] = parts[idx] - - # Special handling for 'id' column (concatenating several parts) - if 'id' not in order: - order['id'] = ''.join(parts[:len(self.columns)-1]) - - orders.append(order) - - return orders - -class BalancesParser: - def __init__(self, lines): - self.lines = lines - self.columns = ['Exchange', 'Asset', 'Total Balance', 'Available Balance'] - - def _parse(self): - # Check if "Balances:" exists in the lines - if not any("Balances:" in line for line in self.lines): - return "No balances" - - balances = [] - for i, line in enumerate(self.lines): - if "Balances:" in line: - start_idx = i + 1 - break - - lines = self.lines[start_idx + 1:] - for line in lines: - - # Break when there's a blank line - if not line.strip(): - break - - parts = line.split() - if len(parts) < len(self.columns): - continue - - # Create the balances dictionary based on provided columns - balance = {} - for idx, col in enumerate(self.columns): - balance[col] = parts[idx] - - # Special handling for 'id' column (concatenating several parts) - if 'id' not in balance: - balance['id'] = ''.join(parts[:len(self.columns)-1]) - - balances.append(balance) - - return balances \ No newline at end of file From b1635de38dbff9410d7a8ef8edd2b8f360752c1e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:17:37 -0400 Subject: [PATCH 050/139] (feat) refactor pages structure --- .../__init__.py | 0 .../analyze}/README.md | 0 .../analyze}/__init__.py | 0 .../analyze}/analyze.py | 0 .../create}/README.md | 0 .../create}/__init__.py | 0 .../create}/create.py | 0 .../optimize}/README.md | 0 .../optimize}/__init__.py | 0 .../optimize}/optimize.py | 0 .../{bot_orchestration => config}/__init__.py | 0 .../pages/{ => config}/bollinger_v1/README.md | 0 .../bollinger_v1}/__init__.py | 0 .../pages/{ => config}/bollinger_v1/app.py | 0 .../{ => config}/dman_maker_v2/README.md | 0 .../dman_maker_v2}/__init__.py | 0 .../pages/{ => config}/dman_maker_v2/app.py | 0 frontend/pages/{ => config}/dman_v5/README.md | 0 .../dman_v5}/__init__.py | 0 frontend/pages/{ => config}/dman_v5/app.py | 0 .../{ => config}/kalman_filter_v1/README.md | 0 .../kalman_filter_v1}/__init__.py | 0 .../{ => config}/kalman_filter_v1/app.py | 0 .../pages/{ => config}/macd_bb_v1/README.md | 0 .../macd_bb_v1}/__init__.py | 0 frontend/pages/{ => config}/macd_bb_v1/app.py | 0 .../pages/{ => config}/pmm_simple/README.md | 0 .../pmm_simple}/__init__.py | 0 frontend/pages/{ => config}/pmm_simple/app.py | 8 +- .../{ => config}/position_builder/README.md | 0 .../position_builder}/__init__.py | 0 .../{ => config}/position_builder/app.py | 0 .../{ => config}/trend_follower_v1/README.md | 0 .../trend_follower_v1}/__init__.py | 0 .../{ => config}/trend_follower_v1/app.py | 0 .../{ => config}/xemm_controller/README.md | 0 .../xemm_controller}/__init__.py | 0 .../pages/{ => config}/xemm_controller/app.py | 0 .../pages/{pmm_simple => data}/__init__.py | 0 .../download_candles}/README.md | 0 .../download_candles}/__init__.py | 0 .../download_candles}/app.py | 0 .../pages/{ => data}/token_spreads/README.md | 0 .../token_spreads}/__init__.py | 0 .../pages/{ => data}/token_spreads/app.py | 0 .../pages/{ => data}/tvl_vs_mcap/README.md | 0 .../tvl_vs_mcap}/__init__.py | 0 frontend/pages/{ => data}/tvl_vs_mcap/app.py | 0 frontend/pages/master_conf/README.md | 19 ----- frontend/pages/master_conf/app.py | 43 ----------- .../__init__.py | 0 .../file_manager/README.md | 0 .../file_manager}/__init__.py | 0 .../{ => orchestration}/file_manager/app.py | 0 .../instances}/README.md | 0 .../instances}/__init__.py | 0 .../instances}/app.py | 0 .../launch_bot_v2}/README.md | 0 .../launch_bot_v2}/__init__.py | 0 .../launch_bot_v2}/app.py | 0 frontend/pages/performance/__init__.py | 0 .../{ => performance}/db_inspector/README.md | 0 .../performance/db_inspector/__init__.py | 0 .../{ => performance}/db_inspector/app.py | 0 .../strategy_performance/README.md | 0 .../strategy_performance/__init__.py | 0 .../strategy_performance/app.py | 0 frontend/pages/reference_data/7_📋_Data.py | 74 ------------------- main.py | 39 +++++----- 69 files changed, 21 insertions(+), 162 deletions(-) rename frontend/pages/{backtest_analyze => backtesting}/__init__.py (100%) rename frontend/pages/{backtest_analyze => backtesting/analyze}/README.md (100%) rename frontend/pages/{backtest_create => backtesting/analyze}/__init__.py (100%) rename frontend/pages/{backtest_analyze => backtesting/analyze}/analyze.py (100%) rename frontend/pages/{backtest_create => backtesting/create}/README.md (100%) rename frontend/pages/{backtest_optimize => backtesting/create}/__init__.py (100%) rename frontend/pages/{backtest_create => backtesting/create}/create.py (100%) rename frontend/pages/{backtest_optimize => backtesting/optimize}/README.md (100%) rename frontend/pages/{bollinger_v1 => backtesting/optimize}/__init__.py (100%) rename frontend/pages/{backtest_optimize => backtesting/optimize}/optimize.py (100%) rename frontend/pages/{bot_orchestration => config}/__init__.py (100%) rename frontend/pages/{ => config}/bollinger_v1/README.md (100%) rename frontend/pages/{data_download_candles => config/bollinger_v1}/__init__.py (100%) rename frontend/pages/{ => config}/bollinger_v1/app.py (100%) rename frontend/pages/{ => config}/dman_maker_v2/README.md (100%) rename frontend/pages/{db_inspector => config/dman_maker_v2}/__init__.py (100%) rename frontend/pages/{ => config}/dman_maker_v2/app.py (100%) rename frontend/pages/{ => config}/dman_v5/README.md (100%) rename frontend/pages/{dman_maker_v2 => config/dman_v5}/__init__.py (100%) rename frontend/pages/{ => config}/dman_v5/app.py (100%) rename frontend/pages/{ => config}/kalman_filter_v1/README.md (100%) rename frontend/pages/{dman_v5 => config/kalman_filter_v1}/__init__.py (100%) rename frontend/pages/{ => config}/kalman_filter_v1/app.py (100%) rename frontend/pages/{ => config}/macd_bb_v1/README.md (100%) rename frontend/pages/{file_manager => config/macd_bb_v1}/__init__.py (100%) rename frontend/pages/{ => config}/macd_bb_v1/app.py (100%) rename frontend/pages/{ => config}/pmm_simple/README.md (100%) rename frontend/pages/{kalman_filter_v1 => config/pmm_simple}/__init__.py (100%) rename frontend/pages/{ => config}/pmm_simple/app.py (78%) rename frontend/pages/{ => config}/position_builder/README.md (100%) rename frontend/pages/{launch_bot => config/position_builder}/__init__.py (100%) rename frontend/pages/{ => config}/position_builder/app.py (100%) rename frontend/pages/{ => config}/trend_follower_v1/README.md (100%) rename frontend/pages/{macd_bb_v1 => config/trend_follower_v1}/__init__.py (100%) rename frontend/pages/{ => config}/trend_follower_v1/app.py (100%) rename frontend/pages/{ => config}/xemm_controller/README.md (100%) rename frontend/pages/{master_conf => config/xemm_controller}/__init__.py (100%) rename frontend/pages/{ => config}/xemm_controller/app.py (100%) rename frontend/pages/{pmm_simple => data}/__init__.py (100%) rename frontend/pages/{data_download_candles => data/download_candles}/README.md (100%) rename frontend/pages/{position_builder => data/download_candles}/__init__.py (100%) rename frontend/pages/{data_download_candles => data/download_candles}/app.py (100%) rename frontend/pages/{ => data}/token_spreads/README.md (100%) rename frontend/pages/{reference_data => data/token_spreads}/__init__.py (100%) rename frontend/pages/{ => data}/token_spreads/app.py (100%) rename frontend/pages/{ => data}/tvl_vs_mcap/README.md (100%) rename frontend/pages/{strategy_performance => data/tvl_vs_mcap}/__init__.py (100%) rename frontend/pages/{ => data}/tvl_vs_mcap/app.py (100%) delete mode 100644 frontend/pages/master_conf/README.md delete mode 100644 frontend/pages/master_conf/app.py rename frontend/pages/{token_spreads => orchestration}/__init__.py (100%) rename frontend/pages/{ => orchestration}/file_manager/README.md (100%) rename frontend/pages/{trend_follower_v1 => orchestration/file_manager}/__init__.py (100%) rename frontend/pages/{ => orchestration}/file_manager/app.py (100%) rename frontend/pages/{bot_orchestration => orchestration/instances}/README.md (100%) rename frontend/pages/{tvl_vs_mcap => orchestration/instances}/__init__.py (100%) rename frontend/pages/{bot_orchestration => orchestration/instances}/app.py (100%) rename frontend/pages/{launch_bot => orchestration/launch_bot_v2}/README.md (100%) rename frontend/pages/{xemm_controller => orchestration/launch_bot_v2}/__init__.py (100%) rename frontend/pages/{launch_bot => orchestration/launch_bot_v2}/app.py (100%) create mode 100644 frontend/pages/performance/__init__.py rename frontend/pages/{ => performance}/db_inspector/README.md (100%) create mode 100644 frontend/pages/performance/db_inspector/__init__.py rename frontend/pages/{ => performance}/db_inspector/app.py (100%) rename frontend/pages/{ => performance}/strategy_performance/README.md (100%) create mode 100644 frontend/pages/performance/strategy_performance/__init__.py rename frontend/pages/{ => performance}/strategy_performance/app.py (100%) delete mode 100644 frontend/pages/reference_data/7_📋_Data.py diff --git a/frontend/pages/backtest_analyze/__init__.py b/frontend/pages/backtesting/__init__.py similarity index 100% rename from frontend/pages/backtest_analyze/__init__.py rename to frontend/pages/backtesting/__init__.py diff --git a/frontend/pages/backtest_analyze/README.md b/frontend/pages/backtesting/analyze/README.md similarity index 100% rename from frontend/pages/backtest_analyze/README.md rename to frontend/pages/backtesting/analyze/README.md diff --git a/frontend/pages/backtest_create/__init__.py b/frontend/pages/backtesting/analyze/__init__.py similarity index 100% rename from frontend/pages/backtest_create/__init__.py rename to frontend/pages/backtesting/analyze/__init__.py diff --git a/frontend/pages/backtest_analyze/analyze.py b/frontend/pages/backtesting/analyze/analyze.py similarity index 100% rename from frontend/pages/backtest_analyze/analyze.py rename to frontend/pages/backtesting/analyze/analyze.py diff --git a/frontend/pages/backtest_create/README.md b/frontend/pages/backtesting/create/README.md similarity index 100% rename from frontend/pages/backtest_create/README.md rename to frontend/pages/backtesting/create/README.md diff --git a/frontend/pages/backtest_optimize/__init__.py b/frontend/pages/backtesting/create/__init__.py similarity index 100% rename from frontend/pages/backtest_optimize/__init__.py rename to frontend/pages/backtesting/create/__init__.py diff --git a/frontend/pages/backtest_create/create.py b/frontend/pages/backtesting/create/create.py similarity index 100% rename from frontend/pages/backtest_create/create.py rename to frontend/pages/backtesting/create/create.py diff --git a/frontend/pages/backtest_optimize/README.md b/frontend/pages/backtesting/optimize/README.md similarity index 100% rename from frontend/pages/backtest_optimize/README.md rename to frontend/pages/backtesting/optimize/README.md diff --git a/frontend/pages/bollinger_v1/__init__.py b/frontend/pages/backtesting/optimize/__init__.py similarity index 100% rename from frontend/pages/bollinger_v1/__init__.py rename to frontend/pages/backtesting/optimize/__init__.py diff --git a/frontend/pages/backtest_optimize/optimize.py b/frontend/pages/backtesting/optimize/optimize.py similarity index 100% rename from frontend/pages/backtest_optimize/optimize.py rename to frontend/pages/backtesting/optimize/optimize.py diff --git a/frontend/pages/bot_orchestration/__init__.py b/frontend/pages/config/__init__.py similarity index 100% rename from frontend/pages/bot_orchestration/__init__.py rename to frontend/pages/config/__init__.py diff --git a/frontend/pages/bollinger_v1/README.md b/frontend/pages/config/bollinger_v1/README.md similarity index 100% rename from frontend/pages/bollinger_v1/README.md rename to frontend/pages/config/bollinger_v1/README.md diff --git a/frontend/pages/data_download_candles/__init__.py b/frontend/pages/config/bollinger_v1/__init__.py similarity index 100% rename from frontend/pages/data_download_candles/__init__.py rename to frontend/pages/config/bollinger_v1/__init__.py diff --git a/frontend/pages/bollinger_v1/app.py b/frontend/pages/config/bollinger_v1/app.py similarity index 100% rename from frontend/pages/bollinger_v1/app.py rename to frontend/pages/config/bollinger_v1/app.py diff --git a/frontend/pages/dman_maker_v2/README.md b/frontend/pages/config/dman_maker_v2/README.md similarity index 100% rename from frontend/pages/dman_maker_v2/README.md rename to frontend/pages/config/dman_maker_v2/README.md diff --git a/frontend/pages/db_inspector/__init__.py b/frontend/pages/config/dman_maker_v2/__init__.py similarity index 100% rename from frontend/pages/db_inspector/__init__.py rename to frontend/pages/config/dman_maker_v2/__init__.py diff --git a/frontend/pages/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py similarity index 100% rename from frontend/pages/dman_maker_v2/app.py rename to frontend/pages/config/dman_maker_v2/app.py diff --git a/frontend/pages/dman_v5/README.md b/frontend/pages/config/dman_v5/README.md similarity index 100% rename from frontend/pages/dman_v5/README.md rename to frontend/pages/config/dman_v5/README.md diff --git a/frontend/pages/dman_maker_v2/__init__.py b/frontend/pages/config/dman_v5/__init__.py similarity index 100% rename from frontend/pages/dman_maker_v2/__init__.py rename to frontend/pages/config/dman_v5/__init__.py diff --git a/frontend/pages/dman_v5/app.py b/frontend/pages/config/dman_v5/app.py similarity index 100% rename from frontend/pages/dman_v5/app.py rename to frontend/pages/config/dman_v5/app.py diff --git a/frontend/pages/kalman_filter_v1/README.md b/frontend/pages/config/kalman_filter_v1/README.md similarity index 100% rename from frontend/pages/kalman_filter_v1/README.md rename to frontend/pages/config/kalman_filter_v1/README.md diff --git a/frontend/pages/dman_v5/__init__.py b/frontend/pages/config/kalman_filter_v1/__init__.py similarity index 100% rename from frontend/pages/dman_v5/__init__.py rename to frontend/pages/config/kalman_filter_v1/__init__.py diff --git a/frontend/pages/kalman_filter_v1/app.py b/frontend/pages/config/kalman_filter_v1/app.py similarity index 100% rename from frontend/pages/kalman_filter_v1/app.py rename to frontend/pages/config/kalman_filter_v1/app.py diff --git a/frontend/pages/macd_bb_v1/README.md b/frontend/pages/config/macd_bb_v1/README.md similarity index 100% rename from frontend/pages/macd_bb_v1/README.md rename to frontend/pages/config/macd_bb_v1/README.md diff --git a/frontend/pages/file_manager/__init__.py b/frontend/pages/config/macd_bb_v1/__init__.py similarity index 100% rename from frontend/pages/file_manager/__init__.py rename to frontend/pages/config/macd_bb_v1/__init__.py diff --git a/frontend/pages/macd_bb_v1/app.py b/frontend/pages/config/macd_bb_v1/app.py similarity index 100% rename from frontend/pages/macd_bb_v1/app.py rename to frontend/pages/config/macd_bb_v1/app.py diff --git a/frontend/pages/pmm_simple/README.md b/frontend/pages/config/pmm_simple/README.md similarity index 100% rename from frontend/pages/pmm_simple/README.md rename to frontend/pages/config/pmm_simple/README.md diff --git a/frontend/pages/kalman_filter_v1/__init__.py b/frontend/pages/config/pmm_simple/__init__.py similarity index 100% rename from frontend/pages/kalman_filter_v1/__init__.py rename to frontend/pages/config/pmm_simple/__init__.py diff --git a/frontend/pages/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py similarity index 78% rename from frontend/pages/pmm_simple/app.py rename to frontend/pages/config/pmm_simple/app.py index 3b6ad19..1ffb2d8 100644 --- a/frontend/pages/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -1,14 +1,14 @@ import streamlit as st -from frontend.components.st_inputs import initialize_st_page from backend.services.backend_api_client import BackendAPIClient from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT # Import submodules -from frontend.pages.pmm_simple.user_inputs import user_inputs -from frontend.pages.pmm_simple.order_calculation import calculate_orders +from frontend.pages.config.pmm_simple.user_inputs import user_inputs +from frontend.pages.config.pmm_simple import calculate_orders from frontend.data_viz.visualization import visualize_orders -from frontend.pages.pmm_simple.config_handling import handle_config +from frontend.pages.config.pmm_simple import handle_config from frontend.components.backtesting import backtesting_section +from frontend.st_utils import initialize_st_page # Initialize the Streamlit page initialize_st_page(title="PMM Simple", icon="👨‍🏫", initial_sidebar_state="collapsed") diff --git a/frontend/pages/position_builder/README.md b/frontend/pages/config/position_builder/README.md similarity index 100% rename from frontend/pages/position_builder/README.md rename to frontend/pages/config/position_builder/README.md diff --git a/frontend/pages/launch_bot/__init__.py b/frontend/pages/config/position_builder/__init__.py similarity index 100% rename from frontend/pages/launch_bot/__init__.py rename to frontend/pages/config/position_builder/__init__.py diff --git a/frontend/pages/position_builder/app.py b/frontend/pages/config/position_builder/app.py similarity index 100% rename from frontend/pages/position_builder/app.py rename to frontend/pages/config/position_builder/app.py diff --git a/frontend/pages/trend_follower_v1/README.md b/frontend/pages/config/trend_follower_v1/README.md similarity index 100% rename from frontend/pages/trend_follower_v1/README.md rename to frontend/pages/config/trend_follower_v1/README.md diff --git a/frontend/pages/macd_bb_v1/__init__.py b/frontend/pages/config/trend_follower_v1/__init__.py similarity index 100% rename from frontend/pages/macd_bb_v1/__init__.py rename to frontend/pages/config/trend_follower_v1/__init__.py diff --git a/frontend/pages/trend_follower_v1/app.py b/frontend/pages/config/trend_follower_v1/app.py similarity index 100% rename from frontend/pages/trend_follower_v1/app.py rename to frontend/pages/config/trend_follower_v1/app.py diff --git a/frontend/pages/xemm_controller/README.md b/frontend/pages/config/xemm_controller/README.md similarity index 100% rename from frontend/pages/xemm_controller/README.md rename to frontend/pages/config/xemm_controller/README.md diff --git a/frontend/pages/master_conf/__init__.py b/frontend/pages/config/xemm_controller/__init__.py similarity index 100% rename from frontend/pages/master_conf/__init__.py rename to frontend/pages/config/xemm_controller/__init__.py diff --git a/frontend/pages/xemm_controller/app.py b/frontend/pages/config/xemm_controller/app.py similarity index 100% rename from frontend/pages/xemm_controller/app.py rename to frontend/pages/config/xemm_controller/app.py diff --git a/frontend/pages/pmm_simple/__init__.py b/frontend/pages/data/__init__.py similarity index 100% rename from frontend/pages/pmm_simple/__init__.py rename to frontend/pages/data/__init__.py diff --git a/frontend/pages/data_download_candles/README.md b/frontend/pages/data/download_candles/README.md similarity index 100% rename from frontend/pages/data_download_candles/README.md rename to frontend/pages/data/download_candles/README.md diff --git a/frontend/pages/position_builder/__init__.py b/frontend/pages/data/download_candles/__init__.py similarity index 100% rename from frontend/pages/position_builder/__init__.py rename to frontend/pages/data/download_candles/__init__.py diff --git a/frontend/pages/data_download_candles/app.py b/frontend/pages/data/download_candles/app.py similarity index 100% rename from frontend/pages/data_download_candles/app.py rename to frontend/pages/data/download_candles/app.py diff --git a/frontend/pages/token_spreads/README.md b/frontend/pages/data/token_spreads/README.md similarity index 100% rename from frontend/pages/token_spreads/README.md rename to frontend/pages/data/token_spreads/README.md diff --git a/frontend/pages/reference_data/__init__.py b/frontend/pages/data/token_spreads/__init__.py similarity index 100% rename from frontend/pages/reference_data/__init__.py rename to frontend/pages/data/token_spreads/__init__.py diff --git a/frontend/pages/token_spreads/app.py b/frontend/pages/data/token_spreads/app.py similarity index 100% rename from frontend/pages/token_spreads/app.py rename to frontend/pages/data/token_spreads/app.py diff --git a/frontend/pages/tvl_vs_mcap/README.md b/frontend/pages/data/tvl_vs_mcap/README.md similarity index 100% rename from frontend/pages/tvl_vs_mcap/README.md rename to frontend/pages/data/tvl_vs_mcap/README.md diff --git a/frontend/pages/strategy_performance/__init__.py b/frontend/pages/data/tvl_vs_mcap/__init__.py similarity index 100% rename from frontend/pages/strategy_performance/__init__.py rename to frontend/pages/data/tvl_vs_mcap/__init__.py diff --git a/frontend/pages/tvl_vs_mcap/app.py b/frontend/pages/data/tvl_vs_mcap/app.py similarity index 100% rename from frontend/pages/tvl_vs_mcap/app.py rename to frontend/pages/data/tvl_vs_mcap/app.py diff --git a/frontend/pages/master_conf/README.md b/frontend/pages/master_conf/README.md deleted file mode 100644 index 18f4d94..0000000 --- a/frontend/pages/master_conf/README.md +++ /dev/null @@ -1,19 +0,0 @@ -### Description - -This page helps you deploy and manage Hummingbot instances: - -- Starting and stopping Hummingbot Broker -- Creating, starting and stopping bot instances -- Managing strategy and script files that instances run -- Fetching status of running instances - -### Maintainers - -This page is maintained by Hummingbot Foundation as a template other pages: - -* [cardosfede](https://github.com/cardosfede) -* [fengtality](https://github.com/fengtality) - -### Wiki - -See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/frontend/pages/master_conf/app.py b/frontend/pages/master_conf/app.py deleted file mode 100644 index 5d59277..0000000 --- a/frontend/pages/master_conf/app.py +++ /dev/null @@ -1,43 +0,0 @@ -from types import SimpleNamespace -import streamlit as st -from streamlit_elements import elements, mui - -from frontend.components.dashboard import Dashboard -from frontend.components.editor import Editor -from frontend.components.launch_master_bot_card import LaunchMasterBotCard -from frontend.components.master_conf_file_explorer import MasterConfFileExplorer -from frontend.st_utils import initialize_st_page - -initialize_st_page(title="Credentials", icon="🗝️", initial_sidebar_state="collapsed") - -if "mc_board" not in st.session_state: - board = Dashboard() - mc_board = SimpleNamespace( - dashboard=board, - launch_master_bot=LaunchMasterBotCard(board, 0, 0, 12, 2), - file_explorer=MasterConfFileExplorer(board, 0, 4, 3, 7), - editor=Editor(board, 4, 4, 9, 7), - ) - st.session_state.mc_board = mc_board - -else: - mc_board = st.session_state.mc_board - -# Add new tabs -for tab_name, content in mc_board.file_explorer.tabs.items(): - if tab_name not in mc_board.editor.tabs: - mc_board.editor.add_tab(tab_name, content["content"], content["language"]) - -# Remove deleted tabs -for tab_name in list(mc_board.editor.tabs.keys()): - if tab_name not in mc_board.file_explorer.tabs: - mc_board.editor.remove_tab(tab_name) - - - -with elements("file_manager"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - with mc_board.dashboard(): - mc_board.launch_master_bot() - mc_board.file_explorer() - mc_board.editor() diff --git a/frontend/pages/token_spreads/__init__.py b/frontend/pages/orchestration/__init__.py similarity index 100% rename from frontend/pages/token_spreads/__init__.py rename to frontend/pages/orchestration/__init__.py diff --git a/frontend/pages/file_manager/README.md b/frontend/pages/orchestration/file_manager/README.md similarity index 100% rename from frontend/pages/file_manager/README.md rename to frontend/pages/orchestration/file_manager/README.md diff --git a/frontend/pages/trend_follower_v1/__init__.py b/frontend/pages/orchestration/file_manager/__init__.py similarity index 100% rename from frontend/pages/trend_follower_v1/__init__.py rename to frontend/pages/orchestration/file_manager/__init__.py diff --git a/frontend/pages/file_manager/app.py b/frontend/pages/orchestration/file_manager/app.py similarity index 100% rename from frontend/pages/file_manager/app.py rename to frontend/pages/orchestration/file_manager/app.py diff --git a/frontend/pages/bot_orchestration/README.md b/frontend/pages/orchestration/instances/README.md similarity index 100% rename from frontend/pages/bot_orchestration/README.md rename to frontend/pages/orchestration/instances/README.md diff --git a/frontend/pages/tvl_vs_mcap/__init__.py b/frontend/pages/orchestration/instances/__init__.py similarity index 100% rename from frontend/pages/tvl_vs_mcap/__init__.py rename to frontend/pages/orchestration/instances/__init__.py diff --git a/frontend/pages/bot_orchestration/app.py b/frontend/pages/orchestration/instances/app.py similarity index 100% rename from frontend/pages/bot_orchestration/app.py rename to frontend/pages/orchestration/instances/app.py diff --git a/frontend/pages/launch_bot/README.md b/frontend/pages/orchestration/launch_bot_v2/README.md similarity index 100% rename from frontend/pages/launch_bot/README.md rename to frontend/pages/orchestration/launch_bot_v2/README.md diff --git a/frontend/pages/xemm_controller/__init__.py b/frontend/pages/orchestration/launch_bot_v2/__init__.py similarity index 100% rename from frontend/pages/xemm_controller/__init__.py rename to frontend/pages/orchestration/launch_bot_v2/__init__.py diff --git a/frontend/pages/launch_bot/app.py b/frontend/pages/orchestration/launch_bot_v2/app.py similarity index 100% rename from frontend/pages/launch_bot/app.py rename to frontend/pages/orchestration/launch_bot_v2/app.py diff --git a/frontend/pages/performance/__init__.py b/frontend/pages/performance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/db_inspector/README.md b/frontend/pages/performance/db_inspector/README.md similarity index 100% rename from frontend/pages/db_inspector/README.md rename to frontend/pages/performance/db_inspector/README.md diff --git a/frontend/pages/performance/db_inspector/__init__.py b/frontend/pages/performance/db_inspector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/db_inspector/app.py b/frontend/pages/performance/db_inspector/app.py similarity index 100% rename from frontend/pages/db_inspector/app.py rename to frontend/pages/performance/db_inspector/app.py diff --git a/frontend/pages/strategy_performance/README.md b/frontend/pages/performance/strategy_performance/README.md similarity index 100% rename from frontend/pages/strategy_performance/README.md rename to frontend/pages/performance/strategy_performance/README.md diff --git a/frontend/pages/performance/strategy_performance/__init__.py b/frontend/pages/performance/strategy_performance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/strategy_performance/app.py b/frontend/pages/performance/strategy_performance/app.py similarity index 100% rename from frontend/pages/strategy_performance/app.py rename to frontend/pages/performance/strategy_performance/app.py diff --git a/frontend/pages/reference_data/7_📋_Data.py b/frontend/pages/reference_data/7_📋_Data.py deleted file mode 100644 index f1a3829..0000000 --- a/frontend/pages/reference_data/7_📋_Data.py +++ /dev/null @@ -1,74 +0,0 @@ -import streamlit as st - -import CONFIG -from backend.services.coingecko_client import CoinGeckoClient -from backend.services.miner_client import MinerClient -from frontend.st_utils import initialize_st_page - - -@st.cache_data -def get_all_coins_df(): - return CoinGeckoClient().get_all_coins_df() - - -@st.cache_data -def get_all_exchanges_df(): - return CoinGeckoClient().get_all_exchanges_df() - - -@st.cache_data -def get_miner_stats_df(): - return MinerClient().get_miner_stats_df() - - -@st.cache_data -def get_coin_tickers_by_id_list(coins_id: list): - return CoinGeckoClient().get_coin_tickers_by_id_list(coins_id) - - -initialize_st_page(title="Crypto Data", icon=":bar_chart:") - -with st.spinner(text='In progress'): - exchanges_df = get_all_exchanges_df() - coins_df = get_all_coins_df() - miner_stats_df = get_miner_stats_df() - - data = st.container() - with data: - data.write("Data loaded successfully!") - -miner_coins = coins_df.loc[coins_df["symbol"].isin(miner_stats_df["base"].str.lower().unique()), "name"] -default_miner_coins = ["Avalanche"] - -st.write("---") -st.write("## Exchanges and coins data") - -with st.expander('Coins data'): - st.dataframe(coins_df) - -with st.expander('Exchanges data'): - st.dataframe(exchanges_df) - -st.write("---") -st.write("## Tickers filtered") - -st.write("### Coins filter 🦅") -tokens = st.multiselect( - "Select the tokens to analyze:", - options=coins_df["name"], - default=default_miner_coins) - -coins_id = coins_df.loc[coins_df["name"].isin(tokens), "id"].tolist() - -with st.spinner(text='Loading coin tickers data...'): - coin_tickers_df = get_coin_tickers_by_id_list(coins_id) - coin_tickers_df["coin_name"] = coin_tickers_df.apply(lambda x: coins_df.loc[coins_df["id"] == x.token_id, "name"].item(), axis=1) - -st.write("### Exchanges filter 🦅") -exchanges = st.multiselect( - "Select the exchanges to analyze:", - options=exchanges_df["name"], - default=[exchange for exchange in CONFIG.MINER_EXCHANGES if exchange in exchanges_df["name"].unique()]) - -with st.expander('Coins Tickers Data'): - st.dataframe(coin_tickers_df) diff --git a/main.py b/main.py index 940c349..3fcd2fe 100644 --- a/main.py +++ b/main.py @@ -11,31 +11,26 @@ def main_page(): [ Page("main.py", "Hummingbot Dashboard", "📊"), Section("Bot Orchestration", "🐙"), - Page("frontend/pages/bot_orchestration/app.py", "Instances", "🦅"), - Page("frontend/pages/launch_bot/app.py", "Deploy", "🚀"), - Section("Config Generator", "🎛️"), - Page("frontend/pages/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), - Page("frontend/pages/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), - Page("frontend/pages/bollinger_v1/app.py", "Bollinger V1", "📈"), - # Page("frontend/pages/trend_follower_v1/app.py", "Trend Follower V1", "📈"), - # Page("frontend/pages/kalman_filter_v1/app.py", "Kalman Filter V1", "👨‍🔬"), - Page("frontend/pages/macd_bb_v1/app.py", "MACD_BB V1", "📊"), - # Page("frontend/pages/dman_v5/app.py", "D-Man V5", "📊"), - Page("frontend/pages/xemm_controller/app.py", "XEMM Controller", "⚡️"), - Page("frontend/pages/position_builder/app.py", "Position Builder", "🔭"), - Page("frontend/pages/dynamic_position_builder/app.py", "Dynamic Position Builder", "🔭"), - # Page("pages/master_conf/app.py", "Credentials", "🗝️"), + Page("frontend/pages/orchestration/instances/app.py", "Instances", "🦅"), + Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy", "🚀"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), + Section("Config Generator", "🎛️"), + Page("frontend/pages/config/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), + Page("frontend/pages/config/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), + Page("frontend/pages/config/bollinger_v1/app.py", "Bollinger V1", "📈"), + Page("frontend/pages/config/macd_bb_v1/app.py", "MACD_BB V1", "📊"), + Page("frontend/pages/config/xemm_controller/app.py", "XEMM Controller", "⚡️"), + Page("frontend/pages/config/position_builder/app.py", "Position Builder", "🔭"), Section("Data", "💾"), - Page("frontend/pages/data_download_candles/app.py", "Download Candles", "💹"), - # Page("pages/backtest_create/create.py", "Create", "⚔️"), - # Page("pages/backtest_optimize/optimize.py", "Optimize", "🧪"), - # Page("pages/backtest_analyze/analyze.py", "Analyze", "🔬"), + Page("frontend/pages/data/download_candles/app.py", "Download Candles", "💹"), + # Page("pages/create/create.py", "Create", "⚔️"), + # Page("pages/optimize/optimize.py", "Optimize", "🧪"), + # Page("pages/analyze/analyze.py", "Analyze", "🔬"), Section("Community Pages", "👨‍👩‍👧‍👦"), - Page("frontend/pages/strategy_performance/app.py", "Strategy Performance", "🚀"), - Page("frontend/pages/db_inspector/app.py", "DB Inspector", "🔍"), - Page("frontend/pages/token_spreads/app.py", "Token Spreads", "🧙"), - Page("frontend/pages/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"), + Page("frontend/pages/performance/strategy_performance/app.py", "Strategy Performance", "🚀"), + Page("frontend/pages/performance/db_inspector/app.py", "DB Inspector", "🔍"), + Page("frontend/pages/data/token_spreads/app.py", "Token Spreads", "🧙"), + Page("frontend/pages/data/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"), ] ) From 5143599b1631b807253804958b5f97fe9a103a84 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:21:17 -0400 Subject: [PATCH 051/139] (feat) add base utils --- frontend/st_utils.py | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 frontend/st_utils.py diff --git a/frontend/st_utils.py b/frontend/st_utils.py new file mode 100644 index 0000000..9218006 --- /dev/null +++ b/frontend/st_utils.py @@ -0,0 +1,67 @@ +import os.path + +import pandas as pd +from pathlib import Path +import inspect + +import streamlit as st +from st_pages import add_page_title + +def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_state="collapsed"): + st.set_page_config( + page_title=title, + page_icon=icon, + layout=layout, + initial_sidebar_state=initial_sidebar_state + ) + caller_frame = inspect.currentframe().f_back + + add_page_title(layout=layout, initial_sidebar_state=initial_sidebar_state) + + current_directory = Path(os.path.dirname(inspect.getframeinfo(caller_frame).filename)) + readme_path = current_directory / "README.md" + with st.expander("About This Page"): + st.write(readme_path.read_text()) + + +def download_csv_button(df: pd.DataFrame, filename: str, key: str): + csv = df.to_csv(index=False).encode('utf-8') + return st.download_button( + label="Download CSV", + data=csv, + file_name=f"{filename}.csv", + mime="text/csv", + key=key + ) + + +def style_metric_cards( + background_color: str = "rgba(255, 255, 255, 0)", + border_size_px: int = 1, + border_color: str = "rgba(255, 255, 255, 0.3)", + border_radius_px: int = 5, + border_left_color: str = "rgba(255, 255, 255, 0.5)", + box_shadow: bool = True, +): + + box_shadow_str = ( + "box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important;" + if box_shadow + else "box-shadow: none !important;" + ) + st.markdown( + f""" + + """, + unsafe_allow_html=True, + ) + From 2e66e2df0e2649bdee7f9de1e5ed9be325f8b763 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:21:22 -0400 Subject: [PATCH 052/139] (feat) add backtesting componetn --- frontend/components/backtesting.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/components/backtesting.py diff --git a/frontend/components/backtesting.py b/frontend/components/backtesting.py new file mode 100644 index 0000000..e465265 --- /dev/null +++ b/frontend/components/backtesting.py @@ -0,0 +1,28 @@ +import streamlit as st +from datetime import datetime + + +def backtesting_section(inputs, backend_api_client): + c1, c2, c3, c4, c5 = st.columns(5) + with c1: + start_date = st.date_input("Start Date", datetime(2024, 5, 1)) + with c2: + end_date = st.date_input("End Date", datetime(2024, 5, 2)) + with c3: + backtesting_resolution = st.selectbox("Backtesting Resolution", options=["1m", "3m", "5m", "15m", "30m"], index=1) + 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, + ) + return backtesting_results From cfc46e848021ce9234f51b5a8954c8e02d0f59c2 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:21:29 -0400 Subject: [PATCH 053/139] (feat) add data viz moduel --- frontend/data_viz/__init__.py | 0 frontend/data_viz/graphs.py | 584 +++++++++++++++++++++++++ frontend/data_viz/strategy_analysis.py | 234 ++++++++++ frontend/data_viz/theme.py | 31 ++ 4 files changed, 849 insertions(+) create mode 100644 frontend/data_viz/__init__.py create mode 100644 frontend/data_viz/graphs.py create mode 100644 frontend/data_viz/strategy_analysis.py create mode 100644 frontend/data_viz/theme.py diff --git a/frontend/data_viz/__init__.py b/frontend/data_viz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/data_viz/graphs.py b/frontend/data_viz/graphs.py new file mode 100644 index 0000000..b4c3eef --- /dev/null +++ b/frontend/data_viz/graphs.py @@ -0,0 +1,584 @@ +import pandas as pd +from plotly.subplots import make_subplots +import plotly.express as px +import pandas_ta as ta # noqa: F401 +import streamlit as st +from typing import Union + +from frontend.data_viz.strategy_analysis import StrategyAnalysis +import plotly.graph_objs as go + +BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" +BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" +FEE_COLOR = "rgba(51, 0, 51, 0.9)" +MIN_INTERVAL_RESOLUTION = "1m" + + +class CandlesGraph: + def __init__(self, candles_df: pd.DataFrame, line_mode=False, show_volume=True, extra_rows=1): + self.candles_df = candles_df + self.show_volume = show_volume + self.line_mode = line_mode + rows, heights = self.get_n_rows_and_heights(extra_rows) + self.rows = rows + specs = [[{"secondary_y": True}]] * rows + self.base_figure = make_subplots(rows=rows, cols=1, shared_xaxes=True, vertical_spacing=0.005, + row_heights=heights, specs=specs) + self.min_time = candles_df.reset_index().timestamp.min() + self.max_time = candles_df.reset_index().timestamp.max() + self.add_candles_graph() + if self.show_volume: + self.add_volume() + self.update_layout() + + def get_n_rows_and_heights(self, extra_rows): + rows = 1 + extra_rows + self.show_volume + row_heights = [0.4] * (extra_rows) + if self.show_volume: + row_heights.insert(0, 0.05) + row_heights.insert(0, 0.8) + return rows, row_heights + + def figure(self): + return self.base_figure + + def add_candles_graph(self): + if self.line_mode: + self.base_figure.add_trace( + go.Scatter(x=self.candles_df.index, + y=self.candles_df['close'], + name="Close", + mode='lines', + line=dict(color='blue')), + row=1, col=1, + ) + else: + hover_text = [] + for i in range(len(self.candles_df)): + hover_text.append( + f"Open: {self.candles_df['open'][i]}
" + f"High: {self.candles_df['high'][i]}
" + f"Low: {self.candles_df['low'][i]}
" + f"Close: {self.candles_df['close'][i]}
" + ) + self.base_figure.add_trace( + go.Candlestick( + x=self.candles_df.index, + open=self.candles_df['open'], + high=self.candles_df['high'], + low=self.candles_df['low'], + close=self.candles_df['close'], + name="OHLC", + hoverinfo="text", + hovertext=hover_text + ), + row=1, col=1, + ) + + def add_buy_trades(self, orders_data: pd.DataFrame): + self.base_figure.add_trace( + go.Scatter( + x=orders_data['timestamp'], + y=orders_data['price'], + name='Buy Orders', + mode='markers', + marker=dict( + symbol='triangle-up', + color='green', + size=12, + line=dict(color='black', width=1), + opacity=0.7, + ), + hoverinfo="text", + hovertext=orders_data["price"].apply(lambda x: f"Buy Order: {x}
")), + row=1, col=1, + ) + + def add_sell_trades(self, orders_data: pd.DataFrame): + self.base_figure.add_trace( + go.Scatter( + x=orders_data['timestamp'], + y=orders_data['price'], + name='Sell Orders', + mode='markers', + marker=dict(symbol='triangle-down', + color='red', + size=12, + line=dict(color='black', width=1), + opacity=0.7,), + hoverinfo="text", + hovertext=orders_data["price"].apply(lambda x: f"Sell Order: {x}
")), + row=1, col=1, + ) + + def add_bollinger_bands(self, length=20, std=2.0, row=1): + df = self.candles_df.copy() + if len(df) < length: + st.warning("Not enough data to calculate Bollinger Bands") + return + df.ta.bbands(length=length, std=std, append=True) + self.base_figure.add_trace( + go.Scatter( + x=df.index, + y=df[f'BBU_{length}_{std}'], + name='Bollinger Bands', + mode='lines', + line=dict(color='blue', width=1)), + row=row, col=1, + ) + self.base_figure.add_trace( + go.Scatter( + x=df.index, + y=df[f'BBM_{length}_{std}'], + name='Bollinger Bands', + mode='lines', + line=dict(color='blue', width=1)), + row=1, col=1, + ) + self.base_figure.add_trace( + go.Scatter( + x=df.index, + y=df[f'BBL_{length}_{std}'], + name='Bollinger Bands', + mode='lines', + line=dict(color='blue', width=1)), + row=1, col=1, + ) + + def add_volume(self): + self.base_figure.add_trace( + go.Bar( + x=self.candles_df.index, + y=self.candles_df['volume'], + name="Volume", + opacity=0.5, + marker=dict(color='lightgreen'), + + ), + row=2, col=1, + ) + + def add_ema(self, length=20, row=1): + df = self.candles_df.copy() + if len(df) < length: + st.warning("Not enough data to calculate EMA") + return + df.ta.ema(length=length, append=True) + self.base_figure.add_trace( + go.Scatter( + x=df.index, + y=df[f'EMA_{length}'], + name='EMA', + mode='lines', + line=dict(color='yellow', width=1)), + row=row, col=1, + ) + + def add_quote_inventory_change(self, strategy_data: StrategyData, row=3): + self.base_figure.add_trace( + go.Scatter( + x=strategy_data.trade_fill.timestamp, + y=strategy_data.trade_fill.inventory_cost, + name="Quote Inventory", + mode="lines", + line=dict(shape="hv"), + ), + row=row, col=1 + ) + self.base_figure.update_yaxes(title_text='Quote Inventory Change', row=row, col=1) + + def add_pnl(self, strategy_data: SingleMarketStrategyData, row=4): + self.base_figure.add_trace( + go.Scatter( + x=strategy_data.trade_fill.timestamp, + y=[max(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], + name="Cum Profit", + mode='lines', + line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.1), + fill="tozeroy", # Fill to the line below (trade pnl) + fillcolor="rgba(0, 255, 0, 0.5)" + ), + row=row, col=1 + ) + self.base_figure.add_trace( + go.Scatter( + x=strategy_data.trade_fill.timestamp, + y=[min(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], + name="Cum Loss", + mode='lines', + line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.3), + # marker=dict(symbol="arrow"), + fill="tozeroy", # Fill to the line below (trade pnl) + fillcolor="rgba(255, 0, 0, 0.5)", + ), + row=row, col=1 + ) + self.base_figure.add_trace( + go.Scatter( + x=strategy_data.trade_fill.timestamp, + y=strategy_data.trade_fill["cum_fees_in_quote"].apply(lambda x: round(x, 4)), + name="Cum Fees", + mode='lines', + line=dict(shape="hv", color="rgba(1, 1, 1, 0.1)", dash="dash", width=0.1), + fill="tozeroy", # Fill to the line below (trade pnl) + fillcolor="rgba(51, 0, 51, 0.5)" + ), + row=row, col=1 + ) + self.base_figure.add_trace(go.Scatter(name="Net Realized Profit", + x=strategy_data.trade_fill.timestamp, + y=strategy_data.trade_fill["net_realized_pnl"], + mode="lines", + line=dict(shape="hv")), + row=row, col=1 + ) + self.base_figure.update_yaxes(title_text='PNL', row=row, col=1) + + def add_positions(self, position_executor_data: pd.DataFrame, row=1): + position_executor_data["close_datetime"] = pd.to_datetime(position_executor_data["close_timestamp"], unit="s") + i = 1 + for index, rown in position_executor_data.iterrows(): + i += 1 + self.base_figure.add_trace(go.Scatter(name=f"Position {index}", + x=[rown.datetime, rown.close_datetime], + y=[rown.entry_price, rown.close_price], + mode="lines", + line=dict(color="lightgreen" if rown.net_pnl_quote > 0 else "red"), + hoverinfo="text", + hovertext=f"Position N°: {i}
" + f"Datetime: {rown.datetime}
" + f"Close datetime: {rown.close_datetime}
" + f"Side: {rown.side}
" + f"Entry price: {rown.entry_price}
" + f"Close price: {rown.close_price}
" + f"Close type: {rown.close_type}
" + f"Stop Loss: {100 * rown.sl:.2f}%
" + f"Take Profit: {100 * rown.tp:.2f}%
" + f"Time Limit: {100 * rown.tl:.2f}
" + f"Open Order Type: {rown.open_order_type}
" + f"Leverage: {rown.leverage}
" + f"Controller name: {rown.controller_name}
", + showlegend=False), + row=row, col=1) + + def update_layout(self): + self.base_figure.update_layout( + title={ + 'text': "Market activity", + 'y': 0.99, + 'x': 0.5, + 'xanchor': 'center', + 'yanchor': 'top' + }, + legend=dict( + orientation="h", + x=0.5, + y=1.04, + xanchor="center", + yanchor="bottom" + ), + height=1000, + xaxis=dict(rangeslider_visible=False, + range=[self.min_time, self.max_time]), + yaxis=dict(range=[self.candles_df.low.min(), self.candles_df.high.max()]), + hovermode='x unified' + ) + self.base_figure.update_yaxes(title_text="Price", row=1, col=1) + if self.show_volume: + self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) + self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) + + +class BacktestingGraphs: + def __init__(self, study_df: pd.DataFrame): + self.study_df = study_df + + def pnl_vs_maxdrawdown(self): + fig = go.Figure() + fig.add_trace(go.Scatter(name="Pnl vs Max Drawdown", + x=-100 * self.study_df["max_drawdown_pct"], + y=100 * self.study_df["net_pnl_pct"], + mode="markers", + text=None, + hovertext=self.study_df["hover_text"])) + fig.update_layout( + title="PnL vs Max Drawdown", + xaxis_title="Max Drawdown [%]", + yaxis_title="Net Profit [%]", + height=800 + ) + fig.data[0].text = [] + return fig + + @staticmethod + def get_trial_metrics(strategy_analysis: StrategyAnalysis, + add_volume: bool = True, + add_positions: bool = True, + add_pnl: bool = True): + """Isolated method because it needs to be called from analyze and simulate pages""" + metrics_container = st.container() + with metrics_container: + col1, col2 = st.columns(2) + with col1: + st.subheader("🏦 Market") + with col2: + st.subheader("📋 General stats") + col1, col2, col3, col4 = st.columns(4) + with col1: + st.metric("Exchange", st.session_state["strategy_params"]["exchange"]) + with col2: + st.metric("Trading Pair", st.session_state["strategy_params"]["trading_pair"]) + with col3: + st.metric("Start date", strategy_analysis.start_date().strftime("%Y-%m-%d %H:%M")) + st.metric("End date", strategy_analysis.end_date().strftime("%Y-%m-%d %H:%M")) + with col4: + st.metric("Duration (hours)", f"{strategy_analysis.duration_in_minutes() / 60:.2f}") + st.metric("Price change", st.session_state["strategy_params"]["trading_pair"]) + st.subheader("📈 Performance") + col1, col2, col3, col4, col5, col6, col7, col8 = st.columns(8) + with col1: + st.metric("Net PnL USD", + f"{strategy_analysis.net_profit_usd():.2f}", + delta=f"{100 * strategy_analysis.net_profit_pct():.2f}%", + help="The overall profit or loss achieved.") + with col2: + st.metric("Total positions", + f"{strategy_analysis.total_positions()}", + help="The total number of closed trades, winning and losing.") + with col3: + st.metric("Accuracy", + f"{100 * (len(strategy_analysis.win_signals()) / strategy_analysis.total_positions()):.2f} %", + help="The percentage of winning trades, the number of winning trades divided by the" + " total number of closed trades") + with col4: + st.metric("Profit factor", + f"{strategy_analysis.profit_factor():.2f}", + help="The amount of money the strategy made for every unit of money it lost, " + "gross profits divided by gross losses.") + with col5: + st.metric("Max Drawdown", + f"{strategy_analysis.max_drawdown_usd():.2f}", + delta=f"{100 * strategy_analysis.max_drawdown_pct():.2f}%", + help="The greatest loss drawdown, i.e., the greatest possible loss the strategy had compared " + "to its highest profits") + with col6: + st.metric("Avg Profit", + f"{strategy_analysis.avg_profit():.2f}", + help="The sum of money gained or lost by the average trade, Net Profit divided by " + "the overall number of closed trades.") + with col7: + st.metric("Avg Minutes", + f"{strategy_analysis.avg_trading_time_in_minutes():.2f}", + help="The average number of minutes that elapsed during trades for all closed trades.") + with col8: + st.metric("Sharpe Ratio", + f"{strategy_analysis.sharpe_ratio():.2f}", + help="The Sharpe ratio is a measure that quantifies the risk-adjusted return of an investment" + " or portfolio. It compares the excess return earned above a risk-free rate per unit of" + " risk taken.") + + st.plotly_chart(strategy_analysis.pnl_over_time(), use_container_width=True) + strategy_analysis.create_base_figure(volume=add_volume, positions=add_positions, trade_pnl=add_pnl) + st.plotly_chart(strategy_analysis.figure(), use_container_width=True) + return metrics_container + + +class PerformanceGraphs: + BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" + BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" + FEE_COLOR = "rgba(51, 0, 51, 0.9)" + + def __init__(self, strategy_data: Union[StrategyData, SingleMarketStrategyData]): + self.strategy_data = strategy_data + + @property + def has_summary_table(self): + if isinstance(self.strategy_data, StrategyData): + return self.strategy_data.strategy_summary is not None + else: + return False + + @property + def has_position_executor_summary(self): + if isinstance(self.strategy_data, StrategyData): + return self.strategy_data.position_executor is not None + else: + return False + + def strategy_summary_table(self): + summary = st.data_editor(self.strategy_data.strategy_summary, + column_config={"PnL Over Time": st.column_config.LineChartColumn("PnL Over Time", + y_min=0, + y_max=5000), + "Explore": st.column_config.CheckboxColumn(required=True) + }, + use_container_width=True, + hide_index=True + ) + selected_rows = summary[summary.Explore] + if len(selected_rows) > 0: + return selected_rows + else: + return None + + def summary_chart(self): + fig = px.bar(self.strategy_data.strategy_summary, x="Trading Pair", y="Realized PnL", color="Exchange") + fig.update_traces(width=min(1.0, 0.1 * len(self.strategy_data.strategy_summary))) + return fig + + def pnl_over_time(self): + df = self.strategy_data.trade_fill.copy() + df.reset_index(drop=True, inplace=True) + df_above = df[df['net_realized_pnl'] >= 0] + df_below = df[df['net_realized_pnl'] < 0] + + fig = go.Figure() + fig.add_trace(go.Bar(name="Cum Realized PnL", + x=df_above.index, + y=df_above["net_realized_pnl"], + marker_color=BULLISH_COLOR, + # hoverdq + showlegend=False)) + fig.add_trace(go.Bar(name="Cum Realized PnL", + x=df_below.index, + y=df_below["net_realized_pnl"], + marker_color=BEARISH_COLOR, + showlegend=False)) + fig.update_layout(title=dict( + text='Cummulative PnL', # Your title text + x=0.43, + y=0.95, + ), + plot_bgcolor='rgba(0,0,0,0)', + paper_bgcolor='rgba(0,0,0,0)') + return fig + + def intraday_performance(self): + df = self.strategy_data.trade_fill.copy() + + def hr2angle(hr): + return (hr * 15) % 360 + + def hr_str(hr): + # Normalize hr to be between 1 and 12 + hr_string = str(((hr - 1) % 12) + 1) + suffix = ' AM' if (hr % 24) < 12 else ' PM' + return hr_string + suffix + + df["hour"] = df["timestamp"].dt.hour + realized_pnl_per_hour = df.groupby("hour")[["realized_pnl", "quote_volume"]].sum().reset_index() + fig = go.Figure() + fig.add_trace(go.Barpolar( + name="Profits", + r=realized_pnl_per_hour["quote_volume"], + theta=realized_pnl_per_hour["hour"] * 15, + marker=dict( + color=realized_pnl_per_hour["realized_pnl"], + colorscale="RdYlGn", + cmin=-(abs(realized_pnl_per_hour["realized_pnl"]).max()), + cmid=0.0, + cmax=(abs(realized_pnl_per_hour["realized_pnl"]).max()), + colorbar=dict( + title='Realized PnL', + x=0, + y=-0.5, + xanchor='left', + yanchor='bottom', + orientation='h' + ) + ))) + fig.update_layout( + polar=dict( + radialaxis=dict( + visible=True, + showline=False, + ), + angularaxis=dict( + rotation=90, + direction="clockwise", + tickvals=[hr2angle(hr) for hr in range(24)], + ticktext=[hr_str(hr) for hr in range(24)], + ), + bgcolor='rgba(255, 255, 255, 0)', + + ), + legend=dict( + orientation="h", + x=0.5, + y=1.08, + xanchor="center", + yanchor="bottom" + ), + title=dict( + text='Intraday Performance', + x=0.5, + y=0.93, + xanchor="center", + yanchor="bottom" + ), + ) + return fig + + def returns_histogram(self): + df = self.strategy_data.trade_fill.copy() + fig = go.Figure() + fig.add_trace(go.Histogram(name="Losses", + x=df.loc[df["realized_pnl"] < 0, "realized_pnl"], + marker_color=BEARISH_COLOR)) + fig.add_trace(go.Histogram(name="Profits", + x=df.loc[df["realized_pnl"] > 0, "realized_pnl"], + marker_color=BULLISH_COLOR)) + fig.update_layout( + title=dict( + text='Returns Distribution', + x=0.5, + xanchor="center", + ), + legend=dict( + orientation="h", + yanchor="bottom", + y=1.02, + xanchor="center", + x=.48 + )) + return fig + + def position_executor_summary_sunburst(self): + if self.strategy_data.position_executor is not None: + df = self.strategy_data.position_executor.copy() + grouped_df = df.groupby(["trading_pair", "side", "close_type"]).size().reset_index(name="count") + + fig = px.sunburst(grouped_df, + path=['trading_pair', 'side', 'close_type'], + values="count", + color_continuous_scale='RdBu', + color_continuous_midpoint=0) + + fig.update_layout( + title=dict( + text='Position Executor Summary', + x=0.5, + xanchor="center", + ), + legend=dict( + orientation="h", + yanchor="bottom", + y=1.02, + xanchor="center", + x=.48 + ) + ) + return fig + else: + return None + + def candles_graph(self, candles: pd.DataFrame, interval="5m", show_volume=False, extra_rows=2): + line_mode = interval == MIN_INTERVAL_RESOLUTION + cg = CandlesGraph(candles, show_volume=show_volume, line_mode=line_mode, extra_rows=extra_rows) + cg.add_buy_trades(self.strategy_data.buys) + cg.add_sell_trades(self.strategy_data.sells) + cg.add_pnl(self.strategy_data, row=2) + cg.add_quote_inventory_change(self.strategy_data, row=3) + if self.strategy_data.position_executor is not None: + cg.add_positions(self.strategy_data.position_executor, row=1) + return cg.figure() diff --git a/frontend/data_viz/strategy_analysis.py b/frontend/data_viz/strategy_analysis.py new file mode 100644 index 0000000..bd4ca0b --- /dev/null +++ b/frontend/data_viz/strategy_analysis.py @@ -0,0 +1,234 @@ +from typing import Optional + +import pandas as pd +from plotly.subplots import make_subplots +import pandas_ta as ta # noqa: F401 +import plotly.graph_objs as go +import numpy as np + + +class StrategyAnalysis: + def __init__(self, positions: pd.DataFrame, candles_df: Optional[pd.DataFrame] = None): + self.candles_df = candles_df + self.positions = positions + self.candles_df["timestamp"] = pd.to_datetime(self.candles_df["timestamp"], unit="ms") + self.positions["timestamp"] = pd.to_datetime(self.positions["timestamp"], unit="ms") + self.positions["close_time"] = pd.to_datetime(self.positions["close_time"], unit="ms") + self.base_figure = None + + def create_base_figure(self, candlestick=True, volume=True, positions=False, trade_pnl=False, extra_rows=0): + rows, heights = self.get_n_rows_and_heights(extra_rows + trade_pnl, volume) + self.rows = rows + specs = [[{"secondary_y": True}]] * rows + 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(row=1, col=1) + if volume: + self.add_volume() + if positions: + self.add_positions() + if trade_pnl: + self.add_trade_pnl(row=rows) + self.update_layout(volume) + + def add_positions(self): + # Add long and short positions + active_signals = self.positions.copy() + active_signals.loc[active_signals["signal"] == -1, "symbol"] = "triangle-down" + active_signals.loc[active_signals["signal"] == 1, "symbol"] = "triangle-up" + active_signals.loc[active_signals["profitable"] == 1, "color"] = "lightgreen" + active_signals.loc[active_signals["profitable"] == -1, "color"] = "red" + self.base_figure.add_trace(go.Scatter(x=active_signals.loc[(active_signals["side"] != 0), "timestamp"], + y=active_signals.loc[active_signals["side"] != 0, "close"], + name="Entry Price: $", + mode="markers", + marker_color=active_signals.loc[(active_signals["side"] != 0), "color"], + marker_symbol=active_signals.loc[(active_signals["side"] != 0), "symbol"], + marker_size=20, + marker_line={"color": "black", "width": 0.7}), + row=1, col=1) + + for index, row in active_signals.iterrows(): + self.base_figure.add_shape(type="rect", + fillcolor="green", + opacity=0.5, + x0=row.timestamp, + y0=row.close, + x1=row.close_time, + y1=row.take_profit_price, + line=dict(color="green"), + row=1, col=1) + # Add SL + self.base_figure.add_shape(type="rect", + fillcolor="red", + opacity=0.5, + x0=row.timestamp, + y0=row.close, + x1=row.close_time, + y1=row.stop_loss_price, + line=dict(color="red"), + row=1, col=1) + + def get_n_rows_and_heights(self, extra_rows, volume=True): + rows = 1 + extra_rows + volume + row_heights = [0.5] * (extra_rows) + if volume: + row_heights.insert(0, 0.2) + row_heights.insert(0, 0.8) + return rows, row_heights + + def figure(self): + return self.base_figure + + def add_candles_graph(self, row, col, name_suffix='', timeframe_suffix=''): + self.base_figure.add_trace( + go.Candlestick( + 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=row, col=col, + ) + + def add_volume(self): + self.base_figure.add_trace( + go.Bar( + x=self.candles_df["timestamp"], + y=self.candles_df["volume"], + name="Volume", + opacity=0.5, + marker=dict(color="lightgreen") + ), + row=2, col=1, + ) + + def add_trade_pnl(self, row=2): + self.base_figure.add_trace( + go.Scatter( + x=self.positions["timestamp"], + y=self.positions["net_pnl_quote"].cumsum(), + name="Cumulative Trade PnL", + mode="lines", + line=dict(color="chocolate", width=2)), + row=row, col=1 + ) + self.base_figure.update_yaxes(title_text="Cum Trade PnL", row=row, col=1) + + def update_layout(self, volume=True): + self.base_figure.update_layout( + title={ + "text": "Backtesting Analysis", + "y": 0.95, + "x": 0.5, + "xanchor": "center", + "yanchor": "top" + }, + legend=dict( + orientation="h", + yanchor="bottom", + y=-0.2, + xanchor="right", + x=1 + ), + height=1500, + xaxis_rangeslider_visible=False, + hovermode="x unified" + ) + self.base_figure.update_yaxes(title_text="Price", row=1, col=1) + if volume: + self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) + self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) + + def initial_portfolio(self): + return self.positions["inventory"].dropna().values[0] + + def final_portfolio(self): + return self.positions["inventory"].dropna().values[-1] + + def net_profit_usd(self): + return self.final_portfolio() - self.initial_portfolio() + + def net_profit_pct(self): + return self.net_profit_usd() / self.initial_portfolio() + + def returns(self): + return self.positions["net_pnl_quote"] / self.initial_portfolio() + + def total_positions(self): + return self.positions.shape[0] - 1 + + def win_signals(self): + return self.positions.loc[(self.positions["profitable"] > 0) & (self.positions["side"] != 0)] + + def loss_signals(self): + return self.positions.loc[(self.positions["profitable"] < 0) & (self.positions["side"] != 0)] + + def accuracy(self): + return self.win_signals().shape[0] / self.total_positions() + + def max_drawdown_usd(self): + cumulative_returns = self.positions["net_pnl_quote"].cumsum() + peak = np.maximum.accumulate(cumulative_returns) + drawdown = (cumulative_returns - peak) + max_draw_down = np.min(drawdown) + return max_draw_down + + def max_drawdown_pct(self): + return self.max_drawdown_usd() / self.initial_portfolio() + + def sharpe_ratio(self): + returns = self.returns() + return returns.mean() / returns.std() + + def profit_factor(self): + 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 + + def duration_in_minutes(self): + return (self.positions["timestamp"].iloc[-1] - self.positions["timestamp"].iloc[0]).total_seconds() / 60 + + def avg_trading_time_in_minutes(self): + time_diff_minutes = (self.positions["close_time"] - self.positions["timestamp"]).dt.total_seconds() / 60 + return time_diff_minutes.mean() + + def start_date(self): + return pd.to_datetime(self.candles_df.timestamp.min(), unit="ms") + + def end_date(self): + return pd.to_datetime(self.candles_df.timestamp.max(), unit="ms") + + def avg_profit(self): + return self.positions.net_pnl_quote.mean() + + def text_report(self): + return f""" +Strategy Performance Report: + - Net Profit: {self.net_profit_usd():,.2f} USD ({self.net_profit_pct() * 100:,.2f}%) + - Total Positions: {self.total_positions()} + - Win Signals: {self.win_signals().shape[0]} + - Loss Signals: {self.loss_signals().shape[0]} + - Accuracy: {self.accuracy():,.2f}% + - Profit Factor: {self.profit_factor():,.2f} + - Max Drawdown: {self.max_drawdown_usd():,.2f} USD | {self.max_drawdown_pct() * 100:,.2f}% + - Sharpe Ratio: {self.sharpe_ratio():,.2f} + - Duration: {self.duration_in_minutes() / 60:,.2f} Hours + - Average Trade Duration: {self.avg_trading_time_in_minutes():,.2f} minutes + """ + + def pnl_over_time(self): + fig = go.Figure() + fig.add_trace(go.Scatter(name="PnL Over Time", + x=self.positions.index, + y=self.positions.net_pnl_quote.cumsum())) + # Update layout with the required attributes + fig.update_layout( + title="PnL Over Time", + xaxis_title="N° Position", + yaxis=dict(title="Net PnL USD", side="left", showgrid=False), + ) + return fig diff --git a/frontend/data_viz/theme.py b/frontend/data_viz/theme.py new file mode 100644 index 0000000..6080002 --- /dev/null +++ b/frontend/data_viz/theme.py @@ -0,0 +1,31 @@ +def get_default_layout(title, height=600, width=800): + return { + "template": "plotly_dark", + "title": title, + "plot_bgcolor": 'rgba(0, 0, 0, 0)', # Transparent background + "paper_bgcolor": 'rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + "font": {"color": 'white', "size": 12}, # Consistent font color and size + "height": height, + "width": width, + "margin": {"l": 20, "r": 20, "t": 50, "b": 20}, + "xaxis": {"title": "Spread (%)"}, + "yaxis": {"title": "Amount (Quote)"} + } + + +def get_color_scheme(): + return { + 'upper_band': '#4682B4', + 'middle_band': '#FFD700', + 'lower_band': '#32CD32', + 'buy_signal': '#1E90FF', + 'sell_signal': '#FF0000', + 'buy': '#32CD32', # Green for buy orders + 'sell': '#FF6347', # Tomato red for sell orders + 'candlestick_increasing': '#2ECC71', + 'candlestick_decreasing': '#E74C3C', + 'macd_line': '#FFA500', # Orange + 'macd_signal': '#800080', # Purple + 'macd_histogram_positive': '#32CD32', # Green + 'macd_histogram_negative': '#FF6347' # Red + } From 7a892d345a710d05c0bdfc6a4de3677ac46331b4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:21:39 -0400 Subject: [PATCH 054/139] (feat) include use inputs --- .../pages/config/pmm_simple/user_inputs.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 frontend/pages/config/pmm_simple/user_inputs.py diff --git a/frontend/pages/config/pmm_simple/user_inputs.py b/frontend/pages/config/pmm_simple/user_inputs.py new file mode 100644 index 0000000..3bc67b3 --- /dev/null +++ b/frontend/pages/config/pmm_simple/user_inputs.py @@ -0,0 +1,47 @@ +import streamlit as st +from hummingbot.connector.connector_base import OrderType + +def user_inputs(): + c1, c2, c3, c4, c5, c6, c7 = st.columns(7) + with c1: + connector = st.text_input("Connector", value="binance_perpetual") + with c2: + trading_pair = st.text_input("Trading Pair", value="WLD-USDT") + with c3: + total_amount_quote = st.number_input("Total amount of quote", value=1000) + with c4: + leverage = st.number_input("Leverage", value=20) + position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) + with c5: + executor_refresh_time = st.number_input("Refresh Time (minutes)", value=3) + cooldown_time = st.number_input("Cooldown Time (minutes)", value=3) + with c6: + 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 c7: + 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) + + buy_order_levels = st.number_input("Number of Buy Order Levels", min_value=1, value=2) + sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) + + inputs = { + "connector": connector, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "leverage": leverage, + "position_mode": position_mode, + "executor_refresh_time": executor_refresh_time, + "cooldown_time": cooldown_time, + "sl": sl, + "tp": tp, + "take_profit_order_type": take_profit_order_type, + "ts_ap": ts_ap, + "ts_delta": ts_delta, + "time_limit": time_limit, + "buy_order_levels": buy_order_levels, + "sell_order_levels": sell_order_levels + } + return inputs From 30144e8aa289b29e34b1ebb8008477c3321f5d97 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:21:56 -0400 Subject: [PATCH 055/139] (feat) add utils and labeling module to quants lab --- quants_lab/utils/__init__.py | 0 quants_lab/utils/labeling.py | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 quants_lab/utils/__init__.py create mode 100644 quants_lab/utils/labeling.py diff --git a/quants_lab/utils/__init__.py b/quants_lab/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/quants_lab/utils/labeling.py b/quants_lab/utils/labeling.py new file mode 100644 index 0000000..0650d56 --- /dev/null +++ b/quants_lab/utils/labeling.py @@ -0,0 +1,70 @@ +from typing import Optional + +import numpy as np +import pandas as pd + + +def triple_barrier_method(df, tp=1.0, sl=1.0, tl=5, std_span: Optional[int] = 100, trade_cost=0.0006, max_executors: int = 1): + df.index = pd.to_datetime(df.timestamp, unit="ms") + if std_span: + df["target"] = df["close"].rolling(std_span).std() / df["close"] + else: + df["target"] = 1 / 100 + df["tl"] = df.index + pd.Timedelta(seconds=tl) + df.dropna(subset="target", inplace=True) + + df = apply_tp_sl_on_tl(df, tp=tp, sl=sl) + + df = get_bins(df, trade_cost) + + df['tp'] = df['close'] * (1 + df['target'] * tp * df["side"]) + df['sl'] = df['close'] * (1 - df['target'] * sl * df["side"]) + + df = add_active_signals(df, max_executors) + return df + + +def add_active_signals(df, max_executors): + close_times = [pd.Timestamp.min] * max_executors + df["active_signal"] = 0 + for index, row in df[(df["side"] != 0)].iterrows(): + for close_time in close_times: + if row["timestamp"] > close_time: + df.loc[df.index == index, "active_signal"] = 1 + close_times.remove(close_time) + close_times.append(row["close_time"]) + break + return df + + +def get_bins(df, trade_cost): + # 1) prices aligned with events + px = df.index.union(df['tl'].values).drop_duplicates() + px = df.close.reindex(px, method='ffill') + + # 2) create out object + df['ret'] = (px.loc[df['close_time'].values].values / px.loc[df.index] - 1) * df['side'] + df['real_class'] = np.sign(df['ret'] - trade_cost) + return df + + +def apply_tp_sl_on_tl(df: pd.DataFrame, tp: float, sl: float): + events = df[df["side"] != 0].copy() + if tp > 0: + take_profit = tp * events['target'] + else: + take_profit = pd.Series(index=df.index) # NaNs + if sl > 0: + stop_loss = - sl * events['target'] + else: + stop_loss = pd.Series(index=df.index) # NaNs + + for loc, tl in events['tl'].fillna(df.index[-1]).items(): + df0 = df.close[loc:tl] # path prices + df0 = (df0 / df.close[loc] - 1) * events.at[loc, 'side'] # path returns + df.loc[loc, 'stop_loss_time'] = df0[df0 < stop_loss[loc]].index.min() # earliest stop loss. + df.loc[loc, 'take_profit_time'] = df0[df0 > take_profit[loc]].index.min() # earliest profit taking. + df["close_time"] = df[["tl", "take_profit_time", "stop_loss_time"]].dropna(how='all').min(axis=1) + df['close_type'] = df[['take_profit_time', 'stop_loss_time', 'tl']].dropna(how='all').idxmin(axis=1) + df['close_type'].replace({'take_profit_time': 'tp', 'stop_loss_time': 'sl'}, inplace=True) + return df From 3509488b858cd6b555210f4529187d2ba7eea4f3 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:22:12 -0400 Subject: [PATCH 056/139] (feat) add backend api, coingecko and miner clients --- backend/services/__init__.py | 0 backend/services/backend_api_client.py | 199 +++++++++++++++++++++++++ backend/services/coingecko_client.py | 54 +++++++ backend/services/miner_client.py | 62 ++++++++ 4 files changed, 315 insertions(+) create mode 100644 backend/services/__init__.py create mode 100644 backend/services/backend_api_client.py create mode 100644 backend/services/coingecko_client.py create mode 100644 backend/services/miner_client.py diff --git a/backend/services/__init__.py b/backend/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py new file mode 100644 index 0000000..ad93cdc --- /dev/null +++ b/backend/services/backend_api_client.py @@ -0,0 +1,199 @@ +import requests + + +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) + return response.json() + + 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 response.json() # Handle errors or no data 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_credentials(self): + """Get available credentials.""" + url = f"{self.base_url}/list-credentials" + 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) + return response.json() diff --git a/backend/services/coingecko_client.py b/backend/services/coingecko_client.py new file mode 100644 index 0000000..d870add --- /dev/null +++ b/backend/services/coingecko_client.py @@ -0,0 +1,54 @@ +import time + +from pycoingecko import CoinGeckoAPI +import pandas as pd +import re + + +class CoinGeckoClient: + def __init__(self): + self.connector = CoinGeckoAPI() + + def get_all_coins_df(self): + coin_list = self.connector.get_coins_list() + return pd.DataFrame(coin_list) + + def get_all_coins_markets_df(self): + coin_list = self.connector.get_coins_markets(vs_currency="USD") + return pd.DataFrame(coin_list) + + def get_coin_tickers_by_id(self, coin_id: str): + coin_tickers = self.connector.get_coin_ticker_by_id(id=coin_id) + coin_tickers_df = pd.DataFrame(coin_tickers["tickers"]) + coin_tickers_df["token_id"] = coin_id + return coin_tickers_df + + def get_coin_tickers_by_id_list(self, coins_id: list): + dfs = [] + for coin_id in coins_id: + df = self.get_coin_tickers_by_id(coin_id) + dfs.append(df) + time.sleep(1) + + coin_tickers_df = pd.concat(dfs) + coin_tickers_df["exchange"] = coin_tickers_df["market"].apply( + lambda x: re.sub("Exchange", "", x["name"])) + coin_tickers_df.drop(columns="market", inplace=True) + coin_tickers_df["trading_pair"] = coin_tickers_df.base + "-" + coin_tickers_df.target + return coin_tickers_df + + def get_all_exchanges_df(self): + exchanges_list = self.connector.get_exchanges_list() + return pd.DataFrame(exchanges_list) + + def get_exchanges_markets_info_by_id_list(self, exchanges_id: list): + dfs = [] + for exchange_id in exchanges_id: + df = pd.DataFrame(self.connector.get_exchanges_by_id(exchange_id)["tickers"]) + dfs.append(df) + exchanges_spreads_df = pd.concat(dfs) + exchanges_spreads_df["exchange"] = exchanges_spreads_df["market"].apply( + lambda x: re.sub("Exchange", "", x["name"])) + exchanges_spreads_df.drop(columns="market", inplace=True) + exchanges_spreads_df["trading_pair"] = exchanges_spreads_df.base + "-" + exchanges_spreads_df.target + return exchanges_spreads_df diff --git a/backend/services/miner_client.py b/backend/services/miner_client.py new file mode 100644 index 0000000..2332d8c --- /dev/null +++ b/backend/services/miner_client.py @@ -0,0 +1,62 @@ +import pandas as pd +import requests +from glom import * + + +class MinerClient: + MARKETS_ENDPOINT = "https://api.hummingbot.io/bounty/markets" + + @staticmethod + def reward_splitter(base, reward_dict): + tmp = {"rewards_HBOT": 0, "rewards_STABLE": 0, "rewards_base": 0, } + if "HBOT" in reward_dict: + tmp["rewards_HBOT"] += reward_dict["HBOT"] + if "USDC" in reward_dict: + tmp["rewards_STABLE"] += reward_dict["USDC"] + if "USDT" in reward_dict: + tmp["rewards_STABLE"] += reward_dict["USDT"] + if base in reward_dict: + tmp["rewards_base"] += reward_dict[base] + + return pd.Series(tmp, dtype=float) + + @staticmethod + def exchange_coingecko_id(exchange: str): + converter = { + "kucoin": "kucoin", + "binance": "binance", + "gateio": "gate", + "ascendex": "bitmax" + } + return converter.get(exchange, None) + + def get_miner_stats_df(self): + miner_data = requests.get(self.MARKETS_ENDPOINT).json() + spec = { + 'market_id': ('markets', ['market_id']), + 'trading_pair': ('markets', ['trading_pair']), + 'exchange': ('markets', ['exchange_name']), + 'base': ('markets', ['base_asset']), + 'quote': ('markets', ['quote_asset']), + 'start_timestamp': ('markets', [("active_bounty_periods", ['start_timestamp'])]), + 'end_timestamp': ('markets', [("active_bounty_periods", ['end_timestamp'])]), + 'budget': ('markets', [("active_bounty_periods", ['budget'])]), + 'spread_max': ('markets', [("active_bounty_periods", ['spread_max'])]), + 'payout_asset': ('markets', [("active_bounty_periods", ['payout_asset'])]), + 'return': ('markets', ['return']), + 'last_snapshot_ts': ('markets', ['last_snapshot_ts']), + 'hourly_payout_usd': ('markets', ['hourly_payout_usd']), + 'bots': ('markets', ['bots']), + 'last_hour_bots': ('markets', ['last_hour_bots']), + 'filled_24h_volume': ('markets', ['filled_24h_volume']), + # 'weekly_reward_in_usd': ('markets', ['weekly_reward_in_usd']), + # 'weekly_reward': ('markets', ['weekly_reward']), + 'market_24h_usd_volume': ('markets', ['market_24h_usd_volume']) + } + + r = glom(miner_data, spec) + df = pd.DataFrame(r) + # df = pd.concat([df, df.apply(lambda x: self.reward_splitter(x.base, x.weekly_reward), axis=1)], axis=1) + df["trading_pair"] = df.apply(lambda x: x.base + "-" + x.quote, axis=1) + df["exchange_coingecko_id"] = df.apply(lambda x: self.exchange_coingecko_id(x.exchange), axis=1) + return df From 2902b4263e73d083a9f3efd1adf390082e9d894f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:22:25 -0400 Subject: [PATCH 057/139] (feat) move utils to backend --- backend/utils/__init__.py | 0 backend/utils/file_templates.py | 169 +++++++++++++ backend/utils/optuna_database_manager.py | 297 +++++++++++++++++++++++ backend/utils/os_utils.py | 162 +++++++++++++ 4 files changed, 628 insertions(+) create mode 100644 backend/utils/__init__.py create mode 100644 backend/utils/file_templates.py create mode 100644 backend/utils/optuna_database_manager.py create mode 100644 backend/utils/os_utils.py diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/utils/file_templates.py b/backend/utils/file_templates.py new file mode 100644 index 0000000..6a570b9 --- /dev/null +++ b/backend/utils/file_templates.py @@ -0,0 +1,169 @@ +from typing import Dict + + +def directional_trading_controller_template(strategy_cls_name: str) -> str: + strategy_config_cls_name = f"{strategy_cls_name}Config" + sma_config_text = "{self.config.sma_length}" + return f"""import time +from typing import Optional + +import pandas as pd +from pydantic import Field + +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, +) + +class {strategy_config_cls_name}(DirectionalTradingControllerConfigBase): + strategy_name: str = "{strategy_cls_name.lower()}" + sma_length: int = Field(default=20, ge=10, le=200) + # ... Add more fields here + + +class {strategy_cls_name}(DirectionalTradingControllerBase): + + def __init__(self, config: {strategy_config_cls_name}): + 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 + + def get_processed_data(self) -> pd.DataFrame: + df = self.candles[0].candles_df + df.ta.sma(length=self.config.sma_length, append=True) + # ... Add more indicators here + # ... Check https://github.com/twopirllc/pandas-ta#indicators-by-category for more indicators + # ... Use help(ta.indicator_name) to get more info + + # Generate long and short conditions + long_cond = (df['close'] > df[f'SMA_{sma_config_text}']) + short_cond = (df['close'] < df[f'SMA_{sma_config_text}']) + + # Choose side + df['signal'] = 0 + df.loc[long_cond, 'signal'] = 1 + df.loc[short_cond, 'signal'] = -1 + return df +""" + + +def get_optuna_suggest_str(field_name: str, properties: Dict): + if field_name == "candles_config": + return f"""{field_name}=[ + CandlesConfig(connector=exchange, trading_pair=trading_pair, + interval="1h", max_records=1000000) + ]""" + if field_name == "strategy_name": + return f"{field_name}='{properties.get('default', '_')}'" + if field_name in ["order_levels", "trading_pair", "exchange"]: + return f"{field_name}={field_name}" + if field_name == "position_mode": + return f"{field_name}=PositionMode.HEDGE" + if field_name == "leverage": + return f"{field_name}=10" + + if properties["type"] == "number": + optuna_trial_str = f"trial.suggest_float('{field_name}', {properties.get('minimum', '_')}, {properties.get('maximum', '_')}, step=0.01)" + elif properties["type"] == "integer": + optuna_trial_str = f"trial.suggest_int('{field_name}', {properties.get('minimum', '_')}, {properties.get('maximum', '_')})" + elif properties["type"] == "string": + optuna_trial_str = f"trial.suggest_categorical('{field_name}', ['{properties.get('default', '_')}',])" + else: + raise Exception(f"Unknown type {properties['type']} for field {field_name}") + return f"{field_name}={optuna_trial_str}" + + +def strategy_optimization_template(strategy_info: dict): + strategy_cls = strategy_info["class"] + strategy_config = strategy_info["config"] + strategy_module = strategy_info["module"] + field_schema = strategy_config.schema()["properties"] + fields_str = [get_optuna_suggest_str(field_name, properties) for field_name, properties in field_schema.items()] + fields_str = "".join([f" {field_str},\n" for field_str in fields_str]) + return f"""import traceback +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.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__} + + +def objective(trial): + try: + # General configuration for the backtesting + exchange = "binance_perpetual" + trading_pair = "BTC-USDT" + start = "2023-01-01" + end = "2024-01-01" + initial_portfolio_usd = 1000.0 + trade_cost = 0.0006 + + # The definition of order levels is not so necessary for directional strategies now but let's you customize the + # amounts for going long or short, the cooldown time between orders and the triple barrier configuration + stop_loss = trial.suggest_float('stop_loss', 0.01, 0.02, step=0.01) + take_profit = trial.suggest_float('take_profit', 0.01, 0.05, step=0.01) + time_limit = trial.suggest_int('time_limit', 60 * 60 * 12, 60 * 60 * 24) + + triple_barrier_conf = TripleBarrierConf( + stop_loss=Decimal(stop_loss), take_profit=Decimal(take_profit), + time_limit=time_limit, + trailing_stop_activation_price_delta=Decimal("0.008"), # It's not working yet with the backtesting engine + trailing_stop_trailing_delta=Decimal("0.004"), + ) + + order_levels = [ + OrderLevel(level=0, side=TradeType.BUY, order_amount_usd=Decimal(50), + cooldown_time=15, triple_barrier_conf=triple_barrier_conf), + OrderLevel(level=0, side=TradeType.SELL, order_amount_usd=Decimal(50), + cooldown_time=15, triple_barrier_conf=triple_barrier_conf), + ] + config = {strategy_config.__name__}( +{fields_str} + ) + controller = {strategy_cls.__name__}(config=config) + engine = DirectionalTradingBacktestingEngine(controller=controller) + engine.load_controller_data("./data/candles") + backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd, trade_cost=trade_cost, + start=start, end=end) + + strategy_analysis = backtesting_results["results"] + encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode) + + trial.set_user_attr("net_pnl_quote", strategy_analysis["net_pnl_quote"]) + trial.set_user_attr("net_pnl_pct", strategy_analysis["net_pnl"]) + trial.set_user_attr("max_drawdown_usd", strategy_analysis["max_drawdown_usd"]) + trial.set_user_attr("max_drawdown_pct", strategy_analysis["max_drawdown_pct"]) + trial.set_user_attr("sharpe_ratio", strategy_analysis["sharpe_ratio"]) + trial.set_user_attr("accuracy", strategy_analysis["accuracy"]) + trial.set_user_attr("total_positions", strategy_analysis["total_positions"]) + trial.set_user_attr("profit_factor", strategy_analysis["profit_factor"]) + trial.set_user_attr("duration_in_hours", strategy_analysis["duration_minutes"] / 60) + trial.set_user_attr("avg_trading_time_in_hours", strategy_analysis["avg_trading_time_minutes"] / 60) + trial.set_user_attr("win_signals", strategy_analysis["win_signals"]) + trial.set_user_attr("loss_signals", strategy_analysis["loss_signals"]) + trial.set_user_attr("config", encoder_decoder.encode(config.dict())) + return strategy_analysis["net_pnl"] + except Exception as e: + traceback.print_exc() + raise TrialPruned() + """ diff --git a/backend/utils/optuna_database_manager.py b/backend/utils/optuna_database_manager.py new file mode 100644 index 0000000..9293c84 --- /dev/null +++ b/backend/utils/optuna_database_manager.py @@ -0,0 +1,297 @@ +import os +import json +from typing import Optional + +import pandas as pd +from sqlalchemy import create_engine, text +from sqlalchemy.orm import sessionmaker + + +class OptunaDBManager: + def __init__(self, db_name, db_root_path: Optional[str]): + db_root_path = db_root_path or "data/backtesting" + self.db_name = db_name + self.db_path = f'sqlite:///{os.path.join(db_root_path, db_name)}' + self.engine = create_engine(self.db_path, connect_args={'check_same_thread': False}) + self.session_maker = sessionmaker(bind=self.engine) + + @property + def status(self): + try: + with self.session_maker() as session: + query = 'SELECT * FROM trials WHERE state = "COMPLETE"' + completed_trials = pd.read_sql_query(text(query), session.connection()) + if len(completed_trials) > 0: + # TODO: improve error handling, think what to do with other cases + return "OK" + else: + return "No records found in the trials table with completed state" + except Exception as e: + return f"Error: {str(e)}" + + @property + def tables(self): + return self._get_tables() + + def _get_tables(self): + try: + with self.session_maker() as session: + query = "SELECT name FROM sqlite_master WHERE type='table';" + tables = pd.read_sql_query(text(query), session.connection()) + return tables["name"].tolist() + except Exception as e: + return f"Error: {str(e)}" + + @property + def trials(self): + return self._get_trials_table() + + def _get_trials_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trials"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def studies(self): + return self._get_studies_table() + + def _get_studies_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM studies"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_params(self): + return self._get_trial_params_table() + + def _get_trial_params_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_params"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_values(self): + return self._get_trial_values_table() + + def _get_trial_values_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_values"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_system_attributes(self): + return self._get_trial_system_attributes_table() + + def _get_trial_system_attributes_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_system_attributes"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_system_attributes(self): + return self._get_trial_system_attributes_table() + + def _get_trial_system_attributes_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_system_attributes"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def version_info(self): + return self._get_version_info_table() + + def _get_version_info_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM version_info"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def study_directions(self): + return self._get_study_directions_table() + + def _get_study_directions_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM study_directions"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def study_user_attributes(self): + return self._get_study_user_attributes_table() + + def _get_study_user_attributes_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM study_user_attributes"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def study_system_attributes(self): + return self._get_study_system_attributes_table() + + def _get_study_system_attributes_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM study_system_attributes"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_user_attributes(self): + return self._get_trial_user_attributes_table() + + def _get_trial_user_attributes_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_user_attributes"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_intermediate_values(self): + return self._get_trial_intermediate_values_table() + + def _get_trial_intermediate_values_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_intermediate_values"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def trial_heartbeats(self): + return self._get_trial_heartbeats_table() + + def _get_trial_heartbeats_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM trial_heartbeats"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def alembic_version(self): + return self._get_alembic_version_table() + + def _get_alembic_version_table(self): + try: + with self.session_maker() as session: + df = pd.read_sql_query(text("SELECT * FROM alembic_version"), session.connection()) + return df + except Exception as e: + return f"Error: {str(e)}" + + @property + def merged_df(self): + return self._get_merged_df() + + @staticmethod + def _add_hovertext(x): + summary_label = (f"Trial ID: {x['trial_id']}
" + f"Study: {x['study_name']}
" + f"--------------------
" + f"Accuracy: {100 * x['accuracy']:.2f} %
" + f"Avg Trading Time in Hours: {x['avg_trading_time_in_hours']:.2f}
" + f"Duration in Hours: {x['duration_in_hours']:.2f}
" + f"Loss Signals: {x['loss_signals']}
" + f"Max Drawdown [%]: {100 * x['max_drawdown_pct']:.2f} %
" + f"Max Drawdown [USD]: $ {x['max_drawdown_usd']:.2f}
" + f"Net Profit [%]: {100 * x['net_pnl_pct']:.2f} %
" + f"Net Profit [$]: $ {x['net_pnl_quote']:.2f}
" + f"Profit Factor: {x['profit_factor']:.2f}
" + f"Sharpe Ratio: {x['sharpe_ratio']:.4f}
" + f"Total Positions: {x['total_positions']}
" + f"Win Signals: {x['win_signals']}
" + f"Trial value: {x['value']}
" + f"Direction: {x['direction']}
" + ) + return summary_label + + def _get_merged_df(self): + float_cols = ["accuracy", "avg_trading_time_in_hours", "duration_in_hours", "max_drawdown_pct", "max_drawdown_usd", + "net_pnl_pct", "net_pnl_quote", "profit_factor", "sharpe_ratio", "value"] + int_cols = ["loss_signals", "total_positions", "win_signals"] + merged_df = self.trials\ + .merge(self.studies, on="study_id")\ + .merge(pd.pivot(self.trial_user_attributes, index="trial_id", columns="key", values="value_json"), + on="trial_id")\ + .merge(self.trial_values, on="trial_id")\ + .merge(self.study_directions, on="study_id") + merged_df[float_cols] = merged_df[float_cols].astype("float") + merged_df[int_cols] = merged_df[int_cols].astype("int64") + merged_df["hover_text"] = merged_df.apply(self._add_hovertext, axis=1) + return merged_df + + def load_studies(self): + df = self.merged_df + study_name_col = 'study_name' + trial_id_col = 'trial_id' + nested_dict = {} + for _, row in df.iterrows(): + study_name = row[study_name_col] + trial_id = row[trial_id_col] + data_dict = row.drop([study_name_col, trial_id_col]).to_dict() + if study_name not in nested_dict: + nested_dict[study_name] = {} + nested_dict[study_name][trial_id] = data_dict + return nested_dict + + def load_params(self): + trial_id_col = 'trial_id' + param_name_col = 'param_name' + param_value_col = 'param_value' + distribution_json_col = 'distribution_json' + nested_dict = {} + for _, row in self.trial_params.iterrows(): + trial_id = row[trial_id_col] + param_name = row[param_name_col] + param_value = row[param_value_col] + distribution_json = row[distribution_json_col] + + if trial_id not in nested_dict: + nested_dict[trial_id] = {} + + dist_json = json.loads(distribution_json) + default_step = None + default_low = None + default_high = None + default_log = None + + nested_dict[trial_id][param_name] = { + 'param_name': param_name, + 'param_value': param_value, + 'step': dist_json["attributes"].get("step", default_step), + 'low': dist_json["attributes"].get("low", default_low), + 'high': dist_json["attributes"].get("high", default_high), + 'log': dist_json["attributes"].get("log", default_log), + } + return nested_dict diff --git a/backend/utils/os_utils.py b/backend/utils/os_utils.py new file mode 100644 index 0000000..ce07dc9 --- /dev/null +++ b/backend/utils/os_utils.py @@ -0,0 +1,162 @@ +import glob +import subprocess +import importlib.util +import inspect +import os + +import pandas as pd + +import yaml +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): + for file in os.listdir(directory): + os.remove(f"{directory}/{file}") + + +def remove_file(file_path: str): + os.remove(file_path) + + +def remove_directory(directory: str): + process = subprocess.Popen(f"rm -rf {directory}", shell=True) + process.wait() + + +def dump_dict_to_yaml(data_dict, filename): + with open(filename, 'w') as file: + yaml.dump(data_dict, file) + + +def read_yaml_file(file_path): + with open(file_path, 'r') as file: + data = yaml.safe_load(file) + return data + + +def directory_exists(directory: str): + return os.path.exists(directory) + + +def save_file(name: str, content: str, path: str): + complete_file_path = os.path.join(path, name) + os.makedirs(path, exist_ok=True) + with open(complete_file_path, "w") as file: + file.write(content) + + +def load_file(path: str) -> str: + try: + with open(path, 'r') as file: + contents = file.read() + return contents + except FileNotFoundError: + print(f"File '{path}' not found.") + return "" + except IOError: + print(f"Error reading file '{path}'.") + return "" + + +def get_directories_from_directory(directory: str) -> list: + directories = glob.glob(directory + "/**/") + return directories + + +def get_python_files_from_directory(directory: str) -> list: + py_files = glob.glob(directory + "/**/*.py", recursive=True) + py_files = [path for path in py_files if not path.endswith("__init__.py")] + return py_files + + +def get_log_files_from_directory(directory: str) -> list: + log_files = glob.glob(directory + "/**/*.log*", recursive=True) + return log_files + + +def get_yml_files_from_directory(directory: str) -> list: + yml = glob.glob(directory + "/**/*.yml", recursive=True) + return yml + + +def load_controllers(path): + controllers = {} + for filename in os.listdir(path): + if filename.endswith('.py') and "__init__" not in filename: + module_name = filename[:-3] # strip the .py to get the module name + controllers[module_name] = {"module": module_name} + file_path = os.path.join(path, filename) + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + for name, cls in inspect.getmembers(module, inspect.isclass): + if issubclass(cls, DirectionalTradingControllerBase) and cls is not DirectionalTradingControllerBase: + controllers[module_name]["class"] = cls + controllers[module_name]["type"] = "directional_trading" + if issubclass(cls, DirectionalTradingControllerConfigBase) and cls is not DirectionalTradingControllerConfigBase: + controllers[module_name]["config"] = cls + if issubclass(cls, MarketMakingControllerBase) and cls is not MarketMakingControllerBase: + controllers[module_name]["class"] = cls + controllers[module_name]["type"] = "market_making" + if issubclass(cls, MarketMakingControllerConfigBase) and cls is not MarketMakingControllerConfigBase: + controllers[module_name]["config"] = cls + return controllers + + +def get_bots_data_paths(): + root_directory = "hummingbot_files/bots" + bots_data_paths = {"General / Uploaded data": "data"} + reserved_word = "hummingbot-" + # Walk through the directory tree + for dirpath, dirnames, filenames in os.walk(root_directory): + for dirname in dirnames: + if dirname == "data": + parent_folder = os.path.basename(dirpath) + if parent_folder.startswith(reserved_word): + bots_data_paths[parent_folder] = os.path.join(dirpath, dirname) + if "dashboard" in bots_data_paths: + del bots_data_paths["dashboard"] + data_sources = {key: value for key, value in bots_data_paths.items() if value is not None} + return data_sources + + +def get_databases(): + databases = {} + bots_data_paths = get_bots_data_paths() + for source_name, source_path in bots_data_paths.items(): + sqlite_files = {} + for db_name in os.listdir(source_path): + if db_name.endswith(".sqlite"): + sqlite_files[db_name] = os.path.join(source_path, db_name) + databases[source_name] = sqlite_files + if len(databases) > 0: + return {key: value for key, value in databases.items() if value} + else: + return None + + +def get_function_from_file(file_path: str, function_name: str): + # Create a module specification from the file path and load it + spec = importlib.util.spec_from_file_location("module.name", file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Get the function from the module + function = getattr(module, function_name) + return function + + +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 From b3ea14d883bbbef0f99ea4de7eb43e2706390ed4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:22:47 -0400 Subject: [PATCH 058/139] (feat) refactor inital state --- frontend/pages/backtesting/analyze/analyze.py | 2 +- frontend/pages/backtesting/create/create.py | 2 +- frontend/pages/backtesting/optimize/optimize.py | 2 +- frontend/pages/config/dman_maker_v2/app.py | 2 +- frontend/pages/config/pmm_simple/app.py | 2 +- frontend/pages/config/position_builder/app.py | 2 +- frontend/pages/config/xemm_controller/app.py | 2 +- frontend/pages/data/download_candles/app.py | 2 +- frontend/pages/orchestration/file_manager/app.py | 2 +- frontend/pages/orchestration/instances/app.py | 2 +- frontend/pages/orchestration/launch_bot_v2/app.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/pages/backtesting/analyze/analyze.py b/frontend/pages/backtesting/analyze/analyze.py index 3c755a3..0975039 100644 --- a/frontend/pages/backtesting/analyze/analyze.py +++ b/frontend/pages/backtesting/analyze/analyze.py @@ -16,7 +16,7 @@ from frontend.data_viz.graphs import BacktestingGraphs from frontend.data_viz.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" diff --git a/frontend/pages/backtesting/create/create.py b/frontend/pages/backtesting/create/create.py index 9f2dee9..9008693 100644 --- a/frontend/pages/backtesting/create/create.py +++ b/frontend/pages/backtesting/create/create.py @@ -9,7 +9,7 @@ from frontend.components.directional_strategy_creation_card import DirectionalSt from frontend.components.editor import Editor from frontend.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, diff --git a/frontend/pages/backtesting/optimize/optimize.py b/frontend/pages/backtesting/optimize/optimize.py index 520e392..498a9d4 100644 --- a/frontend/pages/backtesting/optimize/optimize.py +++ b/frontend/pages/backtesting/optimize/optimize.py @@ -13,7 +13,7 @@ from frontend.components.optimizations_file_explorer import OptimizationsStrateg from backend.utils import os_utils from frontend.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") diff --git a/frontend/pages/config/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py index d171170..730e49a 100644 --- a/frontend/pages/config/dman_maker_v2/app.py +++ b/frontend/pages/config/dman_maker_v2/app.py @@ -10,7 +10,7 @@ from frontend.components.st_inputs import normalize, distribution_inputs, get_di from frontend.st_utils import initialize_st_page # Initialize the Streamlit page -initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️", initial_sidebar_state="collapsed") +initialize_st_page(title="D-Man Maker V2", icon="🧙‍♂️") # Page content st.text("This tool will let you create a config for D-Man Maker V2 and upload it to the BackendAPI.") diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index 1ffb2d8..d02e694 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -11,7 +11,7 @@ from frontend.components.backtesting import backtesting_section from frontend.st_utils import initialize_st_page # Initialize the Streamlit page -initialize_st_page(title="PMM Simple", icon="👨‍🏫", initial_sidebar_state="collapsed") +initialize_st_page(title="PMM Simple", icon="👨‍🏫") backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) # Page content diff --git a/frontend/pages/config/position_builder/app.py b/frontend/pages/config/position_builder/app.py index b7f3a2b..8feb50a 100644 --- a/frontend/pages/config/position_builder/app.py +++ b/frontend/pages/config/position_builder/app.py @@ -8,7 +8,7 @@ from frontend.components.st_inputs import normalize, distribution_inputs, get_di 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.") diff --git a/frontend/pages/config/xemm_controller/app.py b/frontend/pages/config/xemm_controller/app.py index 33c2a39..00bd029 100644 --- a/frontend/pages/config/xemm_controller/app.py +++ b/frontend/pages/config/xemm_controller/app.py @@ -7,7 +7,7 @@ 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="⚡️", initial_sidebar_state="collapsed") +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.") diff --git a/frontend/pages/data/download_candles/app.py b/frontend/pages/data/download_candles/app.py index 6cf6018..d2a8d19 100644 --- a/frontend/pages/data/download_candles/app.py +++ b/frontend/pages/data/download_candles/app.py @@ -7,7 +7,7 @@ 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="💾", initial_sidebar_state="collapsed") +initialize_st_page(title="Download Candles", icon="💾") backend_api_client = BackendAPIClient.get_instance() c1, c2, c3, c4 = st.columns([2, 2, 2, 0.5]) diff --git a/frontend/pages/orchestration/file_manager/app.py b/frontend/pages/orchestration/file_manager/app.py index 70b2e62..8f20839 100644 --- a/frontend/pages/orchestration/file_manager/app.py +++ b/frontend/pages/orchestration/file_manager/app.py @@ -7,7 +7,7 @@ from frontend.components.dashboard import Dashboard from frontend.components.editor import Editor from frontend.st_utils import initialize_st_page -initialize_st_page(title="Strategy Configs", icon="🗂️", initial_sidebar_state="collapsed") +initialize_st_page(title="Strategy Configs", icon="🗂️") if "fe_board" not in st.session_state: diff --git a/frontend/pages/orchestration/instances/app.py b/frontend/pages/orchestration/instances/app.py index 836d3b1..1689212 100644 --- a/frontend/pages/orchestration/instances/app.py +++ b/frontend/pages/orchestration/instances/app.py @@ -19,7 +19,7 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int return sorted(x_y, key=lambda x: (x[1], x[0])) -initialize_st_page(title="Instances", icon="🦅", initial_sidebar_state="collapsed") +initialize_st_page(title="Instances", icon="🦅") api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) diff --git a/frontend/pages/orchestration/launch_bot_v2/app.py b/frontend/pages/orchestration/launch_bot_v2/app.py index c2ea54c..a2c885e 100644 --- a/frontend/pages/orchestration/launch_bot_v2/app.py +++ b/frontend/pages/orchestration/launch_bot_v2/app.py @@ -11,7 +11,7 @@ CARD_WIDTH = 6 CARD_HEIGHT = 3 NUM_CARD_COLS = 2 -initialize_st_page(title="Launch Bot", icon="🙌", initial_sidebar_state="collapsed") +initialize_st_page(title="Launch Bot", icon="🙌") def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_HEIGHT, card_height: int = CARD_WIDTH): From cdf029f365039ac4154f862e3623229f9eb9aa5d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 13:29:13 -0400 Subject: [PATCH 059/139] (feat) default initial state of the sidebar in expanded --- frontend/st_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/st_utils.py b/frontend/st_utils.py index 9218006..82575ca 100644 --- a/frontend/st_utils.py +++ b/frontend/st_utils.py @@ -7,7 +7,8 @@ import inspect import streamlit as st from st_pages import add_page_title -def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_state="collapsed"): + +def initialize_st_page(title: str, icon: str, layout="wide", initial_sidebar_state="expanded"): st.set_page_config( page_title=title, page_icon=icon, From a94cf0b9bc3913bf37c7317c51fa8709eb308724 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:20:36 -0400 Subject: [PATCH 060/139] (feat) normalize backtesting output --- backend/services/backend_api_client.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py index ad93cdc..ebdd8b9 100644 --- a/backend/services/backend_api_client.py +++ b/backend/services/backend_api_client.py @@ -1,4 +1,6 @@ +import pandas as pd import requests +from hummingbot.strategy_v2.models.executors_info import ExecutorInfo class BackendAPIClient: @@ -196,4 +198,9 @@ class BackendAPIClient: "config": config } response = requests.post(url, json=payload) - return response.json() + backtesting_results = response.json() + return { + "processed_data": pd.DataFrame(backtesting_results["processed_data"]), + "executors": [ExecutorInfo(**executor) for executor in backtesting_results["executors"]], + "results": backtesting_results["results"] + } From 7c3fd76d9be185a990b828320df40afeb62bb31c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:20:51 -0400 Subject: [PATCH 061/139] (feat) add input components --- frontend/components/backtesting.py | 11 +++-- frontend/components/executors_distribution.py | 49 +++++++++++++++++++ .../market_making_general_inputs.py | 28 +++++++++++ frontend/components/risk_management.py | 27 ++++++++++ frontend/components/st_inputs.py | 2 +- 5 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 frontend/components/executors_distribution.py create mode 100644 frontend/components/market_making_general_inputs.py create mode 100644 frontend/components/risk_management.py diff --git a/frontend/components/backtesting.py b/frontend/components/backtesting.py index e465265..db5a6aa 100644 --- a/frontend/components/backtesting.py +++ b/frontend/components/backtesting.py @@ -1,15 +1,17 @@ import streamlit as st -from datetime import datetime +from datetime import datetime, timedelta def backtesting_section(inputs, backend_api_client): c1, c2, c3, c4, c5 = st.columns(5) + default_end_time = datetime.now().date() + default_start_time = default_end_time - timedelta(days=2) with c1: - start_date = st.date_input("Start Date", datetime(2024, 5, 1)) + start_date = st.date_input("Start Date", default_start_time) with c2: - end_date = st.date_input("End Date", datetime(2024, 5, 2)) + end_date = st.date_input("End Date", default_end_time) with c3: - backtesting_resolution = st.selectbox("Backtesting Resolution", options=["1m", "3m", "5m", "15m", "30m"], index=1) + 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: @@ -25,4 +27,5 @@ def backtesting_section(inputs, backend_api_client): trade_cost=trade_cost / 100, config=inputs, ) + return backtesting_results diff --git a/frontend/components/executors_distribution.py b/frontend/components/executors_distribution.py new file mode 100644 index 0000000..0dbebbb --- /dev/null +++ b/frontend/components/executors_distribution.py @@ -0,0 +1,49 @@ +import streamlit as st +from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs + + +def get_executors_distribution_inputs(): + 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=2, + 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=2, + help="Enter the number of sell order levels (e.g., 2).") + 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) + 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) + 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) + 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) + + # 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 diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py new file mode 100644 index 0000000..3e65942 --- /dev/null +++ b/frontend/components/market_making_general_inputs.py @@ -0,0 +1,28 @@ +import streamlit as st + + +def get_market_making_general_inputs(): + with st.expander("General Settings", expanded=True): + c1, c2, c3, c4, c5, c6, c7 = st.columns(7) + with c1: + connector_name = st.text_input("Connector", value="binance_perpetual", + help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") + with c2: + trading_pair = st.text_input("Trading Pair", value="WLD-USDT", + help="Enter the trading pair to trade on (e.g., WLD-USDT).") + with c3: + leverage = st.number_input("Leverage", value=20, + 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=1000, + 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=0, + help="Enter the position mode (HEDGE/ONEWAY).") + with c6: + cooldown_time = st.number_input("Cooldown Time (minutes)", value=60, + help="Specify the cooldown time in minutes (e.g., 60).") * 60 + with c7: + executor_refresh_time = st.number_input("Executor Refresh Time (minutes)", value=60, + help="Enter the refresh time in minutes for executors (e.g., 60).") * 60 + return connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time diff --git a/frontend/components/risk_management.py b/frontend/components/risk_management.py new file mode 100644 index 0000000..bfbd6ac --- /dev/null +++ b/frontend/components/risk_management.py @@ -0,0 +1,27 @@ +import streamlit as st +from hummingbot.connector.connector_base import OrderType + + +def get_risk_management_inputs(): + 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=3.0, 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=2.0, 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=60 * 6, + 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=1.8, + 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=0.2, 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), + help="Enter the order type for taking profit (LIMIT/MARKET).") + return sl, tp, time_limit, ts_ap, ts_delta, take_profit_order_type diff --git a/frontend/components/st_inputs.py b/frontend/components/st_inputs.py index 9a63f5b..9a417a5 100644 --- a/frontend/components/st_inputs.py +++ b/frontend/components/st_inputs.py @@ -6,7 +6,7 @@ from hummingbot.strategy_v2.utils.distributions import Distributions def normalize(values): total = sum(values) - return [Decimal(val / total) for val in values] + return [val / total for val in values] def distribution_inputs(column, dist_type_name, levels=3): From 97798db3df20d8bd1a3609998cf216b4645849ae Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:21:02 -0400 Subject: [PATCH 062/139] (feat) remove dataviz module --- frontend/data_viz/graphs.py | 584 ------------------------- frontend/data_viz/strategy_analysis.py | 234 ---------- 2 files changed, 818 deletions(-) delete mode 100644 frontend/data_viz/graphs.py delete mode 100644 frontend/data_viz/strategy_analysis.py diff --git a/frontend/data_viz/graphs.py b/frontend/data_viz/graphs.py deleted file mode 100644 index b4c3eef..0000000 --- a/frontend/data_viz/graphs.py +++ /dev/null @@ -1,584 +0,0 @@ -import pandas as pd -from plotly.subplots import make_subplots -import plotly.express as px -import pandas_ta as ta # noqa: F401 -import streamlit as st -from typing import Union - -from frontend.data_viz.strategy_analysis import StrategyAnalysis -import plotly.graph_objs as go - -BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" -BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" -FEE_COLOR = "rgba(51, 0, 51, 0.9)" -MIN_INTERVAL_RESOLUTION = "1m" - - -class CandlesGraph: - def __init__(self, candles_df: pd.DataFrame, line_mode=False, show_volume=True, extra_rows=1): - self.candles_df = candles_df - self.show_volume = show_volume - self.line_mode = line_mode - rows, heights = self.get_n_rows_and_heights(extra_rows) - self.rows = rows - specs = [[{"secondary_y": True}]] * rows - self.base_figure = make_subplots(rows=rows, cols=1, shared_xaxes=True, vertical_spacing=0.005, - row_heights=heights, specs=specs) - self.min_time = candles_df.reset_index().timestamp.min() - self.max_time = candles_df.reset_index().timestamp.max() - self.add_candles_graph() - if self.show_volume: - self.add_volume() - self.update_layout() - - def get_n_rows_and_heights(self, extra_rows): - rows = 1 + extra_rows + self.show_volume - row_heights = [0.4] * (extra_rows) - if self.show_volume: - row_heights.insert(0, 0.05) - row_heights.insert(0, 0.8) - return rows, row_heights - - def figure(self): - return self.base_figure - - def add_candles_graph(self): - if self.line_mode: - self.base_figure.add_trace( - go.Scatter(x=self.candles_df.index, - y=self.candles_df['close'], - name="Close", - mode='lines', - line=dict(color='blue')), - row=1, col=1, - ) - else: - hover_text = [] - for i in range(len(self.candles_df)): - hover_text.append( - f"Open: {self.candles_df['open'][i]}
" - f"High: {self.candles_df['high'][i]}
" - f"Low: {self.candles_df['low'][i]}
" - f"Close: {self.candles_df['close'][i]}
" - ) - self.base_figure.add_trace( - go.Candlestick( - x=self.candles_df.index, - open=self.candles_df['open'], - high=self.candles_df['high'], - low=self.candles_df['low'], - close=self.candles_df['close'], - name="OHLC", - hoverinfo="text", - hovertext=hover_text - ), - row=1, col=1, - ) - - def add_buy_trades(self, orders_data: pd.DataFrame): - self.base_figure.add_trace( - go.Scatter( - x=orders_data['timestamp'], - y=orders_data['price'], - name='Buy Orders', - mode='markers', - marker=dict( - symbol='triangle-up', - color='green', - size=12, - line=dict(color='black', width=1), - opacity=0.7, - ), - hoverinfo="text", - hovertext=orders_data["price"].apply(lambda x: f"Buy Order: {x}
")), - row=1, col=1, - ) - - def add_sell_trades(self, orders_data: pd.DataFrame): - self.base_figure.add_trace( - go.Scatter( - x=orders_data['timestamp'], - y=orders_data['price'], - name='Sell Orders', - mode='markers', - marker=dict(symbol='triangle-down', - color='red', - size=12, - line=dict(color='black', width=1), - opacity=0.7,), - hoverinfo="text", - hovertext=orders_data["price"].apply(lambda x: f"Sell Order: {x}
")), - row=1, col=1, - ) - - def add_bollinger_bands(self, length=20, std=2.0, row=1): - df = self.candles_df.copy() - if len(df) < length: - st.warning("Not enough data to calculate Bollinger Bands") - return - df.ta.bbands(length=length, std=std, append=True) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBU_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=row, col=1, - ) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBM_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=1, col=1, - ) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'BBL_{length}_{std}'], - name='Bollinger Bands', - mode='lines', - line=dict(color='blue', width=1)), - row=1, col=1, - ) - - def add_volume(self): - self.base_figure.add_trace( - go.Bar( - x=self.candles_df.index, - y=self.candles_df['volume'], - name="Volume", - opacity=0.5, - marker=dict(color='lightgreen'), - - ), - row=2, col=1, - ) - - def add_ema(self, length=20, row=1): - df = self.candles_df.copy() - if len(df) < length: - st.warning("Not enough data to calculate EMA") - return - df.ta.ema(length=length, append=True) - self.base_figure.add_trace( - go.Scatter( - x=df.index, - y=df[f'EMA_{length}'], - name='EMA', - mode='lines', - line=dict(color='yellow', width=1)), - row=row, col=1, - ) - - def add_quote_inventory_change(self, strategy_data: StrategyData, row=3): - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill.inventory_cost, - name="Quote Inventory", - mode="lines", - line=dict(shape="hv"), - ), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text='Quote Inventory Change', row=row, col=1) - - def add_pnl(self, strategy_data: SingleMarketStrategyData, row=4): - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=[max(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], - name="Cum Profit", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.1), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(0, 255, 0, 0.5)" - ), - row=row, col=1 - ) - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=[min(0, realized_pnl) for realized_pnl in strategy_data.trade_fill["realized_trade_pnl"].apply(lambda x: round(x, 4))], - name="Cum Loss", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.5)", dash="dash", width=0.3), - # marker=dict(symbol="arrow"), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(255, 0, 0, 0.5)", - ), - row=row, col=1 - ) - self.base_figure.add_trace( - go.Scatter( - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill["cum_fees_in_quote"].apply(lambda x: round(x, 4)), - name="Cum Fees", - mode='lines', - line=dict(shape="hv", color="rgba(1, 1, 1, 0.1)", dash="dash", width=0.1), - fill="tozeroy", # Fill to the line below (trade pnl) - fillcolor="rgba(51, 0, 51, 0.5)" - ), - row=row, col=1 - ) - self.base_figure.add_trace(go.Scatter(name="Net Realized Profit", - x=strategy_data.trade_fill.timestamp, - y=strategy_data.trade_fill["net_realized_pnl"], - mode="lines", - line=dict(shape="hv")), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text='PNL', row=row, col=1) - - def add_positions(self, position_executor_data: pd.DataFrame, row=1): - position_executor_data["close_datetime"] = pd.to_datetime(position_executor_data["close_timestamp"], unit="s") - i = 1 - for index, rown in position_executor_data.iterrows(): - i += 1 - self.base_figure.add_trace(go.Scatter(name=f"Position {index}", - x=[rown.datetime, rown.close_datetime], - y=[rown.entry_price, rown.close_price], - mode="lines", - line=dict(color="lightgreen" if rown.net_pnl_quote > 0 else "red"), - hoverinfo="text", - hovertext=f"Position N°: {i}
" - f"Datetime: {rown.datetime}
" - f"Close datetime: {rown.close_datetime}
" - f"Side: {rown.side}
" - f"Entry price: {rown.entry_price}
" - f"Close price: {rown.close_price}
" - f"Close type: {rown.close_type}
" - f"Stop Loss: {100 * rown.sl:.2f}%
" - f"Take Profit: {100 * rown.tp:.2f}%
" - f"Time Limit: {100 * rown.tl:.2f}
" - f"Open Order Type: {rown.open_order_type}
" - f"Leverage: {rown.leverage}
" - f"Controller name: {rown.controller_name}
", - showlegend=False), - row=row, col=1) - - def update_layout(self): - self.base_figure.update_layout( - title={ - 'text': "Market activity", - 'y': 0.99, - 'x': 0.5, - 'xanchor': 'center', - 'yanchor': 'top' - }, - legend=dict( - orientation="h", - x=0.5, - y=1.04, - xanchor="center", - yanchor="bottom" - ), - height=1000, - xaxis=dict(rangeslider_visible=False, - range=[self.min_time, self.max_time]), - yaxis=dict(range=[self.candles_df.low.min(), self.candles_df.high.max()]), - hovermode='x unified' - ) - self.base_figure.update_yaxes(title_text="Price", row=1, col=1) - if self.show_volume: - self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) - self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) - - -class BacktestingGraphs: - def __init__(self, study_df: pd.DataFrame): - self.study_df = study_df - - def pnl_vs_maxdrawdown(self): - fig = go.Figure() - fig.add_trace(go.Scatter(name="Pnl vs Max Drawdown", - x=-100 * self.study_df["max_drawdown_pct"], - y=100 * self.study_df["net_pnl_pct"], - mode="markers", - text=None, - hovertext=self.study_df["hover_text"])) - fig.update_layout( - title="PnL vs Max Drawdown", - xaxis_title="Max Drawdown [%]", - yaxis_title="Net Profit [%]", - height=800 - ) - fig.data[0].text = [] - return fig - - @staticmethod - def get_trial_metrics(strategy_analysis: StrategyAnalysis, - add_volume: bool = True, - add_positions: bool = True, - add_pnl: bool = True): - """Isolated method because it needs to be called from analyze and simulate pages""" - metrics_container = st.container() - with metrics_container: - col1, col2 = st.columns(2) - with col1: - st.subheader("🏦 Market") - with col2: - st.subheader("📋 General stats") - col1, col2, col3, col4 = st.columns(4) - with col1: - st.metric("Exchange", st.session_state["strategy_params"]["exchange"]) - with col2: - st.metric("Trading Pair", st.session_state["strategy_params"]["trading_pair"]) - with col3: - st.metric("Start date", strategy_analysis.start_date().strftime("%Y-%m-%d %H:%M")) - st.metric("End date", strategy_analysis.end_date().strftime("%Y-%m-%d %H:%M")) - with col4: - st.metric("Duration (hours)", f"{strategy_analysis.duration_in_minutes() / 60:.2f}") - st.metric("Price change", st.session_state["strategy_params"]["trading_pair"]) - st.subheader("📈 Performance") - col1, col2, col3, col4, col5, col6, col7, col8 = st.columns(8) - with col1: - st.metric("Net PnL USD", - f"{strategy_analysis.net_profit_usd():.2f}", - delta=f"{100 * strategy_analysis.net_profit_pct():.2f}%", - help="The overall profit or loss achieved.") - with col2: - st.metric("Total positions", - f"{strategy_analysis.total_positions()}", - help="The total number of closed trades, winning and losing.") - with col3: - st.metric("Accuracy", - f"{100 * (len(strategy_analysis.win_signals()) / strategy_analysis.total_positions()):.2f} %", - help="The percentage of winning trades, the number of winning trades divided by the" - " total number of closed trades") - with col4: - st.metric("Profit factor", - f"{strategy_analysis.profit_factor():.2f}", - help="The amount of money the strategy made for every unit of money it lost, " - "gross profits divided by gross losses.") - with col5: - st.metric("Max Drawdown", - f"{strategy_analysis.max_drawdown_usd():.2f}", - delta=f"{100 * strategy_analysis.max_drawdown_pct():.2f}%", - help="The greatest loss drawdown, i.e., the greatest possible loss the strategy had compared " - "to its highest profits") - with col6: - st.metric("Avg Profit", - f"{strategy_analysis.avg_profit():.2f}", - help="The sum of money gained or lost by the average trade, Net Profit divided by " - "the overall number of closed trades.") - with col7: - st.metric("Avg Minutes", - f"{strategy_analysis.avg_trading_time_in_minutes():.2f}", - help="The average number of minutes that elapsed during trades for all closed trades.") - with col8: - st.metric("Sharpe Ratio", - f"{strategy_analysis.sharpe_ratio():.2f}", - help="The Sharpe ratio is a measure that quantifies the risk-adjusted return of an investment" - " or portfolio. It compares the excess return earned above a risk-free rate per unit of" - " risk taken.") - - st.plotly_chart(strategy_analysis.pnl_over_time(), use_container_width=True) - strategy_analysis.create_base_figure(volume=add_volume, positions=add_positions, trade_pnl=add_pnl) - st.plotly_chart(strategy_analysis.figure(), use_container_width=True) - return metrics_container - - -class PerformanceGraphs: - BULLISH_COLOR = "rgba(97, 199, 102, 0.9)" - BEARISH_COLOR = "rgba(255, 102, 90, 0.9)" - FEE_COLOR = "rgba(51, 0, 51, 0.9)" - - def __init__(self, strategy_data: Union[StrategyData, SingleMarketStrategyData]): - self.strategy_data = strategy_data - - @property - def has_summary_table(self): - if isinstance(self.strategy_data, StrategyData): - return self.strategy_data.strategy_summary is not None - else: - return False - - @property - def has_position_executor_summary(self): - if isinstance(self.strategy_data, StrategyData): - return self.strategy_data.position_executor is not None - else: - return False - - def strategy_summary_table(self): - summary = st.data_editor(self.strategy_data.strategy_summary, - column_config={"PnL Over Time": st.column_config.LineChartColumn("PnL Over Time", - y_min=0, - y_max=5000), - "Explore": st.column_config.CheckboxColumn(required=True) - }, - use_container_width=True, - hide_index=True - ) - selected_rows = summary[summary.Explore] - if len(selected_rows) > 0: - return selected_rows - else: - return None - - def summary_chart(self): - fig = px.bar(self.strategy_data.strategy_summary, x="Trading Pair", y="Realized PnL", color="Exchange") - fig.update_traces(width=min(1.0, 0.1 * len(self.strategy_data.strategy_summary))) - return fig - - def pnl_over_time(self): - df = self.strategy_data.trade_fill.copy() - df.reset_index(drop=True, inplace=True) - df_above = df[df['net_realized_pnl'] >= 0] - df_below = df[df['net_realized_pnl'] < 0] - - fig = go.Figure() - fig.add_trace(go.Bar(name="Cum Realized PnL", - x=df_above.index, - y=df_above["net_realized_pnl"], - marker_color=BULLISH_COLOR, - # hoverdq - showlegend=False)) - fig.add_trace(go.Bar(name="Cum Realized PnL", - x=df_below.index, - y=df_below["net_realized_pnl"], - marker_color=BEARISH_COLOR, - showlegend=False)) - fig.update_layout(title=dict( - text='Cummulative PnL', # Your title text - x=0.43, - y=0.95, - ), - plot_bgcolor='rgba(0,0,0,0)', - paper_bgcolor='rgba(0,0,0,0)') - return fig - - def intraday_performance(self): - df = self.strategy_data.trade_fill.copy() - - def hr2angle(hr): - return (hr * 15) % 360 - - def hr_str(hr): - # Normalize hr to be between 1 and 12 - hr_string = str(((hr - 1) % 12) + 1) - suffix = ' AM' if (hr % 24) < 12 else ' PM' - return hr_string + suffix - - df["hour"] = df["timestamp"].dt.hour - realized_pnl_per_hour = df.groupby("hour")[["realized_pnl", "quote_volume"]].sum().reset_index() - fig = go.Figure() - fig.add_trace(go.Barpolar( - name="Profits", - r=realized_pnl_per_hour["quote_volume"], - theta=realized_pnl_per_hour["hour"] * 15, - marker=dict( - color=realized_pnl_per_hour["realized_pnl"], - colorscale="RdYlGn", - cmin=-(abs(realized_pnl_per_hour["realized_pnl"]).max()), - cmid=0.0, - cmax=(abs(realized_pnl_per_hour["realized_pnl"]).max()), - colorbar=dict( - title='Realized PnL', - x=0, - y=-0.5, - xanchor='left', - yanchor='bottom', - orientation='h' - ) - ))) - fig.update_layout( - polar=dict( - radialaxis=dict( - visible=True, - showline=False, - ), - angularaxis=dict( - rotation=90, - direction="clockwise", - tickvals=[hr2angle(hr) for hr in range(24)], - ticktext=[hr_str(hr) for hr in range(24)], - ), - bgcolor='rgba(255, 255, 255, 0)', - - ), - legend=dict( - orientation="h", - x=0.5, - y=1.08, - xanchor="center", - yanchor="bottom" - ), - title=dict( - text='Intraday Performance', - x=0.5, - y=0.93, - xanchor="center", - yanchor="bottom" - ), - ) - return fig - - def returns_histogram(self): - df = self.strategy_data.trade_fill.copy() - fig = go.Figure() - fig.add_trace(go.Histogram(name="Losses", - x=df.loc[df["realized_pnl"] < 0, "realized_pnl"], - marker_color=BEARISH_COLOR)) - fig.add_trace(go.Histogram(name="Profits", - x=df.loc[df["realized_pnl"] > 0, "realized_pnl"], - marker_color=BULLISH_COLOR)) - fig.update_layout( - title=dict( - text='Returns Distribution', - x=0.5, - xanchor="center", - ), - legend=dict( - orientation="h", - yanchor="bottom", - y=1.02, - xanchor="center", - x=.48 - )) - return fig - - def position_executor_summary_sunburst(self): - if self.strategy_data.position_executor is not None: - df = self.strategy_data.position_executor.copy() - grouped_df = df.groupby(["trading_pair", "side", "close_type"]).size().reset_index(name="count") - - fig = px.sunburst(grouped_df, - path=['trading_pair', 'side', 'close_type'], - values="count", - color_continuous_scale='RdBu', - color_continuous_midpoint=0) - - fig.update_layout( - title=dict( - text='Position Executor Summary', - x=0.5, - xanchor="center", - ), - legend=dict( - orientation="h", - yanchor="bottom", - y=1.02, - xanchor="center", - x=.48 - ) - ) - return fig - else: - return None - - def candles_graph(self, candles: pd.DataFrame, interval="5m", show_volume=False, extra_rows=2): - line_mode = interval == MIN_INTERVAL_RESOLUTION - cg = CandlesGraph(candles, show_volume=show_volume, line_mode=line_mode, extra_rows=extra_rows) - cg.add_buy_trades(self.strategy_data.buys) - cg.add_sell_trades(self.strategy_data.sells) - cg.add_pnl(self.strategy_data, row=2) - cg.add_quote_inventory_change(self.strategy_data, row=3) - if self.strategy_data.position_executor is not None: - cg.add_positions(self.strategy_data.position_executor, row=1) - return cg.figure() diff --git a/frontend/data_viz/strategy_analysis.py b/frontend/data_viz/strategy_analysis.py deleted file mode 100644 index bd4ca0b..0000000 --- a/frontend/data_viz/strategy_analysis.py +++ /dev/null @@ -1,234 +0,0 @@ -from typing import Optional - -import pandas as pd -from plotly.subplots import make_subplots -import pandas_ta as ta # noqa: F401 -import plotly.graph_objs as go -import numpy as np - - -class StrategyAnalysis: - def __init__(self, positions: pd.DataFrame, candles_df: Optional[pd.DataFrame] = None): - self.candles_df = candles_df - self.positions = positions - self.candles_df["timestamp"] = pd.to_datetime(self.candles_df["timestamp"], unit="ms") - self.positions["timestamp"] = pd.to_datetime(self.positions["timestamp"], unit="ms") - self.positions["close_time"] = pd.to_datetime(self.positions["close_time"], unit="ms") - self.base_figure = None - - def create_base_figure(self, candlestick=True, volume=True, positions=False, trade_pnl=False, extra_rows=0): - rows, heights = self.get_n_rows_and_heights(extra_rows + trade_pnl, volume) - self.rows = rows - specs = [[{"secondary_y": True}]] * rows - 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(row=1, col=1) - if volume: - self.add_volume() - if positions: - self.add_positions() - if trade_pnl: - self.add_trade_pnl(row=rows) - self.update_layout(volume) - - def add_positions(self): - # Add long and short positions - active_signals = self.positions.copy() - active_signals.loc[active_signals["signal"] == -1, "symbol"] = "triangle-down" - active_signals.loc[active_signals["signal"] == 1, "symbol"] = "triangle-up" - active_signals.loc[active_signals["profitable"] == 1, "color"] = "lightgreen" - active_signals.loc[active_signals["profitable"] == -1, "color"] = "red" - self.base_figure.add_trace(go.Scatter(x=active_signals.loc[(active_signals["side"] != 0), "timestamp"], - y=active_signals.loc[active_signals["side"] != 0, "close"], - name="Entry Price: $", - mode="markers", - marker_color=active_signals.loc[(active_signals["side"] != 0), "color"], - marker_symbol=active_signals.loc[(active_signals["side"] != 0), "symbol"], - marker_size=20, - marker_line={"color": "black", "width": 0.7}), - row=1, col=1) - - for index, row in active_signals.iterrows(): - self.base_figure.add_shape(type="rect", - fillcolor="green", - opacity=0.5, - x0=row.timestamp, - y0=row.close, - x1=row.close_time, - y1=row.take_profit_price, - line=dict(color="green"), - row=1, col=1) - # Add SL - self.base_figure.add_shape(type="rect", - fillcolor="red", - opacity=0.5, - x0=row.timestamp, - y0=row.close, - x1=row.close_time, - y1=row.stop_loss_price, - line=dict(color="red"), - row=1, col=1) - - def get_n_rows_and_heights(self, extra_rows, volume=True): - rows = 1 + extra_rows + volume - row_heights = [0.5] * (extra_rows) - if volume: - row_heights.insert(0, 0.2) - row_heights.insert(0, 0.8) - return rows, row_heights - - def figure(self): - return self.base_figure - - def add_candles_graph(self, row, col, name_suffix='', timeframe_suffix=''): - self.base_figure.add_trace( - go.Candlestick( - 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=row, col=col, - ) - - def add_volume(self): - self.base_figure.add_trace( - go.Bar( - x=self.candles_df["timestamp"], - y=self.candles_df["volume"], - name="Volume", - opacity=0.5, - marker=dict(color="lightgreen") - ), - row=2, col=1, - ) - - def add_trade_pnl(self, row=2): - self.base_figure.add_trace( - go.Scatter( - x=self.positions["timestamp"], - y=self.positions["net_pnl_quote"].cumsum(), - name="Cumulative Trade PnL", - mode="lines", - line=dict(color="chocolate", width=2)), - row=row, col=1 - ) - self.base_figure.update_yaxes(title_text="Cum Trade PnL", row=row, col=1) - - def update_layout(self, volume=True): - self.base_figure.update_layout( - title={ - "text": "Backtesting Analysis", - "y": 0.95, - "x": 0.5, - "xanchor": "center", - "yanchor": "top" - }, - legend=dict( - orientation="h", - yanchor="bottom", - y=-0.2, - xanchor="right", - x=1 - ), - height=1500, - xaxis_rangeslider_visible=False, - hovermode="x unified" - ) - self.base_figure.update_yaxes(title_text="Price", row=1, col=1) - if volume: - self.base_figure.update_yaxes(title_text="Volume", row=2, col=1) - self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1) - - def initial_portfolio(self): - return self.positions["inventory"].dropna().values[0] - - def final_portfolio(self): - return self.positions["inventory"].dropna().values[-1] - - def net_profit_usd(self): - return self.final_portfolio() - self.initial_portfolio() - - def net_profit_pct(self): - return self.net_profit_usd() / self.initial_portfolio() - - def returns(self): - return self.positions["net_pnl_quote"] / self.initial_portfolio() - - def total_positions(self): - return self.positions.shape[0] - 1 - - def win_signals(self): - return self.positions.loc[(self.positions["profitable"] > 0) & (self.positions["side"] != 0)] - - def loss_signals(self): - return self.positions.loc[(self.positions["profitable"] < 0) & (self.positions["side"] != 0)] - - def accuracy(self): - return self.win_signals().shape[0] / self.total_positions() - - def max_drawdown_usd(self): - cumulative_returns = self.positions["net_pnl_quote"].cumsum() - peak = np.maximum.accumulate(cumulative_returns) - drawdown = (cumulative_returns - peak) - max_draw_down = np.min(drawdown) - return max_draw_down - - def max_drawdown_pct(self): - return self.max_drawdown_usd() / self.initial_portfolio() - - def sharpe_ratio(self): - returns = self.returns() - return returns.mean() / returns.std() - - def profit_factor(self): - 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 - - def duration_in_minutes(self): - return (self.positions["timestamp"].iloc[-1] - self.positions["timestamp"].iloc[0]).total_seconds() / 60 - - def avg_trading_time_in_minutes(self): - time_diff_minutes = (self.positions["close_time"] - self.positions["timestamp"]).dt.total_seconds() / 60 - return time_diff_minutes.mean() - - def start_date(self): - return pd.to_datetime(self.candles_df.timestamp.min(), unit="ms") - - def end_date(self): - return pd.to_datetime(self.candles_df.timestamp.max(), unit="ms") - - def avg_profit(self): - return self.positions.net_pnl_quote.mean() - - def text_report(self): - return f""" -Strategy Performance Report: - - Net Profit: {self.net_profit_usd():,.2f} USD ({self.net_profit_pct() * 100:,.2f}%) - - Total Positions: {self.total_positions()} - - Win Signals: {self.win_signals().shape[0]} - - Loss Signals: {self.loss_signals().shape[0]} - - Accuracy: {self.accuracy():,.2f}% - - Profit Factor: {self.profit_factor():,.2f} - - Max Drawdown: {self.max_drawdown_usd():,.2f} USD | {self.max_drawdown_pct() * 100:,.2f}% - - Sharpe Ratio: {self.sharpe_ratio():,.2f} - - Duration: {self.duration_in_minutes() / 60:,.2f} Hours - - Average Trade Duration: {self.avg_trading_time_in_minutes():,.2f} minutes - """ - - def pnl_over_time(self): - fig = go.Figure() - fig.add_trace(go.Scatter(name="PnL Over Time", - x=self.positions.index, - y=self.positions.net_pnl_quote.cumsum())) - # Update layout with the required attributes - fig.update_layout( - title="PnL Over Time", - xaxis_title="N° Position", - yaxis=dict(title="Net PnL USD", side="left", showgrid=False), - ) - return fig From 810642df37b75b8ce5a15b9c39bda5825417dc70 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:21:24 -0400 Subject: [PATCH 063/139] (feat) add base visualizations for configs --- .../{data_viz => visualization}/__init__.py | 0 .../visualization/executors_distribution.py | 66 +++++++++++++++++++ frontend/{data_viz => visualization}/theme.py | 18 +++-- 3 files changed, 79 insertions(+), 5 deletions(-) rename frontend/{data_viz => visualization}/__init__.py (100%) create mode 100644 frontend/visualization/executors_distribution.py rename frontend/{data_viz => visualization}/theme.py (65%) diff --git a/frontend/data_viz/__init__.py b/frontend/visualization/__init__.py similarity index 100% rename from frontend/data_viz/__init__.py rename to frontend/visualization/__init__.py diff --git a/frontend/visualization/executors_distribution.py b/frontend/visualization/executors_distribution.py new file mode 100644 index 0000000..38f9600 --- /dev/null +++ b/frontend/visualization/executors_distribution.py @@ -0,0 +1,66 @@ +import plotly.graph_objects as go +import frontend.visualization.theme as theme + + +def create_executors_distribution_traces(config): + colors = theme.get_color_scheme() + + buy_spread_distributions = [spread * 100 for spread in config["buy_spreads"]] + sell_spread_distributions = [spread * 100 for spread in config["sell_spreads"]] + buy_order_amounts_quote = [amount * config["total_amount_quote"] for amount in config["buy_amounts_pct"]] + sell_order_amounts_quote = [amount * config["total_amount_quote"] for amount in config["sell_amounts_pct"]] + buy_order_levels = len(buy_spread_distributions) + sell_order_levels = len(sell_spread_distributions) + + # Create the figure with a dark theme and secondary y-axis + fig = go.Figure() + + # Buy orders on the negative side of x-axis + fig.add_trace(go.Bar( + x=[-dist for dist in buy_spread_distributions], + y=buy_order_amounts_quote, + name='Buy Orders', + marker_color=colors['buy'], + width=[0.2] * buy_order_levels # Adjust the width of the bars as needed + )) + + # Sell orders on the positive side of x-axis + fig.add_trace(go.Bar( + x=sell_spread_distributions, + y=sell_order_amounts_quote, + name='Sell Orders', + marker_color=colors['sell'], + width=[0.2] * sell_order_levels # Adjust the width of the bars as needed + )) + + # Add annotations for buy orders + for i, value in enumerate(buy_order_amounts_quote): + fig.add_annotation( + x=-buy_spread_distributions[i], + y=value + 0.01 * max(buy_order_amounts_quote), # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['buy'], size=10) + ) + + # Add annotations for sell orders + for i, value in enumerate(sell_order_amounts_quote): + fig.add_annotation( + x=sell_spread_distributions[i], + y=value + 0.01 * max(sell_order_amounts_quote), # Offset the text slightly above the bar + text=str(round(value, 2)), + showarrow=False, + font=dict(color=colors['sell'], size=10) + ) + + # Apply the theme layout + layout_settings = theme.get_default_layout("Market Maker Order Distribution") + fig.update_layout(**layout_settings) + fig.update_layout( + xaxis_title="Spread (%)", + yaxis_title="Order Amount (Quote)", + bargap=0.1, # Adjust the gap between the bars + barmode='relative', # Stack the bars on top of each other + showlegend=True + ) + return fig diff --git a/frontend/data_viz/theme.py b/frontend/visualization/theme.py similarity index 65% rename from frontend/data_viz/theme.py rename to frontend/visualization/theme.py index 6080002..1e2767e 100644 --- a/frontend/data_viz/theme.py +++ b/frontend/visualization/theme.py @@ -1,4 +1,4 @@ -def get_default_layout(title, height=600, width=800): +def get_default_layout(title, height=800, width=1800): return { "template": "plotly_dark", "title": title, @@ -7,9 +7,7 @@ def get_default_layout(title, height=600, width=800): "font": {"color": 'white', "size": 12}, # Consistent font color and size "height": height, "width": width, - "margin": {"l": 20, "r": 20, "t": 50, "b": 20}, - "xaxis": {"title": "Spread (%)"}, - "yaxis": {"title": "Amount (Quote)"} + "margin": {"l": 20, "r": 20, "t": 50, "b": 20} } @@ -27,5 +25,15 @@ def get_color_scheme(): 'macd_line': '#FFA500', # Orange 'macd_signal': '#800080', # Purple 'macd_histogram_positive': '#32CD32', # Green - 'macd_histogram_negative': '#FF6347' # Red + 'macd_histogram_negative': '#FF6347', # Red + 'spread': '#00BFFF', # Deep Sky Blue + 'break_even': '#FFD700', # Gold + 'take_profit': '#32CD32', # Green + 'order_amount': '#1E90FF', # Dodger Blue + 'cum_amount': '#4682B4', # Steel Blue + 'stop_loss': '#FF0000', # Red + 'cum_unrealized_pnl': '#FFA07A' # Light Salmon + } + + From 4ba64bfbd4a9aa68fbcba102b209abf2b990ff7f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:21:37 -0400 Subject: [PATCH 064/139] (feat) add visualizations for backtesting --- frontend/visualization/backtesting.py | 33 +++++++++++++++++++++++++++ frontend/visualization/candles.py | 12 ++++++++++ frontend/visualization/executors.py | 23 +++++++++++++++++++ frontend/visualization/pnl.py | 15 ++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 frontend/visualization/backtesting.py create mode 100644 frontend/visualization/candles.py create mode 100644 frontend/visualization/executors.py create mode 100644 frontend/visualization/pnl.py diff --git a/frontend/visualization/backtesting.py b/frontend/visualization/backtesting.py new file mode 100644 index 0000000..6efdc1e --- /dev/null +++ b/frontend/visualization/backtesting.py @@ -0,0 +1,33 @@ +from plotly.subplots import make_subplots +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.executors import add_executors_trace +from frontend.visualization.pnl import get_pnl_trace +from frontend.visualization.theme import get_default_layout + + +def create_backtesting_figure(df, executors, config): + # Create subplots + fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick', 'PNL Quote'), + row_heights=[0.7, 0.3]) + + # Add candlestick trace + fig.add_trace(get_candlestick_trace(df), row=1, col=1) + + # Add executors trace + fig = add_executors_trace(fig, executors, row=1, col=1) + + # Add PNL trace + fig.add_trace(get_pnl_trace(executors), row=2, col=1) + + # Apply the theme layout + layout_settings = get_default_layout(f"Trading Pair: {config['trading_pair']}") + layout_settings["showlegend"] = False + fig.update_layout(**layout_settings) + + # Update axis properties + fig.update_xaxes(rangeslider_visible=False, row=1, col=1) + fig.update_xaxes(row=2, col=1) + fig.update_yaxes(title_text="Price", row=1, col=1) + fig.update_yaxes(title_text="PNL", row=2, col=1) + return fig diff --git a/frontend/visualization/candles.py b/frontend/visualization/candles.py new file mode 100644 index 0000000..9d0b050 --- /dev/null +++ b/frontend/visualization/candles.py @@ -0,0 +1,12 @@ +import plotly.graph_objects as go +import pandas as pd + +def get_candlestick_trace(df): + df.index = pd.to_datetime(df.timestamp, unit='ms') + return 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') diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py new file mode 100644 index 0000000..ef00a2d --- /dev/null +++ b/frontend/visualization/executors.py @@ -0,0 +1,23 @@ +import plotly.graph_objects as go +import pandas as pd +from decimal import Decimal + + +def add_executors_trace(fig, executors, row, col): + for executor in executors: + entry_time = pd.to_datetime(executor.timestamp, unit='ms') + entry_price = executor.custom_info["current_position_average_price"] + exit_time = pd.to_datetime(executor.close_timestamp, unit='ms') + exit_price = executor.custom_info["close_price"] + + if executor.filled_amount_quote == 0: + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='blue', width=2, dash="dash"), name='Buy Entry/Exit'), row=row, col=col) + else: + if executor.net_pnl_quote > Decimal(0): + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='green', width=2), name='Buy Entry/Exit'), row=row, col=col) + else: + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='red', width=2), name='Sell Entry/Exit'), row=row, col=col) + return fig diff --git a/frontend/visualization/pnl.py b/frontend/visualization/pnl.py new file mode 100644 index 0000000..643c610 --- /dev/null +++ b/frontend/visualization/pnl.py @@ -0,0 +1,15 @@ +import plotly.graph_objects as go +import numpy as np +import pandas as pd + + +def get_pnl_trace(executors): + pnl = [e.net_pnl_quote for e in executors] + cum_pnl = np.cumsum(pnl) + return go.Scatter( + x=pd.to_datetime([e.close_timestamp for e in executors], unit="ms"), + y=cum_pnl, + mode='lines', + line=dict(color='blue', width=2, dash="dash"), + name='Cumulative PNL' + ) From d200572793a29f056a6cf01f6289032ef4fa185a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:21:56 -0400 Subject: [PATCH 065/139] (feat) fix imports --- frontend/pages/backtesting/analyze/analyze.py | 4 ++-- frontend/pages/performance/strategy_performance/app.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/pages/backtesting/analyze/analyze.py b/frontend/pages/backtesting/analyze/analyze.py index 0975039..791cd97 100644 --- a/frontend/pages/backtesting/analyze/analyze.py +++ b/frontend/pages/backtesting/analyze/analyze.py @@ -12,8 +12,8 @@ from decimal import Decimal from backend.utils.optuna_database_manager import OptunaDBManager from backend.utils.os_utils import load_controllers -from frontend.data_viz.graphs import BacktestingGraphs -from frontend.data_viz.strategy_analysis import StrategyAnalysis +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="🔬") diff --git a/frontend/pages/performance/strategy_performance/app.py b/frontend/pages/performance/strategy_performance/app.py index 7d0e3ae..b1ccef5 100644 --- a/frontend/pages/performance/strategy_performance/app.py +++ b/frontend/pages/performance/strategy_performance/app.py @@ -4,7 +4,7 @@ import streamlit as st import math from backend.utils.os_utils import get_databases -from frontend.data_viz.graphs import PerformanceGraphs +from frontend.visualization.graphs import PerformanceGraphs from frontend.st_utils import initialize_st_page, style_metric_cards initialize_st_page(title="Strategy Performance", icon="🚀") From d87f959860f2da3cf40fb202821acf158ebda377 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 16 May 2024 18:22:06 -0400 Subject: [PATCH 066/139] (feat) clean up pmm simple page --- frontend/pages/config/pmm_simple/app.py | 28 ++++---- .../pages/config/pmm_simple/user_inputs.py | 69 +++++++++---------- 2 files changed, 44 insertions(+), 53 deletions(-) diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index d02e694..d3ea50b 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -4,32 +4,30 @@ from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT # Import submodules from frontend.pages.config.pmm_simple.user_inputs import user_inputs -from frontend.pages.config.pmm_simple import calculate_orders -from frontend.data_viz.visualization import visualize_orders -from frontend.pages.config.pmm_simple import handle_config 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 # 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 and upload it to the BackendAPI.") +st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") st.write("---") # Get user inputs inputs = user_inputs() +st.write(inputs) +fig = create_executors_distribution_traces(inputs) +st.plotly_chart(fig, use_container_width=True) -# Calculate orders based on inputs -order_data = calculate_orders(inputs) - -# Visualize orders -visualize_orders(order_data) - -# Handle configuration -handle_config(inputs, order_data, backend_api_client) - -st.write("---") st.write("### Backtesting") -backtesting_section(inputs, backend_api_client) +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) + st.plotly_chart(fig, use_container_width=True) \ No newline at end of file diff --git a/frontend/pages/config/pmm_simple/user_inputs.py b/frontend/pages/config/pmm_simple/user_inputs.py index 3bc67b3..c22b484 100644 --- a/frontend/pages/config/pmm_simple/user_inputs.py +++ b/frontend/pages/config/pmm_simple/user_inputs.py @@ -1,47 +1,40 @@ import streamlit as st -from hummingbot.connector.connector_base import OrderType + +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(): - c1, c2, c3, c4, c5, c6, c7 = st.columns(7) - with c1: - connector = st.text_input("Connector", value="binance_perpetual") - with c2: - trading_pair = st.text_input("Trading Pair", value="WLD-USDT") - with c3: - total_amount_quote = st.number_input("Total amount of quote", value=1000) - with c4: - leverage = st.number_input("Leverage", value=20) - position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) - with c5: - executor_refresh_time = st.number_input("Refresh Time (minutes)", value=3) - cooldown_time = st.number_input("Cooldown Time (minutes)", value=3) - with c6: - 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 c7: - 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) - - buy_order_levels = st.number_input("Number of Buy Order Levels", min_value=1, value=2) - sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) - - inputs = { - "connector": connector, + st.title("PMM Simple Configuration") + 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, - "leverage": leverage, - "position_mode": position_mode, + "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, - "sl": sl, - "tp": tp, - "take_profit_order_type": take_profit_order_type, - "ts_ap": ts_ap, - "ts_delta": ts_delta, + "leverage": leverage, + "position_mode": position_mode, + "stop_loss": sl, + "take_profit": tp, "time_limit": time_limit, - "buy_order_levels": buy_order_levels, - "sell_order_levels": sell_order_levels + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + } } - return inputs + + return config From f42b7eccbfe89fef8908421661f0c601e8a4e646 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 01:09:22 -0400 Subject: [PATCH 067/139] (feat) add backtesting components --- frontend/components/backtesting.py | 6 ++++-- frontend/components/st_inputs.py | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/components/backtesting.py b/frontend/components/backtesting.py index db5a6aa..66f7a32 100644 --- a/frontend/components/backtesting.py +++ b/frontend/components/backtesting.py @@ -3,13 +3,15 @@ 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() + 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) + 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: diff --git a/frontend/components/st_inputs.py b/frontend/components/st_inputs.py index 9a417a5..920bc42 100644 --- a/frontend/components/st_inputs.py +++ b/frontend/components/st_inputs.py @@ -35,7 +35,7 @@ def distribution_inputs(column, dist_type_name, levels=3): 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.1, + 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, @@ -56,17 +56,19 @@ def distribution_inputs(column, dist_type_name, levels=3): 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": - return manual_values + distribution = manual_values elif dist_type == "Linear": - return Distributions.linear(n_levels, start, step) + distribution = Distributions.linear(n_levels, start, step) elif dist_type == "Fibonacci": - return Distributions.fibonacci(n_levels, start) + distribution = Distributions.fibonacci(n_levels, start) elif dist_type == "Logarithmic": - return Distributions.logarithmic(n_levels, base, scaling_factor, start) + distribution = Distributions.logarithmic(n_levels, base, scaling_factor, start) elif dist_type == "Arithmetic": - return Distributions.arithmetic(n_levels, start, step) + distribution = Distributions.arithmetic(n_levels, start, step) elif dist_type == "Geometric": - return Distributions.geometric(n_levels, start, ratio) + distribution = Distributions.geometric(n_levels, start, ratio) elif dist_type == "GeoCustom": - return [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio) + distribution = [Decimal("0")] + Distributions.geometric(n_levels - 1, start, ratio) + return [float(val) for val in distribution] From 85e07299a7de51d49dcf39f05c9d10cccf691d72 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 01:09:31 -0400 Subject: [PATCH 068/139] (feat) improve pmm simple page --- frontend/pages/config/pmm_simple/app.py | 16 +++++++++++----- frontend/pages/config/pmm_simple/user_inputs.py | 1 - 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index d3ea50b..164ab9d 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -8,6 +8,8 @@ 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="👨‍🏫") @@ -15,19 +17,23 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B # Page content st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") -st.write("---") - # Get user inputs inputs = user_inputs() -st.write(inputs) fig = create_executors_distribution_traces(inputs) st.plotly_chart(fig, use_container_width=True) -st.write("### Backtesting") bt_results = backtesting_section(inputs, backend_api_client) if bt_results: + st.write("---") fig = create_backtesting_figure( df=bt_results["processed_data"], executors=bt_results["executors"], config=inputs) - st.plotly_chart(fig, use_container_width=True) \ No newline at end of file + 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"]) diff --git a/frontend/pages/config/pmm_simple/user_inputs.py b/frontend/pages/config/pmm_simple/user_inputs.py index c22b484..a4daf56 100644 --- a/frontend/pages/config/pmm_simple/user_inputs.py +++ b/frontend/pages/config/pmm_simple/user_inputs.py @@ -6,7 +6,6 @@ from frontend.components.risk_management import get_risk_management_inputs def user_inputs(): - st.title("PMM Simple Configuration") 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() From 049401b803616a830e2986955523927be8e2cda4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 01:09:46 -0400 Subject: [PATCH 069/139] (feat) add visualizations for performance --- frontend/visualization/backtesting_metrics.py | 63 +++++++++++++++++++ frontend/visualization/pnl.py | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 frontend/visualization/backtesting_metrics.py diff --git a/frontend/visualization/backtesting_metrics.py b/frontend/visualization/backtesting_metrics.py new file mode 100644 index 0000000..016c9df --- /dev/null +++ b/frontend/visualization/backtesting_metrics.py @@ -0,0 +1,63 @@ +import streamlit as st + + +def render_backtesting_metrics(summary_results): + net_pnl = summary_results.get('net_pnl', 0) + net_pnl_quote = summary_results.get('net_pnl_quote', 0) + total_volume = summary_results.get('total_volume', 0) + total_executors_with_position = summary_results.get('total_executors_with_position', 0) + + max_drawdown_usd = summary_results.get('max_drawdown_usd', 0) + max_drawdown_pct = summary_results.get('max_drawdown_pct', 0) + sharpe_ratio = summary_results.get('sharpe_ratio', 0) + profit_factor = summary_results.get('profit_factor', 0) + + # Displaying KPIs in Streamlit + st.write("### Backtesting Metrics") + col1, col2, col3, col4, col5, col6 = st.columns(6) + col1.metric(label="Net PNL (Quote)", value=f"{net_pnl_quote:.2f}", delta=f"{net_pnl:.2%}") + col2.metric(label="Max Drawdown (USD)", value=f"{max_drawdown_usd:.2f}", delta=f"{max_drawdown_pct:.2%}") + col3.metric(label="Total Volume (Quote)", value=f"{total_volume:.2f}") + col4.metric(label="Sharpe Ratio", value=f"{sharpe_ratio:.2f}") + col5.metric(label="Profit Factor", value=f"{profit_factor:.2f}") + col6.metric(label="Total Executors with Position", value=total_executors_with_position) + + +def render_accuracy_metrics(summary_results): + accuracy = summary_results.get('accuracy', 0) + total_long = summary_results.get('total_long', 0) + total_short = summary_results.get('total_short', 0) + accuracy_long = summary_results.get('accuracy_long', 0) + accuracy_short = summary_results.get('accuracy_short', 0) + + st.write("#### Accuracy Metrics") + # col1, col2, col3, col4, col5 = st.columns(5) + st.metric(label="Global Accuracy", value=f"{accuracy:.2%}") + st.metric(label="Total Long", value=total_long) + st.metric(label="Total Short", value=total_short) + st.metric(label="Accuracy Long", value=f"{accuracy_long:.2%}") + st.metric(label="Accuracy Short", value=f"{accuracy_short:.2%}") + +def render_accuracy_metrics2(summary_results): + accuracy = summary_results.get('accuracy', 0) + total_long = summary_results.get('total_long', 0) + total_short = summary_results.get('total_short', 0) + accuracy_long = summary_results.get('accuracy_long', 0) + accuracy_short = summary_results.get('accuracy_short', 0) + + st.write("#### Accuracy Metrics") + col1, col2, col3, col4, col5 = st.columns(5) + col1.metric(label="Global Accuracy", value=f"{accuracy}:.2%") + col2.metric(label="Total Long", value=total_long) + col3.metric(label="Total Short", value=total_short) + col4.metric(label="Accuracy Long", value=f"{accuracy_long:.2%}") + col5.metric(label="Accuracy Short", value=f"{accuracy_short:.2%}") + + +def render_close_types(summary_results): + st.write("#### Close Types") + close_types = summary_results.get('close_types', {}) + st.metric(label="TAKE PROFIT", value=f"{close_types.get('TAKE_PROFIT', 0)}") + st.metric(label="STOP LOSS", value=f"{close_types.get('STOP_LOSS', 0)}") + st.metric(label="TIME LIMIT", value=f"{close_types.get('TIME_LIMIT', 0)}") + st.metric(label="EARLY STOP", value=f"{close_types.get('EARLY_STOP', 0)}") diff --git a/frontend/visualization/pnl.py b/frontend/visualization/pnl.py index 643c610..11f17f6 100644 --- a/frontend/visualization/pnl.py +++ b/frontend/visualization/pnl.py @@ -10,6 +10,6 @@ def get_pnl_trace(executors): x=pd.to_datetime([e.close_timestamp for e in executors], unit="ms"), y=cum_pnl, mode='lines', - line=dict(color='blue', width=2, dash="dash"), + line=dict(color='gold', width=2, dash="dash"), name='Cumulative PNL' ) From d52af90ae9c809074f14aae1627154e2b56df61d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 01:10:00 -0400 Subject: [PATCH 070/139] (feat) reduce the height of the graph --- frontend/visualization/executors_distribution.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/visualization/executors_distribution.py b/frontend/visualization/executors_distribution.py index 38f9600..61c699c 100644 --- a/frontend/visualization/executors_distribution.py +++ b/frontend/visualization/executors_distribution.py @@ -61,6 +61,7 @@ def create_executors_distribution_traces(config): yaxis_title="Order Amount (Quote)", bargap=0.1, # Adjust the gap between the bars barmode='relative', # Stack the bars on top of each other - showlegend=True + showlegend=True, + height=600 ) return fig From 1caaa36b457fef6bcdde6a4df36f4fb889a723ad Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 15:20:18 -0400 Subject: [PATCH 071/139] (feat) revert changes in environment --- environment_conda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment_conda.yml b/environment_conda.yml index 6cabb9a..d31bbbb 100644 --- a/environment_conda.yml +++ b/environment_conda.yml @@ -8,7 +8,7 @@ dependencies: - pydantic=1.9.* - pip - pip: - - /Users/dardonacci/Documents/work/hummingbot/dist/hummingbot-20240429-cp310-cp310-macosx_11_0_arm64.whl + - hummingbot - streamlit - watchdog - python-dotenv From ad6df97f326b80f0fa6787f6c93e211075e2024c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 15:20:30 -0400 Subject: [PATCH 072/139] (feat) improve visualization of executors --- frontend/visualization/executors.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py index ef00a2d..8b006e9 100644 --- a/frontend/visualization/executors.py +++ b/frontend/visualization/executors.py @@ -2,6 +2,8 @@ import plotly.graph_objects as go import pandas as pd from decimal import Decimal +from hummingbot.connector.connector_base import TradeType + def add_executors_trace(fig, executors, row, col): for executor in executors: @@ -9,15 +11,16 @@ def add_executors_trace(fig, executors, row, col): entry_price = executor.custom_info["current_position_average_price"] exit_time = pd.to_datetime(executor.close_timestamp, unit='ms') exit_price = executor.custom_info["close_price"] + name = "Buy Executor" if executor.config.side == TradeType.BUY else "Sell Executor" if executor.filled_amount_quote == 0: - fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', - line=dict(color='blue', width=2, dash="dash"), name='Buy Entry/Exit'), row=row, col=col) + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, entry_price], mode='lines', + line=dict(color='blue', width=2, dash="dash"), name=name), row=row, col=col) else: if executor.net_pnl_quote > Decimal(0): fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', - line=dict(color='green', width=2), name='Buy Entry/Exit'), row=row, col=col) + line=dict(color='green', width=2), name=name), row=row, col=col) else: fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', - line=dict(color='red', width=2), name='Sell Entry/Exit'), row=row, col=col) + line=dict(color='red', width=2), name=name), row=row, col=col) return fig From 02a078b529a9521ab8a27aed14fa29d2ebd7b046 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Fri, 17 May 2024 15:26:32 -0400 Subject: [PATCH 073/139] (feat) remove not necessary libraries --- environment_conda.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/environment_conda.yml b/environment_conda.yml index d31bbbb..e53657c 100644 --- a/environment_conda.yml +++ b/environment_conda.yml @@ -19,14 +19,10 @@ 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 From ef48c8fa984dbcb229026bc8a826a070f7789e37 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 00:12:39 -0400 Subject: [PATCH 074/139] (feat) add upload config to backend api --- frontend/components/save_config.py | 22 ++++++++++++++++++++++ frontend/pages/config/pmm_simple/app.py | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 frontend/components/save_config.py diff --git a/frontend/components/save_config.py b/frontend/components/save_config.py new file mode 100644 index 0000000..89896d7 --- /dev/null +++ b/frontend/components/save_config.py @@ -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!") diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index 164ab9d..5040909 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -1,6 +1,7 @@ import streamlit as st from backend.services.backend_api_client import BackendAPIClient from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT +from frontend.components.save_config import render_save_config # Import submodules from frontend.pages.config.pmm_simple.user_inputs import user_inputs @@ -24,7 +25,6 @@ st.plotly_chart(fig, use_container_width=True) bt_results = backtesting_section(inputs, backend_api_client) if bt_results: - st.write("---") fig = create_backtesting_figure( df=bt_results["processed_data"], executors=bt_results["executors"], @@ -37,3 +37,5 @@ if bt_results: render_accuracy_metrics(bt_results["results"]) st.write("---") render_close_types(bt_results["results"]) +st.write("---") +render_save_config("pmm_simple", inputs) From e67924b4a0b3a29c5608c5d532a1483fb932eed4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 00:13:37 -0400 Subject: [PATCH 075/139] (feat) minor styling improvements --- CONFIG.py | 4 ++- backend/utils/os_utils.py | 1 + main.py | 65 +++++++++++++++++++-------------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/CONFIG.py b/CONFIG.py index bf3ab9e..5e9ea47 100644 --- a/CONFIG.py +++ b/CONFIG.py @@ -2,6 +2,9 @@ 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"] @@ -16,7 +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 -load_dotenv() BACKEND_API_HOST = os.getenv("BACKEND_API_HOST", "localhost") BACKEND_API_PORT = os.getenv("BACKEND_API_PORT", 8000) \ No newline at end of file diff --git a/backend/utils/os_utils.py b/backend/utils/os_utils.py index ce07dc9..bec7293 100644 --- a/backend/utils/os_utils.py +++ b/backend/utils/os_utils.py @@ -10,6 +10,7 @@ import yaml 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): for file in os.listdir(directory): os.remove(f"{directory}/{file}") diff --git a/main.py b/main.py index 3fcd2fe..eb7766a 100644 --- a/main.py +++ b/main.py @@ -27,8 +27,7 @@ def main_page(): # Page("pages/optimize/optimize.py", "Optimize", "🧪"), # Page("pages/analyze/analyze.py", "Analyze", "🔬"), Section("Community Pages", "👨‍👩‍👧‍👦"), - Page("frontend/pages/performance/strategy_performance/app.py", "Strategy Performance", "🚀"), - Page("frontend/pages/performance/db_inspector/app.py", "DB Inspector", "🔍"), + # Page("frontend/pages/performance/strategy_performance/app.py", "Strategy Performance", "🚀"), Page("frontend/pages/data/token_spreads/app.py", "Token Spreads", "🧙"), Page("frontend/pages/data/tvl_vs_mcap/app.py", "TVL vs Market Cap", "🦉"), ] @@ -96,35 +95,35 @@ def main_page(): st.write("If you encounter any bugs or have suggestions for improvement, please create an issue in the [Hummingbot Dashboard Github](https://github.com/hummingbot/dashboard).") -config = read_yaml_file("credentials.yml") +# config = read_yaml_file("credentials.yml") +# +# if "authenticator" not in st.session_state: +# st.session_state.authenticator = Authenticate( +# config['credentials'], +# config['cookie']['name'], +# config['cookie']['key'], +# config['cookie']['expiry_days'], +# config['preauthorized'] +# ) -if "authenticator" not in st.session_state: - st.session_state.authenticator = Authenticate( - config['credentials'], - config['cookie']['name'], - config['cookie']['key'], - config['cookie']['expiry_days'], - config['preauthorized'] - ) - -if not AUTH_SYSTEM_ENABLED: - main_page() -elif st.session_state["authentication_status"]: - config["credentials"] = st.session_state.authenticator_handler.credentials - dump_dict_to_yaml(config, "credentials.yml") - with st.sidebar: - st.write(f'Welcome {st.session_state["name"]}!') - st.session_state.authenticator.logout(location='sidebar') # Updated logout call - main_page() -else: - show_pages([ - Page("main.py", "Hummingbot Dashboard", "📊"), - ]) - name, authentication_status, username = st.session_state.authenticator.login(location='main') # Updated login call - if st.session_state["authentication_status"] == False: - st.error('Username/password is incorrect') - elif st.session_state["authentication_status"] == None: - st.warning('Please enter your username and password') - st.write("---") - st.write("If you are pre-authorized, you can login with your pre-authorized mail!") - st.session_state.authenticator.register_user(location='main') # Updated register user call +# if not AUTH_SYSTEM_ENABLED: +main_page() +# elif st.session_state["authentication_status"]: +# config["credentials"] = st.session_state.authenticator_handler.credentials +# dump_dict_to_yaml(config, "credentials.yml") +# with st.sidebar: +# st.write(f'Welcome {st.session_state["name"]}!') +# st.session_state.authenticator.logout(location='sidebar') # Updated logout call +# main_page() +# else: +# show_pages([ +# Page("main.py", "Hummingbot Dashboard", "📊"), +# ]) +# name, authentication_status, username = st.session_state.authenticator.login(location='main') # Updated login call +# if st.session_state["authentication_status"] == False: +# st.error('Username/password is incorrect') +# elif st.session_state["authentication_status"] == None: +# st.warning('Please enter your username and password') +# st.write("---") +# st.write("If you are pre-authorized, you can login with your pre-authorized mail!") +# st.session_state.authenticator.register_user(location='main') # Updated register user call From 072ad3e241bab1de0b5cfb6779df233c61ee5b0a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 00:38:52 -0400 Subject: [PATCH 076/139] (feat) move launch bot to st --- .../pages/orchestration/launch_bot_v2/app.py | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/frontend/pages/orchestration/launch_bot_v2/app.py b/frontend/pages/orchestration/launch_bot_v2/app.py index a2c885e..304bf20 100644 --- a/frontend/pages/orchestration/launch_bot_v2/app.py +++ b/frontend/pages/orchestration/launch_bot_v2/app.py @@ -1,10 +1,4 @@ -from types import SimpleNamespace - -import streamlit as st -from streamlit_elements import elements, mui - -from frontend.components.dashboard import Dashboard -from frontend.components.launch_strategy_v2 import LaunchStrategyV2 +from frontend.components.deploy_v2_with_controllers import LaunchV2WithControllers from frontend.st_utils import initialize_st_page CARD_WIDTH = 6 @@ -14,25 +8,5 @@ NUM_CARD_COLS = 2 initialize_st_page(title="Launch Bot", icon="🙌") -def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_HEIGHT, card_height: int = CARD_WIDTH): - rows = n_cards // cols + 1 - x_y = [(x * card_width, y * card_height) for x in range(cols) for y in range(rows)] - return sorted(x_y, key=lambda x: (x[1], x[0])) - - -if "launch_bots_board" not in st.session_state: - board = Dashboard() - launch_bots_board = SimpleNamespace( - dashboard=board, - launch_bot=LaunchStrategyV2(board, 0, 0, 12, 10), - ) - st.session_state.launch_bots_board = launch_bots_board - -else: - launch_bots_board = st.session_state.launch_bots_board - - -with elements("create_bot"): - with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): - with launch_bots_board.dashboard(): - launch_bots_board.launch_bot() +launcher = LaunchV2WithControllers() +launcher() From 88faa089e4c23a13737ec2ac8450e80c25c5c51d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:15:24 -0400 Subject: [PATCH 077/139] (feat) remove compressing and cleaning from stop command --- frontend/components/bot_performance_card.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/components/bot_performance_card.py b/frontend/components/bot_performance_card.py index bcc4c11..74d2fa7 100644 --- a/frontend/components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -14,8 +14,6 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B def stop_bot(bot_name): backend_api_client.stop_bot(bot_name) - backend_api_client.stop_container(bot_name) - backend_api_client.remove_container(bot_name) def archive_bot(bot_name): From 55d437e2329280b5826275a50464524f44cfe26c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:15:36 -0400 Subject: [PATCH 078/139] (feat) add dca distribution inputs --- frontend/components/dca_distribution.py | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 frontend/components/dca_distribution.py diff --git a/frontend/components/dca_distribution.py b/frontend/components/dca_distribution.py new file mode 100644 index 0000000..f7851f1 --- /dev/null +++ b/frontend/components/dca_distribution.py @@ -0,0 +1,62 @@ +import streamlit as st +from hummingbot.connector.connector_base import OrderType + +from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs + + +def get_dca_distribution_inputs(): + with st.expander("DCA Distribution", expanded=True): + st.header("DCA Settings") + buy_order_levels = st.number_input("Number of Order Levels", min_value=1, value=3, + help="Enter the number of order levels (e.g., 2).") + + 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) + 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) + + # 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 c1: + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=2.0, 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=1.0, 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=60 * 6, + 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=1.8, + 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=0.2, step=0.1, + help="Enter the trailing stop delta as a percentage (e.g., 0.3 for 0.3%).") / 100 + + return { + "dca_spreads": 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 + }, + } From 07cebfa439afa41f9d6ff43fbc72584e4441889b Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:15:54 -0400 Subject: [PATCH 079/139] (feat) adapt script file name --- frontend/components/launch_strategy_v2.py | 32 +---------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/frontend/components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py index 1de6430..4434aa2 100644 --- a/frontend/components/launch_strategy_v2.py +++ b/frontend/components/launch_strategy_v2.py @@ -59,7 +59,7 @@ class LaunchStrategyV2(Dashboard.Item): "candles_config": [], "controllers_config": self._controller_config_selected, "config_update_interval": 20, - "script_file_name": "v2_generic_with_cash_out.py", + "script_file_name": "v2_with_controllers.py", "time_to_cash_out": None, } } @@ -158,33 +158,3 @@ class LaunchStrategyV2(Dashboard.Item): disableSelectionOnClick=True, onSelectionModelChange=self._handle_row_selection, ) - # with mui.Grid(item=True, xs=8): - # try: - # encoder_decoder = ConfigEncoderDecoder(TradeType, OrderType, PositionMode) - # data = [] - # for config in self._controller_configs_available: - # decoded_config = encoder_decoder.yaml_load(config) - # data.append({"id": config.split("/")[-1], "strategy_name": decoded_config["strategy_name"], - # "exchange": decoded_config["exchange"], "trading_pair": decoded_config["trading_pair"]}) - # except json.JSONDecodeError: - # data = self.DEFAULT_ROWS - - # 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("Data grid") - # - # 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, - # ) - # From 84e60ab8d09e3517bde598a99d8fe94bd56b31cb Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:16:49 -0400 Subject: [PATCH 080/139] (feat) add deploy using streamlit --- .../components/deploy_v2_with_controllers.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 frontend/components/deploy_v2_with_controllers.py diff --git a/frontend/components/deploy_v2_with_controllers.py b/frontend/components/deploy_v2_with_controllers.py new file mode 100644 index 0000000..1c0cba4 --- /dev/null +++ b/frontend/components/deploy_v2_with_controllers.py @@ -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 = "hummingbot/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_generic_with_cash_out.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_available_credentials() + self._credentials = st.selectbox("Credentials", available_credentials, index=0) + with c4: + deploy_button = st.button("Deploy Bot") + if deploy_button: + self.launch_new_bot() \ No newline at end of file From 47e81413e1f4f36485d6b530686eff9e4bdfaaa9 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:17:07 -0400 Subject: [PATCH 081/139] (feat) refactor dman maker v2 --- frontend/pages/config/dman_maker_v2/app.py | 423 ++---------------- .../pages/config/dman_maker_v2/user_inputs.py | 37 ++ 2 files changed, 82 insertions(+), 378 deletions(-) create mode 100644 frontend/pages/config/dman_maker_v2/user_inputs.py diff --git a/frontend/pages/config/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py index 730e49a..0053c98 100644 --- a/frontend/pages/config/dman_maker_v2/app.py +++ b/frontend/pages/config/dman_maker_v2/app.py @@ -1,402 +1,69 @@ import streamlit as st -from plotly.subplots import make_subplots -import plotly.graph_objects as go -from decimal import Decimal -import yaml from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from backend.services.backend_api_client import BackendAPIClient -from frontend.components.st_inputs import normalize, distribution_inputs, get_distribution +from frontend.components.backtesting import backtesting_section +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.") st.write("---") -c1, c2, c3, c4, c5, c6, c7, c8, c9 = st.columns(9) +inputs = user_inputs() +with st.expander("Executor Distribution:", expanded=True): + fig = create_executors_distribution_traces(inputs) + st.plotly_chart(fig, use_container_width=True) -with c1: - connector = st.text_input("Connector", value="binance_perpetual") -with c2: - trading_pair = st.text_input("Trading Pair", value="WLD-USDT") -with c3: - leverage = st.number_input("Leverage", value=20) -with c4: - total_amount_quote = st.number_input("Total amount of quote", value=1000) -with c5: - position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0) -with c6: - cooldown_time = st.number_input("Cooldown Time", value=60) -with c7: - executor_refresh_time = st.number_input("Refresh Time (minutes)", value=60) -with c8: - top_executor_refresh_time = st.number_input("Top Refresh Time (seconds)", value=60) -with c9: - executor_activation_bounds = st.number_input("Activation Bounds (%)", value=0.1) +dca_inputs = get_dca_distribution_inputs() -# Executors configuration -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=2) -with col_sell: - st.header("Sell Order Settings") - sell_order_levels = st.number_input("Number of Sell Order Levels", min_value=1, value=2) - -col_buy_spreads, col_buy_amounts, col_sell_spreads, col_sell_amounts = st.columns(4) - -# Inputs for buy orders -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) -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) -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) -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) - -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 = normalize( - 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 = normalize( - 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)) - -all_orders_amount_normalized = normalize(buy_amount_distributions + sell_amount_distributions) -buy_order_amounts_quote = [amount * total_amount_quote for amount in - all_orders_amount_normalized[:buy_order_levels]] -sell_order_amounts_quote = [amount * total_amount_quote for amount in - all_orders_amount_normalized[buy_order_levels:]] - -# Initialize your figure with a dark theme -fig = make_subplots(specs=[[{"secondary_y": True}]]) -fig.update_layout( - template="plotly_dark", - plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background - paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper - title="Market Maker Order Distribution", - xaxis_title="Spread (%)", - yaxis_title="Amount (Quote)", - legend_title="Order Type", - font=dict(color='white', size=12) # Consistent font color and size -) - -# Define colors for buy and sell orders -colors = { - 'buy': '#32CD32', # Green for buy orders - 'sell': '#FF6347' # Tomato red for sell orders -} - -# Add traces for buy and sell orders -# Buy orders on the negative side of x-axis -fig.add_trace(go.Bar( - x=[-dist for dist in buy_spread_distributions], - y=buy_order_amounts_quote, - name='Buy Orders', - marker_color=colors['buy'], - width=[0.2] * buy_order_levels # Adjust the width of the bars as needed -), secondary_y=False) - -# Sell orders on the positive side of x-axis -fig.add_trace(go.Bar( - x=sell_spread_distributions, - y=sell_order_amounts_quote, - name='Sell Orders', - marker_color=colors['sell'], - width=[0.2] * buy_order_levels # Adjust the width of the bars as needed - -), secondary_y=False) - -# Annotations can be added for each bar to display the value on top -for i, value in enumerate(buy_order_amounts_quote): - fig.add_annotation( - x=-buy_spread_distributions[i], - y=value + 10, # Offset the text slightly above the bar - text=str(round(value, 2)), - showarrow=False, - font=dict(color=colors['buy'], size=10) - ) - -for i, value in enumerate(sell_order_amounts_quote): - fig.add_annotation( - x=sell_spread_distributions[i], - y=value + 10, # Offset the text slightly above the bar - text=str(round(value, 2)), - showarrow=False, - font=dict(color=colors['sell'], size=10) - ) - -# Optional: Add horizontal line or extra annotations if needed -# e.g., for average, threshold, or specific markers - -# Update the layout to make it responsive and visually appealing -fig.update_layout( - height=600, - width=800, - margin=dict(l=20, r=20, t=50, b=20) -) - -# Display the figure in Streamlit -st.plotly_chart(fig, use_container_width=True) - -# Layout in columns -col_dca_quote, col_tp_sl, col_levels, col_spread_dist, col_amount_dist = st.columns([1, 1, 1, 2, 2]) +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)] -with col_dca_quote: +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 = buy_order_amounts_quote[int(level)] + dca_amount = inputs["buy_amounts_pct"][int(level)] * inputs["total_amount_quote"] else: - dca_amount = sell_order_amounts_quote[int(level)] - st.write(f"DCA Amount: {dca_amount:.2f}") - -with col_tp_sl: - 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) - sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=3.0, step=0.1) - -with col_levels: - n_levels = st.number_input("Number of Levels", min_value=1, value=5) - tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=5.0, step=0.1) - time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=60 * 6) - -# Spread and Amount Distributions -spread_dist_type, spread_start, spread_base, spread_scaling, spread_step, spread_ratio, manual_spreads = distribution_inputs( - col_spread_dist, "Spread", n_levels) -amount_dist_type, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, manual_amounts = distribution_inputs( - col_amount_dist, "Amount", n_levels) - -spread_distribution = get_distribution(spread_dist_type, n_levels, spread_start, spread_base, spread_scaling, - spread_step, spread_ratio, manual_spreads) -amount_distribution = normalize( - get_distribution(amount_dist_type, n_levels, amount_start, amount_base, amount_scaling, amount_step, amount_ratio, - manual_amounts)) -dca_order_amounts = [Decimal(amount_dist * dca_amount) for amount_dist in amount_distribution] -dca_spreads = [Decimal(spread - spread_distribution[0]) for spread in spread_distribution] - -break_even_values = [] -take_profit_values = [] -for level in range(n_levels): - dca_spreads_normalized = [Decimal(spread) + Decimal(0.01) for spread in dca_spreads[:level + 1]] - amounts = dca_order_amounts[:level + 1] - break_even = (sum([spread * amount for spread, amount in zip(dca_spreads_normalized, amounts)]) / sum( - amounts)) - Decimal(0.01) - break_even_values.append(break_even) - take_profit_values.append(break_even - Decimal(ts_ap)) - -accumulated_amount = [sum(dca_order_amounts[:i + 1]) for i in range(len(dca_order_amounts))] - - -def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): - unrealized_pnl = [] - for i in range(len(spreads)): - distance = abs(spreads[i] - break_even_values[i]) - pnl = accumulated_amount[i] * distance / 100 # PNL calculation - unrealized_pnl.append(pnl) - return unrealized_pnl - - -# Calculate unrealized PNL -cum_unrealized_pnl = calculate_unrealized_pnl(dca_spreads, break_even_values, accumulated_amount) - -tech_colors = { - 'spread': '#00BFFF', # Deep Sky Blue - 'break_even': '#FFD700', # Gold - 'take_profit': '#32CD32', # Green - 'order_amount': '#1E90FF', # Dodger Blue - 'cum_amount': '#4682B4', # Steel Blue - 'stop_loss': '#FF0000', # Red -} - -# Create Plotly figure with secondary y-axis and a dark theme -fig = make_subplots(specs=[[{"secondary_y": True}]]) -fig.update_layout(template="plotly_dark") - -# Update the Scatter Plots and Horizontal Lines -fig.add_trace(go.Scatter(x=list(range(len(dca_spreads))), y=dca_spreads, name='Spread (%)', mode='lines+markers', - line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) -fig.add_trace( - go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', mode='lines+markers', - line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) -fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', - mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), secondary_y=False) - -# Add the new Bar Plot for Cumulative Unrealized PNL -fig.add_trace(go.Bar( - x=list(range(len(cum_unrealized_pnl))), - y=cum_unrealized_pnl, - text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], - textposition='auto', - textfont=dict(color='white', size=12), - name='Cum Unrealized PNL', - marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed -), secondary_y=True) - -fig.add_trace(go.Bar( - x=list(range(len(dca_order_amounts))), - y=dca_order_amounts, - text=[f"{amt:.2f}" for amt in dca_order_amounts], # List comprehension to format text labels - textposition='auto', - textfont=dict( - color='white', - size=12 - ), - name='Order Amount', - marker=dict(color=tech_colors['order_amount'], opacity=0.5), -), secondary_y=True) - -# Modify the Bar Plot for Accumulated Amount -fig.add_trace(go.Bar( - x=list(range(len(accumulated_amount))), - y=accumulated_amount, - text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels - textposition='auto', - textfont=dict( - color='white', - size=12 - ), - name='Cum Amount', - marker=dict(color=tech_colors['cum_amount'], opacity=0.5), -), secondary_y=True) - -# Add Horizontal Lines for Last Breakeven Price and Stop Loss Level -last_break_even = break_even_values[-1] -stop_loss_value = last_break_even + Decimal(sl) -# Horizontal Lines for Last Breakeven and Stop Loss -fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", - annotation_position="top left", line_color=tech_colors['break_even']) -fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", - annotation_position="bottom right", line_color=tech_colors['stop_loss']) - -# Update Annotations for Spread and Break Even -for i, (spread, be_value, tp_value) in enumerate(zip(dca_spreads, break_even_values, take_profit_values)): - fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, - font=dict(color=tech_colors['spread'])) - fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, - font=dict(color=tech_colors['break_even'])) - fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, - font=dict(color=tech_colors['take_profit'])) -# Update Layout with a Dark Theme -fig.update_layout( - title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", - xaxis_title="Order Level", - yaxis_title="Spread (%)", - yaxis2_title="Amount (Quote)", - height=800, - width=1800, - plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background - paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper - font=dict(color='white') # Font color -) - -# Calculate metrics -dca_max_loss = dca_amount * Decimal(sl / 100) -profit_per_level = [cum_amount * Decimal(ts_ap / 100) for cum_amount in accumulated_amount] -loots_to_recover = [dca_max_loss / profit for profit in profit_per_level] - -# Define a consistent annotation size and maximum value for the secondary y-axis -circle_text = "●" # Unicode character for a circle -max_secondary_value = max(max(accumulated_amount), max(dca_order_amounts), - max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data - -# Determine an appropriate y-offset for annotations -y_offset_secondary = max_secondary_value * Decimal( - 0.1) # Adjusts the height relative to the maximum value on the secondary y-axis - -# Add annotations to the Plotly figure for the secondary y-axis -for i, loot in enumerate(loots_to_recover): - fig.add_annotation( - x=i, - y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset - text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines - showarrow=False, - font=dict(size=16, color='purple'), - xanchor="center", # Centers the text above the x coordinate - yanchor="bottom", # Anchors the text at its bottom to avoid overlapping - align="center", - yref="y2" # Reference the secondary y-axis - ) -# Add Max Loss Metric as an Annotation -dca_max_loss_annotation_text = f"DCA Max Loss (Quote): {dca_max_loss:.2f}" -fig.add_annotation( - x=max(len(dca_spreads), len(break_even_values)) - 1, # Positioning the annotation to the right - text=dca_max_loss_annotation_text, - showarrow=False, - font=dict(size=20, color='white'), - bgcolor='red', # Red background for emphasis - xanchor="left", - yanchor="top", - yref="y2" # Reference the secondary y-axis -) - -st.write("---") - -# Display in Streamlit -st.plotly_chart(fig) -c1, c2, c3 = st.columns([2, 2, 1]) -with c1: - config_base = st.text_input("Config Base", value=f"dman_maker_v2-{connector}-{trading_pair.split('-')[0]}") + dca_amount = inputs["sell_amounts_pct"][int(level)] * inputs["total_amount_quote"] with c2: - config_tag = st.text_input("Config Tag", value="1.1") + 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) -id = f"{config_base}-{config_tag}" -config = { - "id": id.lower(), - "controller_name": "dman_maker_v2", - "controller_type": "market_making", - "manual_kill_switch": None, - "candles_config": [], - "connector_name": connector, - "trading_pair": trading_pair, - "total_amount_quote": total_amount_quote, - "buy_spreads": [Decimal(spread / 100) for spread in buy_spread_distributions], - "sell_spreads": [Decimal(spread / 100) for spread in sell_spread_distributions], - "buy_amounts_pct": buy_order_amounts_quote, - "sell_amounts_pct": sell_order_amounts_quote, - "executor_refresh_time": executor_refresh_time * 60, - "cooldown_time": cooldown_time, - "leverage": leverage, - "position_mode": position_mode, - "stop_loss": sl / 100, - "take_profit": tp / 100, - "time_limit": time_limit * 60, - "take_profit_order_type": 2, - "trailing_stop": { - "activation_price": ts_ap / 100, - "trailing_delta": ts_delta / 100}, - "dca_amounts": dca_order_amounts, - "dca_spreads": [spread / 100 for spread in dca_spreads], - "top_executor_refresh_time": top_executor_refresh_time, - "executor_activation_bounds": [executor_activation_bounds / 100], - } -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!") \ No newline at end of file +# Combine inputs and dca_inputs into final config +config = {**inputs, **dca_inputs} +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", inputs) \ No newline at end of file diff --git a/frontend/pages/config/dman_maker_v2/user_inputs.py b/frontend/pages/config/dman_maker_v2/user_inputs.py new file mode 100644 index 0000000..cabc893 --- /dev/null +++ b/frontend/pages/config/dman_maker_v2/user_inputs.py @@ -0,0 +1,37 @@ +import streamlit as st + +from frontend.components.executors_distribution import get_executors_distribution_inputs +from frontend.components.market_making_general_inputs import get_market_making_general_inputs + + +def user_inputs(): + connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time = get_market_making_general_inputs() + buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() + with st.expander("Custom D-Man Maker V2 Settings"): + c1, c2 = st.columns(2) + with c1: + top_executor_refresh_time = st.number_input("Top Refresh Time (minutes)", value=60) * 60 + with c2: + executor_activation_bounds = st.number_input("Activation Bounds (%)", value=0.1) / 100 + # Create the config + config = { + "controller_name": "dman_maker_v2", + "controller_type": "market_making", + "manual_kill_switch": None, + "candles_config": [], + "connector_name": connector_name, + "trading_pair": trading_pair, + "total_amount_quote": total_amount_quote, + "buy_spreads": buy_spread_distributions, + "sell_spreads": sell_spread_distributions, + "buy_amounts_pct": buy_order_amounts_pct, + "sell_amounts_pct": sell_order_amounts_pct, + "executor_refresh_time": executor_refresh_time, + "cooldown_time": cooldown_time, + "leverage": leverage, + "position_mode": position_mode, + "top_executor_refresh_time": top_executor_refresh_time, + "executor_activation_bounds": [executor_activation_bounds] + } + + return config From d93dd73f9db492cab9ce5f811cd726e8de145b7e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:17:26 -0400 Subject: [PATCH 082/139] (feat) refactor deploy pages --- .../pages/orchestration/launch_bot_v2/app.py | 25 ++++++++++++++++--- .../orchestration/launch_bot_v2_st/README.md | 19 ++++++++++++++ .../launch_bot_v2_st/__init__.py | 0 .../orchestration/launch_bot_v2_st/app.py | 9 +++++++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 frontend/pages/orchestration/launch_bot_v2_st/README.md create mode 100644 frontend/pages/orchestration/launch_bot_v2_st/__init__.py create mode 100644 frontend/pages/orchestration/launch_bot_v2_st/app.py diff --git a/frontend/pages/orchestration/launch_bot_v2/app.py b/frontend/pages/orchestration/launch_bot_v2/app.py index 304bf20..bf1e15b 100644 --- a/frontend/pages/orchestration/launch_bot_v2/app.py +++ b/frontend/pages/orchestration/launch_bot_v2/app.py @@ -1,4 +1,10 @@ -from frontend.components.deploy_v2_with_controllers import LaunchV2WithControllers +from types import SimpleNamespace + +import streamlit as st +from streamlit_elements import elements, mui + +from frontend.components.dashboard import Dashboard +from frontend.components.launch_strategy_v2 import LaunchStrategyV2 from frontend.st_utils import initialize_st_page CARD_WIDTH = 6 @@ -7,6 +13,19 @@ NUM_CARD_COLS = 2 initialize_st_page(title="Launch Bot", icon="🙌") +if "launch_bots_board" not in st.session_state: + board = Dashboard() + launch_bots_board = SimpleNamespace( + dashboard=board, + launch_bot=LaunchStrategyV2(board, 0, 0, 12, 10), + ) + st.session_state.launch_bots_board = launch_bots_board -launcher = LaunchV2WithControllers() -launcher() +else: + launch_bots_board = st.session_state.launch_bots_board + + +with elements("create_bot"): + with mui.Paper(elevation=3, style={"padding": "2rem"}, spacing=[2, 2], container=True): + with launch_bots_board.dashboard(): + launch_bots_board.launch_bot() diff --git a/frontend/pages/orchestration/launch_bot_v2_st/README.md b/frontend/pages/orchestration/launch_bot_v2_st/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/frontend/pages/orchestration/launch_bot_v2_st/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/frontend/pages/orchestration/launch_bot_v2_st/__init__.py b/frontend/pages/orchestration/launch_bot_v2_st/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/orchestration/launch_bot_v2_st/app.py b/frontend/pages/orchestration/launch_bot_v2_st/app.py new file mode 100644 index 0000000..50c2c08 --- /dev/null +++ b/frontend/pages/orchestration/launch_bot_v2_st/app.py @@ -0,0 +1,9 @@ +from frontend.components.deploy_v2_with_controllers import LaunchV2WithControllers +from frontend.st_utils import initialize_st_page + + +initialize_st_page(title="Launch Bot ST", icon="🙌") + + +launcher = LaunchV2WithControllers() +launcher() From 6ec8c780379b5bd15b6ddde5a3ef8eec666ae117 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:17:35 -0400 Subject: [PATCH 083/139] (feat) add dca builder visualization --- frontend/visualization/dca_builder.py | 160 ++++++++++++++++++++++++++ frontend/visualization/theme.py | 12 +- 2 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 frontend/visualization/dca_builder.py diff --git a/frontend/visualization/dca_builder.py b/frontend/visualization/dca_builder.py new file mode 100644 index 0000000..501b5fc --- /dev/null +++ b/frontend/visualization/dca_builder.py @@ -0,0 +1,160 @@ +from decimal import Decimal +import plotly.graph_objects as go +from plotly.subplots import make_subplots +import frontend.visualization.theme as theme + + +def calculate_unrealized_pnl(spreads, break_even_values, accumulated_amount): + unrealized_pnl = [] + for i in range(len(spreads)): + distance = abs(spreads[i] - break_even_values[i]) + pnl = accumulated_amount[i] * distance / 100 # PNL calculation + unrealized_pnl.append(pnl) + return unrealized_pnl + + +def create_dca_graph(dca_inputs, dca_amount): + tech_colors = theme.get_color_scheme() + dca_order_amounts = [amount_dist * dca_amount for amount_dist in dca_inputs["dca_amounts"]] + n_levels = len(dca_inputs["dca_spreads"]) + + break_even_values = [] + take_profit_values = [] + for level in range(n_levels): + dca_spreads_normalized = [spread + 0.01 for spread in dca_inputs["dca_spreads"][:level + 1]] + amounts = dca_order_amounts[:level + 1] + break_even = (sum([spread * amount for spread, amount in zip(dca_spreads_normalized, amounts)]) / sum( + amounts)) - 0.01 + break_even_values.append(break_even) + take_profit_values.append(break_even - dca_inputs["take_profit"] * 100) + + accumulated_amount = [sum(dca_order_amounts[:i + 1]) for i in range(len(dca_order_amounts))] + + # Calculate unrealized PNL + cum_unrealized_pnl = calculate_unrealized_pnl(dca_inputs["dca_spreads"], break_even_values, accumulated_amount) + + # Create Plotly figure with secondary y-axis and a dark theme + fig = make_subplots(specs=[[{"secondary_y": True}]]) + fig.update_layout(template="plotly_dark") + + # Update the Scatter Plots and Horizontal Lines + fig.add_trace( + go.Scatter(x=list(range(len(dca_inputs["dca_spreads"]))), y=dca_inputs["dca_spreads"], name='Spread (%)', + mode='lines+markers', + line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) + fig.add_trace( + go.Scatter(x=list(range(len(break_even_values))), y=break_even_values, name='Break Even (%)', + mode='lines+markers', + line=dict(width=3, color=tech_colors['break_even'])), secondary_y=False) + fig.add_trace(go.Scatter(x=list(range(len(take_profit_values))), y=take_profit_values, name='Take Profit (%)', + mode='lines+markers', line=dict(width=3, color=tech_colors['take_profit'])), + secondary_y=False) + + # Add the new Bar Plot for Cumulative Unrealized PNL + fig.add_trace(go.Bar( + x=list(range(len(cum_unrealized_pnl))), + y=cum_unrealized_pnl, + text=[f"{pnl:.2f}" for pnl in cum_unrealized_pnl], + textposition='auto', + textfont=dict(color='white', size=12), + name='Cum Unrealized PNL', + marker=dict(color='#FFA07A', opacity=0.6) # Light Salmon color, adjust as needed + ), secondary_y=True) + + fig.add_trace(go.Bar( + x=list(range(len(dca_order_amounts))), + y=dca_order_amounts, + text=[f"{amt:.2f}" for amt in dca_order_amounts], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Order Amount', + marker=dict(color=tech_colors['order_amount'], opacity=0.5), + ), secondary_y=True) + + # Modify the Bar Plot for Accumulated Amount + fig.add_trace(go.Bar( + x=list(range(len(accumulated_amount))), + y=accumulated_amount, + text=[f"{amt:.2f}" for amt in accumulated_amount], # List comprehension to format text labels + textposition='auto', + textfont=dict( + color='white', + size=12 + ), + name='Cum Amount', + marker=dict(color=tech_colors['cum_amount'], opacity=0.5), + ), secondary_y=True) + + # Add Horizontal Lines for Last Breakeven Price and Stop Loss Level + last_break_even = break_even_values[-1] + stop_loss_value = last_break_even + dca_inputs["stop_loss"] * 100 + # Horizontal Lines for Last Breakeven and Stop Loss + fig.add_hline(y=last_break_even, line_dash="dash", annotation_text=f"Global Break Even: {last_break_even:.2f} (%)", + annotation_position="top left", line_color=tech_colors['break_even']) + fig.add_hline(y=stop_loss_value, line_dash="dash", annotation_text=f"Stop Loss: {stop_loss_value:.2f} (%)", + annotation_position="bottom right", line_color=tech_colors['stop_loss']) + + # Update Annotations for Spread and Break Even + for i, (spread, be_value, tp_value) in enumerate( + zip(dca_inputs["dca_spreads"], break_even_values, take_profit_values)): + fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, + font=dict(color=tech_colors['spread'])) + fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, + font=dict(color=tech_colors['break_even'])) + fig.add_annotation(x=i, y=tp_value, text=f"{tp_value:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, + font=dict(color=tech_colors['take_profit'])) + # Update Layout with a Dark Theme + fig.update_layout( + title="Spread, Accumulated Amount, Break Even, and Take Profit by Order Level", + xaxis_title="Order Level", + yaxis_title="Spread (%)", + yaxis2_title="Amount (Quote)", + height=800, + width=1800, + plot_bgcolor='rgba(0, 0, 0, 0)', # Transparent background + paper_bgcolor='rgba(0, 0, 0, 0.1)', # Lighter shade for the paper + font=dict(color='white') # Font color + ) + + # Calculate metrics + dca_max_loss = dca_amount * dca_inputs["stop_loss"] + profit_per_level = [cum_amount * dca_inputs["take_profit"] for cum_amount in accumulated_amount] + loots_to_recover = [dca_max_loss / profit for profit in profit_per_level] + + # Define a consistent annotation size and maximum value for the secondary y-axis + circle_text = "●" # Unicode character for a circle + max_secondary_value = max(max(accumulated_amount), max(dca_order_amounts), + max(cum_unrealized_pnl)) # Adjust based on your secondary y-axis data + + # Determine an appropriate y-offset for annotations + y_offset_secondary = max_secondary_value * 0.1 # Adjusts the height relative to the maximum value on the secondary y-axis + + # Add annotations to the Plotly figure for the secondary y-axis + for i, loot in enumerate(loots_to_recover): + fig.add_annotation( + x=i, + y=max_secondary_value + y_offset_secondary, # Position above the maximum value using the offset + text=f"{circle_text}
LTR: {round(loot, 2)}", # Circle symbol and loot value in separate lines + showarrow=False, + font=dict(size=16, color='purple'), + xanchor="center", # Centers the text above the x coordinate + yanchor="bottom", # Anchors the text at its bottom to avoid overlapping + align="center", + yref="y2" # Reference the secondary y-axis + ) + # Add Max Loss Metric as an Annotation + dca_max_loss_annotation_text = f"DCA Max Loss (Quote): {dca_max_loss:.2f}" + fig.add_annotation( + x=max(len(dca_inputs["dca_spreads"]), len(break_even_values)) - 1, # Positioning the annotation to the right + text=dca_max_loss_annotation_text, + showarrow=False, + font=dict(size=20, color='white'), + bgcolor='red', # Red background for emphasis + xanchor="left", + yanchor="top", + yref="y2" # Reference the secondary y-axis + ) + return fig diff --git a/frontend/visualization/theme.py b/frontend/visualization/theme.py index 1e2767e..0bbca11 100644 --- a/frontend/visualization/theme.py +++ b/frontend/visualization/theme.py @@ -1,7 +1,6 @@ -def get_default_layout(title, height=800, width=1800): - return { +def get_default_layout(title=None, height=800, width=1800): + layout = { "template": "plotly_dark", - "title": title, "plot_bgcolor": 'rgba(0, 0, 0, 0)', # Transparent background "paper_bgcolor": 'rgba(0, 0, 0, 0.1)', # Lighter shade for the paper "font": {"color": 'white', "size": 12}, # Consistent font color and size @@ -9,7 +8,9 @@ def get_default_layout(title, height=800, width=1800): "width": width, "margin": {"l": 20, "r": 20, "t": 50, "b": 20} } - + if title: + layout["title"] = title + return layout def get_color_scheme(): return { @@ -33,7 +34,4 @@ def get_color_scheme(): 'cum_amount': '#4682B4', # Steel Blue 'stop_loss': '#FF0000', # Red 'cum_unrealized_pnl': '#FFA07A' # Light Salmon - } - - From 259b6eb4ff017198b896263bfcb983a989b10af5 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 18 May 2024 03:17:50 -0400 Subject: [PATCH 084/139] (feat) add hidden st page --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index eb7766a..40a854f 100644 --- a/main.py +++ b/main.py @@ -13,6 +13,7 @@ def main_page(): Section("Bot Orchestration", "🐙"), Page("frontend/pages/orchestration/instances/app.py", "Instances", "🦅"), Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy", "🚀"), + # Page("frontend/pages/orchestration/launch_bot_v2_st/app.py", "Deploy ST", "🙌"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), Section("Config Generator", "🎛️"), Page("frontend/pages/config/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), From cce2078487875fdfd9148d4f7f2be8eb93e95a5c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 20 May 2024 17:57:25 -0500 Subject: [PATCH 085/139] (feat) apply environment changes --- environment_conda.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/environment_conda.yml b/environment_conda.yml index e53657c..c903dd2 100644 --- a/environment_conda.yml +++ b/environment_conda.yml @@ -4,12 +4,11 @@ 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 @@ -26,3 +25,4 @@ dependencies: - st-pages - streamlit-elements==0.1.* - streamlit-authenticator + - pydantic==1.10.4 From 9ee08c5c16332dd0fdab7d3078f2e4db09ed3fcd Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 20 May 2024 17:58:49 -0500 Subject: [PATCH 086/139] (feat) add directional components --- CONFIG.py | 4 +-- .../directional_trading_general_inputs.py | 34 +++++++++++++++++++ frontend/visualization/indicators.py | 21 ++++++++++++ frontend/visualization/signals.py | 22 ++++++++++++ frontend/visualization/utils.py | 3 ++ 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 frontend/components/directional_trading_general_inputs.py create mode 100644 frontend/visualization/indicators.py create mode 100644 frontend/visualization/signals.py create mode 100644 frontend/visualization/utils.py diff --git a/CONFIG.py b/CONFIG.py index 5e9ea47..527f29b 100644 --- a/CONFIG.py +++ b/CONFIG.py @@ -20,5 +20,5 @@ CERTIFIED_STRATEGIES = ["xemm", "cross exchange market making", "pmm", "pure mar AUTH_SYSTEM_ENABLED = False -BACKEND_API_HOST = os.getenv("BACKEND_API_HOST", "localhost") -BACKEND_API_PORT = os.getenv("BACKEND_API_PORT", 8000) \ No newline at end of file +BACKEND_API_HOST = os.getenv("BACKEND_API_HOST", "127.0.0.1") +BACKEND_API_PORT = os.getenv("BACKEND_API_PORT", 8000) diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py new file mode 100644 index 0000000..4742ddb --- /dev/null +++ b/frontend/components/directional_trading_general_inputs.py @@ -0,0 +1,34 @@ +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) + with c1: + connector_name = st.text_input("Connector", value="binance_perpetual", + help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") + candles_connector_name = st.text_input("Candles Connector", value="binance_perpetual", + 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="WLD-USDT", + help="Enter the trading pair to trade on (e.g., WLD-USDT).") + candles_trading_pair = st.text_input("Candles Trading Pair", value="WLD-USDT", + help="Enter the trading pair to get candles for (e.g., WLD-USDT).") + with c3: + leverage = st.number_input("Leverage", value=20, + 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", "5m", "15m", "1h", "4h", "1d"), index=3, + help="Enter the interval for candles (e.g., 1m).") + with c4: + total_amount_quote = st.number_input("Total amount of quote", value=1000, + 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=5, + help="Enter the maximum number of executors per side (e.g., 5).") + with c6: + cooldown_time = st.number_input("Cooldown Time (minutes)", value=60, + help="Specify the cooldown time in minutes (e.g., 60).") * 60 + with c7: + position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0, + 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 diff --git a/frontend/visualization/indicators.py b/frontend/visualization/indicators.py new file mode 100644 index 0000000..086fd90 --- /dev/null +++ b/frontend/visualization/indicators.py @@ -0,0 +1,21 @@ +import pandas_ta as ta # noqa: F401 +import plotly.graph_objects as go + +from frontend.visualization import theme + + +def get_bbands_traces(candles, bb_length, bb_std): + tech_colors = theme.get_color_scheme() + candles.ta.bbands(length=bb_length, std=bb_std, append=True) + bb_lower = f'BBL_{bb_length}_{bb_std}' + bb_middle = f'BBM_{bb_length}_{bb_std}' + bb_upper = f'BBU_{bb_length}_{bb_std}' + traces = [ + go.Scatter(x=candles.index, y=candles[bb_upper], line=dict(color=tech_colors['upper_band']), + name='Upper Band'), + go.Scatter(x=candles.index, y=candles[bb_middle], line=dict(color=tech_colors['middle_band']), + name='Middle Band'), + go.Scatter(x=candles.index, y=candles[bb_lower], line=dict(color=tech_colors['lower_band']), + name='Lower Band'), + ] + return traces diff --git a/frontend/visualization/signals.py b/frontend/visualization/signals.py new file mode 100644 index 0000000..81055a8 --- /dev/null +++ b/frontend/visualization/signals.py @@ -0,0 +1,22 @@ +from frontend.visualization import theme +import plotly.graph_objects as go +import pandas_ta as ta # noqa: F401 + + +def add_bbands_with_threshold(fig, candles, bb_length, bb_std, bb_long_threshold, bb_short_threshold, row=1, col=1): + tech_colors = theme.get_color_scheme() + # Add Bollinger Bands + candles.ta.bbands(length=bb_length, std=bb_std, append=True) + + # Generate conditions + buy_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] < bb_long_threshold] + sell_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold] + + # 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=row, col=col) + 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=row, col=col) + return fig diff --git a/frontend/visualization/utils.py b/frontend/visualization/utils.py new file mode 100644 index 0000000..8c95b85 --- /dev/null +++ b/frontend/visualization/utils.py @@ -0,0 +1,3 @@ +def add_traces_to_fig(fig, traces, row=1, col=1): + for trace in traces: + fig.add_trace(trace, row=row, col=col) \ No newline at end of file From 4d6b07ae8d7b712ebe803087ca039f1df9a2366a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 20 May 2024 17:59:01 -0500 Subject: [PATCH 087/139] (feat) refactor bollinger --- frontend/pages/config/bollinger_v1/app.py | 232 ++---------------- .../pages/config/bollinger_v1/user_inputs.py | 43 ++++ frontend/pages/config/utils.py | 5 + 3 files changed, 63 insertions(+), 217 deletions(-) create mode 100644 frontend/pages/config/bollinger_v1/user_inputs.py create mode 100644 frontend/pages/config/utils.py diff --git a/frontend/pages/config/bollinger_v1/app.py b/frontend/pages/config/bollinger_v1/app.py index a733491..eb2953c 100644 --- a/frontend/pages/config/bollinger_v1/app.py +++ b/frontend/pages/config/bollinger_v1/app.py @@ -2,14 +2,19 @@ from datetime import datetime import streamlit as st import pandas as pd -import plotly.graph_objects as go import yaml import pandas_ta as ta # noqa: F401 -from hummingbot.connector.connector_base import OrderType from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from backend.services.backend_api_client import BackendAPIClient +from frontend.pages.config.utils import get_max_records 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.candles import get_candlestick_trace +from frontend.visualization.indicators import get_bbands_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") @@ -18,236 +23,29 @@ initialize_st_page(title="Bollinger V1", icon="📈", initial_sidebar_state="exp @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) + return pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) -@st.cache_data -def add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): - # Add Bollinger Bands - df.ta.bbands(length=bb_length, std=bb_std, append=True) - - # Generate signal - long_condition = df[f"BBP_{bb_length}_{bb_std}"] < bb_long_threshold - short_condition = df[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold - - # 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 Bollinger V1 and visualize the strategy.") st.write("---") -# Inputs for Bollinger Band 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("## Bollinger Bands Configuration") -c1, c2, c3, c4 = st.columns(4) -with c1: - bb_length = st.number_input("Bollinger Bands Length", min_value=20, max_value=200, value=100) -with c2: - bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) -with c3: - bb_long_threshold = st.number_input("Long Threshold", value=0.0) -with c4: - bb_short_threshold = st.number_input("Short Threshold", value=1.0) +inputs = user_inputs() +st.write("### Visualizing Bollinger Bands and Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +max_records = get_max_records(days_to_visualize, inputs["interval"]) # 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='ms') -candles_processed = add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold) -# Create a dynamic variable naming convention based on bb_length and bb_std -bb_lower = f'BBL_{bb_length}_{bb_std}' -bb_middle = f'BBM_{bb_length}_{bb_std}' -bb_upper = f'BBU_{bb_length}_{bb_std}' - - -# 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 -} +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], max_records=max_records) # 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', '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) +fig.add_trace(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) -# Bollinger Bands -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed[bb_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[bb_middle], 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[bb_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="Bollinger Bands 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, - "bb_length": bb_length, - "bb_std": bb_std, - "bb_long_threshold": bb_long_threshold, - "bb_short_threshold": bb_short_threshold -} - - -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!") - -st.write("---") -st.write("### Backtesting") -c1, c2, c3, c4, c5 = st.columns(5) -with c1: - start_datetime = st.date_input("Start Date", datetime(2024, 5, 1)) -with c2: - end_datetime = st.date_input("End Date", datetime(2024, 5, 1)) -with c3: - backtesting_resolution = st.selectbox("Backtesting Resolution", options=["1m", "3m", "5m", "15m", "30m"], index=1) -with c4: - trade_cost = st.number_input("Trade Cost", min_value=0.0, value=0.0006, step=0.0001) -with c5: - run_backtesting = st.button("Run Backtesting") - -if run_backtesting: - backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) - backtesting_results = backend_api_client.run_backtesting( - start_time=int(start_datetime.timestamp()) * 1000, - end_time=(end_datetime.timestamp()) * 1000, - backtesting_resolution=backtesting_resolution, - trade_cost=trade_cost, - config=yaml_config, - ) - st.write(backtesting_results) \ No newline at end of file diff --git a/frontend/pages/config/bollinger_v1/user_inputs.py b/frontend/pages/config/bollinger_v1/user_inputs.py new file mode 100644 index 0000000..b8f8483 --- /dev/null +++ b/frontend/pages/config/bollinger_v1/user_inputs.py @@ -0,0 +1,43 @@ +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(): + 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=20, max_value=200, value=100) + with c2: + bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) + with c3: + bb_long_threshold = st.number_input("Long Threshold", value=0.0) + with c4: + bb_short_threshold = st.number_input("Short Threshold", value=1.0) + 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_name": 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, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + }, + "take_profit_order_type": take_profit_order_type + } diff --git a/frontend/pages/config/utils.py b/frontend/pages/config/utils.py new file mode 100644 index 0000000..eda5fb2 --- /dev/null +++ b/frontend/pages/config/utils.py @@ -0,0 +1,5 @@ +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])) From 4f4a2bacca75b7d82b57e0a2807d57e03e6fea31 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:27:25 -0500 Subject: [PATCH 088/139] (feat) change defaults to kucoin to be used in the US --- frontend/components/directional_trading_general_inputs.py | 8 ++++---- frontend/components/market_making_general_inputs.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py index 4742ddb..0667718 100644 --- a/frontend/components/directional_trading_general_inputs.py +++ b/frontend/components/directional_trading_general_inputs.py @@ -5,9 +5,9 @@ def get_directional_trading_general_inputs(): with st.expander("General Settings", expanded=True): c1, c2, c3, c4, c5, c6, c7 = st.columns(7) with c1: - connector_name = st.text_input("Connector", value="binance_perpetual", + connector_name = st.text_input("Connector", value="kucoin", help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") - candles_connector_name = st.text_input("Candles Connector", value="binance_perpetual", + candles_connector_name = st.text_input("Candles Connector", value="kucoin", 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="WLD-USDT", @@ -17,7 +17,7 @@ def get_directional_trading_general_inputs(): with c3: leverage = st.number_input("Leverage", value=20, 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", "5m", "15m", "1h", "4h", "1d"), index=3, + interval = st.selectbox("Candles Interval", ("1m", "3m", "5m", "15m", "1h", "4h", "1d"), index=3, help="Enter the interval for candles (e.g., 1m).") with c4: total_amount_quote = st.number_input("Total amount of quote", value=1000, @@ -26,7 +26,7 @@ def get_directional_trading_general_inputs(): max_executors_per_side = st.number_input("Max Executors Per Side", value=5, help="Enter the maximum number of executors per side (e.g., 5).") with c6: - cooldown_time = st.number_input("Cooldown Time (minutes)", value=60, + cooldown_time = st.number_input("Cooldown Time (minutes)", value=15, help="Specify the cooldown time in minutes (e.g., 60).") * 60 with c7: position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0, diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py index 3e65942..970ca8a 100644 --- a/frontend/components/market_making_general_inputs.py +++ b/frontend/components/market_making_general_inputs.py @@ -5,7 +5,7 @@ def get_market_making_general_inputs(): with st.expander("General Settings", expanded=True): c1, c2, c3, c4, c5, c6, c7 = st.columns(7) with c1: - connector_name = st.text_input("Connector", value="binance_perpetual", + connector_name = st.text_input("Connector", value="kucoin", help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") with c2: trading_pair = st.text_input("Trading Pair", value="WLD-USDT", From e566e86a1c0e5465da278072d74382ed750155c1 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:27:53 -0500 Subject: [PATCH 089/139] (feat) clean up bollinger page --- frontend/pages/config/bollinger_v1/app.py | 45 +++++++++++++------ .../pages/config/bollinger_v1/user_inputs.py | 5 ++- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/frontend/pages/config/bollinger_v1/app.py b/frontend/pages/config/bollinger_v1/app.py index eb2953c..9d88100 100644 --- a/frontend/pages/config/bollinger_v1/app.py +++ b/frontend/pages/config/bollinger_v1/app.py @@ -7,24 +7,25 @@ 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.pages.config.utils import get_max_records +from frontend.components.backtesting import backtesting_section +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 +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") - - -@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 pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) - +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.") @@ -40,12 +41,30 @@ candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=i # 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', 'Trading Signals'), - row_heights=[0.7, 0.3]) + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume'), + row_heights=[0.8, 0.2]) -fig.add_trace(get_candlestick_trace(candles), row=1, col=1) +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) diff --git a/frontend/pages/config/bollinger_v1/user_inputs.py b/frontend/pages/config/bollinger_v1/user_inputs.py index b8f8483..31964c0 100644 --- a/frontend/pages/config/bollinger_v1/user_inputs.py +++ b/frontend/pages/config/bollinger_v1/user_inputs.py @@ -26,7 +26,7 @@ def user_inputs(): "max_executors_per_side": max_executors_per_side, "cooldown_time": cooldown_time, "position_mode": position_mode, - "candles_connector_name": candles_connector_name, + "candles_connector": candles_connector_name, "candles_trading_pair": candles_trading_pair, "interval": interval, "bb_length": bb_length, @@ -35,9 +35,10 @@ def user_inputs(): "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 + "take_profit_order_type": take_profit_order_type.value } From 8cd669dae2e9e29f844bf9548bdf6587a66be0f7 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:28:02 -0500 Subject: [PATCH 090/139] (feat) remove trend follower page --- .../pages/config/trend_follower_v1/README.md | 19 -- .../config/trend_follower_v1/__init__.py | 0 .../pages/config/trend_follower_v1/app.py | 219 ------------------ 3 files changed, 238 deletions(-) delete mode 100644 frontend/pages/config/trend_follower_v1/README.md delete mode 100644 frontend/pages/config/trend_follower_v1/__init__.py delete mode 100644 frontend/pages/config/trend_follower_v1/app.py diff --git a/frontend/pages/config/trend_follower_v1/README.md b/frontend/pages/config/trend_follower_v1/README.md deleted file mode 100644 index 2fa8d53..0000000 --- a/frontend/pages/config/trend_follower_v1/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# D-Man Maker V2 - -## Features -- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. -- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. -- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. - -### Using the Tool -1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. -2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. -3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. -4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. -5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. - -## Troubleshooting -- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. -- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. - -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/frontend/pages/config/trend_follower_v1/__init__.py b/frontend/pages/config/trend_follower_v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/pages/config/trend_follower_v1/app.py b/frontend/pages/config/trend_follower_v1/app.py deleted file mode 100644 index de89149..0000000 --- a/frontend/pages/config/trend_follower_v1/app.py +++ /dev/null @@ -1,219 +0,0 @@ -import streamlit as st -import pandas as pd -import plotly.graph_objects as go -import pandas_ta as ta -import yaml -from hummingbot.connector.connector_base import OrderType - -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="Trend Follower 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, sma_fast, sma_slow, entry_threshold): - # Add indicators - df.ta.sma(close='close', length=sma_fast, append=True) - df.ta.sma(close='close', length=sma_slow, append=True) - df = df.dropna() - cross_up = ta.cross(df[f"SMA_{sma_fast}"], df[f"SMA_{sma_slow}"], above=True) - cross_down = ta.cross(df[f"SMA_{sma_fast}"], df[f"SMA_{sma_slow}"], above=False) - sma_fast = df[f"SMA_{sma_fast}"] - sma_slow = df[f"SMA_{sma_slow}"] - - # Generate signal - long_condition = (cross_up == 1) & (df["close"] * (1 - entry_threshold) < sma_fast) - short_condition = (cross_down == 1) & (df["close"] * (1 + entry_threshold) > sma_fast) - - 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 Trend Follower V1 and visualize the strategy.") -st.write("---") - -# Inputs for Bollinger Band 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("## Bollinger Bands Configuration") -c1, c2, c3 = st.columns(3) -with c1: - sma_fast = st.number_input("Fast SMA Length", min_value=10, max_value=100, value=20) -with c2: - sma_slow = st.number_input("Slow SMA Length", min_value=20, max_value=500, value=200) -with c3: - entry_threshold = st.number_input("Entry Threshold (%)", min_value=0.0, max_value=100.0, value=1.0, step=0.1) / 100 - -# 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='ms') -candles_processed = add_indicators(df, sma_fast, sma_slow, entry_threshold) - - -# 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 Moving Averages', '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) - -# Moving Averages -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed[f"SMA_{sma_fast}"], line=dict(color='blue'), name=f'Fast SMA ({sma_fast})'), row=1, col=1) -fig.add_trace(go.Scatter(x=candles_processed.index, y=candles_processed[f"SMA_{sma_slow}"], line=dict(color='red'), name=f'Slow SMA ({sma_slow})'), 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=20, 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=20, 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="Bollinger Bands 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!") \ No newline at end of file From 397986faa0ee58e11280f025925ed7a899019855 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:28:14 -0500 Subject: [PATCH 091/139] (feat) add method to cache candles --- frontend/pages/config/utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frontend/pages/config/utils.py b/frontend/pages/config/utils.py index eda5fb2..8296d20 100644 --- a/frontend/pages/config/utils.py +++ b/frontend/pages/config/utils.py @@ -1,5 +1,18 @@ +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", max_records=5000): + backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) + return pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) From 57bc4982dab7978bd33435164d7d5c30d43ae8e4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:28:22 -0500 Subject: [PATCH 092/139] (feat) intialize macd_bb page --- frontend/pages/config/macd_bb_v1/app.py | 191 +++++------------- .../pages/config/macd_bb_v1/user_inputs.py | 54 +++++ 2 files changed, 102 insertions(+), 143 deletions(-) create mode 100644 frontend/pages/config/macd_bb_v1/user_inputs.py diff --git a/frontend/pages/config/macd_bb_v1/app.py b/frontend/pages/config/macd_bb_v1/app.py index 797011d..7e44969 100644 --- a/frontend/pages/config/macd_bb_v1/app.py +++ b/frontend/pages/config/macd_bb_v1/app.py @@ -6,157 +6,62 @@ 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.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") - -@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, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal): - # Bollinger Bands - df.ta.bbands(length=bb_length, std=bb_std, append=True) - # MACD - df.ta.macd(fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True) - - # Decision Logic - bbp = df[f"BBP_{bb_length}_{bb_std}"] - macdh = df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] - macd = df[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"] - - long_condition = (bbp < bb_long_threshold) & (macdh > 0) & (macd < 0) - short_condition = (bbp > bb_short_threshold) & (macdh < 0) & (macd > 0) - - df["signal"] = 0 - df.loc[long_condition, "signal"] = 1 - df.loc[short_condition, "signal"] = -1 - - return df - -st.write("## Configuration") -c1, c2, c3, c4 = st.columns(4) -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) -with c4: - bb_length = st.number_input("BB Length", min_value=2, value=100) - bb_std = st.number_input("BB Std Dev", min_value=0.1, value=2.0, step=0.1) - bb_long_threshold = st.number_input("BB Long Threshold", value=0.0) - bb_short_threshold = st.number_input("BB Short Threshold", value=1.0) - -# 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='ms') -df = add_indicators(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal) - -# Prepare data for signals -signals = df[df['signal'] != 0] -buy_signals = signals[signals['signal'] == 1] -sell_signals = signals[signals['signal'] == -1] +backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) -# Define your color palette -tech_colors = { - 'upper_band': '#4682B4', - 'middle_band': '#FFD700', - 'lower_band': '#32CD32', - 'buy_signal': '#1E90FF', - 'sell_signal': '#FF0000', -} +# User inputs +inputs = user_inputs() -# 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 with Bollinger Bands', 'MACD Line and Histogram', 'Trading Signals'), - row_heights=[0.5, 0.3, 0.2]) # Adjust heights to give more space to candlestick and MACD +st.write("### Visualizing MACD Bollinger Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +max_records = get_max_records(days_to_visualize, inputs["interval"]) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], max_records=max_records) -# 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) +# 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]) -fig.add_trace(go.Scatter(x=df.index, y=df[f"BBL_{bb_length}_{bb_std}"], line=dict(color='blue'), name='Lower Band'), row=1, col=1) -fig.add_trace(go.Scatter(x=df.index, y=df[f"BBM_{bb_length}_{bb_std}"], line=dict(color='red'), name='Middle Band'), row=1, col=1) -fig.add_trace(go.Scatter(x=df.index, y=df[f"BBU_{bb_length}_{bb_std}"], line=dict(color='green'), name='Upper Band'), row=1, col=1) +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) -# 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=20, 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=20, 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 +fig.update_layout(**theme.get_default_layout()) +# 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"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, - "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, -} - -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!") +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) diff --git a/frontend/pages/config/macd_bb_v1/user_inputs.py b/frontend/pages/config/macd_bb_v1/user_inputs.py new file mode 100644 index 0000000..aa5d9be --- /dev/null +++ b/frontend/pages/config/macd_bb_v1/user_inputs.py @@ -0,0 +1,54 @@ +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(): + 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=20, max_value=200, value=100) + with c2: + bb_std = st.number_input("Standard Deviation Multiplier", min_value=1.0, max_value=5.0, value=2.0) + with c3: + bb_long_threshold = st.number_input("Long Threshold", value=0.2) + with c4: + bb_short_threshold = st.number_input("Short Threshold", value=0.8) + with c5: + macd_fast = st.number_input("MACD Fast", min_value=1, value=21) + with c6: + macd_slow = st.number_input("MACD Slow", min_value=1, value=42) + with c7: + macd_signal = st.number_input("MACD Signal", min_value=1, value=9) + + 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 + } From 939b0fc42d230c99a35a0231b72542459467c976 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:28:48 -0500 Subject: [PATCH 093/139] (feat) separate live candles from bt candles --- frontend/visualization/backtesting.py | 4 ++-- frontend/visualization/candles.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/visualization/backtesting.py b/frontend/visualization/backtesting.py index 6efdc1e..4d4f3b0 100644 --- a/frontend/visualization/backtesting.py +++ b/frontend/visualization/backtesting.py @@ -1,5 +1,5 @@ from plotly.subplots import make_subplots -from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.candles import get_bt_candlestick_trace, get_candlestick_trace from frontend.visualization.executors import add_executors_trace from frontend.visualization.pnl import get_pnl_trace from frontend.visualization.theme import get_default_layout @@ -12,7 +12,7 @@ def create_backtesting_figure(df, executors, config): row_heights=[0.7, 0.3]) # Add candlestick trace - fig.add_trace(get_candlestick_trace(df), row=1, col=1) + fig.add_trace(get_bt_candlestick_trace(df), row=1, col=1) # Add executors trace fig = add_executors_trace(fig, executors, row=1, col=1) diff --git a/frontend/visualization/candles.py b/frontend/visualization/candles.py index 9d0b050..fce8ae8 100644 --- a/frontend/visualization/candles.py +++ b/frontend/visualization/candles.py @@ -1,6 +1,9 @@ import plotly.graph_objects as go import pandas as pd +from frontend.visualization import theme + + def get_candlestick_trace(df): df.index = pd.to_datetime(df.timestamp, unit='ms') return go.Candlestick(x=df.index, @@ -9,4 +12,13 @@ def get_candlestick_trace(df): low=df['low'], close=df['close'], name="Candlesticks", - increasing_line_color='#2ECC71', decreasing_line_color='#E74C3C') + increasing_line_color='#2ECC71', decreasing_line_color='#E74C3C',) + + +def get_bt_candlestick_trace(df): + df.index = pd.to_datetime(df.timestamp, unit='ms') + return go.Scatter(x=df.index, + y=df['close'], + mode='lines', + line=dict(color=theme.get_color_scheme()["price"]), + ) From 2f563c7a836112c703611a08528bf65beac80396 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:29:19 -0500 Subject: [PATCH 094/139] (feat) add traces for indicators and signals --- frontend/visualization/indicators.py | 32 ++++++++++++++++--- frontend/visualization/signals.py | 46 +++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/frontend/visualization/indicators.py b/frontend/visualization/indicators.py index 086fd90..7f857c7 100644 --- a/frontend/visualization/indicators.py +++ b/frontend/visualization/indicators.py @@ -1,21 +1,43 @@ +import pandas as pd import pandas_ta as ta # noqa: F401 import plotly.graph_objects as go from frontend.visualization import theme -def get_bbands_traces(candles, bb_length, bb_std): +def get_bbands_traces(df, bb_length, bb_std): tech_colors = theme.get_color_scheme() - candles.ta.bbands(length=bb_length, std=bb_std, append=True) + df.ta.bbands(length=bb_length, std=bb_std, append=True) bb_lower = f'BBL_{bb_length}_{bb_std}' bb_middle = f'BBM_{bb_length}_{bb_std}' bb_upper = f'BBU_{bb_length}_{bb_std}' traces = [ - go.Scatter(x=candles.index, y=candles[bb_upper], line=dict(color=tech_colors['upper_band']), + go.Scatter(x=df.index, y=df[bb_upper], line=dict(color=tech_colors['upper_band']), name='Upper Band'), - go.Scatter(x=candles.index, y=candles[bb_middle], line=dict(color=tech_colors['middle_band']), + go.Scatter(x=df.index, y=df[bb_middle], line=dict(color=tech_colors['middle_band']), name='Middle Band'), - go.Scatter(x=candles.index, y=candles[bb_lower], line=dict(color=tech_colors['lower_band']), + go.Scatter(x=df.index, y=df[bb_lower], line=dict(color=tech_colors['lower_band']), name='Lower Band'), ] return traces + + +def get_volume_trace(df): + df.index = pd.to_datetime(df.timestamp, unit='ms') + return go.Bar(x=df.index, y=df['volume'], name="Volume", marker_color=theme.get_color_scheme()["volume"], opacity=0.7) + +def get_macd_traces(df, macd_fast, macd_slow, macd_signal): + tech_colors = theme.get_color_scheme() + df.ta.macd(fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True) + macd = f'MACD_{macd_fast}_{macd_slow}_{macd_signal}' + macd_s = f'MACDs_{macd_fast}_{macd_slow}_{macd_signal}' + macd_hist = f'MACDh_{macd_fast}_{macd_slow}_{macd_signal}' + traces = [ + go.Scatter(x=df.index, y=df[macd], line=dict(color=tech_colors['macd_line']), + name='MACD Line'), + go.Scatter(x=df.index, y=df[macd_s], line=dict(color=tech_colors['macd_signal']), + name='MACD Signal'), + go.Bar(x=df.index, y=df[macd_hist], name='MACD Histogram', + marker_color=df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"].apply(lambda x: '#FF6347' if x < 0 else '#32CD32')) + ] + return traces \ No newline at end of file diff --git a/frontend/visualization/signals.py b/frontend/visualization/signals.py index 81055a8..6762c7c 100644 --- a/frontend/visualization/signals.py +++ b/frontend/visualization/signals.py @@ -3,9 +3,10 @@ import plotly.graph_objects as go import pandas_ta as ta # noqa: F401 -def add_bbands_with_threshold(fig, candles, bb_length, bb_std, bb_long_threshold, bb_short_threshold, row=1, col=1): +def get_bollinger_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): tech_colors = theme.get_color_scheme() # Add Bollinger Bands + candles = df.copy() candles.ta.bbands(length=bb_length, std=bb_std, append=True) # Generate conditions @@ -13,10 +14,39 @@ def add_bbands_with_threshold(fig, candles, bb_length, bb_std, bb_long_threshold sell_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold] # 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=row, col=col) - 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=row, col=col) - return fig + traces = [ + 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'), + 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') + ] + return traces + + +def get_macdbb_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, + macd_signal): + tech_colors = theme.get_color_scheme() + # Add Bollinger Bands + df.ta.bbands(length=bb_length, std=bb_std, append=True) + # Add MACD + df.ta.macd(fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True) + # Decision Logic + bbp = df[f"BBP_{bb_length}_{bb_std}"] + macdh = df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] + macd = df[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"] + + buy_signals = df[(bbp < bb_long_threshold) & (macdh > 0) & (macd < 0)] + sell_signals = df[(bbp > bb_short_threshold) & (macdh < 0) & (macd > 0)] + + # Signals plot + traces = [ + 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'), + 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') + ] + return traces From 7909687adf9cf339aa9395733b8d7ae813883179 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 11:29:36 -0500 Subject: [PATCH 095/139] (feat) remove range slider and improve executors lines --- frontend/visualization/executors.py | 4 ++-- frontend/visualization/theme.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py index 8b006e9..42c0870 100644 --- a/frontend/visualization/executors.py +++ b/frontend/visualization/executors.py @@ -19,8 +19,8 @@ def add_executors_trace(fig, executors, row, col): else: if executor.net_pnl_quote > Decimal(0): fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', - line=dict(color='green', width=2), name=name), row=row, col=col) + line=dict(color='green', width=3), name=name), row=row, col=col) else: fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', - line=dict(color='red', width=2), name=name), row=row, col=col) + line=dict(color='red', width=3), name=name), row=row, col=col) return fig diff --git a/frontend/visualization/theme.py b/frontend/visualization/theme.py index 0bbca11..f59b7c3 100644 --- a/frontend/visualization/theme.py +++ b/frontend/visualization/theme.py @@ -6,12 +6,14 @@ def get_default_layout(title=None, height=800, width=1800): "font": {"color": 'white', "size": 12}, # Consistent font color and size "height": height, "width": width, - "margin": {"l": 20, "r": 20, "t": 50, "b": 20} + "margin": {"l": 20, "r": 20, "t": 50, "b": 20}, + "xaxis_rangeslider_visible": False, } if title: layout["title"] = title return layout + def get_color_scheme(): return { 'upper_band': '#4682B4', @@ -21,8 +23,6 @@ def get_color_scheme(): 'sell_signal': '#FF0000', 'buy': '#32CD32', # Green for buy orders 'sell': '#FF6347', # Tomato red for sell orders - 'candlestick_increasing': '#2ECC71', - 'candlestick_decreasing': '#E74C3C', 'macd_line': '#FFA500', # Orange 'macd_signal': '#800080', # Purple 'macd_histogram_positive': '#32CD32', # Green @@ -33,5 +33,7 @@ def get_color_scheme(): 'order_amount': '#1E90FF', # Dodger Blue 'cum_amount': '#4682B4', # Steel Blue 'stop_loss': '#FF0000', # Red - 'cum_unrealized_pnl': '#FFA07A' # Light Salmon + 'cum_unrealized_pnl': '#FFA07A', # Light Salmon + 'volume': '#FFD700', # Gold + 'price': '#00008B', # Dark Blue } From b69c0444f95290256d264fe05c13c0683f5d53e4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 12:03:58 -0500 Subject: [PATCH 096/139] (feat) finish macd bb page --- frontend/pages/config/macd_bb_v1/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/pages/config/macd_bb_v1/app.py b/frontend/pages/config/macd_bb_v1/app.py index 7e44969..c0c6f4c 100644 --- a/frontend/pages/config/macd_bb_v1/app.py +++ b/frontend/pages/config/macd_bb_v1/app.py @@ -38,7 +38,6 @@ candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=i 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"], From b865eb175c7f1fccb223f10f974cb2308ad6fc66 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 12:04:09 -0500 Subject: [PATCH 097/139] (feat) add index resampling --- frontend/pages/config/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/pages/config/utils.py b/frontend/pages/config/utils.py index 8296d20..8494824 100644 --- a/frontend/pages/config/utils.py +++ b/frontend/pages/config/utils.py @@ -15,4 +15,7 @@ def get_max_records(days_to_download: int, interval: str) -> int: @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 pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) + df = pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) + df.index = pd.to_datetime(df.timestamp, unit='ms') + return df + From d2f4a9ee6a0006c67644497c3bc2c4fae9001b35 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 12:04:20 -0500 Subject: [PATCH 098/139] (feat) clean up visualization --- frontend/visualization/candles.py | 1 - frontend/visualization/indicators.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/visualization/candles.py b/frontend/visualization/candles.py index fce8ae8..3300249 100644 --- a/frontend/visualization/candles.py +++ b/frontend/visualization/candles.py @@ -5,7 +5,6 @@ from frontend.visualization import theme def get_candlestick_trace(df): - df.index = pd.to_datetime(df.timestamp, unit='ms') return go.Candlestick(x=df.index, open=df['open'], high=df['high'], diff --git a/frontend/visualization/indicators.py b/frontend/visualization/indicators.py index 7f857c7..0f21665 100644 --- a/frontend/visualization/indicators.py +++ b/frontend/visualization/indicators.py @@ -40,4 +40,4 @@ def get_macd_traces(df, macd_fast, macd_slow, macd_signal): go.Bar(x=df.index, y=df[macd_hist], name='MACD Histogram', marker_color=df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"].apply(lambda x: '#FF6347' if x < 0 else '#32CD32')) ] - return traces \ No newline at end of file + return traces From e0a6f52a92fe07991b910d46321de91c44d9ae81 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 12:04:28 -0500 Subject: [PATCH 099/139] (feat) adapt base values --- frontend/components/directional_trading_general_inputs.py | 4 ++-- frontend/components/risk_management.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py index 0667718..3e8fb5e 100644 --- a/frontend/components/directional_trading_general_inputs.py +++ b/frontend/components/directional_trading_general_inputs.py @@ -17,7 +17,7 @@ def get_directional_trading_general_inputs(): with c3: leverage = st.number_input("Leverage", value=20, 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=3, + interval = st.selectbox("Candles Interval", ("1m", "3m", "5m", "15m", "1h", "4h", "1d"), index=1, help="Enter the interval for candles (e.g., 1m).") with c4: total_amount_quote = st.number_input("Total amount of quote", value=1000, @@ -26,7 +26,7 @@ def get_directional_trading_general_inputs(): max_executors_per_side = st.number_input("Max Executors Per Side", value=5, help="Enter the maximum number of executors per side (e.g., 5).") with c6: - cooldown_time = st.number_input("Cooldown Time (minutes)", value=15, + cooldown_time = st.number_input("Cooldown Time (minutes)", value=10, help="Specify the cooldown time in minutes (e.g., 60).") * 60 with c7: position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0, diff --git a/frontend/components/risk_management.py b/frontend/components/risk_management.py index bfbd6ac..3b0ed71 100644 --- a/frontend/components/risk_management.py +++ b/frontend/components/risk_management.py @@ -6,13 +6,13 @@ def get_risk_management_inputs(): 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=3.0, step=0.1, + sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=5.0, 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=2.0, 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=60 * 6, + time_limit = st.number_input("Time Limit (minutes)", min_value=0, value=60 * 12, 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=1.8, From 3106b415129f3a0046b61a2ea60a9bb8b26850be Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 16:20:33 -0500 Subject: [PATCH 100/139] (feat) add optional candles darta --- .../components/market_making_general_inputs.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py index 970ca8a..dc9d378 100644 --- a/frontend/components/market_making_general_inputs.py +++ b/frontend/components/market_making_general_inputs.py @@ -1,9 +1,12 @@ import streamlit as st -def get_market_making_general_inputs(): +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) + candles_connector = None + candles_trading_pair = None + interval = None with c1: connector_name = st.text_input("Connector", value="kucoin", help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") @@ -25,4 +28,14 @@ def get_market_making_general_inputs(): with c7: executor_refresh_time = st.number_input("Executor Refresh Time (minutes)", value=60, help="Enter the refresh time in minutes for executors (e.g., 60).") * 60 - return connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time + if custom_candles: + with c1: + candles_connector = st.text_input("Candles Connector", value="kucoin", + 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="WLD-USDT", + help="Enter the trading pair to get candles for (e.g., WLD-USDT).") + with c3: + interval = st.selectbox("Candles Interval", ("1m", "3m", "5m", "15m", "1h", "4h", "1d"), index=1, + 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 From 540725caef8f21cdf54a7f9762d4b68138b66bd1 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 16:20:50 -0500 Subject: [PATCH 101/139] (feat) refactor general user inputs --- .../pages/config/dman_maker_v2/user_inputs.py | 2 +- frontend/pages/config/pmm_dynamic/README.md | 19 ++++++ frontend/pages/config/pmm_dynamic/__init__.py | 0 frontend/pages/config/pmm_dynamic/app.py | 63 ++++++++++++++++++ .../pages/config/pmm_dynamic/user_inputs.py | 57 +++++++++++++++++ .../pages/config/pmm_simple/user_inputs.py | 2 +- frontend/pages/config/supertrend_v1/README.md | 19 ++++++ .../pages/config/supertrend_v1/__init__.py | 0 frontend/pages/config/supertrend_v1/app.py | 64 +++++++++++++++++++ .../pages/config/supertrend_v1/user_inputs.py | 41 ++++++++++++ 10 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 frontend/pages/config/pmm_dynamic/README.md create mode 100644 frontend/pages/config/pmm_dynamic/__init__.py create mode 100644 frontend/pages/config/pmm_dynamic/app.py create mode 100644 frontend/pages/config/pmm_dynamic/user_inputs.py create mode 100644 frontend/pages/config/supertrend_v1/README.md create mode 100644 frontend/pages/config/supertrend_v1/__init__.py create mode 100644 frontend/pages/config/supertrend_v1/app.py create mode 100644 frontend/pages/config/supertrend_v1/user_inputs.py diff --git a/frontend/pages/config/dman_maker_v2/user_inputs.py b/frontend/pages/config/dman_maker_v2/user_inputs.py index cabc893..5ccf4d9 100644 --- a/frontend/pages/config/dman_maker_v2/user_inputs.py +++ b/frontend/pages/config/dman_maker_v2/user_inputs.py @@ -5,7 +5,7 @@ from frontend.components.market_making_general_inputs import get_market_making_g def user_inputs(): - connector_name, trading_pair, leverage, total_amount_quote, position_mode, cooldown_time, executor_refresh_time = get_market_making_general_inputs() + 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) diff --git a/frontend/pages/config/pmm_dynamic/README.md b/frontend/pages/config/pmm_dynamic/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/frontend/pages/config/pmm_dynamic/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/frontend/pages/config/pmm_dynamic/__init__.py b/frontend/pages/config/pmm_dynamic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py new file mode 100644 index 0000000..80eafff --- /dev/null +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -0,0 +1,63 @@ +import streamlit as st +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.save_config import render_save_config + +# Import submodules +from frontend.components.backtesting import backtesting_section +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.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 user inputs +inputs = user_inputs() +st.write("### Visualizing MACD and NATR indicators for PMM Dynamic") +st.text("The MACD is used to shift the mid price and the NATR to make the spreads dynamic. " + "In the order distributions graph, we are going to see the values of the orders affected by the average NATR") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +max_records = get_max_records(days_to_visualize, inputs["interval"]) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], max_records=max_records) +# Create a subplot with 2 rows +fig = make_subplots(rows=4, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume', "MACD"), + 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) + + + +fig = create_executors_distribution_traces(inputs) +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) diff --git a/frontend/pages/config/pmm_dynamic/user_inputs.py b/frontend/pages/config/pmm_dynamic/user_inputs.py new file mode 100644 index 0000000..1962a87 --- /dev/null +++ b/frontend/pages/config/pmm_dynamic/user_inputs.py @@ -0,0 +1,57 @@ +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, candles_connector, candles_trading_pair, interval = get_market_making_general_inputs(custom_candles=True) + 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() + 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=21) + with c2: + macd_slow = st.number_input("MACD Slow Period", min_value=1, max_value=200, value=42) + with c3: + macd_signal = st.number_input("MACD Signal Period", min_value=1, max_value=200, value=9) + with c4: + natr_length = st.number_input("NATR Length", min_value=1, max_value=200, value=14) + + # 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, + "buy_spreads": [100 * spread for spread in buy_spread_distributions], + "sell_spreads": [100 * spread for spread in 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, + "candles_connector": candles_connector, + "candles_trading_pair": candles_trading_pair, + "interval": interval, + "macd_fast": macd_fast, + "macd_slow": macd_slow, + "macd_signal": macd_signal, + "natr_length": natr_length, + "stop_loss": sl, + "take_profit": tp, + "time_limit": time_limit, + "take_profit_order_type": take_profit_order_type.value, + "trailing_stop": { + "activation_price": ts_ap, + "trailing_delta": ts_delta + } + } + + return config diff --git a/frontend/pages/config/pmm_simple/user_inputs.py b/frontend/pages/config/pmm_simple/user_inputs.py index a4daf56..3ae5431 100644 --- a/frontend/pages/config/pmm_simple/user_inputs.py +++ b/frontend/pages/config/pmm_simple/user_inputs.py @@ -6,7 +6,7 @@ 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() + 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 diff --git a/frontend/pages/config/supertrend_v1/README.md b/frontend/pages/config/supertrend_v1/README.md new file mode 100644 index 0000000..2fa8d53 --- /dev/null +++ b/frontend/pages/config/supertrend_v1/README.md @@ -0,0 +1,19 @@ +# D-Man Maker V2 + +## Features +- **Interactive Configuration**: Configure market making parameters such as spreads, amounts, and order levels through an intuitive web interface. +- **Visual Feedback**: Visualize order spread and amount distributions using dynamic Plotly charts. +- **Backend Integration**: Save and deploy configurations directly to a backend system for active management and execution. + +### Using the Tool +1. **Configure Parameters**: Use the Streamlit interface to input parameters such as connector type, trading pair, and leverage. +2. **Set Distributions**: Define distributions for buy and sell orders, including spread and amount, either manually or through predefined distribution types like Geometric or Fibonacci. +3. **Visualize Orders**: View the configured order distributions on a Plotly graph, which illustrates the relationship between spread and amount. +4. **Export Configuration**: Once the configuration is set, export it as a YAML file or directly upload it to the Backend API. +5. **Upload**: Use the "Upload Config to BackendAPI" button to send your configuration to the backend system. Then can be used to deploy a new bot. + +## Troubleshooting +- **UI Not Loading**: Ensure all Python dependencies are installed and that the Streamlit server is running correctly. +- **API Errors**: Check the console for any error messages that may indicate issues with the backend connection. + +For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file diff --git a/frontend/pages/config/supertrend_v1/__init__.py b/frontend/pages/config/supertrend_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/config/supertrend_v1/app.py b/frontend/pages/config/supertrend_v1/app.py new file mode 100644 index 0000000..ec62ca6 --- /dev/null +++ b/frontend/pages/config/supertrend_v1/app.py @@ -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.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_bbands_traces, get_volume_trace, get_macd_traces, \ + get_supertrend_traces +from frontend.visualization.signals import get_macdbb_v1_signal_traces, 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) + + +# User inputs +inputs = user_inputs() + +st.write("### Visualizing Supertrend Trading Signals") +days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) +max_records = get_max_records(days_to_visualize, inputs["interval"]) +# Load candle data +candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], max_records=max_records) + +# 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) diff --git a/frontend/pages/config/supertrend_v1/user_inputs.py b/frontend/pages/config/supertrend_v1/user_inputs.py new file mode 100644 index 0000000..cb7b3b2 --- /dev/null +++ b/frontend/pages/config/supertrend_v1/user_inputs.py @@ -0,0 +1,41 @@ +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(): + 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=20) + with c2: + multiplier = st.number_input("Supertrend Multiplier", min_value=1.0, max_value=5.0, value=3.0) + with c3: + percentage_threshold = st.number_input("Percentage Threshold (%)", value=0.5) / 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 + } From 26a740efcf983e732c7ef9b99e755fec087cc7f0 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 16:21:06 -0500 Subject: [PATCH 102/139] (feat) add supertrend indicators and signals --- frontend/visualization/indicators.py | 39 ++++++++++++++++++++++++ frontend/visualization/signals.py | 45 +++++++++++++++------------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/frontend/visualization/indicators.py b/frontend/visualization/indicators.py index 0f21665..93f86bf 100644 --- a/frontend/visualization/indicators.py +++ b/frontend/visualization/indicators.py @@ -41,3 +41,42 @@ def get_macd_traces(df, macd_fast, macd_slow, macd_signal): marker_color=df[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"].apply(lambda x: '#FF6347' if x < 0 else '#32CD32')) ] return traces + + +def get_supertrend_traces(df, length, multiplier): + tech_colors = theme.get_color_scheme() + df.ta.supertrend(length=length, multiplier=multiplier, append=True) + supertrend_d = f'SUPERTd_{length}_{multiplier}' + supertrend = f'SUPERT_{length}_{multiplier}' + df = df[df[supertrend] > 0] + + # Create segments for line with different colors + segments = [] + current_segment = {"x": [], "y": [], "color": None} + + for i in range(len(df)): + if i == 0 or df[supertrend_d].iloc[i] == df[supertrend_d].iloc[i - 1]: + current_segment["x"].append(df.index[i]) + current_segment["y"].append(df[supertrend].iloc[i]) + current_segment["color"] = tech_colors['buy'] if df[supertrend_d].iloc[i] == 1 else tech_colors['sell'] + else: + segments.append(current_segment) + current_segment = {"x": [df.index[i - 1], df.index[i]], + "y": [df[supertrend].iloc[i - 1], df[supertrend].iloc[i]], + "color": tech_colors['buy'] if df[supertrend_d].iloc[i] == 1 else tech_colors['sell']} + + segments.append(current_segment) + + # Create traces from segments + traces = [ + go.Scatter( + x=segment["x"], + y=segment["y"], + mode='lines', + line=dict(color=segment["color"], width=2), + name='SuperTrend' + ) for segment in segments + ] + + return traces + diff --git a/frontend/visualization/signals.py b/frontend/visualization/signals.py index 6762c7c..1cbec29 100644 --- a/frontend/visualization/signals.py +++ b/frontend/visualization/signals.py @@ -3,17 +3,8 @@ import plotly.graph_objects as go import pandas_ta as ta # noqa: F401 -def get_bollinger_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): +def get_signal_traces(buy_signals, sell_signals): tech_colors = theme.get_color_scheme() - # Add Bollinger Bands - candles = df.copy() - candles.ta.bbands(length=bb_length, std=bb_std, append=True) - - # Generate conditions - buy_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] < bb_long_threshold] - sell_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold] - - # Signals plot traces = [ go.Scatter(x=buy_signals.index, y=buy_signals['close'], mode='markers', marker=dict(color=tech_colors['buy_signal'], size=10, symbol='triangle-up'), @@ -24,6 +15,17 @@ def get_bollinger_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_ ] return traces +def get_bollinger_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold): + # Add Bollinger Bands + candles = df.copy() + candles.ta.bbands(length=bb_length, std=bb_std, append=True) + + # Generate conditions + buy_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] < bb_long_threshold] + sell_signals = candles[candles[f"BBP_{bb_length}_{bb_std}"] > bb_short_threshold] + + return get_signal_traces(buy_signals, sell_signals) + def get_macdbb_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_short_threshold, macd_fast, macd_slow, macd_signal): @@ -40,13 +42,16 @@ def get_macdbb_v1_signal_traces(df, bb_length, bb_std, bb_long_threshold, bb_sho buy_signals = df[(bbp < bb_long_threshold) & (macdh > 0) & (macd < 0)] sell_signals = df[(bbp > bb_short_threshold) & (macdh < 0) & (macd > 0)] - # Signals plot - traces = [ - 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'), - 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') - ] - return traces + return get_signal_traces(buy_signals, sell_signals) + + +def get_supertrend_v1_signal_traces(df, length, multiplier, percentage_threshold): + # Add indicators + df.ta.supertrend(length=length, multiplier=multiplier, append=True) + df["percentage_distance"] = abs(df["close"] - df[f"SUPERT_{length}_{multiplier}"]) / df["close"] + + # Generate long and short conditions + buy_signals = df[(df[f"SUPERTd_{length}_{multiplier}"] == 1) & (df["percentage_distance"] < percentage_threshold)] + sell_signals = df[(df[f"SUPERTd_{length}_{multiplier}"] == -1) & (df["percentage_distance"] < percentage_threshold)] + + return get_signal_traces(buy_signals, sell_signals) From 2e3df3b6375cf66ba604f45d9c9d958bbbb3b1a2 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 16:21:12 -0500 Subject: [PATCH 103/139] (feat) add pmm dynamic page --- main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.py b/main.py index 40a854f..5770dac 100644 --- a/main.py +++ b/main.py @@ -17,9 +17,11 @@ def main_page(): # Page("pages/file_manager/app.py", "File Explorer", "🗂"), Section("Config Generator", "🎛️"), Page("frontend/pages/config/pmm_simple/app.py", "PMM Simple", "👨‍🏫"), + Page("frontend/pages/config/pmm_dynamic/app.py", "PMM Dynamic", "👩‍🏫"), Page("frontend/pages/config/dman_maker_v2/app.py", "D-Man Maker V2", "🤖"), Page("frontend/pages/config/bollinger_v1/app.py", "Bollinger V1", "📈"), Page("frontend/pages/config/macd_bb_v1/app.py", "MACD_BB V1", "📊"), + Page("frontend/pages/config/supertrend_v1/app.py", "SuperTrend V1", "👨‍🔬"), Page("frontend/pages/config/xemm_controller/app.py", "XEMM Controller", "⚡️"), Page("frontend/pages/config/position_builder/app.py", "Position Builder", "🔭"), Section("Data", "💾"), From 5d8d435f774ebf67c3c8b28f2568db3527a4e50c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 17:41:30 -0500 Subject: [PATCH 104/139] (feat) add args to dist function --- frontend/visualization/executors_distribution.py | 10 +++++----- frontend/visualization/theme.py | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/visualization/executors_distribution.py b/frontend/visualization/executors_distribution.py index 61c699c..b41a26d 100644 --- a/frontend/visualization/executors_distribution.py +++ b/frontend/visualization/executors_distribution.py @@ -2,13 +2,13 @@ import plotly.graph_objects as go import frontend.visualization.theme as theme -def create_executors_distribution_traces(config): +def create_executors_distribution_traces(buy_spreads, sell_spreads, buy_amounts_pct, sell_amounts_pct, total_amount_quote): colors = theme.get_color_scheme() - buy_spread_distributions = [spread * 100 for spread in config["buy_spreads"]] - sell_spread_distributions = [spread * 100 for spread in config["sell_spreads"]] - buy_order_amounts_quote = [amount * config["total_amount_quote"] for amount in config["buy_amounts_pct"]] - sell_order_amounts_quote = [amount * config["total_amount_quote"] for amount in config["sell_amounts_pct"]] + buy_spread_distributions = [spread * 100 for spread in buy_spreads] + sell_spread_distributions = [spread * 100 for spread in sell_spreads] + buy_order_amounts_quote = [amount * total_amount_quote for amount in buy_amounts_pct] + sell_order_amounts_quote = [amount * total_amount_quote for amount in sell_amounts_pct] buy_order_levels = len(buy_spread_distributions) sell_order_levels = len(sell_spread_distributions) diff --git a/frontend/visualization/theme.py b/frontend/visualization/theme.py index f59b7c3..5f66da0 100644 --- a/frontend/visualization/theme.py +++ b/frontend/visualization/theme.py @@ -8,6 +8,8 @@ def get_default_layout(title=None, height=800, width=1800): "width": width, "margin": {"l": 20, "r": 20, "t": 50, "b": 20}, "xaxis_rangeslider_visible": False, + "hovermode": "x unified", + "showlegend": False, } if title: layout["title"] = title From c0d5e47ef1cc70440ca67de16b5a086b2bd587ae Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 17:41:36 -0500 Subject: [PATCH 105/139] (feat) add pmm dynamic page --- frontend/pages/config/pmm_dynamic/app.py | 42 ++++++++++++++----- .../spread_and_price_multipliers.py | 17 ++++++++ .../pages/config/pmm_dynamic/user_inputs.py | 5 --- 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 frontend/pages/config/pmm_dynamic/spread_and_price_multipliers.py diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py index 80eafff..474d2da 100644 --- a/frontend/pages/config/pmm_dynamic/app.py +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -1,15 +1,19 @@ 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.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 @@ -33,17 +37,35 @@ days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value= max_records = get_max_records(days_to_visualize, inputs["interval"]) # Load candle data candles = get_candles(connector_name=inputs["candles_connector"], trading_pair=inputs["candles_trading_pair"], interval=inputs["interval"], max_records=max_records) -# Create a subplot with 2 rows -fig = make_subplots(rows=4, cols=1, shared_xaxes=True, - vertical_spacing=0.02, subplot_titles=('Candlestick with Bollinger Bands', 'Volume', "MACD"), - 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) +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="Spreads Multiplier", 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) - - -fig = create_executors_distribution_traces(inputs) -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() +inputs["buy_spreads"] = [spread * 100 for spread in buy_spread_distributions] +inputs["sell_spreads"] = [spread * 100 for spread in sell_spread_distributions] +inputs["buy_amounts_pct"] = buy_order_amounts_pct +inputs["sell_amounts_pct"] = sell_order_amounts_pct +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: diff --git a/frontend/pages/config/pmm_dynamic/spread_and_price_multipliers.py b/frontend/pages/config/pmm_dynamic/spread_and_price_multipliers.py new file mode 100644 index 0000000..dfb85f2 --- /dev/null +++ b/frontend/pages/config/pmm_dynamic/spread_and_price_multipliers.py @@ -0,0 +1,17 @@ +import pandas_ta as ta # noqa: F401 + + +def get_pmm_dynamic_multipliers(df, macd_fast, macd_slow, macd_signal, natr_length): + """ + Get the spread and price multipliers for PMM Dynamic + """ + natr = ta.natr(df["high"], df["low"], df["close"], length=natr_length) / 100 + macd_output = ta.macd(df["close"], fast=macd_fast, + slow=macd_slow, signal=macd_signal) + macd = macd_output[f"MACD_{macd_fast}_{macd_slow}_{macd_signal}"] + macdh = macd_output[f"MACDh_{macd_fast}_{macd_slow}_{macd_signal}"] + macd_signal = - (macd - macd.mean()) / macd.std() + macdh_signal = macdh.apply(lambda x: 1 if x > 0 else -1) + max_price_shift = natr / 2 + price_multiplier = ((0.5 * macd_signal + 0.5 * macdh_signal) * max_price_shift) + return price_multiplier, natr diff --git a/frontend/pages/config/pmm_dynamic/user_inputs.py b/frontend/pages/config/pmm_dynamic/user_inputs.py index 1962a87..b0b1711 100644 --- a/frontend/pages/config/pmm_dynamic/user_inputs.py +++ b/frontend/pages/config/pmm_dynamic/user_inputs.py @@ -7,7 +7,6 @@ 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, candles_connector, candles_trading_pair, interval = get_market_making_general_inputs(custom_candles=True) - 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() with st.expander("PMM Dynamic Configuration", expanded=True): c1, c2, c3, c4 = st.columns(4) @@ -29,10 +28,6 @@ def user_inputs(): "connector_name": connector_name, "trading_pair": trading_pair, "total_amount_quote": total_amount_quote, - "buy_spreads": [100 * spread for spread in buy_spread_distributions], - "sell_spreads": [100 * spread for spread in 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, From 358b69fe41dbf1e77393b453bed924969100e283 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 21 May 2024 17:41:56 -0500 Subject: [PATCH 106/139] (feat) normalize spreads distribution graph --- frontend/pages/config/dman_maker_v2/app.py | 2 +- frontend/pages/config/pmm_simple/app.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/pages/config/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py index 0053c98..65e6546 100644 --- a/frontend/pages/config/dman_maker_v2/app.py +++ b/frontend/pages/config/dman_maker_v2/app.py @@ -24,7 +24,7 @@ st.write("---") inputs = user_inputs() with st.expander("Executor Distribution:", expanded=True): - fig = create_executors_distribution_traces(inputs) + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) st.plotly_chart(fig, use_container_width=True) dca_inputs = get_dca_distribution_inputs() diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index 5040909..a20886f 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -20,8 +20,10 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") # Get user inputs inputs = user_inputs() -fig = create_executors_distribution_traces(inputs) -st.plotly_chart(fig, use_container_width=True) + +with st.expander("Executor Distribution:", expanded=True): + fig = create_executors_distribution_traces(inputs) + st.plotly_chart(fig, use_container_width=True) bt_results = backtesting_section(inputs, backend_api_client) if bt_results: From 811b84a884ba73f5697f9fc5f102f9b36b517756 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 22 May 2024 19:43:01 -0500 Subject: [PATCH 107/139] (feat) improve data collection --- frontend/components/dca_distribution.py | 2 +- frontend/components/deploy_v2_with_controllers.py | 4 ++-- frontend/components/directional_trading_general_inputs.py | 4 ++-- frontend/components/launch_strategy_v2.py | 2 +- frontend/components/market_making_general_inputs.py | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/components/dca_distribution.py b/frontend/components/dca_distribution.py index f7851f1..c2f2fd0 100644 --- a/frontend/components/dca_distribution.py +++ b/frontend/components/dca_distribution.py @@ -50,7 +50,7 @@ def get_dca_distribution_inputs(): help="Enter the trailing stop delta as a percentage (e.g., 0.3 for 0.3%).") / 100 return { - "dca_spreads": spreads_normalized, + "dca_spreads": [spread /100 for spread in spreads_normalized], "dca_amounts": orders_amount_normalized, "stop_loss": sl, "take_profit": tp, diff --git a/frontend/components/deploy_v2_with_controllers.py b/frontend/components/deploy_v2_with_controllers.py index 1c0cba4..49d3e43 100644 --- a/frontend/components/deploy_v2_with_controllers.py +++ b/frontend/components/deploy_v2_with_controllers.py @@ -18,7 +18,7 @@ class LaunchV2WithControllers: self._controller_configs_available = self._backend_api_client.get_all_controllers_config() self._controller_config_selected = [] self._bot_name = None - self._image_name = "hummingbot/hummingbot:latest" + self._image_name = "dardonacci/hummingbot:latest" self._credentials = "master_account" def _set_bot_name(self, bot_name): @@ -49,7 +49,7 @@ class LaunchV2WithControllers: self._backend_api_client.add_script_config(script_config) deploy_config = { "instance_name": bot_name, - "script": "v2_generic_with_cash_out.py", + "script": "v2_with_controllers.py", "script_config": bot_name + ".yml", "image": self._image_name, "credentials_profile": self._credentials, diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py index 3e8fb5e..2edbeae 100644 --- a/frontend/components/directional_trading_general_inputs.py +++ b/frontend/components/directional_trading_general_inputs.py @@ -5,9 +5,9 @@ def get_directional_trading_general_inputs(): with st.expander("General Settings", expanded=True): c1, c2, c3, c4, c5, c6, c7 = st.columns(7) with c1: - connector_name = st.text_input("Connector", value="kucoin", + connector_name = st.text_input("Connector", value="binance", help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") - candles_connector_name = st.text_input("Candles Connector", value="kucoin", + candles_connector_name = st.text_input("Candles Connector", value="binance", 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="WLD-USDT", diff --git a/frontend/components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py index 4434aa2..cd7f256 100644 --- a/frontend/components/launch_strategy_v2.py +++ b/frontend/components/launch_strategy_v2.py @@ -67,7 +67,7 @@ class LaunchStrategyV2(Dashboard.Item): self._backend_api_client.add_script_config(script_config) deploy_config = { "instance_name": bot_name, - "script": "v2_generic_with_cash_out.py", + "script": "v2_with_controllers.py", "script_config": bot_name + ".yml", "image": self._image_name, "credentials_profile": self._credentials, diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py index dc9d378..7cdc15f 100644 --- a/frontend/components/market_making_general_inputs.py +++ b/frontend/components/market_making_general_inputs.py @@ -8,7 +8,7 @@ def get_market_making_general_inputs(custom_candles=False): candles_trading_pair = None interval = None with c1: - connector_name = st.text_input("Connector", value="kucoin", + connector_name = st.text_input("Connector", value="binance", help="Enter the name of the exchange to trade on (e.g., binance_perpetual).") with c2: trading_pair = st.text_input("Trading Pair", value="WLD-USDT", @@ -30,7 +30,7 @@ def get_market_making_general_inputs(custom_candles=False): help="Enter the refresh time in minutes for executors (e.g., 60).") * 60 if custom_candles: with c1: - candles_connector = st.text_input("Candles Connector", value="kucoin", + candles_connector = st.text_input("Candles Connector", value="binance", 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="WLD-USDT", From b084a133eebe3663a3b55154120aaef764886970 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 22 May 2024 19:43:18 -0500 Subject: [PATCH 108/139] (feat) normalize pages --- frontend/pages/config/bollinger_v1/app.py | 5 ++--- frontend/pages/config/dman_maker_v2/app.py | 2 +- frontend/pages/config/macd_bb_v1/app.py | 5 ++--- frontend/pages/config/pmm_dynamic/app.py | 5 ++--- frontend/pages/config/pmm_simple/app.py | 2 +- frontend/pages/config/supertrend_v1/app.py | 5 ++--- frontend/pages/config/utils.py | 11 +++++++++-- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/frontend/pages/config/bollinger_v1/app.py b/frontend/pages/config/bollinger_v1/app.py index 9d88100..a8a8b26 100644 --- a/frontend/pages/config/bollinger_v1/app.py +++ b/frontend/pages/config/bollinger_v1/app.py @@ -34,10 +34,9 @@ st.write("---") inputs = user_inputs() st.write("### Visualizing Bollinger Bands and Trading Signals") -days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) -max_records = get_max_records(days_to_visualize, inputs["interval"]) +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"], max_records=max_records) +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, diff --git a/frontend/pages/config/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py index 65e6546..e840b23 100644 --- a/frontend/pages/config/dman_maker_v2/app.py +++ b/frontend/pages/config/dman_maker_v2/app.py @@ -66,4 +66,4 @@ if bt_results: st.write("---") render_close_types(bt_results["results"]) st.write("---") -render_save_config("dman_maker_v2", inputs) \ No newline at end of file +render_save_config("dman_maker_v2", config) \ No newline at end of file diff --git a/frontend/pages/config/macd_bb_v1/app.py b/frontend/pages/config/macd_bb_v1/app.py index c0c6f4c..961d4d5 100644 --- a/frontend/pages/config/macd_bb_v1/app.py +++ b/frontend/pages/config/macd_bb_v1/app.py @@ -29,10 +29,9 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B inputs = user_inputs() st.write("### Visualizing MACD Bollinger Trading Signals") -days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) -max_records = get_max_records(days_to_visualize, inputs["interval"]) +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"], max_records=max_records) +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, diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py index 474d2da..ecb8840 100644 --- a/frontend/pages/config/pmm_dynamic/app.py +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -33,10 +33,9 @@ inputs = user_inputs() st.write("### Visualizing MACD and NATR indicators for PMM Dynamic") st.text("The MACD is used to shift the mid price and the NATR to make the spreads dynamic. " "In the order distributions graph, we are going to see the values of the orders affected by the average NATR") -days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) -max_records = get_max_records(days_to_visualize, inputs["interval"]) +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"], max_records=max_records) +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"), diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index a20886f..a7b76b5 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -22,7 +22,7 @@ st.text("This tool will let you create a config for PMM Simple, backtest and upl inputs = user_inputs() with st.expander("Executor Distribution:", expanded=True): - fig = create_executors_distribution_traces(inputs) + fig = create_executors_distribution_traces(inputs["buy_spreads"], inputs["sell_spreads"], inputs["buy_amounts_pct"], inputs["sell_amounts_pct"], inputs["total_amount_quote"]) st.plotly_chart(fig, use_container_width=True) bt_results = backtesting_section(inputs, backend_api_client) diff --git a/frontend/pages/config/supertrend_v1/app.py b/frontend/pages/config/supertrend_v1/app.py index ec62ca6..0b38f63 100644 --- a/frontend/pages/config/supertrend_v1/app.py +++ b/frontend/pages/config/supertrend_v1/app.py @@ -27,10 +27,9 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B inputs = user_inputs() st.write("### Visualizing Supertrend Trading Signals") -days_to_visualize = st.number_input("Days to Visualize", min_value=1, max_value=365, value=7) -max_records = get_max_records(days_to_visualize, inputs["interval"]) +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"], max_records=max_records) +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, diff --git a/frontend/pages/config/utils.py b/frontend/pages/config/utils.py index 8494824..df1ec82 100644 --- a/frontend/pages/config/utils.py +++ b/frontend/pages/config/utils.py @@ -1,3 +1,5 @@ +import datetime + import streamlit as st import pandas as pd @@ -13,9 +15,14 @@ def get_max_records(days_to_download: int, interval: str) -> int: @st.cache_data -def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m", max_records=5000): +def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m", days=7): backend_client = BackendAPIClient(BACKEND_API_HOST, BACKEND_API_PORT) - df = pd.DataFrame(backend_client.get_real_time_candles(connector_name, trading_pair, interval, max_records)) + 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='ms') return df From fd3fbe1ba32aa9e10b206fd7e21cb48ac930626b Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 22 May 2024 19:43:29 -0500 Subject: [PATCH 109/139] (feat) fix inputs --- frontend/visualization/dca_builder.py | 9 ++-- frontend/visualization/executors.py | 2 +- .../visualization/executors_distribution.py | 52 ++++++++++++++++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/frontend/visualization/dca_builder.py b/frontend/visualization/dca_builder.py index 501b5fc..bfa3e7b 100644 --- a/frontend/visualization/dca_builder.py +++ b/frontend/visualization/dca_builder.py @@ -17,11 +17,12 @@ def create_dca_graph(dca_inputs, dca_amount): tech_colors = theme.get_color_scheme() dca_order_amounts = [amount_dist * dca_amount for amount_dist in dca_inputs["dca_amounts"]] n_levels = len(dca_inputs["dca_spreads"]) + dca_spreads = [spread * 100 for spread in dca_inputs["dca_spreads"]] break_even_values = [] take_profit_values = [] for level in range(n_levels): - dca_spreads_normalized = [spread + 0.01 for spread in dca_inputs["dca_spreads"][:level + 1]] + dca_spreads_normalized = [spread + 0.01 for spread in dca_spreads[:level + 1]] amounts = dca_order_amounts[:level + 1] break_even = (sum([spread * amount for spread, amount in zip(dca_spreads_normalized, amounts)]) / sum( amounts)) - 0.01 @@ -31,7 +32,7 @@ def create_dca_graph(dca_inputs, dca_amount): accumulated_amount = [sum(dca_order_amounts[:i + 1]) for i in range(len(dca_order_amounts))] # Calculate unrealized PNL - cum_unrealized_pnl = calculate_unrealized_pnl(dca_inputs["dca_spreads"], break_even_values, accumulated_amount) + cum_unrealized_pnl = calculate_unrealized_pnl(dca_spreads, break_even_values, accumulated_amount) # Create Plotly figure with secondary y-axis and a dark theme fig = make_subplots(specs=[[{"secondary_y": True}]]) @@ -39,7 +40,7 @@ def create_dca_graph(dca_inputs, dca_amount): # Update the Scatter Plots and Horizontal Lines fig.add_trace( - go.Scatter(x=list(range(len(dca_inputs["dca_spreads"]))), y=dca_inputs["dca_spreads"], name='Spread (%)', + go.Scatter(x=list(range(len(dca_spreads))), y=dca_spreads, name='Spread (%)', mode='lines+markers', line=dict(width=3, color=tech_colors['spread'])), secondary_y=False) fig.add_trace( @@ -99,7 +100,7 @@ def create_dca_graph(dca_inputs, dca_amount): # Update Annotations for Spread and Break Even for i, (spread, be_value, tp_value) in enumerate( - zip(dca_inputs["dca_spreads"], break_even_values, take_profit_values)): + zip(dca_spreads, break_even_values, take_profit_values)): fig.add_annotation(x=i, y=spread, text=f"{spread:.2f}%", showarrow=True, arrowhead=1, yshift=10, xshift=-2, font=dict(color=tech_colors['spread'])) fig.add_annotation(x=i, y=be_value, text=f"{be_value:.2f}%", showarrow=True, arrowhead=1, yshift=5, xshift=-2, diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py index 42c0870..83fff4a 100644 --- a/frontend/visualization/executors.py +++ b/frontend/visualization/executors.py @@ -15,7 +15,7 @@ def add_executors_trace(fig, executors, row, col): if executor.filled_amount_quote == 0: fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, entry_price], mode='lines', - line=dict(color='blue', width=2, dash="dash"), name=name), row=row, col=col) + line=dict(color='grey', width=2, dash="dash"), name=name), row=row, col=col) else: if executor.net_pnl_quote > Decimal(0): fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', diff --git a/frontend/visualization/executors_distribution.py b/frontend/visualization/executors_distribution.py index b41a26d..5f2220c 100644 --- a/frontend/visualization/executors_distribution.py +++ b/frontend/visualization/executors_distribution.py @@ -1,3 +1,4 @@ +import numpy as np import plotly.graph_objects as go import frontend.visualization.theme as theme @@ -12,6 +13,10 @@ def create_executors_distribution_traces(buy_spreads, sell_spreads, buy_amounts_ buy_order_levels = len(buy_spread_distributions) sell_order_levels = len(sell_spread_distributions) + # Calculate total volumes + total_buy_volume = sum(buy_order_amounts_quote) + total_sell_volume = sum(sell_order_amounts_quote) + # Create the figure with a dark theme and secondary y-axis fig = go.Figure() @@ -37,7 +42,7 @@ def create_executors_distribution_traces(buy_spreads, sell_spreads, buy_amounts_ for i, value in enumerate(buy_order_amounts_quote): fig.add_annotation( x=-buy_spread_distributions[i], - y=value + 0.01 * max(buy_order_amounts_quote), # Offset the text slightly above the bar + y=value + 0.03 * max(buy_order_amounts_quote), # Offset the text slightly above the bar text=str(round(value, 2)), showarrow=False, font=dict(color=colors['buy'], size=10) @@ -47,12 +52,55 @@ def create_executors_distribution_traces(buy_spreads, sell_spreads, buy_amounts_ for i, value in enumerate(sell_order_amounts_quote): fig.add_annotation( x=sell_spread_distributions[i], - y=value + 0.01 * max(sell_order_amounts_quote), # Offset the text slightly above the bar + y=value + 0.03 * max(sell_order_amounts_quote), # Offset the text slightly above the bar text=str(round(value, 2)), showarrow=False, font=dict(color=colors['sell'], size=10) ) + max_y = max(max(buy_order_amounts_quote), max(sell_order_amounts_quote)) + # Add annotations for total volumes + fig.add_annotation( + x=-np.mean(buy_spread_distributions), + y=max_y, + text=f'Total Buy\n{round(total_buy_volume, 2)}', + showarrow=False, + font=dict(color=colors['buy'], size=12, family="Arial Black"), + align='center' + ) + + fig.add_annotation( + x=np.mean(sell_spread_distributions), + y=max_y, + text=f'Total Sell\n{round(total_sell_volume, 2)}', + showarrow=False, + font=dict(color=colors['sell'], size=12, family="Arial Black"), + align='center' + ) + + # Draw circles around the total volume annotations + fig.add_shape( + type='circle', + xref='x', + yref='y', + x0=-np.mean(buy_spread_distributions) - 0.4, + y0=max_y - 0.05 * max_y, + x1=-np.mean(buy_spread_distributions) + 0.4, + y1=max_y + 0.05 * max_y, + line=dict(color=colors['buy']) + ) + + fig.add_shape( + type='circle', + xref='x', + yref='y', + x0=np.mean(sell_spread_distributions) - 0.4, + y0=max_y - 0.05 * max_y, + x1=np.mean(sell_spread_distributions) + 0.4, + y1=max_y + 0.05 * max_y, + line=dict(color=colors['sell']) + ) + # Apply the theme layout layout_settings = theme.get_default_layout("Market Maker Order Distribution") fig.update_layout(**layout_settings) From 4fb2656e8fec56808dc4ac938faa62cd297766d9 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 22 May 2024 19:43:35 -0500 Subject: [PATCH 110/139] (feat) adapt notebooks --- ...ynb => analyze_optimization_results.ipynb} | 0 .../01_strategy_design_dman_maker.ipynb | 11 +- .../02_single_controller_backtest.ipynb | 39353 +++++++++++++++- .../dman_maker/03_optimization_notebook.ipynb | 587 +- .../01_strategy_design_marroq.ipynb | 0 .../02_single_controller_backtest.ipynb | 0 .../03_optimization_notebook.ipynb | 0 ...egy_design_supertrend_multitimeframe.ipynb | 0 .../02_controller_backtest.ipynb | 0 .../03_optimization_notebook.ipynb | 0 10 files changed, 39522 insertions(+), 429 deletions(-) rename quants_lab/research_notebooks/{01_analyze_optimization_results.ipynb => analyze_optimization_results.ipynb} (100%) rename quants_lab/research_notebooks/{marroq => marroq (old)}/01_strategy_design_marroq.ipynb (100%) rename quants_lab/research_notebooks/{marroq => marroq (old)}/02_single_controller_backtest.ipynb (100%) rename quants_lab/research_notebooks/{marroq => marroq (old)}/03_optimization_notebook.ipynb (100%) rename quants_lab/research_notebooks/{supertrend_multitimeframe => supertrend_multitimeframe(old)}/01_strategy_design_supertrend_multitimeframe.ipynb (100%) rename quants_lab/research_notebooks/{supertrend_multitimeframe => supertrend_multitimeframe(old)}/02_controller_backtest.ipynb (100%) rename quants_lab/research_notebooks/{supertrend_multitimeframe => supertrend_multitimeframe(old)}/03_optimization_notebook.ipynb (100%) diff --git a/quants_lab/research_notebooks/01_analyze_optimization_results.ipynb b/quants_lab/research_notebooks/analyze_optimization_results.ipynb similarity index 100% rename from quants_lab/research_notebooks/01_analyze_optimization_results.ipynb rename to quants_lab/research_notebooks/analyze_optimization_results.ipynb diff --git a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb index befef71..5de4890 100644 --- a/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb +++ b/quants_lab/research_notebooks/dman_maker/01_strategy_design_dman_maker.ipynb @@ -3,7 +3,7 @@ { "cell_type": "markdown", "source": [ - "# RESEARCH NOTEBOOK --> SUPERTREND" + "# RESEARCH NOTEBOOK --> DMAN Maker V2" ], "metadata": { "collapsed": false @@ -175,15 +175,6 @@ "start_time": "2024-04-18T22:03:34.504796Z" } } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false - } } ], "metadata": { diff --git a/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb b/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb index 6d32473..b480200 100644 --- a/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb +++ b/quants_lab/research_notebooks/dman_maker/02_single_controller_backtest.ipynb @@ -2,11 +2,15 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:33:43.257748Z", + "start_time": "2024-05-21T23:33:43.251873Z" } }, "outputs": [], @@ -20,26 +24,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "outputs": [], "source": [ "from decimal import Decimal\n", "\n", - "# Market configuration\n", - "exchange = \"binance_perpetual\"\n", + "# Controller configuration\n", + "connector_name = \"kucoin\"\n", "trading_pair = \"WLD-USDT\"\n", - "interval = \"3m\"\n", - "\n", - "# Account configuration\n", - "initial_portfolio_usd = 1000\n", - "order_amount = Decimal(\"25\")\n", - "n_levels = 1\n", - "leverage = 20\n", + "total_amount_quote = 1000\n", "trade_cost = 0.0006\n", - "\n", - "# Backtest period\n", - "start = \"2023-01-01\"\n", - "end = \"2024-01-02\"\n", + "buy_spreads = [Decimal(\"0.002\")]\n", + "sell_spreads = [Decimal(\"0.002\")]\n", + "buy_amounts_pct = [Decimal(\"1\")]\n", + "sell_amounts_pct = [Decimal(\"1\")]\n", + "executor_refresh_time = 60\n", + "cooldown_time = 3600\n", + "top_executor_refresh_time = 60\n", + "executor_activation_bounds = [Decimal(\"0.01\")]\n", + "dca_spreads = [Decimal(\"0.\"), Decimal(\"0.002\"), Decimal(\"0.008\"), Decimal(\"0.01\"), Decimal(\"0.015\"), Decimal(\"0.02\")]\n", + "dca_amounts = [Decimal(\"0.1\"), Decimal(\"0.2\"), Decimal(\"0.3\"), Decimal(\"0.4\"), Decimal(\"0.5\"), Decimal(\"0.6\")]\n", "\n", "# Triple barrier configuration\n", "stop_loss = Decimal(\"0.015\")\n", @@ -49,182 +53,123 @@ "trailing_stop_trailing_delta = Decimal(\"0.004\")" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:21.058600Z", + "start_time": "2024-05-21T23:53:21.024538Z" + } } }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:21.062445Z", + "start_time": "2024-05-21T23:53:21.029893Z" } }, "outputs": [], "source": [ - "from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder\n", - "from hummingbot.smart_components.strategy_frameworks.data_types import (\n", - " TripleBarrierConf\n", - ")\n", + "from hummingbot.strategy_v2.executors.position_executor.data_types import TrailingStop\n", + "from quants_lab.controllers.market_making.dman_maker_v2 import DManMakerV2Config, DManMakerV2\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", - " # for directional strategies we don't need spreads since we are going to use market orders to enter\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", + "# Creating the instance of the configuration and the controller\n", + "config = DManMakerV2Config(\n", + " connector_name=connector_name,\n", + " trading_pair=trading_pair,\n", + " total_amount_quote=total_amount_quote,\n", + " buy_spreads=buy_spreads,\n", + " sell_spreads=sell_spreads,\n", + " buy_amounts_pct=buy_amounts_pct,\n", + " sell_amounts_pct=sell_amounts_pct,\n", + " executor_refresh_time=executor_refresh_time,\n", + " cooldown_time=cooldown_time,\n", + " top_executor_refresh_time=top_executor_refresh_time,\n", + " executor_activation_bounds=executor_activation_bounds,\n", + " stop_loss=stop_loss,\n", + " take_profit=take_profit,\n", + " time_limit=time_limit,\n", + " trailing_stop=TrailingStop(activation_price=trailing_stop_activation_price_delta,\n", + " trailing_delta=trailing_stop_trailing_delta),\n", + " dca_spreads=dca_spreads,\n", + " dca_amounts=dca_amounts\n", ")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.099177Z", + "start_time": "2024-05-21T23:53:39.419416Z" } }, - "outputs": [], - "source": [ - "# Let's inpect the order levels\n", - "order_levels" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-05-21 18:53:41,626 - asyncio - ERROR - Unclosed client session\n", + "client_session: \n", + "2024-05-21 18:53:41,627 - asyncio - ERROR - Unclosed connector\n", + "connections: ['[(, 55330.4882885)]']\n", + "connector: \n" + ] } - }, - "outputs": [], + ], "source": [ - "import sys\n", - "from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig\n", - "from quants_lab.controllers.supertrend import SuperTrend, SuperTrendConfig\n", + "import pandas as pd\n", + "from hummingbot.strategy_v2.backtesting import MarketMakingBacktesting\n", "\n", - "# Controller configuration\n", - "length = 100\n", - "multiplier = 3.0\n", - "percentage_threshold = 0.01\n", + "backtesting_engine = MarketMakingBacktesting()\n", "\n", - "# Creating the instance of the configuration and the controller\n", - "config = SuperTrendConfig(\n", - " exchange=exchange,\n", - " trading_pair=trading_pair,\n", - " order_levels=order_levels,\n", - " candles_config=[\n", - " CandlesConfig(connector=exchange, trading_pair=trading_pair, interval=interval, max_records=sys.maxsize),\n", - " ],\n", - " leverage=leverage,\n", - " length=length,\n", - " multiplier=multiplier,\n", - " percentage_threshold=percentage_threshold,\n", - ")\n", - "controller = SuperTrend(config=config)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "from quants_lab.strategy.strategy_analysis import StrategyAnalysis\n", + "# Backtest period\n", + "start = \"2024-01-01\"\n", + "end = \"2024-01-10\"\n", + "backtesting_resolution = \"1m\"\n", "\n", - "from hummingbot.smart_components.strategy_frameworks.directional_trading.directional_trading_backtesting_engine import \\\n", - " DirectionalTradingBacktestingEngine\n", - "\n", - "# Creating the backtesting engine and loading the historical data\n", - "engine = DirectionalTradingBacktestingEngine(controller=controller)\n", - "engine.load_controller_data(\"../../../data/candles\")" + "start_time = pd.to_datetime(start).timestamp() * 1e3\n", + "end_time = pd.to_datetime(end).timestamp() * 1e3\n", + "backtesting_results = await backtesting_engine.run_backtesting(\n", + " controller_config=config, trade_cost=trade_cost,\n", + " start=int(start_time), end=int(end_time),\n", + " backtesting_resolution=backtesting_resolution)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.105729Z", + "start_time": "2024-05-21T23:53:53.098438Z" } }, - "outputs": [], - "source": [ - "# Let's see what is inside the candles of the controller\n", - "engine.controller.candles" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false + "outputs": [ + { + "data": { + "text/plain": "dict_keys(['executors', 'results', 'processed_data'])" + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" } - }, - "outputs": [], - "source": [ - "engine.controller.candles[0].candles_df" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "# Let's understand what is inside the processed data since this is what we are going to use when generating the signal ;)\n", - "engine.controller.get_processed_data()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "# Let's run the backtesting\n", - "\n", - "backtesting_results = engine.run_backtesting(initial_portfolio_usd=initial_portfolio_usd,\n", - " trade_cost=trade_cost,\n", - " start=start, end=end)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], + ], "source": [ "# Let's see what is inside the backtesting results\n", "backtesting_results.keys()" @@ -232,14 +177,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.107299Z", + "start_time": "2024-05-21T23:53:53.104827Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": "{'reference_price': Decimal('2.497599999999999820232687852694652974605560302734375'),\n 'spread_multiplier': Decimal('1'),\n 'features': timestamp_bt open_bt high_bt low_bt close_bt volume_bt \\\n 0 1704067200000 3.6272 3.6335 3.6268 3.6335 1247.4443 \n 1 1704067260000 3.6333 3.639 3.6317 3.639 373.5355 \n 2 1704067320000 3.6476 3.657 3.6476 3.657 504.2724 \n 3 1704067380000 3.6601 3.6622 3.6586 3.6622 43.5494 \n 4 1704067440000 3.6622 3.6622 3.6622 3.6622 0 \n ... ... ... ... ... ... ... \n 12964 1704844560000 2.4965 2.4965 2.495 2.495 5.1705 \n 12965 1704844620000 2.4978 2.5025 2.4978 2.5025 37.5497 \n 12966 1704844680000 2.5025 2.5025 2.5025 2.5025 0 \n 12967 1704844740000 2.5027 2.5034 2.5006 2.5006 63.5077 \n 12968 1704844800000 2.5026 2.5026 2.4976 2.4976 1009.2771 \n \n quote_asset_volume_bt n_trades_bt taker_buy_base_volume_bt \\\n 0 4528.54482718 0 0 \n 1 1357.28548746 0 0 \n 2 1843.41100357 0 0 \n 3 159.35951529 0 0 \n 4 0 0 0 \n ... ... ... ... \n 12964 12.9018441 0 0 \n 12965 93.93699135 0 0 \n 12966 0 0 0 \n 12967 158.88729718 0 0 \n 12968 2523.49246652 0 0 \n \n taker_buy_quote_volume_bt reference_price spread_multiplier signal \\\n 0 0 3.6335 1 0 \n 1 0 3.639 1 0 \n 2 0 3.657 1 0 \n 3 0 3.6622 1 0 \n 4 0 3.6622 1 0 \n ... ... ... ... ... \n 12964 0 2.495 1 0 \n 12965 0 2.5025 1 0 \n 12966 0 2.5025 1 0 \n 12967 0 2.5006 1 0 \n 12968 0 2.4976 1 0 \n \n timestamp open high low close volume \n 0 1704067200000 3.6272 3.6335 3.6268 3.6335 1247.4443 \n 1 1704067260000 3.6333 3.639 3.6317 3.639 373.5355 \n 2 1704067320000 3.6476 3.657 3.6476 3.657 504.2724 \n 3 1704067380000 3.6601 3.6622 3.6586 3.6622 43.5494 \n 4 1704067440000 3.6622 3.6622 3.6622 3.6622 0 \n ... ... ... ... ... ... ... \n 12964 1704844560000 2.4965 2.4965 2.495 2.495 5.1705 \n 12965 1704844620000 2.4978 2.5025 2.4978 2.5025 37.5497 \n 12966 1704844680000 2.5025 2.5025 2.5025 2.5025 0 \n 12967 1704844740000 2.5027 2.5034 2.5006 2.5006 63.5077 \n 12968 1704844800000 2.5026 2.5026 2.4976 2.4976 1009.2771 \n \n [12961 rows x 19 columns]}" + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# now let's analyze each of the dataframes\n", "\n", @@ -249,29 +207,55 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 50, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.140339Z", + "start_time": "2024-05-21T23:53:53.117236Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": "[ExecutorInfo(id='ATh9By7hcv5V8pLarj4Cy69kKuYgBpNYag1QS428cgjc', timestamp=1704067200000.0, type='dca_executor', close_timestamp=1704067320000.0, close_type=, status=, config=DCAExecutorConfig(id='ATh9By7hcv5V8pLarj4Cy69kKuYgBpNYag1QS428cgjc', type='dca_executor', timestamp=1704067200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.626233000000000173583529495'), Decimal('3.618980534000000173236362436'), Decimal('3.597223136000000172194861259'), Decimal('3.589970670000000171847694200'), Decimal('3.571839505000000170979776553'), Decimal('3.553708340000000170111858905')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.657, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.626233}, controller_id=None),\n ExecutorInfo(id='J5kLBeH8TTiQRd1iMTnEG9hHRwp44sBRbgta7PhkP7U4', timestamp=1704067200000.0, type='dca_executor', close_timestamp=1704072300000.0, close_type=, status=, config=DCAExecutorConfig(id='J5kLBeH8TTiQRd1iMTnEG9hHRwp44sBRbgta7PhkP7U4', type='dca_executor', timestamp=1704067200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.640767000000000174582411026'), Decimal('3.648048534000000174931575848'), Decimal('3.669893136000000175979070314'), Decimal('3.677174670000000176328235136'), Decimal('3.695378505000000177201147191'), Decimal('3.713582340000000178074059247')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01516281491309645797216365536996818264015018939971923828125'), net_pnl_quote=Decimal('-7.67635555945642789055227694916538894176483154296875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.7496, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.686505601555519}, controller_id=None),\n ExecutorInfo(id='FbqtGEUMzuDrG1G9cEP3aV31rLQwjWGxLoyV7TnDeDXs', timestamp=1704067380000.0, type='dca_executor', close_timestamp=1704072360000.0, close_type=, status=, config=DCAExecutorConfig(id='FbqtGEUMzuDrG1G9cEP3aV31rLQwjWGxLoyV7TnDeDXs', type='dca_executor', timestamp=1704067380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.654875599999999899861377606'), Decimal('3.647565848799999900061654851'), Decimal('3.625636595199999900662486585'), Decimal('3.618326843999999900862763830'), Decimal('3.600052465999999901363456942'), Decimal('3.581778087999999901864150054')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.033093745264367972758190461490812595002353191375732421875'), net_pnl_quote=Decimal('2.36068716219158236668818062753416597843170166015625'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.7679, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.650005685649132}, controller_id=None),\n ExecutorInfo(id='HVMBhGCSPyoW2MxZEFeKyq5CmdqEDVmaYz4Up44JrfV9', timestamp=1704075900000.0, type='dca_executor', close_timestamp=1704076020000.0, close_type=, status=, config=DCAExecutorConfig(id='HVMBhGCSPyoW2MxZEFeKyq5CmdqEDVmaYz4Up44JrfV9', type='dca_executor', timestamp=1704075900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.688762800000000005848979632'), Decimal('3.696140325600000005860677591'), Decimal('3.718272902400000005895771469'), Decimal('3.725650428000000005907469428'), Decimal('3.744094242000000005936714326'), Decimal('3.762538056000000005965959225')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6849, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6887628}, controller_id=None),\n ExecutorInfo(id='Giv5Y3r8oT7THrkusS6Pjef5hU8Wc5rHhiy47RugTyTa', timestamp=1704076080000.0, type='dca_executor', close_timestamp=1704076200000.0, close_type=, status=, config=DCAExecutorConfig(id='Giv5Y3r8oT7THrkusS6Pjef5hU8Wc5rHhiy47RugTyTa', type='dca_executor', timestamp=1704076080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.693772799999999899054614614'), Decimal('3.701160345599999898852723843'), Decimal('3.723322982399999898247051531'), Decimal('3.730710527999999898045160760'), Decimal('3.749179391999999897540433833'), Decimal('3.767648255999999897035706906')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6843, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6937728}, controller_id=None),\n ExecutorInfo(id='A2nCeBXQfU9WT8xQkW9fkuS1EUqQaqaUCeX5R5XkLXbd', timestamp=1704076260000.0, type='dca_executor', close_timestamp=1704076380000.0, close_type=, status=, config=DCAExecutorConfig(id='A2nCeBXQfU9WT8xQkW9fkuS1EUqQaqaUCeX5R5XkLXbd', type='dca_executor', timestamp=1704076260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('71.99999999999999999999999998'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.688362000000000049990719895'), Decimal('3.695738724000000050090701335'), Decimal('3.717868896000000050390645654'), Decimal('3.725245620000000050490627094'), Decimal('3.743687430000000050740580693'), Decimal('3.762129240000000050990534293')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6783, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.688362}, controller_id=None),\n ExecutorInfo(id='EpHqffK2HVxQJZnwy3DtiFAfpioZH4X2KDGTQvDxuZE2', timestamp=1704076440000.0, type='dca_executor', close_timestamp=1704076560000.0, close_type=, status=, config=DCAExecutorConfig(id='EpHqffK2HVxQJZnwy3DtiFAfpioZH4X2KDGTQvDxuZE2', type='dca_executor', timestamp=1704076440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.685957199999999869863773204'), Decimal('3.693329114399999869603500750'), Decimal('3.715444857599999868822683390'), Decimal('3.722816771999999868562410936'), Decimal('3.741246557999999867911729802'), Decimal('3.759676343999999867261048668')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6786, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6859572}, controller_id=None),\n ExecutorInfo(id='76VV1Dj9mDBg7vpAsSR1i55gbndn1Wds9auLpiAd6CQk', timestamp=1704076620000.0, type='dca_executor', close_timestamp=1704076740000.0, close_type=, status=, config=DCAExecutorConfig(id='76VV1Dj9mDBg7vpAsSR1i55gbndn1Wds9auLpiAd6CQk', type='dca_executor', timestamp=1704076620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.680546400000000020799878485'), Decimal('3.687907492800000020841478242'), Decimal('3.709990771200000020966277513'), Decimal('3.717351864000000021007877270'), Decimal('3.735754596000000021111876662'), Decimal('3.754157328000000021215876055')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6715, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6805464}, controller_id=None),\n ExecutorInfo(id='E3vRDqYQbgm6YFYYxqDAjd5Wcy4SRZYJ11AvibZbHVTU', timestamp=1704072360000.0, type='dca_executor', close_timestamp=1704076860000.0, close_type=, status=, config=DCAExecutorConfig(id='E3vRDqYQbgm6YFYYxqDAjd5Wcy4SRZYJ11AvibZbHVTU', type='dca_executor', timestamp=1704072360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.760364200000000026789752361'), Decimal('3.752843471600000026736172856'), Decimal('3.730281286400000026575434342'), Decimal('3.722760558000000026521854837'), Decimal('3.703958737000000026387906076'), Decimal('3.685156916000000026253957314')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0145037687138440223033608589275900158099830150604248046875'), net_pnl_quote=Decimal('-7.1610631385472256482671582489274442195892333984375'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.6552, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.713420848392969}, controller_id=None),\n ExecutorInfo(id='2oEWXZQv7ewiAUt6VRU5VHLqBgJc3vJXVU3SCjS9FyxT', timestamp=1704076800000.0, type='dca_executor', close_timestamp=1704076920000.0, close_type=, status=, config=DCAExecutorConfig(id='2oEWXZQv7ewiAUt6VRU5VHLqBgJc3vJXVU3SCjS9FyxT', type='dca_executor', timestamp=1704076800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000006'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.676137600000000061381633108'), Decimal('3.683489875200000061504396374'), Decimal('3.705546700800000061872686173'), Decimal('3.712898976000000061995449439'), Decimal('3.731279664000000062302357605'), Decimal('3.749660352000000062609265770')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6576, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6761376}, controller_id=None),\n ExecutorInfo(id='6GmfMuYpmpcoZ8S4qsdwsNrGFLbykxRTPjA4MRjho8YE', timestamp=1704076980000.0, type='dca_executor', close_timestamp=1704077100000.0, close_type=, status=, config=DCAExecutorConfig(id='6GmfMuYpmpcoZ8S4qsdwsNrGFLbykxRTPjA4MRjho8YE', type='dca_executor', timestamp=1704076980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000006'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.654995399999999942482796498'), Decimal('3.662305390799999942367762091'), Decimal('3.684235363199999942022658870'), Decimal('3.691545353999999941907624463'), Decimal('3.709820330999999941620038445'), Decimal('3.728095307999999941332452428')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6333, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6549954}, controller_id=None),\n ExecutorInfo(id='C3gb5NYU3hcdJfzUvzTaLwXK8t7ZqtvgkC7jNGkBaB3b', timestamp=1704077160000.0, type='dca_executor', close_timestamp=1704077280000.0, close_type=, status=, config=DCAExecutorConfig(id='C3gb5NYU3hcdJfzUvzTaLwXK8t7ZqtvgkC7jNGkBaB3b', type='dca_executor', timestamp=1704077160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.639364199999999884101113677'), Decimal('3.646642928399999883869315904'), Decimal('3.668479113599999883173922586'), Decimal('3.675757841999999882942124814'), Decimal('3.693954662999999882362630382'), Decimal('3.712151483999999881783135951')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6304, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6393641999999997}, controller_id=None),\n ExecutorInfo(id='2Q78Zgrg28Vj1zxYs4RaawWc1uJBbAS1vkkdjQdB9n7i', timestamp=1704077340000.0, type='dca_executor', close_timestamp=1704109320000.0, close_type=, status=, config=DCAExecutorConfig(id='2Q78Zgrg28Vj1zxYs4RaawWc1uJBbAS1vkkdjQdB9n7i', type='dca_executor', timestamp=1704077340000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.637059599999999915427426054'), Decimal('3.644333719199999915258280906'), Decimal('3.666156076799999914750845462'), Decimal('3.673430195999999914581700315'), Decimal('3.691615493999999914158837445'), Decimal('3.709800791999999913735974575')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.031084693949450438454906731067239888943731784820556640625'), net_pnl_quote=Decimal('2.22329572914640749559112009592354297637939453125'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 3.5315, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.641912241437017}, controller_id=None),\n ExecutorInfo(id='AzHcWY7a6BZcEZL1YHHwoZAYq8bSKe9zQQ68nBCBnzCt', timestamp=1704109320000.0, type='dca_executor', close_timestamp=1704109440000.0, close_type=, status=, config=DCAExecutorConfig(id='AzHcWY7a6BZcEZL1YHHwoZAYq8bSKe9zQQ68nBCBnzCt', type='dca_executor', timestamp=1704109320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000006'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.538562999999999861314083081'), Decimal('3.545640125999999861036711247'), Decimal('3.566871503999999860204595746'), Decimal('3.573948629999999859927223912'), Decimal('3.591641444999999859233794327'), Decimal('3.609334259999999858540364743')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5385, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.538563}, controller_id=None),\n ExecutorInfo(id='G676QHWqXVGeBr7MtEw7BCkgJsy1v8gZ6QFUrYouBrBy', timestamp=1704109500000.0, type='dca_executor', close_timestamp=1704109620000.0, close_type=, status=, config=DCAExecutorConfig(id='G676QHWqXVGeBr7MtEw7BCkgJsy1v8gZ6QFUrYouBrBy', type='dca_executor', timestamp=1704109500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.548182200000000136844481578'), Decimal('3.555278564400000137118170541'), Decimal('3.576567657600000137939237431'), Decimal('3.583664022000000138212926394'), Decimal('3.601404933000000138897148802'), Decimal('3.619145844000000139581371210')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5404, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5481822000000003}, controller_id=None),\n ExecutorInfo(id='78aQMv4CM5ZX2qK8cpK4kaZMG9p5LfgaST4upkumjHDe', timestamp=1704109680000.0, type='dca_executor', close_timestamp=1704109800000.0, close_type=, status=, config=DCAExecutorConfig(id='78aQMv4CM5ZX2qK8cpK4kaZMG9p5LfgaST4upkumjHDe', type='dca_executor', timestamp=1704109680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.540567000000000085582770036'), Decimal('3.547648134000000085753935576'), Decimal('3.568891536000000086267432196'), Decimal('3.575972670000000086438597736'), Decimal('3.593675505000000086866511587'), Decimal('3.611378340000000087294425437')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5366, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5405670000000002}, controller_id=None),\n ExecutorInfo(id='DMuck9wctakrNJ2sZ3qNiJ4oNxZvqJ9SPcFDEtoFq7bf', timestamp=1704109860000.0, type='dca_executor', close_timestamp=1704109980000.0, close_type=, status=, config=DCAExecutorConfig(id='DMuck9wctakrNJ2sZ3qNiJ4oNxZvqJ9SPcFDEtoFq7bf', type='dca_executor', timestamp=1704109860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.543673199999999965972977132'), Decimal('3.550760546399999965904923086'), Decimal('3.572022585599999965700760949'), Decimal('3.579109931999999965632706903'), Decimal('3.596828297999999965462571789'), Decimal('3.614546663999999965292436675')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.539, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5436731999999997}, controller_id=None),\n ExecutorInfo(id='9oSUcrrAE3MccCqVK7Hd4rJr2fcq4QLopXju24y6RXnc', timestamp=1704080460000.0, type='dca_executor', close_timestamp=1704123660000.0, close_type=, status=, config=DCAExecutorConfig(id='9oSUcrrAE3MccCqVK7Hd4rJr2fcq4QLopXju24y6RXnc', type='dca_executor', timestamp=1704080460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.569446800000000001269337413'), Decimal('3.562307906400000001266798738'), Decimal('3.540891225600000001259182714'), Decimal('3.533752332000000001256644039'), Decimal('3.515905098000000001250297352'), Decimal('3.498057864000000001243950665')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.00023754063515637728761474567473754859747714363038539886474609375'), net_pnl_quote=Decimal('-0.117282860743757044730273264576680958271026611328125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.5185, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.524886808663259}, controller_id=None),\n ExecutorInfo(id='8Tkirv9ee195HKe5iLoEucwThiHVD5oaa5zVkqXFkMCy', timestamp=1704123660000.0, type='dca_executor', close_timestamp=1704123780000.0, close_type=, status=, config=DCAExecutorConfig(id='8Tkirv9ee195HKe5iLoEucwThiHVD5oaa5zVkqXFkMCy', type='dca_executor', timestamp=1704123660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.511462999999999960851822245'), Decimal('3.504440073999999960930118601'), Decimal('3.483371295999999961165007667'), Decimal('3.476348369999999961243304023'), Decimal('3.458791054999999961439044911'), Decimal('3.441233739999999961634785800')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5228, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.511463}, controller_id=None),\n ExecutorInfo(id='3A2bsXXCUsutNgULychab5goCkaRvWoZSzdmjbfzhk4U', timestamp=1704123840000.0, type='dca_executor', close_timestamp=1704123960000.0, close_type=, status=, config=DCAExecutorConfig(id='3A2bsXXCUsutNgULychab5goCkaRvWoZSzdmjbfzhk4U', type='dca_executor', timestamp=1704123840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.525135600000000006235037486'), Decimal('3.518085328800000006222567411'), Decimal('3.496934515200000006185157186'), Decimal('3.489884244000000006172687111'), Decimal('3.472258566000000006141511924'), Decimal('3.454632888000000006110336736')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5363, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.5251356}, controller_id=None),\n ExecutorInfo(id='3ZGoQ6gtLd49DNDuMbYTkJs9cXGgpPR7MRLxbzCxWbeA', timestamp=1704124020000.0, type='dca_executor', close_timestamp=1704139260000.0, close_type=, status=, config=DCAExecutorConfig(id='3ZGoQ6gtLd49DNDuMbYTkJs9cXGgpPR7MRLxbzCxWbeA', type='dca_executor', timestamp=1704124020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.529227400000000220389605177'), Decimal('3.522168945200000219948825967'), Decimal('3.500993580800000218626488336'), Decimal('3.493935126000000218185709125'), Decimal('3.476288989000000217083761099'), Decimal('3.458642852000000215981813073')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0308962131428041804348527676893354509957134723663330078125'), net_pnl_quote=Decimal('7.30621878415074110790783379343338310718536376953125'), cum_fees_quote=Decimal('0.14188571428571428167941803621943108737468719482421875'), filled_amount_quote=Decimal('236.4761904761904816041351296007633209228515625'), is_active=False, is_trading=False, custom_info={'close_price': 3.6117, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.5052775483326624}, controller_id=None),\n ExecutorInfo(id='HfeBhgPMXUN7FEf9X2hYcEn1vDgmCZ78LQ1igd48eYYM', timestamp=1704139260000.0, type='dca_executor', close_timestamp=1704139380000.0, close_type=, status=, config=DCAExecutorConfig(id='HfeBhgPMXUN7FEf9X2hYcEn1vDgmCZ78LQ1igd48eYYM', type='dca_executor', timestamp=1704139260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714284'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.604476599999999910500304845'), Decimal('3.597267646799999910679304235'), Decimal('3.575640787199999911216302406'), Decimal('3.568431833999999911395301797'), Decimal('3.550409450999999911842800272'), Decimal('3.532387067999999912290298748')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6112, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.6044766}, controller_id=None),\n ExecutorInfo(id='4RRTfhyXDBSH4SpAt5eCNoRwAsCBcgrCnjUQNjTRDocb', timestamp=1704139440000.0, type='dca_executor', close_timestamp=1704139560000.0, close_type=, status=, config=DCAExecutorConfig(id='4RRTfhyXDBSH4SpAt5eCNoRwAsCBcgrCnjUQNjTRDocb', type='dca_executor', timestamp=1704139440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.598887800000000082817099012'), Decimal('3.591690024400000082651464814'), Decimal('3.570096697600000082154562220'), Decimal('3.562898922000000081988928022'), Decimal('3.544904483000000081574842527'), Decimal('3.526910044000000081160757032')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6138, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.5988878}, controller_id=None),\n ExecutorInfo(id='CN8X5KdydpXCEYWBSxvWVFSbai96m8TYai2TJNaSnB4L', timestamp=1704139620000.0, type='dca_executor', close_timestamp=1704139740000.0, close_type=, status=, config=DCAExecutorConfig(id='CN8X5KdydpXCEYWBSxvWVFSbai96m8TYai2TJNaSnB4L', type='dca_executor', timestamp=1704139620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.607869799999999979994085020'), Decimal('3.600654060399999980034096850'), Decimal('3.579006841599999980154132340'), Decimal('3.571791101999999980194144170'), Decimal('3.553751752999999980294173745'), Decimal('3.535712403999999980394203320')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6148, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.6078698}, controller_id=None),\n ExecutorInfo(id='9Hbf6s4dUzibQBzKGtxBYiXmgaFFHa8SxXdwky3rhK2a', timestamp=1704139800000.0, type='dca_executor', close_timestamp=1704139920000.0, close_type=, status=, config=DCAExecutorConfig(id='9Hbf6s4dUzibQBzKGtxBYiXmgaFFHa8SxXdwky3rhK2a', type='dca_executor', timestamp=1704139800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.607370799999999813350518019'), Decimal('3.600156058399999813723816983'), Decimal('3.578511833599999814843713875'), Decimal('3.571297091999999815217012839'), Decimal('3.553260237999999816150260249'), Decimal('3.535223383999999817083507659')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6124, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.6073708}, controller_id=None),\n ExecutorInfo(id='EDn4KZGBAuJkiEJ6tZ6CofT6DqUgZ8wsuTyNNgq5gEYK', timestamp=1704110040000.0, type='dca_executor', close_timestamp=1704153240000.0, close_type=, status=, config=DCAExecutorConfig(id='EDn4KZGBAuJkiEJ6tZ6CofT6DqUgZ8wsuTyNNgq5gEYK', type='dca_executor', timestamp=1704110040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.546078000000000146099923824'), Decimal('3.553170156000000146392123672'), Decimal('3.574446624000000147268723215'), Decimal('3.581538780000000147560923062'), Decimal('3.599269170000000148291422681'), Decimal('3.616999560000000149021922300')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.00854299774488135633954133396628094487823545932769775390625'), net_pnl_quote=Decimal('-4.3249943107002923881054812227375805377960205078125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.6245, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.590627032862249}, controller_id=None),\n ExecutorInfo(id='9ZcJRETDNT2qt5zqyGAfdStFRB4eqj5y9FVVfsJJMVVd', timestamp=1704153240000.0, type='dca_executor', close_timestamp=1704153360000.0, close_type=, status=, config=DCAExecutorConfig(id='9ZcJRETDNT2qt5zqyGAfdStFRB4eqj5y9FVVfsJJMVVd', type='dca_executor', timestamp=1704153240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047616'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.631748999999999832839402135'), Decimal('3.639012497999999832505080939'), Decimal('3.660802991999999831502117352'), Decimal('3.668066489999999831167796156'), Decimal('3.686225234999999830331993167'), Decimal('3.704383979999999829496190178')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6174, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6317489999999997}, controller_id=None),\n ExecutorInfo(id='7qv4cbX8wcYNQoBzjKFX8KNneQ8jwtePgwbZdDCbpTP8', timestamp=1704153420000.0, type='dca_executor', close_timestamp=1704153540000.0, close_type=, status=, config=DCAExecutorConfig(id='7qv4cbX8wcYNQoBzjKFX8KNneQ8jwtePgwbZdDCbpTP8', type='dca_executor', timestamp=1704153420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.624735000000000160342468468'), Decimal('3.631984470000000160663153405'), Decimal('3.653732880000000161625208216'), Decimal('3.660982350000000161945893153'), Decimal('3.679106025000000162747605495'), Decimal('3.697229700000000163549317837')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6119, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6247350000000003}, controller_id=None),\n ExecutorInfo(id='CYAt1pa4TTc4goAfMXC9NA4PUuSpudVf4JCoujG6Pv5U', timestamp=1704153600000.0, type='dca_executor', close_timestamp=1704153720000.0, close_type=, status=, config=DCAExecutorConfig(id='CYAt1pa4TTc4goAfMXC9NA4PUuSpudVf4JCoujG6Pv5U', type='dca_executor', timestamp=1704153600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.613512600000000061379031023'), Decimal('3.620739625200000061501789085'), Decimal('3.642420700800000061870063271'), Decimal('3.649647726000000061992821333'), Decimal('3.667715289000000062299716488'), Decimal('3.685782852000000062606611643')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.599, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6135126}, controller_id=None),\n ExecutorInfo(id='HbP6YqUFhpxnFWdEw9N2uviWQ7TVuWWuKKnW3q75KTv9', timestamp=1704139980000.0, type='dca_executor', close_timestamp=1704160620000.0, close_type=, status=, config=DCAExecutorConfig(id='HbP6YqUFhpxnFWdEw9N2uviWQ7TVuWWuKKnW3q75KTv9', type='dca_executor', timestamp=1704139980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.606771999999999879298856476'), Decimal('3.599558455999999879540258763'), Decimal('3.577917823999999880264465624'), Decimal('3.570704279999999880505867911'), Decimal('3.552670419999999881109373629'), Decimal('3.534636559999999881712879346')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.03145933504437083139126940523055964149534702301025390625'), net_pnl_quote=Decimal('7.4393837062069305687828091322444379329681396484375'), cum_fees_quote=Decimal('0.14188571428571428167941803621943108737468719482421875'), filled_amount_quote=Decimal('236.4761904761904816041351296007633209228515625'), is_active=False, is_trading=False, custom_info={'close_price': 3.6908, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.5822959193717274}, controller_id=None),\n ExecutorInfo(id='BCwKiF9hHYvyG4uiyZVrsiGYBLY2bsktMPBZqaa2gR5U', timestamp=1704153780000.0, type='dca_executor', close_timestamp=1704161760000.0, close_type=, status=, config=DCAExecutorConfig(id='BCwKiF9hHYvyG4uiyZVrsiGYBLY2bsktMPBZqaa2gR5U', type='dca_executor', timestamp=1704153780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.603692999999999807919502657'), Decimal('3.610900385999999807535341662'), Decimal('3.632522543999999806382858678'), Decimal('3.639729929999999805998697684'), Decimal('3.657748394999999805038295197'), Decimal('3.675766859999999804077892710')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01493250714901908811349517947064668987877666950225830078125'), net_pnl_quote=Decimal('-7.55975951213316310628442806773819029331207275390625'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.706, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.648965844501011}, controller_id=None),\n ExecutorInfo(id='mxTQ9PYU7EkQQaY7BLcFwcLxT4HtQTQRdYQxSxwkx9V', timestamp=1704160620000.0, type='dca_executor', close_timestamp=1704177900000.0, close_type=, status=, config=DCAExecutorConfig(id='mxTQ9PYU7EkQQaY7BLcFwcLxT4HtQTQRdYQxSxwkx9V', type='dca_executor', timestamp=1704160620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714292'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000001')], prices=[Decimal('3.683418399999999858731131175'), Decimal('3.676051563199999859013668913'), Decimal('3.653951052799999859861282126'), Decimal('3.646584215999999860143819863'), Decimal('3.628167123999999860850164207'), Decimal('3.609750031999999861556508552')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.032656674102879611198968490270999609492719173431396484375'), net_pnl_quote=Decimal('2.32950941933874577927099380758590996265411376953125'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.7946, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.678510453987717}, controller_id=None),\n ExecutorInfo(id='3gqAmdWvLCPHMXCsKA3MZ5JemnTMF4Q7SiR6mdmQYmZq', timestamp=1704165360000.0, type='dca_executor', close_timestamp=1704189000000.0, close_type=, status=, config=DCAExecutorConfig(id='3gqAmdWvLCPHMXCsKA3MZ5JemnTMF4Q7SiR6mdmQYmZq', type='dca_executor', timestamp=1704165360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.743271599999999787278351369'), Decimal('3.750758143199999786852908072'), Decimal('3.773217772799999785576578180'), Decimal('3.780704315999999785151134883'), Decimal('3.799420673999999784087526640'), Decimal('3.818137031999999783023918396')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01390103960291601097043479029480295139364898204803466796875'), net_pnl_quote=Decimal('-7.037566787542932189580824342556297779083251953125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.8476, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.7902979568710906}, controller_id=None),\n ExecutorInfo(id='8TA9qPcksvZ9gtDM31BMdJiVsR2zt2JPTxVwAyDX2PR1', timestamp=1704177900000.0, type='dca_executor', close_timestamp=1704189120000.0, close_type=, status=, config=DCAExecutorConfig(id='8TA9qPcksvZ9gtDM31BMdJiVsR2zt2JPTxVwAyDX2PR1', type='dca_executor', timestamp=1704177900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.787010799999999972895395328'), Decimal('3.779436778399999972949604537'), Decimal('3.756714713599999973112232165'), Decimal('3.749140691999999973166441375'), Decimal('3.730205637999999973301964398'), Decimal('3.711270583999999973437487421')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.036201224464641391931341019017054350115358829498291015625'), net_pnl_quote=Decimal('5.147469345305676569068964454345405101776123046875'), cum_fees_quote=Decimal('0.08531428571428571749901692555795307271182537078857421875'), filled_amount_quote=Decimal('142.19047619047620401033782400190830230712890625'), is_active=False, is_trading=False, custom_info={'close_price': 3.9043, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.769382049122572}, controller_id=None),\n ExecutorInfo(id='DkU1sUjB4rGrQtcnh3Y31oCKdrdH15MHaLT2LQUV2mDp', timestamp=1704189120000.0, type='dca_executor', close_timestamp=1704191940000.0, close_type=, status=, config=DCAExecutorConfig(id='DkU1sUjB4rGrQtcnh3Y31oCKdrdH15MHaLT2LQUV2mDp', type='dca_executor', timestamp=1704189120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.896491400000000103369211801'), Decimal('3.888698417200000103162473377'), Decimal('3.865319468800000102542258107'), Decimal('3.857526486000000102335519683'), Decimal('3.838044029000000101818673624'), Decimal('3.818561572000000101301827565')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0132542149687274839198192211142668384127318859100341796875'), net_pnl_quote=Decimal('-6.54411085253575830478212083107791841030120849609375'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.7888, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.847848673898105}, controller_id=None),\n ExecutorInfo(id='HaKQSmrpfox47QNdakGadZjBSfeJ2mEAjiyNhHNqQYZK', timestamp=1704192600000.0, type='dca_executor', close_timestamp=1704192720000.0, close_type=, status=, config=DCAExecutorConfig(id='HaKQSmrpfox47QNdakGadZjBSfeJ2mEAjiyNhHNqQYZK', type='dca_executor', timestamp=1704192600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.774533999999999904041717012'), Decimal('3.782083067999999903849800446'), Decimal('3.804730271999999903274050748'), Decimal('3.812279339999999903082134182'), Decimal('3.831152009999999902602342767'), Decimal('3.850024679999999902122551352')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7523, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.774534}, controller_id=None),\n ExecutorInfo(id='BkfmvzjJWjFsjXjmPYb5Baa1k195rQ6EsL7yngZzHrca', timestamp=1704195540000.0, type='dca_executor', close_timestamp=1704195660000.0, close_type=, status=, config=DCAExecutorConfig(id='BkfmvzjJWjFsjXjmPYb5Baa1k195rQ6EsL7yngZzHrca', type='dca_executor', timestamp=1704195540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.711661800000000072175569688'), Decimal('3.704238476400000072031218549'), Decimal('3.681968505600000071598165130'), Decimal('3.674545182000000071453813991'), Decimal('3.655986873000000071092936143'), Decimal('3.637428564000000070732058294')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7206, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.7116618}, controller_id=None),\n ExecutorInfo(id='GhmdG1UuNYxHo5HjAVgMuktTav4nK3rZNWMYHAnsTcec', timestamp=1704195720000.0, type='dca_executor', close_timestamp=1704195840000.0, close_type=, status=, config=DCAExecutorConfig(id='GhmdG1UuNYxHo5HjAVgMuktTav4nK3rZNWMYHAnsTcec', type='dca_executor', timestamp=1704195720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.712460199999999984244451745'), Decimal('3.705035279599999984275962842'), Decimal('3.682760518399999984370496131'), Decimal('3.675335597999999984402007228'), Decimal('3.656773296999999984480784969'), Decimal('3.638210995999999984559562710')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7128, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.7124601999999998}, controller_id=None),\n ExecutorInfo(id='7QufBAMdvc1eeup3wNAH2aDKjJq2w4MiaYecsNxBAUFJ', timestamp=1704195900000.0, type='dca_executor', close_timestamp=1704202320000.0, close_type=, status=, config=DCAExecutorConfig(id='7QufBAMdvc1eeup3wNAH2aDKjJq2w4MiaYecsNxBAUFJ', type='dca_executor', timestamp=1704195900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.719146799999999912622886122'), Decimal('3.711708506399999912797640350'), Decimal('3.689393625599999913321903033'), Decimal('3.681955331999999913496657261'), Decimal('3.663359597999999913933542830'), Decimal('3.644763863999999914370428400')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0322810098878496543051141998148523271083831787109375'), net_pnl_quote=Decimal('2.302712038666609117854022770188748836517333984375'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.832, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.714191247922029}, controller_id=None),\n ExecutorInfo(id='6LkhuSxFWhpFFSmyFSkMFr2EdcADe7Z9CKfnxLEFedeN', timestamp=1704202320000.0, type='dca_executor', close_timestamp=1704205500000.0, close_type=, status=, config=DCAExecutorConfig(id='6LkhuSxFWhpFFSmyFSkMFr2EdcADe7Z9CKfnxLEFedeN', type='dca_executor', timestamp=1704202320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.824335999999999850924914391'), Decimal('3.816687327999999851223064562'), Decimal('3.793741311999999852117515076'), Decimal('3.786092639999999852415665247'), Decimal('3.766970959999999853161040675'), Decimal('3.747849279999999853906416103')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01559459184714712988439888619041084893979132175445556640625'), net_pnl_quote=Decimal('-7.69964407462595357145573871093802154064178466796875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.714, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.776594042050441}, controller_id=None),\n ExecutorInfo(id='346HDSG85aqivXjSABkFJSoQPYW4DgT9iNyh2uG6ufhg', timestamp=1704192780000.0, type='dca_executor', close_timestamp=1704205920000.0, close_type=, status=, config=DCAExecutorConfig(id='346HDSG85aqivXjSABkFJSoQPYW4DgT9iNyh2uG6ufhg', type='dca_executor', timestamp=1704192780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.760506000000000114070461408'), Decimal('3.768027012000000114298602331'), Decimal('3.790590048000000114983025099'), Decimal('3.798111060000000115211166022'), Decimal('3.816913590000000115781518329'), Decimal('3.835716120000000116351870636')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.034961247657317638670715354010098963044583797454833984375'), net_pnl_quote=Decimal('17.69954783184630997538988594897091388702392578125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.6854, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.8077488709612943}, controller_id=None),\n ExecutorInfo(id='GGSePg14t2FWjCC8VxyibfyCR2MLkmg6w7ATy6byG5Ju', timestamp=1704205920000.0, type='dca_executor', close_timestamp=1704206040000.0, close_type=, status=, config=DCAExecutorConfig(id='GGSePg14t2FWjCC8VxyibfyCR2MLkmg6w7ATy6byG5Ju', type='dca_executor', timestamp=1704205920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.692770800000000009408965272'), Decimal('3.700156341600000009427783203'), Decimal('3.722312966400000009484236994'), Decimal('3.729698508000000009503054925'), Decimal('3.748162362000000009550099751'), Decimal('3.766626216000000009597144577')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6769, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6927708}, controller_id=None),\n ExecutorInfo(id='7rAyQqPqub9DsQLHYdGFaxhfEzyxJkfj4AR6mQ97Vr8b', timestamp=1704206100000.0, type='dca_executor', close_timestamp=1704211860000.0, close_type=, status=, config=DCAExecutorConfig(id='7rAyQqPqub9DsQLHYdGFaxhfEzyxJkfj4AR6mQ97Vr8b', type='dca_executor', timestamp=1704206100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.677239800000000162480541520'), Decimal('3.684594279600000162805502603'), Decimal('3.706657718400000163780385852'), Decimal('3.714012198000000164105346935'), Decimal('3.732398397000000164917749643'), Decimal('3.750784596000000165730152350')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.031711147218162601124635813221175340004265308380126953125'), net_pnl_quote=Decimal('2.268102053413343810461810790002346038818359375'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 3.5685, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.6821460507051933}, controller_id=None),\n ExecutorInfo(id='BKuDzKDZk3LCbwXaoiLoMhvgDygUQYSRMg5rqQz6smDi', timestamp=1704211860000.0, type='dca_executor', close_timestamp=1704211980000.0, close_type=, status=, config=DCAExecutorConfig(id='BKuDzKDZk3LCbwXaoiLoMhvgDygUQYSRMg5rqQz6smDi', type='dca_executor', timestamp=1704211860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.575636999999999782999603180'), Decimal('3.582788273999999782565602386'), Decimal('3.604242095999999781263600005'), Decimal('3.611393369999999780829599212'), Decimal('3.629271554999999779744597228'), Decimal('3.647149739999999778659595244')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5699, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.575637}, controller_id=None),\n ExecutorInfo(id='7W2XaaDr9XMtareJnGAcyZGiac9T1UsxV9eACkV7uvjv', timestamp=1704212040000.0, type='dca_executor', close_timestamp=1704212160000.0, close_type=, status=, config=DCAExecutorConfig(id='7W2XaaDr9XMtareJnGAcyZGiac9T1UsxV9eACkV7uvjv', type='dca_executor', timestamp=1704212040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142856')], prices=[Decimal('3.578442599999999918984809609'), Decimal('3.585599485199999918822779228'), Decimal('3.607070140799999918336688086'), Decimal('3.614227025999999918174657705'), Decimal('3.632119238999999917769581753'), Decimal('3.650011451999999917364505801')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5759, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5784426}, controller_id=None),\n ExecutorInfo(id='3WWGshTprU6CHY7gNha3QSU7DSKbvfFRZwZ5uwLUcVxt', timestamp=1704212220000.0, type='dca_executor', close_timestamp=1704212340000.0, close_type=, status=, config=DCAExecutorConfig(id='3WWGshTprU6CHY7gNha3QSU7DSKbvfFRZwZ5uwLUcVxt', type='dca_executor', timestamp=1704212220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.587460599999999815750430231'), Decimal('3.594635521199999815381931091'), Decimal('3.616160284799999814276433673'), Decimal('3.623335205999999813907934533'), Decimal('3.641272508999999812986686684'), Decimal('3.659209811999999812065438836')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5704, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5874606}, controller_id=None),\n ExecutorInfo(id='3V4eLchsWfVTJhkHE192wP9CNom8NzsYQYNWPpunetsC', timestamp=1704212400000.0, type='dca_executor', close_timestamp=1704212520000.0, close_type=, status=, config=DCAExecutorConfig(id='3V4eLchsWfVTJhkHE192wP9CNom8NzsYQYNWPpunetsC', type='dca_executor', timestamp=1704212400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.580045800000000187395236827'), Decimal('3.587205891600000187770027301'), Decimal('3.608686166400000188894398722'), Decimal('3.615846258000000189269189195'), Decimal('3.633746487000000190206165379'), Decimal('3.651646716000000191143141564')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5572, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.5800458}, controller_id=None),\n ExecutorInfo(id='Fz2F9cR4yjRAp94MJ2Py9p9cJodsg99Xc5GkDXs94dCm', timestamp=1704212580000.0, type='dca_executor', close_timestamp=1704212700000.0, close_type=, status=, config=DCAExecutorConfig(id='Fz2F9cR4yjRAp94MJ2Py9p9cJodsg99Xc5GkDXs94dCm', type='dca_executor', timestamp=1704212580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.575136000000000060665472644'), Decimal('3.582286272000000060786803589'), Decimal('3.603737088000000061150796425'), Decimal('3.610887360000000061272127370'), Decimal('3.628763040000000061575454734'), Decimal('3.646638720000000061878782097')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.5744, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.575136}, controller_id=None),\n ExecutorInfo(id='6zxuKQfrSfHxjHinTQVQQtuzU4p45Ct52MQAh9tdJeAE', timestamp=1704212760000.0, type='dca_executor', close_timestamp=1704229380000.0, close_type=, status=, config=DCAExecutorConfig(id='6zxuKQfrSfHxjHinTQVQQtuzU4p45Ct52MQAh9tdJeAE', type='dca_executor', timestamp=1704212760000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.590767200000000119047155465'), Decimal('3.597948734400000119285249776'), Decimal('3.619493337600000119999532709'), Decimal('3.626674872000000120237627020'), Decimal('3.644628708000000120832862797'), Decimal('3.662582544000000121428098574')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.03109490402272947762174482022601296193897724151611328125'), net_pnl_quote=Decimal('11.21119360990934410438057966530323028564453125'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 3.515, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.625084956258337}, controller_id=None),\n ExecutorInfo(id='HL62x9eQoubPuYEARCrrMmQSa4VGMdxXZttCmbQFvHrP', timestamp=1704229380000.0, type='dca_executor', close_timestamp=1704240600000.0, close_type=, status=, config=DCAExecutorConfig(id='HL62x9eQoubPuYEARCrrMmQSa4VGMdxXZttCmbQFvHrP', type='dca_executor', timestamp=1704229380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.522030000000000124740009986'), Decimal('3.529074060000000124989490006'), Decimal('3.550206240000000125737930066'), Decimal('3.557250300000000125987410086'), Decimal('3.574860450000000126611110136'), Decimal('3.592470600000000127234810186')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01450100081272183948077891812999951071105897426605224609375'), net_pnl_quote=Decimal('-7.341304292402487163826663163490593433380126953125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.6202, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.566276920178244}, controller_id=None),\n ExecutorInfo(id='9ieqt1rvuN6xKqGfxw6ShAbqYei88234uzv6QGkFvnsL', timestamp=1704209100000.0, type='dca_executor', close_timestamp=1704241020000.0, close_type=, status=, config=DCAExecutorConfig(id='9ieqt1rvuN6xKqGfxw6ShAbqYei88234uzv6QGkFvnsL', type='dca_executor', timestamp=1704209100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.596492599999999903409421409'), Decimal('3.589299614799999903602602566'), Decimal('3.567720659199999904182146038'), Decimal('3.560527673999999904375327195'), Decimal('3.542545210999999904858280088'), Decimal('3.524562747999999905341232981')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0323125623759738800355734156255493871867656707763671875'), net_pnl_quote=Decimal('15.95394299977548513425062992610037326812744140625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.6624, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.5515949763405987}, controller_id=None),\n ExecutorInfo(id='GV6mrTPwwDyq4yHji5QfhzszVQorqAdyWzgiGjmTnzqh', timestamp=1704244200000.0, type='dca_executor', close_timestamp=1704244320000.0, close_type=, status=, config=DCAExecutorConfig(id='GV6mrTPwwDyq4yHji5QfhzszVQorqAdyWzgiGjmTnzqh', type='dca_executor', timestamp=1704244200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.708101399999999878408259156'), Decimal('3.715517602799999878165075674'), Decimal('3.737766211199999877435525229'), Decimal('3.745182413999999877192341748'), Decimal('3.763722920999999876584383043'), Decimal('3.782263427999999875976424339')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7079, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.7081014}, controller_id=None),\n ExecutorInfo(id='6W93bN3bQKwnpA2Sh6qLyjBPbRXxaukAvFvKe5nPCL4P', timestamp=1704244380000.0, type='dca_executor', close_timestamp=1704244500000.0, close_type=, status=, config=DCAExecutorConfig(id='6W93bN3bQKwnpA2Sh6qLyjBPbRXxaukAvFvKe5nPCL4P', type='dca_executor', timestamp=1704244380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047615'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.710506200000000058535205848'), Decimal('3.717927212400000058652276260'), Decimal('3.740190249600000059003487495'), Decimal('3.747611262000000059120557906'), Decimal('3.766163793000000059413233936'), Decimal('3.784716324000000059705909965')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6655, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.7105062}, controller_id=None),\n ExecutorInfo(id='4BYCPWt2PPo4WXPKSxkjUKbeyT8G6sNjjZ1suNkhtu38', timestamp=1704241020000.0, type='dca_executor', close_timestamp=1704245220000.0, close_type=, status=, config=DCAExecutorConfig(id='4BYCPWt2PPo4WXPKSxkjUKbeyT8G6sNjjZ1suNkhtu38', type='dca_executor', timestamp=1704241020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.655075199999999877878598120'), Decimal('3.647765049599999878122840924'), Decimal('3.625834598399999878855569335'), Decimal('3.618524447999999879099812139'), Decimal('3.600249071999999879710419148'), Decimal('3.581973695999999880321026158')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.03258121944274920689377239568784716539084911346435546875'), net_pnl_quote=Decimal('11.525218506212500102492413134314119815826416015625'), cum_fees_quote=Decimal('0.212242857142857122010326520467060618102550506591796875'), filled_amount_quote=Decimal('353.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.7295, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.62031914232483}, controller_id=None),\n ExecutorInfo(id='HFVNitZ4R1oW9f5495PRCLEqhbxUfssii2Lq55XKaABc', timestamp=1704245220000.0, type='dca_executor', close_timestamp=1704245340000.0, close_type=, status=, config=DCAExecutorConfig(id='HFVNitZ4R1oW9f5495PRCLEqhbxUfssii2Lq55XKaABc', type='dca_executor', timestamp=1704245220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.722040999999999815473099296'), Decimal('3.714596917999999815842153097'), Decimal('3.692264671999999816949314502'), Decimal('3.684820589999999817318368303'), Decimal('3.666210384999999818241002807'), Decimal('3.647600179999999819163637310')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7312, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.722041}, controller_id=None),\n ExecutorInfo(id='GwP9uByaYVAkccmftF2q3UgeVk9kprvVuBzoWRHDF874', timestamp=1704244560000.0, type='dca_executor', close_timestamp=1704255480000.0, close_type=, status=, config=DCAExecutorConfig(id='GwP9uByaYVAkccmftF2q3UgeVk9kprvVuBzoWRHDF874', type='dca_executor', timestamp=1704244560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428572'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142856')], prices=[Decimal('3.675135600000000171735983766'), Decimal('3.682485871200000172079455734'), Decimal('3.704536684800000173109871636'), Decimal('3.711886956000000173453343604'), Decimal('3.730262634000000174312023522'), Decimal('3.748638312000000175170703441')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0137716680756585323208707194453381816856563091278076171875'), net_pnl_quote=Decimal('-6.9720709117316044256540408241562545299530029296875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.78, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.7213059709330762}, controller_id=None),\n ExecutorInfo(id='74NikdSGUtPrvJYY2NL57GSdTEG2yYFytiY6p6tYAE9p', timestamp=1704259080000.0, type='dca_executor', close_timestamp=1704259200000.0, close_type=, status=, config=DCAExecutorConfig(id='74NikdSGUtPrvJYY2NL57GSdTEG2yYFytiY6p6tYAE9p', type='dca_executor', timestamp=1704259080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.795475800000000045011423753'), Decimal('3.803066751600000045101446601'), Decimal('3.825839606400000045371515143'), Decimal('3.833430558000000045461537991'), Decimal('3.852407937000000045686595109'), Decimal('3.871385316000000045911652228')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.7747, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.7954758}, controller_id=None),\n ExecutorInfo(id='d2ipo5guRUudxKiYNfrHichxt5i3e9nAbWM6Y9yCkyT', timestamp=1704245400000.0, type='dca_executor', close_timestamp=1704259860000.0, close_type=, status=, config=DCAExecutorConfig(id='d2ipo5guRUudxKiYNfrHichxt5i3e9nAbWM6Y9yCkyT', type='dca_executor', timestamp=1704245400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.723737599999999850219989384'), Decimal('3.716290124799999850519549405'), Decimal('3.693947699199999851418229469'), Decimal('3.686500223999999851717789490'), Decimal('3.667881535999999852466689543'), Decimal('3.649262847999999853215589596')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.033182323229279310494721499935621977783739566802978515625'), net_pnl_quote=Decimal('7.8468293884095743351281271316111087799072265625'), cum_fees_quote=Decimal('0.14188571428571428167941803621943108737468719482421875'), filled_amount_quote=Decimal('236.4761904761904816041351296007633209228515625'), is_active=False, is_trading=False, custom_info={'close_price': 3.8164, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.6984677737575513}, controller_id=None),\n ExecutorInfo(id='73TYTDnBAEKdTddu4YfLQHxci6N3zCMaZBHtES2tJGCa', timestamp=1704259260000.0, type='dca_executor', close_timestamp=1704280260000.0, close_type=, status=, config=DCAExecutorConfig(id='73TYTDnBAEKdTddu4YfLQHxci6N3zCMaZBHtES2tJGCa', type='dca_executor', timestamp=1704259260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.785756400000000003005154457'), Decimal('3.793327912800000003011164766'), Decimal('3.816042451200000003029195693'), Decimal('3.823613964000000003035206002'), Decimal('3.842542746000000003050231774'), Decimal('3.861471528000000003065257546')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0332292905953751327441381135940901003777980804443359375'), net_pnl_quote=Decimal('11.9807416068039440659731553751043975353240966796875'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 3.6995, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.8219377111662154}, controller_id=None),\n ExecutorInfo(id='33Eqbq7QQokveGypRapvfdoAeBxn57awm146xyGHv2Sn', timestamp=1704259860000.0, type='dca_executor', close_timestamp=1704280260000.0, close_type=, status=, config=DCAExecutorConfig(id='33Eqbq7QQokveGypRapvfdoAeBxn57awm146xyGHv2Sn', type='dca_executor', timestamp=1704259860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.808767199999999792777588548'), Decimal('3.801149665599999793192033371'), Decimal('3.778297062399999794435367840'), Decimal('3.770679527999999794849812663'), Decimal('3.751635691999999795885924720'), Decimal('3.732591855999999796922036777')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01472786297712492219191826592350480495952069759368896484375'), net_pnl_quote=Decimal('-7.27170701325332213826868610340170562267303466796875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.6995, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.761219598664223}, controller_id=None),\n ExecutorInfo(id='AbcwgEZtzHNydHG84d1uXSrCs5XRyMLkwWRrc858n83E', timestamp=1704280260000.0, type='dca_executor', close_timestamp=1704280380000.0, close_type=, status=, config=DCAExecutorConfig(id='AbcwgEZtzHNydHG84d1uXSrCs5XRyMLkwWRrc858n83E', type='dca_executor', timestamp=1704280260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428575'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.706899000000000010833479945'), Decimal('3.714312798000000010855146905'), Decimal('3.736554192000000010920147785'), Decimal('3.743967990000000010941814744'), Decimal('3.762502485000000010995982144'), Decimal('3.781036980000000011050149544')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.6961, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.706899}, controller_id=None),\n ExecutorInfo(id='HdKRmy6nGXsmqYMkh2x6wrAvf1EoNqhuGiqM4cha4hv4', timestamp=1704280440000.0, type='dca_executor', close_timestamp=1704283260000.0, close_type=, status=, config=DCAExecutorConfig(id='HdKRmy6nGXsmqYMkh2x6wrAvf1EoNqhuGiqM4cha4hv4', type='dca_executor', timestamp=1704280440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.680346000000000042870748616'), Decimal('3.687706692000000042956490113'), Decimal('3.709788768000000043213714605'), Decimal('3.717149460000000043299456102'), Decimal('3.735551190000000043513809845'), Decimal('3.753952920000000043728163588')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0578587166740297254019509409772581420838832855224609375'), net_pnl_quote=Decimal('8.3041034312155037611091756843961775302886962890625'), cum_fees_quote=Decimal('0.08611428571428569878065673037781380116939544677734375'), filled_amount_quote=Decimal('143.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 3.488, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.697563278898474}, controller_id=None),\n ExecutorInfo(id='8urnJCcj1kVQ6TAKEfBzrrqQBxqMJXdcZYWgcWirdAjS', timestamp=1704283260000.0, type='dca_executor', close_timestamp=1704283680000.0, close_type=, status=, config=DCAExecutorConfig(id='8urnJCcj1kVQ6TAKEfBzrrqQBxqMJXdcZYWgcWirdAjS', type='dca_executor', timestamp=1704283260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.494975999999999989465759852'), Decimal('3.501965951999999989444691372'), Decimal('3.522935807999999989381485931'), Decimal('3.529925759999999989360417451'), Decimal('3.547400639999999989307746250'), Decimal('3.564875519999999989255075049')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.1406777257904436861934982516686432063579559326171875'), net_pnl_quote=Decimal('20.19060312059034600906670675612986087799072265625'), cum_fees_quote=Decimal('0.08611428571428569878065673037781380116939544677734375'), filled_amount_quote=Decimal('143.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 3.0304, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.511326086794957}, controller_id=None),\n ExecutorInfo(id='D77zT11dfVfbQqYJ5x72A52zvzmPH9FDP4ELGNE4YMfD', timestamp=1704283680000.0, type='dca_executor', close_timestamp=1704283800000.0, close_type=, status=, config=DCAExecutorConfig(id='D77zT11dfVfbQqYJ5x72A52zvzmPH9FDP4ELGNE4YMfD', type='dca_executor', timestamp=1704283680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.036460800000000205171746258'), Decimal('3.042533721600000205582089751'), Decimal('3.060752486400000206813120228'), Decimal('3.066825408000000207223463721'), Decimal('3.082007712000000208249322452'), Decimal('3.097190016000000209275181183')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.4556, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0364608000000004}, controller_id=None),\n ExecutorInfo(id='652B2Bdp5qehoqQbTeJgUEYWNJQEsEfuGHwGcqFzrcTJ', timestamp=1704283860000.0, type='dca_executor', close_timestamp=1704283920000.0, close_type=, status=, config=DCAExecutorConfig(id='652B2Bdp5qehoqQbTeJgUEYWNJQEsEfuGHwGcqFzrcTJ', type='dca_executor', timestamp=1704283860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.722634400000000059918087157'), Decimal('2.728079668800000060037923331'), Decimal('2.744415475200000060397431854'), Decimal('2.749860744000000060517268029'), Decimal('2.763473916000000060816858464'), Decimal('2.777087088000000061116448900')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.8168, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.756838591040775}, controller_id=None),\n ExecutorInfo(id='69jSp1WSNi3sFfJmR1xeQpFdiTB7TutdqzPb7kVFzjyj', timestamp=1704283860000.0, type='dca_executor', close_timestamp=1704283980000.0, close_type=, status=, config=DCAExecutorConfig(id='69jSp1WSNi3sFfJmR1xeQpFdiTB7TutdqzPb7kVFzjyj', type='dca_executor', timestamp=1704283860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.711765600000000059453092449'), Decimal('2.706342068800000059334186264'), Decimal('2.690071475200000058977467709'), Decimal('2.684647944000000058858561525'), Decimal('2.671089116000000058561296062'), Decimal('2.657530288000000058264030600')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9242, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7117656}, controller_id=None),\n ExecutorInfo(id='BCaK5pj9Sk7ZdEhm5BsCxuXk1SF1GFZCfrjN9UEs7D36', timestamp=1704284040000.0, type='dca_executor', close_timestamp=1704284160000.0, close_type=, status=, config=DCAExecutorConfig(id='BCaK5pj9Sk7ZdEhm5BsCxuXk1SF1GFZCfrjN9UEs7D36', type='dca_executor', timestamp=1704284040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571425'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.976335399999999950946442950'), Decimal('2.970382729199999951044550064'), Decimal('2.952524716799999951338871406'), Decimal('2.946572045999999951436978520'), Decimal('2.931690368999999951682246306'), Decimal('2.916808691999999951927514091')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0258, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9763354}, controller_id=None),\n ExecutorInfo(id='9hCimsNzMt6LmsMQuyJCUEXgcAR9x1QNx1S34LRn18Zq', timestamp=1704284220000.0, type='dca_executor', close_timestamp=1704284640000.0, close_type=, status=, config=DCAExecutorConfig(id='9hCimsNzMt6LmsMQuyJCUEXgcAR9x1QNx1S34LRn18Zq', type='dca_executor', timestamp=1704284220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.042303199999999998454841554'), Decimal('3.036218593599999998457931871'), Decimal('3.017964774399999998467202822'), Decimal('3.011880167999999998470293138'), Decimal('2.996668651999999998478018931'), Decimal('2.981457135999999998485744723')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.032962694914138612745091450051404535770416259765625'), net_pnl_quote=Decimal('0.78482606938425270026726821015472523868083953857421875'), cum_fees_quote=Decimal('0.01428571428571428526821396332024960429407656192779541015625'), filled_amount_quote=Decimal('23.8095238095238102005168912000954151153564453125'), is_active=False, is_trading=False, custom_info={'close_price': 3.14, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0423032}, controller_id=None),\n ExecutorInfo(id='Ga4c6Y27UX19pG8CgfSxMTxh92CQd8EVUD4NLeMyHD7P', timestamp=1704284640000.0, type='dca_executor', close_timestamp=1704284940000.0, close_type=, status=, config=DCAExecutorConfig(id='Ga4c6Y27UX19pG8CgfSxMTxh92CQd8EVUD4NLeMyHD7P', type='dca_executor', timestamp=1704284640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.133720000000000123965560039'), Decimal('3.127452560000000123717628919'), Decimal('3.108650240000000122973835559'), Decimal('3.102382800000000122725904439'), Decimal('3.086714200000000122106076638'), Decimal('3.071045600000000121486248838')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.041373410367910368190536019028513692319393157958984375'), net_pnl_quote=Decimal('9.7838264708115669776589129469357430934906005859375'), cum_fees_quote=Decimal('0.14188571428571428167941803621943108737468719482421875'), filled_amount_quote=Decimal('236.4761904761904816041351296007633209228515625'), is_active=False, is_trading=False, custom_info={'close_price': 3.2265, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1124541192106325}, controller_id=None),\n ExecutorInfo(id='4mksuVN9mukXBrFL5GKJ5FWz1SSinyUHKGVWLhzARuZY', timestamp=1704284940000.0, type='dca_executor', close_timestamp=1704285120000.0, close_type=, status=, config=DCAExecutorConfig(id='4mksuVN9mukXBrFL5GKJ5FWz1SSinyUHKGVWLhzARuZY', type='dca_executor', timestamp=1704284940000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.220047000000000145235608262'), Decimal('3.213606906000000144945137045'), Decimal('3.194286624000000144073723396'), Decimal('3.187846530000000143783252179'), Decimal('3.171746295000000143057074138'), Decimal('3.155646060000000142330896097')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.010682266166465392842610526713542640209197998046875'), net_pnl_quote=Decimal('-5.274241749856972916177255683578550815582275390625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.1118, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1798488195917924}, controller_id=None),\n ExecutorInfo(id='FXBoFrr5XtWo2EZc8iAZEbu5z6a3cW7aLiNtospFcCsv', timestamp=1704287520000.0, type='dca_executor', close_timestamp=1704287640000.0, close_type=, status=, config=DCAExecutorConfig(id='FXBoFrr5XtWo2EZc8iAZEbu5z6a3cW7aLiNtospFcCsv', type='dca_executor', timestamp=1704287520000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047625'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.207101399999999878387442474'), Decimal('3.213515602799999878144217359'), Decimal('3.232758211199999877414542014'), Decimal('3.239172413999999877171316899'), Decimal('3.255207920999999876563254111'), Decimal('3.271243427999999875955191323')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.181, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.2071014}, controller_id=None),\n ExecutorInfo(id='FsAfLV4DpmUAmQnuFmbWBy4xegYGQXNftqb7dg6FZsJE', timestamp=1704287700000.0, type='dca_executor', close_timestamp=1704287820000.0, close_type=, status=, config=DCAExecutorConfig(id='FsAfLV4DpmUAmQnuFmbWBy4xegYGQXNftqb7dg6FZsJE', type='dca_executor', timestamp=1704287700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047615'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.207301799999999856316572343'), Decimal('3.213716403599999856029205488'), Decimal('3.232960214399999855167104922'), Decimal('3.239374817999999854879738066'), Decimal('3.255411326999999854161320928'), Decimal('3.271447835999999853442903790')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1951, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.2073017999999998}, controller_id=None),\n ExecutorInfo(id='GFV6SsX4e5WsLAYccbPYtmAtskWQaVYEvad9rUjc4Shw', timestamp=1704287880000.0, type='dca_executor', close_timestamp=1704288000000.0, close_type=, status=, config=DCAExecutorConfig(id='GFV6SsX4e5WsLAYccbPYtmAtskWQaVYEvad9rUjc4Shw', type='dca_executor', timestamp=1704287880000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142856')], prices=[Decimal('3.191871000000000220841407660'), Decimal('3.198254742000000221283090475'), Decimal('3.217405968000000222608138921'), Decimal('3.223789710000000223049821737'), Decimal('3.239749065000000224154028775'), Decimal('3.255708420000000225258235813')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1658, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1918710000000003}, controller_id=None),\n ExecutorInfo(id='ANiGDGBTKdC8gSA6TjjRgs8W3y3UmW7FPcreN6JbDhcy', timestamp=1704288060000.0, type='dca_executor', close_timestamp=1704288180000.0, close_type=, status=, config=DCAExecutorConfig(id='ANiGDGBTKdC8gSA6TjjRgs8W3y3UmW7FPcreN6JbDhcy', type='dca_executor', timestamp=1704288060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.169325999999999811461273701'), Decimal('3.175664651999999811084196248'), Decimal('3.194680607999999809952963891'), Decimal('3.201019259999999809575886438'), Decimal('3.216865889999999808633192807'), Decimal('3.232712519999999807690499175')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1498, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.169326}, controller_id=None),\n ExecutorInfo(id='AjDqTnAY7BvmRQ4wRkGSKFg62qSP7s1DEY4Lc4Twzu9j', timestamp=1704288240000.0, type='dca_executor', close_timestamp=1704288360000.0, close_type=, status=, config=DCAExecutorConfig(id='AjDqTnAY7BvmRQ4wRkGSKFg62qSP7s1DEY4Lc4Twzu9j', type='dca_executor', timestamp=1704288240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.155097600000000043560888229'), Decimal('3.161407795200000043648010005'), Decimal('3.180338380800000043909375335'), Decimal('3.186648576000000043996497111'), Decimal('3.202424064000000044214301552'), Decimal('3.218199552000000044432105994')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1378, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1550976}, controller_id=None),\n ExecutorInfo(id='EwnWpvUmVtDXF4ELaNfvN5LWv2oed9SqFS9n7KmE1ZQz', timestamp=1704288420000.0, type='dca_executor', close_timestamp=1704288540000.0, close_type=, status=, config=DCAExecutorConfig(id='EwnWpvUmVtDXF4ELaNfvN5LWv2oed9SqFS9n7KmE1ZQz', type='dca_executor', timestamp=1704288420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.143274000000000010810061178'), Decimal('3.149560548000000010831681300'), Decimal('3.168420192000000010896541667'), Decimal('3.174706740000000010918161790'), Decimal('3.190423110000000010972212096'), Decimal('3.206139480000000011026262402')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1383, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.143274}, controller_id=None),\n ExecutorInfo(id='cqZ6b8oXi4azNZ83H3ED4uNdNpgdPqvBe8QW2YMK9bH', timestamp=1704288600000.0, type='dca_executor', close_timestamp=1704288720000.0, close_type=, status=, config=DCAExecutorConfig(id='cqZ6b8oXi4azNZ83H3ED4uNdNpgdPqvBe8QW2YMK9bH', type='dca_executor', timestamp=1704288600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.157301999999999800781316782'), Decimal('3.163616603999999800382879416'), Decimal('3.182560415999999799187567316'), Decimal('3.188875019999999798789129950'), Decimal('3.204661529999999797793036534'), Decimal('3.220448039999999796796943118')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.128, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1573019999999996}, controller_id=None),\n ExecutorInfo(id='3ei5wbgLnsd88xgVrLucHYuCUEmeW5GK9xafWpDBajks', timestamp=1704288720000.0, type='dca_executor', close_timestamp=1704288840000.0, close_type=, status=, config=DCAExecutorConfig(id='3ei5wbgLnsd88xgVrLucHYuCUEmeW5GK9xafWpDBajks', type='dca_executor', timestamp=1704288720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.121744000000000113329234885'), Decimal('3.115500512000000113102576415'), Decimal('3.096770048000000112422601006'), Decimal('3.090526560000000112195942536'), Decimal('3.074917840000000111629296362'), Decimal('3.059309120000000111062650187')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1371, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.121744}, controller_id=None),\n ExecutorInfo(id='GYvKgxy8vL4avnrbf4DhVp7wQJBeinbSV6emCVLRLF8n', timestamp=1704288900000.0, type='dca_executor', close_timestamp=1704289020000.0, close_type=, status=, config=DCAExecutorConfig(id='GYvKgxy8vL4avnrbf4DhVp7wQJBeinbSV6emCVLRLF8n', type='dca_executor', timestamp=1704288900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.162063200000000104818093094'), Decimal('3.155739073600000104608456908'), Decimal('3.136766694400000103979548349'), Decimal('3.130442568000000103769912163'), Decimal('3.114632252000000103245821698'), Decimal('3.098821936000000102721731232')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1631, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1620632}, controller_id=None),\n ExecutorInfo(id='2Q7aLqqLVZafqCSH2ggn5QxECrbb7YGfKmU4kwYmz6sd', timestamp=1704289080000.0, type='dca_executor', close_timestamp=1704289200000.0, close_type=, status=, config=DCAExecutorConfig(id='2Q7aLqqLVZafqCSH2ggn5QxECrbb7YGfKmU4kwYmz6sd', type='dca_executor', timestamp=1704289080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.161863600000000126800872580'), Decimal('3.155539872800000126547270835'), Decimal('3.136568691200000125786465599'), Decimal('3.130244964000000125532863854'), Decimal('3.114435646000000124898859491'), Decimal('3.098626328000000124264855128')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1803, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1618636}, controller_id=None),\n ExecutorInfo(id='3TsjvC2JstYxn2DxdkyJLzJgGsFX27QQD6dsDmXvhRT8', timestamp=1704289260000.0, type='dca_executor', close_timestamp=1704291360000.0, close_type=, status=, config=DCAExecutorConfig(id='3TsjvC2JstYxn2DxdkyJLzJgGsFX27QQD6dsDmXvhRT8', type='dca_executor', timestamp=1704289260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.176434399999999851661064421'), Decimal('3.170081531199999851957742292'), Decimal('3.151022924799999852847775906'), Decimal('3.144670055999999853144453777'), Decimal('3.128787883999999853886148455'), Decimal('3.112905711999999854627843133')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.018363013039559193673166959115405916236340999603271484375'), net_pnl_quote=Decimal('-9.0665190809842624020120638306252658367156982421875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.0707, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.136780667285528}, controller_id=None),\n ExecutorInfo(id='J7qssTJ4FpeobdRXML53djhz78hwJy2K6Qh1e2XqejMN', timestamp=1704288780000.0, type='dca_executor', close_timestamp=1704292680000.0, close_type=, status=, config=DCAExecutorConfig(id='J7qssTJ4FpeobdRXML53djhz78hwJy2K6Qh1e2XqejMN', type='dca_executor', timestamp=1704288780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.144075599999999922526580652'), Decimal('3.150363751199999922371633813'), Decimal('3.169228204799999921906793297'), Decimal('3.175516355999999921751846459'), Decimal('3.191236733999999921364479362'), Decimal('3.206957111999999920977112265')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.034104156072522116838197092647533281706273555755615234375'), net_pnl_quote=Decimal('8.175253412813159314964650548063218593597412109375'), cum_fees_quote=Decimal('0.1438285714285714167459673262783326208591461181640625'), filled_amount_quote=Decimal('239.71428571428572240620269440114498138427734375'), is_active=False, is_trading=False, custom_info={'close_price': 3.0684, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.165498284362336}, controller_id=None),\n ExecutorInfo(id='AndGfBStS7tZWA2ZnHwbDQwpuWW85yPLzwt8LVoaDcfG', timestamp=1704294960000.0, type='dca_executor', close_timestamp=1704295080000.0, close_type=, status=, config=DCAExecutorConfig(id='AndGfBStS7tZWA2ZnHwbDQwpuWW85yPLzwt8LVoaDcfG', type='dca_executor', timestamp=1704294960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.062263200000000016182050144'), Decimal('3.056138673600000016149686044'), Decimal('3.037765094400000016052593743'), Decimal('3.031640568000000016020229643'), Decimal('3.016329252000000015939319392'), Decimal('3.001017936000000015858409141')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0628, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0622632}, controller_id=None),\n ExecutorInfo(id='6DEHf8T52zYFcjf9ckB5d1wvTytSQEoVa9HDGDabqAvZ', timestamp=1704295140000.0, type='dca_executor', close_timestamp=1704297540000.0, close_type=, status=, config=DCAExecutorConfig(id='6DEHf8T52zYFcjf9ckB5d1wvTytSQEoVa9HDGDabqAvZ', type='dca_executor', timestamp=1704295140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.051085599999999917614607048'), Decimal('3.044983428799999917779377834'), Decimal('3.026676915199999918273690192'), Decimal('3.020574743999999918438460978'), Decimal('3.005319315999999918850387942'), Decimal('2.990063887999999919262314907')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.036281667430440533994584484389633871614933013916015625'), net_pnl_quote=Decimal('2.5880922767047582766508639906533062458038330078125'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.1453, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0470202015636847}, controller_id=None),\n ExecutorInfo(id='AGCb9Nh9weDSyiEVPPeUVgsLHntqvzNdraP5nmZUSE57', timestamp=1704292680000.0, type='dca_executor', close_timestamp=1704298320000.0, close_type=, status=, config=DCAExecutorConfig(id='AGCb9Nh9weDSyiEVPPeUVgsLHntqvzNdraP5nmZUSE57', type='dca_executor', timestamp=1704292680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.074536800000000016502915699'), Decimal('3.080685873600000016535921530'), Decimal('3.099133094400000016634939025'), Decimal('3.105282168000000016667944856'), Decimal('3.120654852000000016750459434'), Decimal('3.136027536000000016832974013')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0139393572832908520242600758365369983948767185211181640625'), net_pnl_quote=Decimal('-7.0569655693955564146335746045224368572235107421875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.1632, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1131619066500495}, controller_id=None),\n ExecutorInfo(id='BvoDGJ6JnhRE5HoVBJXSC96ESFNCSzxx76JsoCz4HS3u', timestamp=1704301920000.0, type='dca_executor', close_timestamp=1704302040000.0, close_type=, status=, config=DCAExecutorConfig(id='BvoDGJ6JnhRE5HoVBJXSC96ESFNCSzxx76JsoCz4HS3u', type='dca_executor', timestamp=1704301920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.103193999999999975210204782'), Decimal('3.109400387999999975160625192'), Decimal('3.128019551999999975011886420'), Decimal('3.134225939999999974962306830'), Decimal('3.149741909999999974838357854'), Decimal('3.165257879999999974714408878')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0969, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.103194}, controller_id=None),\n ExecutorInfo(id='3i7yjMcgaLmmGhQeJoAUzECqp8BiDxtLcpNmMXJA8k3A', timestamp=1704302100000.0, type='dca_executor', close_timestamp=1704302220000.0, close_type=, status=, config=DCAExecutorConfig(id='3i7yjMcgaLmmGhQeJoAUzECqp8BiDxtLcpNmMXJA8k3A', type='dca_executor', timestamp=1704302100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.102192000000000085564555440'), Decimal('3.108396384000000085735684551'), Decimal('3.127009536000000086249071884'), Decimal('3.133213920000000086420200994'), Decimal('3.148724880000000086848023772'), Decimal('3.164235840000000087275846549')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0875, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.102192}, controller_id=None),\n ExecutorInfo(id='6LxzTH4USCntJcpqvdR5FJnMFkWHE4wWEaVZDYwmQqWA', timestamp=1704302280000.0, type='dca_executor', close_timestamp=1704302400000.0, close_type=, status=, config=DCAExecutorConfig(id='6LxzTH4USCntJcpqvdR5FJnMFkWHE4wWEaVZDYwmQqWA', type='dca_executor', timestamp=1704302280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.083554799999999913268536321'), Decimal('3.089721909599999913095073394'), Decimal('3.108223238399999912574684612'), Decimal('3.114390347999999912401221684'), Decimal('3.129808121999999911967564366'), Decimal('3.145225895999999911533907047')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0807, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0835548}, controller_id=None),\n ExecutorInfo(id='Ccwz7KaYhnaTzgYWSQxwJWnXGhKq9oyjTAg9xntypfRd', timestamp=1704302460000.0, type='dca_executor', close_timestamp=1704302580000.0, close_type=, status=, config=DCAExecutorConfig(id='Ccwz7KaYhnaTzgYWSQxwJWnXGhKq9oyjTAg9xntypfRd', type='dca_executor', timestamp=1704302460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.082753200000000001552016847'), Decimal('3.088918706400000001555120881'), Decimal('3.107415225600000001564432982'), Decimal('3.113580732000000001567537015'), Decimal('3.128994498000000001575297100'), Decimal('3.144408264000000001583057184')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0615, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0827532}, controller_id=None),\n ExecutorInfo(id='BmPF4ppQ1hgn5RSD55rpSqxtm9Um6CP9UFHqqCXqSBbG', timestamp=1704302640000.0, type='dca_executor', close_timestamp=1704302760000.0, close_type=, status=, config=DCAExecutorConfig(id='BmPF4ppQ1hgn5RSD55rpSqxtm9Um6CP9UFHqqCXqSBbG', type='dca_executor', timestamp=1704302640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.076440600000000029318343585'), Decimal('3.082593481200000029376980272'), Decimal('3.101052124800000029552890334'), Decimal('3.107205006000000029611527021'), Decimal('3.122587209000000029758118739'), Decimal('3.137969412000000029904710457')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0635, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0764406}, controller_id=None),\n ExecutorInfo(id='DvSPrzw6KrCG2xZMz3k9AfVgHmCuqfnFpEHC3817vDwM', timestamp=1704302820000.0, type='dca_executor', close_timestamp=1704302940000.0, close_type=, status=, config=DCAExecutorConfig(id='DvSPrzw6KrCG2xZMz3k9AfVgHmCuqfnFpEHC3817vDwM', type='dca_executor', timestamp=1704302820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.084456600000000036438314864'), Decimal('3.090625513200000036511191494'), Decimal('3.109132252800000036729821383'), Decimal('3.115301166000000036802698013'), Decimal('3.130723449000000036984889587'), Decimal('3.146145732000000037167081161')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0667, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0844566}, controller_id=None),\n ExecutorInfo(id='BiJBgY2repvDwh16yXBS6k71NCBogorSda2QkDynEZux', timestamp=1704297540000.0, type='dca_executor', close_timestamp=1704308100000.0, close_type=, status=, config=DCAExecutorConfig(id='BiJBgY2repvDwh16yXBS6k71NCBogorSda2QkDynEZux', type='dca_executor', timestamp=1704297540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.139009400000000206223450816'), Decimal('3.132731381200000205811003914'), Decimal('3.113897324800000204573663209'), Decimal('3.107619306000000204161216308'), Decimal('3.091924259000000203130099054'), Decimal('3.076229212000000202098981800')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0157490360619489365434642280661137192510068416595458984375'), net_pnl_quote=Decimal('-7.7758990670627401442516202223487198352813720703125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.0467, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.099822870684043}, controller_id=None),\n ExecutorInfo(id='6LQuSPYcmYdp5QWb492QVzi3T3njQiKv7RG7btzj6ptv', timestamp=1704311700000.0, type='dca_executor', close_timestamp=1704311820000.0, close_type=, status=, config=DCAExecutorConfig(id='6LQuSPYcmYdp5QWb492QVzi3T3njQiKv7RG7btzj6ptv', type='dca_executor', timestamp=1704311700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.052981799999999930378717650'), Decimal('3.046875836399999930517960215'), Decimal('3.028557945599999930935687909'), Decimal('3.022451981999999931074930474'), Decimal('3.007187072999999931423036885'), Decimal('2.991922163999999931771143297')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0563, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0529818}, controller_id=None),\n ExecutorInfo(id='998TLAKgn1YaGp4DPPRqEnH4B1D8fSzm5Fa9Nkf1pVYY', timestamp=1704311880000.0, type='dca_executor', close_timestamp=1704312000000.0, close_type=, status=, config=DCAExecutorConfig(id='998TLAKgn1YaGp4DPPRqEnH4B1D8fSzm5Fa9Nkf1pVYY', type='dca_executor', timestamp=1704311880000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.048191400000000014764393874'), Decimal('3.042095017200000014734865086'), Decimal('3.023805868800000014646278723'), Decimal('3.017709486000000014616749935'), Decimal('3.002468529000000014542927966'), Decimal('2.987227572000000014469105997')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0549, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0481914}, controller_id=None),\n ExecutorInfo(id='7wfzbRwsc5z388eNAkCmonfbvCuQGBQ5Z2RFr3VqDZeX', timestamp=1704312060000.0, type='dca_executor', close_timestamp=1704312180000.0, close_type=, status=, config=DCAExecutorConfig(id='7wfzbRwsc5z388eNAkCmonfbvCuQGBQ5Z2RFr3VqDZeX', type='dca_executor', timestamp=1704312060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.051684399999999851666268591'), Decimal('3.045581031199999851962936054'), Decimal('3.027270924799999852852938442'), Decimal('3.021167555999999853149605905'), Decimal('3.005909133999999853891274562'), Decimal('2.990650711999999854632943219')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0638, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0516843999999996}, controller_id=None),\n ExecutorInfo(id='9yEmrBacX18Xmzqmewve2GYqvQBXjVa6NbJfQ9h49wcm', timestamp=1704312240000.0, type='dca_executor', close_timestamp=1704317520000.0, close_type=, status=, config=DCAExecutorConfig(id='9yEmrBacX18Xmzqmewve2GYqvQBXjVa6NbJfQ9h49wcm', type='dca_executor', timestamp=1704312240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.051983800000000040292615078'), Decimal('3.045879832400000040212029848'), Decimal('3.027567929600000039970274157'), Decimal('3.021463962000000039889688927'), Decimal('3.006204043000000039688225852'), Decimal('2.990944124000000039486762776')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.031078717970544240956076720294731785543262958526611328125'), net_pnl_quote=Decimal('2.21694854856548939636695649824105203151702880859375'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.1407, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.047917204763151}, controller_id=None),\n ExecutorInfo(id='DJYuBgMGxccABFVqnT3bQgkFY3ysfPro2xfXF7Pzdm5R', timestamp=1704317520000.0, type='dca_executor', close_timestamp=1704317640000.0, close_type=, status=, config=DCAExecutorConfig(id='DJYuBgMGxccABFVqnT3bQgkFY3ysfPro2xfXF7Pzdm5R', type='dca_executor', timestamp=1704317520000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.134418599999999825425316125'), Decimal('3.128149762799999825774465493'), Decimal('3.109343251199999826821913596'), Decimal('3.103074413999999827171062964'), Decimal('3.087402320999999828043936383'), Decimal('3.071730227999999828916809802')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1408, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1344185999999996}, controller_id=None),\n ExecutorInfo(id='23kVFqkPYKs3kfH7jJtqVsbHXeB2PB7LMhag7CGtrWBA', timestamp=1704317700000.0, type='dca_executor', close_timestamp=1704317820000.0, close_type=, status=, config=DCAExecutorConfig(id='23kVFqkPYKs3kfH7jJtqVsbHXeB2PB7LMhag7CGtrWBA', type='dca_executor', timestamp=1704317700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.133720000000000123965560039'), Decimal('3.127452560000000123717628919'), Decimal('3.108650240000000122973835559'), Decimal('3.102382800000000122725904439'), Decimal('3.086714200000000122106076638'), Decimal('3.071045600000000121486248838')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1449, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1337200000000003}, controller_id=None),\n ExecutorInfo(id='2oBqknwYBznjQfd7xLW4mQ69PErpT628kpNPD4XZtv3j', timestamp=1704303000000.0, type='dca_executor', close_timestamp=1704318420000.0, close_type=, status=, config=DCAExecutorConfig(id='2oBqknwYBznjQfd7xLW4mQ69PErpT628kpNPD4XZtv3j', type='dca_executor', timestamp=1704303000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999998'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.072833399999999981616617682'), Decimal('3.078979066799999981579850917'), Decimal('3.097416067199999981469550623'), Decimal('3.103561733999999981432783859'), Decimal('3.118925900999999981340866947'), Decimal('3.134290067999999981248950036')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.014410874612652495307951738823248888365924358367919921875'), net_pnl_quote=Decimal('-7.295676830686428360195350251160562038421630859375'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.1598, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.111437107001599}, controller_id=None),\n ExecutorInfo(id='6Q83eeTDQDrMLzmautWJm7bk4fyim6PFL475avG3ns95', timestamp=1704322020000.0, type='dca_executor', close_timestamp=1704322140000.0, close_type=, status=, config=DCAExecutorConfig(id='6Q83eeTDQDrMLzmautWJm7bk4fyim6PFL475avG3ns95', type='dca_executor', timestamp=1704322020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.073735200000000104786396225'), Decimal('3.079882670400000104995969017'), Decimal('3.098325081600000105624687395'), Decimal('3.104472552000000105834260187'), Decimal('3.119841228000000106358192168'), Decimal('3.135209904000000106882124150')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0668, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0737352000000002}, controller_id=None),\n ExecutorInfo(id='3DaXty3ASyC5omVix9CPb77yikQ1v2zTQX4xKuLzkEPq', timestamp=1704317880000.0, type='dca_executor', close_timestamp=1704323400000.0, close_type=, status=, config=DCAExecutorConfig(id='3DaXty3ASyC5omVix9CPb77yikQ1v2zTQX4xKuLzkEPq', type='dca_executor', timestamp=1704317880000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.135117199999999970086103640'), Decimal('3.128846965599999970145931433'), Decimal('3.110036262399999970325414811'), Decimal('3.103766027999999970385242604'), Decimal('3.088090441999999970534812085'), Decimal('3.072414855999999970684381567')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01482051719277664714180531291276565752923488616943359375'), net_pnl_quote=Decimal('-7.31745392920498449740307478350587189197540283203125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.0439, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0959792598374887}, controller_id=None),\n ExecutorInfo(id='36wmFhM1opxrBQgM5bYPDpBC3WgE1bDYVwaSVje342Mn', timestamp=1704327000000.0, type='dca_executor', close_timestamp=1704327120000.0, close_type=, status=, config=DCAExecutorConfig(id='36wmFhM1opxrBQgM5bYPDpBC3WgE1bDYVwaSVje342Mn', type='dca_executor', timestamp=1704327000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.075237199999999916904477870'), Decimal('3.069086725599999917070668914'), Decimal('3.050635302399999917569242047'), Decimal('3.044484827999999917735433091'), Decimal('3.029108641999999918150910702'), Decimal('3.013732455999999918566388313')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0848, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0752372}, controller_id=None),\n ExecutorInfo(id='BLvSMNzPyUgG64mtC9AHPEkXMLatPzYkQ6WX1viU81qi', timestamp=1704327180000.0, type='dca_executor', close_timestamp=1704327300000.0, close_type=, status=, config=DCAExecutorConfig(id='BLvSMNzPyUgG64mtC9AHPEkXMLatPzYkQ6WX1viU81qi', type='dca_executor', timestamp=1704327180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.099388799999999916194348693'), Decimal('3.093190022399999916361959996'), Decimal('3.074593689599999916864793903'), Decimal('3.068394911999999917032405206'), Decimal('3.052897967999999917451433463'), Decimal('3.037401023999999917870461719')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1201, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0993888}, controller_id=None),\n ExecutorInfo(id='8WDr1ESrfmeHZfDebRDpU4Xb2uxLjNB4z6aJALjk8Gid', timestamp=1704327360000.0, type='dca_executor', close_timestamp=1704331200000.0, close_type=, status=, config=DCAExecutorConfig(id='8WDr1ESrfmeHZfDebRDpU4Xb2uxLjNB4z6aJALjk8Gid', type='dca_executor', timestamp=1704327360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.114159200000000062272792478'), Decimal('3.107930881600000062148246893'), Decimal('3.089245926400000061774610138'), Decimal('3.083017608000000061650064553'), Decimal('3.067446812000000061338700591'), Decimal('3.051876016000000061027336628')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01367262712594022495971035624506839667446911334991455078125'), net_pnl_quote=Decimal('-6.75069687406243890137602647882886230945587158203125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.029, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0752828937406567}, controller_id=None),\n ExecutorInfo(id='M341Byz8v9UDZjay1LnrYU9Hy568Lcxu4UtQNScwVr7', timestamp=1704322200000.0, type='dca_executor', close_timestamp=1704331440000.0, close_type=, status=, config=DCAExecutorConfig(id='M341Byz8v9UDZjay1LnrYU9Hy568Lcxu4UtQNScwVr7', type='dca_executor', timestamp=1704322200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.070027799999999845631411254'), Decimal('3.076167855599999845322674077'), Decimal('3.094588022399999844396462544'), Decimal('3.100728077999999844087725367'), Decimal('3.116078216999999843315882423'), Decimal('3.131428355999999842544039479')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.034615295998641569530374084706636494956910610198974609375'), net_pnl_quote=Decimal('12.480462554938792862913032877258956432342529296875'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 2.997, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0993687346467014}, controller_id=None),\n ExecutorInfo(id='NzXHdJ5uWm6chPf8BrgUez3te6Mg3Xo1AnAy1gcTPxd', timestamp=1704334800000.0, type='dca_executor', close_timestamp=1704334920000.0, close_type=, status=, config=DCAExecutorConfig(id='NzXHdJ5uWm6chPf8BrgUez3te6Mg3Xo1AnAy1gcTPxd', type='dca_executor', timestamp=1704334800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.079229199999999920449919588'), Decimal('3.073070741599999920609019749'), Decimal('3.054595366399999921086320231'), Decimal('3.048436907999999921245420392'), Decimal('3.033040761999999921643170794'), Decimal('3.017644615999999922040921196')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0832, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0792292}, controller_id=None),\n ExecutorInfo(id='6iddh1YnxQBgYgsbKqjbKzvoXDwBPMQdkvCxGLipu3cR', timestamp=1704334980000.0, type='dca_executor', close_timestamp=1704335100000.0, close_type=, status=, config=DCAExecutorConfig(id='6iddh1YnxQBgYgsbKqjbKzvoXDwBPMQdkvCxGLipu3cR', type='dca_executor', timestamp=1704334980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.069548599999999878612146065'), Decimal('3.063409502799999878854921773'), Decimal('3.044992211199999879583248896'), Decimal('3.038853113999999879826024604'), Decimal('3.023505370999999880432963874'), Decimal('3.008157627999999881039903144')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0824, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0695486}, controller_id=None),\n ExecutorInfo(id='21MGFxoir7L5xTFi51TQzEoPrPxcWvmP6ZwJVTPv98tM', timestamp=1704331440000.0, type='dca_executor', close_timestamp=1704335820000.0, close_type=, status=, config=DCAExecutorConfig(id='21MGFxoir7L5xTFi51TQzEoPrPxcWvmP6ZwJVTPv98tM', type='dca_executor', timestamp=1704331440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999998'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.002993999999999886210563792'), Decimal('3.008999987999999885982984920'), Decimal('3.027017951999999885300248302'), Decimal('3.033023939999999885072669430'), Decimal('3.048038909999999884503722249'), Decimal('3.063053879999999883934775068')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0177243060058717427640573305325233377516269683837890625'), net_pnl_quote=Decimal('-8.973140919115497382563262362964451313018798828125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.0992, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0407203214151344}, controller_id=None),\n ExecutorInfo(id='AUKZTALxV9rUWHTHU419cmQxvTpF51DiBa8ofu4acxPy', timestamp=1704339420000.0, type='dca_executor', close_timestamp=1704339540000.0, close_type=, status=, config=DCAExecutorConfig(id='AUKZTALxV9rUWHTHU419cmQxvTpF51DiBa8ofu4acxPy', type='dca_executor', timestamp=1704339420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.096881400000000002976531520'), Decimal('3.103075162800000002982484583'), Decimal('3.121656451200000003000343772'), Decimal('3.127850214000000003006296835'), Decimal('3.143334621000000003021179493'), Decimal('3.158819028000000003036062150')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.091, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0968814}, controller_id=None),\n ExecutorInfo(id='E1XYqCLQjAZCzDC5jviLcQ9pxtCFM7JYcYLWBSXY7fPe', timestamp=1704339600000.0, type='dca_executor', close_timestamp=1704339720000.0, close_type=, status=, config=DCAExecutorConfig(id='E1XYqCLQjAZCzDC5jviLcQ9pxtCFM7JYcYLWBSXY7fPe', type='dca_executor', timestamp=1704339600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000006'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.099486600000000161032608080'), Decimal('3.105685573200000161354673296'), Decimal('3.124282492800000162320868945'), Decimal('3.130481466000000162642934161'), Decimal('3.145978899000000163448097201'), Decimal('3.161476332000000164253260242')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0913, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0994866}, controller_id=None),\n ExecutorInfo(id='CatHQLwYiWzKXid3G6yb9LFvqH25WJktxeTy2mAgBLay', timestamp=1704339780000.0, type='dca_executor', close_timestamp=1704339900000.0, close_type=, status=, config=DCAExecutorConfig(id='CatHQLwYiWzKXid3G6yb9LFvqH25WJktxeTy2mAgBLay', type='dca_executor', timestamp=1704339780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.102292199999999852040426239'), Decimal('3.108496784399999851744507091'), Decimal('3.127110537599999850856749649'), Decimal('3.133315121999999850560830501'), Decimal('3.148826582999999849821032633'), Decimal('3.164338043999999849081234764')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0981, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1022922}, controller_id=None),\n ExecutorInfo(id='CgU7sRKX1TF6TivEpRXUFoyPdpregPt6f689yzRF7Mj3', timestamp=1704335160000.0, type='dca_executor', close_timestamp=1704362160000.0, close_type=, status=, config=DCAExecutorConfig(id='CgU7sRKX1TF6TivEpRXUFoyPdpregPt6f689yzRF7Mj3', type='dca_executor', timestamp=1704335160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.076235199999999806990580442'), Decimal('3.070082729599999807376599281'), Decimal('3.051625318399999808534655798'), Decimal('3.045472847999999808920674638'), Decimal('3.030091671999999809885721735'), Decimal('3.014710495999999810850768833')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.032257939517162347031753455439684330485761165618896484375'), net_pnl_quote=Decimal('2.30106635222424760200965465628542006015777587890625'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 3.1691, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0721362911487313}, controller_id=None),\n ExecutorInfo(id='JBdrBqxYVFfRZQp4YPaMBMnwzyxvQTeHob5XJFfEUFiR', timestamp=1704362160000.0, type='dca_executor', close_timestamp=1704363360000.0, close_type=, status=, config=DCAExecutorConfig(id='JBdrBqxYVFfRZQp4YPaMBMnwzyxvQTeHob5XJFfEUFiR', type='dca_executor', timestamp=1704362160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.162761799999999806277849179'), Decimal('3.156436276399999806665293481'), Decimal('3.137459705599999807827626386'), Decimal('3.131134181999999808215070687'), Decimal('3.115320372999999809183681441'), Decimal('3.099506563999999810152292195')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01334749331035865564654141479650206747464835643768310546875'), net_pnl_quote=Decimal('-6.59016592325970140819890730199404060840606689453125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.0727, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.123278752260452}, controller_id=None),\n ExecutorInfo(id='HhAbhFALdu1DY9fjuc3i4rc4uLXNuvxKViqFMCXUpBce', timestamp=1704366960000.0, type='dca_executor', close_timestamp=1704367080000.0, close_type=, status=, config=DCAExecutorConfig(id='HhAbhFALdu1DY9fjuc3i4rc4uLXNuvxKViqFMCXUpBce', type='dca_executor', timestamp=1704366960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.072442799999999781462359239'), Decimal('3.066297914399999781899434521'), Decimal('3.047863257599999783210660365'), Decimal('3.041718371999999783647735647'), Decimal('3.026356157999999784740423850'), Decimal('3.010993943999999785833112054')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0967, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0724427999999997}, controller_id=None),\n ExecutorInfo(id='Du2p8GDpETqQij7iKQ1DzZe5tJHufLavn8TRaYiWMpbf', timestamp=1704367140000.0, type='dca_executor', close_timestamp=1704367260000.0, close_type=, status=, config=DCAExecutorConfig(id='Du2p8GDpETqQij7iKQ1DzZe5tJHufLavn8TRaYiWMpbf', type='dca_executor', timestamp=1704367140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.089608400000000106948480627'), Decimal('3.083429183200000106734583666'), Decimal('3.064891532800000106092892782'), Decimal('3.058712316000000105878995821'), Decimal('3.043264274000000105344253418'), Decimal('3.027816232000000104809511014')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1031, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0896084}, controller_id=None),\n ExecutorInfo(id='9Vg2DyiJTLc9LoLQ1uzYYowzuFsTJHaa5tg5Cab7aCuQ', timestamp=1704367320000.0, type='dca_executor', close_timestamp=1704367440000.0, close_type=, status=, config=DCAExecutorConfig(id='9Vg2DyiJTLc9LoLQ1uzYYowzuFsTJHaa5tg5Cab7aCuQ', type='dca_executor', timestamp=1704367320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.096893799999999969378576548'), Decimal('3.090700012399999969439819395'), Decimal('3.072118649599999969623547936'), Decimal('3.065924861999999969684790783'), Decimal('3.050440392999999969837897900'), Decimal('3.034955923999999969991005017')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1059, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0968938}, controller_id=None),\n ExecutorInfo(id='EyFhy8o8HirDjzPoS5A5tXgctwJKANNp3FmnCULqm2Qa', timestamp=1704367500000.0, type='dca_executor', close_timestamp=1704367620000.0, close_type=, status=, config=DCAExecutorConfig(id='EyFhy8o8HirDjzPoS5A5tXgctwJKANNp3FmnCULqm2Qa', type='dca_executor', timestamp=1704367500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.096694199999999991361356033'), Decimal('3.090500811599999991378633321'), Decimal('3.071920646399999991430465185'), Decimal('3.065727257999999991447742473'), Decimal('3.050243786999999991490935693'), Decimal('3.034760315999999991534128912')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0977, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0966942}, controller_id=None),\n ExecutorInfo(id='3fsX92tY4dDSoVw9A67bCoBnhJWGMB54XMAhxpRgGsNZ', timestamp=1704367680000.0, type='dca_executor', close_timestamp=1704367800000.0, close_type=, status=, config=DCAExecutorConfig(id='3fsX92tY4dDSoVw9A67bCoBnhJWGMB54XMAhxpRgGsNZ', type='dca_executor', timestamp=1704367680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('3.094199200000000044545583888'), Decimal('3.088010801600000044456492720'), Decimal('3.069445606400000044189219217'), Decimal('3.063257208000000044100128049'), Decimal('3.047786212000000043877400130'), Decimal('3.032315216000000043654672210')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0975, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0941992000000003}, controller_id=None),\n ExecutorInfo(id='2fdUq3rzrMjRusCHmCocT6LiZNniddC38SpxqygWxfKD', timestamp=1704367860000.0, type='dca_executor', close_timestamp=1704367980000.0, close_type=, status=, config=DCAExecutorConfig(id='2fdUq3rzrMjRusCHmCocT6LiZNniddC38SpxqygWxfKD', type='dca_executor', timestamp=1704367860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.098889800000000192751813122'), Decimal('3.092692020400000192366309496'), Decimal('3.074098681600000191209798617'), Decimal('3.067900902000000190824294991'), Decimal('3.052406453000000189860535925'), Decimal('3.036912004000000188896776860')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1144, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0988898000000002}, controller_id=None),\n ExecutorInfo(id='BEU5gE7XgFbLYuibRYyKNsMABq6edzssBRvAfbBBbe75', timestamp=1704368040000.0, type='dca_executor', close_timestamp=1704368160000.0, close_type=, status=, config=DCAExecutorConfig(id='BEU5gE7XgFbLYuibRYyKNsMABq6edzssBRvAfbBBbe75', type='dca_executor', timestamp=1704368040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.095496600000000123258032947'), Decimal('3.089305606800000123011516881'), Decimal('3.070732627200000122271968683'), Decimal('3.064541634000000122025452618'), Decimal('3.049064151000000121409162453'), Decimal('3.033586668000000120792872288')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1069, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0954966}, controller_id=None),\n ExecutorInfo(id='EVdrMFhiq6k323ev7tRovcUtBDrYkzMxAeHegyU2cnd8', timestamp=1704368220000.0, type='dca_executor', close_timestamp=1704368340000.0, close_type=, status=, config=DCAExecutorConfig(id='EVdrMFhiq6k323ev7tRovcUtBDrYkzMxAeHegyU2cnd8', type='dca_executor', timestamp=1704368220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571434'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000001')], prices=[Decimal('3.096993600000000179987702520'), Decimal('3.090799612800000179627727115'), Decimal('3.072217651200000178547800900'), Decimal('3.066023664000000178187825495'), Decimal('3.050538696000000177287886982'), Decimal('3.035053728000000176387948470')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1089, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0969936000000002}, controller_id=None),\n ExecutorInfo(id='5xAn8SG3mg2Ge6TqFf8KMNmLgMa6NXprEPBBuq3XLjQe', timestamp=1704339960000.0, type='dca_executor', close_timestamp=1704381540000.0, close_type=, status=, config=DCAExecutorConfig(id='5xAn8SG3mg2Ge6TqFf8KMNmLgMa6NXprEPBBuq3XLjQe', type='dca_executor', timestamp=1704339960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.102793200000000019351945045'), Decimal('3.108998786400000019390648935'), Decimal('3.127615545600000019506760605'), Decimal('3.133821132000000019545464495'), Decimal('3.149335098000000019642224221'), Decimal('3.164849064000000019738983946')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01488779426553446007253089788946454063989222049713134765625'), net_pnl_quote=Decimal('-7.5371230825728385838147005415521562099456787109375'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.1933, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1417732890537553}, controller_id=None),\n ExecutorInfo(id='GSEzTaGjJf6idatM2Mfj7XeS1bUZvsVquprpL3pPoQFT', timestamp=1704368400000.0, type='dca_executor', close_timestamp=1704382020000.0, close_type=, status=, config=DCAExecutorConfig(id='GSEzTaGjJf6idatM2Mfj7XeS1bUZvsVquprpL3pPoQFT', type='dca_executor', timestamp=1704368400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.102083399999999841027341352'), Decimal('3.095879233199999841345286669'), Decimal('3.077266732799999842299122621'), Decimal('3.071062565999999842617067938'), Decimal('3.055552148999999843411931232'), Decimal('3.040041731999999844206794525')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0313729283549353255011737928725779056549072265625'), net_pnl_quote=Decimal('0.7469744846413173267052343362593092024326324462890625'), cum_fees_quote=Decimal('0.01428571428571428526821396332024960429407656192779541015625'), filled_amount_quote=Decimal('23.8095238095238102005168912000954151153564453125'), is_active=False, is_trading=False, custom_info={'close_price': 3.1987, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1020833999999997}, controller_id=None),\n ExecutorInfo(id='9W7nBNiBGPNdCkS42itVHHLcifh6XjQG3eQVVau4X1EV', timestamp=1704382020000.0, type='dca_executor', close_timestamp=1704382140000.0, close_type=, status=, config=DCAExecutorConfig(id='9W7nBNiBGPNdCkS42itVHHLcifh6XjQG3eQVVau4X1EV', type='dca_executor', timestamp=1704382020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.192302600000000098434736751'), Decimal('3.185917994800000098237867277'), Decimal('3.166764179200000097647258857'), Decimal('3.160379574000000097450389383'), Decimal('3.144418061000000096958215700'), Decimal('3.128456548000000096466042016')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.2092, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1923026}, controller_id=None),\n ExecutorInfo(id='8MtgqDbZBreJCZK8YUFkR3onLoZo8WNHUCuyPMvJr36K', timestamp=1704382200000.0, type='dca_executor', close_timestamp=1704382320000.0, close_type=, status=, config=DCAExecutorConfig(id='8MtgqDbZBreJCZK8YUFkR3onLoZo8WNHUCuyPMvJr36K', type='dca_executor', timestamp=1704382200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.214757600000000062977717485'), Decimal('3.208328084800000062851762050'), Decimal('3.189039539200000062473895745'), Decimal('3.182610024000000062347940310'), Decimal('3.166536236000000062033051723'), Decimal('3.150462448000000061718163135')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.2186, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.2147576}, controller_id=None),\n ExecutorInfo(id='7Rne4n8QcQKZ4hEmqbYEwUpd57UgAe93ES1yn7RjFhr6', timestamp=1704382380000.0, type='dca_executor', close_timestamp=1704382500000.0, close_type=, status=, config=DCAExecutorConfig(id='7Rne4n8QcQKZ4hEmqbYEwUpd57UgAe93ES1yn7RjFhr6', type='dca_executor', timestamp=1704382380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.196095000000000123962957954'), Decimal('3.189702810000000123715032038'), Decimal('3.170526240000000122971254290'), Decimal('3.164134050000000122723328374'), Decimal('3.148153575000000122103513585'), Decimal('3.132173100000000121483698795')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.2143, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.196095}, controller_id=None),\n ExecutorInfo(id='WMYXLpERBcyMoMMnm36TCgmdCi2r6kgTP9RGw4A6B9v', timestamp=1704382560000.0, type='dca_executor', close_timestamp=1704382680000.0, close_type=, status=, config=DCAExecutorConfig(id='WMYXLpERBcyMoMMnm36TCgmdCi2r6kgTP9RGw4A6B9v', type='dca_executor', timestamp=1704382560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.215156800000000019012158514'), Decimal('3.208726486400000018974134197'), Decimal('3.189435545600000018860061246'), Decimal('3.183005232000000018822036929'), Decimal('3.166929448000000018726976136'), Decimal('3.150853664000000018631915344')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.2272, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.2151568}, controller_id=None),\n ExecutorInfo(id='GyQrh1Lb8LSxWXXmRrdGS8LK4DxBgSc6iryeZrXYwMNJ', timestamp=1704385140000.0, type='dca_executor', close_timestamp=1704385260000.0, close_type=, status=, config=DCAExecutorConfig(id='GyQrh1Lb8LSxWXXmRrdGS8LK4DxBgSc6iryeZrXYwMNJ', type='dca_executor', timestamp=1704385140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.187863000000000217281422020'), Decimal('3.194238726000000217715984864'), Decimal('3.213365904000000219019673396'), Decimal('3.219741630000000219454236240'), Decimal('3.235680945000000220540643350'), Decimal('3.251620260000000221627050460')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1788, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.187863}, controller_id=None),\n ExecutorInfo(id='44AvEJnyHHAPhJbNLUTKdftKL8DntHrdPgL4BeFSUx1g', timestamp=1704385320000.0, type='dca_executor', close_timestamp=1704385440000.0, close_type=, status=, config=DCAExecutorConfig(id='44AvEJnyHHAPhJbNLUTKdftKL8DntHrdPgL4BeFSUx1g', type='dca_executor', timestamp=1704385320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047616'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.171530400000000013659090524'), Decimal('3.177873460800000013686408705'), Decimal('3.196902643200000013768363248'), Decimal('3.203245704000000013795681429'), Decimal('3.219103356000000013863976882'), Decimal('3.234961008000000013932272334')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.157, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1715304}, controller_id=None),\n ExecutorInfo(id='8NBoYg87bYmLpZUQrLHiGBegJRDkr8BtZFb2k1vy5Fn8', timestamp=1704385500000.0, type='dca_executor', close_timestamp=1704385620000.0, close_type=, status=, config=DCAExecutorConfig(id='8NBoYg87bYmLpZUQrLHiGBegJRDkr8BtZFb2k1vy5Fn8', type='dca_executor', timestamp=1704385500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.164315999999999918255638719'), Decimal('3.170644631999999918092149996'), Decimal('3.189630527999999917601683829'), Decimal('3.195959159999999917438195106'), Decimal('3.211780739999999917029473300'), Decimal('3.227602319999999916620751493')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1639, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.164316}, controller_id=None),\n ExecutorInfo(id='DXY3RNbq8n8XsDuKqvZH6yEopbetfUEtdAbNPqgQtcr4', timestamp=1704385680000.0, type='dca_executor', close_timestamp=1704385800000.0, close_type=, status=, config=DCAExecutorConfig(id='DXY3RNbq8n8XsDuKqvZH6yEopbetfUEtdAbNPqgQtcr4', type='dca_executor', timestamp=1704385680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('3.162312000000000138964340034'), Decimal('3.168636624000000139242268714'), Decimal('3.187610496000000140076054754'), Decimal('3.193935120000000140353983434'), Decimal('3.209746680000000141048805135'), Decimal('3.225558240000000141743626835')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.157, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.162312}, controller_id=None),\n ExecutorInfo(id='7DR7mV9DedEiQT5vwuDhQ2zjnLPH5c6cVC8dm4Bj7E9g', timestamp=1704385860000.0, type='dca_executor', close_timestamp=1704385980000.0, close_type=, status=, config=DCAExecutorConfig(id='7DR7mV9DedEiQT5vwuDhQ2zjnLPH5c6cVC8dm4Bj7E9g', type='dca_executor', timestamp=1704385860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.169325999999999811461273701'), Decimal('3.175664651999999811084196248'), Decimal('3.194680607999999809952963891'), Decimal('3.201019259999999809575886438'), Decimal('3.216865889999999808633192807'), Decimal('3.232712519999999807690499175')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1646, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.169326}, controller_id=None),\n ExecutorInfo(id='6SmD513qHvvXHtTuU1izxLuibE24PCDiZGVj59RDoMth', timestamp=1704386040000.0, type='dca_executor', close_timestamp=1704386160000.0, close_type=, status=, config=DCAExecutorConfig(id='6SmD513qHvvXHtTuU1izxLuibE24PCDiZGVj59RDoMth', type='dca_executor', timestamp=1704386040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.170428199999999912560182112'), Decimal('3.176769056399999912385302476'), Decimal('3.195791625599999911860663569'), Decimal('3.202132481999999911685783933'), Decimal('3.217984622999999911248584844'), Decimal('3.233836763999999910811385754')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1563, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1704282}, controller_id=None),\n ExecutorInfo(id='8ZXz97CXUNKgPZuSkyHzb2xZTUbKj5zQEERWTwvQWHv', timestamp=1704386220000.0, type='dca_executor', close_timestamp=1704386340000.0, close_type=, status=, config=DCAExecutorConfig(id='8ZXz97CXUNKgPZuSkyHzb2xZTUbKj5zQEERWTwvQWHv', type='dca_executor', timestamp=1704386220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.175037399999999849907557357'), Decimal('3.181387474799999849607372472'), Decimal('3.200437699199999848706817816'), Decimal('3.206787773999999848406632931'), Decimal('3.222662960999999847656170717'), Decimal('3.238538147999999846905708504')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.1646, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1750374}, controller_id=None),\n ExecutorInfo(id='8Z4hUJv5ThyeW26vQaA6iqRKe1s1fsV6p8bY8SMHTNmx', timestamp=1704386400000.0, type='dca_executor', close_timestamp=1704386520000.0, close_type=, status=, config=DCAExecutorConfig(id='8Z4hUJv5ThyeW26vQaA6iqRKe1s1fsV6p8bY8SMHTNmx', type='dca_executor', timestamp=1704386400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.170929200000000079871700918'), Decimal('3.177271058400000080031444320'), Decimal('3.196296633600000080510674525'), Decimal('3.202638492000000080670417927'), Decimal('3.218493138000000081069776432'), Decimal('3.234347784000000081469134936')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.163, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.1709292000000002}, controller_id=None),\n ExecutorInfo(id='7LBtNuoSAGBHKEd2Lf8JwhUFKVYHn44fhdYrMwsN8NJs', timestamp=1704382740000.0, type='dca_executor', close_timestamp=1704419220000.0, close_type=, status=, config=DCAExecutorConfig(id='7LBtNuoSAGBHKEd2Lf8JwhUFKVYHn44fhdYrMwsN8NJs', type='dca_executor', timestamp=1704382740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.220745599999999846695364347'), Decimal('3.214304108799999847001973618'), Decimal('3.194979635199999847921801432'), Decimal('3.188538143999999848228410704'), Decimal('3.172434415999999848994933882'), Decimal('3.156330687999999849761457060')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0144564951537943971182631486271930043585598468780517578125'), net_pnl_quote=Decimal('-7.13772238105320067091952296323142945766448974609375'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 3.131, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.1805386984616866}, controller_id=None),\n ExecutorInfo(id='Ah4hikxVXa7gQ93Srmvf14FxqjviBxyyW3J3jeYiFFSM', timestamp=1704386580000.0, type='dca_executor', close_timestamp=1704419280000.0, close_type=, status=, config=DCAExecutorConfig(id='Ah4hikxVXa7gQ93Srmvf14FxqjviBxyyW3J3jeYiFFSM', type='dca_executor', timestamp=1704386580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.169325999999999811461273701'), Decimal('3.175664651999999811084196248'), Decimal('3.194680607999999809952963891'), Decimal('3.201019259999999809575886438'), Decimal('3.216865889999999808633192807'), Decimal('3.232712519999999807690499175')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.064999559240774329449408241998753510415554046630859375'), net_pnl_quote=Decimal('32.906800669918681023773388005793094635009765625'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.0041, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.209141934146169}, controller_id=None),\n ExecutorInfo(id='8V9cGMojSQ5HTwMwc8Dr5awhtBFh1ddoBQYtxkQpTszU', timestamp=1704419280000.0, type='dca_executor', close_timestamp=1704419640000.0, close_type=, status=, config=DCAExecutorConfig(id='8V9cGMojSQ5HTwMwc8Dr5awhtBFh1ddoBQYtxkQpTszU', type='dca_executor', timestamp=1704419280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.010108200000000215138144798'), Decimal('3.016128416400000215568421088'), Decimal('3.034189065600000216859249956'), Decimal('3.040209282000000217289526246'), Decimal('3.055259823000000218365216970'), Decimal('3.070310364000000219440907694')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0068582201131117681569548949482850730419158935546875'), net_pnl_quote=Decimal('-3.47205557774036943641249308711849153041839599609375'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 3.0977, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.047923896417486}, controller_id=None),\n ExecutorInfo(id='ERhnEqELbFJBFHMCCSmAvbpixVWcsx1DESYgCgEEzuu7', timestamp=1704422820000.0, type='dca_executor', close_timestamp=1704422940000.0, close_type=, status=, config=DCAExecutorConfig(id='ERhnEqELbFJBFHMCCSmAvbpixVWcsx1DESYgCgEEzuu7', type='dca_executor', timestamp=1704422820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.078330999999999797771911558'), Decimal('3.072174337999999798176367735'), Decimal('3.053704351999999799389736266'), Decimal('3.047547689999999799794192442'), Decimal('3.032156034999999800805332885'), Decimal('3.016764379999999801816473327')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0886, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.078331}, controller_id=None),\n ExecutorInfo(id='8pGwy1m3MstZSmMHNQHsbqdWkQYRGDYWWkDJefzXQE9q', timestamp=1704423240000.0, type='dca_executor', close_timestamp=1704423360000.0, close_type=, status=, config=DCAExecutorConfig(id='8pGwy1m3MstZSmMHNQHsbqdWkQYRGDYWWkDJefzXQE9q', type='dca_executor', timestamp=1704423240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.082352400000000045693757110'), Decimal('3.088517104800000045785144624'), Decimal('3.107011219200000046059307167'), Decimal('3.113175924000000046150694681'), Decimal('3.128587686000000046379163467'), Decimal('3.143999448000000046607632252')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0689, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0823524}, controller_id=None),\n ExecutorInfo(id='JA3Xwcum9oY2azFwE7GT28WviVCP7CJfuNhmhGkvq36R', timestamp=1704423420000.0, type='dca_executor', close_timestamp=1704423540000.0, close_type=, status=, config=DCAExecutorConfig(id='JA3Xwcum9oY2azFwE7GT28WviVCP7CJfuNhmhGkvq36R', type='dca_executor', timestamp=1704423420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.075037800000000183814434505'), Decimal('3.081187875600000184182063374'), Decimal('3.099638102400000185284949981'), Decimal('3.105788178000000185652578850'), Decimal('3.121163367000000186571651023'), Decimal('3.136538556000000187490723195')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 3.0732, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0750378}, controller_id=None),\n ExecutorInfo(id='CWJPgeLupjVB4WJzaBqyKHVXQLbktTC5E6c9ZUF4z1pq', timestamp=1704423600000.0, type='dca_executor', close_timestamp=1704464400000.0, close_type=, status=, config=DCAExecutorConfig(id='CWJPgeLupjVB4WJzaBqyKHVXQLbktTC5E6c9ZUF4z1pq', type='dca_executor', timestamp=1704423600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.078144000000000064204641602'), Decimal('3.084300288000000064333050885'), Decimal('3.102769152000000064718278735'), Decimal('3.108925440000000064846688018'), Decimal('3.124316160000000065167711226'), Decimal('3.139706880000000065488734434')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.034742183517674628256965974060221924446523189544677734375'), net_pnl_quote=Decimal('8.3281977060940040047398724709637463092803955078125'), cum_fees_quote=Decimal('0.1438285714285714167459673262783326208591461181640625'), filled_amount_quote=Decimal('239.71428571428572240620269440114498138427734375'), is_active=False, is_trading=False, custom_info={'close_price': 2.9949, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.099117448390942}, controller_id=None),\n ExecutorInfo(id='fGaMG4uC8dSbKRDVK9khkbKKFV8piL4u5NLYM84iSmJ', timestamp=1704423000000.0, type='dca_executor', close_timestamp=1704465300000.0, close_type=, status=, config=DCAExecutorConfig(id='fGaMG4uC8dSbKRDVK9khkbKKFV8piL4u5NLYM84iSmJ', type='dca_executor', timestamp=1704423000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('3.075736200000000083548044871'), Decimal('3.069584727600000083380948781'), Decimal('3.051130310400000082879660512'), Decimal('3.044978838000000082712564422'), Decimal('3.029600157000000082294824198'), Decimal('3.014221476000000081877083974')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.02088586157362193329589672430302016437053680419921875'), net_pnl_quote=Decimal('-10.31214551076661933848299668170511722564697265625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.9723, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 3.0373395558964655}, controller_id=None),\n ExecutorInfo(id='Ce6Bhfou2ThAeZCaN1XGYhsQgUYaMvTX8vVponKTUNLj', timestamp=1704468900000.0, type='dca_executor', close_timestamp=1704469020000.0, close_type=, status=, config=DCAExecutorConfig(id='Ce6Bhfou2ThAeZCaN1XGYhsQgUYaMvTX8vVponKTUNLj', type='dca_executor', timestamp=1704468900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.964359399999999940310117796'), Decimal('2.958430681199999940429497560'), Decimal('2.940644524799999940787636854'), Decimal('2.934715805999999940907016618'), Decimal('2.919894008999999941205466029'), Decimal('2.905072211999999941503915440')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9652, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9643593999999998}, controller_id=None),\n ExecutorInfo(id='A6iftg1kz8GwN1WSmJRpKahG7YG1ua1pkYAFVJr6hGWP', timestamp=1704469080000.0, type='dca_executor', close_timestamp=1704469200000.0, close_type=, status=, config=DCAExecutorConfig(id='A6iftg1kz8GwN1WSmJRpKahG7YG1ua1pkYAFVJr6hGWP', type='dca_executor', timestamp=1704469080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.960766599999999892799117107'), Decimal('2.954845066799999893013518873'), Decimal('2.937080467199999893656724170'), Decimal('2.931158933999999893871125936'), Decimal('2.916355100999999894407130350'), Decimal('2.901551267999999894943134765')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9663, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9607666}, controller_id=None),\n ExecutorInfo(id='7BnBih2a5oTG3yQSJw6TzTm1dYYqFTvdMFXggdqXNytU', timestamp=1704469260000.0, type='dca_executor', close_timestamp=1704469380000.0, close_type=, status=, config=DCAExecutorConfig(id='7BnBih2a5oTG3yQSJw6TzTm1dYYqFTvdMFXggdqXNytU', type='dca_executor', timestamp=1704469260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.965157799999999852378999854'), Decimal('2.959227484399999852674241854'), Decimal('2.941436537599999853559967855'), Decimal('2.935506221999999853855209855'), Decimal('2.920680432999999854593314856'), Decimal('2.905854643999999855331419857')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9705, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9651578}, controller_id=None),\n ExecutorInfo(id='7g11a4r7RFRCBSDhLXCkz56qeivsKDyhQa743z8KVmo', timestamp=1704469440000.0, type='dca_executor', close_timestamp=1704469560000.0, close_type=, status=, config=DCAExecutorConfig(id='7g11a4r7RFRCBSDhLXCkz56qeivsKDyhQa743z8KVmo', type='dca_executor', timestamp=1704469440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.947193800000000058025027838'), Decimal('2.941299412400000057908977782'), Decimal('2.923616249600000057560827615'), Decimal('2.917721862000000057444777560'), Decimal('2.902985893000000057154652420'), Decimal('2.888249924000000056864527281')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9635, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9471938}, controller_id=None),\n ExecutorInfo(id='4S9AuEjwY5M6Ap8PHX85UiHxJUuwCkqVnV7AqTA6LWru', timestamp=1704469620000.0, type='dca_executor', close_timestamp=1704469740000.0, close_type=, status=, config=DCAExecutorConfig(id='4S9AuEjwY5M6Ap8PHX85UiHxJUuwCkqVnV7AqTA6LWru', type='dca_executor', timestamp=1704469620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.952982199999999863725454186'), Decimal('2.947076235599999863998003278'), Decimal('2.929358342399999864815650553'), Decimal('2.923452377999999865088199644'), Decimal('2.908687466999999865769572373'), Decimal('2.893922555999999866450945102')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9688, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9529821999999997}, controller_id=None),\n ExecutorInfo(id='9uURWtLRnUeTbuFPmirHBhQvjEPYjPZFz4XXEATZFtdm', timestamp=1704469800000.0, type='dca_executor', close_timestamp=1704469920000.0, close_type=, status=, config=DCAExecutorConfig(id='9uURWtLRnUeTbuFPmirHBhQvjEPYjPZFz4XXEATZFtdm', type='dca_executor', timestamp=1704469800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.954579000000000131064249731'), Decimal('2.948669842000000130802121232'), Decimal('2.930942368000000130015735733'), Decimal('2.925033210000000129753607234'), Decimal('2.910260315000000129098285985'), Decimal('2.895487420000000128442964736')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9605, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9545790000000003}, controller_id=None),\n ExecutorInfo(id='5eFLc6iobJAB4Bsgst62XaNvU9qk2Cv4PT46FuoZ5Yo2', timestamp=1704469980000.0, type='dca_executor', close_timestamp=1704470100000.0, close_type=, status=, config=DCAExecutorConfig(id='5eFLc6iobJAB4Bsgst62XaNvU9qk2Cv4PT46FuoZ5Yo2', type='dca_executor', timestamp=1704469980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.946195800000000167938925266'), Decimal('2.940303408400000167603047415'), Decimal('2.922626233600000166595413864'), Decimal('2.916733842000000166259536013'), Decimal('2.902002863000000165419841387'), Decimal('2.887271884000000164580146761')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9482, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9461958000000004}, controller_id=None),\n ExecutorInfo(id='Bv374hG8F2wZPtQtWC8mW9MRQmBKLRkTwGncnvCfu6Fs', timestamp=1704470160000.0, type='dca_executor', close_timestamp=1704470280000.0, close_type=, status=, config=DCAExecutorConfig(id='Bv374hG8F2wZPtQtWC8mW9MRQmBKLRkTwGncnvCfu6Fs', type='dca_executor', timestamp=1704470160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.942303599999999931801578090'), Decimal('2.936418992799999931937974934'), Decimal('2.918765171199999932347165465'), Decimal('2.912880563999999932483562309'), Decimal('2.898169045999999932824554419'), Decimal('2.883457527999999933165546528')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9605, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9423036}, controller_id=None),\n ExecutorInfo(id='4ji5N3fikGpS38xe9CoTQppqwSSDH8rsihCBty2E65tx', timestamp=1704470340000.0, type='dca_executor', close_timestamp=1704470460000.0, close_type=, status=, config=DCAExecutorConfig(id='4ji5N3fikGpS38xe9CoTQppqwSSDH8rsihCBty2E65tx', type='dca_executor', timestamp=1704470340000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000001')], prices=[Decimal('2.958171800000000178575250420'), Decimal('2.952255456400000178218099919'), Decimal('2.934506425600000177146648417'), Decimal('2.928590082000000176789497916'), Decimal('2.913799223000000175896621664'), Decimal('2.899008364000000175003745412')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9853, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9581718}, controller_id=None),\n ExecutorInfo(id='2xM6HRJTkLUBR3s352rnmSe6Nk5wYRiLU5hxwJfoSK1o', timestamp=1704464400000.0, type='dca_executor', close_timestamp=1704474300000.0, close_type=, status=, config=DCAExecutorConfig(id='2xM6HRJTkLUBR3s352rnmSe6Nk5wYRiLU5hxwJfoSK1o', type='dca_executor', timestamp=1704464400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('3.000889799999999895466006038'), Decimal('3.006891579599999895256938050'), Decimal('3.024896918399999894629734086'), Decimal('3.030898697999999894420666098'), Decimal('3.045903146999999893897996129'), Decimal('3.060907595999999893375326159')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.036492954001594364588090257939256844110786914825439453125'), net_pnl_quote=Decimal('2.6101150909711776648691738955676555633544921875'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 2.899, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 3.0048936503057257}, controller_id=None),\n ExecutorInfo(id='BGaP2V7nznqr4czBcNBCsZqqqd74pjqfXt1aPtBWkEgV', timestamp=1704470520000.0, type='dca_executor', close_timestamp=1704474480000.0, close_type=, status=, config=DCAExecutorConfig(id='BGaP2V7nznqr4czBcNBCsZqqqd74pjqfXt1aPtBWkEgV', type='dca_executor', timestamp=1704470520000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.976235600000000183538348408'), Decimal('2.970283128800000183171271711'), Decimal('2.952425715200000182070041621'), Decimal('2.946473244000000181702964924'), Decimal('2.931592066000000180785273182'), Decimal('2.916710888000000179867581440')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0121839387249758727971293836844779434613883495330810546875'), net_pnl_quote=Decimal('-6.0156746985672544525414195959456264972686767578125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.8949, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.93908109399865}, controller_id=None),\n ExecutorInfo(id='94XYuwV5RtrPedXRPmejAtAQWK3mx4g4krDznR1oMHi6', timestamp=1704478080000.0, type='dca_executor', close_timestamp=1704478200000.0, close_type=, status=, config=DCAExecutorConfig(id='94XYuwV5RtrPedXRPmejAtAQWK3mx4g4krDznR1oMHi6', type='dca_executor', timestamp=1704478080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.921345600000000023988266928'), Decimal('2.915502908800000023940290394'), Decimal('2.897974835200000023796360793'), Decimal('2.892132144000000023748384259'), Decimal('2.877525416000000023628442924'), Decimal('2.862918688000000023508501589')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9239, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9213456}, controller_id=None),\n ExecutorInfo(id='EhPNQhBgD9qFTXkNpGidPkFrcSijs42sNjni8ENE1jxv', timestamp=1704478260000.0, type='dca_executor', close_timestamp=1704478380000.0, close_type=, status=, config=DCAExecutorConfig(id='EhPNQhBgD9qFTXkNpGidPkFrcSijs42sNjni8ENE1jxv', type='dca_executor', timestamp=1704478260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.920148000000000155884943842'), Decimal('2.914307704000000155573173954'), Decimal('2.896786816000000154637864291'), Decimal('2.890946520000000154326094404'), Decimal('2.876345780000000153546669684'), Decimal('2.861745040000000152767244965')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9285, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.920148}, controller_id=None),\n ExecutorInfo(id='EpMSPThyAxpHRwmT8SuJUrMJEvoFqjahNMn3gPcRr698', timestamp=1704478440000.0, type='dca_executor', close_timestamp=1704478560000.0, close_type=, status=, config=DCAExecutorConfig(id='EpMSPThyAxpHRwmT8SuJUrMJEvoFqjahNMn3gPcRr698', type='dca_executor', timestamp=1704478440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.926834600000000084263378219'), Decimal('2.920980930800000084094851463'), Decimal('2.903419923200000083589271193'), Decimal('2.897566254000000083420744437'), Decimal('2.882932081000000082999427546'), Decimal('2.868297908000000082578110655')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9303, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9268346000000003}, controller_id=None),\n ExecutorInfo(id='9BtHjkc39WSYdynXZzwo2rT7wwsRDum3z59PF9oHyUiq', timestamp=1704478620000.0, type='dca_executor', close_timestamp=1704478740000.0, close_type=, status=, config=DCAExecutorConfig(id='9BtHjkc39WSYdynXZzwo2rT7wwsRDum3z59PF9oHyUiq', type='dca_executor', timestamp=1704478620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.921545200000000002005487443'), Decimal('2.915702109600000002001476468'), Decimal('2.898172838400000001989443543'), Decimal('2.892329748000000001985432569'), Decimal('2.877722022000000001975405131'), Decimal('2.863114296000000001965377694')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9219, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9215452}, controller_id=None),\n ExecutorInfo(id='32PF8nTXMJ1rkcpfL4WYVNDyQh1nQdQdRbsSGxGAouxP', timestamp=1704478800000.0, type='dca_executor', close_timestamp=1704498960000.0, close_type=, status=, config=DCAExecutorConfig(id='32PF8nTXMJ1rkcpfL4WYVNDyQh1nQdQdRbsSGxGAouxP', type='dca_executor', timestamp=1704478800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.913561199999999994914604007'), Decimal('2.907734077599999994924774799'), Decimal('2.890252710399999994955287175'), Decimal('2.884425587999999994965457967'), Decimal('2.869857781999999994990884947'), Decimal('2.855289975999999995016311927')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.03178415474597177936555425503684091381728649139404296875'), net_pnl_quote=Decimal('11.243266358592922671277847257442772388458251953125'), cum_fees_quote=Decimal('0.212242857142857122010326520467060618102550506591796875'), filled_amount_quote=Decimal('353.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.9731, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.885856188319984}, controller_id=None),\n ExecutorInfo(id='3bqrFYQGxqtbsAzdoN8Aut84PNjNg1jAfMQ3d8MFwgiy', timestamp=1704498960000.0, type='dca_executor', close_timestamp=1704499080000.0, close_type=, status=, config=DCAExecutorConfig(id='3bqrFYQGxqtbsAzdoN8Aut84PNjNg1jAfMQ3d8MFwgiy', type='dca_executor', timestamp=1704498960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.967153800000000075752236428'), Decimal('2.961219492400000075600731955'), Decimal('2.943416569600000075146218537'), Decimal('2.937482262000000074994714064'), Decimal('2.922646493000000074615952882'), Decimal('2.907810724000000074237191699')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.9773, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.9671538}, controller_id=None),\n ExecutorInfo(id='94M3BE3Ci45ZePH5jWZgbwdEg7BNw3eqWTYcZZGG1GsQ', timestamp=1704499140000.0, type='dca_executor', close_timestamp=1704504000000.0, close_type=, status=, config=DCAExecutorConfig(id='94M3BE3Ci45ZePH5jWZgbwdEg7BNw3eqWTYcZZGG1GsQ', type='dca_executor', timestamp=1704499140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.975137800000000082843119864'), Decimal('2.969187524400000082677433624'), Decimal('2.951336697600000082180374905'), Decimal('2.945386422000000082014688665'), Decimal('2.930510733000000081600473066'), Decimal('2.915635044000000081186257467')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0156901973586849215192007278574237716384232044219970703125'), net_pnl_quote=Decimal('-7.7468481577868857357316301204264163970947265625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.8906, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.937996998631673}, controller_id=None),\n ExecutorInfo(id='8v4XLKD2RCRC6VFiRn3354Gr8PDZaoynzFBmc6HEuGR1', timestamp=1704507600000.0, type='dca_executor', close_timestamp=1704507720000.0, close_type=, status=, config=DCAExecutorConfig(id='8v4XLKD2RCRC6VFiRn3354Gr8PDZaoynzFBmc6HEuGR1', type='dca_executor', timestamp=1704507600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.859070399999999791398963556'), Decimal('2.853352259199999791816165629'), Decimal('2.836197836799999793067771848'), Decimal('2.830479695999999793484973920'), Decimal('2.816184343999999794527979103'), Decimal('2.801888991999999795570984285')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8715, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8590704}, controller_id=None),\n ExecutorInfo(id='31jaDzZHoPchfBxNw9mTZTScdgSbG8Pzz4rU1z4RSoH7', timestamp=1704507780000.0, type='dca_executor', close_timestamp=1704507900000.0, close_type=, status=, config=DCAExecutorConfig(id='31jaDzZHoPchfBxNw9mTZTScdgSbG8Pzz4rU1z4RSoH7', type='dca_executor', timestamp=1704507780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.869449599999999977897524595'), Decimal('2.863710700799999977941729546'), Decimal('2.846494003199999978074344398'), Decimal('2.840755103999999978118549349'), Decimal('2.826407855999999978229061726'), Decimal('2.812060607999999978339574103')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8844, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8694496}, controller_id=None),\n ExecutorInfo(id='3SKjPgfK5PhWjBtJeTMHJLk7Wsc1aYqrUNPZGSHDMKbQ', timestamp=1704507960000.0, type='dca_executor', close_timestamp=1704508080000.0, close_type=, status=, config=DCAExecutorConfig(id='3SKjPgfK5PhWjBtJeTMHJLk7Wsc1aYqrUNPZGSHDMKbQ', type='dca_executor', timestamp=1704507960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571425'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.881824799999999944568290777'), Decimal('2.876061150399999944679154195'), Decimal('2.858770201599999945011744451'), Decimal('2.853006551999999945122607869'), Decimal('2.838597427999999945399766415'), Decimal('2.824188303999999945676924961')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8896, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8818248}, controller_id=None),\n ExecutorInfo(id='2tqxuywFsCfiEFCuKbwhPggaVu5BcXRrhCFjVasYU6kG', timestamp=1704508140000.0, type='dca_executor', close_timestamp=1704508260000.0, close_type=, status=, config=DCAExecutorConfig(id='2tqxuywFsCfiEFCuKbwhPggaVu5BcXRrhCFjVasYU6kG', type='dca_executor', timestamp=1704508140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.882623199999999856637172835'), Decimal('2.876857953599999856923898489'), Decimal('2.859562214399999857784075452'), Decimal('2.853796967999999858070801107'), Decimal('2.839383851999999858787615242'), Decimal('2.824970735999999859504429378')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8932, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8826232}, controller_id=None),\n ExecutorInfo(id='3Fe6d3wnCU6eoqMrVvFnwrDaVRxm5uLxoBQbG1tMPygN', timestamp=1704508320000.0, type='dca_executor', close_timestamp=1704508440000.0, close_type=, status=, config=DCAExecutorConfig(id='3Fe6d3wnCU6eoqMrVvFnwrDaVRxm5uLxoBQbG1tMPygN', type='dca_executor', timestamp=1704508320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.888910599999999828981166183'), Decimal('2.883132778799999829323203851'), Decimal('2.865799315199999830349316854'), Decimal('2.860021493999999830691354521'), Decimal('2.845576940999999831546448690'), Decimal('2.831132387999999832401542859')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8947, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8889106}, controller_id=None),\n ExecutorInfo(id='HoWXibemEGgsRNVZisx8e6d1JEDUXAV911sf3CemmHuX', timestamp=1704508500000.0, type='dca_executor', close_timestamp=1704508620000.0, close_type=, status=, config=DCAExecutorConfig(id='HoWXibemEGgsRNVZisx8e6d1JEDUXAV911sf3CemmHuX', type='dca_executor', timestamp=1704508500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.888910599999999828981166183'), Decimal('2.883132778799999829323203851'), Decimal('2.865799315199999830349316854'), Decimal('2.860021493999999830691354521'), Decimal('2.845576940999999831546448690'), Decimal('2.831132387999999832401542859')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.901, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8889106}, controller_id=None),\n ExecutorInfo(id='AgwR8BW9EWNA75aLQiNCemWxcX6o2WXjhvHz2gvSKyDn', timestamp=1704508680000.0, type='dca_executor', close_timestamp=1704508800000.0, close_type=, status=, config=DCAExecutorConfig(id='AgwR8BW9EWNA75aLQiNCemWxcX6o2WXjhvHz2gvSKyDn', type='dca_executor', timestamp=1704508680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.892503399999999876492166873'), Decimal('2.886718393199999876739182539'), Decimal('2.869363372799999877480229538'), Decimal('2.863578365999999877727245204'), Decimal('2.849115848999999878344784370'), Decimal('2.834653331999999878962323536')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8951, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8925034}, controller_id=None),\n ExecutorInfo(id='49SjdbWJUSJ18c4Ng6xu2SWrRGMS5nLzhwAmfT7rhQQo', timestamp=1704508860000.0, type='dca_executor', close_timestamp=1704508980000.0, close_type=, status=, config=DCAExecutorConfig(id='49SjdbWJUSJ18c4Ng6xu2SWrRGMS5nLzhwAmfT7rhQQo', type='dca_executor', timestamp=1704508860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.875337799999999994207076914'), Decimal('2.869587124399999994218662760'), Decimal('2.852335097599999994253420299'), Decimal('2.846584421999999994265006145'), Decimal('2.832207732999999994293970760'), Decimal('2.817831043999999994322935376')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8831, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8753378}, controller_id=None),\n ExecutorInfo(id='81uzWMXDdYMgQabsFp4WNAPJ9Tc29QKhUwq6C2pYXFVm', timestamp=1704509040000.0, type='dca_executor', close_timestamp=1704509160000.0, close_type=, status=, config=DCAExecutorConfig(id='81uzWMXDdYMgQabsFp4WNAPJ9Tc29QKhUwq6C2pYXFVm', type='dca_executor', timestamp=1704509040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.877333800000000217580313489'), Decimal('2.871579132400000217145152862'), Decimal('2.854315129600000215839670981'), Decimal('2.848560462000000215404510354'), Decimal('2.834173793000000214316608787'), Decimal('2.819787124000000213228707219')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8921, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8773338}, controller_id=None),\n ExecutorInfo(id='FxZB9vq81VqhVrrCMgjH2p3MRwRkm283fsAWsZ6BuTjb', timestamp=1704509220000.0, type='dca_executor', close_timestamp=1704509340000.0, close_type=, status=, config=DCAExecutorConfig(id='FxZB9vq81VqhVrrCMgjH2p3MRwRkm283fsAWsZ6BuTjb', type='dca_executor', timestamp=1704509220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.878032399999999919040069574'), Decimal('2.872276335199999919201989435'), Decimal('2.855008140799999919687749017'), Decimal('2.849252075999999919849668878'), Decimal('2.834861913999999920254468530'), Decimal('2.820471751999999920659268183')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8821, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8780324}, controller_id=None),\n ExecutorInfo(id='FqtfgYCmg5x4inYLmCHbkSZqQ346Ery5H2hFFV6sc8wQ', timestamp=1704509400000.0, type='dca_executor', close_timestamp=1704509520000.0, close_type=, status=, config=DCAExecutorConfig(id='FqtfgYCmg5x4inYLmCHbkSZqQ346Ery5H2hFFV6sc8wQ', type='dca_executor', timestamp=1704509400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.876435600000000094902305459'), Decimal('2.870682728800000094712500848'), Decimal('2.853424115200000094143087015'), Decimal('2.847671244000000093953282404'), Decimal('2.833289066000000093478770877'), Decimal('2.818906888000000093004259350')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8822, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8764356}, controller_id=None),\n ExecutorInfo(id='Hr668z3eYaHgvdA6FmQLJ5V2oZXzTU6tU6nTmR5TMJQv', timestamp=1704509580000.0, type='dca_executor', close_timestamp=1704509700000.0, close_type=, status=, config=DCAExecutorConfig(id='Hr668z3eYaHgvdA6FmQLJ5V2oZXzTU6tU6nTmR5TMJQv', type='dca_executor', timestamp=1704509580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.876435600000000094902305459'), Decimal('2.870682728800000094712500848'), Decimal('2.853424115200000094143087015'), Decimal('2.847671244000000093953282404'), Decimal('2.833289066000000093478770877'), Decimal('2.818906888000000093004259350')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8857, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8764356}, controller_id=None),\n ExecutorInfo(id='G6L3bjAnFMmYQ6M418oHp9dqXrskNVKdEiua6mmpHTZQ', timestamp=1704509760000.0, type='dca_executor', close_timestamp=1704509880000.0, close_type=, status=, config=DCAExecutorConfig(id='G6L3bjAnFMmYQ6M418oHp9dqXrskNVKdEiua6mmpHTZQ', type='dca_executor', timestamp=1704509760000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.874639199999999849546289399'), Decimal('2.868889921599999849847196820'), Decimal('2.851642086399999850749919084'), Decimal('2.845892807999999851050826505'), Decimal('2.831519611999999851803095058'), Decimal('2.817146415999999852555363611')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8833, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8746392}, controller_id=None),\n ExecutorInfo(id='7jfGV8kfxiS5FM3HG1FgzaF68mEG8mkHjQk2NvuytsY9', timestamp=1704509940000.0, type='dca_executor', close_timestamp=1704510060000.0, close_type=, status=, config=DCAExecutorConfig(id='7jfGV8kfxiS5FM3HG1FgzaF68mEG8mkHjQk2NvuytsY9', type='dca_executor', timestamp=1704509940000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.872244000000000113339643226'), Decimal('2.866499512000000113112963940'), Decimal('2.849266048000000112432926080'), Decimal('2.843521560000000112206246794'), Decimal('2.829160340000000111639548578'), Decimal('2.814799120000000111072850361')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8799, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8722440000000002}, controller_id=None),\n ExecutorInfo(id='CQjaXT8ekhH5Lcx4XutaGs8nT2hiiyfXsZqeHu8ErSeg', timestamp=1704510120000.0, type='dca_executor', close_timestamp=1704510240000.0, close_type=, status=, config=DCAExecutorConfig(id='CQjaXT8ekhH5Lcx4XutaGs8nT2hiiyfXsZqeHu8ErSeg', type='dca_executor', timestamp=1704510120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.875437600000000204816202887'), Decimal('2.869686724800000204406570481'), Decimal('2.852434099200000203177673264'), Decimal('2.846683224000000202768040858'), Decimal('2.832306036000000201743959844'), Decimal('2.817928848000000200719878829')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8761, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8754376}, controller_id=None),\n ExecutorInfo(id='Co38LJy4QbZ9gHwLzy2HieuWs8EoHaLX14vwzcPJkca6', timestamp=1704510300000.0, type='dca_executor', close_timestamp=1704510420000.0, close_type=, status=, config=DCAExecutorConfig(id='Co38LJy4QbZ9gHwLzy2HieuWs8EoHaLX14vwzcPJkca6', type='dca_executor', timestamp=1704510300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.870347800000000100575532625'), Decimal('2.864607104400000100374381560'), Decimal('2.847385017600000099770928364'), Decimal('2.841644322000000099569777299'), Decimal('2.827292583000000099066899636'), Decimal('2.812940844000000098564021972')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.886, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8703478000000002}, controller_id=None),\n ExecutorInfo(id='H1w4UgkSprGQyJcAN8uZGrXSPE5ENVgrmukfVeww9hof', timestamp=1704510480000.0, type='dca_executor', close_timestamp=1704510600000.0, close_type=, status=, config=DCAExecutorConfig(id='H1w4UgkSprGQyJcAN8uZGrXSPE5ENVgrmukfVeww9hof', type='dca_executor', timestamp=1704510480000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.880228000000000120430526662'), Decimal('2.874467544000000120189665609'), Decimal('2.857186176000000119467082449'), Decimal('2.851425720000000119226221395'), Decimal('2.837024580000000118624068762'), Decimal('2.822623440000000118021916129')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.881, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8802280000000002}, controller_id=None),\n ExecutorInfo(id='8WCztfAYL7DvhJvbbK6JvgPPcaPJVm6npwq3dpoyc2Sj', timestamp=1704510660000.0, type='dca_executor', close_timestamp=1704510780000.0, close_type=, status=, config=DCAExecutorConfig(id='8WCztfAYL7DvhJvbbK6JvgPPcaPJVm6npwq3dpoyc2Sj', type='dca_executor', timestamp=1704510660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.875237999999999783597950942'), Decimal('2.869487523999999784030755040'), Decimal('2.852236095999999785329167334'), Decimal('2.846485619999999785761971433'), Decimal('2.832109429999999786843981678'), Decimal('2.817733239999999787925991923')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8809, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.875238}, controller_id=None),\n ExecutorInfo(id='9WP5uBD17D5HusaERkEcLuN49FmqhqEeStdYYuumJ7Sp', timestamp=1704510840000.0, type='dca_executor', close_timestamp=1704510960000.0, close_type=, status=, config=DCAExecutorConfig(id='9WP5uBD17D5HusaERkEcLuN49FmqhqEeStdYYuumJ7Sp', type='dca_executor', timestamp=1704510840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.873042400000000025408525284'), Decimal('2.867296315200000025357708233'), Decimal('2.850058060800000025205257082'), Decimal('2.844311976000000025154440031'), Decimal('2.829946764000000025027397405'), Decimal('2.815581552000000024900354778')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8788, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8730424}, controller_id=None),\n ExecutorInfo(id='E6vrH1Ue6wqaAwduKo57TWNh3BERRe5ayuvcnprAmbZe', timestamp=1704511020000.0, type='dca_executor', close_timestamp=1704511140000.0, close_type=, status=, config=DCAExecutorConfig(id='E6vrH1Ue6wqaAwduKo57TWNh3BERRe5ayuvcnprAmbZe', type='dca_executor', timestamp=1704511020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.877832799999999941022849059'), Decimal('2.872077134399999941140803361'), Decimal('2.854810137599999941494666267'), Decimal('2.849054471999999941612620568'), Decimal('2.834665307999999941907506323'), Decimal('2.820276143999999942202392078')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8882, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8778327999999997}, controller_id=None),\n ExecutorInfo(id='DzrbuxQbKhATe5THNeHRdGqHecnAy8ufG1Khm7g5EKZi', timestamp=1704511200000.0, type='dca_executor', close_timestamp=1704511320000.0, close_type=, status=, config=DCAExecutorConfig(id='DzrbuxQbKhATe5THNeHRdGqHecnAy8ufG1Khm7g5EKZi', type='dca_executor', timestamp=1704511200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.883521399999999979315180865'), Decimal('2.877754357199999979356550503'), Decimal('2.860453228799999979480659418'), Decimal('2.854686185999999979522029056'), Decimal('2.840268578999999979625453152'), Decimal('2.825850971999999979728877248')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8893, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8835214}, controller_id=None),\n ExecutorInfo(id='AACtC1s99b4jubwNp7XL3NuEh3buYkFCjuaHnqrg989h', timestamp=1704511380000.0, type='dca_executor', close_timestamp=1704511500000.0, close_type=, status=, config=DCAExecutorConfig(id='AACtC1s99b4jubwNp7XL3NuEh3buYkFCjuaHnqrg989h', type='dca_executor', timestamp=1704511380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.883521399999999979315180865'), Decimal('2.877754357199999979356550503'), Decimal('2.860453228799999979480659418'), Decimal('2.854686185999999979522029056'), Decimal('2.840268578999999979625453152'), Decimal('2.825850971999999979728877248')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.891, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8835214}, controller_id=None),\n ExecutorInfo(id='9fbsMxc2inLGBrjs6VuSZKX1g68b5TwPbGaUmTzNQH5P', timestamp=1704511560000.0, type='dca_executor', close_timestamp=1704511680000.0, close_type=, status=, config=DCAExecutorConfig(id='9fbsMxc2inLGBrjs6VuSZKX1g68b5TwPbGaUmTzNQH5P', type='dca_executor', timestamp=1704511560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571425'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.885218000000000014062070952'), Decimal('2.879447564000000014033946810'), Decimal('2.862136256000000013949574384'), Decimal('2.856365820000000013921450242'), Decimal('2.841939730000000013851139888'), Decimal('2.827513640000000013780829533')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8892, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.885218}, controller_id=None),\n ExecutorInfo(id='6nb8wkx9efmhy98c4QcN9VrA3b6NrWtmXQScP6HH83PV', timestamp=1704511740000.0, type='dca_executor', close_timestamp=1704511860000.0, close_type=, status=, config=DCAExecutorConfig(id='6nb8wkx9efmhy98c4QcN9VrA3b6NrWtmXQScP6HH83PV', type='dca_executor', timestamp=1704511740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.883421600000000211907086323'), Decimal('2.877654756800000211483272150'), Decimal('2.860354227200000210211829632'), Decimal('2.854587384000000209788015460'), Decimal('2.840170276000000208728480028'), Decimal('2.825753168000000207668944597')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8838, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8834216}, controller_id=None),\n ExecutorInfo(id='H6kJWSbZdTWbgNNXX55xJuEhixxw6akt1j2KFY9oWiHa', timestamp=1704511920000.0, type='dca_executor', close_timestamp=1704512040000.0, close_type=, status=, config=DCAExecutorConfig(id='H6kJWSbZdTWbgNNXX55xJuEhixxw6akt1j2KFY9oWiHa', type='dca_executor', timestamp=1704511920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.881226000000000010516629234'), Decimal('2.875463548000000010495595976'), Decimal('2.858176192000000010432496200'), Decimal('2.852413740000000010411462942'), Decimal('2.838007610000000010358879795'), Decimal('2.823601480000000010306296649')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8892, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.881226}, controller_id=None),\n ExecutorInfo(id='6A9ogcXJN7UsDq2S68Qx3zRBycHM5PVLnxCYWYaGWjjx', timestamp=1704512100000.0, type='dca_executor', close_timestamp=1704512220000.0, close_type=, status=, config=DCAExecutorConfig(id='6A9ogcXJN7UsDq2S68Qx3zRBycHM5PVLnxCYWYaGWjjx', type='dca_executor', timestamp=1704512100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.885417599999999992079291467'), Decimal('2.879646764799999992095132884'), Decimal('2.862334259199999992142657135'), Decimal('2.856563423999999992158498552'), Decimal('2.842136335999999992198102095'), Decimal('2.827709247999999992237705638')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.892, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8854176}, controller_id=None),\n ExecutorInfo(id='Fs2o2nMWxsvHdgQ9jAsvzahFCXYM69BhKEXHG1xo7aPS', timestamp=1704512280000.0, type='dca_executor', close_timestamp=1704512400000.0, close_type=, status=, config=DCAExecutorConfig(id='Fs2o2nMWxsvHdgQ9jAsvzahFCXYM69BhKEXHG1xo7aPS', type='dca_executor', timestamp=1704512280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000001')], prices=[Decimal('2.894998399999999823307939018'), Decimal('2.889208403199999823661323140'), Decimal('2.871838412799999824721475506'), Decimal('2.866048415999999825074859628'), Decimal('2.851573423999999825958319933'), Decimal('2.837098431999999826841780238')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8997, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8949984}, controller_id=None),\n ExecutorInfo(id='7nZu1mvTnSDCDoffZAfgnPVT8dsphaaPovjgb3kn4uZd', timestamp=1704512460000.0, type='dca_executor', close_timestamp=1704512580000.0, close_type=, status=, config=DCAExecutorConfig(id='7nZu1mvTnSDCDoffZAfgnPVT8dsphaaPovjgb3kn4uZd', type='dca_executor', timestamp=1704512460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.885118199999999803452944980'), Decimal('2.879347963599999803846039090'), Decimal('2.862037254399999805025321420'), Decimal('2.856267017999999805418415530'), Decimal('2.841841426999999806401150805'), Decimal('2.827415835999999807383886080')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8865, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8851182}, controller_id=None),\n ExecutorInfo(id='DJF2PePDjCRGsrhuQepZcr4uHBXe8CLimhtgo7AbyNEd', timestamp=1704512640000.0, type='dca_executor', close_timestamp=1704512760000.0, close_type=, status=, config=DCAExecutorConfig(id='DJF2PePDjCRGsrhuQepZcr4uHBXe8CLimhtgo7AbyNEd', type='dca_executor', timestamp=1704512640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.879429600000000208361644605'), Decimal('2.873670740800000207944921316'), Decimal('2.856394163200000206694751448'), Decimal('2.850635304000000206278028159'), Decimal('2.836238156000000205236219936'), Decimal('2.821841008000000204194411713')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.884, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8794296000000004}, controller_id=None),\n ExecutorInfo(id='552gmKn12YHA96aUHN8Bt1rw84SnYdBupRjpHjja9zWB', timestamp=1704474300000.0, type='dca_executor', close_timestamp=1704516000000.0, close_type=, status=, config=DCAExecutorConfig(id='552gmKn12YHA96aUHN8Bt1rw84SnYdBupRjpHjja9zWB', type='dca_executor', timestamp=1704474300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.904798000000000021479609756'), Decimal('2.910607596000000021522568976'), Decimal('2.928036384000000021651446634'), Decimal('2.933845980000000021694405854'), Decimal('2.948369970000000021801803902'), Decimal('2.962893960000000021909201951')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0333493407592587443044607198316953144967555999755859375'), net_pnl_quote=Decimal('16.883500775336159449580009095370769500732421875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.8453, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.9412906946221136}, controller_id=None),\n ExecutorInfo(id='9UEiLyWHCcKvxaYnmdJ3TcJUEMyqvNCtJE7R8gmxostL', timestamp=1704516000000.0, type='dca_executor', close_timestamp=1704516120000.0, close_type=, status=, config=DCAExecutorConfig(id='9UEiLyWHCcKvxaYnmdJ3TcJUEMyqvNCtJE7R8gmxostL', type='dca_executor', timestamp=1704516000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.850990599999999940313498424'), Decimal('2.856692581199999940194125421'), Decimal('2.873798524799999939836006411'), Decimal('2.879500505999999939716633408'), Decimal('2.893755458999999939418200900'), Decimal('2.908010411999999939119768392')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8481, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8509906}, controller_id=None),\n ExecutorInfo(id='DgVWvLuCCJAVNsUopnbsw25Xh4aiEh4abWEfSMy4SQfo', timestamp=1704516180000.0, type='dca_executor', close_timestamp=1704516300000.0, close_type=, status=, config=DCAExecutorConfig(id='DgVWvLuCCJAVNsUopnbsw25Xh4aiEh4abWEfSMy4SQfo', type='dca_executor', timestamp=1704516180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.853796200000000076298704853'), Decimal('2.859503792400000076451302263'), Decimal('2.876626569600000076909094492'), Decimal('2.882334162000000077061691902'), Decimal('2.896603143000000077443185426'), Decimal('2.910872124000000077824678950')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8266, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8537962}, controller_id=None),\n ExecutorInfo(id='CccHSJj3AiLMBRrgem2KoCvVkaPTLyujUZHzvULJjyoM', timestamp=1704516360000.0, type='dca_executor', close_timestamp=1704516480000.0, close_type=, status=, config=DCAExecutorConfig(id='CccHSJj3AiLMBRrgem2KoCvVkaPTLyujUZHzvULJjyoM', type='dca_executor', timestamp=1704516360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.832854399999999935328998112'), Decimal('2.838520108799999935199656108'), Decimal('2.855517235199999934811630097'), Decimal('2.861182943999999934682288093'), Decimal('2.875347215999999934358933084'), Decimal('2.889511487999999934035578074')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8239, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8328544}, controller_id=None),\n ExecutorInfo(id='9q1vAc4dwtuLk2n3Q7CFAVH8ARXkg5ts9w8qYJheoBMi', timestamp=1704512820000.0, type='dca_executor', close_timestamp=1704517680000.0, close_type=, status=, config=DCAExecutorConfig(id='9q1vAc4dwtuLk2n3Q7CFAVH8ARXkg5ts9w8qYJheoBMi', type='dca_executor', timestamp=1704512820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.872244000000000113339643226'), Decimal('2.866499512000000113112963940'), Decimal('2.849266048000000112432926080'), Decimal('2.843521560000000112206246794'), Decimal('2.829160340000000111639548578'), Decimal('2.814799120000000111072850361')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0129355617171167218415472888182193855755031108856201171875'), net_pnl_quote=Decimal('-6.3867796030440349142054401454515755176544189453125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.7926, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8363876965086563}, controller_id=None),\n ExecutorInfo(id='Qocc8fAshdXs5umDMqePYimF8d69dUB7LiY9fhGBmYY', timestamp=1704516540000.0, type='dca_executor', close_timestamp=1704523800000.0, close_type=, status=, config=DCAExecutorConfig(id='Qocc8fAshdXs5umDMqePYimF8d69dUB7LiY9fhGBmYY', type='dca_executor', timestamp=1704516540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.829547800000000077009661147'), Decimal('2.835206895600000077163680469'), Decimal('2.852184182400000077625738436'), Decimal('2.857843278000000077779757758'), Decimal('2.871991017000000078164806064'), Decimal('2.886138756000000078549854370')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.033876152460347253025929603609256446361541748046875'), net_pnl_quote=Decimal('0.8065750585796964688967136680730618536472320556640625'), cum_fees_quote=Decimal('0.01428571428571428526821396332024960429407656192779541015625'), filled_amount_quote=Decimal('23.8095238095238102005168912000954151153564453125'), is_active=False, is_trading=False, custom_info={'close_price': 2.735, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8295478000000003}, controller_id=None),\n ExecutorInfo(id='EEm3CjMa7AZ35UWr7LGg8dQbHQBVxPavEGM6xj293xQ1', timestamp=1704523800000.0, type='dca_executor', close_timestamp=1704523920000.0, close_type=, status=, config=DCAExecutorConfig(id='EEm3CjMa7AZ35UWr7LGg8dQbHQBVxPavEGM6xj293xQ1', type='dca_executor', timestamp=1704523800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.740469999999999875520198532'), Decimal('2.745950939999999875271238929'), Decimal('2.762393759999999874524360120'), Decimal('2.767874699999999874275400517'), Decimal('2.781577049999999873653001510'), Decimal('2.795279399999999873030602503')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.739, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7404699999999997}, controller_id=None),\n ExecutorInfo(id='5ik72Z7zVMVvAhGbMz7DFRTRWonPG5UgPe4EighSXuBM', timestamp=1704521280000.0, type='dca_executor', close_timestamp=1704524100000.0, close_type=, status=, config=DCAExecutorConfig(id='5ik72Z7zVMVvAhGbMz7DFRTRWonPG5UgPe4EighSXuBM', type='dca_executor', timestamp=1704521280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.785617599999999903443248517'), Decimal('2.780046364799999903636362020'), Decimal('2.763332659199999904215702529'), Decimal('2.757761423999999904408816032'), Decimal('2.743833335999999904891599789'), Decimal('2.729905247999999905374383547')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01225597852579453882204685299939228571020066738128662109375'), net_pnl_quote=Decimal('-6.051243492604793772215998615138232707977294921875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.7072, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7508427166417513}, controller_id=None),\n ExecutorInfo(id='FhLrAeRpUbMyRb4ZApHHF2EFiHvcuZro3pHDheDudKKW', timestamp=1704523980000.0, type='dca_executor', close_timestamp=1704524100000.0, close_type=, status=, config=DCAExecutorConfig(id='FhLrAeRpUbMyRb4ZApHHF2EFiHvcuZro3pHDheDudKKW', type='dca_executor', timestamp=1704523980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.754798599999999854873843074'), Decimal('2.760308197199999854583590760'), Decimal('2.776836988799999853712833819'), Decimal('2.782346585999999853422581505'), Decimal('2.796120578999999852696950720'), Decimal('2.809894571999999851971319935')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7072, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7547986}, controller_id=None),\n ExecutorInfo(id='2bMcEvTC4RqovEkKpyyz18grzpYQUq4URLdAKMhUBJFs', timestamp=1704527700000.0, type='dca_executor', close_timestamp=1704527820000.0, close_type=, status=, config=DCAExecutorConfig(id='2bMcEvTC4RqovEkKpyyz18grzpYQUq4URLdAKMhUBJFs', type='dca_executor', timestamp=1704527700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.758072799999999834659597520'), Decimal('2.752556654399999834990278325'), Decimal('2.736008217599999835982320740'), Decimal('2.730492071999999836313001545'), Decimal('2.716701707999999837139703557'), Decimal('2.702911343999999837966405570')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7659, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7580728}, controller_id=None),\n ExecutorInfo(id='95DqAZJ5gjpNrRDuM1H8Nm39B96FjQ4oWy4heqMfonmK', timestamp=1704527880000.0, type='dca_executor', close_timestamp=1704528000000.0, close_type=, status=, config=DCAExecutorConfig(id='95DqAZJ5gjpNrRDuM1H8Nm39B96FjQ4oWy4heqMfonmK', type='dca_executor', timestamp=1704527880000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714284'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.764959000000000184256283842'), Decimal('2.759429082000000183887771274'), Decimal('2.742839328000000182782233571'), Decimal('2.737309410000000182413721004'), Decimal('2.723484615000000181492439584'), Decimal('2.709659820000000180571158165')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7684, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.764959}, controller_id=None),\n ExecutorInfo(id='344Zi9rRFHVBa17Y8bBXKnoGhy4SfUNxyXMmc5rDLwAK', timestamp=1704528060000.0, type='dca_executor', close_timestamp=1704528180000.0, close_type=, status=, config=DCAExecutorConfig(id='344Zi9rRFHVBa17Y8bBXKnoGhy4SfUNxyXMmc5rDLwAK', type='dca_executor', timestamp=1704528060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.758671600000000211912290493'), Decimal('2.753154256800000211488465912'), Decimal('2.736602227200000210216992169'), Decimal('2.731084884000000209793167588'), Decimal('2.717291526000000208733606136'), Decimal('2.703498168000000207674044683')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7643, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7586716}, controller_id=None),\n ExecutorInfo(id='3CzrxuCabXr4Nn6ujEiyT9JrJaWnw3xDxptX5RUdr3TH', timestamp=1704524160000.0, type='dca_executor', close_timestamp=1704528720000.0, close_type=, status=, config=DCAExecutorConfig(id='3CzrxuCabXr4Nn6ujEiyT9JrJaWnw3xDxptX5RUdr3TH', type='dca_executor', timestamp=1704524160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.700389999999999839920342136'), Decimal('2.705790779999999839600182820'), Decimal('2.721993119999999838639704873'), Decimal('2.727393899999999838319545557'), Decimal('2.740895849999999837519147268'), Decimal('2.754397799999999836718748979')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.014648871266261333234925956503502675332129001617431640625'), net_pnl_quote=Decimal('-7.4161654698693979526069597341120243072509765625'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.7775, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.73431473680807}, controller_id=None),\n ExecutorInfo(id='7jBiQ1WUjFfPKPsciKJfErDWbZ2GZdCBAmrbGw2uYU98', timestamp=1704532320000.0, type='dca_executor', close_timestamp=1704532440000.0, close_type=, status=, config=DCAExecutorConfig(id='7jBiQ1WUjFfPKPsciKJfErDWbZ2GZdCBAmrbGw2uYU98', type='dca_executor', timestamp=1704532320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.790970800000000098366972899'), Decimal('2.796552741600000098563706845'), Decimal('2.813298566400000099153908682'), Decimal('2.818880508000000099350642628'), Decimal('2.832835362000000099842477492'), Decimal('2.846790216000000100334312357')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.787, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7909708}, controller_id=None),\n ExecutorInfo(id='JbRU5cZHJdnsZ9FPr6cNutHXvodwESurvjE2Dab76HF', timestamp=1704532500000.0, type='dca_executor', close_timestamp=1704532620000.0, close_type=, status=, config=DCAExecutorConfig(id='JbRU5cZHJdnsZ9FPr6cNutHXvodwESurvjE2Dab76HF', type='dca_executor', timestamp=1704532500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.793776399999999789374791058'), Decimal('2.799363952799999788953540640'), Decimal('2.816126611199999787689789386'), Decimal('2.821714163999999787268538969'), Decimal('2.835683045999999786215412924'), Decimal('2.849651927999999785162286879')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7885, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7937763999999996}, controller_id=None),\n ExecutorInfo(id='BbG4MUCZ1FtJC6V4TGyn9uoaWuR1w2Q2chGk4VyihWy2', timestamp=1704532680000.0, type='dca_executor', close_timestamp=1704532800000.0, close_type=, status=, config=DCAExecutorConfig(id='BbG4MUCZ1FtJC6V4TGyn9uoaWuR1w2Q2chGk4VyihWy2', type='dca_executor', timestamp=1704532680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.795379600000000057785218276'), Decimal('2.800970359200000057900788713'), Decimal('2.817742636800000058247500022'), Decimal('2.823333396000000058363070459'), Decimal('2.837310294000000058651996550'), Decimal('2.851287192000000058940922642')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.791, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7953796}, controller_id=None),\n ExecutorInfo(id='ELxDBokXFhs19rQryCdho4cpJbPFSZuxVBbwhnDs1p3j', timestamp=1704532860000.0, type='dca_executor', close_timestamp=1704532980000.0, close_type=, status=, config=DCAExecutorConfig(id='ELxDBokXFhs19rQryCdho4cpJbPFSZuxVBbwhnDs1p3j', type='dca_executor', timestamp=1704532860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.794778400000000123997828670'), Decimal('2.800367956800000124245824327'), Decimal('2.817136627200000124989811299'), Decimal('2.822726184000000125237806957'), Decimal('2.836700076000000125857796100'), Decimal('2.850673968000000126477785243')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7767, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7947784}, controller_id=None),\n ExecutorInfo(id='8EPEdEuC8qMazf2Tn5PzWX6scb2x4n8CzwyUk9KgKeKF', timestamp=1704533040000.0, type='dca_executor', close_timestamp=1704533160000.0, close_type=, status=, config=DCAExecutorConfig(id='8EPEdEuC8qMazf2Tn5PzWX6scb2x4n8CzwyUk9KgKeKF', type='dca_executor', timestamp=1704533040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.784758399999999892609170436'), Decimal('2.790327916799999892394388777'), Decimal('2.807036467199999891750043799'), Decimal('2.812605983999999891535262140'), Decimal('2.826529775999999890998307993'), Decimal('2.840453567999999890461353845')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7801, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7847584}, controller_id=None),\n ExecutorInfo(id='CPwWsajKGDYgNo7TvgwNbRnZC1Ptidp1Tp4v8fkCPZYN', timestamp=1704533220000.0, type='dca_executor', close_timestamp=1704533340000.0, close_type=, status=, config=DCAExecutorConfig(id='CPwWsajKGDYgNo7TvgwNbRnZC1Ptidp1Tp4v8fkCPZYN', type='dca_executor', timestamp=1704533220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.788966799999999874098285945'), Decimal('2.794544733599999873846482517'), Decimal('2.811278534399999873091072233'), Decimal('2.816856467999999872839268804'), Decimal('2.830801301999999872209760234'), Decimal('2.844746135999999871580251664')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.775, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7889668}, controller_id=None),\n ExecutorInfo(id='E6E7g5SwWzpSuuDwrEuFHEEcqHccyCBsSwB1KC8985Xj', timestamp=1704528240000.0, type='dca_executor', close_timestamp=1704557820000.0, close_type=, status=, config=DCAExecutorConfig(id='E6E7g5SwWzpSuuDwrEuFHEEcqHccyCBsSwB1KC8985Xj', type='dca_executor', timestamp=1704528240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.764460000000000017612716841'), Decimal('2.758931080000000017577491407'), Decimal('2.742344320000000017471815106'), Decimal('2.736815400000000017436589673'), Decimal('2.722993100000000017348526088'), Decimal('2.709170800000000017260462504')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0315703837923945462851094134748564101755619049072265625'), net_pnl_quote=Decimal('4.48900790495667223467535222880542278289794921875'), cum_fees_quote=Decimal('0.08531428571428571749901692555795307271182537078857421875'), filled_amount_quote=Decimal('142.19047619047620401033782400190830230712890625'), is_active=False, is_trading=False, custom_info={'close_price': 2.8344, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.751591281312793}, controller_id=None),\n ExecutorInfo(id='9SUn9qeV4Jj7okT4ZfsqsC1AXQqEgpC6ey98WAXYbutk', timestamp=1704533400000.0, type='dca_executor', close_timestamp=1704576600000.0, close_type=, status=, config=DCAExecutorConfig(id='9SUn9qeV4Jj7okT4ZfsqsC1AXQqEgpC6ey98WAXYbutk', type='dca_executor', timestamp=1704533400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.781251400000000056360703603'), Decimal('2.786813902800000056473425010'), Decimal('2.803501411200000056811589232'), Decimal('2.809063914000000056924310639'), Decimal('2.822970171000000057206114157'), Decimal('2.836876428000000057487917675')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.00970968279059442572476168464845613925717771053314208984375'), net_pnl_quote=Decimal('4.9156425042002211966973845846951007843017578125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.7933, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8161919907080373}, controller_id=None),\n ExecutorInfo(id='2AcfUjg9VkoKYagogAfybdvTeq228nQjidNZdbm7N8ZX', timestamp=1704576600000.0, type='dca_executor', close_timestamp=1704576720000.0, close_type=, status=, config=DCAExecutorConfig(id='2AcfUjg9VkoKYagogAfybdvTeq228nQjidNZdbm7N8ZX', type='dca_executor', timestamp=1704576600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.798886599999999894033685109'), Decimal('2.804484373199999893821752479'), Decimal('2.821277692799999893185954590'), Decimal('2.826875465999999892974021960'), Decimal('2.840869898999999892444190386'), Decimal('2.854864331999999891914358811')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7901, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7988866}, controller_id=None),\n ExecutorInfo(id='AY8AD6swpaeRhMCdyJCmWZMRDngBdgwWd5cCXQoi3C3X', timestamp=1704557820000.0, type='dca_executor', close_timestamp=1704582240000.0, close_type=, status=, config=DCAExecutorConfig(id='AY8AD6swpaeRhMCdyJCmWZMRDngBdgwWd5cCXQoi3C3X', type='dca_executor', timestamp=1704557820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.828731200000000030374225357'), Decimal('2.823073737600000030313476906'), Decimal('2.806101350400000030131231554'), Decimal('2.800443888000000030070483103'), Decimal('2.786300232000000029918611977'), Decimal('2.772156576000000029766740850')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.015333232123732158658224733471797662787139415740966796875'), net_pnl_quote=Decimal('-7.5706008226150895978889820980839431285858154296875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.7484, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7934180983266623}, controller_id=None),\n ExecutorInfo(id='LSpVLPKG6mJMa7B78aAZfhs2BWY1M35vwPtUKJEXuRa', timestamp=1704585840000.0, type='dca_executor', close_timestamp=1704585960000.0, close_type=, status=, config=DCAExecutorConfig(id='LSpVLPKG6mJMa7B78aAZfhs2BWY1M35vwPtUKJEXuRa', type='dca_executor', timestamp=1704585840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.758272399999999812676818034'), Decimal('2.752755855199999813051464398'), Decimal('2.736206220799999814175403490'), Decimal('2.730689675999999814550049854'), Decimal('2.716898313999999815486665763'), Decimal('2.703106951999999816423281673')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7693, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7582723999999996}, controller_id=None),\n ExecutorInfo(id='JAkFoNjSMFq7Qq3wejkEKiyMjkk8kJwK547daDibhLrN', timestamp=1704586020000.0, type='dca_executor', close_timestamp=1704586140000.0, close_type=, status=, config=DCAExecutorConfig(id='JAkFoNjSMFq7Qq3wejkEKiyMjkk8kJwK547daDibhLrN', type='dca_executor', timestamp=1704586020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.767454000000000131072055987'), Decimal('2.761919092000000130809911875'), Decimal('2.745314368000000130023479539'), Decimal('2.739779460000000129761335427'), Decimal('2.725942190000000129105975147'), Decimal('2.712104920000000128450614867')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.768, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7674540000000003}, controller_id=None),\n ExecutorInfo(id='2RK1gTqMw9fq6VB6VkXcNPCk4yqaNJT7hEoL7nK3mini', timestamp=1704586200000.0, type='dca_executor', close_timestamp=1704586320000.0, close_type=, status=, config=DCAExecutorConfig(id='2RK1gTqMw9fq6VB6VkXcNPCk4yqaNJT7hEoL7nK3mini', type='dca_executor', timestamp=1704586200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.766256399999999819767701470'), Decimal('2.760723887199999820128166067'), Decimal('2.744126348799999821209559858'), Decimal('2.738593835999999821570024455'), Decimal('2.724762553999999822471185948'), Decimal('2.710931271999999823372347441')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7776, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7662563999999996}, controller_id=None),\n ExecutorInfo(id='6XhnMDfQue9GWKDWHs1K9seaxmPSrcCPzeiWjtrN2RRp', timestamp=1704586380000.0, type='dca_executor', close_timestamp=1704586500000.0, close_type=, status=, config=DCAExecutorConfig(id='6XhnMDfQue9GWKDWHs1K9seaxmPSrcCPzeiWjtrN2RRp', type='dca_executor', timestamp=1704586380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.770647599999999779347584217'), Decimal('2.765106304799999779788889049'), Decimal('2.748482419199999781112803543'), Decimal('2.742941123999999781554108375'), Decimal('2.729087885999999782657370454'), Decimal('2.715234647999999783760632533')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7791, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7706475999999998}, controller_id=None),\n ExecutorInfo(id='AKpKZWapdPxNpkwCbum99hGfY1x8GtpzkWKYEyF2xrdC', timestamp=1704586560000.0, type='dca_executor', close_timestamp=1704586680000.0, close_type=, status=, config=DCAExecutorConfig(id='AKpKZWapdPxNpkwCbum99hGfY1x8GtpzkWKYEyF2xrdC', type='dca_executor', timestamp=1704586560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.774140600000000059450490364'), Decimal('2.768592318800000059331589383'), Decimal('2.751947475200000058974886441'), Decimal('2.746399194000000058855985460'), Decimal('2.732528491000000058558733009'), Decimal('2.718657788000000058261480557')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7899, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7741406}, controller_id=None),\n ExecutorInfo(id='zjxVfyvhr4V97D7fARyydLtYuzQMpr569SDMoqP4DSH', timestamp=1704586740000.0, type='dca_executor', close_timestamp=1704586860000.0, close_type=, status=, config=DCAExecutorConfig(id='zjxVfyvhr4V97D7fARyydLtYuzQMpr569SDMoqP4DSH', type='dca_executor', timestamp=1704586740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714284'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.787114599999999960172918090'), Decimal('2.781540370799999960252572254'), Decimal('2.764817683199999960491534745'), Decimal('2.759243453999999960571188909'), Decimal('2.745307880999999960770324319'), Decimal('2.731372307999999960969459728')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.795, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7871145999999998}, controller_id=None),\n ExecutorInfo(id='5FzYpf96oyUCyewJCyWBezLAT5uahgdifpgUdXWbiaqJ', timestamp=1704586920000.0, type='dca_executor', close_timestamp=1704587040000.0, close_type=, status=, config=DCAExecutorConfig(id='5FzYpf96oyUCyewJCyWBezLAT5uahgdifpgUdXWbiaqJ', type='dca_executor', timestamp=1704586920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.789409999999999928971469720'), Decimal('2.783831179999999929113526781'), Decimal('2.767094719999999929539697962'), Decimal('2.761515899999999929681755023'), Decimal('2.747568849999999930036897674'), Decimal('2.733621799999999930392040326')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7952, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7894099999999997}, controller_id=None),\n ExecutorInfo(id='Kb7vb4TWncFnS2B2jkPzX3n57Fqn1niE9YkaxZ9Bvqy', timestamp=1704587100000.0, type='dca_executor', close_timestamp=1704587220000.0, close_type=, status=, config=DCAExecutorConfig(id='Kb7vb4TWncFnS2B2jkPzX3n57Fqn1niE9YkaxZ9Bvqy', type='dca_executor', timestamp=1704587100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.790507800000000029666698265'), Decimal('2.784926784400000029607364868'), Decimal('2.768183737600000029429364679'), Decimal('2.762602722000000029370031282'), Decimal('2.748650183000000029221697791'), Decimal('2.734697644000000029073364300')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7932, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7905078}, controller_id=None),\n ExecutorInfo(id='acyUA98nfJY9XH1Rf1fV8NW9PFi4x2nmFuNgm5vqPcN', timestamp=1704587280000.0, type='dca_executor', close_timestamp=1704587400000.0, close_type=, status=, config=DCAExecutorConfig(id='acyUA98nfJY9XH1Rf1fV8NW9PFi4x2nmFuNgm5vqPcN', type='dca_executor', timestamp=1704587280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.779729399999999887133696197'), Decimal('2.774169941199999887359428805'), Decimal('2.757491564799999888036626627'), Decimal('2.751932105999999888262359235'), Decimal('2.738033458999999888826690754'), Decimal('2.724134811999999889391022273')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7881, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7797294}, controller_id=None),\n ExecutorInfo(id='B5amAVm96gZ1oiNLpP2FNmrgbgxneqbFhJ3G4L1CsAD3', timestamp=1704587460000.0, type='dca_executor', close_timestamp=1704587580000.0, close_type=, status=, config=DCAExecutorConfig(id='B5amAVm96gZ1oiNLpP2FNmrgbgxneqbFhJ3G4L1CsAD3', type='dca_executor', timestamp=1704587460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.786615599999999793529351089'), Decimal('2.781042368799999793942292387'), Decimal('2.764322675199999795181116280'), Decimal('2.758749443999999795594057578'), Decimal('2.744816365999999796626410823'), Decimal('2.730883287999999797658764067')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7891, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7866155999999997}, controller_id=None),\n ExecutorInfo(id='6Ne6wtCHAADCszJSW54YoZfefVHuCVM4gFRyqJYhqptv', timestamp=1704587640000.0, type='dca_executor', close_timestamp=1704587760000.0, close_type=, status=, config=DCAExecutorConfig(id='6Ne6wtCHAADCszJSW54YoZfefVHuCVM4gFRyqJYhqptv', type='dca_executor', timestamp=1704587640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.786316200000000048104036032'), Decimal('2.780743567600000048007827960'), Decimal('2.764025670400000047719203744'), Decimal('2.758453038000000047622995672'), Decimal('2.744521457000000047382475492'), Decimal('2.730589876000000047141955311')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.794, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7863162}, controller_id=None),\n ExecutorInfo(id='CjC9dY2sXAr4D8pxLq2JMiPKKU9Ptdst7uXC7Zqm7d8z', timestamp=1704587820000.0, type='dca_executor', close_timestamp=1704587940000.0, close_type=, status=, config=DCAExecutorConfig(id='CjC9dY2sXAr4D8pxLq2JMiPKKU9Ptdst7uXC7Zqm7d8z', type='dca_executor', timestamp=1704587820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.793901000000000099160478440'), Decimal('2.788313198000000098962157483'), Decimal('2.771549792000000098367194612'), Decimal('2.765961990000000098168873656'), Decimal('2.751992485000000097673071263'), Decimal('2.738022980000000097177268871')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8089, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.793901}, controller_id=None),\n ExecutorInfo(id='CudGB7dW6yxgwWvHfjSFjczaDkXQggZD3F5SxryPw3VP', timestamp=1704576780000.0, type='dca_executor', close_timestamp=1704619920000.0, close_type=, status=, config=DCAExecutorConfig(id='CudGB7dW6yxgwWvHfjSFjczaDkXQggZD3F5SxryPw3VP', type='dca_executor', timestamp=1704576780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('71.99999999999999999999999998'), Decimal('96.19047619047619047619047616'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.794978800000000101926958539'), Decimal('2.800568757600000102130812456'), Decimal('2.817338630400000102742374207'), Decimal('2.822928588000000102946228124'), Decimal('2.836903482000000103455862917'), Decimal('2.850878376000000103965497710')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.03133838088979633285102721629300503991544246673583984375'), net_pnl_quote=Decimal('11.29897861462347208316714386455714702606201171875'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 2.736, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8216910305243346}, controller_id=None),\n ExecutorInfo(id='69Eky2AWhnnyEXfSHKCxJDT2qm3uqRq54gHZmP3ym53V', timestamp=1704588000000.0, type='dca_executor', close_timestamp=1704619980000.0, close_type=, status=, config=DCAExecutorConfig(id='69Eky2AWhnnyEXfSHKCxJDT2qm3uqRq54gHZmP3ym53V', type='dca_executor', timestamp=1704588000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.806176399999999855222118650'), Decimal('2.800564047199999855511674413'), Decimal('2.783726988799999856380341701'), Decimal('2.778114635999999856669897464'), Decimal('2.764083753999999857393786870'), Decimal('2.750052871999999858117676277')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01503634152403388046792542809271253645420074462890625'), net_pnl_quote=Decimal('-7.4240146234259665192212196416221559047698974609375'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.7262, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7711448662415004}, controller_id=None),\n ExecutorInfo(id='7YeGgdub7PvzgkMbDTx1nM7FNshVxwmadnJobGF49USu', timestamp=1704619920000.0, type='dca_executor', close_timestamp=1704620040000.0, close_type=, status=, config=DCAExecutorConfig(id='7YeGgdub7PvzgkMbDTx1nM7FNshVxwmadnJobGF49USu', type='dca_executor', timestamp=1704619920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.741472000000000210143236144'), Decimal('2.746954944000000210563522616'), Decimal('2.763403776000000211824382033'), Decimal('2.768886720000000212244668505'), Decimal('2.782594080000000213295384686'), Decimal('2.796301440000000214346100867')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7332, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7414720000000004}, controller_id=None),\n ExecutorInfo(id='5HahyNDnu1uEMVrFZgGQG3kXqTgtns181AoVbHvsGjh7', timestamp=1704623580000.0, type='dca_executor', close_timestamp=1704623700000.0, close_type=, status=, config=DCAExecutorConfig(id='5HahyNDnu1uEMVrFZgGQG3kXqTgtns181AoVbHvsGjh7', type='dca_executor', timestamp=1704623580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.744699599999999977902728765'), Decimal('2.739210200799999977946923307'), Decimal('2.722742003199999978079506935'), Decimal('2.717252603999999978123701477'), Decimal('2.703529105999999978234187834'), Decimal('2.689805607999999978344674190')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.75, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7446996}, controller_id=None),\n ExecutorInfo(id='4Q91agNcDPnTAyYB68iEEQARWCvb4wzEbkGfhvKM9dhs', timestamp=1704623760000.0, type='dca_executor', close_timestamp=1704623880000.0, close_type=, status=, config=DCAExecutorConfig(id='4Q91agNcDPnTAyYB68iEEQARWCvb4wzEbkGfhvKM9dhs', type='dca_executor', timestamp=1704623760000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.745198600000000144546295766'), Decimal('2.739708202800000144257203174'), Decimal('2.723237011200000143389925400'), Decimal('2.717746614000000143100832808'), Decimal('2.704020621000000142378101330'), Decimal('2.690294628000000141655369851')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7578, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7451986}, controller_id=None),\n ExecutorInfo(id='5menQYkrTsKJvEygMd4HNV6NXY1wvh1v2yKjhVkjFhgF', timestamp=1704623940000.0, type='dca_executor', close_timestamp=1704624060000.0, close_type=, status=, config=DCAExecutorConfig(id='5menQYkrTsKJvEygMd4HNV6NXY1wvh1v2yKjhVkjFhgF', type='dca_executor', timestamp=1704623940000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.751984999999999840332824685'), Decimal('2.746481029999999840652159036'), Decimal('2.729969119999999841610162088'), Decimal('2.724465149999999841929496438'), Decimal('2.710705224999999842727832315'), Decimal('2.696945299999999843526168191')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7558, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.751985}, controller_id=None),\n ExecutorInfo(id='8MPQ9j4KHxxgvy9MQixKKq4z5JSaXRvNMnag8zQspFNw', timestamp=1704624120000.0, type='dca_executor', close_timestamp=1704624240000.0, close_type=, status=, config=DCAExecutorConfig(id='8MPQ9j4KHxxgvy9MQixKKq4z5JSaXRvNMnag8zQspFNw', type='dca_executor', timestamp=1704624120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.751386199999999906281163142'), Decimal('2.745883427599999906468600816'), Decimal('2.729375110399999907030913837'), Decimal('2.723872337999999907218351511'), Decimal('2.710115406999999907686945695'), Decimal('2.696358475999999908155539879')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7562, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7513862}, controller_id=None),\n ExecutorInfo(id='DM4t52nqhbSVALXmp9ziKUFzecokD7opt754fhDXoV6D', timestamp=1704624300000.0, type='dca_executor', close_timestamp=1704624420000.0, close_type=, status=, config=DCAExecutorConfig(id='DM4t52nqhbSVALXmp9ziKUFzecokD7opt754fhDXoV6D', type='dca_executor', timestamp=1704624300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.748891199999999959465390997'), Decimal('2.743393417599999959546460215'), Decimal('2.726900070399999959789667869'), Decimal('2.721402287999999959870737087'), Decimal('2.707657831999999960073410132'), Decimal('2.693913375999999960276083177')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7516, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7488912}, controller_id=None),\n ExecutorInfo(id='3ywhuSNiN2td66QpHQkkCj1A9h7Cv7WgeEefW7XAeGau', timestamp=1704624480000.0, type='dca_executor', close_timestamp=1704624600000.0, close_type=, status=, config=DCAExecutorConfig(id='3ywhuSNiN2td66QpHQkkCj1A9h7Cv7WgeEefW7XAeGau', type='dca_executor', timestamp=1704624480000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.747194599999999924718500910'), Decimal('2.741700210799999924869063908'), Decimal('2.725217043199999925320752903'), Decimal('2.719722653999999925471315901'), Decimal('2.705986680999999925847723396'), Decimal('2.692250707999999926224130892')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7521, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7471946}, controller_id=None),\n ExecutorInfo(id='w2BYxQs3FhaQsUWjt58YHzFULZWWHs48kBPRdAHsr9t', timestamp=1704624660000.0, type='dca_executor', close_timestamp=1704624780000.0, close_type=, status=, config=DCAExecutorConfig(id='w2BYxQs3FhaQsUWjt58YHzFULZWWHs48kBPRdAHsr9t', type='dca_executor', timestamp=1704624660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.741905199999999842460610133'), Decimal('2.736421389599999842775688913'), Decimal('2.719969958399999843720925252'), Decimal('2.714486147999999844036004032'), Decimal('2.700776621999999844823700981'), Decimal('2.687067095999999845611397930')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7546, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7419051999999997}, controller_id=None),\n ExecutorInfo(id='2g8yPDTEcuaz9SVM7qPWmBVV3djdTG2WsBMsgSrZSuCi', timestamp=1704624840000.0, type='dca_executor', close_timestamp=1704624960000.0, close_type=, status=, config=DCAExecutorConfig(id='2g8yPDTEcuaz9SVM7qPWmBVV3djdTG2WsBMsgSrZSuCi', type='dca_executor', timestamp=1704624840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.750288399999999805585934598'), Decimal('2.744787823199999805974762729'), Decimal('2.728286092799999807141247121'), Decimal('2.722785515999999807530075252'), Decimal('2.709034073999999808502145579'), Decimal('2.695282631999999809474215906')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7505, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7502883999999996}, controller_id=None),\n ExecutorInfo(id='5hrTZBxS1tUCat4xZVTMBfYmpDuh58RZhhQ18eRPXEEP', timestamp=1704625020000.0, type='dca_executor', close_timestamp=1704625140000.0, close_type=, status=, config=DCAExecutorConfig(id='5hrTZBxS1tUCat4xZVTMBfYmpDuh58RZhhQ18eRPXEEP', type='dca_executor', timestamp=1704625020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.744999000000000166529075252'), Decimal('2.739509002000000166196017101'), Decimal('2.723039008000000165196842650'), Decimal('2.717549010000000164863784499'), Decimal('2.703824015000000164031139123'), Decimal('2.690099020000000163198493747')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7528, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.744999}, controller_id=None),\n ExecutorInfo(id='DURbNwaRxyCteABi7H9xq1bArCNWrs64fwfzWjz88DtQ', timestamp=1704625200000.0, type='dca_executor', close_timestamp=1704625320000.0, close_type=, status=, config=DCAExecutorConfig(id='DURbNwaRxyCteABi7H9xq1bArCNWrs64fwfzWjz88DtQ', type='dca_executor', timestamp=1704625200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571426'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.748991000000000170074516970'), Decimal('2.743493018000000169734367936'), Decimal('2.726999072000000168713920834'), Decimal('2.721501090000000168373771800'), Decimal('2.707756135000000167523399215'), Decimal('2.694011180000000166673026631')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7503, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.748991}, controller_id=None),\n ExecutorInfo(id='5jeGzN6ipVpBHPCDvTxK1pdot5UwodLCuoZHNCtFa8Qg', timestamp=1704625380000.0, type='dca_executor', close_timestamp=1704625500000.0, close_type=, status=, config=DCAExecutorConfig(id='5jeGzN6ipVpBHPCDvTxK1pdot5UwodLCuoZHNCtFa8Qg', type='dca_executor', timestamp=1704625380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.744799400000000188511854737'), Decimal('2.739309801200000188134831028'), Decimal('2.722841004800000187003759899'), Decimal('2.717351406000000186626736190'), Decimal('2.703627409000000185684176916'), Decimal('2.689903412000000184741617642')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7593, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7447994000000002}, controller_id=None),\n ExecutorInfo(id='H7Gb5uop54hFVLSXKdgeHL5xTUoKpz2bycgurunEdR7E', timestamp=1704620100000.0, type='dca_executor', close_timestamp=1704646080000.0, close_type=, status=, config=DCAExecutorConfig(id='H7Gb5uop54hFVLSXKdgeHL5xTUoKpz2bycgurunEdR7E', type='dca_executor', timestamp=1704620100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.733656400000000180952394734'), Decimal('2.739123712800000181314299523'), Decimal('2.755525651200000182400013892'), Decimal('2.760992964000000182761918681'), Decimal('2.774661246000000183666680655'), Decimal('2.788329528000000184571442629')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.033392556783727440905540362336978432722389698028564453125'), net_pnl_quote=Decimal('16.90537940219991952517375466413795948028564453125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.678, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7679990593542776}, controller_id=None),\n ExecutorInfo(id='yBgTBRvyf8XmCDN2btnNnPC9DnpZVaLpTXU4AV3jjJc', timestamp=1704625560000.0, type='dca_executor', close_timestamp=1704646140000.0, close_type=, status=, config=DCAExecutorConfig(id='yBgTBRvyf8XmCDN2btnNnPC9DnpZVaLpTXU4AV3jjJc', type='dca_executor', timestamp=1704625560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.750388200000000016195060570'), Decimal('2.744887423600000016162670449'), Decimal('2.728385094400000016065500085'), Decimal('2.722884318000000016033109964'), Decimal('2.709132377000000015952134661'), Decimal('2.695380436000000015871159359')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0149259343217204264175901329281259677372872829437255859375'), net_pnl_quote=Decimal('-7.3695023816551543660580136929638683795928955078125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6731, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.71605311077422}, controller_id=None),\n ExecutorInfo(id='9vtJ6BymoC11Cgy98YfsvVcgqnr4p6nxFYGxotGtiWf3', timestamp=1704646080000.0, type='dca_executor', close_timestamp=1704646200000.0, close_type=, status=, config=DCAExecutorConfig(id='9vtJ6BymoC11Cgy98YfsvVcgqnr4p6nxFYGxotGtiWf3', type='dca_executor', timestamp=1704646080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.683355999999999936034750235'), Decimal('2.688722711999999935906819735'), Decimal('2.704822847999999935523028237'), Decimal('2.710189559999999935395097737'), Decimal('2.723606339999999935075271489'), Decimal('2.737023119999999934755445240')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6793, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.683356}, controller_id=None),\n ExecutorInfo(id='2T22vUS5B42yfaWJx1VZ4DgdWM9BVU6Ky7QTJHqAZKVa', timestamp=1704646260000.0, type='dca_executor', close_timestamp=1704646380000.0, close_type=, status=, config=DCAExecutorConfig(id='2T22vUS5B42yfaWJx1VZ4DgdWM9BVU6Ky7QTJHqAZKVa', type='dca_executor', timestamp=1704646260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.684458200000000037133658647'), Decimal('2.689827116400000037207925964'), Decimal('2.705933865600000037430727916'), Decimal('2.711302782000000037504995233'), Decimal('2.724725073000000037690663527'), Decimal('2.738147364000000037876331820')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6714, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6844582}, controller_id=None),\n ExecutorInfo(id='5jfmx94fUwpCW1dqxPyf1sQKqQbwzJuQCaqBwhJi4Nhp', timestamp=1704649740000.0, type='dca_executor', close_timestamp=1704649860000.0, close_type=, status=, config=DCAExecutorConfig(id='5jfmx94fUwpCW1dqxPyf1sQKqQbwzJuQCaqBwhJi4Nhp', type='dca_executor', timestamp=1704649740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.720148800000000022578416914'), Decimal('2.714708502400000022533260080'), Decimal('2.698387609600000022397789579'), Decimal('2.692947312000000022352632745'), Decimal('2.679346568000000022239740660'), Decimal('2.665745824000000022126848576')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7268, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7201488}, controller_id=None),\n ExecutorInfo(id='5ht6z6F8G5oesDxb84LEg6NrAs3UE6JcGVdeCFYKvqQQ', timestamp=1704649920000.0, type='dca_executor', close_timestamp=1704650040000.0, close_type=, status=, config=DCAExecutorConfig(id='5ht6z6F8G5oesDxb84LEg6NrAs3UE6JcGVdeCFYKvqQQ', type='dca_executor', timestamp=1704649920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.712763599999999949539195021'), Decimal('2.707338072799999949640116631'), Decimal('2.691061491199999949942881461'), Decimal('2.685635963999999950043803071'), Decimal('2.672072145999999950296107096'), Decimal('2.658508327999999950548411121')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7246, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7127636}, controller_id=None),\n ExecutorInfo(id='G4PB74DBa63jB9RRTpdjxdXk1rwWsMACcaPt9PYjRHHb', timestamp=1704650100000.0, type='dca_executor', close_timestamp=1704650220000.0, close_type=, status=, config=DCAExecutorConfig(id='G4PB74DBa63jB9RRTpdjxdXk1rwWsMACcaPt9PYjRHHb', type='dca_executor', timestamp=1704650100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.719150800000000132492314342'), Decimal('2.713712498400000132227329713'), Decimal('2.697397593600000131432375827'), Decimal('2.691959292000000131167391199'), Decimal('2.678363538000000130504929627'), Decimal('2.664767784000000129842468055')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7219, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7191508}, controller_id=None),\n ExecutorInfo(id='BXaRoVP8nuyqo9K7548BNYaTUKq4unUQSAxs5pFM6MXN', timestamp=1704650280000.0, type='dca_executor', close_timestamp=1704650400000.0, close_type=, status=, config=DCAExecutorConfig(id='BXaRoVP8nuyqo9K7548BNYaTUKq4unUQSAxs5pFM6MXN', type='dca_executor', timestamp=1704650280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.719949200000000044561196400'), Decimal('2.714509301600000044472074007'), Decimal('2.698189606400000044204706829'), Decimal('2.692749708000000044115584436'), Decimal('2.679149962000000043892778454'), Decimal('2.665550216000000043669972472')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.728, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7199492}, controller_id=None),\n ExecutorInfo(id='CkEhHkzv6JNTnV4zh7k2ieBdRUkdTAeNmXnY7fXDmYTq', timestamp=1704650460000.0, type='dca_executor', close_timestamp=1704650580000.0, close_type=, status=, config=DCAExecutorConfig(id='CkEhHkzv6JNTnV4zh7k2ieBdRUkdTAeNmXnY7fXDmYTq', type='dca_executor', timestamp=1704650460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.721945199999999824733401543'), Decimal('2.716501309599999825083934740'), Decimal('2.700169638399999826135534331'), Decimal('2.694725747999999826486067528'), Decimal('2.681116021999999827362400520'), Decimal('2.667506295999999828238733512')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.733, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7219452}, controller_id=None),\n ExecutorInfo(id='6cgHhF1RsS9wJhJwhDMncDVuuNT3JKdNun6ami4cTEQM', timestamp=1704650640000.0, type='dca_executor', close_timestamp=1704650760000.0, close_type=, status=, config=DCAExecutorConfig(id='6cgHhF1RsS9wJhJwhDMncDVuuNT3JKdNun6ami4cTEQM', type='dca_executor', timestamp=1704650640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.727733600000000073634859321'), Decimal('2.722278132800000073487589602'), Decimal('2.705911731200000073045780446'), Decimal('2.700456264000000072898510728'), Decimal('2.686817596000000072530336431'), Decimal('2.673178928000000072162162135')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7341, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7277336}, controller_id=None),\n ExecutorInfo(id='EdvoB3j52pqMLh1qemSckpemHbTYFVKUAdXoqndwtu6f', timestamp=1704650820000.0, type='dca_executor', close_timestamp=1704650940000.0, close_type=, status=, config=DCAExecutorConfig(id='EdvoB3j52pqMLh1qemSckpemHbTYFVKUAdXoqndwtu6f', type='dca_executor', timestamp=1704650820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.728631800000000196312867351'), Decimal('2.723174536400000195920241616'), Decimal('2.706802745600000194742364412'), Decimal('2.701345482000000194349738677'), Decimal('2.687702323000000193368174341'), Decimal('2.674059164000000192386610004')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7319, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7286318}, controller_id=None),\n ExecutorInfo(id='2cL7sFv79C4QejdqveYyoJN9yJfihx51nYv1cFfbnX2N', timestamp=1704651000000.0, type='dca_executor', close_timestamp=1704651120000.0, close_type=, status=, config=DCAExecutorConfig(id='2cL7sFv79C4QejdqveYyoJN9yJfihx51nYv1cFfbnX2N', type='dca_executor', timestamp=1704651000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.730827399999999954502293009'), Decimal('2.725365745199999954593288423'), Decimal('2.708980780799999954866274665'), Decimal('2.703519125999999954957270079'), Decimal('2.689864988999999955184758614'), Decimal('2.676210851999999955412247149')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7402, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7308274}, controller_id=None),\n ExecutorInfo(id='6T19Ujy2GiwJQxLqwWvyHDNxo5UVujSZUkmxjg7zXndN', timestamp=1704651180000.0, type='dca_executor', close_timestamp=1704651300000.0, close_type=, status=, config=DCAExecutorConfig(id='6T19Ujy2GiwJQxLqwWvyHDNxo5UVujSZUkmxjg7zXndN', type='dca_executor', timestamp=1704651180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.729529999999999875789843951'), Decimal('2.724070939999999876038264263'), Decimal('2.707693759999999876783525199'), Decimal('2.702234699999999877031945511'), Decimal('2.688587049999999877652996292'), Decimal('2.674939399999999878274047072')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7376, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.72953}, controller_id=None),\n ExecutorInfo(id='9mAt8GMnFpaRsqEk6ArH7TRaSdhn5fgPm6QqG6YM55Nn', timestamp=1704651360000.0, type='dca_executor', close_timestamp=1704651480000.0, close_type=, status=, config=DCAExecutorConfig(id='9mAt8GMnFpaRsqEk6ArH7TRaSdhn5fgPm6QqG6YM55Nn', type='dca_executor', timestamp=1704651360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.732124800000000033214742068'), Decimal('2.726660550400000033148312584'), Decimal('2.710267801600000032949024131'), Decimal('2.704803552000000032882594647'), Decimal('2.691142928000000032716520937'), Decimal('2.677482304000000032550447227')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7361, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7321248000000002}, controller_id=None),\n ExecutorInfo(id='AQ1ojiqpo7d9boeRA2TQmMfq7gvD8ujMSnt8we78Qi5', timestamp=1704651540000.0, type='dca_executor', close_timestamp=1704651660000.0, close_type=, status=, config=DCAExecutorConfig(id='AQ1ojiqpo7d9boeRA2TQmMfq7gvD8ujMSnt8we78Qi5', type='dca_executor', timestamp=1704651540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.730827399999999954502293009'), Decimal('2.725365745199999954593288423'), Decimal('2.708980780799999954866274665'), Decimal('2.703519125999999954957270079'), Decimal('2.689864988999999955184758614'), Decimal('2.676210851999999955412247149')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7377, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7308274}, controller_id=None),\n ExecutorInfo(id='82EiqYPSSuemEJu8oPn37fTDMM7m5TbEmRtiz7ozC2cc', timestamp=1704651720000.0, type='dca_executor', close_timestamp=1704651840000.0, close_type=, status=, config=DCAExecutorConfig(id='82EiqYPSSuemEJu8oPn37fTDMM7m5TbEmRtiz7ozC2cc', type='dca_executor', timestamp=1704651720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.733821400000000067961632155'), Decimal('2.728353757200000067825708891'), Decimal('2.711950828800000067417939098'), Decimal('2.706483186000000067282015833'), Decimal('2.692814079000000066942207673'), Decimal('2.679144972000000066602399512')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7393, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7338214}, controller_id=None),\n ExecutorInfo(id='9Y1ywJ6eHAusfUBTUbjiKbwdGxd4AppcQDEsE6mqsRrw', timestamp=1704646440000.0, type='dca_executor', close_timestamp=1704653400000.0, close_type=, status=, config=DCAExecutorConfig(id='9Y1ywJ6eHAusfUBTUbjiKbwdGxd4AppcQDEsE6mqsRrw', type='dca_executor', timestamp=1704646440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.680951200000000200885191813'), Decimal('2.686313102400000201286962197'), Decimal('2.702398809600000202492273348'), Decimal('2.707760712000000202894043731'), Decimal('2.721165468000000203898469690'), Decimal('2.734570224000000204902895649')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01516875694464119538051871671768822125159204006195068359375'), net_pnl_quote=Decimal('-7.6793637836644226268845159211196005344390869140625'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.7582, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7146317290551663}, controller_id=None),\n ExecutorInfo(id='9tK3TeF9pZKfCkpkbagEETXCgfpHWnC2CLKPzARobL5J', timestamp=1704657000000.0, type='dca_executor', close_timestamp=1704657120000.0, close_type=, status=, config=DCAExecutorConfig(id='9tK3TeF9pZKfCkpkbagEETXCgfpHWnC2CLKPzARobL5J', type='dca_executor', timestamp=1704657000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.758004999999999946717309239'), Decimal('2.763521009999999946610743857'), Decimal('2.780069039999999946291047713'), Decimal('2.785585049999999946184482331'), Decimal('2.799375074999999945918068878'), Decimal('2.813165099999999945651655424')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7553, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.758005}, controller_id=None),\n ExecutorInfo(id='iDsYnQHTZ6mXZAaAAZ5NkcYc4Vs6cnPXoS7aPjRk4Tc', timestamp=1704657180000.0, type='dca_executor', close_timestamp=1704657300000.0, close_type=, status=, config=DCAExecutorConfig(id='iDsYnQHTZ6mXZAaAAZ5NkcYc4Vs6cnPXoS7aPjRk4Tc', type='dca_executor', timestamp=1704657180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.761311599999999805036646204'), Decimal('2.766834223199999804646719496'), Decimal('2.783402092799999803476939374'), Decimal('2.788924715999999803087012666'), Decimal('2.802731273999999802112195897'), Decimal('2.816537831999999801137379128')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7535, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7613116}, controller_id=None),\n ExecutorInfo(id='AsHmrG44ygQBi3tQihBoDiZnkVGu2SGVe2LRX4zR1tK6', timestamp=1704657360000.0, type='dca_executor', close_timestamp=1704657480000.0, close_type=, status=, config=DCAExecutorConfig(id='AsHmrG44ygQBi3tQihBoDiZnkVGu2SGVe2LRX4zR1tK6', type='dca_executor', timestamp=1704657360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.758806599999999858433828713'), Decimal('2.764324213199999858150696370'), Decimal('2.780877052799999857301299343'), Decimal('2.786394665999999857018167000'), Decimal('2.800188698999999856310336144'), Decimal('2.813982731999999855602505287')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7527, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7588065999999998}, controller_id=None),\n ExecutorInfo(id='Cpg49jjpmmv5DRE9CUJu4Gk7PRTDcf62x7Rk5pkUsCpS', timestamp=1704657540000.0, type='dca_executor', close_timestamp=1704657660000.0, close_type=, status=, config=DCAExecutorConfig(id='Cpg49jjpmmv5DRE9CUJu4Gk7PRTDcf62x7Rk5pkUsCpS', type='dca_executor', timestamp=1704657540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.758806599999999858433828713'), Decimal('2.764324213199999858150696370'), Decimal('2.780877052799999857301299343'), Decimal('2.786394665999999857018167000'), Decimal('2.800188698999999856310336144'), Decimal('2.813982731999999855602505287')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7499, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7588065999999998}, controller_id=None),\n ExecutorInfo(id='HWMrv3QmJcbcoVkncxqxp1Tkf2nZbAB7tJmBiqekVC3w', timestamp=1704657720000.0, type='dca_executor', close_timestamp=1704657840000.0, close_type=, status=, config=DCAExecutorConfig(id='HWMrv3QmJcbcoVkncxqxp1Tkf2nZbAB7tJmBiqekVC3w', type='dca_executor', timestamp=1704657720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.755399799999999788661232679'), Decimal('2.760910599599999788238555144'), Decimal('2.777442998399999786970522540'), Decimal('2.782953797999999786547845006'), Decimal('2.796730796999999785491151169'), Decimal('2.810507795999999784434457333')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7534, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7553997999999997}, controller_id=None),\n ExecutorInfo(id='AcFrewj9PrX2vcyT4GsjTbt5TZthpsmCB9mGf86eguhe', timestamp=1704657900000.0, type='dca_executor', close_timestamp=1704658020000.0, close_type=, status=, config=DCAExecutorConfig(id='AcFrewj9PrX2vcyT4GsjTbt5TZthpsmCB9mGf86eguhe', type='dca_executor', timestamp=1704657900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.758906800000000069887087782'), Decimal('2.764424613600000070026861958'), Decimal('2.780978054400000070446184484'), Decimal('2.786495868000000070585958660'), Decimal('2.800290402000000070935394099'), Decimal('2.814084936000000071284829538')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7534, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7589068}, controller_id=None),\n ExecutorInfo(id='7wbx2BtdEDXCtN2P7fWgDegEDRqsf8nCxoZiN29chei5', timestamp=1704658080000.0, type='dca_executor', close_timestamp=1704658200000.0, close_type=, status=, config=DCAExecutorConfig(id='7wbx2BtdEDXCtN2P7fWgDegEDRqsf8nCxoZiN29chei5', type='dca_executor', timestamp=1704658080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.766020999999999953837280519'), Decimal('2.771553041999999953744955080'), Decimal('2.788149167999999953467978763'), Decimal('2.793681209999999953375653324'), Decimal('2.807511314999999953144839727'), Decimal('2.821341419999999952914026129')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7652, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.766021}, controller_id=None),\n ExecutorInfo(id='3YjYcmVzX7DQtfWgD37M4QrvNCaWZDmmnGvenc5jxHrA', timestamp=1704658260000.0, type='dca_executor', close_timestamp=1704658380000.0, close_type=, status=, config=DCAExecutorConfig(id='3YjYcmVzX7DQtfWgD37M4QrvNCaWZDmmnGvenc5jxHrA', type='dca_executor', timestamp=1704658260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.770229399999999935326396027'), Decimal('2.775769858799999935197048819'), Decimal('2.792391235199999934809007195'), Decimal('2.797931693999999934679659987'), Decimal('2.811782840999999934356291967'), Decimal('2.825633987999999934032923948')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7655, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7702294}, controller_id=None),\n ExecutorInfo(id='EyWM9PPWQTrs1phdgH8f53Nb9qCnawjuFGYvNjXTmugK', timestamp=1704658440000.0, type='dca_executor', close_timestamp=1704658560000.0, close_type=, status=, config=DCAExecutorConfig(id='EyWM9PPWQTrs1phdgH8f53Nb9qCnawjuFGYvNjXTmugK', type='dca_executor', timestamp=1704658440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.769728400000000212992265490'), Decimal('2.775267856800000213418250021'), Decimal('2.791886227200000214696203614'), Decimal('2.797425684000000215122188145'), Decimal('2.811274326000000216187149472'), Decimal('2.825122968000000217252110800')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7549, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7697284000000004}, controller_id=None),\n ExecutorInfo(id='8wwvZLmEJJXETCr8wfLqhKLiZwZSx3xaKkutdpDsSm1M', timestamp=1704658620000.0, type='dca_executor', close_timestamp=1704658740000.0, close_type=, status=, config=DCAExecutorConfig(id='8wwvZLmEJJXETCr8wfLqhKLiZwZSx3xaKkutdpDsSm1M', type='dca_executor', timestamp=1704658620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.760409800000000126844255931'), Decimal('2.765930619600000127097944443'), Decimal('2.782493078400000127859009978'), Decimal('2.788013898000000128112698490'), Decimal('2.801815947000000128746919770'), Decimal('2.815617996000000129381141050')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.752, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7604098}, controller_id=None),\n ExecutorInfo(id='692LNSqqMFu4RQyFJJ75w45Hgq9UUAMpjzV13NCh9cVn', timestamp=1704658800000.0, type='dca_executor', close_timestamp=1704658920000.0, close_type=, status=, config=DCAExecutorConfig(id='692LNSqqMFu4RQyFJJ75w45Hgq9UUAMpjzV13NCh9cVn', type='dca_executor', timestamp=1704658800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047616'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142856')], prices=[Decimal('2.756301599999999911831011222'), Decimal('2.761814203199999911654673244'), Decimal('2.778352012799999911125659312'), Decimal('2.783864615999999910949321334'), Decimal('2.797646123999999910508476390'), Decimal('2.811427631999999910067631446')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.756, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7563016}, controller_id=None),\n ExecutorInfo(id='CE5N9rt1rff8xgUUy6q9fTu6BprG5C1ch6BDR3rup7r6', timestamp=1704658980000.0, type='dca_executor', close_timestamp=1704659100000.0, close_type=, status=, config=DCAExecutorConfig(id='CE5N9rt1rff8xgUUy6q9fTu6BprG5C1ch6BDR3rup7r6', type='dca_executor', timestamp=1704658980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.761511999999999782965776073'), Decimal('2.767035023999999782531707625'), Decimal('2.783604095999999781229502282'), Decimal('2.789127119999999780795433834'), Decimal('2.802934679999999779710262714'), Decimal('2.816742239999999778625091594')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7544, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7615119999999997}, controller_id=None),\n ExecutorInfo(id='FnMtYHMSUw1WY9dBatCydtmVzr6YHf9JRvfSnLYTAkTU', timestamp=1704659160000.0, type='dca_executor', close_timestamp=1704659280000.0, close_type=, status=, config=DCAExecutorConfig(id='FnMtYHMSUw1WY9dBatCydtmVzr6YHf9JRvfSnLYTAkTU', type='dca_executor', timestamp=1704659160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.759908799999999959532737125'), Decimal('2.765428617599999959451802599'), Decimal('2.781988070399999959208999022'), Decimal('2.787507887999999959128064496'), Decimal('2.801307431999999958925728182'), Decimal('2.815106975999999958723391868')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.755, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7599088}, controller_id=None),\n ExecutorInfo(id='4DTemdWSKWC728xq8Xbb26oxLx6hGUrsmyCAS8889WVW', timestamp=1704659340000.0, type='dca_executor', close_timestamp=1704659460000.0, close_type=, status=, config=DCAExecutorConfig(id='4DTemdWSKWC728xq8Xbb26oxLx6hGUrsmyCAS8889WVW', type='dca_executor', timestamp=1704659340000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.760509999999999893320126730'), Decimal('2.766031019999999893106766983'), Decimal('2.782594079999999892466687744'), Decimal('2.788115099999999892253327997'), Decimal('2.801917649999999891719928631'), Decimal('2.815720199999999891186529265')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.755, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.76051}, controller_id=None),\n ExecutorInfo(id='9rUsuon41zpkJb2mKCZLZmFaaZMs1CLiJfrioo6rtyCg', timestamp=1704659520000.0, type='dca_executor', close_timestamp=1704659640000.0, close_type=, status=, config=DCAExecutorConfig(id='9rUsuon41zpkJb2mKCZLZmFaaZMs1CLiJfrioo6rtyCg', type='dca_executor', timestamp=1704659520000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.760509999999999893320126730'), Decimal('2.766031019999999893106766983'), Decimal('2.782594079999999892466687744'), Decimal('2.788115099999999892253327997'), Decimal('2.801917649999999891719928631'), Decimal('2.815720199999999891186529265')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.755, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.76051}, controller_id=None),\n ExecutorInfo(id='AFXLKZB4ghActtJq5ZefMgNXy8bEeURg8WYwzDzfNHwt', timestamp=1704659700000.0, type='dca_executor', close_timestamp=1704659820000.0, close_type=, status=, config=DCAExecutorConfig(id='AFXLKZB4ghActtJq5ZefMgNXy8bEeURg8WYwzDzfNHwt', type='dca_executor', timestamp=1704659700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.757103199999999823547530696'), Decimal('2.762617406399999823194625757'), Decimal('2.779160025599999822135910942'), Decimal('2.784674231999999821783006003'), Decimal('2.798459747999999820900743656'), Decimal('2.812245263999999820018481310')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7442, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7571032}, controller_id=None),\n ExecutorInfo(id='71uxrmdhT3MfwTRiin3CeFJLbrEY2LUKsq23paqxHrre', timestamp=1704659880000.0, type='dca_executor', close_timestamp=1704660000000.0, close_type=, status=, config=DCAExecutorConfig(id='71uxrmdhT3MfwTRiin3CeFJLbrEY2LUKsq23paqxHrre', type='dca_executor', timestamp=1704659880000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.745880800000000169561481521'), Decimal('2.751372561600000169900604484'), Decimal('2.767847846400000170917973373'), Decimal('2.773339608000000171257096336'), Decimal('2.787069012000000172104903744'), Decimal('2.800798416000000172952711151')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7404, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7458808}, controller_id=None),\n ExecutorInfo(id='2SmyUAWEvgqvrAGDEvkhQ4Lg93TN27oCsEunfyorShrK', timestamp=1704660060000.0, type='dca_executor', close_timestamp=1704660180000.0, close_type=, status=, config=DCAExecutorConfig(id='2SmyUAWEvgqvrAGDEvkhQ4Lg93TN27oCsEunfyorShrK', type='dca_executor', timestamp=1704660060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.745880800000000169561481521'), Decimal('2.751372561600000169900604484'), Decimal('2.767847846400000170917973373'), Decimal('2.773339608000000171257096336'), Decimal('2.787069012000000172104903744'), Decimal('2.800798416000000172952711151')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7397, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7458808}, controller_id=None),\n ExecutorInfo(id='6arfc33WaTkotJB6apt6pPSJnFmCDNagt1xV9haEGf1c', timestamp=1704660240000.0, type='dca_executor', close_timestamp=1704660360000.0, close_type=, status=, config=DCAExecutorConfig(id='6arfc33WaTkotJB6apt6pPSJnFmCDNagt1xV9haEGf1c', type='dca_executor', timestamp=1704660240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.745179400000000024320832846'), Decimal('2.750669758800000024369474512'), Decimal('2.767140835200000024515399509'), Decimal('2.772631194000000024564041174'), Decimal('2.786357091000000024685645339'), Decimal('2.800082988000000024807249503')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.741, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7451794}, controller_id=None),\n ExecutorInfo(id='93bnYzYESnBgkJGsAYcLgNSnv95Fwt7tJq1CbH1kAQpd', timestamp=1704660420000.0, type='dca_executor', close_timestamp=1704660540000.0, close_type=, status=, config=DCAExecutorConfig(id='93bnYzYESnBgkJGsAYcLgNSnv95Fwt7tJq1CbH1kAQpd', type='dca_executor', timestamp=1704660420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.745279599999999790796703646'), Decimal('2.750770159199999790378297053'), Decimal('2.767241836799999789123077275'), Decimal('2.772732395999999788704670682'), Decimal('2.786458793999999787658654201'), Decimal('2.800185191999999786612637719')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7381, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7452796}, controller_id=None),\n ExecutorInfo(id='96X2hDSNjLJ9tKUGehebQPkm8EmEMKA5ReGw7NQo2Wxz', timestamp=1704660600000.0, type='dca_executor', close_timestamp=1704660720000.0, close_type=, status=, config=DCAExecutorConfig(id='96X2hDSNjLJ9tKUGehebQPkm8EmEMKA5ReGw7NQo2Wxz', type='dca_executor', timestamp=1704660600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.746882800000000059207130864'), Decimal('2.752376565600000059325545126'), Decimal('2.768857862400000059680787911'), Decimal('2.774351628000000059799202173'), Decimal('2.788086042000000060095237827'), Decimal('2.801820456000000060391273481')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7389, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7468828}, controller_id=None),\n ExecutorInfo(id='HbkQaVrgKQWBkR3cYP7YLz1MDJyqCmBai8mC1FGPc1ep', timestamp=1704660780000.0, type='dca_executor', close_timestamp=1704660900000.0, close_type=, status=, config=DCAExecutorConfig(id='HbkQaVrgKQWBkR3cYP7YLz1MDJyqCmBai8mC1FGPc1ep', type='dca_executor', timestamp=1704660780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.744377800000000112604313373'), Decimal('2.749866555600000112829522000'), Decimal('2.766332822400000113505147880'), Decimal('2.771821578000000113730356507'), Decimal('2.785543467000000114293378074'), Decimal('2.799265356000000114856399640')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7389, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7443778}, controller_id=None),\n ExecutorInfo(id='7J1h319fSLn9TUkSfDwbC6Sqata4XcpBqqmZQs8Upve3', timestamp=1704660960000.0, type='dca_executor', close_timestamp=1704661080000.0, close_type=, status=, config=DCAExecutorConfig(id='7J1h319fSLn9TUkSfDwbC6Sqata4XcpBqqmZQs8Upve3', type='dca_executor', timestamp=1704660960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.744377800000000112604313373'), Decimal('2.749866555600000112829522000'), Decimal('2.766332822400000113505147880'), Decimal('2.771821578000000113730356507'), Decimal('2.785543467000000114293378074'), Decimal('2.799265356000000114856399640')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.735, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7443778}, controller_id=None),\n ExecutorInfo(id='Es7ydLEMpuBkn34ZvFsYnYwAgq2bLw9BkknLVQG15rxj', timestamp=1704661140000.0, type='dca_executor', close_timestamp=1704661260000.0, close_type=, status=, config=DCAExecutorConfig(id='Es7ydLEMpuBkn34ZvFsYnYwAgq2bLw9BkknLVQG15rxj', type='dca_executor', timestamp=1704661140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.740469999999999875520198532'), Decimal('2.745950939999999875271238929'), Decimal('2.762393759999999874524360120'), Decimal('2.767874699999999874275400517'), Decimal('2.781577049999999873653001510'), Decimal('2.795279399999999873030602503')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7296, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7404699999999997}, controller_id=None),\n ExecutorInfo(id='AVo51FLz2F6RS6asJ8fQrWYCCZefKrpYWQAxGFYW9U7k', timestamp=1704661320000.0, type='dca_executor', close_timestamp=1704661440000.0, close_type=, status=, config=DCAExecutorConfig(id='AVo51FLz2F6RS6asJ8fQrWYCCZefKrpYWQAxGFYW9U7k', type='dca_executor', timestamp=1704661320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.738966999999999818563030384'), Decimal('2.744444933999999818200156445'), Decimal('2.760878735999999817111534627'), Decimal('2.766356669999999816748660688'), Decimal('2.780051504999999815841475840'), Decimal('2.793746339999999814934290992')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7335, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7389669999999997}, controller_id=None),\n ExecutorInfo(id='6ujk5o2pbtfdtQijgvrE9dYpruq25bpZXdqmiMNM3WP3', timestamp=1704661500000.0, type='dca_executor', close_timestamp=1704661620000.0, close_type=, status=, config=DCAExecutorConfig(id='6ujk5o2pbtfdtQijgvrE9dYpruq25bpZXdqmiMNM3WP3', type='dca_executor', timestamp=1704661500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.738966999999999818563030384'), Decimal('2.744444933999999818200156445'), Decimal('2.760878735999999817111534627'), Decimal('2.766356669999999816748660688'), Decimal('2.780051504999999815841475840'), Decimal('2.793746339999999814934290992')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7381, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7389669999999997}, controller_id=None),\n ExecutorInfo(id='A8bBnxjpWsaZjDi8ae5DHPvaVbFn9eL3wnCBNtuTfJzM', timestamp=1704661680000.0, type='dca_executor', close_timestamp=1704661800000.0, close_type=, status=, config=DCAExecutorConfig(id='A8bBnxjpWsaZjDi8ae5DHPvaVbFn9eL3wnCBNtuTfJzM', type='dca_executor', timestamp=1704661680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.743576200000000200887793899'), Decimal('2.749063352400000201289569487'), Decimal('2.765524809600000202494896250'), Decimal('2.771011962000000202896671838'), Decimal('2.784729843000000203901110807'), Decimal('2.798447724000000204905549777')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7428, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7435762}, controller_id=None),\n ExecutorInfo(id='13a2xWfLccv1KX9qbYbu3JMo9MpcxwxNjThQ2aSBvQ1n', timestamp=1704661860000.0, type='dca_executor', close_timestamp=1704661980000.0, close_type=, status=, config=DCAExecutorConfig(id='13a2xWfLccv1KX9qbYbu3JMo9MpcxwxNjThQ2aSBvQ1n', type='dca_executor', timestamp=1704661860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.748285599999999904711039943'), Decimal('2.753782171199999904520462023'), Decimal('2.770271884799999903948728263'), Decimal('2.775768455999999903758150342'), Decimal('2.789509883999999903281705542'), Decimal('2.803251311999999902805260742')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7401, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7482856}, controller_id=None),\n ExecutorInfo(id='31nKe3NrnAWoL943GnWGh2W5L65xpJ6ut54HzfjNYtE4', timestamp=1704662040000.0, type='dca_executor', close_timestamp=1704662160000.0, close_type=, status=, config=DCAExecutorConfig(id='31nKe3NrnAWoL943GnWGh2W5L65xpJ6ut54HzfjNYtE4', type='dca_executor', timestamp=1704662040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.745580199999999980179092583'), Decimal('2.751071360399999980139450768'), Decimal('2.767544841599999980020525324'), Decimal('2.773036001999999979980883509'), Decimal('2.786763902999999979881778972'), Decimal('2.800491803999999979782674435')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7411, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7455802}, controller_id=None),\n ExecutorInfo(id='HahS3Ca8ReNLaccrhzLi8UA4bU59Cfv4mQeuEYYNyXEj', timestamp=1704662220000.0, type='dca_executor', close_timestamp=1704662340000.0, close_type=, status=, config=DCAExecutorConfig(id='HahS3Ca8ReNLaccrhzLi8UA4bU59Cfv4mQeuEYYNyXEj', type='dca_executor', timestamp=1704662220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.746582199999999869824741926'), Decimal('2.752075364399999869564391410'), Decimal('2.768554857599999868783339861'), Decimal('2.774048021999999868522989345'), Decimal('2.787780932999999867872113055'), Decimal('2.801513843999999867221236765')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7338, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7465821999999998}, controller_id=None),\n ExecutorInfo(id='H6YApyvwiQqHJdcrDN3Qq5cv8dt6AfjSGKmpHRKCUX4Z', timestamp=1704662400000.0, type='dca_executor', close_timestamp=1704662520000.0, close_type=, status=, config=DCAExecutorConfig(id='H6YApyvwiQqHJdcrDN3Qq5cv8dt6AfjSGKmpHRKCUX4Z', type='dca_executor', timestamp=1704662400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.740269599999999897591068664'), Decimal('2.745750139199999897386250801'), Decimal('2.762191756799999896771797213'), Decimal('2.767672295999999896566979351'), Decimal('2.781373643999999896054934694'), Decimal('2.795074991999999895542890037')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7348, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7402696}, controller_id=None),\n ExecutorInfo(id='9wDCjoBo42t62Rw1rq58itiXbeEp15PiQXSVmH4kuVYu', timestamp=1704662580000.0, type='dca_executor', close_timestamp=1704662700000.0, close_type=, status=, config=DCAExecutorConfig(id='9wDCjoBo42t62Rw1rq58itiXbeEp15PiQXSVmH4kuVYu', type='dca_executor', timestamp=1704662580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.739267600000000007945419321'), Decimal('2.744746135200000007961310160'), Decimal('2.761181740800000008008982676'), Decimal('2.766660276000000008024873514'), Decimal('2.780356614000000008064600611'), Decimal('2.794052952000000008104327707')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7317, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7392676}, controller_id=None),\n ExecutorInfo(id='332FcETfZ65CFwBox448nPtZPe1aRkbjs4Gcr85Go3sz', timestamp=1704662760000.0, type='dca_executor', close_timestamp=1704662880000.0, close_type=, status=, config=DCAExecutorConfig(id='332FcETfZ65CFwBox448nPtZPe1aRkbjs4Gcr85Go3sz', type='dca_executor', timestamp=1704662760000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999998'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.736061199999999916101953156'), Decimal('2.741533322399999915934157062'), Decimal('2.757949689599999915430768781'), Decimal('2.763421811999999915262972688'), Decimal('2.777102117999999914843482453'), Decimal('2.790782423999999914423992219')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7313, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7360612}, controller_id=None),\n ExecutorInfo(id='HhY9Fqqc8BAEEgNx9rY74XF2LenRyag9kWegBb2U1B2p', timestamp=1704662940000.0, type='dca_executor', close_timestamp=1704663060000.0, close_type=, status=, config=DCAExecutorConfig(id='HhY9Fqqc8BAEEgNx9rY74XF2LenRyag9kWegBb2U1B2p', type='dca_executor', timestamp=1704662940000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.736762600000000061342601830'), Decimal('2.742236125200000061465287034'), Decimal('2.758656700800000061833342645'), Decimal('2.764130226000000061956027848'), Decimal('2.777814039000000062262740857'), Decimal('2.791497852000000062569453867')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.727, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7367626}, controller_id=None),\n ExecutorInfo(id='4sqCzUN6VAmsvhdPa9RFquZ5kjxQMRXfGXSkVhBJBEkQ', timestamp=1704663120000.0, type='dca_executor', close_timestamp=1704663240000.0, close_type=, status=, config=DCAExecutorConfig(id='4sqCzUN6VAmsvhdPa9RFquZ5kjxQMRXfGXSkVhBJBEkQ', type='dca_executor', timestamp=1704663120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.732453999999999868400227253'), Decimal('2.737918907999999868137027708'), Decimal('2.754313631999999867347429071'), Decimal('2.759778539999999867084229526'), Decimal('2.773440809999999866426230662'), Decimal('2.787103079999999865768231798')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7253, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7324539999999997}, controller_id=None),\n ExecutorInfo(id='3kmkDpdweyAmic6RMMHwWCqSoNKNPaesiMufhfBZFyzk', timestamp=1704663300000.0, type='dca_executor', close_timestamp=1704663420000.0, close_type=, status=, config=DCAExecutorConfig(id='3kmkDpdweyAmic6RMMHwWCqSoNKNPaesiMufhfBZFyzk', type='dca_executor', timestamp=1704663300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.728947000000000032151760420'), Decimal('2.734404894000000032216063941'), Decimal('2.750778576000000032408974503'), Decimal('2.756236470000000032473278024'), Decimal('2.769881205000000032634036826'), Decimal('2.783525940000000032794795628')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7167, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7289470000000002}, controller_id=None),\n ExecutorInfo(id='QumDTYefG6n8WhLJAa7wPq52PYTfzcAnaFtrof76ErG', timestamp=1704663480000.0, type='dca_executor', close_timestamp=1704663600000.0, close_type=, status=, config=DCAExecutorConfig(id='QumDTYefG6n8WhLJAa7wPq52PYTfzcAnaFtrof76ErG', type='dca_executor', timestamp=1704663480000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.721331799999999980890048877'), Decimal('2.726774463599999980851828975'), Decimal('2.743102454399999980737169268'), Decimal('2.748545117999999980698949366'), Decimal('2.762151776999999980603399610'), Decimal('2.775758435999999980507849855')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7124, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7213318}, controller_id=None),\n ExecutorInfo(id='EUK4QwWePhS2GALeVxymYcL2MHYunguMunbKsiw61bAK', timestamp=1704663660000.0, type='dca_executor', close_timestamp=1704663780000.0, close_type=, status=, config=DCAExecutorConfig(id='EUK4QwWePhS2GALeVxymYcL2MHYunguMunbKsiw61bAK', type='dca_executor', timestamp=1704663660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.713315799999999973770077598'), Decimal('2.718742431599999973717617753'), Decimal('2.735022326399999973560238219'), Decimal('2.740448957999999973507778374'), Decimal('2.754015536999999973376628762'), Decimal('2.767582115999999973245479150')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7127, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7133158}, controller_id=None),\n ExecutorInfo(id='3zuvvGN3UaZQxxj83DtK3tZtxdqPLqu6p8oG6woh8aXz', timestamp=1704663840000.0, type='dca_executor', close_timestamp=1704663960000.0, close_type=, status=, config=DCAExecutorConfig(id='3zuvvGN3UaZQxxj83DtK3tZtxdqPLqu6p8oG6woh8aXz', type='dca_executor', timestamp=1704663840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.718125399999999889046582712'), Decimal('2.723561650799999888824675877'), Decimal('2.739870403199999888158955374'), Decimal('2.745306653999999887937048539'), Decimal('2.758897280999999887382281453'), Decimal('2.772487907999999886827514366')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.705, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7181254}, controller_id=None),\n ExecutorInfo(id='6izmHkrDjoBJtyL8aoBsPt2hnV9S6N6XXHsspSY9mUxy', timestamp=1704664020000.0, type='dca_executor', close_timestamp=1704664140000.0, close_type=, status=, config=DCAExecutorConfig(id='6izmHkrDjoBJtyL8aoBsPt2hnV9S6N6XXHsspSY9mUxy', type='dca_executor', timestamp=1704664020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.708907000000000014351832222'), Decimal('2.714324814000000014380535886'), Decimal('2.730578256000000014466646880'), Decimal('2.735996070000000014495350544'), Decimal('2.749540605000000014567109705'), Decimal('2.763085140000000014638868866')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7035, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.708907}, controller_id=None),\n ExecutorInfo(id='CkxEPep7vJLEdx5byXcG77yFanpv2EQAdZyFHgG6UnYW', timestamp=1704664200000.0, type='dca_executor', close_timestamp=1704664320000.0, close_type=, status=, config=DCAExecutorConfig(id='CkxEPep7vJLEdx5byXcG77yFanpv2EQAdZyFHgG6UnYW', type='dca_executor', timestamp=1704664200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.713516199999999951699207467'), Decimal('2.718943232399999951602605882'), Decimal('2.735224329599999951312801127'), Decimal('2.740651361999999951216199542'), Decimal('2.754218942999999950974695579'), Decimal('2.767786523999999950733191616')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7081, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7135162}, controller_id=None),\n ExecutorInfo(id='9KeANedw2yNnEJBTkkwAsiqB5HNy65L4rDENhTCj77KB', timestamp=1704664380000.0, type='dca_executor', close_timestamp=1704664500000.0, close_type=, status=, config=DCAExecutorConfig(id='9KeANedw2yNnEJBTkkwAsiqB5HNy65L4rDENhTCj77KB', type='dca_executor', timestamp=1704664380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.713215600000000207294206799'), Decimal('2.718642031200000207708795213'), Decimal('2.734921324800000208952560453'), Decimal('2.740347756000000209367148867'), Decimal('2.753913834000000210403619901'), Decimal('2.767479912000000211440090935')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7093, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7132156000000003}, controller_id=None),\n ExecutorInfo(id='H7LkGLutHwurFmFLznz7KtDioYpakyGVNs6ruDkQ4Efo', timestamp=1704664560000.0, type='dca_executor', close_timestamp=1704664680000.0, close_type=, status=, config=DCAExecutorConfig(id='H7LkGLutHwurFmFLznz7KtDioYpakyGVNs6ruDkQ4Efo', type='dca_executor', timestamp=1704664560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.715720600000000153897024290'), Decimal('2.721152041200000154204818339'), Decimal('2.737446364800000155128200484'), Decimal('2.742877806000000155435994533'), Decimal('2.756456409000000156205479654'), Decimal('2.770035012000000156974964776')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7132, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7157206}, controller_id=None),\n ExecutorInfo(id='J3ExgLQzyYCxXhx8M2DabL1qyX1eUBp4hbd7xQe33duC', timestamp=1704664740000.0, type='dca_executor', close_timestamp=1704664860000.0, close_type=, status=, config=DCAExecutorConfig(id='J3ExgLQzyYCxXhx8M2DabL1qyX1eUBp4hbd7xQe33duC', type='dca_executor', timestamp=1704664740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.715419999999999964514635352'), Decimal('2.720850839999999964443664623'), Decimal('2.737143359999999964230752435'), Decimal('2.742574199999999964159781706'), Decimal('2.756151299999999963982354882'), Decimal('2.769728399999999963804928059')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.701, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.71542}, controller_id=None),\n ExecutorInfo(id='A6cE6eAdymGkvL9wsRuMdJjGA22gfTutjGvk6AxkTpx6', timestamp=1704664920000.0, type='dca_executor', close_timestamp=1704665040000.0, close_type=, status=, config=DCAExecutorConfig(id='A6cE6eAdymGkvL9wsRuMdJjGA22gfTutjGvk6AxkTpx6', type='dca_executor', timestamp=1704664920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.706402000000000067749014730'), Decimal('2.711814804000000067884512759'), Decimal('2.728053216000000068291006848'), Decimal('2.733466020000000068426504877'), Decimal('2.746998030000000068765249951'), Decimal('2.760530040000000069103995025')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6977, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.706402}, controller_id=None),\n ExecutorInfo(id='8f55LoscGJ5q2VcTk9CJZTmUvJfnduNtQcTKNAGxKFaP', timestamp=1704651900000.0, type='dca_executor', close_timestamp=1704665760000.0, close_type=, status=, config=DCAExecutorConfig(id='8f55LoscGJ5q2VcTk9CJZTmUvJfnduNtQcTKNAGxKFaP', type='dca_executor', timestamp=1704651900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.735418199999999892099396270'), Decimal('2.729947363599999892315197477'), Decimal('2.713534854399999892962601100'), Decimal('2.708064017999999893178402307'), Decimal('2.694386926999999893717905326'), Decimal('2.680709835999999894257408345')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0157408656589923083568560713274564477615058422088623046875'), net_pnl_quote=Decimal('-7.7718650278696070898831749218516051769256591796875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6568, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.701269992133626}, controller_id=None),\n ExecutorInfo(id='42MribVgyfdKcXPJuUA89n8RD8fKanTZboQz3fsDEqom', timestamp=1704665100000.0, type='dca_executor', close_timestamp=1704666900000.0, close_type=, status=, config=DCAExecutorConfig(id='42MribVgyfdKcXPJuUA89n8RD8fKanTZboQz3fsDEqom', type='dca_executor', timestamp=1704665100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428571'), Decimal('71.99999999999999999999999999'), Decimal('96.19047619047619047619047616'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.694778800000000012927317549'), Decimal('2.700168357600000012953172184'), Decimal('2.716337030400000013030736089'), Decimal('2.721726588000000013056590724'), Decimal('2.735200482000000013121227312'), Decimal('2.748674376000000013185863900')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.03149577088588839668403807081631384789943695068359375'), net_pnl_quote=Decimal('0.74989930680686656128131062359898351132869720458984375'), cum_fees_quote=Decimal('0.01428571428571428526821396332024960429407656192779541015625'), filled_amount_quote=Decimal('23.8095238095238102005168912000954151153564453125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6107, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6947788}, controller_id=None),\n ExecutorInfo(id='BWtYh8XJnXL1W77DsogFWNu8KUgygXQA1JNdPV33SjTq', timestamp=1704666900000.0, type='dca_executor', close_timestamp=1704678180000.0, close_type=, status=, config=DCAExecutorConfig(id='BWtYh8XJnXL1W77DsogFWNu8KUgygXQA1JNdPV33SjTq', type='dca_executor', timestamp=1704666900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.615921400000000020755643037'), Decimal('2.621153242800000020797154323'), Decimal('2.636848771200000020921688181'), Decimal('2.642080614000000020963199467'), Decimal('2.655160221000000021066977683'), Decimal('2.668239828000000021170755898')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0375307606003321136967088023084215819835662841796875'), net_pnl_quote=Decimal('13.53162637549593227959121577441692352294921875'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 2.5472, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6409223393525063}, controller_id=None),\n ExecutorInfo(id='3PcxXJLAywAeBj8mkgBaG58vBDq1fRKaB6oRJjhSNa4t', timestamp=1704669360000.0, type='dca_executor', close_timestamp=1704678180000.0, close_type=, status=, config=DCAExecutorConfig(id='3PcxXJLAywAeBj8mkgBaG58vBDq1fRKaB6oRJjhSNa4t', type='dca_executor', timestamp=1704669360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.630328800000000164406493974'), Decimal('2.625068142400000164077680986'), Decimal('2.609286169600000163091242022'), Decimal('2.604025512000000162762429034'), Decimal('2.590873868000000161940396564'), Decimal('2.577722224000000161118364095')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.018065381088094516204289874394817161373794078826904296875'), net_pnl_quote=Decimal('-8.9195668481860952425677169230766594409942626953125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.5472, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5974924992766555}, controller_id=None),\n ExecutorInfo(id='HYttT7TPfvMTi63QP5UvuKR3nku2dSeeyxomnEtpWQUU', timestamp=1704678180000.0, type='dca_executor', close_timestamp=1704681420000.0, close_type=, status=, config=DCAExecutorConfig(id='HYttT7TPfvMTi63QP5UvuKR3nku2dSeeyxomnEtpWQUU', type='dca_executor', timestamp=1704678180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.552294400000000131107391609'), Decimal('2.557398988800000131369606392'), Decimal('2.572712755200000132156250742'), Decimal('2.577817344000000132418465525'), Decimal('2.590578816000000133074002483'), Decimal('2.603340288000000133729539441')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.031414858673586766590890562156346277333796024322509765625'), net_pnl_quote=Decimal('11.326552497479152492587672895751893520355224609375'), cum_fees_quote=Decimal('0.2163285714285713978721759076506714336574077606201171875'), filled_amount_quote=Decimal('360.547619047619036791729740798473358154296875'), is_active=False, is_trading=False, custom_info={'close_price': 2.498, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5766872420418676}, controller_id=None),\n ExecutorInfo(id='AEvoriYoHDnW6RRaapZfhkkS7vPBwbrm1sSoJ9QMghS', timestamp=1704681780000.0, type='dca_executor', close_timestamp=1704681900000.0, close_type=, status=, config=DCAExecutorConfig(id='AEvoriYoHDnW6RRaapZfhkkS7vPBwbrm1sSoJ9QMghS', type='dca_executor', timestamp=1704681780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.506476999999999843888674744'), Decimal('2.501464045999999844200897395'), Decimal('2.486425183999999845137565346'), Decimal('2.481412229999999845449787997'), Decimal('2.468879844999999846230344623'), Decimal('2.456347459999999847010901249')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5107, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.506477}, controller_id=None),\n ExecutorInfo(id='FSMRK3cTPBfBkkLK9KfbZAf6ZCrULVH3UEoc4LpvQYFg', timestamp=1704681960000.0, type='dca_executor', close_timestamp=1704682080000.0, close_type=, status=, config=DCAExecutorConfig(id='FSMRK3cTPBfBkkLK9KfbZAf6ZCrULVH3UEoc4LpvQYFg', type='dca_executor', timestamp=1704681960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.483722600000000133920378953'), Decimal('2.478755154800000133652538195'), Decimal('2.463852819200000132849015921'), Decimal('2.458885374000000132581175163'), Decimal('2.446466761000000131911573269'), Decimal('2.434048148000000131241971374')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.508, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.4837226}, controller_id=None),\n ExecutorInfo(id='wyqPc111MMUc5qDnwcHV69Wck1zLZjPhBSsRqs5yk4P', timestamp=1704682140000.0, type='dca_executor', close_timestamp=1704684240000.0, close_type=, status=, config=DCAExecutorConfig(id='wyqPc111MMUc5qDnwcHV69Wck1zLZjPhBSsRqs5yk4P', type='dca_executor', timestamp=1704682140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.506377200000000076480580202'), Decimal('2.501364445600000076327619042'), Decimal('2.486326182400000075868735560'), Decimal('2.481313428000000075715774400'), Decimal('2.468781542000000075333371499'), Decimal('2.456249656000000074950968598')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01326318625583954198543512603691851836629211902618408203125'), net_pnl_quote=Decimal('-6.5485403187462996044132523820735514163970947265625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.435, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.475088276932536}, controller_id=None),\n ExecutorInfo(id='2bc8JNunT5958j2Dg7DnAQmK1vDgU8r9jaSssqxxPYak', timestamp=1704681420000.0, type='dca_executor', close_timestamp=1704684300000.0, close_type=, status=, config=DCAExecutorConfig(id='2bc8JNunT5958j2Dg7DnAQmK1vDgU8r9jaSssqxxPYak', type='dca_executor', timestamp=1704681420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.502996000000000220812784723'), Decimal('2.508001992000000221254410292'), Decimal('2.523019968000000222579287001'), Decimal('2.528025960000000223020912570'), Decimal('2.540540940000000224124976494'), Decimal('2.553055920000000225229040417')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0379631101923998404146232132916338741779327392578125'), net_pnl_quote=Decimal('2.71526626233259804621411603875458240509033203125'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 2.4201, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.50633554992277}, controller_id=None),\n ExecutorInfo(id='6A2P272WuqcYB46gcAsNmikjuTZFAiGFhbMSpBxR9wT8', timestamp=1704684300000.0, type='dca_executor', close_timestamp=1704685680000.0, close_type=, status=, config=DCAExecutorConfig(id='6A2P272WuqcYB46gcAsNmikjuTZFAiGFhbMSpBxR9wT8', type='dca_executor', timestamp=1704684300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.424940200000000140357629684'), Decimal('2.429790080400000140638344943'), Decimal('2.444339721600000141480490721'), Decimal('2.449189602000000141761205981'), Decimal('2.461314303000000142462994129'), Decimal('2.473439004000000143164782278')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01370487273407631871602330164705563220195472240447998046875'), net_pnl_quote=Decimal('-6.9382549748729704930383377359248697757720947265625'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.497, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.4554044877733623}, controller_id=None),\n ExecutorInfo(id='CF37NHAeFkCkJch1ubZfvr89NHmmsz17UfKdr9y6wSLU', timestamp=1704687840000.0, type='dca_executor', close_timestamp=1704687960000.0, close_type=, status=, config=DCAExecutorConfig(id='CF37NHAeFkCkJch1ubZfvr89NHmmsz17UfKdr9y6wSLU', type='dca_executor', timestamp=1704687840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.503383199999999963021241056'), Decimal('2.498376433599999963095198574'), Decimal('2.483356134399999963317071128'), Decimal('2.478349367999999963391028645'), Decimal('2.465832451999999963575922440'), Decimal('2.453315535999999963760816235')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5241, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5033832}, controller_id=None),\n ExecutorInfo(id='9XM6Hm69dBpoSp1uTjEXWbtQWb9a2fyX21o4TSJtj5HH', timestamp=1704688020000.0, type='dca_executor', close_timestamp=1704688140000.0, close_type=, status=, config=DCAExecutorConfig(id='9XM6Hm69dBpoSp1uTjEXWbtQWb9a2fyX21o4TSJtj5HH', type='dca_executor', timestamp=1704688020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.513762400000000149519802095'), Decimal('2.508734875200000149220762491'), Decimal('2.493652300800000148323643678'), Decimal('2.488624776000000148024604074'), Decimal('2.476055964000000147277005064'), Decimal('2.463487152000000146529406053')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5267, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5137624}, controller_id=None),\n ExecutorInfo(id='8pRsJQFqxWkcKbhRyaRxjemTnHBGBUdE8XYZrWUVWFNh', timestamp=1704688200000.0, type='dca_executor', close_timestamp=1704688320000.0, close_type=, status=, config=DCAExecutorConfig(id='8pRsJQFqxWkcKbhRyaRxjemTnHBGBUdE8XYZrWUVWFNh', type='dca_executor', timestamp=1704688200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.524640600000000059460898705'), Decimal('2.519591318800000059341976908'), Decimal('2.504443475200000058985211515'), Decimal('2.499394194000000058866289718'), Decimal('2.486770991000000058568985224'), Decimal('2.474147788000000058271680731')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.529, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5246406}, controller_id=None),\n ExecutorInfo(id='GkVgBVpFkJgxuLBX2691DJUDUH8qpA4uiovPcksSxiAK', timestamp=1704688380000.0, type='dca_executor', close_timestamp=1704688500000.0, close_type=, status=, config=DCAExecutorConfig(id='GkVgBVpFkJgxuLBX2691DJUDUH8qpA4uiovPcksSxiAK', type='dca_executor', timestamp=1704688380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.525039800000000015495339734'), Decimal('2.519989720400000015464349055'), Decimal('2.504839481600000015371377016'), Decimal('2.499789402000000015340386337'), Decimal('2.487164203000000015262909638'), Decimal('2.474539004000000015185432939')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5356, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5250398}, controller_id=None),\n ExecutorInfo(id='8LtDHQpKLjnUPqLYX1m4hvuXQugA8GjpFk9ZUXzaUD8K', timestamp=1704688560000.0, type='dca_executor', close_timestamp=1704688680000.0, close_type=, status=, config=DCAExecutorConfig(id='8LtDHQpKLjnUPqLYX1m4hvuXQugA8GjpFk9ZUXzaUD8K', type='dca_executor', timestamp=1704688560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.533722400000000167247010685'), Decimal('2.528654955200000166912516664'), Decimal('2.513452620800000165909034600'), Decimal('2.508385176000000165574540578'), Decimal('2.495716564000000164738305525'), Decimal('2.483047952000000163902070471')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5423, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5337224000000003}, controller_id=None),\n ExecutorInfo(id='9C6V2jA2qzuJ67HXpAqeLCgRYmozDLb1uTvgox7XcAzN', timestamp=1704688740000.0, type='dca_executor', close_timestamp=1704688860000.0, close_type=, status=, config=DCAExecutorConfig(id='9C6V2jA2qzuJ67HXpAqeLCgRYmozDLb1uTvgox7XcAzN', type='dca_executor', timestamp=1704688740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.537215400000000004148885402'), Decimal('2.532140969200000004140587631'), Decimal('2.516917676800000004115694319'), Decimal('2.511843246000000004107396548'), Decimal('2.499157169000000004086652121'), Decimal('2.486471092000000004065907694')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5456, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5372154}, controller_id=None),\n ExecutorInfo(id='2KxrSfbdibicBNUhXou1sVgJvLMs73FDcTFhtk3P8Pdo', timestamp=1704688920000.0, type='dca_executor', close_timestamp=1704689040000.0, close_type=, status=, config=DCAExecutorConfig(id='2KxrSfbdibicBNUhXou1sVgJvLMs73FDcTFhtk3P8Pdo', type='dca_executor', timestamp=1704688920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.536117599999999903453656858'), Decimal('2.531045364799999903646749544'), Decimal('2.515828659199999904226027603'), Decimal('2.510756423999999904419120289'), Decimal('2.498075835999999904901852005'), Decimal('2.485395247999999905384583721')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5493, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5361176}, controller_id=None),\n ExecutorInfo(id='5F6CvLQqZrCisWFP4gRug9XgEDHEH1MtvFrqQycTB8Tq', timestamp=1704689100000.0, type='dca_executor', close_timestamp=1704689220000.0, close_type=, status=, config=DCAExecutorConfig(id='5F6CvLQqZrCisWFP4gRug9XgEDHEH1MtvFrqQycTB8Tq', type='dca_executor', timestamp=1704689100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.544600600000000077188107295'), Decimal('2.539511398800000077033731080'), Decimal('2.524243795200000076570602437'), Decimal('2.519154594000000076416226222'), Decimal('2.506431591000000076030285686'), Decimal('2.493708588000000075644345149')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5482, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5446006}, controller_id=None),\n ExecutorInfo(id='71NQbivdTti6dFhA1rpZnV2CAGYXxMqpXgStr9AQe4CR', timestamp=1704689280000.0, type='dca_executor', close_timestamp=1704689400000.0, close_type=, status=, config=DCAExecutorConfig(id='71NQbivdTti6dFhA1rpZnV2CAGYXxMqpXgStr9AQe4CR', type='dca_executor', timestamp=1704689280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047617'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.553997800000000165993689626'), Decimal('2.559105795600000166325677005'), Decimal('2.574429782400000167321639143'), Decimal('2.579537778000000167653626522'), Decimal('2.592307767000000168483594970'), Decimal('2.605077756000000169313563419')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5419, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5539978000000003}, controller_id=None),\n ExecutorInfo(id='584xc5nzxWYdmvLGC7YkxLY1MNQMqnaPsV393RrX4S8x', timestamp=1704689460000.0, type='dca_executor', close_timestamp=1704689580000.0, close_type=, status=, config=DCAExecutorConfig(id='584xc5nzxWYdmvLGC7YkxLY1MNQMqnaPsV393RrX4S8x', type='dca_executor', timestamp=1704689460000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.545681199999999969491329409'), Decimal('2.550772562399999969430312068'), Decimal('2.566046649599999969247260044'), Decimal('2.571138011999999969186242703'), Decimal('2.583866417999999969033699350'), Decimal('2.596594823999999968881155997')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5379, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5456811999999998}, controller_id=None),\n ExecutorInfo(id='AxwG9Ngjy1njAy74Dk1qypsyxs5qu6xhprz7ut4N4jFM', timestamp=1704689640000.0, type='dca_executor', close_timestamp=1704689760000.0, close_type=, status=, config=DCAExecutorConfig(id='AxwG9Ngjy1njAy74Dk1qypsyxs5qu6xhprz7ut4N4jFM', type='dca_executor', timestamp=1704689640000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.546883599999999837066108620'), Decimal('2.551977367199999836740240837'), Decimal('2.567258668799999835762637489'), Decimal('2.572352435999999835436769706'), Decimal('2.585086853999999834622100249'), Decimal('2.597821271999999833807430792')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5402, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5468835999999997}, controller_id=None),\n ExecutorInfo(id='8R8K54juRqjHAAfFMAWzFJWdmFwKbrYvPSKuk3NV2KFp', timestamp=1704689280000.0, type='dca_executor', close_timestamp=1704702060000.0, close_type=, status=, config=DCAExecutorConfig(id='8R8K54juRqjHAAfFMAWzFJWdmFwKbrYvPSKuk3NV2KFp', type='dca_executor', timestamp=1704689280000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.543802200000000165119225237'), Decimal('2.538714595600000164788986787'), Decimal('2.523451782400000163798271435'), Decimal('2.518364178000000163468032985'), Decimal('2.505645167000000162642436858'), Decimal('2.492926156000000161816840732')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.03180270840338129956226254080320359207689762115478515625'), net_pnl_quote=Decimal('2.26859319944119963707862552837468683719635009765625'), cum_fees_quote=Decimal('0.042800000000000004707345624410663731396198272705078125'), filled_amount_quote=Decimal('71.333333333333342807236476801335811614990234375'), is_active=False, is_trading=False, custom_info={'close_price': 2.6193, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.5404127279097466}, controller_id=None),\n ExecutorInfo(id='GeEK5CmRaXtvtciTrB1oXgcxPVrxth9z4cQL3RHum4J', timestamp=1704689820000.0, type='dca_executor', close_timestamp=1704702060000.0, close_type=, status=, config=DCAExecutorConfig(id='GeEK5CmRaXtvtciTrB1oXgcxPVrxth9z4cQL3RHum4J', type='dca_executor', timestamp=1704689820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.547083999999999814995238489'), Decimal('2.552178167999999814625228966'), Decimal('2.567460671999999813515200397'), Decimal('2.572554839999999813145190874'), Decimal('2.585290259999999812220167066'), Decimal('2.598025679999999811295143259')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.01393955986358939337643736422478468739427626132965087890625'), net_pnl_quote=Decimal('-7.0570681280833635895533006987534463405609130859375'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.6193, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5790827684475377}, controller_id=None),\n ExecutorInfo(id='8j1xtjy3gge4RVUWPZp6hxKkxF8o7XZ1q5dQp57XBDRc', timestamp=1704705660000.0, type='dca_executor', close_timestamp=1704705780000.0, close_type=, status=, config=DCAExecutorConfig(id='8j1xtjy3gge4RVUWPZp6hxKkxF8o7XZ1q5dQp57XBDRc', type='dca_executor', timestamp=1704705660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.593877399999999778687027884'), Decimal('2.599065154799999778244401940'), Decimal('2.614628419199999776916524107'), Decimal('2.619816173999999776473898163'), Decimal('2.632785560999999775367333302'), Decimal('2.645754947999999774260768442')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.584, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5938773999999998}, controller_id=None),\n ExecutorInfo(id='2b1fPkxhsX12NUHBMNT3wLjSoe9s8pNd6v9bA7dqAZVs', timestamp=1704705840000.0, type='dca_executor', close_timestamp=1704705960000.0, close_type=, status=, config=DCAExecutorConfig(id='2b1fPkxhsX12NUHBMNT3wLjSoe9s8pNd6v9bA7dqAZVs', type='dca_executor', timestamp=1704705840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.587865399999999995835743559'), Decimal('2.593041130799999995827415046'), Decimal('2.608568323199999995802429507'), Decimal('2.613744053999999995794100995'), Decimal('2.626683380999999995773279712'), Decimal('2.639622707999999995752458430')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5827, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5878654}, controller_id=None),\n ExecutorInfo(id='HqSnX5YCHgmhwzdtJR9FNJfrYoUoAeX4bwMJt5mCWpof', timestamp=1704706020000.0, type='dca_executor', close_timestamp=1704706140000.0, close_type=, status=, config=DCAExecutorConfig(id='HqSnX5YCHgmhwzdtJR9FNJfrYoUoAeX4bwMJt5mCWpof', type='dca_executor', timestamp=1704706020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428569'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.597684999999999804317883655'), Decimal('2.602880369999999803926519422'), Decimal('2.618466479999999802752426724'), Decimal('2.623661849999999802361062492'), Decimal('2.636650274999999801382651910'), Decimal('2.649638699999999800404241328')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5964, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.597685}, controller_id=None),\n ExecutorInfo(id='32FPS6xMYR3nw59iew4KYgbfao7Xs5XRVS1Aa6C4gw3Y', timestamp=1704706200000.0, type='dca_executor', close_timestamp=1704706320000.0, close_type=, status=, config=DCAExecutorConfig(id='32FPS6xMYR3nw59iew4KYgbfao7Xs5XRVS1Aa6C4gw3Y', type='dca_executor', timestamp=1704706200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.599187999999999861275051803'), Decimal('2.604386375999999860997601907'), Decimal('2.619981503999999860165252217'), Decimal('2.625179879999999859887802321'), Decimal('2.638175819999999859194177580'), Decimal('2.651171759999999858500552839')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5798, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.599188}, controller_id=None),\n ExecutorInfo(id='8huNA31NKhYHdf5gsRbGDDmhn5vaTxCgEd3Z5nYnttMm', timestamp=1704706380000.0, type='dca_executor', close_timestamp=1704706500000.0, close_type=, status=, config=DCAExecutorConfig(id='8huNA31NKhYHdf5gsRbGDDmhn5vaTxCgEd3Z5nYnttMm', type='dca_executor', timestamp=1704706380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.584959600000000093374666331'), Decimal('2.590129519200000093561415664'), Decimal('2.605639276800000094121663662'), Decimal('2.610809196000000094308412994'), Decimal('2.623733994000000094775286326'), Decimal('2.636658792000000095242159658')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5798, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5849596}, controller_id=None),\n ExecutorInfo(id='De9UcR7aXprEY1KAvtegjV8T8NrwnNxULb2ob5xwYPRk', timestamp=1704706560000.0, type='dca_executor', close_timestamp=1704715260000.0, close_type=, status=, config=DCAExecutorConfig(id='De9UcR7aXprEY1KAvtegjV8T8NrwnNxULb2ob5xwYPRk', type='dca_executor', timestamp=1704706560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.578647000000000121140993069'), Decimal('2.583804294000000121383275055'), Decimal('2.599276176000000122110121014'), Decimal('2.604433470000000122352403000'), Decimal('2.617326705000000122958107965'), Decimal('2.630219940000000123563812930')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0142964864358016245649007913698369520716369152069091796875'), net_pnl_quote=Decimal('-7.23776645439166532725039360229857265949249267578125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.6533, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.611042291345295}, controller_id=None),\n ExecutorInfo(id='EYhv3pSLBn232afgsFbAUo1wQRQXPsrK5YCXsG5Hgxsf', timestamp=1704702060000.0, type='dca_executor', close_timestamp=1704715380000.0, close_type=, status=, config=DCAExecutorConfig(id='EYhv3pSLBn232afgsFbAUo1wQRQXPsrK5YCXsG5Hgxsf', type='dca_executor', timestamp=1704702060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.614061399999999961598380616'), Decimal('2.608833277199999961675183855'), Decimal('2.593148908799999961905593571'), Decimal('2.587920785999999961982396810'), Decimal('2.574850478999999962174404907'), Decimal('2.561780171999999962366413004')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.031266794892132181160615544968095491640269756317138671875'), net_pnl_quote=Decimal('15.4376077542415490739813321852125227451324462890625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6591, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.581428177020543}, controller_id=None),\n ExecutorInfo(id='8Q1WyWPbh3eu7aN3LSTYuyeenA6EYKtQ1fEVyPbiyj7p', timestamp=1704715380000.0, type='dca_executor', close_timestamp=1704715500000.0, close_type=, status=, config=DCAExecutorConfig(id='8Q1WyWPbh3eu7aN3LSTYuyeenA6EYKtQ1fEVyPbiyj7p', type='dca_executor', timestamp=1704715380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.653781800000000019035577281'), Decimal('2.648474236400000018997506126'), Decimal('2.632551545600000018883292663'), Decimal('2.627243982000000018845221508'), Decimal('2.613975073000000018750043622'), Decimal('2.600706164000000018654865735')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6544, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6537818}, controller_id=None),\n ExecutorInfo(id='97LTY8tKkyYFV9GoQwyx2SgksNykmg3EX7YQYU959Caq', timestamp=1704715560000.0, type='dca_executor', close_timestamp=1704715680000.0, close_type=, status=, config=DCAExecutorConfig(id='97LTY8tKkyYFV9GoQwyx2SgksNykmg3EX7YQYU959Caq', type='dca_executor', timestamp=1704715560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.643202999999999854519795728'), Decimal('2.637916593999999854810756137'), Decimal('2.622057375999999855683637362'), Decimal('2.616770969999999855974597771'), Decimal('2.603554954999999856701998792'), Decimal('2.590338939999999857429399813')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.68, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6432029999999997}, controller_id=None),\n ExecutorInfo(id='3eQbCsm5KwvXkpzvTDmeMzyUow4MbAvMDDRGZPeG31QB', timestamp=1704715740000.0, type='dca_executor', close_timestamp=1704715860000.0, close_type=, status=, config=DCAExecutorConfig(id='3eQbCsm5KwvXkpzvTDmeMzyUow4MbAvMDDRGZPeG31QB', type='dca_executor', timestamp=1704715740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571434'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.671246800000000089947013726'), Decimal('2.665904306400000089767119699'), Decimal('2.649876825600000089227437616'), Decimal('2.644534332000000089047543589'), Decimal('2.631178098000000088597808520'), Decimal('2.617821864000000088148073451')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6752, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6712468}, controller_id=None),\n ExecutorInfo(id='5B97U9nwGaQPdTFBXNuS34mZhoXoe72k59CeCJ8VoYrT', timestamp=1704715920000.0, type='dca_executor', close_timestamp=1704716040000.0, close_type=, status=, config=DCAExecutorConfig(id='5B97U9nwGaQPdTFBXNuS34mZhoXoe72k59CeCJ8VoYrT', type='dca_executor', timestamp=1704715920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.678332599999999974359889132'), Decimal('2.672975934799999974411169354'), Decimal('2.656905939199999974565010019'), Decimal('2.651549273999999974616290241'), Decimal('2.638157610999999974744490795'), Decimal('2.624765947999999974872691349')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6862, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6783326}, controller_id=None),\n ExecutorInfo(id='FhWZ74Bzi91g2UoE7M5jzL2teetzmCoc4gZoBMyR41Ew', timestamp=1704718860000.0, type='dca_executor', close_timestamp=1704718980000.0, close_type=, status=, config=DCAExecutorConfig(id='FhWZ74Bzi91g2UoE7M5jzL2teetzmCoc4gZoBMyR41Ew', type='dca_executor', timestamp=1704718860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.688866999999999996551904023'), Decimal('2.694244733999999996545007831'), Decimal('2.710377935999999996524319255'), Decimal('2.715755669999999996517423063'), Decimal('2.729200004999999996500182583'), Decimal('2.742644339999999996482942103')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6852, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.688867}, controller_id=None),\n ExecutorInfo(id='3HyKvwEjujjtwWaiwioTuDTjzCUPuEhs4rjS63vXDSAC', timestamp=1704719040000.0, type='dca_executor', close_timestamp=1704719160000.0, close_type=, status=, config=DCAExecutorConfig(id='3HyKvwEjujjtwWaiwioTuDTjzCUPuEhs4rjS63vXDSAC', type='dca_executor', timestamp=1704719040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000000'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.690570400000000031438202041'), Decimal('2.695951540800000031501078445'), Decimal('2.712094963200000031689707657'), Decimal('2.717476104000000031752584061'), Decimal('2.730928956000000031909775072'), Decimal('2.744381808000000032066966082')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6749, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6905704}, controller_id=None),\n ExecutorInfo(id='FekwuF5EBVmzpQ9epKjtDvxqvhWoHqN83rszFotS7gq8', timestamp=1704719220000.0, type='dca_executor', close_timestamp=1704719340000.0, close_type=, status=, config=DCAExecutorConfig(id='FekwuF5EBVmzpQ9epKjtDvxqvhWoHqN83rszFotS7gq8', type='dca_executor', timestamp=1704719220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000006'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.682053399999999857006711955'), Decimal('2.687417506799999856720725379'), Decimal('2.703509827199999855862765651'), Decimal('2.708873933999999855576779075'), Decimal('2.722284200999999854861812634'), Decimal('2.735694467999999854146846194')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6757, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6820534}, controller_id=None),\n ExecutorInfo(id='5AJcoXDDKwtcgfznrRCQwBkLwnaQtNtqAuSGkA3PSSps', timestamp=1704716100000.0, type='dca_executor', close_timestamp=1704729120000.0, close_type=, status=, config=DCAExecutorConfig(id='5AJcoXDDKwtcgfznrRCQwBkLwnaQtNtqAuSGkA3PSSps', type='dca_executor', timestamp=1704716100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.694001199999999799915309517'), Decimal('2.688613197599999800315478898'), Decimal('2.672449190399999801515987041'), Decimal('2.667061187999999801916156422'), Decimal('2.653591181999999802916579874'), Decimal('2.640121175999999803917003327')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0371008297154550581442578049973235465586185455322265625'), net_pnl_quote=Decimal('13.1239768352979950805092812515795230865478515625'), cum_fees_quote=Decimal('0.212242857142857122010326520467060618102550506591796875'), filled_amount_quote=Decimal('353.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.7631, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.668383981212223}, controller_id=None),\n ExecutorInfo(id='52cQMids319G91TFaaraxaQTiMbDRbUWuVAX2iUUph4a', timestamp=1704719400000.0, type='dca_executor', close_timestamp=1704729120000.0, close_type=, status=, config=DCAExecutorConfig(id='52cQMids319G91TFaaraxaQTiMbDRbUWuVAX2iUUph4a', type='dca_executor', timestamp=1704719400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.680750799999999777978673675'), Decimal('2.686112301599999777534631022'), Decimal('2.702196806399999776202503064'), Decimal('2.707558307999999775758460412'), Decimal('2.720962061999999774648353780'), Decimal('2.734365815999999773538247148')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0160133022677683110879254257952197804115712642669677734375'), net_pnl_quote=Decimal('-8.1069249076085139193992290529422461986541748046875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.7631, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.714428811449466}, controller_id=None),\n ExecutorInfo(id='FsyawCzju9JH9hK6GSm4h4b6sgHP3Mf6UUEkoxdqPcVB', timestamp=1704732720000.0, type='dca_executor', close_timestamp=1704732840000.0, close_type=, status=, config=DCAExecutorConfig(id='FsyawCzju9JH9hK6GSm4h4b6sgHP3Mf6UUEkoxdqPcVB', type='dca_executor', timestamp=1704732720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.726341799999999874095683859'), Decimal('2.731794483599999873843875227'), Decimal('2.748152534399999873088449330'), Decimal('2.753605217999999872836640698'), Decimal('2.767236926999999872207119117'), Decimal('2.780868635999999871577597536')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7182, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7263417999999997}, controller_id=None),\n ExecutorInfo(id='GmsQQ35x3ihN7XKvFDZ6kaDDUmcSgbCubBjivx9vgWSa', timestamp=1704732900000.0, type='dca_executor', close_timestamp=1704733020000.0, close_type=, status=, config=DCAExecutorConfig(id='GmsQQ35x3ihN7XKvFDZ6kaDDUmcSgbCubBjivx9vgWSa', type='dca_executor', timestamp=1704732900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.724538200000000072733515043'), Decimal('2.729987276400000072878982073'), Decimal('2.746334505600000073315383163'), Decimal('2.751783582000000073460850193'), Decimal('2.765406273000000073824517769'), Decimal('2.779028964000000074188185344')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7206, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7245382}, controller_id=None),\n ExecutorInfo(id='HjqjdmxRmec1vZz1C8RgWZL9HfQqN98Wku5P8uFkCRdE', timestamp=1704733080000.0, type='dca_executor', close_timestamp=1704733200000.0, close_type=, status=, config=DCAExecutorConfig(id='HjqjdmxRmec1vZz1C8RgWZL9HfQqN98Wku5P8uFkCRdE', type='dca_executor', timestamp=1704733080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.726041200000000129690683192'), Decimal('2.731493282400000129950064558'), Decimal('2.747849529600000130728208658'), Decimal('2.753301612000000130987590024'), Decimal('2.766931818000000131636043440'), Decimal('2.780562024000000132284496856')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7163, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7260412}, controller_id=None),\n ExecutorInfo(id='6eSjkcq8M5GNyGjT31kgrqtxjJcWtX7Woao3M57tkhwH', timestamp=1704733260000.0, type='dca_executor', close_timestamp=1704733380000.0, close_type=, status=, config=DCAExecutorConfig(id='6eSjkcq8M5GNyGjT31kgrqtxjJcWtX7Woao3M57tkhwH', type='dca_executor', timestamp=1704733260000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.721732599999999936748308614'), Decimal('2.727176065199999936621805231'), Decimal('2.743506460799999936242295083'), Decimal('2.748949925999999936115791700'), Decimal('2.762558588999999935799533243'), Decimal('2.776167251999999935483274786')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7209, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7217325999999997}, controller_id=None),\n ExecutorInfo(id='Dw7PUBmAGTWX3x935UwmoQBy5niXejvyfwzNrv1VHbCa', timestamp=1704733440000.0, type='dca_executor', close_timestamp=1704733560000.0, close_type=, status=, config=DCAExecutorConfig(id='Dw7PUBmAGTWX3x935UwmoQBy5niXejvyfwzNrv1VHbCa', type='dca_executor', timestamp=1704733440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.731953000000000146066096717'), Decimal('2.737416906000000146358228910'), Decimal('2.753808624000000147234625491'), Decimal('2.759272530000000147526757684'), Decimal('2.772932295000000148257088168'), Decimal('2.786592060000000148987418651')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7261, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7319530000000003}, controller_id=None),\n ExecutorInfo(id='4ndh1SBPoBRPEeBQ9Ri68oq2bHpP5qaSzYH67wq7EWAm', timestamp=1704733620000.0, type='dca_executor', close_timestamp=1704733740000.0, close_type=, status=, config=DCAExecutorConfig(id='4ndh1SBPoBRPEeBQ9Ri68oq2bHpP5qaSzYH67wq7EWAm', type='dca_executor', timestamp=1704733620000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.739067200000000030016289453'), Decimal('2.744545334400000030076322032'), Decimal('2.760979737600000030256419769'), Decimal('2.766457872000000030316452348'), Decimal('2.780153208000000030466533795'), Decimal('2.793848544000000030616615242')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7268, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7390672}, controller_id=None),\n ExecutorInfo(id='3YAt8KaTTqtan5LbSyKeo1AcvJTuVeMrgV9LSwYr3hvt', timestamp=1704733800000.0, type='dca_executor', close_timestamp=1704733920000.0, close_type=, status=, config=DCAExecutorConfig(id='3YAt8KaTTqtan5LbSyKeo1AcvJTuVeMrgV9LSwYr3hvt', type='dca_executor', timestamp=1704733800000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.737764599999999950988251173'), Decimal('2.743240129199999950890227675'), Decimal('2.759666716799999950596157182'), Decimal('2.765142245999999950498133685'), Decimal('2.778831068999999950253074941'), Decimal('2.792519891999999950008016196')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7303, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7377645999999998}, controller_id=None),\n ExecutorInfo(id='A1CAebyAraYN1Gyvf2vCYBRrNDQMh7H6Pw1WUwc6Lic2', timestamp=1704733980000.0, type='dca_executor', close_timestamp=1704734100000.0, close_type=, status=, config=DCAExecutorConfig(id='A1CAebyAraYN1Gyvf2vCYBRrNDQMh7H6Pw1WUwc6Lic2', type='dca_executor', timestamp=1704733980000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.747283600000000015065390601'), Decimal('2.752778167200000015095521382'), Decimal('2.769261868800000015185913726'), Decimal('2.774756436000000015216044507'), Decimal('2.788492854000000015291371460'), Decimal('2.802229272000000015366698413')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7409, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7472836}, controller_id=None),\n ExecutorInfo(id='BsDD4chiLzSMDvrCwdPQU6DwkRfdkpTghtr8VGTaDHei', timestamp=1704734160000.0, type='dca_executor', close_timestamp=1704734280000.0, close_type=, status=, config=DCAExecutorConfig(id='BsDD4chiLzSMDvrCwdPQU6DwkRfdkpTghtr8VGTaDHei', type='dca_executor', timestamp=1704734160000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.732153400000000123995226585'), Decimal('2.737617706800000124243217038'), Decimal('2.754010627200000124987188398'), Decimal('2.759474934000000125235178851'), Decimal('2.773135701000000125855154984'), Decimal('2.786796468000000126475131117')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7317, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7321534}, controller_id=None),\n ExecutorInfo(id='GyjEp5KznE7beSYpYQM9casGo4Hg4pFt6UyuLWdS9xVk', timestamp=1704734340000.0, type='dca_executor', close_timestamp=1704734460000.0, close_type=, status=, config=DCAExecutorConfig(id='GyjEp5KznE7beSYpYQM9casGo4Hg4pFt6UyuLWdS9xVk', type='dca_executor', timestamp=1704734340000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.737163400000000017200861567'), Decimal('2.742637726800000017235263290'), Decimal('2.759060707200000017338468460'), Decimal('2.764535034000000017372870183'), Decimal('2.778220851000000017458874491'), Decimal('2.791906668000000017544878798')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7322, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7371634}, controller_id=None),\n ExecutorInfo(id='A9jHkiQ4AQyxuz7Snq3oQBMDJwEhe3DeYwyAeYDhPWUa', timestamp=1704734520000.0, type='dca_executor', close_timestamp=1704738720000.0, close_type=, status=, config=DCAExecutorConfig(id='A9jHkiQ4AQyxuz7Snq3oQBMDJwEhe3DeYwyAeYDhPWUa', type='dca_executor', timestamp=1704734520000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428574'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.730349799999999877655669499'), Decimal('2.735810499599999877410980838'), Decimal('2.752192598399999876676914855'), Decimal('2.757653297999999876432226194'), Decimal('2.771305046999999875820504541'), Decimal('2.784956795999999875208782889')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0150632936348847508434278807953887735493481159210205078125'), net_pnl_quote=Decimal('-7.62597172758462971131621088716201484203338623046875'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.8107, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.7646509188602266}, controller_id=None),\n ExecutorInfo(id='FK9NACu5Lhv5DWmG6p5LAEhe9o3MudixhVK9sWWcqvro', timestamp=1704729120000.0, type='dca_executor', close_timestamp=1704738900000.0, close_type=, status=, config=DCAExecutorConfig(id='FK9NACu5Lhv5DWmG6p5LAEhe9o3MudixhVK9sWWcqvro', type='dca_executor', timestamp=1704729120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571435'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.757573800000000111217061949'), Decimal('2.752058652400000110994627825'), Decimal('2.735513209600000110327325453'), Decimal('2.729998062000000110104891330'), Decimal('2.716210193000000109548806020'), Decimal('2.702422324000000108992720710')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0325987909926984398278904109247378073632717132568359375'), net_pnl_quote=Decimal('11.5314342328219208155815067584626376628875732421875'), cum_fees_quote=Decimal('0.212242857142857122010326520467060618102550506591796875'), filled_amount_quote=Decimal('353.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.8183, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.731352070270243}, controller_id=None),\n ExecutorInfo(id='J9gmjwwFYxjczMgFy3kuXkwdb15wLdU9HiXPs2SNBD26', timestamp=1704738900000.0, type='dca_executor', close_timestamp=1704739020000.0, close_type=, status=, config=DCAExecutorConfig(id='J9gmjwwFYxjczMgFy3kuXkwdb15wLdU9HiXPs2SNBD26', type='dca_executor', timestamp=1704738900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571434'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000001')], prices=[Decimal('2.812663399999999805583332513'), Decimal('2.807038073199999805972165848'), Decimal('2.790162092799999807138665853'), Decimal('2.784536765999999807527499188'), Decimal('2.770473448999999808499582525'), Decimal('2.756410131999999809471665863')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8156, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.8126634}, controller_id=None),\n ExecutorInfo(id='Dq4tJruPAM5PgyQiXyVhwjyFeBWVFrPTSPbQ9TfD21KR', timestamp=1704742320000.0, type='dca_executor', close_timestamp=1704742440000.0, close_type=, status=, config=DCAExecutorConfig(id='Dq4tJruPAM5PgyQiXyVhwjyFeBWVFrPTSPbQ9TfD21KR', type='dca_executor', timestamp=1704742320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428570'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.845780200000000069178733574'), Decimal('2.851471760400000069317091041'), Decimal('2.868546441600000069732163443'), Decimal('2.874238002000000069870520910'), Decimal('2.888466903000000070216414578'), Decimal('2.902695804000000070562308245')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.837, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8457802}, controller_id=None),\n ExecutorInfo(id='6JWbJcQ8i7uW8wQYcicMRgUDuXew6CWguLXnyWpmv1Vc', timestamp=1704742500000.0, type='dca_executor', close_timestamp=1704742620000.0, close_type=, status=, config=DCAExecutorConfig(id='6JWbJcQ8i7uW8wQYcicMRgUDuXew6CWguLXnyWpmv1Vc', type='dca_executor', timestamp=1704742500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.844577800000000201603954363'), Decimal('2.850266955600000202007162272'), Decimal('2.867334422400000203216785998'), Decimal('2.873023578000000203619993907'), Decimal('2.887246467000000204628013678'), Decimal('2.901469356000000205636033450')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8418, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8445778}, controller_id=None),\n ExecutorInfo(id='AXzrWzgf6CQRToN7LyPX4zG5jgbnxQ9GirJX3fvcQwYk', timestamp=1704742680000.0, type='dca_executor', close_timestamp=1704742800000.0, close_type=, status=, config=DCAExecutorConfig(id='AXzrWzgf6CQRToN7LyPX4zG5jgbnxQ9GirJX3fvcQwYk', type='dca_executor', timestamp=1704742680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.851291200000000129695887362'), Decimal('2.856993782400000129955279137'), Decimal('2.874101529600000130733454461'), Decimal('2.879804112000000130992846236'), Decimal('2.894060568000000131641325672'), Decimal('2.908317024000000132289805109')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8277, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8512912}, controller_id=None),\n ExecutorInfo(id='4Q8mvfckdKqpjuaadsfBwBjQd5YUEpb5bA4k68uqFBqw', timestamp=1704742860000.0, type='dca_executor', close_timestamp=1704742980000.0, close_type=, status=, config=DCAExecutorConfig(id='4Q8mvfckdKqpjuaadsfBwBjQd5YUEpb5bA4k68uqFBqw', type='dca_executor', timestamp=1704742860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000003'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.840970600000000153902228460'), Decimal('2.846652541200000154210032917'), Decimal('2.863698364800000155133446288'), Decimal('2.869380306000000155441250745'), Decimal('2.883585159000000156210761887'), Decimal('2.897790012000000156980273029')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.8384, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8409706000000003}, controller_id=None),\n ExecutorInfo(id='EJxBAdHDXJd7ksisoZsioCMQJnDfReig2dCiC9q8cmZv', timestamp=1704743040000.0, type='dca_executor', close_timestamp=1704760080000.0, close_type=, status=, config=DCAExecutorConfig(id='EJxBAdHDXJd7ksisoZsioCMQJnDfReig2dCiC9q8cmZv', type='dca_executor', timestamp=1704743040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.843876400000000056363305688'), Decimal('2.849564152800000056476032299'), Decimal('2.866627411200000056814212134'), Decimal('2.872315164000000056926938745'), Decimal('2.886534546000000057208755273'), Decimal('2.900753928000000057490571802')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.03310010250620980254421255040142568759620189666748046875'), net_pnl_quote=Decimal('7.9345674293457211234681381029076874256134033203125'), cum_fees_quote=Decimal('0.1438285714285714167459673262783326208591461181640625'), filled_amount_quote=Decimal('239.71428571428572240620269440114498138427734375'), is_active=False, is_trading=False, custom_info={'close_price': 2.7714, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.8632536269606677}, controller_id=None),\n ExecutorInfo(id='F9C3hrsEfT8BrLGJPCr2RZRJe9cTLcAeFqcRE3qFz9Uz', timestamp=1704739080000.0, type='dca_executor', close_timestamp=1704773940000.0, close_type=, status=, config=DCAExecutorConfig(id='F9C3hrsEfT8BrLGJPCr2RZRJe9cTLcAeFqcRE3qFz9Uz', type='dca_executor', timestamp=1704739080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714285'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.817453800000000164398687719'), Decimal('2.811818892400000164069890344'), Decimal('2.794914169600000163083498217'), Decimal('2.789279262000000162754700842'), Decimal('2.775191993000000161932707403'), Decimal('2.761104724000000161110713965')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.01522397459528530838657189860896323807537555694580078125'), net_pnl_quote=Decimal('-7.5166562186293202074693908798508346080780029296875'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.734, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.782281482284082}, controller_id=None),\n ExecutorInfo(id='8erKNH4PxGxGCw1WmjCR3yrfkoJh52Vzv2RKhGF1T2Mt', timestamp=1704777540000.0, type='dca_executor', close_timestamp=1704777660000.0, close_type=, status=, config=DCAExecutorConfig(id='8erKNH4PxGxGCw1WmjCR3yrfkoJh52Vzv2RKhGF1T2Mt', type='dca_executor', timestamp=1704777540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.758871200000000189929511008'), Decimal('2.753353457600000189549651986'), Decimal('2.736800230400000188410074920'), Decimal('2.731282488000000188030215898'), Decimal('2.717488132000000187080568343'), Decimal('2.703693776000000186130920788')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7653, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7588712}, controller_id=None),\n ExecutorInfo(id='Db2mmQaSW7peRfaR56jFfeF6rnkR2QKSJoSeQm996Uui', timestamp=1704760080000.0, type='dca_executor', close_timestamp=1704791700000.0, close_type=, status=, config=DCAExecutorConfig(id='Db2mmQaSW7peRfaR56jFfeF6rnkR2QKSJoSeQm996Uui', type='dca_executor', timestamp=1704760080000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.776942799999999863418329026'), Decimal('2.782496685599999863145165684'), Decimal('2.799158342399999862325675658'), Decimal('2.804712227999999862052512316'), Decimal('2.818596941999999861369603961'), Decimal('2.832481655999999860686695607')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.036861732644866140873318727244623005390167236328125'), net_pnl_quote=Decimal('2.6364915444089973561858641915023326873779296875'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 2.686, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.780647855506791}, controller_id=None),\n ExecutorInfo(id='CboGuSxy9PYAfXEGh7baRVoyeNx1tSZ4GDNDGpau9rwA', timestamp=1704777720000.0, type='dca_executor', close_timestamp=1704792000000.0, close_type=, status=, config=DCAExecutorConfig(id='CboGuSxy9PYAfXEGh7baRVoyeNx1tSZ4GDNDGpau9rwA', type='dca_executor', timestamp=1704777720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.760767400000000202693621609'), Decimal('2.755245865200000202288234366'), Decimal('2.738681260800000201072072636'), Decimal('2.733159726000000200666685393'), Decimal('2.719355889000000199653217285'), Decimal('2.705552052000000198639749177')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0159798523983966135608714154159315512515604496002197265625'), net_pnl_quote=Decimal('-7.88986188537025245892664315761066973209381103515625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6789, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7263027396983652}, controller_id=None),\n ExecutorInfo(id='8To54dk9VXwfTPamAiDBLhK1WhpEZztGaU8d7eQEkyrC', timestamp=1704795600000.0, type='dca_executor', close_timestamp=1704795720000.0, close_type=, status=, config=DCAExecutorConfig(id='8To54dk9VXwfTPamAiDBLhK1WhpEZztGaU8d7eQEkyrC', type='dca_executor', timestamp=1704795600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.692903400000000142421112404'), Decimal('2.687517593200000142136270179'), Decimal('2.671360172800000141281743505'), Decimal('2.665974366000000140996901280'), Decimal('2.652509849000000140284795718'), Decimal('2.639045332000000139572690156')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7007, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6929034}, controller_id=None),\n ExecutorInfo(id='8q4UCiPfCpewkvBfag2jQzqi5XVumvMGGuyi5pD4B8Wy', timestamp=1704795780000.0, type='dca_executor', close_timestamp=1704795900000.0, close_type=, status=, config=DCAExecutorConfig(id='8q4UCiPfCpewkvBfag2jQzqi5XVumvMGGuyi5pD4B8Wy', type='dca_executor', timestamp=1704795780000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.697793599999999825443530721'), Decimal('2.692398012799999825792643660'), Decimal('2.676211251199999826839982475'), Decimal('2.670815663999999827189095414'), Decimal('2.657326695999999828061877760'), Decimal('2.643837727999999828934660107')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.703, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6977936}, controller_id=None),\n ExecutorInfo(id='Dpdo7xPAZzdJssPVVUwHF1UDc72boCLWFwH72LCu9vxM', timestamp=1704795960000.0, type='dca_executor', close_timestamp=1704796080000.0, close_type=, status=, config=DCAExecutorConfig(id='Dpdo7xPAZzdJssPVVUwHF1UDc72boCLWFwH72LCu9vxM', type='dca_executor', timestamp=1704795960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.700288600000000215460334296'), Decimal('2.694888022800000215029413627'), Decimal('2.678686291200000213736651622'), Decimal('2.673285714000000213305730953'), Decimal('2.659784271000000212228429282'), Decimal('2.646282828000000211151127610')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7026, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7002886000000004}, controller_id=None),\n ExecutorInfo(id='HwWrDYT95sTwy2aDUPT95u9b8kFw7VPitJmXYV9UdT3E', timestamp=1704796140000.0, type='dca_executor', close_timestamp=1704796260000.0, close_type=, status=, config=DCAExecutorConfig(id='HwWrDYT95sTwy2aDUPT95u9b8kFw7VPitJmXYV9UdT3E', type='dca_executor', timestamp=1704796140000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714291'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.694500199999999966558876519'), Decimal('2.689111199599999966625758766'), Decimal('2.672944198399999966826405507'), Decimal('2.667555197999999966893287754'), Decimal('2.654082696999999967060493371'), Decimal('2.640610195999999967227698989')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6995, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6945002}, controller_id=None),\n ExecutorInfo(id='9z1NQSNCtrXwu9s4rAT5jog6XtTAxJDnvCWitgNfR9ef', timestamp=1704796320000.0, type='dca_executor', close_timestamp=1704796440000.0, close_type=, status=, config=DCAExecutorConfig(id='9z1NQSNCtrXwu9s4rAT5jog6XtTAxJDnvCWitgNfR9ef', type='dca_executor', timestamp=1704796320000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.694101000000000010524435490'), Decimal('2.688712798000000010503386619'), Decimal('2.672548192000000010440240006'), Decimal('2.667159990000000010419191135'), Decimal('2.653689485000000010366568958'), Decimal('2.640218980000000010313946780')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6995, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.694101}, controller_id=None),\n ExecutorInfo(id='ARnd45aYRTXR8vwTCs1exrwtMj16zyRAV9qVmaoPS6oA', timestamp=1704796500000.0, type='dca_executor', close_timestamp=1704796620000.0, close_type=, status=, config=DCAExecutorConfig(id='ARnd45aYRTXR8vwTCs1exrwtMj16zyRAV9qVmaoPS6oA', type='dca_executor', timestamp=1704796500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.694101000000000010524435490'), Decimal('2.688712798000000010503386619'), Decimal('2.672548192000000010440240006'), Decimal('2.667159990000000010419191135'), Decimal('2.653689485000000010366568958'), Decimal('2.640218980000000010313946780')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.697, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.694101}, controller_id=None),\n ExecutorInfo(id='AFYF7a4KaS1rCCXLte5DfTPPF5ad6AZfmnXApKMykH2w', timestamp=1704796680000.0, type='dca_executor', close_timestamp=1704796800000.0, close_type=, status=, config=DCAExecutorConfig(id='AFYF7a4KaS1rCCXLte5DfTPPF5ad6AZfmnXApKMykH2w', type='dca_executor', timestamp=1704796680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.692005200000000019743104374'), Decimal('2.686621189600000019703618165'), Decimal('2.670469158400000019585159539'), Decimal('2.665085148000000019545673330'), Decimal('2.651625122000000019446957808'), Decimal('2.638165096000000019348242287')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7045, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6920052}, controller_id=None),\n ExecutorInfo(id='5NvbuRaT1sB4DmrPbDSJjeVrNuE4tdCahWTwu23k685u', timestamp=1704796860000.0, type='dca_executor', close_timestamp=1704796980000.0, close_type=, status=, config=DCAExecutorConfig(id='5NvbuRaT1sB4DmrPbDSJjeVrNuE4tdCahWTwu23k685u', type='dca_executor', timestamp=1704796860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.699090999999999904155979780'), Decimal('2.693692817999999904347667820'), Decimal('2.677498271999999904922731942'), Decimal('2.672100089999999905114419982'), Decimal('2.658604634999999905593640083'), Decimal('2.645109179999999906072860184')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.702, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6990909999999997}, controller_id=None),\n ExecutorInfo(id='2q7EFjNpHBAhmWQHU4Dk6Afk3E3WchKNZXSBs5B6J3bw', timestamp=1704797040000.0, type='dca_executor', close_timestamp=1704797160000.0, close_type=, status=, config=DCAExecutorConfig(id='2q7EFjNpHBAhmWQHU4Dk6Afk3E3WchKNZXSBs5B6J3bw', type='dca_executor', timestamp=1704797040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.696595999999999957340207635'), Decimal('2.691202807999999957425527220'), Decimal('2.675023231999999957681485974'), Decimal('2.669630039999999957766805559'), Decimal('2.656147059999999957980104520'), Decimal('2.642664079999999958193403482')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6982, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.696596}, controller_id=None),\n ExecutorInfo(id='FiNogpiBpB6KTXrDGWAxzwgKMgSMn5zWccajP3f2VBfi', timestamp=1704797220000.0, type='dca_executor', close_timestamp=1704797340000.0, close_type=, status=, config=DCAExecutorConfig(id='FiNogpiBpB6KTXrDGWAxzwgKMgSMn5zWccajP3f2VBfi', type='dca_executor', timestamp=1704797220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.688611999999999950249324199'), Decimal('2.683234775999999950348825551'), Decimal('2.667103103999999950647329605'), Decimal('2.661725879999999950746830957'), Decimal('2.648282819999999950995584336'), Decimal('2.634839759999999951244337715')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6905, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.688612}, controller_id=None),\n ExecutorInfo(id='DyBQ9o5moFXHqtL58eSoLX1MUzWNcHC3i2ZquUJM7hyS', timestamp=1704797400000.0, type='dca_executor', close_timestamp=1704797520000.0, close_type=, status=, config=DCAExecutorConfig(id='DyBQ9o5moFXHqtL58eSoLX1MUzWNcHC3i2ZquUJM7hyS', type='dca_executor', timestamp=1704797400000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.681825599999999811261763849'), Decimal('2.676461948799999811639240321'), Decimal('2.660370995199999812771669738'), Decimal('2.655007343999999813149146211'), Decimal('2.641598215999999814092837391'), Decimal('2.628189087999999815036528572')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6901, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6818256}, controller_id=None),\n ExecutorInfo(id='6uQqdbMBG7TrDhai5JTVwgb6erP38DHEmzxuwwWuee8U', timestamp=1704797580000.0, type='dca_executor', close_timestamp=1704797700000.0, close_type=, status=, config=DCAExecutorConfig(id='6uQqdbMBG7TrDhai5JTVwgb6erP38DHEmzxuwwWuee8U', type='dca_executor', timestamp=1704797580000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.682424400000000188514456823'), Decimal('2.677059551200000188137427909'), Decimal('2.660965004800000187006341168'), Decimal('2.655600156000000186629312255'), Decimal('2.642188034000000185686739971'), Decimal('2.628775912000000184744167687')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6871, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6824244000000004}, controller_id=None),\n ExecutorInfo(id='FEgC3X6FRkbFH52XWQaevSdbMaB8K3TpRUSd5dTRXEuy', timestamp=1704797760000.0, type='dca_executor', close_timestamp=1704797880000.0, close_type=, status=, config=DCAExecutorConfig(id='FEgC3X6FRkbFH52XWQaevSdbMaB8K3TpRUSd5dTRXEuy', type='dca_executor', timestamp=1704797760000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380955'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571433'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.679929399999999798497653247'), Decimal('2.674569541199999798900657941'), Decimal('2.658489964799999800109672021'), Decimal('2.653130105999999800512676715'), Decimal('2.639730458999999801520188448'), Decimal('2.626330811999999802527700182')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6853, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6799294}, controller_id=None),\n ExecutorInfo(id='DmhGg1rnbt2EFzZZotGDWWxE1553Sz9C4f5JaYZxJVKC', timestamp=1704797940000.0, type='dca_executor', close_timestamp=1704798060000.0, close_type=, status=, config=DCAExecutorConfig(id='DmhGg1rnbt2EFzZZotGDWWxE1553Sz9C4f5JaYZxJVKC', type='dca_executor', timestamp=1704797940000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380950'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.680627999999999943158440763'), Decimal('2.675266743999999943272123881'), Decimal('2.659182975999999943613173237'), Decimal('2.653821719999999943726856355'), Decimal('2.640418579999999944011064152'), Decimal('2.627015439999999944295271948')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6884, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.680628}, controller_id=None),\n ExecutorInfo(id='9wHU1YQ9VPuvSidaeeCUU4uyoFqexPaVLHLRyKpV37s1', timestamp=1704798120000.0, type='dca_executor', close_timestamp=1704798240000.0, close_type=, status=, config=DCAExecutorConfig(id='9wHU1YQ9VPuvSidaeeCUU4uyoFqexPaVLHLRyKpV37s1', type='dca_executor', timestamp=1704798120000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.688013200000000016197662656'), Decimal('2.682637173600000016165267331'), Decimal('2.666509094400000016068081355'), Decimal('2.661133068000000016035686029'), Decimal('2.647693002000000015954697716'), Decimal('2.634252936000000015873709403')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6934, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6880132}, controller_id=None),\n ExecutorInfo(id='h9qFHGvxsaoBUZxjBwiC5xXTTRtzSVimVJfmXi8AzXk', timestamp=1704798300000.0, type='dca_executor', close_timestamp=1704798420000.0, close_type=, status=, config=DCAExecutorConfig(id='h9qFHGvxsaoBUZxjBwiC5xXTTRtzSVimVJfmXi8AzXk', type='dca_executor', timestamp=1704798300000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.688711800000000160858450171'), Decimal('2.683334376400000160536733271'), Decimal('2.667202105600000159571582570'), Decimal('2.661824682000000159249865669'), Decimal('2.648381123000000158445573418'), Decimal('2.634937564000000157641281168')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6915, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6887118}, controller_id=None),\n ExecutorInfo(id='HMjbk4mUj7UEGKinfDiGxu1bcVVqkSE9CKfPkAw2kgxs', timestamp=1704798480000.0, type='dca_executor', close_timestamp=1704798600000.0, close_type=, status=, config=DCAExecutorConfig(id='HMjbk4mUj7UEGKinfDiGxu1bcVVqkSE9CKfPkAw2kgxs', type='dca_executor', timestamp=1704798480000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.686715799999999937485213597'), Decimal('2.681342368399999937610243170'), Decimal('2.665222073599999937985331888'), Decimal('2.659848641999999938110361461'), Decimal('2.646415062999999938422935393'), Decimal('2.632981483999999938735509325')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6921, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6867158}, controller_id=None),\n ExecutorInfo(id='Fe8TR1jqxrrSTiW5m4pZJKZ4vVEc6UHz27YJTsMEQW5L', timestamp=1704798660000.0, type='dca_executor', close_timestamp=1704798780000.0, close_type=, status=, config=DCAExecutorConfig(id='Fe8TR1jqxrrSTiW5m4pZJKZ4vVEc6UHz27YJTsMEQW5L', type='dca_executor', timestamp=1704798660000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.686715799999999937485213597'), Decimal('2.681342368399999937610243170'), Decimal('2.665222073599999937985331888'), Decimal('2.659848641999999938110361461'), Decimal('2.646415062999999938422935393'), Decimal('2.632981483999999938735509325')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6921, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6867158}, controller_id=None),\n ExecutorInfo(id='Agi495JReE4EpfqBLEJEZjsu89GLkh9WBKmSrBX9TvTB', timestamp=1704798840000.0, type='dca_executor', close_timestamp=1704798960000.0, close_type=, status=, config=DCAExecutorConfig(id='Agi495JReE4EpfqBLEJEZjsu89GLkh9WBKmSrBX9TvTB', type='dca_executor', timestamp=1704798840000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.686715799999999937485213597'), Decimal('2.681342368399999937610243170'), Decimal('2.665222073599999937985331888'), Decimal('2.659848641999999938110361461'), Decimal('2.646415062999999938422935393'), Decimal('2.632981483999999938735509325')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6971, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6867158}, controller_id=None),\n ExecutorInfo(id='HdpzKPfS24fu4vm8otwAEnBAKgDnUChfk6Nac9fcTUyD', timestamp=1704799020000.0, type='dca_executor', close_timestamp=1704799140000.0, close_type=, status=, config=DCAExecutorConfig(id='HdpzKPfS24fu4vm8otwAEnBAKgDnUChfk6Nac9fcTUyD', type='dca_executor', timestamp=1704799020000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.696096999999999790696640634'), Decimal('2.690704805999999791115247353'), Decimal('2.674528223999999792371067509'), Decimal('2.669136029999999792789674228'), Decimal('2.655655544999999793836191024'), Decimal('2.642175059999999794882707821')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.702, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.696097}, controller_id=None),\n ExecutorInfo(id='6WqixtyNTd64qzHyrBiWH6tKxvxRF1eKBXnoLQfVqUae', timestamp=1704799200000.0, type='dca_executor', close_timestamp=1704799320000.0, close_type=, status=, config=DCAExecutorConfig(id='6WqixtyNTd64qzHyrBiWH6tKxvxRF1eKBXnoLQfVqUae', type='dca_executor', timestamp=1704799200000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.696895400000000145966554122'), Decimal('2.691501609200000145674621014'), Decimal('2.675320236800000144798821689'), Decimal('2.669926446000000144506888581'), Decimal('2.656441969000000143777055810'), Decimal('2.642957492000000143047223040')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7099, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6968954000000003}, controller_id=None),\n ExecutorInfo(id='SffP8V59qViDWk7hH6g9SoUrcoxWbGpEfUHRixhcbJZ', timestamp=1704799380000.0, type='dca_executor', close_timestamp=1704799500000.0, close_type=, status=, config=DCAExecutorConfig(id='SffP8V59qViDWk7hH6g9SoUrcoxWbGpEfUHRixhcbJZ', type='dca_executor', timestamp=1704799380000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571427'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.705777599999999832534414157'), Decimal('2.700366044799999832869345329'), Decimal('2.684131379199999833874138844'), Decimal('2.678719823999999834209070015'), Decimal('2.665190935999999835046397945'), Decimal('2.651662047999999835883725874')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7114, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7057776}, controller_id=None),\n ExecutorInfo(id='Q9vD6rEv5Bvc8P4ZaYUXPugmbk22CnenXGVT7bPBiVC', timestamp=1704799560000.0, type='dca_executor', close_timestamp=1704799680000.0, close_type=, status=, config=DCAExecutorConfig(id='Q9vD6rEv5Bvc8P4ZaYUXPugmbk22CnenXGVT7bPBiVC', type='dca_executor', timestamp=1704799560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.707374400000000099873209702'), Decimal('2.701959651200000099673463283'), Decimal('2.685715404800000099074224024'), Decimal('2.680300656000000098874477605'), Decimal('2.666763784000000098375111556'), Decimal('2.653226912000000097875745508')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7121, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7073744}, controller_id=None),\n ExecutorInfo(id='HuMyKFrnrRPPDrib1HsrUvoPasmjmXqmpM1hV7F1md9x', timestamp=1704799740000.0, type='dca_executor', close_timestamp=1704799860000.0, close_type=, status=, config=DCAExecutorConfig(id='HuMyKFrnrRPPDrib1HsrUvoPasmjmXqmpM1hV7F1md9x', type='dca_executor', timestamp=1704799740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.706675799999999955212422187'), Decimal('2.701262448399999955301997343'), Decimal('2.685022393599999955570722810'), Decimal('2.679609041999999955660297965'), Decimal('2.666075662999999955884235854'), Decimal('2.652542283999999956108173743')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7121, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7066757999999997}, controller_id=None),\n ExecutorInfo(id='CeuvWDiGCfu4o3e5ZD58mm7M3Li128QPuDM7yx7b9o6H', timestamp=1704799920000.0, type='dca_executor', close_timestamp=1704800040000.0, close_type=, status=, config=DCAExecutorConfig(id='CeuvWDiGCfu4o3e5ZD58mm7M3Li128QPuDM7yx7b9o6H', type='dca_executor', timestamp=1704799920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.706675799999999955212422187'), Decimal('2.701262448399999955301997343'), Decimal('2.685022393599999955570722810'), Decimal('2.679609041999999955660297965'), Decimal('2.666075662999999955884235854'), Decimal('2.652542283999999956108173743')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.7077, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.7066757999999997}, controller_id=None),\n ExecutorInfo(id='8PqXdfAi8EGmZh99j8MyNgyq6MhPwxqGak8dBVLZdhYL', timestamp=1704791700000.0, type='dca_executor', close_timestamp=1704804960000.0, close_type=, status=, config=DCAExecutorConfig(id='8PqXdfAi8EGmZh99j8MyNgyq6MhPwxqGak8dBVLZdhYL', type='dca_executor', timestamp=1704791700000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.691371999999999943154721514'), Decimal('2.696754743999999943041030957'), Decimal('2.712902975999999942699959286'), Decimal('2.718285719999999942586268729'), Decimal('2.731742579999999942302042337'), Decimal('2.745199439999999942017815944')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.045790110121999315351271064855609438382089138031005859375'), net_pnl_quote=Decimal('3.2750831144401413297373437671922147274017333984375'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 2.5736, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6949628851451397}, controller_id=None),\n ExecutorInfo(id='GMvKfogWv1dDx9RvzLkSqM3iLribZBMB7PfyLa3EAazf', timestamp=1704800100000.0, type='dca_executor', close_timestamp=1704804960000.0, close_type=, status=, config=DCAExecutorConfig(id='GMvKfogWv1dDx9RvzLkSqM3iLribZBMB7PfyLa3EAazf', type='dca_executor', timestamp=1704800100000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.702384400000000206241665413'), Decimal('2.696979631200000205829182082'), Decimal('2.680765324800000204591732090'), Decimal('2.675360556000000204179248759'), Decimal('2.661848634000000203148040432'), Decimal('2.648336712000000202116832105')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.026780683021526731357742079353556619025766849517822265625'), net_pnl_quote=Decimal('-13.2226434242238060079444039729423820972442626953125'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.5736, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6686485770000483}, controller_id=None),\n ExecutorInfo(id='3n1imgepneMBt9yYv29YzYhPa97FmN9ti6RuvxE3vweU', timestamp=1704804960000.0, type='dca_executor', close_timestamp=1704806220000.0, close_type=, status=, config=DCAExecutorConfig(id='3n1imgepneMBt9yYv29YzYhPa97FmN9ti6RuvxE3vweU', type='dca_executor', timestamp=1704804960000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047623'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.578747199999999887616863868'), Decimal('2.583904694399999887392097596'), Decimal('2.599377177599999886717798779'), Decimal('2.604534671999999886493032507'), Decimal('2.617428407999999885931116826'), Decimal('2.630322143999999885369201145')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.0111603318274736788839884837898352998308837413787841796875'), net_pnl_quote=Decimal('-5.6500508487517340228123430279083549976348876953125'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.6521, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6111437501481447}, controller_id=None),\n ExecutorInfo(id='GRFKuQxgZiPBu1uhsbkHMueWrWr8kZ5RMkvg8BbKmSby', timestamp=1704808560000.0, type='dca_executor', close_timestamp=1704808680000.0, close_type=, status=, config=DCAExecutorConfig(id='GRFKuQxgZiPBu1uhsbkHMueWrWr8kZ5RMkvg8BbKmSby', type='dca_executor', timestamp=1704808560000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.671646000000000045981454755'), Decimal('2.666302708000000045889491845'), Decimal('2.650272832000000045613603117'), Decimal('2.644929540000000045521640207'), Decimal('2.631571310000000045291732934'), Decimal('2.618213080000000045061825660')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6825, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.671646}, controller_id=None),\n ExecutorInfo(id='63HWTZjiQJSYxtFyC47KhmhiWvVchEdjF8osuA6qVygV', timestamp=1704808740000.0, type='dca_executor', close_timestamp=1704808860000.0, close_type=, status=, config=DCAExecutorConfig(id='63HWTZjiQJSYxtFyC47KhmhiWvVchEdjF8osuA6qVygV', type='dca_executor', timestamp=1704808740000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.676236799999999983578558016'), Decimal('2.670884326399999983611400900'), Decimal('2.654826905599999983709929552'), Decimal('2.649474431999999983742772436'), Decimal('2.636093247999999983824879646'), Decimal('2.622712063999999983906986856')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.691, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6762368}, controller_id=None),\n ExecutorInfo(id='EzmZKkPTVBFr7bCKgRzDfzK8byEVZQog2WSBkvhMG3uy', timestamp=1704809820000.0, type='dca_executor', close_timestamp=1704809940000.0, close_type=, status=, config=DCAExecutorConfig(id='EzmZKkPTVBFr7bCKgRzDfzK8byEVZQog2WSBkvhMG3uy', type='dca_executor', timestamp=1704809820000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047624'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.650991400000000163149864451'), Decimal('2.656293382800000163476164180'), Decimal('2.672199331200000164455063367'), Decimal('2.677501314000000164781363096'), Decimal('2.690756271000000165597112418'), Decimal('2.704011228000000166412861740')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6457, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6509914}, controller_id=None),\n ExecutorInfo(id='EtGfXBjb1uNu8yvwHwKTEXBkchsK4hUJJSGcoYHqDS4D', timestamp=1704810000000.0, type='dca_executor', close_timestamp=1704810120000.0, close_type=, status=, config=DCAExecutorConfig(id='EtGfXBjb1uNu8yvwHwKTEXBkchsK4hUJJSGcoYHqDS4D', type='dca_executor', timestamp=1704810000000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.659809000000000081986355204'), Decimal('2.665128618000000082150327914'), Decimal('2.681087472000000082642246046'), Decimal('2.686407090000000082806218756'), Decimal('2.699706135000000083216150532'), Decimal('2.713005180000000083626082308')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6534, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.659809}, controller_id=None),\n ExecutorInfo(id='B83oMVk8kDd8oi5ZuNALSnJ2za4ttwuqtHbxsajEULs8', timestamp=1704810180000.0, type='dca_executor', close_timestamp=1704810300000.0, close_type=, status=, config=DCAExecutorConfig(id='B83oMVk8kDd8oi5ZuNALSnJ2za4ttwuqtHbxsajEULs8', type='dca_executor', timestamp=1704810180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.657003399999999946001148775'), Decimal('2.662317406799999945893151073'), Decimal('2.678259427199999945569157965'), Decimal('2.683573433999999945461160263'), Decimal('2.696858450999999945191166007'), Decimal('2.710143467999999944921171750')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6452, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6570034}, controller_id=None),\n ExecutorInfo(id='HcFZCe5fEewmutCAfD23DB8HCxr5KatWChfgUdY1GmxJ', timestamp=1704810360000.0, type='dca_executor', close_timestamp=1704810480000.0, close_type=, status=, config=DCAExecutorConfig(id='HcFZCe5fEewmutCAfD23DB8HCxr5KatWChfgUdY1GmxJ', type='dca_executor', timestamp=1704810360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.651492399999999885483994987'), Decimal('2.656795384799999885254962977'), Decimal('2.672704339199999884567866947'), Decimal('2.678007323999999884338834937'), Decimal('2.691264785999999883766254912'), Decimal('2.704522247999999883193674887')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6471, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6514924}, controller_id=None),\n ExecutorInfo(id='jxGpC3AKHaDKWLcnDSqioEBTKTNHYQ61hEgYzBMqW9o', timestamp=1704810540000.0, type='dca_executor', close_timestamp=1704810660000.0, close_type=, status=, config=DCAExecutorConfig(id='jxGpC3AKHaDKWLcnDSqioEBTKTNHYQ61hEgYzBMqW9o', type='dca_executor', timestamp=1704810540000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.655400200000000122568109827'), Decimal('2.660711000400000122813246047'), Decimal('2.676643401600000123548654706'), Decimal('2.681954202000000123793790925'), Decimal('2.695231203000000124406631474'), Decimal('2.708508204000000125019472024')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6284, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.6554002000000003}, controller_id=None),\n ExecutorInfo(id='6GYN9meRya8cPxAUtU8h9fbHfjVskTxGGM5Gj7zzthF5', timestamp=1704808920000.0, type='dca_executor', close_timestamp=1704812460000.0, close_type=, status=, config=DCAExecutorConfig(id='6GYN9meRya8cPxAUtU8h9fbHfjVskTxGGM5Gj7zzthF5', type='dca_executor', timestamp=1704808920000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.685617999999999836789985053'), Decimal('2.680246763999999837116405083'), Decimal('2.664133055999999838095665173'), Decimal('2.658761819999999838422085202'), Decimal('2.645333729999999839238135277'), Decimal('2.631905639999999840054185352')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0186607559157423337492520687419528258033096790313720703125'), net_pnl_quote=Decimal('-9.21352608154163732478991732932627201080322265625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.6007, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6520914841225824}, controller_id=None),\n ExecutorInfo(id='HFtoJosWLMDUnhyKGfARQGRAZ2AAVE1dVNvHH1wXvv3i', timestamp=1704816060000.0, type='dca_executor', close_timestamp=1704816180000.0, close_type=, status=, config=DCAExecutorConfig(id='HFtoJosWLMDUnhyKGfARQGRAZ2AAVE1dVNvHH1wXvv3i', type='dca_executor', timestamp=1704816060000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.615558400000000018328050189'), Decimal('2.610327283200000018291394089'), Decimal('2.594633932800000018181425787'), Decimal('2.589402816000000018144769687'), Decimal('2.576325024000000018053129436'), Decimal('2.563247232000000017961489185')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.6158, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.6155584}, controller_id=None),\n ExecutorInfo(id='BihDHDCMjZkxjAye1R85tS2a6zyNp6vze24Tgqkt3qK8', timestamp=1704810720000.0, type='dca_executor', close_timestamp=1704828420000.0, close_type=, status=, config=DCAExecutorConfig(id='BihDHDCMjZkxjAye1R85tS2a6zyNp6vze24Tgqkt3qK8', type='dca_executor', timestamp=1704810720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428572'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047618'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.634759000000000170980792024'), Decimal('2.640028518000000171322753608'), Decimal('2.655837072000000172348638360'), Decimal('2.661106590000000172690599944'), Decimal('2.674280385000000173545503904'), Decimal('2.687454180000000174400407864')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.03315116640521038882116755530660157091915607452392578125'), net_pnl_quote=Decimal('2.3710977114583808855741153820417821407318115234375'), cum_fees_quote=Decimal('0.0429142857142857103713851074644480831921100616455078125'), filled_amount_quote=Decimal('71.5238095238095183958648703992366790771484375'), is_active=False, is_trading=False, custom_info={'close_price': 2.5529, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.638274350889481}, controller_id=None),\n ExecutorInfo(id='8rDwYBtJh2BExy89GgKg9G2Z8MTtmuZxNaXoWM1okVBw', timestamp=1704828420000.0, type='dca_executor', close_timestamp=1704828540000.0, close_type=, status=, config=DCAExecutorConfig(id='8rDwYBtJh2BExy89GgKg9G2Z8MTtmuZxNaXoWM1okVBw', type='dca_executor', timestamp=1704828420000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000002'), Decimal('96.19047619047619047619047622'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.558005800000000169553675266'), Decimal('2.563121811600000169892782617'), Decimal('2.578469846400000170910104668'), Decimal('2.583585858000000171249212019'), Decimal('2.596375887000000172096980395'), Decimal('2.609165916000000172944748771')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.5494, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5580058}, controller_id=None),\n ExecutorInfo(id='71K4zGQBKMpnrX9Np4dmXrJXYbnrYznS9LhUkupkWMPS', timestamp=1704816240000.0, type='dca_executor', close_timestamp=1704828900000.0, close_type=, status=, config=DCAExecutorConfig(id='71K4zGQBKMpnrX9Np4dmXrJXYbnrYznS9LhUkupkWMPS', type='dca_executor', timestamp=1704816240000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.615059399999999851684483188'), Decimal('2.609829281199999851981114222'), Decimal('2.594138924799999852871007322'), Decimal('2.588908805999999853167638356'), Decimal('2.575833508999999853909215940'), Decimal('2.562758211999999854650793524')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('-0.0140859578389018573474356088581771473400294780731201171875'), net_pnl_quote=Decimal('-6.95477399298351972589671277091838419437408447265625'), cum_fees_quote=Decimal('0.29624285714285714110616254401975311338901519775390625'), filled_amount_quote=Decimal('493.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.5434, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.582413718263249}, controller_id=None),\n ExecutorInfo(id='D2QzdDD3b37pP26bWr6ziY5MjdzXpCy4sQ4YZAec11qF', timestamp=1704828600000.0, type='dca_executor', close_timestamp=1704831180000.0, close_type=, status=, config=DCAExecutorConfig(id='D2QzdDD3b37pP26bWr6ziY5MjdzXpCy4sQ4YZAec11qF', type='dca_executor', timestamp=1704828600000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428571'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047619'), Decimal('120.8333333333333333333333333'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.560210199999999926774103819'), Decimal('2.565330620399999926627652027'), Decimal('2.580691881599999926188296650'), Decimal('2.585812301999999926041844857'), Decimal('2.598613352999999925675715376'), Decimal('2.611414403999999925309585895')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0.0346588693957112781163232284598052501678466796875'), net_pnl_quote=Decimal('0.82521117608836380696857304428704082965850830078125'), cum_fees_quote=Decimal('0.01428571428571428526821396332024960429407656192779541015625'), filled_amount_quote=Decimal('23.8095238095238102005168912000954151153564453125'), is_active=False, is_trading=False, custom_info={'close_price': 2.4761, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.5602101999999998}, controller_id=None),\n ExecutorInfo(id='5YkSKnuRkkWK8gdjSm9mUvhd6965ri7TnmdSwBWgpdXd', timestamp=1704831180000.0, type='dca_executor', close_timestamp=1704831300000.0, close_type=, status=, config=DCAExecutorConfig(id='5YkSKnuRkkWK8gdjSm9mUvhd6965ri7TnmdSwBWgpdXd', type='dca_executor', timestamp=1704831180000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000004'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.481052200000000190197428639'), Decimal('2.486014304400000190577823496'), Decimal('2.500900617600000191719008068'), Decimal('2.505862722000000192099402925'), Decimal('2.518267983000000193050390069'), Decimal('2.530673244000000194001377212')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.4022, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.4810522}, controller_id=None),\n ExecutorInfo(id='HDJbm9oZZ1PBg996aTHwyvGVpyfrk4ZXCeFK9S6FQcsT', timestamp=1704832500000.0, type='dca_executor', close_timestamp=1704832620000.0, close_type=, status=, config=DCAExecutorConfig(id='HDJbm9oZZ1PBg996aTHwyvGVpyfrk4ZXCeFK9S6FQcsT', type='dca_executor', timestamp=1704832500000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714287'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.390210000000000017628329352'), Decimal('2.385429580000000017593072693'), Decimal('2.371088320000000017487302717'), Decimal('2.366307900000000017452046058'), Decimal('2.354356850000000017363904412'), Decimal('2.342405800000000017275762765')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.3924, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.39021}, controller_id=None),\n ExecutorInfo(id='EYEaNNAtCmPQ31YxYY9Yga2Y6KuWPppYJ9HyDhGTQ73R', timestamp=1704832680000.0, type='dca_executor', close_timestamp=1704832800000.0, close_type=, status=, config=DCAExecutorConfig(id='EYEaNNAtCmPQ31YxYY9Yga2Y6KuWPppYJ9HyDhGTQ73R', type='dca_executor', timestamp=1704832680000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714289'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.388613200000000193490565237'), Decimal('2.383835973600000193103584107'), Decimal('2.369504294400000191942640715'), Decimal('2.364727068000000191555659585'), Decimal('2.352784002000000190588206758'), Decimal('2.340840936000000189620753932')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.4014, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.3886132}, controller_id=None),\n ExecutorInfo(id='CdtnTNduSiv3C6r8wA2ueV5C8MvrXq1z8RJDAV2PqAXb', timestamp=1704832860000.0, type='dca_executor', close_timestamp=1704832980000.0, close_type=, status=, config=DCAExecutorConfig(id='CdtnTNduSiv3C6r8wA2ueV5C8MvrXq1z8RJDAV2PqAXb', type='dca_executor', timestamp=1704832860000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380952'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619047'), Decimal('139.9999999999999999999999999')], prices=[Decimal('2.396397599999999779363196728'), Decimal('2.391604804799999779804470335'), Decimal('2.377226419199999781128291154'), Decimal('2.372433623999999781569564761'), Decimal('2.360451635999999782672748777'), Decimal('2.348469647999999783775932793')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.4042, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.3963976}, controller_id=None),\n ExecutorInfo(id='Fz2Mqi4kXTiznqEDbbLfpjCmQJGt2NHYhY1cU4k4boQ2', timestamp=1704833040000.0, type='dca_executor', close_timestamp=1704833160000.0, close_type=, status=, config=DCAExecutorConfig(id='Fz2Mqi4kXTiznqEDbbLfpjCmQJGt2NHYhY1cU4k4boQ2', type='dca_executor', timestamp=1704833040000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714288'), Decimal('94.28571428571428571428571431'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.396696999999999967989543215'), Decimal('2.391903605999999968053564129'), Decimal('2.377523423999999968245626869'), Decimal('2.372730029999999968309647783'), Decimal('2.360746544999999968469700067'), Decimal('2.348763059999999968629752351')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.403, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.396697}, controller_id=None),\n ExecutorInfo(id='B6yU5k1aSxv4wrcwZqvox99M47v1f3C8d2pRYNM92Kku', timestamp=1704833220000.0, type='dca_executor', close_timestamp=1704834720000.0, close_type=, status=, config=DCAExecutorConfig(id='B6yU5k1aSxv4wrcwZqvox99M47v1f3C8d2pRYNM92Kku', type='dca_executor', timestamp=1704833220000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714286'), Decimal('94.28571428571428571428571429'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.396497399999999989972322700'), Decimal('2.391704405199999989992378055'), Decimal('2.377325420799999990052544118'), Decimal('2.372532425999999990072599473'), Decimal('2.360549938999999990122737860'), Decimal('2.348567451999999990172876246')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.034594376927727744919227603759281919337809085845947265625'), net_pnl_quote=Decimal('4.91899092886643085620335114072076976299285888671875'), cum_fees_quote=Decimal('0.08531428571428571749901692555795307271182537078857421875'), filled_amount_quote=Decimal('142.19047619047620401033782400190830230712890625'), is_active=False, is_trading=False, custom_info={'close_price': 2.4622, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.385341568164769}, controller_id=None),\n ExecutorInfo(id='GaJxTCNCCy8X7UmQuxvjBSF2PYjXm8qHVJxfHsxkm2G4', timestamp=1704831360000.0, type='dca_executor', close_timestamp=1704834840000.0, close_type=, status=, config=DCAExecutorConfig(id='GaJxTCNCCy8X7UmQuxvjBSF2PYjXm8qHVJxfHsxkm2G4', type='dca_executor', timestamp=1704831360000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952382'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000001'), Decimal('96.19047619047619047619047620'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142858')], prices=[Decimal('2.399790000000000017898807435'), Decimal('2.404589580000000017934605050'), Decimal('2.418988320000000018041997894'), Decimal('2.423787900000000018077795509'), Decimal('2.435786850000000018167289547'), Decimal('2.447785800000000018256783584')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.018034339400883760140548162098639295436441898345947265625'), net_pnl_quote=Decimal('-9.1300990162140802652857018983922898769378662109375'), cum_fees_quote=Decimal('0.3037571428571428366893769634771160781383514404296875'), filled_amount_quote=Decimal('506.26190476190475919793243519961833953857421875'), is_active=False, is_trading=False, custom_info={'close_price': 2.4793, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.429938328258007}, controller_id=None),\n ExecutorInfo(id='9gi7MJQwKkFb3htCQiksgSsJJueS5EPUPcNRcnnq4p4v', timestamp=1704834720000.0, type='dca_executor', close_timestamp=1704834840000.0, close_type=, status=, config=DCAExecutorConfig(id='9gi7MJQwKkFb3htCQiksgSsJJueS5EPUPcNRcnnq4p4v', type='dca_executor', timestamp=1704834720000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380954'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571432'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.457275600000000165831956500'), Decimal('2.452361048800000165500292587'), Decimal('2.437617395200000164505300848'), Decimal('2.432702844000000164173636935'), Decimal('2.420416466000000163344477152'), Decimal('2.408130088000000162515317370')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0'), net_pnl_quote=Decimal('0'), cum_fees_quote=Decimal('0'), filled_amount_quote=Decimal('0'), is_active=False, is_trading=False, custom_info={'close_price': 2.4793, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.4572756}, controller_id=None),\n ExecutorInfo(id='4rZaR1RGf8NpHZVgBEpVGRjAaABghXFjeKUfR9MwFSiP', timestamp=1704834900000.0, type='dca_executor', close_timestamp=1704839340000.0, close_type=, status=, config=DCAExecutorConfig(id='4rZaR1RGf8NpHZVgBEpVGRjAaABghXFjeKUfR9MwFSiP', type='dca_executor', timestamp=1704834900000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952380'), Decimal('47.52380952380952380952380951'), Decimal('70.85714285714285714285714284'), Decimal('94.28571428571428571428571428'), Decimal('117.2619047619047619047619047'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.442006199999999853109945713'), Decimal('2.437122187599999853403725822'), Decimal('2.422470150399999854285066147'), Decimal('2.417586137999999854578846256'), Decimal('2.405376106999999855313296527'), Decimal('2.393166075999999856047746799')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.03293539068315211315596258145887986756861209869384765625'), net_pnl_quote=Decimal('7.7884357205968282045205342001281678676605224609375'), cum_fees_quote=Decimal('0.14188571428571428167941803621943108737468719482421875'), filled_amount_quote=Decimal('236.4761904761904816041351296007633209228515625'), is_active=False, is_trading=False, custom_info={'close_price': 2.4991, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.4254343899033426}, controller_id=None),\n ExecutorInfo(id='CJzY1qHR8CKqZVm4sUmP55ZPXknLDcCf9UAZcTi7dcGD', timestamp=1704838440000.0, type='dca_executor', close_timestamp=1704844800000.0, close_type=, status=, config=DCAExecutorConfig(id='CJzY1qHR8CKqZVm4sUmP55ZPXknLDcCf9UAZcTi7dcGD', type='dca_executor', timestamp=1704838440000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.71428571428571428571428573'), Decimal('72.00000000000000000000000005'), Decimal('96.19047619047619047619047621'), Decimal('120.8333333333333333333333334'), Decimal('145.7142857142857142857142857')], prices=[Decimal('2.471332800000000148191159343'), Decimal('2.476275465600000148487541662'), Decimal('2.491103462400000149376688618'), Decimal('2.496046128000000149673070936'), Decimal('2.508402792000000150414026733'), Decimal('2.520759456000000151154982530')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='sell_0'), net_pnl_pct=Decimal('-0.00296554582491358360851751996278835576958954334259033203125'), net_pnl_quote=Decimal('-0.710883699172141891864384888322092592716217041015625'), cum_fees_quote=Decimal('0.1438285714285714167459673262783326208591461181640625'), filled_amount_quote=Decimal('239.71428571428572240620269440114498138427734375'), is_active=False, is_trading=False, custom_info={'close_price': 2.4976, 'level_id': 'sell_0', 'side': , 'current_position_average_price': 2.4881716389034567}, controller_id=None),\n ExecutorInfo(id='4Zwck6t3JedN37rwTm6fCEGTTkCUpQDdurLbhnvQ3kQU', timestamp=1704839340000.0, type='dca_executor', close_timestamp=1704844800000.0, close_type=, status=, config=DCAExecutorConfig(id='4Zwck6t3JedN37rwTm6fCEGTTkCUpQDdurLbhnvQ3kQU', type='dca_executor', timestamp=1704839340000.0, controller_id='main', connector_name='kucoin', trading_pair='WLD-USDT', side=, leverage=20, amounts_quote=[Decimal('23.80952380952380952380952381'), Decimal('47.52380952380952380952380953'), Decimal('70.85714285714285714285714290'), Decimal('94.28571428571428571428571430'), Decimal('117.2619047619047619047619048'), Decimal('140.0000000000000000000000000')], prices=[Decimal('2.494101799999999877217908562'), Decimal('2.489113596399999877463472745'), Decimal('2.474148985599999878200165294'), Decimal('2.469160781999999878445729476'), Decimal('2.456690272999999879059639934'), Decimal('2.444219763999999879673550391')], take_profit=Decimal('0.03'), stop_loss=Decimal('0.015'), trailing_stop=TrailingStop(activation_price=Decimal('0.008'), trailing_delta=Decimal('0.004')), time_limit=43200, mode=, activation_bounds=[Decimal('0.01')], level_id='buy_0'), net_pnl_pct=Decimal('0.0132894552408876105709101267393634770996868610382080078125'), net_pnl_quote=Decimal('4.7009865836635054137104816618375480175018310546875'), cum_fees_quote=Decimal('0.212242857142857122010326520467060618102550506591796875'), filled_amount_quote=Decimal('353.73809523809524080206756480038166046142578125'), is_active=False, is_trading=False, custom_info={'close_price': 2.4976, 'level_id': 'buy_0', 'side': , 'current_position_average_price': 2.47038542174093}, controller_id=None)]" + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# 2. The executors dataframe: this is the dataframe that contains the information of the orders that were executed\n", - "backtesting_results[\"executors_df\"]" + "backtesting_results[\"executors\"]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.160692Z", + "start_time": "2024-05-21T23:53:53.130394Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": "{'net_pnl': 0.042269981264627964,\n 'net_pnl_quote': 42.269981264627965,\n 'total_executors': 414,\n 'total_executors_with_position': 99,\n 'total_volume': 70749.14285714286,\n 'total_long': 52,\n 'total_short': 47,\n 'close_types': {'EARLY_STOP': 314,\n 'STOP_LOSS': 47,\n 'TAKE_PROFIT': 48,\n 'TIME_LIMIT': 5},\n 'accuracy_long': 0.46153846153846156,\n 'accuracy_short': 0.5531914893617021,\n 'total_positions': 99,\n 'accuracy': 0.5050505050505051,\n 'max_drawdown_usd': -52.26680777736686,\n 'max_drawdown_pct': -0.052671130099730776,\n 'sharpe_ratio': 0.479129045415411,\n 'profit_factor': 1.1213189350926154,\n 'win_signals': 50,\n 'loss_signals': 49}" + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# 3. The results dataframe: this is the dataframe that contains the information of the pnl of the strategy\n", "backtesting_results[\"results\"]" @@ -279,172 +263,35929 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 52, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.332827Z", + "start_time": "2024-05-21T23:53:53.140541Z" } }, "outputs": [], "source": [ - "# Now let's analyze the results using the StrategyAnalysis class\n", - "strategy_analysis = StrategyAnalysis(\n", - " positions=backtesting_results[\"executors_df\"],\n", - " candles_df=backtesting_results[\"processed_data\"],\n", - ")" + "from frontend.visualization.backtesting import create_backtesting_figure\n", + "\n", + "fig = create_backtesting_figure(\n", + " df=backtesting_results[\"processed_data\"][\"features\"],\n", + " executors=backtesting_results[\"executors\"],\n", + " config=config.dict())" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 53, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:53:53.477392Z", + "start_time": "2024-05-21T23:53:53.340118Z" } }, - "outputs": [], - "source": [ - "# let's visualize the PNL over time of the strategy\n", - "strategy_analysis.pnl_over_time()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "line": { + "color": "#00008B" + }, + "mode": "lines", + "x": [ + "2024-01-01T00:00:00", + "2024-01-01T00:01:00", + "2024-01-01T00:02:00", + "2024-01-01T00:03:00", + "2024-01-01T00:04:00", + "2024-01-01T00:05:00", + "2024-01-01T00:06:00", + "2024-01-01T00:07:00", + "2024-01-01T00:08:00", + "2024-01-01T00:09:00", + "2024-01-01T00:10:00", + "2024-01-01T00:11:00", + "2024-01-01T00:12:00", + "2024-01-01T00:13:00", + "2024-01-01T00:14:00", + "2024-01-01T00:15:00", + "2024-01-01T00:16:00", + "2024-01-01T00:17:00", + "2024-01-01T00:18:00", + "2024-01-01T00:19:00", + "2024-01-01T00:20:00", + "2024-01-01T00:21:00", + "2024-01-01T00:22:00", + "2024-01-01T00:23:00", + "2024-01-01T00:24:00", + "2024-01-01T00:25:00", + "2024-01-01T00:26:00", + "2024-01-01T00:27:00", + "2024-01-01T00:28:00", + "2024-01-01T00:29:00", + "2024-01-01T00:30:00", + "2024-01-01T00:31:00", + "2024-01-01T00:32:00", + "2024-01-01T00:33:00", + "2024-01-01T00:34:00", + "2024-01-01T00:35:00", + "2024-01-01T00:36:00", + "2024-01-01T00:37:00", + "2024-01-01T00:38:00", + "2024-01-01T00:39:00", + "2024-01-01T00:40:00", + "2024-01-01T00:41:00", + "2024-01-01T00:42:00", + "2024-01-01T00:43:00", + "2024-01-01T00:44:00", + "2024-01-01T00:45:00", + "2024-01-01T00:46:00", + "2024-01-01T00:47:00", + "2024-01-01T00:48:00", + "2024-01-01T00:49:00", + "2024-01-01T00:50:00", + "2024-01-01T00:51:00", + "2024-01-01T00:52:00", + "2024-01-01T00:53:00", + "2024-01-01T00:54:00", + "2024-01-01T00:55:00", + "2024-01-01T00:56:00", + "2024-01-01T00:57:00", + "2024-01-01T00:58:00", + "2024-01-01T00:59:00", + "2024-01-01T01:00:00", + "2024-01-01T01:01:00", + "2024-01-01T01:02:00", + "2024-01-01T01:03:00", + "2024-01-01T01:04:00", + "2024-01-01T01:05:00", + "2024-01-01T01:06:00", + "2024-01-01T01:07:00", + "2024-01-01T01:08:00", + "2024-01-01T01:09:00", + "2024-01-01T01:10:00", + "2024-01-01T01:11:00", + "2024-01-01T01:12:00", + "2024-01-01T01:13:00", + "2024-01-01T01:14:00", + "2024-01-01T01:15:00", + "2024-01-01T01:16:00", + "2024-01-01T01:17:00", + "2024-01-01T01:18:00", + "2024-01-01T01:19:00", + "2024-01-01T01:20:00", + "2024-01-01T01:21:00", + "2024-01-01T01:22:00", + "2024-01-01T01:23:00", + "2024-01-01T01:24:00", + "2024-01-01T01:25:00", + "2024-01-01T01:26:00", + "2024-01-01T01:27:00", + "2024-01-01T01:28:00", + "2024-01-01T01:29:00", + "2024-01-01T01:30:00", + "2024-01-01T01:31:00", + "2024-01-01T01:32:00", + "2024-01-01T01:33:00", + "2024-01-01T01:34:00", + "2024-01-01T01:35:00", + "2024-01-01T01:36:00", + "2024-01-01T01:37:00", + "2024-01-01T01:38:00", + "2024-01-01T01:39:00", + "2024-01-01T01:40:00", + "2024-01-01T01:41:00", + "2024-01-01T01:42:00", + "2024-01-01T01:43:00", + "2024-01-01T01:44:00", + "2024-01-01T01:45:00", + "2024-01-01T01:46:00", + "2024-01-01T01:47:00", + "2024-01-01T01:48:00", + "2024-01-01T01:49:00", + "2024-01-01T01:50:00", + "2024-01-01T01:51:00", + "2024-01-01T01:52:00", + "2024-01-01T01:53:00", + "2024-01-01T01:54:00", + "2024-01-01T01:55:00", + "2024-01-01T01:56:00", + "2024-01-01T01:57:00", + "2024-01-01T01:58:00", + "2024-01-01T01:59:00", + "2024-01-01T02:00:00", + "2024-01-01T02:01:00", + "2024-01-01T02:02:00", + "2024-01-01T02:03:00", + "2024-01-01T02:04:00", + "2024-01-01T02:05:00", + "2024-01-01T02:06:00", + "2024-01-01T02:07:00", + "2024-01-01T02:08:00", + "2024-01-01T02:09:00", + "2024-01-01T02:10:00", + "2024-01-01T02:11:00", + "2024-01-01T02:12:00", + "2024-01-01T02:13:00", + "2024-01-01T02:14:00", + "2024-01-01T02:15:00", + "2024-01-01T02:16:00", + "2024-01-01T02:17:00", + "2024-01-01T02:18:00", + "2024-01-01T02:19:00", + "2024-01-01T02:20:00", + "2024-01-01T02:21:00", + "2024-01-01T02:22:00", + "2024-01-01T02:23:00", + "2024-01-01T02:24:00", + "2024-01-01T02:25:00", + "2024-01-01T02:26:00", + "2024-01-01T02:27:00", + "2024-01-01T02:28:00", + "2024-01-01T02:29:00", + "2024-01-01T02:30:00", + "2024-01-01T02:31:00", + "2024-01-01T02:32:00", + "2024-01-01T02:33:00", + "2024-01-01T02:34:00", + "2024-01-01T02:35:00", + "2024-01-01T02:36:00", + "2024-01-01T02:37:00", + "2024-01-01T02:38:00", + "2024-01-01T02:39:00", + "2024-01-01T02:40:00", + "2024-01-01T02:41:00", + "2024-01-01T02:42:00", + "2024-01-01T02:43:00", + "2024-01-01T02:44:00", + "2024-01-01T02:45:00", + "2024-01-01T02:46:00", + "2024-01-01T02:47:00", + "2024-01-01T02:48:00", + "2024-01-01T02:49:00", + "2024-01-01T02:50:00", + "2024-01-01T02:51:00", + "2024-01-01T02:52:00", + "2024-01-01T02:53:00", + "2024-01-01T02:54:00", + "2024-01-01T02:55:00", + "2024-01-01T02:56:00", + "2024-01-01T02:57:00", + "2024-01-01T02:58:00", + "2024-01-01T02:59:00", + "2024-01-01T03:00:00", + "2024-01-01T03:01:00", + "2024-01-01T03:02:00", + "2024-01-01T03:03:00", + "2024-01-01T03:04:00", + "2024-01-01T03:05:00", + "2024-01-01T03:06:00", + "2024-01-01T03:07:00", + "2024-01-01T03:08:00", + "2024-01-01T03:09:00", + "2024-01-01T03:10:00", + "2024-01-01T03:11:00", + "2024-01-01T03:12:00", + "2024-01-01T03:13:00", + "2024-01-01T03:14:00", + "2024-01-01T03:15:00", + "2024-01-01T03:16:00", + "2024-01-01T03:17:00", + "2024-01-01T03:18:00", + "2024-01-01T03:19:00", + "2024-01-01T03:20:00", + "2024-01-01T03:21:00", + "2024-01-01T03:22:00", + "2024-01-01T03:23:00", + "2024-01-01T03:24:00", + "2024-01-01T03:25:00", + "2024-01-01T03:26:00", + "2024-01-01T03:27:00", + "2024-01-01T03:28:00", + "2024-01-01T03:29:00", + "2024-01-01T03:30:00", + "2024-01-01T03:31:00", + "2024-01-01T03:32:00", + "2024-01-01T03:33:00", + "2024-01-01T03:34:00", + "2024-01-01T03:35:00", + "2024-01-01T03:36:00", + "2024-01-01T03:37:00", + "2024-01-01T03:38:00", + "2024-01-01T03:39:00", + "2024-01-01T03:40:00", + "2024-01-01T03:41:00", + "2024-01-01T03:42:00", + "2024-01-01T03:43:00", + "2024-01-01T03:44:00", + "2024-01-01T03:45:00", + "2024-01-01T03:46:00", + "2024-01-01T03:47:00", + "2024-01-01T03:48:00", + "2024-01-01T03:49:00", + "2024-01-01T03:50:00", + "2024-01-01T03:51:00", + "2024-01-01T03:52:00", + "2024-01-01T03:53:00", + "2024-01-01T03:54:00", + "2024-01-01T03:55:00", + "2024-01-01T03:56:00", + "2024-01-01T03:57:00", + "2024-01-01T03:58:00", + "2024-01-01T03:59:00", + "2024-01-01T04:00:00", + "2024-01-01T04:01:00", + "2024-01-01T04:02:00", + "2024-01-01T04:03:00", + "2024-01-01T04:04:00", + "2024-01-01T04:05:00", + "2024-01-01T04:06:00", + "2024-01-01T04:07:00", + "2024-01-01T04:08:00", + "2024-01-01T04:09:00", + "2024-01-01T04:10:00", + "2024-01-01T04:11:00", + "2024-01-01T04:12:00", + "2024-01-01T04:13:00", + "2024-01-01T04:14:00", + "2024-01-01T04:15:00", + "2024-01-01T04:16:00", + "2024-01-01T04:17:00", + "2024-01-01T04:18:00", + "2024-01-01T04:19:00", + "2024-01-01T04:20:00", + "2024-01-01T04:21:00", + "2024-01-01T04:22:00", + "2024-01-01T04:23:00", + "2024-01-01T04:24:00", + "2024-01-01T04:25:00", + "2024-01-01T04:26:00", + "2024-01-01T04:27:00", + "2024-01-01T04:28:00", + "2024-01-01T04:29:00", + "2024-01-01T04:30:00", + "2024-01-01T04:31:00", + "2024-01-01T04:32:00", + "2024-01-01T04:33:00", + "2024-01-01T04:34:00", + "2024-01-01T04:35:00", + "2024-01-01T04:36:00", + "2024-01-01T04:37:00", + "2024-01-01T04:38:00", + "2024-01-01T04:39:00", + "2024-01-01T04:40:00", + "2024-01-01T04:41:00", + "2024-01-01T04:42:00", + "2024-01-01T04:43:00", + "2024-01-01T04:44:00", + "2024-01-01T04:45:00", + "2024-01-01T04:46:00", + "2024-01-01T04:47:00", + "2024-01-01T04:48:00", + "2024-01-01T04:49:00", + "2024-01-01T04:50:00", + "2024-01-01T04:51:00", + "2024-01-01T04:52:00", + "2024-01-01T04:53:00", + "2024-01-01T04:54:00", + "2024-01-01T04:55:00", + "2024-01-01T04:56:00", + "2024-01-01T04:57:00", + "2024-01-01T04:58:00", + "2024-01-01T04:59:00", + "2024-01-01T05:00:00", + "2024-01-01T05:01:00", + "2024-01-01T05:02:00", + "2024-01-01T05:03:00", + "2024-01-01T05:04:00", + "2024-01-01T05:05:00", + "2024-01-01T05:06:00", + "2024-01-01T05:07:00", + "2024-01-01T05:08:00", + "2024-01-01T05:09:00", + "2024-01-01T05:10:00", + "2024-01-01T05:11:00", + "2024-01-01T05:12:00", + "2024-01-01T05:13:00", + "2024-01-01T05:14:00", + "2024-01-01T05:15:00", + "2024-01-01T05:16:00", + "2024-01-01T05:17:00", + "2024-01-01T05:18:00", + "2024-01-01T05:19:00", + "2024-01-01T05:20:00", + "2024-01-01T05:21:00", + "2024-01-01T05:22:00", + "2024-01-01T05:23:00", + "2024-01-01T05:24:00", + "2024-01-01T05:25:00", + "2024-01-01T05:26:00", + "2024-01-01T05:27:00", + "2024-01-01T05:28:00", + "2024-01-01T05:29:00", + "2024-01-01T05:30:00", + "2024-01-01T05:31:00", + "2024-01-01T05:32:00", + "2024-01-01T05:33:00", + "2024-01-01T05:34:00", + "2024-01-01T05:35:00", + "2024-01-01T05:36:00", + "2024-01-01T05:37:00", + "2024-01-01T05:38:00", + "2024-01-01T05:39:00", + "2024-01-01T05:40:00", + "2024-01-01T05:41:00", + "2024-01-01T05:42:00", + "2024-01-01T05:43:00", + "2024-01-01T05:44:00", + "2024-01-01T05:45:00", + "2024-01-01T05:46:00", + "2024-01-01T05:47:00", + "2024-01-01T05:48:00", + "2024-01-01T05:49:00", + "2024-01-01T05:50:00", + "2024-01-01T05:51:00", + "2024-01-01T05:52:00", + "2024-01-01T05:53:00", + "2024-01-01T05:54:00", + "2024-01-01T05:55:00", + "2024-01-01T05:56:00", + "2024-01-01T05:57:00", + "2024-01-01T05:58:00", + "2024-01-01T05:59:00", + "2024-01-01T06:00:00", + "2024-01-01T06:01:00", + "2024-01-01T06:02:00", + "2024-01-01T06:03:00", + "2024-01-01T06:04:00", + "2024-01-01T06:05:00", + "2024-01-01T06:06:00", + "2024-01-01T06:07:00", + "2024-01-01T06:08:00", + "2024-01-01T06:09:00", + "2024-01-01T06:10:00", + "2024-01-01T06:11:00", + "2024-01-01T06:12:00", + "2024-01-01T06:13:00", + "2024-01-01T06:14:00", + "2024-01-01T06:15:00", + "2024-01-01T06:16:00", + "2024-01-01T06:17:00", + "2024-01-01T06:18:00", + "2024-01-01T06:19:00", + "2024-01-01T06:20:00", + "2024-01-01T06:21:00", + "2024-01-01T06:22:00", + "2024-01-01T06:23:00", + "2024-01-01T06:24:00", + "2024-01-01T06:25:00", + "2024-01-01T06:26:00", + "2024-01-01T06:27:00", + "2024-01-01T06:28:00", + "2024-01-01T06:29:00", + "2024-01-01T06:30:00", + "2024-01-01T06:31:00", + "2024-01-01T06:32:00", + "2024-01-01T06:33:00", + "2024-01-01T06:34:00", + "2024-01-01T06:35:00", + "2024-01-01T06:36:00", + "2024-01-01T06:37:00", + "2024-01-01T06:38:00", + "2024-01-01T06:39:00", + "2024-01-01T06:40:00", + "2024-01-01T06:41:00", + "2024-01-01T06:42:00", + "2024-01-01T06:43:00", + "2024-01-01T06:44:00", + "2024-01-01T06:45:00", + "2024-01-01T06:46:00", + "2024-01-01T06:47:00", + "2024-01-01T06:48:00", + "2024-01-01T06:49:00", + "2024-01-01T06:50:00", + "2024-01-01T06:51:00", + "2024-01-01T06:52:00", + "2024-01-01T06:53:00", + "2024-01-01T06:54:00", + "2024-01-01T06:55:00", + "2024-01-01T06:56:00", + "2024-01-01T06:57:00", + "2024-01-01T06:58:00", + "2024-01-01T06:59:00", + "2024-01-01T07:00:00", + "2024-01-01T07:01:00", + "2024-01-01T07:02:00", + "2024-01-01T07:03:00", + "2024-01-01T07:04:00", + "2024-01-01T07:05:00", + "2024-01-01T07:06:00", + "2024-01-01T07:07:00", + "2024-01-01T07:08:00", + "2024-01-01T07:09:00", + "2024-01-01T07:10:00", + "2024-01-01T07:11:00", + "2024-01-01T07:12:00", + "2024-01-01T07:13:00", + "2024-01-01T07:14:00", + "2024-01-01T07:15:00", + "2024-01-01T07:16:00", + "2024-01-01T07:17:00", + "2024-01-01T07:18:00", + "2024-01-01T07:19:00", + "2024-01-01T07:20:00", + "2024-01-01T07:21:00", + "2024-01-01T07:22:00", + "2024-01-01T07:23:00", + "2024-01-01T07:24:00", + "2024-01-01T07:25:00", + "2024-01-01T07:26:00", + "2024-01-01T07:27:00", + "2024-01-01T07:28:00", + "2024-01-01T07:29:00", + "2024-01-01T07:30:00", + "2024-01-01T07:31:00", + "2024-01-01T07:32:00", + "2024-01-01T07:33:00", + "2024-01-01T07:34:00", + "2024-01-01T07:35:00", + "2024-01-01T07:36:00", + "2024-01-01T07:37:00", + "2024-01-01T07:38:00", + "2024-01-01T07:39:00", + "2024-01-01T07:40:00", + "2024-01-01T07:41:00", + "2024-01-01T07:42:00", + "2024-01-01T07:43:00", + "2024-01-01T07:44:00", + "2024-01-01T07:45:00", + "2024-01-01T07:46:00", + "2024-01-01T07:47:00", + "2024-01-01T07:48:00", + "2024-01-01T07:49:00", + "2024-01-01T07:50:00", + "2024-01-01T07:51:00", + "2024-01-01T07:52:00", + "2024-01-01T07:53:00", + "2024-01-01T07:54:00", + "2024-01-01T07:55:00", + "2024-01-01T07:56:00", + "2024-01-01T07:57:00", + "2024-01-01T07:58:00", + "2024-01-01T07:59:00", + "2024-01-01T08:00:00", + "2024-01-01T08:01:00", + "2024-01-01T08:02:00", + "2024-01-01T08:03:00", + "2024-01-01T08:04:00", + "2024-01-01T08:05:00", + "2024-01-01T08:06:00", + "2024-01-01T08:07:00", + "2024-01-01T08:08:00", + "2024-01-01T08:09:00", + "2024-01-01T08:10:00", + "2024-01-01T08:11:00", + "2024-01-01T08:12:00", + "2024-01-01T08:13:00", + "2024-01-01T08:14:00", + "2024-01-01T08:15:00", + "2024-01-01T08:16:00", + "2024-01-01T08:17:00", + "2024-01-01T08:18:00", + "2024-01-01T08:19:00", + "2024-01-01T08:20:00", + "2024-01-01T08:21:00", + "2024-01-01T08:22:00", + "2024-01-01T08:23:00", + "2024-01-01T08:24:00", + "2024-01-01T08:25:00", + "2024-01-01T08:26:00", + "2024-01-01T08:27:00", + "2024-01-01T08:28:00", + "2024-01-01T08:29:00", + "2024-01-01T08:30:00", + "2024-01-01T08:31:00", + "2024-01-01T08:32:00", + "2024-01-01T08:33:00", + "2024-01-01T08:34:00", + "2024-01-01T08:35:00", + "2024-01-01T08:36:00", + "2024-01-01T08:37:00", + "2024-01-01T08:38:00", + "2024-01-01T08:39:00", + "2024-01-01T08:40:00", + "2024-01-01T08:41:00", + "2024-01-01T08:42:00", + "2024-01-01T08:43:00", + "2024-01-01T08:44:00", + "2024-01-01T08:45:00", + "2024-01-01T08:46:00", + "2024-01-01T08:47:00", + "2024-01-01T08:48:00", + "2024-01-01T08:49:00", + "2024-01-01T08:50:00", + "2024-01-01T08:51:00", + "2024-01-01T08:52:00", + "2024-01-01T08:53:00", + "2024-01-01T08:54:00", + "2024-01-01T08:55:00", + "2024-01-01T08:56:00", + "2024-01-01T08:57:00", + "2024-01-01T08:58:00", + "2024-01-01T08:59:00", + "2024-01-01T09:00:00", + "2024-01-01T09:01:00", + "2024-01-01T09:02:00", + "2024-01-01T09:03:00", + "2024-01-01T09:04:00", + "2024-01-01T09:05:00", + "2024-01-01T09:06:00", + "2024-01-01T09:07:00", + "2024-01-01T09:08:00", + "2024-01-01T09:09:00", + "2024-01-01T09:10:00", + "2024-01-01T09:11:00", + "2024-01-01T09:12:00", + "2024-01-01T09:13:00", + "2024-01-01T09:14:00", + "2024-01-01T09:15:00", + "2024-01-01T09:16:00", + "2024-01-01T09:17:00", + "2024-01-01T09:18:00", + "2024-01-01T09:19:00", + "2024-01-01T09:20:00", + "2024-01-01T09:21:00", + "2024-01-01T09:22:00", + "2024-01-01T09:23:00", + "2024-01-01T09:24:00", + "2024-01-01T09:25:00", + "2024-01-01T09:26:00", + "2024-01-01T09:27:00", + "2024-01-01T09:28:00", + "2024-01-01T09:29:00", + "2024-01-01T09:30:00", + "2024-01-01T09:31:00", + "2024-01-01T09:32:00", + "2024-01-01T09:33:00", + "2024-01-01T09:34:00", + "2024-01-01T09:35:00", + "2024-01-01T09:36:00", + "2024-01-01T09:37:00", + "2024-01-01T09:38:00", + "2024-01-01T09:39:00", + "2024-01-01T09:40:00", + "2024-01-01T09:41:00", + "2024-01-01T09:42:00", + "2024-01-01T09:43:00", + "2024-01-01T09:44:00", + "2024-01-01T09:45:00", + "2024-01-01T09:46:00", + "2024-01-01T09:47:00", + "2024-01-01T09:48:00", + "2024-01-01T09:49:00", + "2024-01-01T09:50:00", + "2024-01-01T09:51:00", + "2024-01-01T09:52:00", + "2024-01-01T09:53:00", + "2024-01-01T09:54:00", + "2024-01-01T09:55:00", + "2024-01-01T09:56:00", + "2024-01-01T09:57:00", + "2024-01-01T09:58:00", + "2024-01-01T09:59:00", + "2024-01-01T10:00:00", + "2024-01-01T10:01:00", + "2024-01-01T10:02:00", + "2024-01-01T10:03:00", + "2024-01-01T10:04:00", + "2024-01-01T10:05:00", + "2024-01-01T10:06:00", + "2024-01-01T10:07:00", + "2024-01-01T10:08:00", + "2024-01-01T10:09:00", + "2024-01-01T10:10:00", + "2024-01-01T10:11:00", + "2024-01-01T10:12:00", + "2024-01-01T10:13:00", + "2024-01-01T10:14:00", + "2024-01-01T10:15:00", + "2024-01-01T10:16:00", + "2024-01-01T10:17:00", + "2024-01-01T10:18:00", + "2024-01-01T10:19:00", + "2024-01-01T10:20:00", + "2024-01-01T10:21:00", + "2024-01-01T10:22:00", + "2024-01-01T10:23:00", + "2024-01-01T10:24:00", + "2024-01-01T10:25:00", + "2024-01-01T10:26:00", + "2024-01-01T10:27:00", + "2024-01-01T10:28:00", + "2024-01-01T10:29:00", + "2024-01-01T10:30:00", + "2024-01-01T10:31:00", + "2024-01-01T10:32:00", + "2024-01-01T10:33:00", + "2024-01-01T10:34:00", + "2024-01-01T10:35:00", + "2024-01-01T10:36:00", + "2024-01-01T10:37:00", + "2024-01-01T10:38:00", + "2024-01-01T10:39:00", + "2024-01-01T10:40:00", + "2024-01-01T10:41:00", + "2024-01-01T10:42:00", + "2024-01-01T10:43:00", + "2024-01-01T10:44:00", + "2024-01-01T10:45:00", + "2024-01-01T10:46:00", + "2024-01-01T10:47:00", + "2024-01-01T10:48:00", + "2024-01-01T10:49:00", + "2024-01-01T10:50:00", + "2024-01-01T10:51:00", + "2024-01-01T10:52:00", + "2024-01-01T10:53:00", + "2024-01-01T10:54:00", + "2024-01-01T10:55:00", + "2024-01-01T10:56:00", + "2024-01-01T10:57:00", + "2024-01-01T10:58:00", + "2024-01-01T10:59:00", + "2024-01-01T11:00:00", + "2024-01-01T11:01:00", + "2024-01-01T11:02:00", + "2024-01-01T11:03:00", + "2024-01-01T11:04:00", + "2024-01-01T11:05:00", + "2024-01-01T11:06:00", + "2024-01-01T11:07:00", + "2024-01-01T11:08:00", + "2024-01-01T11:09:00", + "2024-01-01T11:10:00", + "2024-01-01T11:11:00", + "2024-01-01T11:12:00", + "2024-01-01T11:13:00", + "2024-01-01T11:14:00", + "2024-01-01T11:15:00", + "2024-01-01T11:16:00", + "2024-01-01T11:17:00", + "2024-01-01T11:18:00", + "2024-01-01T11:19:00", + "2024-01-01T11:20:00", + "2024-01-01T11:21:00", + "2024-01-01T11:22:00", + "2024-01-01T11:23:00", + "2024-01-01T11:24:00", + "2024-01-01T11:25:00", + "2024-01-01T11:26:00", + "2024-01-01T11:27:00", + "2024-01-01T11:28:00", + "2024-01-01T11:29:00", + "2024-01-01T11:30:00", + "2024-01-01T11:31:00", + "2024-01-01T11:32:00", + "2024-01-01T11:33:00", + "2024-01-01T11:34:00", + "2024-01-01T11:35:00", + "2024-01-01T11:36:00", + "2024-01-01T11:37:00", + "2024-01-01T11:38:00", + "2024-01-01T11:39:00", + "2024-01-01T11:40:00", + "2024-01-01T11:41:00", + "2024-01-01T11:42:00", + "2024-01-01T11:43:00", + "2024-01-01T11:44:00", + "2024-01-01T11:45:00", + "2024-01-01T11:46:00", + "2024-01-01T11:47:00", + "2024-01-01T11:48:00", + "2024-01-01T11:49:00", + "2024-01-01T11:50:00", + "2024-01-01T11:51:00", + "2024-01-01T11:52:00", + "2024-01-01T11:53:00", + "2024-01-01T11:54:00", + "2024-01-01T11:55:00", + "2024-01-01T11:56:00", + "2024-01-01T11:57:00", + "2024-01-01T11:58:00", + "2024-01-01T11:59:00", + "2024-01-01T12:00:00", + "2024-01-01T12:01:00", + "2024-01-01T12:02:00", + "2024-01-01T12:03:00", + "2024-01-01T12:04:00", + "2024-01-01T12:05:00", + "2024-01-01T12:06:00", + "2024-01-01T12:07:00", + "2024-01-01T12:08:00", + "2024-01-01T12:09:00", + "2024-01-01T12:10:00", + "2024-01-01T12:11:00", + "2024-01-01T12:12:00", + "2024-01-01T12:13:00", + "2024-01-01T12:14:00", + "2024-01-01T12:15:00", + "2024-01-01T12:16:00", + "2024-01-01T12:17:00", + "2024-01-01T12:18:00", + "2024-01-01T12:19:00", + "2024-01-01T12:20:00", + "2024-01-01T12:21:00", + "2024-01-01T12:22:00", + "2024-01-01T12:23:00", + "2024-01-01T12:24:00", + "2024-01-01T12:25:00", + "2024-01-01T12:26:00", + "2024-01-01T12:27:00", + "2024-01-01T12:28:00", + "2024-01-01T12:29:00", + "2024-01-01T12:30:00", + "2024-01-01T12:31:00", + "2024-01-01T12:32:00", + "2024-01-01T12:33:00", + "2024-01-01T12:34:00", + "2024-01-01T12:35:00", + "2024-01-01T12:36:00", + "2024-01-01T12:37:00", + "2024-01-01T12:38:00", + "2024-01-01T12:39:00", + "2024-01-01T12:40:00", + "2024-01-01T12:41:00", + "2024-01-01T12:42:00", + "2024-01-01T12:43:00", + "2024-01-01T12:44:00", + "2024-01-01T12:45:00", + "2024-01-01T12:46:00", + "2024-01-01T12:47:00", + "2024-01-01T12:48:00", + "2024-01-01T12:49:00", + "2024-01-01T12:50:00", + "2024-01-01T12:51:00", + "2024-01-01T12:52:00", + "2024-01-01T12:53:00", + "2024-01-01T12:54:00", + "2024-01-01T12:55:00", + "2024-01-01T12:56:00", + "2024-01-01T12:57:00", + "2024-01-01T12:58:00", + "2024-01-01T12:59:00", + "2024-01-01T13:00:00", + "2024-01-01T13:01:00", + "2024-01-01T13:02:00", + "2024-01-01T13:03:00", + "2024-01-01T13:04:00", + "2024-01-01T13:05:00", + "2024-01-01T13:06:00", + "2024-01-01T13:07:00", + "2024-01-01T13:08:00", + "2024-01-01T13:09:00", + "2024-01-01T13:10:00", + "2024-01-01T13:11:00", + "2024-01-01T13:12:00", + "2024-01-01T13:13:00", + "2024-01-01T13:14:00", + "2024-01-01T13:15:00", + "2024-01-01T13:16:00", + "2024-01-01T13:17:00", + "2024-01-01T13:18:00", + "2024-01-01T13:19:00", + "2024-01-01T13:20:00", + "2024-01-01T13:21:00", + "2024-01-01T13:22:00", + "2024-01-01T13:23:00", + "2024-01-01T13:24:00", + "2024-01-01T13:25:00", + "2024-01-01T13:26:00", + "2024-01-01T13:27:00", + "2024-01-01T13:28:00", + "2024-01-01T13:29:00", + "2024-01-01T13:30:00", + "2024-01-01T13:31:00", + "2024-01-01T13:32:00", + "2024-01-01T13:33:00", + "2024-01-01T13:34:00", + "2024-01-01T13:35:00", + "2024-01-01T13:36:00", + "2024-01-01T13:37:00", + "2024-01-01T13:38:00", + "2024-01-01T13:39:00", + "2024-01-01T13:40:00", + "2024-01-01T13:41:00", + "2024-01-01T13:42:00", + "2024-01-01T13:43:00", + "2024-01-01T13:44:00", + "2024-01-01T13:45:00", + "2024-01-01T13:46:00", + "2024-01-01T13:47:00", + "2024-01-01T13:48:00", + "2024-01-01T13:49:00", + "2024-01-01T13:50:00", + "2024-01-01T13:51:00", + "2024-01-01T13:52:00", + "2024-01-01T13:53:00", + "2024-01-01T13:54:00", + "2024-01-01T13:55:00", + "2024-01-01T13:56:00", + "2024-01-01T13:57:00", + "2024-01-01T13:58:00", + "2024-01-01T13:59:00", + "2024-01-01T14:00:00", + "2024-01-01T14:01:00", + "2024-01-01T14:02:00", + "2024-01-01T14:03:00", + "2024-01-01T14:04:00", + "2024-01-01T14:05:00", + "2024-01-01T14:06:00", + "2024-01-01T14:07:00", + "2024-01-01T14:08:00", + "2024-01-01T14:09:00", + "2024-01-01T14:10:00", + "2024-01-01T14:11:00", + "2024-01-01T14:12:00", + "2024-01-01T14:13:00", + "2024-01-01T14:14:00", + "2024-01-01T14:15:00", + "2024-01-01T14:16:00", + "2024-01-01T14:17:00", + "2024-01-01T14:18:00", + "2024-01-01T14:19:00", + "2024-01-01T14:20:00", + "2024-01-01T14:21:00", + "2024-01-01T14:22:00", + "2024-01-01T14:23:00", + "2024-01-01T14:24:00", + "2024-01-01T14:25:00", + "2024-01-01T14:26:00", + "2024-01-01T14:27:00", + "2024-01-01T14:28:00", + "2024-01-01T14:29:00", + "2024-01-01T14:30:00", + "2024-01-01T14:31:00", + "2024-01-01T14:32:00", + "2024-01-01T14:33:00", + "2024-01-01T14:34:00", + "2024-01-01T14:35:00", + "2024-01-01T14:36:00", + "2024-01-01T14:37:00", + "2024-01-01T14:38:00", + "2024-01-01T14:39:00", + "2024-01-01T14:40:00", + "2024-01-01T14:41:00", + "2024-01-01T14:42:00", + "2024-01-01T14:43:00", + "2024-01-01T14:44:00", + "2024-01-01T14:45:00", + "2024-01-01T14:46:00", + "2024-01-01T14:47:00", + "2024-01-01T14:48:00", + "2024-01-01T14:49:00", + "2024-01-01T14:50:00", + "2024-01-01T14:51:00", + "2024-01-01T14:52:00", + "2024-01-01T14:53:00", + "2024-01-01T14:54:00", + "2024-01-01T14:55:00", + "2024-01-01T14:56:00", + "2024-01-01T14:57:00", + "2024-01-01T14:58:00", + "2024-01-01T14:59:00", + "2024-01-01T15:00:00", + "2024-01-01T15:01:00", + "2024-01-01T15:02:00", + "2024-01-01T15:03:00", + "2024-01-01T15:04:00", + "2024-01-01T15:05:00", + "2024-01-01T15:06:00", + "2024-01-01T15:07:00", + "2024-01-01T15:08:00", + "2024-01-01T15:09:00", + "2024-01-01T15:10:00", + "2024-01-01T15:11:00", + "2024-01-01T15:12:00", + "2024-01-01T15:13:00", + "2024-01-01T15:14:00", + "2024-01-01T15:15:00", + "2024-01-01T15:16:00", + "2024-01-01T15:17:00", + "2024-01-01T15:18:00", + "2024-01-01T15:19:00", + "2024-01-01T15:20:00", + "2024-01-01T15:21:00", + "2024-01-01T15:22:00", + "2024-01-01T15:23:00", + "2024-01-01T15:24:00", + "2024-01-01T15:25:00", + "2024-01-01T15:26:00", + "2024-01-01T15:27:00", + "2024-01-01T15:28:00", + "2024-01-01T15:29:00", + "2024-01-01T15:30:00", + "2024-01-01T15:31:00", + "2024-01-01T15:32:00", + "2024-01-01T15:33:00", + "2024-01-01T15:34:00", + "2024-01-01T15:35:00", + "2024-01-01T15:36:00", + "2024-01-01T15:37:00", + "2024-01-01T15:38:00", + "2024-01-01T15:39:00", + "2024-01-01T15:40:00", + "2024-01-01T15:41:00", + "2024-01-01T15:42:00", + "2024-01-01T15:43:00", + "2024-01-01T15:44:00", + "2024-01-01T15:45:00", + "2024-01-01T15:46:00", + "2024-01-01T15:47:00", + "2024-01-01T15:48:00", + "2024-01-01T15:49:00", + "2024-01-01T15:50:00", + "2024-01-01T15:51:00", + "2024-01-01T15:52:00", + "2024-01-01T15:53:00", + "2024-01-01T15:54:00", + "2024-01-01T15:55:00", + "2024-01-01T15:56:00", + "2024-01-01T15:57:00", + "2024-01-01T15:58:00", + "2024-01-01T15:59:00", + "2024-01-01T16:00:00", + "2024-01-01T16:01:00", + "2024-01-01T16:02:00", + "2024-01-01T16:03:00", + "2024-01-01T16:04:00", + "2024-01-01T16:05:00", + "2024-01-01T16:06:00", + "2024-01-01T16:07:00", + "2024-01-01T16:08:00", + "2024-01-01T16:09:00", + "2024-01-01T16:10:00", + "2024-01-01T16:11:00", + "2024-01-01T16:12:00", + "2024-01-01T16:13:00", + "2024-01-01T16:14:00", + "2024-01-01T16:15:00", + "2024-01-01T16:16:00", + "2024-01-01T16:17:00", + "2024-01-01T16:18:00", + "2024-01-01T16:19:00", + "2024-01-01T16:20:00", + "2024-01-01T16:21:00", + "2024-01-01T16:22:00", + "2024-01-01T16:23:00", + "2024-01-01T16:24:00", + "2024-01-01T16:25:00", + "2024-01-01T16:26:00", + "2024-01-01T16:27:00", + "2024-01-01T16:28:00", + "2024-01-01T16:29:00", + "2024-01-01T16:30:00", + "2024-01-01T16:31:00", + "2024-01-01T16:32:00", + "2024-01-01T16:33:00", + "2024-01-01T16:34:00", + "2024-01-01T16:35:00", + "2024-01-01T16:36:00", + "2024-01-01T16:37:00", + "2024-01-01T16:38:00", + "2024-01-01T16:39:00", + "2024-01-01T16:40:00", + "2024-01-01T16:41:00", + "2024-01-01T16:42:00", + "2024-01-01T16:43:00", + "2024-01-01T16:44:00", + "2024-01-01T16:45:00", + "2024-01-01T16:46:00", + "2024-01-01T16:47:00", + "2024-01-01T16:48:00", + "2024-01-01T16:49:00", + "2024-01-01T16:50:00", + "2024-01-01T16:51:00", + "2024-01-01T16:52:00", + "2024-01-01T16:53:00", + "2024-01-01T16:54:00", + "2024-01-01T16:55:00", + "2024-01-01T16:56:00", + "2024-01-01T16:57:00", + "2024-01-01T16:58:00", + "2024-01-01T16:59:00", + "2024-01-01T17:00:00", + "2024-01-01T17:01:00", + "2024-01-01T17:02:00", + "2024-01-01T17:03:00", + "2024-01-01T17:04:00", + "2024-01-01T17:05:00", + "2024-01-01T17:06:00", + "2024-01-01T17:07:00", + "2024-01-01T17:08:00", + "2024-01-01T17:09:00", + "2024-01-01T17:10:00", + "2024-01-01T17:11:00", + "2024-01-01T17:12:00", + "2024-01-01T17:13:00", + "2024-01-01T17:14:00", + "2024-01-01T17:15:00", + "2024-01-01T17:16:00", + "2024-01-01T17:17:00", + "2024-01-01T17:18:00", + "2024-01-01T17:19:00", + "2024-01-01T17:20:00", + "2024-01-01T17:21:00", + "2024-01-01T17:22:00", + "2024-01-01T17:23:00", + "2024-01-01T17:24:00", + "2024-01-01T17:25:00", + "2024-01-01T17:26:00", + "2024-01-01T17:27:00", + "2024-01-01T17:28:00", + "2024-01-01T17:29:00", + "2024-01-01T17:30:00", + "2024-01-01T17:31:00", + "2024-01-01T17:32:00", + "2024-01-01T17:33:00", + "2024-01-01T17:34:00", + "2024-01-01T17:35:00", + "2024-01-01T17:36:00", + "2024-01-01T17:37:00", + "2024-01-01T17:38:00", + "2024-01-01T17:39:00", + "2024-01-01T17:40:00", + "2024-01-01T17:41:00", + "2024-01-01T17:42:00", + "2024-01-01T17:43:00", + "2024-01-01T17:44:00", + "2024-01-01T17:45:00", + "2024-01-01T17:46:00", + "2024-01-01T17:47:00", + "2024-01-01T17:48:00", + "2024-01-01T17:49:00", + "2024-01-01T17:50:00", + "2024-01-01T17:51:00", + "2024-01-01T17:52:00", + "2024-01-01T17:53:00", + "2024-01-01T17:54:00", + "2024-01-01T17:55:00", + "2024-01-01T17:56:00", + "2024-01-01T17:57:00", + "2024-01-01T17:58:00", + "2024-01-01T17:59:00", + "2024-01-01T18:00:00", + "2024-01-01T18:01:00", + "2024-01-01T18:02:00", + "2024-01-01T18:03:00", + "2024-01-01T18:04:00", + "2024-01-01T18:05:00", + "2024-01-01T18:06:00", + "2024-01-01T18:07:00", + "2024-01-01T18:08:00", + "2024-01-01T18:09:00", + "2024-01-01T18:10:00", + "2024-01-01T18:11:00", + "2024-01-01T18:12:00", + "2024-01-01T18:13:00", + "2024-01-01T18:14:00", + "2024-01-01T18:15:00", + "2024-01-01T18:16:00", + "2024-01-01T18:17:00", + "2024-01-01T18:18:00", + "2024-01-01T18:19:00", + "2024-01-01T18:20:00", + "2024-01-01T18:21:00", + "2024-01-01T18:22:00", + "2024-01-01T18:23:00", + "2024-01-01T18:24:00", + "2024-01-01T18:25:00", + "2024-01-01T18:26:00", + "2024-01-01T18:27:00", + "2024-01-01T18:28:00", + "2024-01-01T18:29:00", + "2024-01-01T18:30:00", + "2024-01-01T18:31:00", + "2024-01-01T18:32:00", + "2024-01-01T18:33:00", + "2024-01-01T18:34:00", + "2024-01-01T18:35:00", + "2024-01-01T18:36:00", + "2024-01-01T18:37:00", + "2024-01-01T18:38:00", + "2024-01-01T18:39:00", + "2024-01-01T18:40:00", + "2024-01-01T18:41:00", + "2024-01-01T18:42:00", + "2024-01-01T18:43:00", + "2024-01-01T18:44:00", + "2024-01-01T18:45:00", + "2024-01-01T18:46:00", + "2024-01-01T18:47:00", + "2024-01-01T18:48:00", + "2024-01-01T18:49:00", + "2024-01-01T18:50:00", + "2024-01-01T18:51:00", + "2024-01-01T18:52:00", + "2024-01-01T18:53:00", + "2024-01-01T18:54:00", + "2024-01-01T18:55:00", + "2024-01-01T18:56:00", + "2024-01-01T18:57:00", + "2024-01-01T18:58:00", + "2024-01-01T18:59:00", + "2024-01-01T19:00:00", + "2024-01-01T19:01:00", + "2024-01-01T19:02:00", + "2024-01-01T19:03:00", + "2024-01-01T19:04:00", + "2024-01-01T19:05:00", + "2024-01-01T19:06:00", + "2024-01-01T19:07:00", + "2024-01-01T19:08:00", + "2024-01-01T19:09:00", + "2024-01-01T19:10:00", + "2024-01-01T19:11:00", + "2024-01-01T19:12:00", + "2024-01-01T19:13:00", + "2024-01-01T19:14:00", + "2024-01-01T19:15:00", + "2024-01-01T19:16:00", + "2024-01-01T19:17:00", + "2024-01-01T19:18:00", + "2024-01-01T19:19:00", + "2024-01-01T19:20:00", + "2024-01-01T19:21:00", + "2024-01-01T19:22:00", + "2024-01-01T19:23:00", + "2024-01-01T19:24:00", + "2024-01-01T19:25:00", + "2024-01-01T19:26:00", + "2024-01-01T19:27:00", + "2024-01-01T19:28:00", + "2024-01-01T19:29:00", + "2024-01-01T19:30:00", + "2024-01-01T19:31:00", + "2024-01-01T19:32:00", + "2024-01-01T19:33:00", + "2024-01-01T19:34:00", + "2024-01-01T19:35:00", + "2024-01-01T19:36:00", + "2024-01-01T19:37:00", + "2024-01-01T19:38:00", + "2024-01-01T19:39:00", + "2024-01-01T19:40:00", + "2024-01-01T19:41:00", + "2024-01-01T19:42:00", + "2024-01-01T19:43:00", + "2024-01-01T19:44:00", + "2024-01-01T19:45:00", + "2024-01-01T19:46:00", + "2024-01-01T19:47:00", + "2024-01-01T19:48:00", + "2024-01-01T19:49:00", + "2024-01-01T19:50:00", + "2024-01-01T19:51:00", + "2024-01-01T19:52:00", + "2024-01-01T19:53:00", + "2024-01-01T19:54:00", + "2024-01-01T19:55:00", + "2024-01-01T19:56:00", + "2024-01-01T19:57:00", + "2024-01-01T19:58:00", + "2024-01-01T19:59:00", + "2024-01-01T20:00:00", + "2024-01-01T20:01:00", + "2024-01-01T20:02:00", + "2024-01-01T20:03:00", + "2024-01-01T20:04:00", + "2024-01-01T20:05:00", + "2024-01-01T20:06:00", + "2024-01-01T20:07:00", + "2024-01-01T20:08:00", + "2024-01-01T20:09:00", + "2024-01-01T20:10:00", + "2024-01-01T20:11:00", + "2024-01-01T20:12:00", + "2024-01-01T20:13:00", + "2024-01-01T20:14:00", + "2024-01-01T20:15:00", + "2024-01-01T20:16:00", + "2024-01-01T20:17:00", + "2024-01-01T20:18:00", + "2024-01-01T20:19:00", + "2024-01-01T20:20:00", + "2024-01-01T20:21:00", + "2024-01-01T20:22:00", + "2024-01-01T20:23:00", + "2024-01-01T20:24:00", + "2024-01-01T20:25:00", + "2024-01-01T20:26:00", + "2024-01-01T20:27:00", + "2024-01-01T20:28:00", + "2024-01-01T20:29:00", + "2024-01-01T20:30:00", + "2024-01-01T20:31:00", + "2024-01-01T20:32:00", + "2024-01-01T20:33:00", + "2024-01-01T20:34:00", + "2024-01-01T20:35:00", + "2024-01-01T20:36:00", + "2024-01-01T20:37:00", + "2024-01-01T20:38:00", + "2024-01-01T20:39:00", + "2024-01-01T20:40:00", + "2024-01-01T20:41:00", + "2024-01-01T20:42:00", + "2024-01-01T20:43:00", + "2024-01-01T20:44:00", + "2024-01-01T20:45:00", + "2024-01-01T20:46:00", + "2024-01-01T20:47:00", + "2024-01-01T20:48:00", + "2024-01-01T20:49:00", + "2024-01-01T20:50:00", + "2024-01-01T20:51:00", + "2024-01-01T20:52:00", + "2024-01-01T20:53:00", + "2024-01-01T20:54:00", + "2024-01-01T20:55:00", + "2024-01-01T20:56:00", + "2024-01-01T20:57:00", + "2024-01-01T20:58:00", + "2024-01-01T20:59:00", + "2024-01-01T21:00:00", + "2024-01-01T21:01:00", + "2024-01-01T21:02:00", + "2024-01-01T21:03:00", + "2024-01-01T21:04:00", + "2024-01-01T21:05:00", + "2024-01-01T21:06:00", + "2024-01-01T21:07:00", + "2024-01-01T21:08:00", + "2024-01-01T21:09:00", + "2024-01-01T21:10:00", + "2024-01-01T21:11:00", + "2024-01-01T21:12:00", + "2024-01-01T21:13:00", + "2024-01-01T21:14:00", + "2024-01-01T21:15:00", + "2024-01-01T21:16:00", + "2024-01-01T21:17:00", + "2024-01-01T21:18:00", + "2024-01-01T21:19:00", + "2024-01-01T21:20:00", + "2024-01-01T21:21:00", + "2024-01-01T21:22:00", + "2024-01-01T21:23:00", + "2024-01-01T21:24:00", + "2024-01-01T21:25:00", + "2024-01-01T21:26:00", + "2024-01-01T21:27:00", + "2024-01-01T21:28:00", + "2024-01-01T21:29:00", + "2024-01-01T21:30:00", + "2024-01-01T21:31:00", + "2024-01-01T21:32:00", + "2024-01-01T21:33:00", + "2024-01-01T21:34:00", + "2024-01-01T21:35:00", + "2024-01-01T21:36:00", + "2024-01-01T21:37:00", + "2024-01-01T21:38:00", + "2024-01-01T21:39:00", + "2024-01-01T21:40:00", + "2024-01-01T21:41:00", + "2024-01-01T21:42:00", + "2024-01-01T21:43:00", + "2024-01-01T21:44:00", + "2024-01-01T21:45:00", + "2024-01-01T21:46:00", + "2024-01-01T21:47:00", + "2024-01-01T21:48:00", + "2024-01-01T21:49:00", + "2024-01-01T21:50:00", + "2024-01-01T21:51:00", + "2024-01-01T21:52:00", + "2024-01-01T21:53:00", + "2024-01-01T21:54:00", + "2024-01-01T21:55:00", + "2024-01-01T21:56:00", + "2024-01-01T21:57:00", + "2024-01-01T21:58:00", + "2024-01-01T21:59:00", + "2024-01-01T22:00:00", + "2024-01-01T22:01:00", + "2024-01-01T22:02:00", + "2024-01-01T22:03:00", + "2024-01-01T22:04:00", + "2024-01-01T22:05:00", + "2024-01-01T22:06:00", + "2024-01-01T22:07:00", + "2024-01-01T22:08:00", + "2024-01-01T22:09:00", + "2024-01-01T22:10:00", + "2024-01-01T22:11:00", + "2024-01-01T22:12:00", + "2024-01-01T22:13:00", + "2024-01-01T22:14:00", + "2024-01-01T22:15:00", + "2024-01-01T22:16:00", + "2024-01-01T22:17:00", + "2024-01-01T22:18:00", + "2024-01-01T22:19:00", + "2024-01-01T22:20:00", + "2024-01-01T22:21:00", + "2024-01-01T22:22:00", + "2024-01-01T22:23:00", + "2024-01-01T22:24:00", + "2024-01-01T22:25:00", + "2024-01-01T22:26:00", + "2024-01-01T22:27:00", + "2024-01-01T22:28:00", + "2024-01-01T22:29:00", + "2024-01-01T22:30:00", + "2024-01-01T22:31:00", + "2024-01-01T22:32:00", + "2024-01-01T22:33:00", + "2024-01-01T22:34:00", + "2024-01-01T22:35:00", + "2024-01-01T22:36:00", + "2024-01-01T22:37:00", + "2024-01-01T22:38:00", + "2024-01-01T22:39:00", + "2024-01-01T22:40:00", + "2024-01-01T22:41:00", + "2024-01-01T22:42:00", + "2024-01-01T22:43:00", + "2024-01-01T22:44:00", + "2024-01-01T22:45:00", + "2024-01-01T22:46:00", + "2024-01-01T22:47:00", + "2024-01-01T22:48:00", + "2024-01-01T22:49:00", + "2024-01-01T22:50:00", + "2024-01-01T22:51:00", + "2024-01-01T22:52:00", + "2024-01-01T22:53:00", + "2024-01-01T22:54:00", + "2024-01-01T22:55:00", + "2024-01-01T22:56:00", + "2024-01-01T22:57:00", + "2024-01-01T22:58:00", + "2024-01-01T22:59:00", + "2024-01-01T23:00:00", + "2024-01-01T23:01:00", + "2024-01-01T23:02:00", + "2024-01-01T23:03:00", + "2024-01-01T23:04:00", + "2024-01-01T23:05:00", + "2024-01-01T23:06:00", + "2024-01-01T23:07:00", + "2024-01-01T23:08:00", + "2024-01-01T23:09:00", + "2024-01-01T23:10:00", + "2024-01-01T23:11:00", + "2024-01-01T23:12:00", + "2024-01-01T23:13:00", + "2024-01-01T23:14:00", + "2024-01-01T23:15:00", + "2024-01-01T23:16:00", + "2024-01-01T23:17:00", + "2024-01-01T23:18:00", + "2024-01-01T23:19:00", + "2024-01-01T23:20:00", + "2024-01-01T23:21:00", + "2024-01-01T23:22:00", + "2024-01-01T23:23:00", + "2024-01-01T23:24:00", + "2024-01-01T23:25:00", + "2024-01-01T23:26:00", + "2024-01-01T23:27:00", + "2024-01-01T23:28:00", + "2024-01-01T23:29:00", + "2024-01-01T23:30:00", + "2024-01-01T23:31:00", + "2024-01-01T23:32:00", + "2024-01-01T23:33:00", + "2024-01-01T23:34:00", + "2024-01-01T23:35:00", + "2024-01-01T23:36:00", + "2024-01-01T23:37:00", + "2024-01-01T23:38:00", + "2024-01-01T23:39:00", + "2024-01-01T23:40:00", + "2024-01-01T23:41:00", + "2024-01-01T23:42:00", + "2024-01-01T23:43:00", + "2024-01-01T23:44:00", + "2024-01-01T23:45:00", + "2024-01-01T23:46:00", + "2024-01-01T23:47:00", + "2024-01-01T23:48:00", + "2024-01-01T23:49:00", + "2024-01-01T23:50:00", + "2024-01-01T23:51:00", + "2024-01-01T23:52:00", + "2024-01-01T23:53:00", + "2024-01-01T23:54:00", + "2024-01-01T23:55:00", + "2024-01-01T23:56:00", + "2024-01-01T23:57:00", + "2024-01-01T23:58:00", + "2024-01-01T23:59:00", + "2024-01-02T00:00:00", + "2024-01-02T00:01:00", + "2024-01-02T00:02:00", + "2024-01-02T00:03:00", + "2024-01-02T00:04:00", + "2024-01-02T00:05:00", + "2024-01-02T00:06:00", + "2024-01-02T00:07:00", + "2024-01-02T00:08:00", + "2024-01-02T00:09:00", + "2024-01-02T00:10:00", + "2024-01-02T00:11:00", + "2024-01-02T00:12:00", + "2024-01-02T00:13:00", + "2024-01-02T00:14:00", + "2024-01-02T00:15:00", + "2024-01-02T00:16:00", + "2024-01-02T00:17:00", + "2024-01-02T00:18:00", + "2024-01-02T00:19:00", + "2024-01-02T00:20:00", + "2024-01-02T00:21:00", + "2024-01-02T00:22:00", + "2024-01-02T00:23:00", + "2024-01-02T00:24:00", + "2024-01-02T00:25:00", + "2024-01-02T00:26:00", + "2024-01-02T00:27:00", + "2024-01-02T00:28:00", + "2024-01-02T00:29:00", + "2024-01-02T00:30:00", + "2024-01-02T00:31:00", + "2024-01-02T00:32:00", + "2024-01-02T00:33:00", + "2024-01-02T00:34:00", + "2024-01-02T00:35:00", + "2024-01-02T00:36:00", + "2024-01-02T00:37:00", + "2024-01-02T00:38:00", + "2024-01-02T00:39:00", + "2024-01-02T00:40:00", + "2024-01-02T00:41:00", + "2024-01-02T00:42:00", + "2024-01-02T00:43:00", + "2024-01-02T00:44:00", + "2024-01-02T00:45:00", + "2024-01-02T00:46:00", + "2024-01-02T00:47:00", + "2024-01-02T00:48:00", + "2024-01-02T00:49:00", + "2024-01-02T00:50:00", + "2024-01-02T00:51:00", + "2024-01-02T00:52:00", + "2024-01-02T00:53:00", + "2024-01-02T00:54:00", + "2024-01-02T00:55:00", + "2024-01-02T00:56:00", + "2024-01-02T00:57:00", + "2024-01-02T00:58:00", + "2024-01-02T00:59:00", + "2024-01-02T01:00:00", + "2024-01-02T01:01:00", + "2024-01-02T01:02:00", + "2024-01-02T01:03:00", + "2024-01-02T01:04:00", + "2024-01-02T01:05:00", + "2024-01-02T01:06:00", + "2024-01-02T01:07:00", + "2024-01-02T01:08:00", + "2024-01-02T01:09:00", + "2024-01-02T01:10:00", + "2024-01-02T01:11:00", + "2024-01-02T01:12:00", + "2024-01-02T01:13:00", + "2024-01-02T01:14:00", + "2024-01-02T01:15:00", + "2024-01-02T01:16:00", + "2024-01-02T01:17:00", + "2024-01-02T01:18:00", + "2024-01-02T01:19:00", + "2024-01-02T01:20:00", + "2024-01-02T01:21:00", + "2024-01-02T01:22:00", + "2024-01-02T01:23:00", + "2024-01-02T01:24:00", + "2024-01-02T01:25:00", + "2024-01-02T01:26:00", + "2024-01-02T01:27:00", + "2024-01-02T01:28:00", + "2024-01-02T01:29:00", + "2024-01-02T01:30:00", + "2024-01-02T01:31:00", + "2024-01-02T01:32:00", + "2024-01-02T01:33:00", + "2024-01-02T01:34:00", + "2024-01-02T01:35:00", + "2024-01-02T01:36:00", + "2024-01-02T01:37:00", + "2024-01-02T01:38:00", + "2024-01-02T01:39:00", + "2024-01-02T01:40:00", + "2024-01-02T01:41:00", + "2024-01-02T01:42:00", + "2024-01-02T01:43:00", + "2024-01-02T01:44:00", + "2024-01-02T01:45:00", + "2024-01-02T01:46:00", + "2024-01-02T01:47:00", + "2024-01-02T01:48:00", + "2024-01-02T01:49:00", + "2024-01-02T01:50:00", + "2024-01-02T01:51:00", + "2024-01-02T01:52:00", + "2024-01-02T01:53:00", + "2024-01-02T01:54:00", + "2024-01-02T01:55:00", + "2024-01-02T01:56:00", + "2024-01-02T01:57:00", + "2024-01-02T01:58:00", + "2024-01-02T01:59:00", + "2024-01-02T02:00:00", + "2024-01-02T02:01:00", + "2024-01-02T02:02:00", + "2024-01-02T02:03:00", + "2024-01-02T02:04:00", + "2024-01-02T02:05:00", + "2024-01-02T02:06:00", + "2024-01-02T02:07:00", + "2024-01-02T02:08:00", + "2024-01-02T02:09:00", + "2024-01-02T02:10:00", + "2024-01-02T02:11:00", + "2024-01-02T02:12:00", + "2024-01-02T02:13:00", + "2024-01-02T02:14:00", + "2024-01-02T02:15:00", + "2024-01-02T02:16:00", + "2024-01-02T02:17:00", + "2024-01-02T02:18:00", + "2024-01-02T02:19:00", + "2024-01-02T02:20:00", + "2024-01-02T02:21:00", + "2024-01-02T02:22:00", + "2024-01-02T02:23:00", + "2024-01-02T02:24:00", + "2024-01-02T02:25:00", + "2024-01-02T02:26:00", + "2024-01-02T02:27:00", + "2024-01-02T02:28:00", + "2024-01-02T02:29:00", + "2024-01-02T02:30:00", + "2024-01-02T02:31:00", + "2024-01-02T02:32:00", + "2024-01-02T02:33:00", + "2024-01-02T02:34:00", + "2024-01-02T02:35:00", + "2024-01-02T02:36:00", + "2024-01-02T02:37:00", + "2024-01-02T02:38:00", + "2024-01-02T02:39:00", + "2024-01-02T02:40:00", + "2024-01-02T02:41:00", + "2024-01-02T02:42:00", + "2024-01-02T02:43:00", + "2024-01-02T02:44:00", + "2024-01-02T02:45:00", + "2024-01-02T02:46:00", + "2024-01-02T02:47:00", + "2024-01-02T02:48:00", + "2024-01-02T02:49:00", + "2024-01-02T02:50:00", + "2024-01-02T02:51:00", + "2024-01-02T02:52:00", + "2024-01-02T02:53:00", + "2024-01-02T02:54:00", + "2024-01-02T02:55:00", + "2024-01-02T02:56:00", + "2024-01-02T02:57:00", + "2024-01-02T02:58:00", + "2024-01-02T02:59:00", + "2024-01-02T03:00:00", + "2024-01-02T03:01:00", + "2024-01-02T03:02:00", + "2024-01-02T03:03:00", + "2024-01-02T03:04:00", + "2024-01-02T03:05:00", + "2024-01-02T03:06:00", + "2024-01-02T03:07:00", + "2024-01-02T03:08:00", + "2024-01-02T03:09:00", + "2024-01-02T03:10:00", + "2024-01-02T03:11:00", + "2024-01-02T03:12:00", + "2024-01-02T03:13:00", + "2024-01-02T03:14:00", + "2024-01-02T03:15:00", + "2024-01-02T03:16:00", + "2024-01-02T03:17:00", + "2024-01-02T03:18:00", + "2024-01-02T03:19:00", + "2024-01-02T03:20:00", + "2024-01-02T03:21:00", + "2024-01-02T03:22:00", + "2024-01-02T03:23:00", + "2024-01-02T03:24:00", + "2024-01-02T03:25:00", + "2024-01-02T03:26:00", + "2024-01-02T03:27:00", + "2024-01-02T03:28:00", + "2024-01-02T03:29:00", + "2024-01-02T03:30:00", + "2024-01-02T03:31:00", + "2024-01-02T03:32:00", + "2024-01-02T03:33:00", + "2024-01-02T03:34:00", + "2024-01-02T03:35:00", + "2024-01-02T03:36:00", + "2024-01-02T03:37:00", + "2024-01-02T03:38:00", + "2024-01-02T03:39:00", + "2024-01-02T03:40:00", + "2024-01-02T03:41:00", + "2024-01-02T03:42:00", + "2024-01-02T03:43:00", + "2024-01-02T03:44:00", + "2024-01-02T03:45:00", + "2024-01-02T03:46:00", + "2024-01-02T03:47:00", + "2024-01-02T03:48:00", + "2024-01-02T03:49:00", + "2024-01-02T03:50:00", + "2024-01-02T03:51:00", + "2024-01-02T03:52:00", + "2024-01-02T03:53:00", + "2024-01-02T03:54:00", + "2024-01-02T03:55:00", + "2024-01-02T03:56:00", + "2024-01-02T03:57:00", + "2024-01-02T03:58:00", + "2024-01-02T03:59:00", + "2024-01-02T04:00:00", + "2024-01-02T04:01:00", + "2024-01-02T04:02:00", + "2024-01-02T04:03:00", + "2024-01-02T04:04:00", + "2024-01-02T04:05:00", + "2024-01-02T04:06:00", + "2024-01-02T04:07:00", + "2024-01-02T04:08:00", + "2024-01-02T04:09:00", + "2024-01-02T04:10:00", + "2024-01-02T04:11:00", + "2024-01-02T04:12:00", + "2024-01-02T04:13:00", + "2024-01-02T04:14:00", + "2024-01-02T04:15:00", + "2024-01-02T04:16:00", + "2024-01-02T04:17:00", + "2024-01-02T04:18:00", + "2024-01-02T04:19:00", + "2024-01-02T04:20:00", + "2024-01-02T04:21:00", + "2024-01-02T04:22:00", + "2024-01-02T04:23:00", + "2024-01-02T04:24:00", + "2024-01-02T04:25:00", + "2024-01-02T04:26:00", + "2024-01-02T04:27:00", + "2024-01-02T04:28:00", + "2024-01-02T04:29:00", + "2024-01-02T04:30:00", + "2024-01-02T04:31:00", + "2024-01-02T04:32:00", + "2024-01-02T04:33:00", + "2024-01-02T04:34:00", + "2024-01-02T04:35:00", + "2024-01-02T04:36:00", + "2024-01-02T04:37:00", + "2024-01-02T04:38:00", + "2024-01-02T04:39:00", + "2024-01-02T04:40:00", + "2024-01-02T04:41:00", + "2024-01-02T04:42:00", + "2024-01-02T04:43:00", + "2024-01-02T04:44:00", + "2024-01-02T04:45:00", + "2024-01-02T04:46:00", + "2024-01-02T04:47:00", + "2024-01-02T04:48:00", + "2024-01-02T04:49:00", + "2024-01-02T04:50:00", + "2024-01-02T04:51:00", + "2024-01-02T04:52:00", + "2024-01-02T04:53:00", + "2024-01-02T04:54:00", + "2024-01-02T04:55:00", + "2024-01-02T04:56:00", + "2024-01-02T04:57:00", + "2024-01-02T04:58:00", + "2024-01-02T04:59:00", + "2024-01-02T05:00:00", + "2024-01-02T05:01:00", + "2024-01-02T05:02:00", + "2024-01-02T05:03:00", + "2024-01-02T05:04:00", + "2024-01-02T05:05:00", + "2024-01-02T05:06:00", + "2024-01-02T05:07:00", + "2024-01-02T05:08:00", + "2024-01-02T05:09:00", + "2024-01-02T05:10:00", + "2024-01-02T05:11:00", + "2024-01-02T05:12:00", + "2024-01-02T05:13:00", + "2024-01-02T05:14:00", + "2024-01-02T05:15:00", + "2024-01-02T05:16:00", + "2024-01-02T05:17:00", + "2024-01-02T05:18:00", + "2024-01-02T05:19:00", + "2024-01-02T05:20:00", + "2024-01-02T05:21:00", + "2024-01-02T05:22:00", + "2024-01-02T05:23:00", + "2024-01-02T05:24:00", + "2024-01-02T05:25:00", + "2024-01-02T05:26:00", + "2024-01-02T05:27:00", + "2024-01-02T05:28:00", + "2024-01-02T05:29:00", + "2024-01-02T05:30:00", + "2024-01-02T05:31:00", + "2024-01-02T05:32:00", + "2024-01-02T05:33:00", + "2024-01-02T05:34:00", + "2024-01-02T05:35:00", + "2024-01-02T05:36:00", + "2024-01-02T05:37:00", + "2024-01-02T05:38:00", + "2024-01-02T05:39:00", + "2024-01-02T05:40:00", + "2024-01-02T05:41:00", + "2024-01-02T05:42:00", + "2024-01-02T05:43:00", + "2024-01-02T05:44:00", + "2024-01-02T05:45:00", + "2024-01-02T05:46:00", + "2024-01-02T05:47:00", + "2024-01-02T05:48:00", + "2024-01-02T05:49:00", + "2024-01-02T05:50:00", + "2024-01-02T05:51:00", + "2024-01-02T05:52:00", + "2024-01-02T05:53:00", + "2024-01-02T05:54:00", + "2024-01-02T05:55:00", + "2024-01-02T05:56:00", + "2024-01-02T05:57:00", + "2024-01-02T05:58:00", + "2024-01-02T05:59:00", + "2024-01-02T06:00:00", + "2024-01-02T06:01:00", + "2024-01-02T06:02:00", + "2024-01-02T06:03:00", + "2024-01-02T06:04:00", + "2024-01-02T06:05:00", + "2024-01-02T06:06:00", + "2024-01-02T06:07:00", + "2024-01-02T06:08:00", + "2024-01-02T06:09:00", + "2024-01-02T06:10:00", + "2024-01-02T06:11:00", + "2024-01-02T06:12:00", + "2024-01-02T06:13:00", + "2024-01-02T06:14:00", + "2024-01-02T06:15:00", + "2024-01-02T06:16:00", + "2024-01-02T06:17:00", + "2024-01-02T06:18:00", + "2024-01-02T06:19:00", + "2024-01-02T06:20:00", + "2024-01-02T06:21:00", + "2024-01-02T06:22:00", + "2024-01-02T06:23:00", + "2024-01-02T06:24:00", + "2024-01-02T06:25:00", + "2024-01-02T06:26:00", + "2024-01-02T06:27:00", + "2024-01-02T06:28:00", + "2024-01-02T06:29:00", + "2024-01-02T06:30:00", + "2024-01-02T06:31:00", + "2024-01-02T06:32:00", + "2024-01-02T06:33:00", + "2024-01-02T06:34:00", + "2024-01-02T06:35:00", + "2024-01-02T06:36:00", + "2024-01-02T06:37:00", + "2024-01-02T06:38:00", + "2024-01-02T06:39:00", + "2024-01-02T06:40:00", + "2024-01-02T06:41:00", + "2024-01-02T06:42:00", + "2024-01-02T06:43:00", + "2024-01-02T06:44:00", + "2024-01-02T06:45:00", + "2024-01-02T06:46:00", + "2024-01-02T06:47:00", + "2024-01-02T06:48:00", + "2024-01-02T06:49:00", + "2024-01-02T06:50:00", + "2024-01-02T06:51:00", + "2024-01-02T06:52:00", + "2024-01-02T06:53:00", + "2024-01-02T06:54:00", + "2024-01-02T06:55:00", + "2024-01-02T06:56:00", + "2024-01-02T06:57:00", + "2024-01-02T06:58:00", + "2024-01-02T06:59:00", + "2024-01-02T07:00:00", + "2024-01-02T07:01:00", + "2024-01-02T07:02:00", + "2024-01-02T07:03:00", + "2024-01-02T07:04:00", + "2024-01-02T07:05:00", + "2024-01-02T07:06:00", + "2024-01-02T07:07:00", + "2024-01-02T07:08:00", + "2024-01-02T07:09:00", + "2024-01-02T07:10:00", + "2024-01-02T07:11:00", + "2024-01-02T07:12:00", + "2024-01-02T07:13:00", + "2024-01-02T07:14:00", + "2024-01-02T07:15:00", + "2024-01-02T07:16:00", + "2024-01-02T07:17:00", + "2024-01-02T07:18:00", + "2024-01-02T07:19:00", + "2024-01-02T07:20:00", + "2024-01-02T07:21:00", + "2024-01-02T07:22:00", + "2024-01-02T07:23:00", + "2024-01-02T07:24:00", + "2024-01-02T07:25:00", + "2024-01-02T07:26:00", + "2024-01-02T07:27:00", + "2024-01-02T07:28:00", + "2024-01-02T07:29:00", + "2024-01-02T07:30:00", + "2024-01-02T07:31:00", + "2024-01-02T07:32:00", + "2024-01-02T07:33:00", + "2024-01-02T07:34:00", + "2024-01-02T07:35:00", + "2024-01-02T07:36:00", + "2024-01-02T07:37:00", + "2024-01-02T07:38:00", + "2024-01-02T07:39:00", + "2024-01-02T07:40:00", + "2024-01-02T07:41:00", + "2024-01-02T07:42:00", + "2024-01-02T07:43:00", + "2024-01-02T07:44:00", + "2024-01-02T07:45:00", + "2024-01-02T07:46:00", + "2024-01-02T07:47:00", + "2024-01-02T07:48:00", + "2024-01-02T07:49:00", + "2024-01-02T07:50:00", + "2024-01-02T07:51:00", + "2024-01-02T07:52:00", + "2024-01-02T07:53:00", + "2024-01-02T07:54:00", + "2024-01-02T07:55:00", + "2024-01-02T07:56:00", + "2024-01-02T07:57:00", + "2024-01-02T07:58:00", + "2024-01-02T07:59:00", + "2024-01-02T08:00:00", + "2024-01-02T08:01:00", + "2024-01-02T08:02:00", + "2024-01-02T08:03:00", + "2024-01-02T08:04:00", + "2024-01-02T08:05:00", + "2024-01-02T08:06:00", + "2024-01-02T08:07:00", + "2024-01-02T08:08:00", + "2024-01-02T08:09:00", + "2024-01-02T08:10:00", + "2024-01-02T08:11:00", + "2024-01-02T08:12:00", + "2024-01-02T08:13:00", + "2024-01-02T08:14:00", + "2024-01-02T08:15:00", + "2024-01-02T08:16:00", + "2024-01-02T08:17:00", + "2024-01-02T08:18:00", + "2024-01-02T08:19:00", + "2024-01-02T08:20:00", + "2024-01-02T08:21:00", + "2024-01-02T08:22:00", + "2024-01-02T08:23:00", + "2024-01-02T08:24:00", + "2024-01-02T08:25:00", + "2024-01-02T08:26:00", + "2024-01-02T08:27:00", + "2024-01-02T08:28:00", + "2024-01-02T08:29:00", + "2024-01-02T08:30:00", + "2024-01-02T08:31:00", + "2024-01-02T08:32:00", + "2024-01-02T08:33:00", + "2024-01-02T08:34:00", + "2024-01-02T08:35:00", + "2024-01-02T08:36:00", + "2024-01-02T08:37:00", + "2024-01-02T08:38:00", + "2024-01-02T08:39:00", + "2024-01-02T08:40:00", + "2024-01-02T08:41:00", + "2024-01-02T08:42:00", + "2024-01-02T08:43:00", + "2024-01-02T08:44:00", + "2024-01-02T08:45:00", + "2024-01-02T08:46:00", + "2024-01-02T08:47:00", + "2024-01-02T08:48:00", + "2024-01-02T08:49:00", + "2024-01-02T08:50:00", + "2024-01-02T08:51:00", + "2024-01-02T08:52:00", + "2024-01-02T08:53:00", + "2024-01-02T08:54:00", + "2024-01-02T08:55:00", + "2024-01-02T08:56:00", + "2024-01-02T08:57:00", + "2024-01-02T08:58:00", + "2024-01-02T08:59:00", + "2024-01-02T09:00:00", + "2024-01-02T09:01:00", + "2024-01-02T09:02:00", + "2024-01-02T09:03:00", + "2024-01-02T09:04:00", + "2024-01-02T09:05:00", + "2024-01-02T09:06:00", + "2024-01-02T09:07:00", + "2024-01-02T09:08:00", + "2024-01-02T09:09:00", + "2024-01-02T09:10:00", + "2024-01-02T09:11:00", + "2024-01-02T09:12:00", + "2024-01-02T09:13:00", + "2024-01-02T09:14:00", + "2024-01-02T09:15:00", + "2024-01-02T09:16:00", + "2024-01-02T09:17:00", + "2024-01-02T09:18:00", + "2024-01-02T09:19:00", + "2024-01-02T09:20:00", + "2024-01-02T09:21:00", + "2024-01-02T09:22:00", + "2024-01-02T09:23:00", + "2024-01-02T09:24:00", + "2024-01-02T09:25:00", + "2024-01-02T09:26:00", + "2024-01-02T09:27:00", + "2024-01-02T09:28:00", + "2024-01-02T09:29:00", + "2024-01-02T09:30:00", + "2024-01-02T09:31:00", + "2024-01-02T09:32:00", + "2024-01-02T09:33:00", + "2024-01-02T09:34:00", + "2024-01-02T09:35:00", + "2024-01-02T09:36:00", + "2024-01-02T09:37:00", + "2024-01-02T09:38:00", + "2024-01-02T09:39:00", + "2024-01-02T09:40:00", + "2024-01-02T09:41:00", + "2024-01-02T09:42:00", + "2024-01-02T09:43:00", + "2024-01-02T09:44:00", + "2024-01-02T09:45:00", + "2024-01-02T09:46:00", + "2024-01-02T09:47:00", + "2024-01-02T09:48:00", + "2024-01-02T09:49:00", + "2024-01-02T09:50:00", + "2024-01-02T09:51:00", + "2024-01-02T09:52:00", + "2024-01-02T09:53:00", + "2024-01-02T09:54:00", + "2024-01-02T09:55:00", + "2024-01-02T09:56:00", + "2024-01-02T09:57:00", + "2024-01-02T09:58:00", + "2024-01-02T09:59:00", + "2024-01-02T10:00:00", + "2024-01-02T10:01:00", + "2024-01-02T10:02:00", + "2024-01-02T10:03:00", + "2024-01-02T10:04:00", + "2024-01-02T10:05:00", + "2024-01-02T10:06:00", + "2024-01-02T10:07:00", + "2024-01-02T10:08:00", + "2024-01-02T10:09:00", + "2024-01-02T10:10:00", + "2024-01-02T10:11:00", + "2024-01-02T10:12:00", + "2024-01-02T10:13:00", + "2024-01-02T10:14:00", + "2024-01-02T10:15:00", + "2024-01-02T10:16:00", + "2024-01-02T10:17:00", + "2024-01-02T10:18:00", + "2024-01-02T10:19:00", + "2024-01-02T10:20:00", + "2024-01-02T10:21:00", + "2024-01-02T10:22:00", + "2024-01-02T10:23:00", + "2024-01-02T10:24:00", + "2024-01-02T10:25:00", + "2024-01-02T10:26:00", + "2024-01-02T10:27:00", + "2024-01-02T10:28:00", + "2024-01-02T10:29:00", + "2024-01-02T10:30:00", + "2024-01-02T10:31:00", + "2024-01-02T10:32:00", + "2024-01-02T10:33:00", + "2024-01-02T10:34:00", + "2024-01-02T10:35:00", + "2024-01-02T10:36:00", + "2024-01-02T10:37:00", + "2024-01-02T10:38:00", + "2024-01-02T10:39:00", + "2024-01-02T10:40:00", + "2024-01-02T10:41:00", + "2024-01-02T10:42:00", + "2024-01-02T10:43:00", + "2024-01-02T10:44:00", + "2024-01-02T10:45:00", + "2024-01-02T10:46:00", + "2024-01-02T10:47:00", + "2024-01-02T10:48:00", + "2024-01-02T10:49:00", + "2024-01-02T10:50:00", + "2024-01-02T10:51:00", + "2024-01-02T10:52:00", + "2024-01-02T10:53:00", + "2024-01-02T10:54:00", + "2024-01-02T10:55:00", + "2024-01-02T10:56:00", + "2024-01-02T10:57:00", + "2024-01-02T10:58:00", + "2024-01-02T10:59:00", + "2024-01-02T11:00:00", + "2024-01-02T11:01:00", + "2024-01-02T11:02:00", + "2024-01-02T11:03:00", + "2024-01-02T11:04:00", + "2024-01-02T11:05:00", + "2024-01-02T11:06:00", + "2024-01-02T11:07:00", + "2024-01-02T11:08:00", + "2024-01-02T11:09:00", + "2024-01-02T11:10:00", + "2024-01-02T11:11:00", + "2024-01-02T11:12:00", + "2024-01-02T11:13:00", + "2024-01-02T11:14:00", + "2024-01-02T11:15:00", + "2024-01-02T11:16:00", + "2024-01-02T11:17:00", + "2024-01-02T11:18:00", + "2024-01-02T11:19:00", + "2024-01-02T11:20:00", + "2024-01-02T11:21:00", + "2024-01-02T11:22:00", + "2024-01-02T11:23:00", + "2024-01-02T11:24:00", + "2024-01-02T11:25:00", + "2024-01-02T11:26:00", + "2024-01-02T11:27:00", + "2024-01-02T11:28:00", + "2024-01-02T11:29:00", + "2024-01-02T11:30:00", + "2024-01-02T11:31:00", + "2024-01-02T11:32:00", + "2024-01-02T11:33:00", + "2024-01-02T11:34:00", + "2024-01-02T11:35:00", + "2024-01-02T11:36:00", + "2024-01-02T11:37:00", + "2024-01-02T11:38:00", + "2024-01-02T11:39:00", + "2024-01-02T11:40:00", + "2024-01-02T11:41:00", + "2024-01-02T11:42:00", + "2024-01-02T11:43:00", + "2024-01-02T11:44:00", + "2024-01-02T11:45:00", + "2024-01-02T11:46:00", + "2024-01-02T11:47:00", + "2024-01-02T11:48:00", + "2024-01-02T11:49:00", + "2024-01-02T11:50:00", + "2024-01-02T11:51:00", + "2024-01-02T11:52:00", + "2024-01-02T11:53:00", + "2024-01-02T11:54:00", + "2024-01-02T11:55:00", + "2024-01-02T11:56:00", + "2024-01-02T11:57:00", + "2024-01-02T11:58:00", + "2024-01-02T11:59:00", + "2024-01-02T12:00:00", + "2024-01-02T12:01:00", + "2024-01-02T12:02:00", + "2024-01-02T12:03:00", + "2024-01-02T12:04:00", + "2024-01-02T12:05:00", + "2024-01-02T12:06:00", + "2024-01-02T12:07:00", + "2024-01-02T12:08:00", + "2024-01-02T12:09:00", + "2024-01-02T12:10:00", + "2024-01-02T12:11:00", + "2024-01-02T12:12:00", + "2024-01-02T12:13:00", + "2024-01-02T12:14:00", + "2024-01-02T12:15:00", + "2024-01-02T12:16:00", + "2024-01-02T12:17:00", + "2024-01-02T12:18:00", + "2024-01-02T12:19:00", + "2024-01-02T12:20:00", + "2024-01-02T12:21:00", + "2024-01-02T12:22:00", + "2024-01-02T12:23:00", + "2024-01-02T12:24:00", + "2024-01-02T12:25:00", + "2024-01-02T12:26:00", + "2024-01-02T12:27:00", + "2024-01-02T12:28:00", + "2024-01-02T12:29:00", + "2024-01-02T12:30:00", + "2024-01-02T12:31:00", + "2024-01-02T12:32:00", + "2024-01-02T12:33:00", + "2024-01-02T12:34:00", + "2024-01-02T12:35:00", + "2024-01-02T12:36:00", + "2024-01-02T12:37:00", + "2024-01-02T12:38:00", + "2024-01-02T12:39:00", + "2024-01-02T12:40:00", + "2024-01-02T12:41:00", + "2024-01-02T12:42:00", + "2024-01-02T12:43:00", + "2024-01-02T12:44:00", + "2024-01-02T12:45:00", + "2024-01-02T12:46:00", + "2024-01-02T12:47:00", + "2024-01-02T12:48:00", + "2024-01-02T12:49:00", + "2024-01-02T12:50:00", + "2024-01-02T12:51:00", + "2024-01-02T12:52:00", + "2024-01-02T12:53:00", + "2024-01-02T12:54:00", + "2024-01-02T12:55:00", + "2024-01-02T12:56:00", + "2024-01-02T12:57:00", + "2024-01-02T12:58:00", + "2024-01-02T12:59:00", + "2024-01-02T13:00:00", + "2024-01-02T13:01:00", + "2024-01-02T13:02:00", + "2024-01-02T13:03:00", + "2024-01-02T13:04:00", + "2024-01-02T13:05:00", + "2024-01-02T13:06:00", + "2024-01-02T13:07:00", + "2024-01-02T13:08:00", + "2024-01-02T13:09:00", + "2024-01-02T13:10:00", + "2024-01-02T13:11:00", + "2024-01-02T13:12:00", + "2024-01-02T13:13:00", + "2024-01-02T13:14:00", + "2024-01-02T13:15:00", + "2024-01-02T13:16:00", + "2024-01-02T13:17:00", + "2024-01-02T13:18:00", + "2024-01-02T13:19:00", + "2024-01-02T13:20:00", + "2024-01-02T13:21:00", + "2024-01-02T13:22:00", + "2024-01-02T13:23:00", + "2024-01-02T13:24:00", + "2024-01-02T13:25:00", + "2024-01-02T13:26:00", + "2024-01-02T13:27:00", + "2024-01-02T13:28:00", + "2024-01-02T13:29:00", + "2024-01-02T13:30:00", + "2024-01-02T13:31:00", + "2024-01-02T13:32:00", + "2024-01-02T13:33:00", + "2024-01-02T13:34:00", + "2024-01-02T13:35:00", + "2024-01-02T13:36:00", + "2024-01-02T13:37:00", + "2024-01-02T13:38:00", + "2024-01-02T13:39:00", + "2024-01-02T13:40:00", + "2024-01-02T13:41:00", + "2024-01-02T13:42:00", + "2024-01-02T13:43:00", + "2024-01-02T13:44:00", + "2024-01-02T13:45:00", + "2024-01-02T13:46:00", + "2024-01-02T13:47:00", + "2024-01-02T13:48:00", + "2024-01-02T13:49:00", + "2024-01-02T13:50:00", + "2024-01-02T13:51:00", + "2024-01-02T13:52:00", + "2024-01-02T13:53:00", + "2024-01-02T13:54:00", + "2024-01-02T13:55:00", + "2024-01-02T13:56:00", + "2024-01-02T13:57:00", + "2024-01-02T13:58:00", + "2024-01-02T13:59:00", + "2024-01-02T14:00:00", + "2024-01-02T14:01:00", + "2024-01-02T14:02:00", + "2024-01-02T14:03:00", + "2024-01-02T14:04:00", + "2024-01-02T14:05:00", + "2024-01-02T14:06:00", + "2024-01-02T14:07:00", + "2024-01-02T14:08:00", + "2024-01-02T14:09:00", + "2024-01-02T14:10:00", + "2024-01-02T14:11:00", + "2024-01-02T14:12:00", + "2024-01-02T14:13:00", + "2024-01-02T14:14:00", + "2024-01-02T14:15:00", + "2024-01-02T14:16:00", + "2024-01-02T14:17:00", + "2024-01-02T14:18:00", + "2024-01-02T14:19:00", + "2024-01-02T14:20:00", + "2024-01-02T14:21:00", + "2024-01-02T14:22:00", + "2024-01-02T14:23:00", + "2024-01-02T14:24:00", + "2024-01-02T14:25:00", + "2024-01-02T14:26:00", + "2024-01-02T14:27:00", + "2024-01-02T14:28:00", + "2024-01-02T14:29:00", + "2024-01-02T14:30:00", + "2024-01-02T14:31:00", + "2024-01-02T14:32:00", + "2024-01-02T14:33:00", + "2024-01-02T14:34:00", + "2024-01-02T14:35:00", + "2024-01-02T14:36:00", + "2024-01-02T14:37:00", + "2024-01-02T14:38:00", + "2024-01-02T14:39:00", + "2024-01-02T14:40:00", + "2024-01-02T14:41:00", + "2024-01-02T14:42:00", + "2024-01-02T14:43:00", + "2024-01-02T14:44:00", + "2024-01-02T14:45:00", + "2024-01-02T14:46:00", + "2024-01-02T14:47:00", + "2024-01-02T14:48:00", + "2024-01-02T14:49:00", + "2024-01-02T14:50:00", + "2024-01-02T14:51:00", + "2024-01-02T14:52:00", + "2024-01-02T14:53:00", + "2024-01-02T14:54:00", + "2024-01-02T14:55:00", + "2024-01-02T14:56:00", + "2024-01-02T14:57:00", + "2024-01-02T14:58:00", + "2024-01-02T14:59:00", + "2024-01-02T15:00:00", + "2024-01-02T15:01:00", + "2024-01-02T15:02:00", + "2024-01-02T15:03:00", + "2024-01-02T15:04:00", + "2024-01-02T15:05:00", + "2024-01-02T15:06:00", + "2024-01-02T15:07:00", + "2024-01-02T15:08:00", + "2024-01-02T15:09:00", + "2024-01-02T15:10:00", + "2024-01-02T15:11:00", + "2024-01-02T15:12:00", + "2024-01-02T15:13:00", + "2024-01-02T15:14:00", + "2024-01-02T15:15:00", + "2024-01-02T15:16:00", + "2024-01-02T15:17:00", + "2024-01-02T15:18:00", + "2024-01-02T15:19:00", + "2024-01-02T15:20:00", + "2024-01-02T15:21:00", + "2024-01-02T15:22:00", + "2024-01-02T15:23:00", + "2024-01-02T15:24:00", + "2024-01-02T15:25:00", + "2024-01-02T15:26:00", + "2024-01-02T15:27:00", + "2024-01-02T15:28:00", + "2024-01-02T15:29:00", + "2024-01-02T15:30:00", + "2024-01-02T15:31:00", + "2024-01-02T15:32:00", + "2024-01-02T15:33:00", + "2024-01-02T15:34:00", + "2024-01-02T15:35:00", + "2024-01-02T15:36:00", + "2024-01-02T15:37:00", + "2024-01-02T15:38:00", + "2024-01-02T15:39:00", + "2024-01-02T15:40:00", + "2024-01-02T15:41:00", + "2024-01-02T15:42:00", + "2024-01-02T15:43:00", + "2024-01-02T15:44:00", + "2024-01-02T15:45:00", + "2024-01-02T15:46:00", + "2024-01-02T15:47:00", + "2024-01-02T15:48:00", + "2024-01-02T15:49:00", + "2024-01-02T15:50:00", + "2024-01-02T15:51:00", + "2024-01-02T15:52:00", + "2024-01-02T15:53:00", + "2024-01-02T15:54:00", + "2024-01-02T15:55:00", + "2024-01-02T15:56:00", + "2024-01-02T15:57:00", + "2024-01-02T15:58:00", + "2024-01-02T15:59:00", + "2024-01-02T16:00:00", + "2024-01-02T16:01:00", + "2024-01-02T16:02:00", + "2024-01-02T16:03:00", + "2024-01-02T16:04:00", + "2024-01-02T16:05:00", + "2024-01-02T16:06:00", + "2024-01-02T16:07:00", + "2024-01-02T16:08:00", + "2024-01-02T16:09:00", + "2024-01-02T16:10:00", + "2024-01-02T16:11:00", + "2024-01-02T16:12:00", + "2024-01-02T16:13:00", + "2024-01-02T16:14:00", + "2024-01-02T16:15:00", + "2024-01-02T16:16:00", + "2024-01-02T16:17:00", + "2024-01-02T16:18:00", + "2024-01-02T16:19:00", + "2024-01-02T16:20:00", + "2024-01-02T16:21:00", + "2024-01-02T16:22:00", + "2024-01-02T16:23:00", + "2024-01-02T16:24:00", + "2024-01-02T16:25:00", + "2024-01-02T16:26:00", + "2024-01-02T16:27:00", + "2024-01-02T16:28:00", + "2024-01-02T16:29:00", + "2024-01-02T16:30:00", + "2024-01-02T16:31:00", + "2024-01-02T16:32:00", + "2024-01-02T16:33:00", + "2024-01-02T16:34:00", + "2024-01-02T16:35:00", + "2024-01-02T16:36:00", + "2024-01-02T16:37:00", + "2024-01-02T16:38:00", + "2024-01-02T16:39:00", + "2024-01-02T16:40:00", + "2024-01-02T16:41:00", + "2024-01-02T16:42:00", + "2024-01-02T16:43:00", + "2024-01-02T16:44:00", + "2024-01-02T16:45:00", + "2024-01-02T16:46:00", + "2024-01-02T16:47:00", + "2024-01-02T16:48:00", + "2024-01-02T16:49:00", + "2024-01-02T16:50:00", + "2024-01-02T16:51:00", + "2024-01-02T16:52:00", + "2024-01-02T16:53:00", + "2024-01-02T16:54:00", + "2024-01-02T16:55:00", + "2024-01-02T16:56:00", + "2024-01-02T16:57:00", + "2024-01-02T16:58:00", + "2024-01-02T16:59:00", + "2024-01-02T17:00:00", + "2024-01-02T17:01:00", + "2024-01-02T17:02:00", + "2024-01-02T17:03:00", + "2024-01-02T17:04:00", + "2024-01-02T17:05:00", + "2024-01-02T17:06:00", + "2024-01-02T17:07:00", + "2024-01-02T17:08:00", + "2024-01-02T17:09:00", + "2024-01-02T17:10:00", + "2024-01-02T17:11:00", + "2024-01-02T17:12:00", + "2024-01-02T17:13:00", + "2024-01-02T17:14:00", + "2024-01-02T17:15:00", + "2024-01-02T17:16:00", + "2024-01-02T17:17:00", + "2024-01-02T17:18:00", + "2024-01-02T17:19:00", + "2024-01-02T17:20:00", + "2024-01-02T17:21:00", + "2024-01-02T17:22:00", + "2024-01-02T17:23:00", + "2024-01-02T17:24:00", + "2024-01-02T17:25:00", + "2024-01-02T17:26:00", + "2024-01-02T17:27:00", + "2024-01-02T17:28:00", + "2024-01-02T17:29:00", + "2024-01-02T17:30:00", + "2024-01-02T17:31:00", + "2024-01-02T17:32:00", + "2024-01-02T17:33:00", + "2024-01-02T17:34:00", + "2024-01-02T17:35:00", + "2024-01-02T17:36:00", + "2024-01-02T17:37:00", + "2024-01-02T17:38:00", + "2024-01-02T17:39:00", + "2024-01-02T17:40:00", + "2024-01-02T17:41:00", + "2024-01-02T17:42:00", + "2024-01-02T17:43:00", + "2024-01-02T17:44:00", + "2024-01-02T17:45:00", + "2024-01-02T17:46:00", + "2024-01-02T17:47:00", + "2024-01-02T17:48:00", + "2024-01-02T17:49:00", + "2024-01-02T17:50:00", + "2024-01-02T17:51:00", + "2024-01-02T17:52:00", + "2024-01-02T17:53:00", + "2024-01-02T17:54:00", + "2024-01-02T17:55:00", + "2024-01-02T17:56:00", + "2024-01-02T17:57:00", + "2024-01-02T17:58:00", + "2024-01-02T17:59:00", + "2024-01-02T18:00:00", + "2024-01-02T18:01:00", + "2024-01-02T18:02:00", + "2024-01-02T18:03:00", + "2024-01-02T18:04:00", + "2024-01-02T18:05:00", + "2024-01-02T18:06:00", + "2024-01-02T18:07:00", + "2024-01-02T18:08:00", + "2024-01-02T18:09:00", + "2024-01-02T18:10:00", + "2024-01-02T18:11:00", + "2024-01-02T18:12:00", + "2024-01-02T18:13:00", + "2024-01-02T18:14:00", + "2024-01-02T18:15:00", + "2024-01-02T18:16:00", + "2024-01-02T18:17:00", + "2024-01-02T18:18:00", + "2024-01-02T18:19:00", + "2024-01-02T18:20:00", + "2024-01-02T18:21:00", + "2024-01-02T18:22:00", + "2024-01-02T18:23:00", + "2024-01-02T18:24:00", + "2024-01-02T18:25:00", + "2024-01-02T18:26:00", + "2024-01-02T18:27:00", + "2024-01-02T18:28:00", + "2024-01-02T18:29:00", + "2024-01-02T18:30:00", + "2024-01-02T18:31:00", + "2024-01-02T18:32:00", + "2024-01-02T18:33:00", + "2024-01-02T18:34:00", + "2024-01-02T18:35:00", + "2024-01-02T18:36:00", + "2024-01-02T18:37:00", + "2024-01-02T18:38:00", + "2024-01-02T18:39:00", + "2024-01-02T18:40:00", + "2024-01-02T18:41:00", + "2024-01-02T18:42:00", + "2024-01-02T18:43:00", + "2024-01-02T18:44:00", + "2024-01-02T18:45:00", + "2024-01-02T18:46:00", + "2024-01-02T18:47:00", + "2024-01-02T18:48:00", + "2024-01-02T18:49:00", + "2024-01-02T18:50:00", + "2024-01-02T18:51:00", + "2024-01-02T18:52:00", + "2024-01-02T18:53:00", + "2024-01-02T18:54:00", + "2024-01-02T18:55:00", + "2024-01-02T18:56:00", + "2024-01-02T18:57:00", + "2024-01-02T18:58:00", + "2024-01-02T18:59:00", + "2024-01-02T19:00:00", + "2024-01-02T19:01:00", + "2024-01-02T19:02:00", + "2024-01-02T19:03:00", + "2024-01-02T19:04:00", + "2024-01-02T19:05:00", + "2024-01-02T19:06:00", + "2024-01-02T19:07:00", + "2024-01-02T19:08:00", + "2024-01-02T19:09:00", + "2024-01-02T19:10:00", + "2024-01-02T19:11:00", + "2024-01-02T19:12:00", + "2024-01-02T19:13:00", + "2024-01-02T19:14:00", + "2024-01-02T19:15:00", + "2024-01-02T19:16:00", + "2024-01-02T19:17:00", + "2024-01-02T19:18:00", + "2024-01-02T19:19:00", + "2024-01-02T19:20:00", + "2024-01-02T19:21:00", + "2024-01-02T19:22:00", + "2024-01-02T19:23:00", + "2024-01-02T19:24:00", + "2024-01-02T19:25:00", + "2024-01-02T19:26:00", + "2024-01-02T19:27:00", + "2024-01-02T19:28:00", + "2024-01-02T19:29:00", + "2024-01-02T19:30:00", + "2024-01-02T19:31:00", + "2024-01-02T19:32:00", + "2024-01-02T19:33:00", + "2024-01-02T19:34:00", + "2024-01-02T19:35:00", + "2024-01-02T19:36:00", + "2024-01-02T19:37:00", + "2024-01-02T19:38:00", + "2024-01-02T19:39:00", + "2024-01-02T19:40:00", + "2024-01-02T19:41:00", + "2024-01-02T19:42:00", + "2024-01-02T19:43:00", + "2024-01-02T19:44:00", + "2024-01-02T19:45:00", + "2024-01-02T19:46:00", + "2024-01-02T19:47:00", + "2024-01-02T19:48:00", + "2024-01-02T19:49:00", + "2024-01-02T19:50:00", + "2024-01-02T19:51:00", + "2024-01-02T19:52:00", + "2024-01-02T19:53:00", + "2024-01-02T19:54:00", + "2024-01-02T19:55:00", + "2024-01-02T19:56:00", + "2024-01-02T19:57:00", + "2024-01-02T19:58:00", + "2024-01-02T19:59:00", + "2024-01-02T20:00:00", + "2024-01-02T20:01:00", + "2024-01-02T20:02:00", + "2024-01-02T20:03:00", + "2024-01-02T20:04:00", + "2024-01-02T20:05:00", + "2024-01-02T20:06:00", + "2024-01-02T20:07:00", + "2024-01-02T20:08:00", + "2024-01-02T20:09:00", + "2024-01-02T20:10:00", + "2024-01-02T20:11:00", + "2024-01-02T20:12:00", + "2024-01-02T20:13:00", + "2024-01-02T20:14:00", + "2024-01-02T20:15:00", + "2024-01-02T20:16:00", + "2024-01-02T20:17:00", + "2024-01-02T20:18:00", + "2024-01-02T20:19:00", + "2024-01-02T20:20:00", + "2024-01-02T20:21:00", + "2024-01-02T20:22:00", + "2024-01-02T20:23:00", + "2024-01-02T20:24:00", + "2024-01-02T20:25:00", + "2024-01-02T20:26:00", + "2024-01-02T20:27:00", + "2024-01-02T20:28:00", + "2024-01-02T20:29:00", + "2024-01-02T20:30:00", + "2024-01-02T20:31:00", + "2024-01-02T20:32:00", + "2024-01-02T20:33:00", + "2024-01-02T20:34:00", + "2024-01-02T20:35:00", + "2024-01-02T20:36:00", + "2024-01-02T20:37:00", + "2024-01-02T20:38:00", + "2024-01-02T20:39:00", + "2024-01-02T20:40:00", + "2024-01-02T20:41:00", + "2024-01-02T20:42:00", + "2024-01-02T20:43:00", + "2024-01-02T20:44:00", + "2024-01-02T20:45:00", + "2024-01-02T20:46:00", + "2024-01-02T20:47:00", + "2024-01-02T20:48:00", + "2024-01-02T20:49:00", + "2024-01-02T20:50:00", + "2024-01-02T20:51:00", + "2024-01-02T20:52:00", + "2024-01-02T20:53:00", + "2024-01-02T20:54:00", + "2024-01-02T20:55:00", + "2024-01-02T20:56:00", + "2024-01-02T20:57:00", + "2024-01-02T20:58:00", + "2024-01-02T20:59:00", + "2024-01-02T21:00:00", + "2024-01-02T21:01:00", + "2024-01-02T21:02:00", + "2024-01-02T21:03:00", + "2024-01-02T21:04:00", + "2024-01-02T21:05:00", + "2024-01-02T21:06:00", + "2024-01-02T21:07:00", + "2024-01-02T21:08:00", + "2024-01-02T21:09:00", + "2024-01-02T21:10:00", + "2024-01-02T21:11:00", + "2024-01-02T21:12:00", + "2024-01-02T21:13:00", + "2024-01-02T21:14:00", + "2024-01-02T21:15:00", + "2024-01-02T21:16:00", + "2024-01-02T21:17:00", + "2024-01-02T21:18:00", + "2024-01-02T21:19:00", + "2024-01-02T21:20:00", + "2024-01-02T21:21:00", + "2024-01-02T21:22:00", + "2024-01-02T21:23:00", + "2024-01-02T21:24:00", + "2024-01-02T21:25:00", + "2024-01-02T21:26:00", + "2024-01-02T21:27:00", + "2024-01-02T21:28:00", + "2024-01-02T21:29:00", + "2024-01-02T21:30:00", + "2024-01-02T21:31:00", + "2024-01-02T21:32:00", + "2024-01-02T21:33:00", + "2024-01-02T21:34:00", + "2024-01-02T21:35:00", + "2024-01-02T21:36:00", + "2024-01-02T21:37:00", + "2024-01-02T21:38:00", + "2024-01-02T21:39:00", + "2024-01-02T21:40:00", + "2024-01-02T21:41:00", + "2024-01-02T21:42:00", + "2024-01-02T21:43:00", + "2024-01-02T21:44:00", + "2024-01-02T21:45:00", + "2024-01-02T21:46:00", + "2024-01-02T21:47:00", + "2024-01-02T21:48:00", + "2024-01-02T21:49:00", + "2024-01-02T21:50:00", + "2024-01-02T21:51:00", + "2024-01-02T21:52:00", + "2024-01-02T21:53:00", + "2024-01-02T21:54:00", + "2024-01-02T21:55:00", + "2024-01-02T21:56:00", + "2024-01-02T21:57:00", + "2024-01-02T21:58:00", + "2024-01-02T21:59:00", + "2024-01-02T22:00:00", + "2024-01-02T22:01:00", + "2024-01-02T22:02:00", + "2024-01-02T22:03:00", + "2024-01-02T22:04:00", + "2024-01-02T22:05:00", + "2024-01-02T22:06:00", + "2024-01-02T22:07:00", + "2024-01-02T22:08:00", + "2024-01-02T22:09:00", + "2024-01-02T22:10:00", + "2024-01-02T22:11:00", + "2024-01-02T22:12:00", + "2024-01-02T22:13:00", + "2024-01-02T22:14:00", + "2024-01-02T22:15:00", + "2024-01-02T22:16:00", + "2024-01-02T22:17:00", + "2024-01-02T22:18:00", + "2024-01-02T22:19:00", + "2024-01-02T22:20:00", + "2024-01-02T22:21:00", + "2024-01-02T22:22:00", + "2024-01-02T22:23:00", + "2024-01-02T22:24:00", + "2024-01-02T22:25:00", + "2024-01-02T22:26:00", + "2024-01-02T22:27:00", + "2024-01-02T22:28:00", + "2024-01-02T22:29:00", + "2024-01-02T22:30:00", + "2024-01-02T22:31:00", + "2024-01-02T22:32:00", + "2024-01-02T22:33:00", + "2024-01-02T22:34:00", + "2024-01-02T22:35:00", + "2024-01-02T22:36:00", + "2024-01-02T22:37:00", + "2024-01-02T22:38:00", + "2024-01-02T22:39:00", + "2024-01-02T22:40:00", + "2024-01-02T22:41:00", + "2024-01-02T22:42:00", + "2024-01-02T22:43:00", + "2024-01-02T22:44:00", + "2024-01-02T22:45:00", + "2024-01-02T22:46:00", + "2024-01-02T22:47:00", + "2024-01-02T22:48:00", + "2024-01-02T22:49:00", + "2024-01-02T22:50:00", + "2024-01-02T22:51:00", + "2024-01-02T22:52:00", + "2024-01-02T22:53:00", + "2024-01-02T22:54:00", + "2024-01-02T22:55:00", + "2024-01-02T22:56:00", + "2024-01-02T22:57:00", + "2024-01-02T22:58:00", + "2024-01-02T22:59:00", + "2024-01-02T23:00:00", + "2024-01-02T23:01:00", + "2024-01-02T23:02:00", + "2024-01-02T23:03:00", + "2024-01-02T23:04:00", + "2024-01-02T23:05:00", + "2024-01-02T23:06:00", + "2024-01-02T23:07:00", + "2024-01-02T23:08:00", + "2024-01-02T23:09:00", + "2024-01-02T23:10:00", + "2024-01-02T23:11:00", + "2024-01-02T23:12:00", + "2024-01-02T23:13:00", + "2024-01-02T23:14:00", + "2024-01-02T23:15:00", + "2024-01-02T23:16:00", + "2024-01-02T23:17:00", + "2024-01-02T23:18:00", + "2024-01-02T23:19:00", + "2024-01-02T23:20:00", + "2024-01-02T23:21:00", + "2024-01-02T23:22:00", + "2024-01-02T23:23:00", + "2024-01-02T23:24:00", + "2024-01-02T23:25:00", + "2024-01-02T23:26:00", + "2024-01-02T23:27:00", + "2024-01-02T23:28:00", + "2024-01-02T23:29:00", + "2024-01-02T23:30:00", + "2024-01-02T23:31:00", + "2024-01-02T23:32:00", + "2024-01-02T23:33:00", + "2024-01-02T23:34:00", + "2024-01-02T23:35:00", + "2024-01-02T23:36:00", + "2024-01-02T23:37:00", + "2024-01-02T23:38:00", + "2024-01-02T23:39:00", + "2024-01-02T23:40:00", + "2024-01-02T23:41:00", + "2024-01-02T23:42:00", + "2024-01-02T23:43:00", + "2024-01-02T23:44:00", + "2024-01-02T23:45:00", + "2024-01-02T23:46:00", + "2024-01-02T23:47:00", + "2024-01-02T23:48:00", + "2024-01-02T23:49:00", + "2024-01-02T23:50:00", + "2024-01-02T23:51:00", + "2024-01-02T23:52:00", + "2024-01-02T23:53:00", + "2024-01-02T23:54:00", + "2024-01-02T23:55:00", + "2024-01-02T23:56:00", + "2024-01-02T23:57:00", + "2024-01-02T23:58:00", + "2024-01-02T23:59:00", + "2024-01-03T00:00:00", + "2024-01-03T00:01:00", + "2024-01-03T00:02:00", + "2024-01-03T00:03:00", + "2024-01-03T00:04:00", + "2024-01-03T00:05:00", + "2024-01-03T00:06:00", + "2024-01-03T00:07:00", + "2024-01-03T00:08:00", + "2024-01-03T00:09:00", + "2024-01-03T00:10:00", + "2024-01-03T00:11:00", + "2024-01-03T00:12:00", + "2024-01-03T00:13:00", + "2024-01-03T00:14:00", + "2024-01-03T00:15:00", + "2024-01-03T00:16:00", + "2024-01-03T00:17:00", + "2024-01-03T00:18:00", + "2024-01-03T00:19:00", + "2024-01-03T00:20:00", + "2024-01-03T00:21:00", + "2024-01-03T00:22:00", + "2024-01-03T00:23:00", + "2024-01-03T00:24:00", + "2024-01-03T00:25:00", + "2024-01-03T00:26:00", + "2024-01-03T00:27:00", + "2024-01-03T00:28:00", + "2024-01-03T00:29:00", + "2024-01-03T00:30:00", + "2024-01-03T00:31:00", + "2024-01-03T00:32:00", + "2024-01-03T00:33:00", + "2024-01-03T00:34:00", + "2024-01-03T00:35:00", + "2024-01-03T00:36:00", + "2024-01-03T00:37:00", + "2024-01-03T00:38:00", + "2024-01-03T00:39:00", + "2024-01-03T00:40:00", + "2024-01-03T00:41:00", + "2024-01-03T00:42:00", + "2024-01-03T00:43:00", + "2024-01-03T00:44:00", + "2024-01-03T00:45:00", + "2024-01-03T00:46:00", + "2024-01-03T00:47:00", + "2024-01-03T00:48:00", + "2024-01-03T00:49:00", + "2024-01-03T00:50:00", + "2024-01-03T00:51:00", + "2024-01-03T00:52:00", + "2024-01-03T00:53:00", + "2024-01-03T00:54:00", + "2024-01-03T00:55:00", + "2024-01-03T00:56:00", + "2024-01-03T00:57:00", + "2024-01-03T00:58:00", + "2024-01-03T00:59:00", + "2024-01-03T01:00:00", + "2024-01-03T01:01:00", + "2024-01-03T01:02:00", + "2024-01-03T01:03:00", + "2024-01-03T01:04:00", + "2024-01-03T01:05:00", + "2024-01-03T01:06:00", + "2024-01-03T01:07:00", + "2024-01-03T01:08:00", + "2024-01-03T01:09:00", + "2024-01-03T01:10:00", + "2024-01-03T01:11:00", + "2024-01-03T01:12:00", + "2024-01-03T01:13:00", + "2024-01-03T01:14:00", + "2024-01-03T01:15:00", + "2024-01-03T01:16:00", + "2024-01-03T01:17:00", + "2024-01-03T01:18:00", + "2024-01-03T01:19:00", + "2024-01-03T01:20:00", + "2024-01-03T01:21:00", + "2024-01-03T01:22:00", + "2024-01-03T01:23:00", + "2024-01-03T01:24:00", + "2024-01-03T01:25:00", + "2024-01-03T01:26:00", + "2024-01-03T01:27:00", + "2024-01-03T01:28:00", + "2024-01-03T01:29:00", + "2024-01-03T01:30:00", + "2024-01-03T01:31:00", + "2024-01-03T01:32:00", + "2024-01-03T01:33:00", + "2024-01-03T01:34:00", + "2024-01-03T01:35:00", + "2024-01-03T01:36:00", + "2024-01-03T01:37:00", + "2024-01-03T01:38:00", + "2024-01-03T01:39:00", + "2024-01-03T01:40:00", + "2024-01-03T01:41:00", + "2024-01-03T01:42:00", + "2024-01-03T01:43:00", + "2024-01-03T01:44:00", + "2024-01-03T01:45:00", + "2024-01-03T01:46:00", + "2024-01-03T01:47:00", + "2024-01-03T01:48:00", + "2024-01-03T01:49:00", + "2024-01-03T01:50:00", + "2024-01-03T01:51:00", + "2024-01-03T01:52:00", + "2024-01-03T01:53:00", + "2024-01-03T01:54:00", + "2024-01-03T01:55:00", + "2024-01-03T01:56:00", + "2024-01-03T01:57:00", + "2024-01-03T01:58:00", + "2024-01-03T01:59:00", + "2024-01-03T02:00:00", + "2024-01-03T02:01:00", + "2024-01-03T02:02:00", + "2024-01-03T02:03:00", + "2024-01-03T02:04:00", + "2024-01-03T02:05:00", + "2024-01-03T02:06:00", + "2024-01-03T02:07:00", + "2024-01-03T02:08:00", + "2024-01-03T02:09:00", + "2024-01-03T02:10:00", + "2024-01-03T02:11:00", + "2024-01-03T02:12:00", + "2024-01-03T02:13:00", + "2024-01-03T02:14:00", + "2024-01-03T02:15:00", + "2024-01-03T02:16:00", + "2024-01-03T02:17:00", + "2024-01-03T02:18:00", + "2024-01-03T02:19:00", + "2024-01-03T02:20:00", + "2024-01-03T02:21:00", + "2024-01-03T02:22:00", + "2024-01-03T02:23:00", + "2024-01-03T02:24:00", + "2024-01-03T02:25:00", + "2024-01-03T02:26:00", + "2024-01-03T02:27:00", + "2024-01-03T02:28:00", + "2024-01-03T02:29:00", + "2024-01-03T02:30:00", + "2024-01-03T02:31:00", + "2024-01-03T02:32:00", + "2024-01-03T02:33:00", + "2024-01-03T02:34:00", + "2024-01-03T02:35:00", + "2024-01-03T02:36:00", + "2024-01-03T02:37:00", + "2024-01-03T02:38:00", + "2024-01-03T02:39:00", + "2024-01-03T02:40:00", + "2024-01-03T02:41:00", + "2024-01-03T02:42:00", + "2024-01-03T02:43:00", + "2024-01-03T02:44:00", + "2024-01-03T02:45:00", + "2024-01-03T02:46:00", + "2024-01-03T02:47:00", + "2024-01-03T02:48:00", + "2024-01-03T02:49:00", + "2024-01-03T02:50:00", + "2024-01-03T02:51:00", + "2024-01-03T02:52:00", + "2024-01-03T02:53:00", + "2024-01-03T02:54:00", + "2024-01-03T02:55:00", + "2024-01-03T02:56:00", + "2024-01-03T02:57:00", + "2024-01-03T02:58:00", + "2024-01-03T02:59:00", + "2024-01-03T03:00:00", + "2024-01-03T03:01:00", + "2024-01-03T03:02:00", + "2024-01-03T03:03:00", + "2024-01-03T03:04:00", + "2024-01-03T03:05:00", + "2024-01-03T03:06:00", + "2024-01-03T03:07:00", + "2024-01-03T03:08:00", + "2024-01-03T03:09:00", + "2024-01-03T03:10:00", + "2024-01-03T03:11:00", + "2024-01-03T03:12:00", + "2024-01-03T03:13:00", + "2024-01-03T03:14:00", + "2024-01-03T03:15:00", + "2024-01-03T03:16:00", + "2024-01-03T03:17:00", + "2024-01-03T03:18:00", + "2024-01-03T03:19:00", + "2024-01-03T03:20:00", + "2024-01-03T03:21:00", + "2024-01-03T03:22:00", + "2024-01-03T03:23:00", + "2024-01-03T03:24:00", + "2024-01-03T03:25:00", + "2024-01-03T03:26:00", + "2024-01-03T03:27:00", + "2024-01-03T03:28:00", + "2024-01-03T03:29:00", + "2024-01-03T03:30:00", + "2024-01-03T03:31:00", + "2024-01-03T03:32:00", + "2024-01-03T03:33:00", + "2024-01-03T03:34:00", + "2024-01-03T03:35:00", + "2024-01-03T03:36:00", + "2024-01-03T03:37:00", + "2024-01-03T03:38:00", + "2024-01-03T03:39:00", + "2024-01-03T03:40:00", + "2024-01-03T03:41:00", + "2024-01-03T03:42:00", + "2024-01-03T03:43:00", + "2024-01-03T03:44:00", + "2024-01-03T03:45:00", + "2024-01-03T03:46:00", + "2024-01-03T03:47:00", + "2024-01-03T03:48:00", + "2024-01-03T03:49:00", + "2024-01-03T03:50:00", + "2024-01-03T03:51:00", + "2024-01-03T03:52:00", + "2024-01-03T03:53:00", + "2024-01-03T03:54:00", + "2024-01-03T03:55:00", + "2024-01-03T03:56:00", + "2024-01-03T03:57:00", + "2024-01-03T03:58:00", + "2024-01-03T03:59:00", + "2024-01-03T04:00:00", + "2024-01-03T04:01:00", + "2024-01-03T04:02:00", + "2024-01-03T04:03:00", + "2024-01-03T04:04:00", + "2024-01-03T04:05:00", + "2024-01-03T04:06:00", + "2024-01-03T04:07:00", + "2024-01-03T04:08:00", + "2024-01-03T04:09:00", + "2024-01-03T04:10:00", + "2024-01-03T04:11:00", + "2024-01-03T04:12:00", + "2024-01-03T04:13:00", + "2024-01-03T04:14:00", + "2024-01-03T04:15:00", + "2024-01-03T04:16:00", + "2024-01-03T04:17:00", + "2024-01-03T04:18:00", + "2024-01-03T04:19:00", + "2024-01-03T04:20:00", + "2024-01-03T04:21:00", + "2024-01-03T04:22:00", + "2024-01-03T04:23:00", + "2024-01-03T04:24:00", + "2024-01-03T04:25:00", + "2024-01-03T04:26:00", + "2024-01-03T04:27:00", + "2024-01-03T04:28:00", + "2024-01-03T04:29:00", + "2024-01-03T04:30:00", + "2024-01-03T04:31:00", + "2024-01-03T04:32:00", + "2024-01-03T04:33:00", + "2024-01-03T04:34:00", + "2024-01-03T04:35:00", + "2024-01-03T04:36:00", + "2024-01-03T04:37:00", + "2024-01-03T04:38:00", + "2024-01-03T04:39:00", + "2024-01-03T04:40:00", + "2024-01-03T04:41:00", + "2024-01-03T04:42:00", + "2024-01-03T04:43:00", + "2024-01-03T04:44:00", + "2024-01-03T04:45:00", + "2024-01-03T04:46:00", + "2024-01-03T04:47:00", + "2024-01-03T04:48:00", + "2024-01-03T04:49:00", + "2024-01-03T04:50:00", + "2024-01-03T04:51:00", + "2024-01-03T04:52:00", + "2024-01-03T04:53:00", + "2024-01-03T04:54:00", + "2024-01-03T04:55:00", + "2024-01-03T04:56:00", + "2024-01-03T04:57:00", + "2024-01-03T04:58:00", + "2024-01-03T04:59:00", + "2024-01-03T05:00:00", + "2024-01-03T05:01:00", + "2024-01-03T05:02:00", + "2024-01-03T05:03:00", + "2024-01-03T05:04:00", + "2024-01-03T05:05:00", + "2024-01-03T05:06:00", + "2024-01-03T05:07:00", + "2024-01-03T05:08:00", + "2024-01-03T05:09:00", + "2024-01-03T05:10:00", + "2024-01-03T05:11:00", + "2024-01-03T05:12:00", + "2024-01-03T05:13:00", + "2024-01-03T05:14:00", + "2024-01-03T05:15:00", + "2024-01-03T05:16:00", + "2024-01-03T05:17:00", + "2024-01-03T05:18:00", + "2024-01-03T05:19:00", + "2024-01-03T05:20:00", + "2024-01-03T05:21:00", + "2024-01-03T05:22:00", + "2024-01-03T05:23:00", + "2024-01-03T05:24:00", + "2024-01-03T05:25:00", + "2024-01-03T05:26:00", + "2024-01-03T05:27:00", + "2024-01-03T05:28:00", + "2024-01-03T05:29:00", + "2024-01-03T05:30:00", + "2024-01-03T05:31:00", + "2024-01-03T05:32:00", + "2024-01-03T05:33:00", + "2024-01-03T05:34:00", + "2024-01-03T05:35:00", + "2024-01-03T05:36:00", + "2024-01-03T05:37:00", + "2024-01-03T05:38:00", + "2024-01-03T05:39:00", + "2024-01-03T05:40:00", + "2024-01-03T05:41:00", + "2024-01-03T05:42:00", + "2024-01-03T05:43:00", + "2024-01-03T05:44:00", + "2024-01-03T05:45:00", + "2024-01-03T05:46:00", + "2024-01-03T05:47:00", + "2024-01-03T05:48:00", + "2024-01-03T05:49:00", + "2024-01-03T05:50:00", + "2024-01-03T05:51:00", + "2024-01-03T05:52:00", + "2024-01-03T05:53:00", + "2024-01-03T05:54:00", + "2024-01-03T05:55:00", + "2024-01-03T05:56:00", + "2024-01-03T05:57:00", + "2024-01-03T05:58:00", + "2024-01-03T05:59:00", + "2024-01-03T06:00:00", + "2024-01-03T06:01:00", + "2024-01-03T06:02:00", + "2024-01-03T06:03:00", + "2024-01-03T06:04:00", + "2024-01-03T06:05:00", + "2024-01-03T06:06:00", + "2024-01-03T06:07:00", + "2024-01-03T06:08:00", + "2024-01-03T06:09:00", + "2024-01-03T06:10:00", + "2024-01-03T06:11:00", + "2024-01-03T06:12:00", + "2024-01-03T06:13:00", + "2024-01-03T06:14:00", + "2024-01-03T06:15:00", + "2024-01-03T06:16:00", + "2024-01-03T06:17:00", + "2024-01-03T06:18:00", + "2024-01-03T06:19:00", + "2024-01-03T06:20:00", + "2024-01-03T06:21:00", + "2024-01-03T06:22:00", + "2024-01-03T06:23:00", + "2024-01-03T06:24:00", + "2024-01-03T06:25:00", + "2024-01-03T06:26:00", + "2024-01-03T06:27:00", + "2024-01-03T06:28:00", + "2024-01-03T06:29:00", + "2024-01-03T06:30:00", + "2024-01-03T06:31:00", + "2024-01-03T06:32:00", + "2024-01-03T06:33:00", + "2024-01-03T06:34:00", + "2024-01-03T06:35:00", + "2024-01-03T06:36:00", + "2024-01-03T06:37:00", + "2024-01-03T06:38:00", + "2024-01-03T06:39:00", + "2024-01-03T06:40:00", + "2024-01-03T06:41:00", + "2024-01-03T06:42:00", + "2024-01-03T06:43:00", + "2024-01-03T06:44:00", + "2024-01-03T06:45:00", + "2024-01-03T06:46:00", + "2024-01-03T06:47:00", + "2024-01-03T06:48:00", + "2024-01-03T06:49:00", + "2024-01-03T06:50:00", + "2024-01-03T06:51:00", + "2024-01-03T06:52:00", + "2024-01-03T06:53:00", + "2024-01-03T06:54:00", + "2024-01-03T06:55:00", + "2024-01-03T06:56:00", + "2024-01-03T06:57:00", + "2024-01-03T06:58:00", + "2024-01-03T06:59:00", + "2024-01-03T07:00:00", + "2024-01-03T07:01:00", + "2024-01-03T07:02:00", + "2024-01-03T07:03:00", + "2024-01-03T07:04:00", + "2024-01-03T07:05:00", + "2024-01-03T07:06:00", + "2024-01-03T07:07:00", + "2024-01-03T07:08:00", + "2024-01-03T07:09:00", + "2024-01-03T07:10:00", + "2024-01-03T07:11:00", + "2024-01-03T07:12:00", + "2024-01-03T07:13:00", + "2024-01-03T07:14:00", + "2024-01-03T07:15:00", + "2024-01-03T07:16:00", + "2024-01-03T07:17:00", + "2024-01-03T07:18:00", + "2024-01-03T07:19:00", + "2024-01-03T07:20:00", + "2024-01-03T07:21:00", + "2024-01-03T07:22:00", + "2024-01-03T07:23:00", + "2024-01-03T07:24:00", + "2024-01-03T07:25:00", + "2024-01-03T07:26:00", + "2024-01-03T07:27:00", + "2024-01-03T07:28:00", + "2024-01-03T07:29:00", + "2024-01-03T07:30:00", + "2024-01-03T07:31:00", + "2024-01-03T07:32:00", + "2024-01-03T07:33:00", + "2024-01-03T07:34:00", + "2024-01-03T07:35:00", + "2024-01-03T07:36:00", + "2024-01-03T07:37:00", + "2024-01-03T07:38:00", + "2024-01-03T07:39:00", + "2024-01-03T07:40:00", + "2024-01-03T07:41:00", + "2024-01-03T07:42:00", + "2024-01-03T07:43:00", + "2024-01-03T07:44:00", + "2024-01-03T07:45:00", + "2024-01-03T07:46:00", + "2024-01-03T07:47:00", + "2024-01-03T07:48:00", + "2024-01-03T07:49:00", + "2024-01-03T07:50:00", + "2024-01-03T07:51:00", + "2024-01-03T07:52:00", + "2024-01-03T07:53:00", + "2024-01-03T07:54:00", + "2024-01-03T07:55:00", + "2024-01-03T07:56:00", + "2024-01-03T07:57:00", + "2024-01-03T07:58:00", + "2024-01-03T07:59:00", + "2024-01-03T08:00:00", + "2024-01-03T08:01:00", + "2024-01-03T08:02:00", + "2024-01-03T08:03:00", + "2024-01-03T08:04:00", + "2024-01-03T08:05:00", + "2024-01-03T08:06:00", + "2024-01-03T08:07:00", + "2024-01-03T08:08:00", + "2024-01-03T08:09:00", + "2024-01-03T08:10:00", + "2024-01-03T08:11:00", + "2024-01-03T08:12:00", + "2024-01-03T08:13:00", + "2024-01-03T08:14:00", + "2024-01-03T08:15:00", + "2024-01-03T08:16:00", + "2024-01-03T08:17:00", + "2024-01-03T08:18:00", + "2024-01-03T08:19:00", + "2024-01-03T08:20:00", + "2024-01-03T08:21:00", + "2024-01-03T08:22:00", + "2024-01-03T08:23:00", + "2024-01-03T08:24:00", + "2024-01-03T08:25:00", + "2024-01-03T08:26:00", + "2024-01-03T08:27:00", + "2024-01-03T08:28:00", + "2024-01-03T08:29:00", + "2024-01-03T08:30:00", + "2024-01-03T08:31:00", + "2024-01-03T08:32:00", + "2024-01-03T08:33:00", + "2024-01-03T08:34:00", + "2024-01-03T08:35:00", + "2024-01-03T08:36:00", + "2024-01-03T08:37:00", + "2024-01-03T08:38:00", + "2024-01-03T08:39:00", + "2024-01-03T08:40:00", + "2024-01-03T08:41:00", + "2024-01-03T08:42:00", + "2024-01-03T08:43:00", + "2024-01-03T08:44:00", + "2024-01-03T08:45:00", + "2024-01-03T08:46:00", + "2024-01-03T08:47:00", + "2024-01-03T08:48:00", + "2024-01-03T08:49:00", + "2024-01-03T08:50:00", + "2024-01-03T08:51:00", + "2024-01-03T08:52:00", + "2024-01-03T08:53:00", + "2024-01-03T08:54:00", + "2024-01-03T08:55:00", + "2024-01-03T08:56:00", + "2024-01-03T08:57:00", + "2024-01-03T08:58:00", + "2024-01-03T08:59:00", + "2024-01-03T09:00:00", + "2024-01-03T09:01:00", + "2024-01-03T09:02:00", + "2024-01-03T09:03:00", + "2024-01-03T09:04:00", + "2024-01-03T09:05:00", + "2024-01-03T09:06:00", + "2024-01-03T09:07:00", + "2024-01-03T09:08:00", + "2024-01-03T09:09:00", + "2024-01-03T09:10:00", + "2024-01-03T09:11:00", + "2024-01-03T09:12:00", + "2024-01-03T09:13:00", + "2024-01-03T09:14:00", + "2024-01-03T09:15:00", + "2024-01-03T09:16:00", + "2024-01-03T09:17:00", + "2024-01-03T09:18:00", + "2024-01-03T09:19:00", + "2024-01-03T09:20:00", + "2024-01-03T09:21:00", + "2024-01-03T09:22:00", + "2024-01-03T09:23:00", + "2024-01-03T09:24:00", + "2024-01-03T09:25:00", + "2024-01-03T09:26:00", + "2024-01-03T09:27:00", + "2024-01-03T09:28:00", + "2024-01-03T09:29:00", + "2024-01-03T09:30:00", + "2024-01-03T09:31:00", + "2024-01-03T09:32:00", + "2024-01-03T09:33:00", + "2024-01-03T09:34:00", + "2024-01-03T09:35:00", + "2024-01-03T09:36:00", + "2024-01-03T09:37:00", + "2024-01-03T09:38:00", + "2024-01-03T09:39:00", + "2024-01-03T09:40:00", + "2024-01-03T09:41:00", + "2024-01-03T09:42:00", + "2024-01-03T09:43:00", + "2024-01-03T09:44:00", + "2024-01-03T09:45:00", + "2024-01-03T09:46:00", + "2024-01-03T09:47:00", + "2024-01-03T09:48:00", + "2024-01-03T09:49:00", + "2024-01-03T09:50:00", + "2024-01-03T09:51:00", + "2024-01-03T09:52:00", + "2024-01-03T09:53:00", + "2024-01-03T09:54:00", + "2024-01-03T09:55:00", + "2024-01-03T09:56:00", + "2024-01-03T09:57:00", + "2024-01-03T09:58:00", + "2024-01-03T09:59:00", + "2024-01-03T10:00:00", + "2024-01-03T10:01:00", + "2024-01-03T10:02:00", + "2024-01-03T10:03:00", + "2024-01-03T10:04:00", + "2024-01-03T10:05:00", + "2024-01-03T10:06:00", + "2024-01-03T10:07:00", + "2024-01-03T10:08:00", + "2024-01-03T10:09:00", + "2024-01-03T10:10:00", + "2024-01-03T10:11:00", + "2024-01-03T10:12:00", + "2024-01-03T10:13:00", + "2024-01-03T10:14:00", + "2024-01-03T10:15:00", + "2024-01-03T10:16:00", + "2024-01-03T10:17:00", + "2024-01-03T10:18:00", + "2024-01-03T10:19:00", + "2024-01-03T10:20:00", + "2024-01-03T10:21:00", + "2024-01-03T10:22:00", + "2024-01-03T10:23:00", + "2024-01-03T10:24:00", + "2024-01-03T10:25:00", + "2024-01-03T10:26:00", + "2024-01-03T10:27:00", + "2024-01-03T10:28:00", + "2024-01-03T10:29:00", + "2024-01-03T10:30:00", + "2024-01-03T10:31:00", + "2024-01-03T10:32:00", + "2024-01-03T10:33:00", + "2024-01-03T10:34:00", + "2024-01-03T10:35:00", + "2024-01-03T10:36:00", + "2024-01-03T10:37:00", + "2024-01-03T10:38:00", + "2024-01-03T10:39:00", + "2024-01-03T10:40:00", + "2024-01-03T10:41:00", + "2024-01-03T10:42:00", + "2024-01-03T10:43:00", + "2024-01-03T10:44:00", + "2024-01-03T10:45:00", + "2024-01-03T10:46:00", + "2024-01-03T10:47:00", + "2024-01-03T10:48:00", + "2024-01-03T10:49:00", + "2024-01-03T10:50:00", + "2024-01-03T10:51:00", + "2024-01-03T10:52:00", + "2024-01-03T10:53:00", + "2024-01-03T10:54:00", + "2024-01-03T10:55:00", + "2024-01-03T10:56:00", + "2024-01-03T10:57:00", + "2024-01-03T10:58:00", + "2024-01-03T10:59:00", + "2024-01-03T11:00:00", + "2024-01-03T11:01:00", + "2024-01-03T11:02:00", + "2024-01-03T11:03:00", + "2024-01-03T11:04:00", + "2024-01-03T11:05:00", + "2024-01-03T11:06:00", + "2024-01-03T11:07:00", + "2024-01-03T11:08:00", + "2024-01-03T11:09:00", + "2024-01-03T11:10:00", + "2024-01-03T11:11:00", + "2024-01-03T11:12:00", + "2024-01-03T11:13:00", + "2024-01-03T11:14:00", + "2024-01-03T11:15:00", + "2024-01-03T11:16:00", + "2024-01-03T11:17:00", + "2024-01-03T11:18:00", + "2024-01-03T11:19:00", + "2024-01-03T11:20:00", + "2024-01-03T11:21:00", + "2024-01-03T11:22:00", + "2024-01-03T11:23:00", + "2024-01-03T11:24:00", + "2024-01-03T11:25:00", + "2024-01-03T11:26:00", + "2024-01-03T11:27:00", + "2024-01-03T11:28:00", + "2024-01-03T11:29:00", + "2024-01-03T11:30:00", + "2024-01-03T11:31:00", + "2024-01-03T11:32:00", + "2024-01-03T11:33:00", + "2024-01-03T11:34:00", + "2024-01-03T11:35:00", + "2024-01-03T11:36:00", + "2024-01-03T11:37:00", + "2024-01-03T11:38:00", + "2024-01-03T11:39:00", + "2024-01-03T11:40:00", + "2024-01-03T11:41:00", + "2024-01-03T11:42:00", + "2024-01-03T11:43:00", + "2024-01-03T11:44:00", + "2024-01-03T11:45:00", + "2024-01-03T11:46:00", + "2024-01-03T11:47:00", + "2024-01-03T11:48:00", + "2024-01-03T11:49:00", + "2024-01-03T11:50:00", + "2024-01-03T11:51:00", + "2024-01-03T11:52:00", + "2024-01-03T11:53:00", + "2024-01-03T11:54:00", + "2024-01-03T11:55:00", + "2024-01-03T11:56:00", + "2024-01-03T11:57:00", + "2024-01-03T11:58:00", + "2024-01-03T11:59:00", + "2024-01-03T12:00:00", + "2024-01-03T12:01:00", + "2024-01-03T12:02:00", + "2024-01-03T12:03:00", + "2024-01-03T12:04:00", + "2024-01-03T12:05:00", + "2024-01-03T12:06:00", + "2024-01-03T12:07:00", + "2024-01-03T12:08:00", + "2024-01-03T12:09:00", + "2024-01-03T12:10:00", + "2024-01-03T12:11:00", + "2024-01-03T12:12:00", + "2024-01-03T12:13:00", + "2024-01-03T12:14:00", + "2024-01-03T12:15:00", + "2024-01-03T12:16:00", + "2024-01-03T12:17:00", + "2024-01-03T12:18:00", + "2024-01-03T12:19:00", + "2024-01-03T12:20:00", + "2024-01-03T12:21:00", + "2024-01-03T12:22:00", + "2024-01-03T12:23:00", + "2024-01-03T12:24:00", + "2024-01-03T12:25:00", + "2024-01-03T12:26:00", + "2024-01-03T12:27:00", + "2024-01-03T12:28:00", + "2024-01-03T12:29:00", + "2024-01-03T12:30:00", + "2024-01-03T12:31:00", + "2024-01-03T12:32:00", + "2024-01-03T12:33:00", + "2024-01-03T12:34:00", + "2024-01-03T12:35:00", + "2024-01-03T12:36:00", + "2024-01-03T12:37:00", + "2024-01-03T12:38:00", + "2024-01-03T12:39:00", + "2024-01-03T12:40:00", + "2024-01-03T12:41:00", + "2024-01-03T12:42:00", + "2024-01-03T12:43:00", + "2024-01-03T12:44:00", + "2024-01-03T12:45:00", + "2024-01-03T12:46:00", + "2024-01-03T12:47:00", + "2024-01-03T12:48:00", + "2024-01-03T12:49:00", + "2024-01-03T12:50:00", + "2024-01-03T12:51:00", + "2024-01-03T12:52:00", + "2024-01-03T12:53:00", + "2024-01-03T12:54:00", + "2024-01-03T12:55:00", + "2024-01-03T12:56:00", + "2024-01-03T12:57:00", + "2024-01-03T12:58:00", + "2024-01-03T12:59:00", + "2024-01-03T13:00:00", + "2024-01-03T13:01:00", + "2024-01-03T13:02:00", + "2024-01-03T13:03:00", + "2024-01-03T13:04:00", + "2024-01-03T13:05:00", + "2024-01-03T13:06:00", + "2024-01-03T13:07:00", + "2024-01-03T13:08:00", + "2024-01-03T13:09:00", + "2024-01-03T13:10:00", + "2024-01-03T13:11:00", + "2024-01-03T13:12:00", + "2024-01-03T13:13:00", + "2024-01-03T13:14:00", + "2024-01-03T13:15:00", + "2024-01-03T13:16:00", + "2024-01-03T13:17:00", + "2024-01-03T13:18:00", + "2024-01-03T13:19:00", + "2024-01-03T13:20:00", + "2024-01-03T13:21:00", + "2024-01-03T13:22:00", + "2024-01-03T13:23:00", + "2024-01-03T13:24:00", + "2024-01-03T13:25:00", + "2024-01-03T13:26:00", + "2024-01-03T13:27:00", + "2024-01-03T13:28:00", + "2024-01-03T13:29:00", + "2024-01-03T13:30:00", + "2024-01-03T13:31:00", + "2024-01-03T13:32:00", + "2024-01-03T13:33:00", + "2024-01-03T13:34:00", + "2024-01-03T13:35:00", + "2024-01-03T13:36:00", + "2024-01-03T13:37:00", + "2024-01-03T13:38:00", + "2024-01-03T13:39:00", + "2024-01-03T13:40:00", + "2024-01-03T13:41:00", + "2024-01-03T13:42:00", + "2024-01-03T13:43:00", + "2024-01-03T13:44:00", + "2024-01-03T13:45:00", + "2024-01-03T13:46:00", + "2024-01-03T13:47:00", + "2024-01-03T13:48:00", + "2024-01-03T13:49:00", + "2024-01-03T13:50:00", + "2024-01-03T13:51:00", + "2024-01-03T13:52:00", + "2024-01-03T13:53:00", + "2024-01-03T13:54:00", + "2024-01-03T13:55:00", + "2024-01-03T13:56:00", + "2024-01-03T13:57:00", + "2024-01-03T13:58:00", + "2024-01-03T13:59:00", + "2024-01-03T14:00:00", + "2024-01-03T14:01:00", + "2024-01-03T14:02:00", + "2024-01-03T14:03:00", + "2024-01-03T14:04:00", + "2024-01-03T14:05:00", + "2024-01-03T14:06:00", + "2024-01-03T14:07:00", + "2024-01-03T14:08:00", + "2024-01-03T14:09:00", + "2024-01-03T14:10:00", + "2024-01-03T14:11:00", + "2024-01-03T14:12:00", + "2024-01-03T14:13:00", + "2024-01-03T14:14:00", + "2024-01-03T14:15:00", + "2024-01-03T14:16:00", + "2024-01-03T14:17:00", + "2024-01-03T14:18:00", + "2024-01-03T14:19:00", + "2024-01-03T14:20:00", + "2024-01-03T14:21:00", + "2024-01-03T14:22:00", + "2024-01-03T14:23:00", + "2024-01-03T14:24:00", + "2024-01-03T14:25:00", + "2024-01-03T14:26:00", + "2024-01-03T14:27:00", + "2024-01-03T14:28:00", + "2024-01-03T14:29:00", + "2024-01-03T14:30:00", + "2024-01-03T14:31:00", + "2024-01-03T14:32:00", + "2024-01-03T14:33:00", + "2024-01-03T14:34:00", + "2024-01-03T14:35:00", + "2024-01-03T14:36:00", + "2024-01-03T14:37:00", + "2024-01-03T14:38:00", + "2024-01-03T14:39:00", + "2024-01-03T14:40:00", + "2024-01-03T14:41:00", + "2024-01-03T14:42:00", + "2024-01-03T14:43:00", + "2024-01-03T14:44:00", + "2024-01-03T14:45:00", + "2024-01-03T14:46:00", + "2024-01-03T14:47:00", + "2024-01-03T14:48:00", + "2024-01-03T14:49:00", + "2024-01-03T14:50:00", + "2024-01-03T14:51:00", + "2024-01-03T14:52:00", + "2024-01-03T14:53:00", + "2024-01-03T14:54:00", + "2024-01-03T14:55:00", + "2024-01-03T14:56:00", + "2024-01-03T14:57:00", + "2024-01-03T14:58:00", + "2024-01-03T14:59:00", + "2024-01-03T15:00:00", + "2024-01-03T15:01:00", + "2024-01-03T15:02:00", + "2024-01-03T15:03:00", + "2024-01-03T15:04:00", + "2024-01-03T15:05:00", + "2024-01-03T15:06:00", + "2024-01-03T15:07:00", + "2024-01-03T15:08:00", + "2024-01-03T15:09:00", + "2024-01-03T15:10:00", + "2024-01-03T15:11:00", + "2024-01-03T15:12:00", + "2024-01-03T15:13:00", + "2024-01-03T15:14:00", + "2024-01-03T15:15:00", + "2024-01-03T15:16:00", + "2024-01-03T15:17:00", + "2024-01-03T15:18:00", + "2024-01-03T15:19:00", + "2024-01-03T15:20:00", + "2024-01-03T15:21:00", + "2024-01-03T15:22:00", + "2024-01-03T15:23:00", + "2024-01-03T15:24:00", + "2024-01-03T15:25:00", + "2024-01-03T15:26:00", + "2024-01-03T15:27:00", + "2024-01-03T15:28:00", + "2024-01-03T15:29:00", + "2024-01-03T15:30:00", + "2024-01-03T15:31:00", + "2024-01-03T15:32:00", + "2024-01-03T15:33:00", + "2024-01-03T15:34:00", + "2024-01-03T15:35:00", + "2024-01-03T15:36:00", + "2024-01-03T15:37:00", + "2024-01-03T15:38:00", + "2024-01-03T15:39:00", + "2024-01-03T15:40:00", + "2024-01-03T15:41:00", + "2024-01-03T15:42:00", + "2024-01-03T15:43:00", + "2024-01-03T15:44:00", + "2024-01-03T15:45:00", + "2024-01-03T15:46:00", + "2024-01-03T15:47:00", + "2024-01-03T15:48:00", + "2024-01-03T15:49:00", + "2024-01-03T15:50:00", + "2024-01-03T15:51:00", + "2024-01-03T15:52:00", + "2024-01-03T15:53:00", + "2024-01-03T15:54:00", + "2024-01-03T15:55:00", + "2024-01-03T15:56:00", + "2024-01-03T15:57:00", + "2024-01-03T15:58:00", + "2024-01-03T15:59:00", + "2024-01-03T16:00:00", + "2024-01-03T16:01:00", + "2024-01-03T16:02:00", + "2024-01-03T16:03:00", + "2024-01-03T16:04:00", + "2024-01-03T16:05:00", + "2024-01-03T16:06:00", + "2024-01-03T16:07:00", + "2024-01-03T16:08:00", + "2024-01-03T16:09:00", + "2024-01-03T16:10:00", + "2024-01-03T16:11:00", + "2024-01-03T16:12:00", + "2024-01-03T16:13:00", + "2024-01-03T16:14:00", + "2024-01-03T16:15:00", + "2024-01-03T16:16:00", + "2024-01-03T16:17:00", + "2024-01-03T16:18:00", + "2024-01-03T16:19:00", + "2024-01-03T16:20:00", + "2024-01-03T16:21:00", + "2024-01-03T16:22:00", + "2024-01-03T16:23:00", + "2024-01-03T16:24:00", + "2024-01-03T16:25:00", + "2024-01-03T16:26:00", + "2024-01-03T16:27:00", + "2024-01-03T16:28:00", + "2024-01-03T16:29:00", + "2024-01-03T16:30:00", + "2024-01-03T16:31:00", + "2024-01-03T16:32:00", + "2024-01-03T16:33:00", + "2024-01-03T16:34:00", + "2024-01-03T16:35:00", + "2024-01-03T16:36:00", + "2024-01-03T16:37:00", + "2024-01-03T16:38:00", + "2024-01-03T16:39:00", + "2024-01-03T16:40:00", + "2024-01-03T16:41:00", + "2024-01-03T16:42:00", + "2024-01-03T16:43:00", + "2024-01-03T16:44:00", + "2024-01-03T16:45:00", + "2024-01-03T16:46:00", + "2024-01-03T16:47:00", + "2024-01-03T16:48:00", + "2024-01-03T16:49:00", + "2024-01-03T16:50:00", + "2024-01-03T16:51:00", + "2024-01-03T16:52:00", + "2024-01-03T16:53:00", + "2024-01-03T16:54:00", + "2024-01-03T16:55:00", + "2024-01-03T16:56:00", + "2024-01-03T16:57:00", + "2024-01-03T16:58:00", + "2024-01-03T16:59:00", + "2024-01-03T17:00:00", + "2024-01-03T17:01:00", + "2024-01-03T17:02:00", + "2024-01-03T17:03:00", + "2024-01-03T17:04:00", + "2024-01-03T17:05:00", + "2024-01-03T17:06:00", + "2024-01-03T17:07:00", + "2024-01-03T17:08:00", + "2024-01-03T17:09:00", + "2024-01-03T17:10:00", + "2024-01-03T17:11:00", + "2024-01-03T17:12:00", + "2024-01-03T17:13:00", + "2024-01-03T17:14:00", + "2024-01-03T17:15:00", + "2024-01-03T17:16:00", + "2024-01-03T17:17:00", + "2024-01-03T17:18:00", + "2024-01-03T17:19:00", + "2024-01-03T17:20:00", + "2024-01-03T17:21:00", + "2024-01-03T17:22:00", + "2024-01-03T17:23:00", + "2024-01-03T17:24:00", + "2024-01-03T17:25:00", + "2024-01-03T17:26:00", + "2024-01-03T17:27:00", + "2024-01-03T17:28:00", + "2024-01-03T17:29:00", + "2024-01-03T17:30:00", + "2024-01-03T17:31:00", + "2024-01-03T17:32:00", + "2024-01-03T17:33:00", + "2024-01-03T17:34:00", + "2024-01-03T17:35:00", + "2024-01-03T17:36:00", + "2024-01-03T17:37:00", + "2024-01-03T17:38:00", + "2024-01-03T17:39:00", + "2024-01-03T17:40:00", + "2024-01-03T17:41:00", + "2024-01-03T17:42:00", + "2024-01-03T17:43:00", + "2024-01-03T17:44:00", + "2024-01-03T17:45:00", + "2024-01-03T17:46:00", + "2024-01-03T17:47:00", + "2024-01-03T17:48:00", + "2024-01-03T17:49:00", + "2024-01-03T17:50:00", + "2024-01-03T17:51:00", + "2024-01-03T17:52:00", + "2024-01-03T17:53:00", + "2024-01-03T17:54:00", + "2024-01-03T17:55:00", + "2024-01-03T17:56:00", + "2024-01-03T17:57:00", + "2024-01-03T17:58:00", + "2024-01-03T17:59:00", + "2024-01-03T18:00:00", + "2024-01-03T18:01:00", + "2024-01-03T18:02:00", + "2024-01-03T18:03:00", + "2024-01-03T18:04:00", + "2024-01-03T18:05:00", + "2024-01-03T18:06:00", + "2024-01-03T18:07:00", + "2024-01-03T18:08:00", + "2024-01-03T18:09:00", + "2024-01-03T18:10:00", + "2024-01-03T18:11:00", + "2024-01-03T18:12:00", + "2024-01-03T18:13:00", + "2024-01-03T18:14:00", + "2024-01-03T18:15:00", + "2024-01-03T18:16:00", + "2024-01-03T18:17:00", + "2024-01-03T18:18:00", + "2024-01-03T18:19:00", + "2024-01-03T18:20:00", + "2024-01-03T18:21:00", + "2024-01-03T18:22:00", + "2024-01-03T18:23:00", + "2024-01-03T18:24:00", + "2024-01-03T18:25:00", + "2024-01-03T18:26:00", + "2024-01-03T18:27:00", + "2024-01-03T18:28:00", + "2024-01-03T18:29:00", + "2024-01-03T18:30:00", + "2024-01-03T18:31:00", + "2024-01-03T18:32:00", + "2024-01-03T18:33:00", + "2024-01-03T18:34:00", + "2024-01-03T18:35:00", + "2024-01-03T18:36:00", + "2024-01-03T18:37:00", + "2024-01-03T18:38:00", + "2024-01-03T18:39:00", + "2024-01-03T18:40:00", + "2024-01-03T18:41:00", + "2024-01-03T18:42:00", + "2024-01-03T18:43:00", + "2024-01-03T18:44:00", + "2024-01-03T18:45:00", + "2024-01-03T18:46:00", + "2024-01-03T18:47:00", + "2024-01-03T18:48:00", + "2024-01-03T18:49:00", + "2024-01-03T18:50:00", + "2024-01-03T18:51:00", + "2024-01-03T18:52:00", + "2024-01-03T18:53:00", + "2024-01-03T18:54:00", + "2024-01-03T18:55:00", + "2024-01-03T18:56:00", + "2024-01-03T18:57:00", + "2024-01-03T18:58:00", + "2024-01-03T18:59:00", + "2024-01-03T19:00:00", + "2024-01-03T19:01:00", + "2024-01-03T19:02:00", + "2024-01-03T19:03:00", + "2024-01-03T19:04:00", + "2024-01-03T19:05:00", + "2024-01-03T19:06:00", + "2024-01-03T19:07:00", + "2024-01-03T19:08:00", + "2024-01-03T19:09:00", + "2024-01-03T19:10:00", + "2024-01-03T19:11:00", + "2024-01-03T19:12:00", + "2024-01-03T19:13:00", + "2024-01-03T19:14:00", + "2024-01-03T19:15:00", + "2024-01-03T19:16:00", + "2024-01-03T19:17:00", + "2024-01-03T19:18:00", + "2024-01-03T19:19:00", + "2024-01-03T19:20:00", + "2024-01-03T19:21:00", + "2024-01-03T19:22:00", + "2024-01-03T19:23:00", + "2024-01-03T19:24:00", + "2024-01-03T19:25:00", + "2024-01-03T19:26:00", + "2024-01-03T19:27:00", + "2024-01-03T19:28:00", + "2024-01-03T19:29:00", + "2024-01-03T19:30:00", + "2024-01-03T19:31:00", + "2024-01-03T19:32:00", + "2024-01-03T19:33:00", + "2024-01-03T19:34:00", + "2024-01-03T19:35:00", + "2024-01-03T19:36:00", + "2024-01-03T19:37:00", + "2024-01-03T19:38:00", + "2024-01-03T19:39:00", + "2024-01-03T19:40:00", + "2024-01-03T19:41:00", + "2024-01-03T19:42:00", + "2024-01-03T19:43:00", + "2024-01-03T19:44:00", + "2024-01-03T19:45:00", + "2024-01-03T19:46:00", + "2024-01-03T19:47:00", + "2024-01-03T19:48:00", + "2024-01-03T19:49:00", + "2024-01-03T19:50:00", + "2024-01-03T19:51:00", + "2024-01-03T19:52:00", + "2024-01-03T19:53:00", + "2024-01-03T19:54:00", + "2024-01-03T19:55:00", + "2024-01-03T19:56:00", + "2024-01-03T19:57:00", + "2024-01-03T19:58:00", + "2024-01-03T19:59:00", + "2024-01-03T20:00:00", + "2024-01-03T20:01:00", + "2024-01-03T20:02:00", + "2024-01-03T20:03:00", + "2024-01-03T20:04:00", + "2024-01-03T20:05:00", + "2024-01-03T20:06:00", + "2024-01-03T20:07:00", + "2024-01-03T20:08:00", + "2024-01-03T20:09:00", + "2024-01-03T20:10:00", + "2024-01-03T20:11:00", + "2024-01-03T20:12:00", + "2024-01-03T20:13:00", + "2024-01-03T20:14:00", + "2024-01-03T20:15:00", + "2024-01-03T20:16:00", + "2024-01-03T20:17:00", + "2024-01-03T20:18:00", + "2024-01-03T20:19:00", + "2024-01-03T20:20:00", + "2024-01-03T20:21:00", + "2024-01-03T20:22:00", + "2024-01-03T20:23:00", + "2024-01-03T20:24:00", + "2024-01-03T20:25:00", + "2024-01-03T20:26:00", + "2024-01-03T20:27:00", + "2024-01-03T20:28:00", + "2024-01-03T20:29:00", + "2024-01-03T20:30:00", + "2024-01-03T20:31:00", + "2024-01-03T20:32:00", + "2024-01-03T20:33:00", + "2024-01-03T20:34:00", + "2024-01-03T20:35:00", + "2024-01-03T20:36:00", + "2024-01-03T20:37:00", + "2024-01-03T20:38:00", + "2024-01-03T20:39:00", + "2024-01-03T20:40:00", + "2024-01-03T20:41:00", + "2024-01-03T20:42:00", + "2024-01-03T20:43:00", + "2024-01-03T20:44:00", + "2024-01-03T20:45:00", + "2024-01-03T20:46:00", + "2024-01-03T20:47:00", + "2024-01-03T20:48:00", + "2024-01-03T20:49:00", + "2024-01-03T20:50:00", + "2024-01-03T20:51:00", + "2024-01-03T20:52:00", + "2024-01-03T20:53:00", + "2024-01-03T20:54:00", + "2024-01-03T20:55:00", + "2024-01-03T20:56:00", + "2024-01-03T20:57:00", + "2024-01-03T20:58:00", + "2024-01-03T20:59:00", + "2024-01-03T21:00:00", + "2024-01-03T21:01:00", + "2024-01-03T21:02:00", + "2024-01-03T21:03:00", + "2024-01-03T21:04:00", + "2024-01-03T21:05:00", + "2024-01-03T21:06:00", + "2024-01-03T21:07:00", + "2024-01-03T21:08:00", + "2024-01-03T21:09:00", + "2024-01-03T21:10:00", + "2024-01-03T21:11:00", + "2024-01-03T21:12:00", + "2024-01-03T21:13:00", + "2024-01-03T21:14:00", + "2024-01-03T21:15:00", + "2024-01-03T21:16:00", + "2024-01-03T21:17:00", + "2024-01-03T21:18:00", + "2024-01-03T21:19:00", + "2024-01-03T21:20:00", + "2024-01-03T21:21:00", + "2024-01-03T21:22:00", + "2024-01-03T21:23:00", + "2024-01-03T21:24:00", + "2024-01-03T21:25:00", + "2024-01-03T21:26:00", + "2024-01-03T21:27:00", + "2024-01-03T21:28:00", + "2024-01-03T21:29:00", + "2024-01-03T21:30:00", + "2024-01-03T21:31:00", + "2024-01-03T21:32:00", + "2024-01-03T21:33:00", + "2024-01-03T21:34:00", + "2024-01-03T21:35:00", + "2024-01-03T21:36:00", + "2024-01-03T21:37:00", + "2024-01-03T21:38:00", + "2024-01-03T21:39:00", + "2024-01-03T21:40:00", + "2024-01-03T21:41:00", + "2024-01-03T21:42:00", + "2024-01-03T21:43:00", + "2024-01-03T21:44:00", + "2024-01-03T21:45:00", + "2024-01-03T21:46:00", + "2024-01-03T21:47:00", + "2024-01-03T21:48:00", + "2024-01-03T21:49:00", + "2024-01-03T21:50:00", + "2024-01-03T21:51:00", + "2024-01-03T21:52:00", + "2024-01-03T21:53:00", + "2024-01-03T21:54:00", + "2024-01-03T21:55:00", + "2024-01-03T21:56:00", + "2024-01-03T21:57:00", + "2024-01-03T21:58:00", + "2024-01-03T21:59:00", + "2024-01-03T22:00:00", + "2024-01-03T22:01:00", + "2024-01-03T22:02:00", + "2024-01-03T22:03:00", + "2024-01-03T22:04:00", + "2024-01-03T22:05:00", + "2024-01-03T22:06:00", + "2024-01-03T22:07:00", + "2024-01-03T22:08:00", + "2024-01-03T22:09:00", + "2024-01-03T22:10:00", + "2024-01-03T22:11:00", + "2024-01-03T22:12:00", + "2024-01-03T22:13:00", + "2024-01-03T22:14:00", + "2024-01-03T22:15:00", + "2024-01-03T22:16:00", + "2024-01-03T22:17:00", + "2024-01-03T22:18:00", + "2024-01-03T22:19:00", + "2024-01-03T22:20:00", + "2024-01-03T22:21:00", + "2024-01-03T22:22:00", + "2024-01-03T22:23:00", + "2024-01-03T22:24:00", + "2024-01-03T22:25:00", + "2024-01-03T22:26:00", + "2024-01-03T22:27:00", + "2024-01-03T22:28:00", + "2024-01-03T22:29:00", + "2024-01-03T22:30:00", + "2024-01-03T22:31:00", + "2024-01-03T22:32:00", + "2024-01-03T22:33:00", + "2024-01-03T22:34:00", + "2024-01-03T22:35:00", + "2024-01-03T22:36:00", + "2024-01-03T22:37:00", + "2024-01-03T22:38:00", + "2024-01-03T22:39:00", + "2024-01-03T22:40:00", + "2024-01-03T22:41:00", + "2024-01-03T22:42:00", + "2024-01-03T22:43:00", + "2024-01-03T22:44:00", + "2024-01-03T22:45:00", + "2024-01-03T22:46:00", + "2024-01-03T22:47:00", + "2024-01-03T22:48:00", + "2024-01-03T22:49:00", + "2024-01-03T22:50:00", + "2024-01-03T22:51:00", + "2024-01-03T22:52:00", + "2024-01-03T22:53:00", + "2024-01-03T22:54:00", + "2024-01-03T22:55:00", + "2024-01-03T22:56:00", + "2024-01-03T22:57:00", + "2024-01-03T22:58:00", + "2024-01-03T22:59:00", + "2024-01-03T23:00:00", + "2024-01-03T23:01:00", + "2024-01-03T23:02:00", + "2024-01-03T23:03:00", + "2024-01-03T23:04:00", + "2024-01-03T23:05:00", + "2024-01-03T23:06:00", + "2024-01-03T23:07:00", + "2024-01-03T23:08:00", + "2024-01-03T23:09:00", + "2024-01-03T23:10:00", + "2024-01-03T23:11:00", + "2024-01-03T23:12:00", + "2024-01-03T23:13:00", + "2024-01-03T23:14:00", + "2024-01-03T23:15:00", + "2024-01-03T23:16:00", + "2024-01-03T23:17:00", + "2024-01-03T23:18:00", + "2024-01-03T23:19:00", + "2024-01-03T23:20:00", + "2024-01-03T23:21:00", + "2024-01-03T23:22:00", + "2024-01-03T23:23:00", + "2024-01-03T23:24:00", + "2024-01-03T23:25:00", + "2024-01-03T23:26:00", + "2024-01-03T23:27:00", + "2024-01-03T23:28:00", + "2024-01-03T23:29:00", + "2024-01-03T23:30:00", + "2024-01-03T23:31:00", + "2024-01-03T23:32:00", + "2024-01-03T23:33:00", + "2024-01-03T23:34:00", + "2024-01-03T23:35:00", + "2024-01-03T23:36:00", + "2024-01-03T23:37:00", + "2024-01-03T23:38:00", + "2024-01-03T23:39:00", + "2024-01-03T23:40:00", + "2024-01-03T23:41:00", + "2024-01-03T23:42:00", + "2024-01-03T23:43:00", + "2024-01-03T23:44:00", + "2024-01-03T23:45:00", + "2024-01-03T23:46:00", + "2024-01-03T23:47:00", + "2024-01-03T23:48:00", + "2024-01-03T23:49:00", + "2024-01-03T23:50:00", + "2024-01-03T23:51:00", + "2024-01-03T23:52:00", + "2024-01-03T23:53:00", + "2024-01-03T23:54:00", + "2024-01-03T23:55:00", + "2024-01-03T23:56:00", + "2024-01-03T23:57:00", + "2024-01-03T23:58:00", + "2024-01-03T23:59:00", + "2024-01-04T00:00:00", + "2024-01-04T00:01:00", + "2024-01-04T00:02:00", + "2024-01-04T00:03:00", + "2024-01-04T00:04:00", + "2024-01-04T00:05:00", + "2024-01-04T00:06:00", + "2024-01-04T00:07:00", + "2024-01-04T00:08:00", + "2024-01-04T00:09:00", + "2024-01-04T00:10:00", + "2024-01-04T00:11:00", + "2024-01-04T00:12:00", + "2024-01-04T00:13:00", + "2024-01-04T00:14:00", + "2024-01-04T00:15:00", + "2024-01-04T00:16:00", + "2024-01-04T00:17:00", + "2024-01-04T00:18:00", + "2024-01-04T00:19:00", + "2024-01-04T00:20:00", + "2024-01-04T00:21:00", + "2024-01-04T00:22:00", + "2024-01-04T00:23:00", + "2024-01-04T00:24:00", + "2024-01-04T00:25:00", + "2024-01-04T00:26:00", + "2024-01-04T00:27:00", + "2024-01-04T00:28:00", + "2024-01-04T00:29:00", + "2024-01-04T00:30:00", + "2024-01-04T00:31:00", + "2024-01-04T00:32:00", + "2024-01-04T00:33:00", + "2024-01-04T00:34:00", + "2024-01-04T00:35:00", + "2024-01-04T00:36:00", + "2024-01-04T00:37:00", + "2024-01-04T00:38:00", + "2024-01-04T00:39:00", + "2024-01-04T00:40:00", + "2024-01-04T00:41:00", + "2024-01-04T00:42:00", + "2024-01-04T00:43:00", + "2024-01-04T00:44:00", + "2024-01-04T00:45:00", + "2024-01-04T00:46:00", + "2024-01-04T00:47:00", + "2024-01-04T00:48:00", + "2024-01-04T00:49:00", + "2024-01-04T00:50:00", + "2024-01-04T00:51:00", + "2024-01-04T00:52:00", + "2024-01-04T00:53:00", + "2024-01-04T00:54:00", + "2024-01-04T00:55:00", + "2024-01-04T00:56:00", + "2024-01-04T00:57:00", + "2024-01-04T00:58:00", + "2024-01-04T00:59:00", + "2024-01-04T01:00:00", + "2024-01-04T01:01:00", + "2024-01-04T01:02:00", + "2024-01-04T01:03:00", + "2024-01-04T01:04:00", + "2024-01-04T01:05:00", + "2024-01-04T01:06:00", + "2024-01-04T01:07:00", + "2024-01-04T01:08:00", + "2024-01-04T01:09:00", + "2024-01-04T01:10:00", + "2024-01-04T01:11:00", + "2024-01-04T01:12:00", + "2024-01-04T01:13:00", + "2024-01-04T01:14:00", + "2024-01-04T01:15:00", + "2024-01-04T01:16:00", + "2024-01-04T01:17:00", + "2024-01-04T01:18:00", + "2024-01-04T01:19:00", + "2024-01-04T01:20:00", + "2024-01-04T01:21:00", + "2024-01-04T01:22:00", + "2024-01-04T01:23:00", + "2024-01-04T01:24:00", + "2024-01-04T01:25:00", + "2024-01-04T01:26:00", + "2024-01-04T01:27:00", + "2024-01-04T01:28:00", + "2024-01-04T01:29:00", + "2024-01-04T01:30:00", + "2024-01-04T01:31:00", + "2024-01-04T01:32:00", + "2024-01-04T01:33:00", + "2024-01-04T01:34:00", + "2024-01-04T01:35:00", + "2024-01-04T01:36:00", + "2024-01-04T01:37:00", + "2024-01-04T01:38:00", + "2024-01-04T01:39:00", + "2024-01-04T01:40:00", + "2024-01-04T01:41:00", + "2024-01-04T01:42:00", + "2024-01-04T01:43:00", + "2024-01-04T01:44:00", + "2024-01-04T01:45:00", + "2024-01-04T01:46:00", + "2024-01-04T01:47:00", + "2024-01-04T01:48:00", + "2024-01-04T01:49:00", + "2024-01-04T01:50:00", + "2024-01-04T01:51:00", + "2024-01-04T01:52:00", + "2024-01-04T01:53:00", + "2024-01-04T01:54:00", + "2024-01-04T01:55:00", + "2024-01-04T01:56:00", + "2024-01-04T01:57:00", + "2024-01-04T01:58:00", + "2024-01-04T01:59:00", + "2024-01-04T02:00:00", + "2024-01-04T02:01:00", + "2024-01-04T02:02:00", + "2024-01-04T02:03:00", + "2024-01-04T02:04:00", + "2024-01-04T02:05:00", + "2024-01-04T02:06:00", + "2024-01-04T02:07:00", + "2024-01-04T02:08:00", + "2024-01-04T02:09:00", + "2024-01-04T02:10:00", + "2024-01-04T02:11:00", + "2024-01-04T02:12:00", + "2024-01-04T02:13:00", + "2024-01-04T02:14:00", + "2024-01-04T02:15:00", + "2024-01-04T02:16:00", + "2024-01-04T02:17:00", + "2024-01-04T02:18:00", + "2024-01-04T02:19:00", + "2024-01-04T02:20:00", + "2024-01-04T02:21:00", + "2024-01-04T02:22:00", + "2024-01-04T02:23:00", + "2024-01-04T02:24:00", + "2024-01-04T02:25:00", + "2024-01-04T02:26:00", + "2024-01-04T02:27:00", + "2024-01-04T02:28:00", + "2024-01-04T02:29:00", + "2024-01-04T02:30:00", + "2024-01-04T02:31:00", + "2024-01-04T02:32:00", + "2024-01-04T02:33:00", + "2024-01-04T02:34:00", + "2024-01-04T02:35:00", + "2024-01-04T02:36:00", + "2024-01-04T02:37:00", + "2024-01-04T02:38:00", + "2024-01-04T02:39:00", + "2024-01-04T02:40:00", + "2024-01-04T02:41:00", + "2024-01-04T02:42:00", + "2024-01-04T02:43:00", + "2024-01-04T02:44:00", + "2024-01-04T02:45:00", + "2024-01-04T02:46:00", + "2024-01-04T02:47:00", + "2024-01-04T02:48:00", + "2024-01-04T02:49:00", + "2024-01-04T02:50:00", + "2024-01-04T02:51:00", + "2024-01-04T02:52:00", + "2024-01-04T02:53:00", + "2024-01-04T02:54:00", + "2024-01-04T02:55:00", + "2024-01-04T02:56:00", + "2024-01-04T02:57:00", + "2024-01-04T02:58:00", + "2024-01-04T02:59:00", + "2024-01-04T03:00:00", + "2024-01-04T03:01:00", + "2024-01-04T03:02:00", + "2024-01-04T03:03:00", + "2024-01-04T03:04:00", + "2024-01-04T03:05:00", + "2024-01-04T03:06:00", + "2024-01-04T03:07:00", + "2024-01-04T03:08:00", + "2024-01-04T03:09:00", + "2024-01-04T03:10:00", + "2024-01-04T03:11:00", + "2024-01-04T03:12:00", + "2024-01-04T03:13:00", + "2024-01-04T03:14:00", + "2024-01-04T03:15:00", + "2024-01-04T03:16:00", + "2024-01-04T03:17:00", + "2024-01-04T03:18:00", + "2024-01-04T03:19:00", + "2024-01-04T03:20:00", + "2024-01-04T03:21:00", + "2024-01-04T03:22:00", + "2024-01-04T03:23:00", + "2024-01-04T03:24:00", + "2024-01-04T03:25:00", + "2024-01-04T03:26:00", + "2024-01-04T03:27:00", + "2024-01-04T03:28:00", + "2024-01-04T03:29:00", + "2024-01-04T03:30:00", + "2024-01-04T03:31:00", + "2024-01-04T03:32:00", + "2024-01-04T03:33:00", + "2024-01-04T03:34:00", + "2024-01-04T03:35:00", + "2024-01-04T03:36:00", + "2024-01-04T03:37:00", + "2024-01-04T03:38:00", + "2024-01-04T03:39:00", + "2024-01-04T03:40:00", + "2024-01-04T03:41:00", + "2024-01-04T03:42:00", + "2024-01-04T03:43:00", + "2024-01-04T03:44:00", + "2024-01-04T03:45:00", + "2024-01-04T03:46:00", + "2024-01-04T03:47:00", + "2024-01-04T03:48:00", + "2024-01-04T03:49:00", + "2024-01-04T03:50:00", + "2024-01-04T03:51:00", + "2024-01-04T03:52:00", + "2024-01-04T03:53:00", + "2024-01-04T03:54:00", + "2024-01-04T03:55:00", + "2024-01-04T03:56:00", + "2024-01-04T03:57:00", + "2024-01-04T03:58:00", + "2024-01-04T03:59:00", + "2024-01-04T04:00:00", + "2024-01-04T04:01:00", + "2024-01-04T04:02:00", + "2024-01-04T04:03:00", + "2024-01-04T04:04:00", + "2024-01-04T04:05:00", + "2024-01-04T04:06:00", + "2024-01-04T04:07:00", + "2024-01-04T04:08:00", + "2024-01-04T04:09:00", + "2024-01-04T04:10:00", + "2024-01-04T04:11:00", + "2024-01-04T04:12:00", + "2024-01-04T04:13:00", + "2024-01-04T04:14:00", + "2024-01-04T04:15:00", + "2024-01-04T04:16:00", + "2024-01-04T04:17:00", + "2024-01-04T04:18:00", + "2024-01-04T04:19:00", + "2024-01-04T04:20:00", + "2024-01-04T04:21:00", + "2024-01-04T04:22:00", + "2024-01-04T04:23:00", + "2024-01-04T04:24:00", + "2024-01-04T04:25:00", + "2024-01-04T04:26:00", + "2024-01-04T04:27:00", + "2024-01-04T04:28:00", + "2024-01-04T04:29:00", + "2024-01-04T04:30:00", + "2024-01-04T04:31:00", + "2024-01-04T04:32:00", + "2024-01-04T04:33:00", + "2024-01-04T04:34:00", + "2024-01-04T04:35:00", + "2024-01-04T04:36:00", + "2024-01-04T04:37:00", + "2024-01-04T04:38:00", + "2024-01-04T04:39:00", + "2024-01-04T04:40:00", + "2024-01-04T04:41:00", + "2024-01-04T04:42:00", + "2024-01-04T04:43:00", + "2024-01-04T04:44:00", + "2024-01-04T04:45:00", + "2024-01-04T04:46:00", + "2024-01-04T04:47:00", + "2024-01-04T04:48:00", + "2024-01-04T04:49:00", + "2024-01-04T04:50:00", + "2024-01-04T04:51:00", + "2024-01-04T04:52:00", + "2024-01-04T04:53:00", + "2024-01-04T04:54:00", + "2024-01-04T04:55:00", + "2024-01-04T04:56:00", + "2024-01-04T04:57:00", + "2024-01-04T04:58:00", + "2024-01-04T04:59:00", + "2024-01-04T05:00:00", + "2024-01-04T05:01:00", + "2024-01-04T05:02:00", + "2024-01-04T05:03:00", + "2024-01-04T05:04:00", + "2024-01-04T05:05:00", + "2024-01-04T05:06:00", + "2024-01-04T05:07:00", + "2024-01-04T05:08:00", + "2024-01-04T05:09:00", + "2024-01-04T05:10:00", + "2024-01-04T05:11:00", + "2024-01-04T05:12:00", + "2024-01-04T05:13:00", + "2024-01-04T05:14:00", + "2024-01-04T05:15:00", + "2024-01-04T05:16:00", + "2024-01-04T05:17:00", + "2024-01-04T05:18:00", + "2024-01-04T05:19:00", + "2024-01-04T05:20:00", + "2024-01-04T05:21:00", + "2024-01-04T05:22:00", + "2024-01-04T05:23:00", + "2024-01-04T05:24:00", + "2024-01-04T05:25:00", + "2024-01-04T05:26:00", + "2024-01-04T05:27:00", + "2024-01-04T05:28:00", + "2024-01-04T05:29:00", + "2024-01-04T05:30:00", + "2024-01-04T05:31:00", + "2024-01-04T05:32:00", + "2024-01-04T05:33:00", + "2024-01-04T05:34:00", + "2024-01-04T05:35:00", + "2024-01-04T05:36:00", + "2024-01-04T05:37:00", + "2024-01-04T05:38:00", + "2024-01-04T05:39:00", + "2024-01-04T05:40:00", + "2024-01-04T05:41:00", + "2024-01-04T05:42:00", + "2024-01-04T05:43:00", + "2024-01-04T05:44:00", + "2024-01-04T05:45:00", + "2024-01-04T05:46:00", + "2024-01-04T05:47:00", + "2024-01-04T05:48:00", + "2024-01-04T05:49:00", + "2024-01-04T05:50:00", + "2024-01-04T05:51:00", + "2024-01-04T05:52:00", + "2024-01-04T05:53:00", + "2024-01-04T05:54:00", + "2024-01-04T05:55:00", + "2024-01-04T05:56:00", + "2024-01-04T05:57:00", + "2024-01-04T05:58:00", + "2024-01-04T05:59:00", + "2024-01-04T06:00:00", + "2024-01-04T06:01:00", + "2024-01-04T06:02:00", + "2024-01-04T06:03:00", + "2024-01-04T06:04:00", + "2024-01-04T06:05:00", + "2024-01-04T06:06:00", + "2024-01-04T06:07:00", + "2024-01-04T06:08:00", + "2024-01-04T06:09:00", + "2024-01-04T06:10:00", + "2024-01-04T06:11:00", + "2024-01-04T06:12:00", + "2024-01-04T06:13:00", + "2024-01-04T06:14:00", + "2024-01-04T06:15:00", + "2024-01-04T06:16:00", + "2024-01-04T06:17:00", + "2024-01-04T06:18:00", + "2024-01-04T06:19:00", + "2024-01-04T06:20:00", + "2024-01-04T06:21:00", + "2024-01-04T06:22:00", + "2024-01-04T06:23:00", + "2024-01-04T06:24:00", + "2024-01-04T06:25:00", + "2024-01-04T06:26:00", + "2024-01-04T06:27:00", + "2024-01-04T06:28:00", + "2024-01-04T06:29:00", + "2024-01-04T06:30:00", + "2024-01-04T06:31:00", + "2024-01-04T06:32:00", + "2024-01-04T06:33:00", + "2024-01-04T06:34:00", + "2024-01-04T06:35:00", + "2024-01-04T06:36:00", + "2024-01-04T06:37:00", + "2024-01-04T06:38:00", + "2024-01-04T06:39:00", + "2024-01-04T06:40:00", + "2024-01-04T06:41:00", + "2024-01-04T06:42:00", + "2024-01-04T06:43:00", + "2024-01-04T06:44:00", + "2024-01-04T06:45:00", + "2024-01-04T06:46:00", + "2024-01-04T06:47:00", + "2024-01-04T06:48:00", + "2024-01-04T06:49:00", + "2024-01-04T06:50:00", + "2024-01-04T06:51:00", + "2024-01-04T06:52:00", + "2024-01-04T06:53:00", + "2024-01-04T06:54:00", + "2024-01-04T06:55:00", + "2024-01-04T06:56:00", + "2024-01-04T06:57:00", + "2024-01-04T06:58:00", + "2024-01-04T06:59:00", + "2024-01-04T07:00:00", + "2024-01-04T07:01:00", + "2024-01-04T07:02:00", + "2024-01-04T07:03:00", + "2024-01-04T07:04:00", + "2024-01-04T07:05:00", + "2024-01-04T07:06:00", + "2024-01-04T07:07:00", + "2024-01-04T07:08:00", + "2024-01-04T07:09:00", + "2024-01-04T07:10:00", + "2024-01-04T07:11:00", + "2024-01-04T07:12:00", + "2024-01-04T07:13:00", + "2024-01-04T07:14:00", + "2024-01-04T07:15:00", + "2024-01-04T07:16:00", + "2024-01-04T07:17:00", + "2024-01-04T07:18:00", + "2024-01-04T07:19:00", + "2024-01-04T07:20:00", + "2024-01-04T07:21:00", + "2024-01-04T07:22:00", + "2024-01-04T07:23:00", + "2024-01-04T07:24:00", + "2024-01-04T07:25:00", + "2024-01-04T07:26:00", + "2024-01-04T07:27:00", + "2024-01-04T07:28:00", + "2024-01-04T07:29:00", + "2024-01-04T07:30:00", + "2024-01-04T07:31:00", + "2024-01-04T07:32:00", + "2024-01-04T07:33:00", + "2024-01-04T07:34:00", + "2024-01-04T07:35:00", + "2024-01-04T07:36:00", + "2024-01-04T07:37:00", + "2024-01-04T07:38:00", + "2024-01-04T07:39:00", + "2024-01-04T07:40:00", + "2024-01-04T07:41:00", + "2024-01-04T07:42:00", + "2024-01-04T07:43:00", + "2024-01-04T07:44:00", + "2024-01-04T07:45:00", + "2024-01-04T07:46:00", + "2024-01-04T07:47:00", + "2024-01-04T07:48:00", + "2024-01-04T07:49:00", + "2024-01-04T07:50:00", + "2024-01-04T07:51:00", + "2024-01-04T07:52:00", + "2024-01-04T07:53:00", + "2024-01-04T07:54:00", + "2024-01-04T07:55:00", + "2024-01-04T07:56:00", + "2024-01-04T07:57:00", + "2024-01-04T07:58:00", + "2024-01-04T07:59:00", + "2024-01-04T08:00:00", + "2024-01-04T08:01:00", + "2024-01-04T08:02:00", + "2024-01-04T08:03:00", + "2024-01-04T08:04:00", + "2024-01-04T08:05:00", + "2024-01-04T08:06:00", + "2024-01-04T08:07:00", + "2024-01-04T08:08:00", + "2024-01-04T08:09:00", + "2024-01-04T08:10:00", + "2024-01-04T08:11:00", + "2024-01-04T08:12:00", + "2024-01-04T08:13:00", + "2024-01-04T08:14:00", + "2024-01-04T08:15:00", + "2024-01-04T08:16:00", + "2024-01-04T08:17:00", + "2024-01-04T08:18:00", + "2024-01-04T08:19:00", + "2024-01-04T08:20:00", + "2024-01-04T08:21:00", + "2024-01-04T08:22:00", + "2024-01-04T08:23:00", + "2024-01-04T08:24:00", + "2024-01-04T08:25:00", + "2024-01-04T08:26:00", + "2024-01-04T08:27:00", + "2024-01-04T08:28:00", + "2024-01-04T08:29:00", + "2024-01-04T08:30:00", + "2024-01-04T08:31:00", + "2024-01-04T08:32:00", + "2024-01-04T08:33:00", + "2024-01-04T08:34:00", + "2024-01-04T08:35:00", + "2024-01-04T08:36:00", + "2024-01-04T08:37:00", + "2024-01-04T08:38:00", + "2024-01-04T08:39:00", + "2024-01-04T08:40:00", + "2024-01-04T08:41:00", + "2024-01-04T08:42:00", + "2024-01-04T08:43:00", + "2024-01-04T08:44:00", + "2024-01-04T08:45:00", + "2024-01-04T08:46:00", + "2024-01-04T08:47:00", + "2024-01-04T08:48:00", + "2024-01-04T08:49:00", + "2024-01-04T08:50:00", + "2024-01-04T08:51:00", + "2024-01-04T08:52:00", + "2024-01-04T08:53:00", + "2024-01-04T08:54:00", + "2024-01-04T08:55:00", + "2024-01-04T08:56:00", + "2024-01-04T08:57:00", + "2024-01-04T08:58:00", + "2024-01-04T08:59:00", + "2024-01-04T09:00:00", + "2024-01-04T09:01:00", + "2024-01-04T09:02:00", + "2024-01-04T09:03:00", + "2024-01-04T09:04:00", + "2024-01-04T09:05:00", + "2024-01-04T09:06:00", + "2024-01-04T09:07:00", + "2024-01-04T09:08:00", + "2024-01-04T09:09:00", + "2024-01-04T09:10:00", + "2024-01-04T09:11:00", + "2024-01-04T09:12:00", + "2024-01-04T09:13:00", + "2024-01-04T09:14:00", + "2024-01-04T09:15:00", + "2024-01-04T09:16:00", + "2024-01-04T09:17:00", + "2024-01-04T09:18:00", + "2024-01-04T09:19:00", + "2024-01-04T09:20:00", + "2024-01-04T09:21:00", + "2024-01-04T09:22:00", + "2024-01-04T09:23:00", + "2024-01-04T09:24:00", + "2024-01-04T09:25:00", + "2024-01-04T09:26:00", + "2024-01-04T09:27:00", + "2024-01-04T09:28:00", + "2024-01-04T09:29:00", + "2024-01-04T09:30:00", + "2024-01-04T09:31:00", + "2024-01-04T09:32:00", + "2024-01-04T09:33:00", + "2024-01-04T09:34:00", + "2024-01-04T09:35:00", + "2024-01-04T09:36:00", + "2024-01-04T09:37:00", + "2024-01-04T09:38:00", + "2024-01-04T09:39:00", + "2024-01-04T09:40:00", + "2024-01-04T09:41:00", + "2024-01-04T09:42:00", + "2024-01-04T09:43:00", + "2024-01-04T09:44:00", + "2024-01-04T09:45:00", + "2024-01-04T09:46:00", + "2024-01-04T09:47:00", + "2024-01-04T09:48:00", + "2024-01-04T09:49:00", + "2024-01-04T09:50:00", + "2024-01-04T09:51:00", + "2024-01-04T09:52:00", + "2024-01-04T09:53:00", + "2024-01-04T09:54:00", + "2024-01-04T09:55:00", + "2024-01-04T09:56:00", + "2024-01-04T09:57:00", + "2024-01-04T09:58:00", + "2024-01-04T09:59:00", + "2024-01-04T10:00:00", + "2024-01-04T10:01:00", + "2024-01-04T10:02:00", + "2024-01-04T10:03:00", + "2024-01-04T10:04:00", + "2024-01-04T10:05:00", + "2024-01-04T10:06:00", + "2024-01-04T10:07:00", + "2024-01-04T10:08:00", + "2024-01-04T10:09:00", + "2024-01-04T10:10:00", + "2024-01-04T10:11:00", + "2024-01-04T10:12:00", + "2024-01-04T10:13:00", + "2024-01-04T10:14:00", + "2024-01-04T10:15:00", + "2024-01-04T10:16:00", + "2024-01-04T10:17:00", + "2024-01-04T10:18:00", + "2024-01-04T10:19:00", + "2024-01-04T10:20:00", + "2024-01-04T10:21:00", + "2024-01-04T10:22:00", + "2024-01-04T10:23:00", + "2024-01-04T10:24:00", + "2024-01-04T10:25:00", + "2024-01-04T10:26:00", + "2024-01-04T10:27:00", + "2024-01-04T10:28:00", + "2024-01-04T10:29:00", + "2024-01-04T10:30:00", + "2024-01-04T10:31:00", + "2024-01-04T10:32:00", + "2024-01-04T10:33:00", + "2024-01-04T10:34:00", + "2024-01-04T10:35:00", + "2024-01-04T10:36:00", + "2024-01-04T10:37:00", + "2024-01-04T10:38:00", + "2024-01-04T10:39:00", + "2024-01-04T10:40:00", + "2024-01-04T10:41:00", + "2024-01-04T10:42:00", + "2024-01-04T10:43:00", + "2024-01-04T10:44:00", + "2024-01-04T10:45:00", + "2024-01-04T10:46:00", + "2024-01-04T10:47:00", + "2024-01-04T10:48:00", + "2024-01-04T10:49:00", + "2024-01-04T10:50:00", + "2024-01-04T10:51:00", + "2024-01-04T10:52:00", + "2024-01-04T10:53:00", + "2024-01-04T10:54:00", + "2024-01-04T10:55:00", + "2024-01-04T10:56:00", + "2024-01-04T10:57:00", + "2024-01-04T10:58:00", + "2024-01-04T10:59:00", + "2024-01-04T11:00:00", + "2024-01-04T11:01:00", + "2024-01-04T11:02:00", + "2024-01-04T11:03:00", + "2024-01-04T11:04:00", + "2024-01-04T11:05:00", + "2024-01-04T11:06:00", + "2024-01-04T11:07:00", + "2024-01-04T11:08:00", + "2024-01-04T11:09:00", + "2024-01-04T11:10:00", + "2024-01-04T11:11:00", + "2024-01-04T11:12:00", + "2024-01-04T11:13:00", + "2024-01-04T11:14:00", + "2024-01-04T11:15:00", + "2024-01-04T11:16:00", + "2024-01-04T11:17:00", + "2024-01-04T11:18:00", + "2024-01-04T11:19:00", + "2024-01-04T11:20:00", + "2024-01-04T11:21:00", + "2024-01-04T11:22:00", + "2024-01-04T11:23:00", + "2024-01-04T11:24:00", + "2024-01-04T11:25:00", + "2024-01-04T11:26:00", + "2024-01-04T11:27:00", + "2024-01-04T11:28:00", + "2024-01-04T11:29:00", + "2024-01-04T11:30:00", + "2024-01-04T11:31:00", + "2024-01-04T11:32:00", + "2024-01-04T11:33:00", + "2024-01-04T11:34:00", + "2024-01-04T11:35:00", + "2024-01-04T11:36:00", + "2024-01-04T11:37:00", + "2024-01-04T11:38:00", + "2024-01-04T11:39:00", + "2024-01-04T11:40:00", + "2024-01-04T11:41:00", + "2024-01-04T11:42:00", + "2024-01-04T11:43:00", + "2024-01-04T11:44:00", + "2024-01-04T11:45:00", + "2024-01-04T11:46:00", + "2024-01-04T11:47:00", + "2024-01-04T11:48:00", + "2024-01-04T11:49:00", + "2024-01-04T11:50:00", + "2024-01-04T11:51:00", + "2024-01-04T11:52:00", + "2024-01-04T11:53:00", + "2024-01-04T11:54:00", + "2024-01-04T11:55:00", + "2024-01-04T11:56:00", + "2024-01-04T11:57:00", + "2024-01-04T11:58:00", + "2024-01-04T11:59:00", + "2024-01-04T12:00:00", + "2024-01-04T12:01:00", + "2024-01-04T12:02:00", + "2024-01-04T12:03:00", + "2024-01-04T12:04:00", + "2024-01-04T12:05:00", + "2024-01-04T12:06:00", + "2024-01-04T12:07:00", + "2024-01-04T12:08:00", + "2024-01-04T12:09:00", + "2024-01-04T12:10:00", + "2024-01-04T12:11:00", + "2024-01-04T12:12:00", + "2024-01-04T12:13:00", + "2024-01-04T12:14:00", + "2024-01-04T12:15:00", + "2024-01-04T12:16:00", + "2024-01-04T12:17:00", + "2024-01-04T12:18:00", + "2024-01-04T12:19:00", + "2024-01-04T12:20:00", + "2024-01-04T12:21:00", + "2024-01-04T12:22:00", + "2024-01-04T12:23:00", + "2024-01-04T12:24:00", + "2024-01-04T12:25:00", + "2024-01-04T12:26:00", + "2024-01-04T12:27:00", + "2024-01-04T12:28:00", + "2024-01-04T12:29:00", + "2024-01-04T12:30:00", + "2024-01-04T12:31:00", + "2024-01-04T12:32:00", + "2024-01-04T12:33:00", + "2024-01-04T12:34:00", + "2024-01-04T12:35:00", + "2024-01-04T12:36:00", + "2024-01-04T12:37:00", + "2024-01-04T12:38:00", + "2024-01-04T12:39:00", + "2024-01-04T12:40:00", + "2024-01-04T12:41:00", + "2024-01-04T12:42:00", + "2024-01-04T12:43:00", + "2024-01-04T12:44:00", + "2024-01-04T12:45:00", + "2024-01-04T12:46:00", + "2024-01-04T12:47:00", + "2024-01-04T12:48:00", + "2024-01-04T12:49:00", + "2024-01-04T12:50:00", + "2024-01-04T12:51:00", + "2024-01-04T12:52:00", + "2024-01-04T12:53:00", + "2024-01-04T12:54:00", + "2024-01-04T12:55:00", + "2024-01-04T12:56:00", + "2024-01-04T12:57:00", + "2024-01-04T12:58:00", + "2024-01-04T12:59:00", + "2024-01-04T13:00:00", + "2024-01-04T13:01:00", + "2024-01-04T13:02:00", + "2024-01-04T13:03:00", + "2024-01-04T13:04:00", + "2024-01-04T13:05:00", + "2024-01-04T13:06:00", + "2024-01-04T13:07:00", + "2024-01-04T13:08:00", + "2024-01-04T13:09:00", + "2024-01-04T13:10:00", + "2024-01-04T13:11:00", + "2024-01-04T13:12:00", + "2024-01-04T13:13:00", + "2024-01-04T13:14:00", + "2024-01-04T13:15:00", + "2024-01-04T13:16:00", + "2024-01-04T13:17:00", + "2024-01-04T13:18:00", + "2024-01-04T13:19:00", + "2024-01-04T13:20:00", + "2024-01-04T13:21:00", + "2024-01-04T13:22:00", + "2024-01-04T13:23:00", + "2024-01-04T13:24:00", + "2024-01-04T13:25:00", + "2024-01-04T13:26:00", + "2024-01-04T13:27:00", + "2024-01-04T13:28:00", + "2024-01-04T13:29:00", + "2024-01-04T13:30:00", + "2024-01-04T13:31:00", + "2024-01-04T13:32:00", + "2024-01-04T13:33:00", + "2024-01-04T13:34:00", + "2024-01-04T13:35:00", + "2024-01-04T13:36:00", + "2024-01-04T13:37:00", + "2024-01-04T13:38:00", + "2024-01-04T13:39:00", + "2024-01-04T13:40:00", + "2024-01-04T13:41:00", + "2024-01-04T13:42:00", + "2024-01-04T13:43:00", + "2024-01-04T13:44:00", + "2024-01-04T13:45:00", + "2024-01-04T13:46:00", + "2024-01-04T13:47:00", + "2024-01-04T13:48:00", + "2024-01-04T13:49:00", + "2024-01-04T13:50:00", + "2024-01-04T13:51:00", + "2024-01-04T13:52:00", + "2024-01-04T13:53:00", + "2024-01-04T13:54:00", + "2024-01-04T13:55:00", + "2024-01-04T13:56:00", + "2024-01-04T13:57:00", + "2024-01-04T13:58:00", + "2024-01-04T13:59:00", + "2024-01-04T14:00:00", + "2024-01-04T14:01:00", + "2024-01-04T14:02:00", + "2024-01-04T14:03:00", + "2024-01-04T14:04:00", + "2024-01-04T14:05:00", + "2024-01-04T14:06:00", + "2024-01-04T14:07:00", + "2024-01-04T14:08:00", + "2024-01-04T14:09:00", + "2024-01-04T14:10:00", + "2024-01-04T14:11:00", + "2024-01-04T14:12:00", + "2024-01-04T14:13:00", + "2024-01-04T14:14:00", + "2024-01-04T14:15:00", + "2024-01-04T14:16:00", + "2024-01-04T14:17:00", + "2024-01-04T14:18:00", + "2024-01-04T14:19:00", + "2024-01-04T14:20:00", + "2024-01-04T14:21:00", + "2024-01-04T14:22:00", + "2024-01-04T14:23:00", + "2024-01-04T14:24:00", + "2024-01-04T14:25:00", + "2024-01-04T14:26:00", + "2024-01-04T14:27:00", + "2024-01-04T14:28:00", + "2024-01-04T14:29:00", + "2024-01-04T14:30:00", + "2024-01-04T14:31:00", + "2024-01-04T14:32:00", + "2024-01-04T14:33:00", + "2024-01-04T14:34:00", + "2024-01-04T14:35:00", + "2024-01-04T14:36:00", + "2024-01-04T14:37:00", + "2024-01-04T14:38:00", + "2024-01-04T14:39:00", + "2024-01-04T14:40:00", + "2024-01-04T14:41:00", + "2024-01-04T14:42:00", + "2024-01-04T14:43:00", + "2024-01-04T14:44:00", + "2024-01-04T14:45:00", + "2024-01-04T14:46:00", + "2024-01-04T14:47:00", + "2024-01-04T14:48:00", + "2024-01-04T14:49:00", + "2024-01-04T14:50:00", + "2024-01-04T14:51:00", + "2024-01-04T14:52:00", + "2024-01-04T14:53:00", + "2024-01-04T14:54:00", + "2024-01-04T14:55:00", + "2024-01-04T14:56:00", + "2024-01-04T14:57:00", + "2024-01-04T14:58:00", + "2024-01-04T14:59:00", + "2024-01-04T15:00:00", + "2024-01-04T15:01:00", + "2024-01-04T15:02:00", + "2024-01-04T15:03:00", + "2024-01-04T15:04:00", + "2024-01-04T15:05:00", + "2024-01-04T15:06:00", + "2024-01-04T15:07:00", + "2024-01-04T15:08:00", + "2024-01-04T15:09:00", + "2024-01-04T15:10:00", + "2024-01-04T15:11:00", + "2024-01-04T15:12:00", + "2024-01-04T15:13:00", + "2024-01-04T15:14:00", + "2024-01-04T15:15:00", + "2024-01-04T15:16:00", + "2024-01-04T15:17:00", + "2024-01-04T15:18:00", + "2024-01-04T15:19:00", + "2024-01-04T15:20:00", + "2024-01-04T15:21:00", + "2024-01-04T15:22:00", + "2024-01-04T15:23:00", + "2024-01-04T15:24:00", + "2024-01-04T15:25:00", + "2024-01-04T15:26:00", + "2024-01-04T15:27:00", + "2024-01-04T15:28:00", + "2024-01-04T15:29:00", + "2024-01-04T15:30:00", + "2024-01-04T15:31:00", + "2024-01-04T15:32:00", + "2024-01-04T15:33:00", + "2024-01-04T15:34:00", + "2024-01-04T15:35:00", + "2024-01-04T15:36:00", + "2024-01-04T15:37:00", + "2024-01-04T15:38:00", + "2024-01-04T15:39:00", + "2024-01-04T15:40:00", + "2024-01-04T15:41:00", + "2024-01-04T15:42:00", + "2024-01-04T15:43:00", + "2024-01-04T15:44:00", + "2024-01-04T15:45:00", + "2024-01-04T15:46:00", + "2024-01-04T15:47:00", + "2024-01-04T15:48:00", + "2024-01-04T15:49:00", + "2024-01-04T15:50:00", + "2024-01-04T15:51:00", + "2024-01-04T15:52:00", + "2024-01-04T15:53:00", + "2024-01-04T15:54:00", + "2024-01-04T15:55:00", + "2024-01-04T15:56:00", + "2024-01-04T15:57:00", + "2024-01-04T15:58:00", + "2024-01-04T15:59:00", + "2024-01-04T16:00:00", + "2024-01-04T16:01:00", + "2024-01-04T16:02:00", + "2024-01-04T16:03:00", + "2024-01-04T16:04:00", + "2024-01-04T16:05:00", + "2024-01-04T16:06:00", + "2024-01-04T16:07:00", + "2024-01-04T16:08:00", + "2024-01-04T16:09:00", + "2024-01-04T16:10:00", + "2024-01-04T16:11:00", + "2024-01-04T16:12:00", + "2024-01-04T16:13:00", + "2024-01-04T16:14:00", + "2024-01-04T16:15:00", + "2024-01-04T16:16:00", + "2024-01-04T16:17:00", + "2024-01-04T16:18:00", + "2024-01-04T16:19:00", + "2024-01-04T16:20:00", + "2024-01-04T16:21:00", + "2024-01-04T16:22:00", + "2024-01-04T16:23:00", + "2024-01-04T16:24:00", + "2024-01-04T16:25:00", + "2024-01-04T16:26:00", + "2024-01-04T16:27:00", + "2024-01-04T16:28:00", + "2024-01-04T16:29:00", + "2024-01-04T16:30:00", + "2024-01-04T16:31:00", + "2024-01-04T16:32:00", + "2024-01-04T16:33:00", + "2024-01-04T16:34:00", + "2024-01-04T16:35:00", + "2024-01-04T16:36:00", + "2024-01-04T16:37:00", + "2024-01-04T16:38:00", + "2024-01-04T16:39:00", + "2024-01-04T16:40:00", + "2024-01-04T16:41:00", + "2024-01-04T16:42:00", + "2024-01-04T16:43:00", + "2024-01-04T16:44:00", + "2024-01-04T16:45:00", + "2024-01-04T16:46:00", + "2024-01-04T16:47:00", + "2024-01-04T16:48:00", + "2024-01-04T16:49:00", + "2024-01-04T16:50:00", + "2024-01-04T16:51:00", + "2024-01-04T16:52:00", + "2024-01-04T16:53:00", + "2024-01-04T16:54:00", + "2024-01-04T16:55:00", + "2024-01-04T16:56:00", + "2024-01-04T16:57:00", + "2024-01-04T16:58:00", + "2024-01-04T16:59:00", + "2024-01-04T17:00:00", + "2024-01-04T17:01:00", + "2024-01-04T17:02:00", + "2024-01-04T17:03:00", + "2024-01-04T17:04:00", + "2024-01-04T17:05:00", + "2024-01-04T17:06:00", + "2024-01-04T17:07:00", + "2024-01-04T17:08:00", + "2024-01-04T17:09:00", + "2024-01-04T17:10:00", + "2024-01-04T17:11:00", + "2024-01-04T17:12:00", + "2024-01-04T17:13:00", + "2024-01-04T17:14:00", + "2024-01-04T17:15:00", + "2024-01-04T17:16:00", + "2024-01-04T17:17:00", + "2024-01-04T17:18:00", + "2024-01-04T17:19:00", + "2024-01-04T17:20:00", + "2024-01-04T17:21:00", + "2024-01-04T17:22:00", + "2024-01-04T17:23:00", + "2024-01-04T17:24:00", + "2024-01-04T17:25:00", + "2024-01-04T17:26:00", + "2024-01-04T17:27:00", + "2024-01-04T17:28:00", + "2024-01-04T17:29:00", + "2024-01-04T17:30:00", + "2024-01-04T17:31:00", + "2024-01-04T17:32:00", + "2024-01-04T17:33:00", + "2024-01-04T17:34:00", + "2024-01-04T17:35:00", + "2024-01-04T17:36:00", + "2024-01-04T17:37:00", + "2024-01-04T17:38:00", + "2024-01-04T17:39:00", + "2024-01-04T17:40:00", + "2024-01-04T17:41:00", + "2024-01-04T17:42:00", + "2024-01-04T17:43:00", + "2024-01-04T17:44:00", + "2024-01-04T17:45:00", + "2024-01-04T17:46:00", + "2024-01-04T17:47:00", + "2024-01-04T17:48:00", + "2024-01-04T17:49:00", + "2024-01-04T17:50:00", + "2024-01-04T17:51:00", + "2024-01-04T17:52:00", + "2024-01-04T17:53:00", + "2024-01-04T17:54:00", + "2024-01-04T17:55:00", + "2024-01-04T17:56:00", + "2024-01-04T17:57:00", + "2024-01-04T17:58:00", + "2024-01-04T17:59:00", + "2024-01-04T18:00:00", + "2024-01-04T18:01:00", + "2024-01-04T18:02:00", + "2024-01-04T18:03:00", + "2024-01-04T18:04:00", + "2024-01-04T18:05:00", + "2024-01-04T18:06:00", + "2024-01-04T18:07:00", + "2024-01-04T18:08:00", + "2024-01-04T18:09:00", + "2024-01-04T18:10:00", + "2024-01-04T18:11:00", + "2024-01-04T18:12:00", + "2024-01-04T18:13:00", + "2024-01-04T18:14:00", + "2024-01-04T18:15:00", + "2024-01-04T18:16:00", + "2024-01-04T18:17:00", + "2024-01-04T18:18:00", + "2024-01-04T18:19:00", + "2024-01-04T18:20:00", + "2024-01-04T18:21:00", + "2024-01-04T18:22:00", + "2024-01-04T18:23:00", + "2024-01-04T18:24:00", + "2024-01-04T18:25:00", + "2024-01-04T18:26:00", + "2024-01-04T18:27:00", + "2024-01-04T18:28:00", + "2024-01-04T18:29:00", + "2024-01-04T18:30:00", + "2024-01-04T18:31:00", + "2024-01-04T18:32:00", + "2024-01-04T18:33:00", + "2024-01-04T18:34:00", + "2024-01-04T18:35:00", + "2024-01-04T18:36:00", + "2024-01-04T18:37:00", + "2024-01-04T18:38:00", + "2024-01-04T18:39:00", + "2024-01-04T18:40:00", + "2024-01-04T18:41:00", + "2024-01-04T18:42:00", + "2024-01-04T18:43:00", + "2024-01-04T18:44:00", + "2024-01-04T18:45:00", + "2024-01-04T18:46:00", + "2024-01-04T18:47:00", + "2024-01-04T18:48:00", + "2024-01-04T18:49:00", + "2024-01-04T18:50:00", + "2024-01-04T18:51:00", + "2024-01-04T18:52:00", + "2024-01-04T18:53:00", + "2024-01-04T18:54:00", + "2024-01-04T18:55:00", + "2024-01-04T18:56:00", + "2024-01-04T18:57:00", + "2024-01-04T18:58:00", + "2024-01-04T18:59:00", + "2024-01-04T19:00:00", + "2024-01-04T19:01:00", + "2024-01-04T19:02:00", + "2024-01-04T19:03:00", + "2024-01-04T19:04:00", + "2024-01-04T19:05:00", + "2024-01-04T19:06:00", + "2024-01-04T19:07:00", + "2024-01-04T19:08:00", + "2024-01-04T19:09:00", + "2024-01-04T19:10:00", + "2024-01-04T19:11:00", + "2024-01-04T19:12:00", + "2024-01-04T19:13:00", + "2024-01-04T19:14:00", + "2024-01-04T19:15:00", + "2024-01-04T19:16:00", + "2024-01-04T19:17:00", + "2024-01-04T19:18:00", + "2024-01-04T19:19:00", + "2024-01-04T19:20:00", + "2024-01-04T19:21:00", + "2024-01-04T19:22:00", + "2024-01-04T19:23:00", + "2024-01-04T19:24:00", + "2024-01-04T19:25:00", + "2024-01-04T19:26:00", + "2024-01-04T19:27:00", + "2024-01-04T19:28:00", + "2024-01-04T19:29:00", + "2024-01-04T19:30:00", + "2024-01-04T19:31:00", + "2024-01-04T19:32:00", + "2024-01-04T19:33:00", + "2024-01-04T19:34:00", + "2024-01-04T19:35:00", + "2024-01-04T19:36:00", + "2024-01-04T19:37:00", + "2024-01-04T19:38:00", + "2024-01-04T19:39:00", + "2024-01-04T19:40:00", + "2024-01-04T19:41:00", + "2024-01-04T19:42:00", + "2024-01-04T19:43:00", + "2024-01-04T19:44:00", + "2024-01-04T19:45:00", + "2024-01-04T19:46:00", + "2024-01-04T19:47:00", + "2024-01-04T19:48:00", + "2024-01-04T19:49:00", + "2024-01-04T19:50:00", + "2024-01-04T19:51:00", + "2024-01-04T19:52:00", + "2024-01-04T19:53:00", + "2024-01-04T19:54:00", + "2024-01-04T19:55:00", + "2024-01-04T19:56:00", + "2024-01-04T19:57:00", + "2024-01-04T19:58:00", + "2024-01-04T19:59:00", + "2024-01-04T20:00:00", + "2024-01-04T20:01:00", + "2024-01-04T20:02:00", + "2024-01-04T20:03:00", + "2024-01-04T20:04:00", + "2024-01-04T20:05:00", + "2024-01-04T20:06:00", + "2024-01-04T20:07:00", + "2024-01-04T20:08:00", + "2024-01-04T20:09:00", + "2024-01-04T20:10:00", + "2024-01-04T20:11:00", + "2024-01-04T20:12:00", + "2024-01-04T20:13:00", + "2024-01-04T20:14:00", + "2024-01-04T20:15:00", + "2024-01-04T20:16:00", + "2024-01-04T20:17:00", + "2024-01-04T20:18:00", + "2024-01-04T20:19:00", + "2024-01-04T20:20:00", + "2024-01-04T20:21:00", + "2024-01-04T20:22:00", + "2024-01-04T20:23:00", + "2024-01-04T20:24:00", + "2024-01-04T20:25:00", + "2024-01-04T20:26:00", + "2024-01-04T20:27:00", + "2024-01-04T20:28:00", + "2024-01-04T20:29:00", + "2024-01-04T20:30:00", + "2024-01-04T20:31:00", + "2024-01-04T20:32:00", + "2024-01-04T20:33:00", + "2024-01-04T20:34:00", + "2024-01-04T20:35:00", + "2024-01-04T20:36:00", + "2024-01-04T20:37:00", + "2024-01-04T20:38:00", + "2024-01-04T20:39:00", + "2024-01-04T20:40:00", + "2024-01-04T20:41:00", + "2024-01-04T20:42:00", + "2024-01-04T20:43:00", + "2024-01-04T20:44:00", + "2024-01-04T20:45:00", + "2024-01-04T20:46:00", + "2024-01-04T20:47:00", + "2024-01-04T20:48:00", + "2024-01-04T20:49:00", + "2024-01-04T20:50:00", + "2024-01-04T20:51:00", + "2024-01-04T20:52:00", + "2024-01-04T20:53:00", + "2024-01-04T20:54:00", + "2024-01-04T20:55:00", + "2024-01-04T20:56:00", + "2024-01-04T20:57:00", + "2024-01-04T20:58:00", + "2024-01-04T20:59:00", + "2024-01-04T21:00:00", + "2024-01-04T21:01:00", + "2024-01-04T21:02:00", + "2024-01-04T21:03:00", + "2024-01-04T21:04:00", + "2024-01-04T21:05:00", + "2024-01-04T21:06:00", + "2024-01-04T21:07:00", + "2024-01-04T21:08:00", + "2024-01-04T21:09:00", + "2024-01-04T21:10:00", + "2024-01-04T21:11:00", + "2024-01-04T21:12:00", + "2024-01-04T21:13:00", + "2024-01-04T21:14:00", + "2024-01-04T21:15:00", + "2024-01-04T21:16:00", + "2024-01-04T21:17:00", + "2024-01-04T21:18:00", + "2024-01-04T21:19:00", + "2024-01-04T21:20:00", + "2024-01-04T21:21:00", + "2024-01-04T21:22:00", + "2024-01-04T21:23:00", + "2024-01-04T21:24:00", + "2024-01-04T21:25:00", + "2024-01-04T21:26:00", + "2024-01-04T21:27:00", + "2024-01-04T21:28:00", + "2024-01-04T21:29:00", + "2024-01-04T21:30:00", + "2024-01-04T21:31:00", + "2024-01-04T21:32:00", + "2024-01-04T21:33:00", + "2024-01-04T21:34:00", + "2024-01-04T21:35:00", + "2024-01-04T21:36:00", + "2024-01-04T21:37:00", + "2024-01-04T21:38:00", + "2024-01-04T21:39:00", + "2024-01-04T21:40:00", + "2024-01-04T21:41:00", + "2024-01-04T21:42:00", + "2024-01-04T21:43:00", + "2024-01-04T21:44:00", + "2024-01-04T21:45:00", + "2024-01-04T21:46:00", + "2024-01-04T21:47:00", + "2024-01-04T21:48:00", + "2024-01-04T21:49:00", + "2024-01-04T21:50:00", + "2024-01-04T21:51:00", + "2024-01-04T21:52:00", + "2024-01-04T21:53:00", + "2024-01-04T21:54:00", + "2024-01-04T21:55:00", + "2024-01-04T21:56:00", + "2024-01-04T21:57:00", + "2024-01-04T21:58:00", + "2024-01-04T21:59:00", + "2024-01-04T22:00:00", + "2024-01-04T22:01:00", + "2024-01-04T22:02:00", + "2024-01-04T22:03:00", + "2024-01-04T22:04:00", + "2024-01-04T22:05:00", + "2024-01-04T22:06:00", + "2024-01-04T22:07:00", + "2024-01-04T22:08:00", + "2024-01-04T22:09:00", + "2024-01-04T22:10:00", + "2024-01-04T22:11:00", + "2024-01-04T22:12:00", + "2024-01-04T22:13:00", + "2024-01-04T22:14:00", + "2024-01-04T22:15:00", + "2024-01-04T22:16:00", + "2024-01-04T22:17:00", + "2024-01-04T22:18:00", + "2024-01-04T22:19:00", + "2024-01-04T22:20:00", + "2024-01-04T22:21:00", + "2024-01-04T22:22:00", + "2024-01-04T22:23:00", + "2024-01-04T22:24:00", + "2024-01-04T22:25:00", + "2024-01-04T22:26:00", + "2024-01-04T22:27:00", + "2024-01-04T22:28:00", + "2024-01-04T22:29:00", + "2024-01-04T22:30:00", + "2024-01-04T22:31:00", + "2024-01-04T22:32:00", + "2024-01-04T22:33:00", + "2024-01-04T22:34:00", + "2024-01-04T22:35:00", + "2024-01-04T22:36:00", + "2024-01-04T22:37:00", + "2024-01-04T22:38:00", + "2024-01-04T22:39:00", + "2024-01-04T22:40:00", + "2024-01-04T22:41:00", + "2024-01-04T22:42:00", + "2024-01-04T22:43:00", + "2024-01-04T22:44:00", + "2024-01-04T22:45:00", + "2024-01-04T22:46:00", + "2024-01-04T22:47:00", + "2024-01-04T22:48:00", + "2024-01-04T22:49:00", + "2024-01-04T22:50:00", + "2024-01-04T22:51:00", + "2024-01-04T22:52:00", + "2024-01-04T22:53:00", + "2024-01-04T22:54:00", + "2024-01-04T22:55:00", + "2024-01-04T22:56:00", + "2024-01-04T22:57:00", + "2024-01-04T22:58:00", + "2024-01-04T22:59:00", + "2024-01-04T23:00:00", + "2024-01-04T23:01:00", + "2024-01-04T23:02:00", + "2024-01-04T23:03:00", + "2024-01-04T23:04:00", + "2024-01-04T23:05:00", + "2024-01-04T23:06:00", + "2024-01-04T23:07:00", + "2024-01-04T23:08:00", + "2024-01-04T23:09:00", + "2024-01-04T23:10:00", + "2024-01-04T23:11:00", + "2024-01-04T23:12:00", + "2024-01-04T23:13:00", + "2024-01-04T23:14:00", + "2024-01-04T23:15:00", + "2024-01-04T23:16:00", + "2024-01-04T23:17:00", + "2024-01-04T23:18:00", + "2024-01-04T23:19:00", + "2024-01-04T23:20:00", + "2024-01-04T23:21:00", + "2024-01-04T23:22:00", + "2024-01-04T23:23:00", + "2024-01-04T23:24:00", + "2024-01-04T23:25:00", + "2024-01-04T23:26:00", + "2024-01-04T23:27:00", + "2024-01-04T23:28:00", + "2024-01-04T23:29:00", + "2024-01-04T23:30:00", + "2024-01-04T23:31:00", + "2024-01-04T23:32:00", + "2024-01-04T23:33:00", + "2024-01-04T23:34:00", + "2024-01-04T23:35:00", + "2024-01-04T23:36:00", + "2024-01-04T23:37:00", + "2024-01-04T23:38:00", + "2024-01-04T23:39:00", + "2024-01-04T23:40:00", + "2024-01-04T23:41:00", + "2024-01-04T23:42:00", + "2024-01-04T23:43:00", + "2024-01-04T23:44:00", + "2024-01-04T23:45:00", + "2024-01-04T23:46:00", + "2024-01-04T23:47:00", + "2024-01-04T23:48:00", + "2024-01-04T23:49:00", + "2024-01-04T23:50:00", + "2024-01-04T23:51:00", + "2024-01-04T23:52:00", + "2024-01-04T23:53:00", + "2024-01-04T23:54:00", + "2024-01-04T23:55:00", + "2024-01-04T23:56:00", + "2024-01-04T23:57:00", + "2024-01-04T23:58:00", + "2024-01-04T23:59:00", + "2024-01-05T00:00:00", + "2024-01-05T00:01:00", + "2024-01-05T00:02:00", + "2024-01-05T00:03:00", + "2024-01-05T00:04:00", + "2024-01-05T00:05:00", + "2024-01-05T00:06:00", + "2024-01-05T00:07:00", + "2024-01-05T00:08:00", + "2024-01-05T00:09:00", + "2024-01-05T00:10:00", + "2024-01-05T00:11:00", + "2024-01-05T00:12:00", + "2024-01-05T00:13:00", + "2024-01-05T00:14:00", + "2024-01-05T00:15:00", + "2024-01-05T00:16:00", + "2024-01-05T00:17:00", + "2024-01-05T00:18:00", + "2024-01-05T00:19:00", + "2024-01-05T00:20:00", + "2024-01-05T00:21:00", + "2024-01-05T00:22:00", + "2024-01-05T00:23:00", + "2024-01-05T00:24:00", + "2024-01-05T00:25:00", + "2024-01-05T00:26:00", + "2024-01-05T00:27:00", + "2024-01-05T00:28:00", + "2024-01-05T00:29:00", + "2024-01-05T00:30:00", + "2024-01-05T00:31:00", + "2024-01-05T00:32:00", + "2024-01-05T00:33:00", + "2024-01-05T00:34:00", + "2024-01-05T00:35:00", + "2024-01-05T00:36:00", + "2024-01-05T00:37:00", + "2024-01-05T00:38:00", + "2024-01-05T00:39:00", + "2024-01-05T00:40:00", + "2024-01-05T00:41:00", + "2024-01-05T00:42:00", + "2024-01-05T00:43:00", + "2024-01-05T00:44:00", + "2024-01-05T00:45:00", + "2024-01-05T00:46:00", + "2024-01-05T00:47:00", + "2024-01-05T00:48:00", + "2024-01-05T00:49:00", + "2024-01-05T00:50:00", + "2024-01-05T00:51:00", + "2024-01-05T00:52:00", + "2024-01-05T00:53:00", + "2024-01-05T00:54:00", + "2024-01-05T00:55:00", + "2024-01-05T00:56:00", + "2024-01-05T00:57:00", + "2024-01-05T00:58:00", + "2024-01-05T00:59:00", + "2024-01-05T01:00:00", + "2024-01-05T01:01:00", + "2024-01-05T01:02:00", + "2024-01-05T01:03:00", + "2024-01-05T01:04:00", + "2024-01-05T01:05:00", + "2024-01-05T01:06:00", + "2024-01-05T01:07:00", + "2024-01-05T01:08:00", + "2024-01-05T01:09:00", + "2024-01-05T01:10:00", + "2024-01-05T01:11:00", + "2024-01-05T01:12:00", + "2024-01-05T01:13:00", + "2024-01-05T01:14:00", + "2024-01-05T01:15:00", + "2024-01-05T01:16:00", + "2024-01-05T01:17:00", + "2024-01-05T01:18:00", + "2024-01-05T01:19:00", + "2024-01-05T01:20:00", + "2024-01-05T01:21:00", + "2024-01-05T01:22:00", + "2024-01-05T01:23:00", + "2024-01-05T01:24:00", + "2024-01-05T01:25:00", + "2024-01-05T01:26:00", + "2024-01-05T01:27:00", + "2024-01-05T01:28:00", + "2024-01-05T01:29:00", + "2024-01-05T01:30:00", + "2024-01-05T01:31:00", + "2024-01-05T01:32:00", + "2024-01-05T01:33:00", + "2024-01-05T01:34:00", + "2024-01-05T01:35:00", + "2024-01-05T01:36:00", + "2024-01-05T01:37:00", + "2024-01-05T01:38:00", + "2024-01-05T01:39:00", + "2024-01-05T01:40:00", + "2024-01-05T01:41:00", + "2024-01-05T01:42:00", + "2024-01-05T01:43:00", + "2024-01-05T01:44:00", + "2024-01-05T01:45:00", + "2024-01-05T01:46:00", + "2024-01-05T01:47:00", + "2024-01-05T01:48:00", + "2024-01-05T01:49:00", + "2024-01-05T01:50:00", + "2024-01-05T01:51:00", + "2024-01-05T01:52:00", + "2024-01-05T01:53:00", + "2024-01-05T01:54:00", + "2024-01-05T01:55:00", + "2024-01-05T01:56:00", + "2024-01-05T01:57:00", + "2024-01-05T01:58:00", + "2024-01-05T01:59:00", + "2024-01-05T02:00:00", + "2024-01-05T02:01:00", + "2024-01-05T02:02:00", + "2024-01-05T02:03:00", + "2024-01-05T02:04:00", + "2024-01-05T02:05:00", + "2024-01-05T02:06:00", + "2024-01-05T02:07:00", + "2024-01-05T02:08:00", + "2024-01-05T02:09:00", + "2024-01-05T02:10:00", + "2024-01-05T02:11:00", + "2024-01-05T02:12:00", + "2024-01-05T02:13:00", + "2024-01-05T02:14:00", + "2024-01-05T02:15:00", + "2024-01-05T02:16:00", + "2024-01-05T02:17:00", + "2024-01-05T02:18:00", + "2024-01-05T02:19:00", + "2024-01-05T02:20:00", + "2024-01-05T02:21:00", + "2024-01-05T02:22:00", + "2024-01-05T02:23:00", + "2024-01-05T02:24:00", + "2024-01-05T02:25:00", + "2024-01-05T02:26:00", + "2024-01-05T02:27:00", + "2024-01-05T02:28:00", + "2024-01-05T02:29:00", + "2024-01-05T02:30:00", + "2024-01-05T02:31:00", + "2024-01-05T02:32:00", + "2024-01-05T02:33:00", + "2024-01-05T02:34:00", + "2024-01-05T02:35:00", + "2024-01-05T02:36:00", + "2024-01-05T02:37:00", + "2024-01-05T02:38:00", + "2024-01-05T02:39:00", + "2024-01-05T02:40:00", + "2024-01-05T02:41:00", + "2024-01-05T02:42:00", + "2024-01-05T02:43:00", + "2024-01-05T02:44:00", + "2024-01-05T02:45:00", + "2024-01-05T02:46:00", + "2024-01-05T02:47:00", + "2024-01-05T02:48:00", + "2024-01-05T02:49:00", + "2024-01-05T02:50:00", + "2024-01-05T02:51:00", + "2024-01-05T02:52:00", + "2024-01-05T02:53:00", + "2024-01-05T02:54:00", + "2024-01-05T02:55:00", + "2024-01-05T02:56:00", + "2024-01-05T02:57:00", + "2024-01-05T02:58:00", + "2024-01-05T02:59:00", + "2024-01-05T03:00:00", + "2024-01-05T03:01:00", + "2024-01-05T03:02:00", + "2024-01-05T03:03:00", + "2024-01-05T03:04:00", + "2024-01-05T03:05:00", + "2024-01-05T03:06:00", + "2024-01-05T03:07:00", + "2024-01-05T03:08:00", + "2024-01-05T03:09:00", + "2024-01-05T03:10:00", + "2024-01-05T03:11:00", + "2024-01-05T03:12:00", + "2024-01-05T03:13:00", + "2024-01-05T03:14:00", + "2024-01-05T03:15:00", + "2024-01-05T03:16:00", + "2024-01-05T03:17:00", + "2024-01-05T03:18:00", + "2024-01-05T03:19:00", + "2024-01-05T03:20:00", + "2024-01-05T03:21:00", + "2024-01-05T03:22:00", + "2024-01-05T03:23:00", + "2024-01-05T03:24:00", + "2024-01-05T03:25:00", + "2024-01-05T03:26:00", + "2024-01-05T03:27:00", + "2024-01-05T03:28:00", + "2024-01-05T03:29:00", + "2024-01-05T03:30:00", + "2024-01-05T03:31:00", + "2024-01-05T03:32:00", + "2024-01-05T03:33:00", + "2024-01-05T03:34:00", + "2024-01-05T03:35:00", + "2024-01-05T03:36:00", + "2024-01-05T03:37:00", + "2024-01-05T03:38:00", + "2024-01-05T03:39:00", + "2024-01-05T03:40:00", + "2024-01-05T03:41:00", + "2024-01-05T03:42:00", + "2024-01-05T03:43:00", + "2024-01-05T03:44:00", + "2024-01-05T03:45:00", + "2024-01-05T03:46:00", + "2024-01-05T03:47:00", + "2024-01-05T03:48:00", + "2024-01-05T03:49:00", + "2024-01-05T03:50:00", + "2024-01-05T03:51:00", + "2024-01-05T03:52:00", + "2024-01-05T03:53:00", + "2024-01-05T03:54:00", + "2024-01-05T03:55:00", + "2024-01-05T03:56:00", + "2024-01-05T03:57:00", + "2024-01-05T03:58:00", + "2024-01-05T03:59:00", + "2024-01-05T04:00:00", + "2024-01-05T04:01:00", + "2024-01-05T04:02:00", + "2024-01-05T04:03:00", + "2024-01-05T04:04:00", + "2024-01-05T04:05:00", + "2024-01-05T04:06:00", + "2024-01-05T04:07:00", + "2024-01-05T04:08:00", + "2024-01-05T04:09:00", + "2024-01-05T04:10:00", + "2024-01-05T04:11:00", + "2024-01-05T04:12:00", + "2024-01-05T04:13:00", + "2024-01-05T04:14:00", + "2024-01-05T04:15:00", + "2024-01-05T04:16:00", + "2024-01-05T04:17:00", + "2024-01-05T04:18:00", + "2024-01-05T04:19:00", + "2024-01-05T04:20:00", + "2024-01-05T04:21:00", + "2024-01-05T04:22:00", + "2024-01-05T04:23:00", + "2024-01-05T04:24:00", + "2024-01-05T04:25:00", + "2024-01-05T04:26:00", + "2024-01-05T04:27:00", + "2024-01-05T04:28:00", + "2024-01-05T04:29:00", + "2024-01-05T04:30:00", + "2024-01-05T04:31:00", + "2024-01-05T04:32:00", + "2024-01-05T04:33:00", + "2024-01-05T04:34:00", + "2024-01-05T04:35:00", + "2024-01-05T04:36:00", + "2024-01-05T04:37:00", + "2024-01-05T04:38:00", + "2024-01-05T04:39:00", + "2024-01-05T04:40:00", + "2024-01-05T04:41:00", + "2024-01-05T04:42:00", + "2024-01-05T04:43:00", + "2024-01-05T04:44:00", + "2024-01-05T04:45:00", + "2024-01-05T04:46:00", + "2024-01-05T04:47:00", + "2024-01-05T04:48:00", + "2024-01-05T04:49:00", + "2024-01-05T04:50:00", + "2024-01-05T04:51:00", + "2024-01-05T04:52:00", + "2024-01-05T04:53:00", + "2024-01-05T04:54:00", + "2024-01-05T04:55:00", + "2024-01-05T04:56:00", + "2024-01-05T04:57:00", + "2024-01-05T04:58:00", + "2024-01-05T04:59:00", + "2024-01-05T05:00:00", + "2024-01-05T05:01:00", + "2024-01-05T05:02:00", + "2024-01-05T05:03:00", + "2024-01-05T05:04:00", + "2024-01-05T05:05:00", + "2024-01-05T05:06:00", + "2024-01-05T05:07:00", + "2024-01-05T05:08:00", + "2024-01-05T05:09:00", + "2024-01-05T05:10:00", + "2024-01-05T05:11:00", + "2024-01-05T05:12:00", + "2024-01-05T05:13:00", + "2024-01-05T05:14:00", + "2024-01-05T05:15:00", + "2024-01-05T05:16:00", + "2024-01-05T05:17:00", + "2024-01-05T05:18:00", + "2024-01-05T05:19:00", + "2024-01-05T05:20:00", + "2024-01-05T05:21:00", + "2024-01-05T05:22:00", + "2024-01-05T05:23:00", + "2024-01-05T05:24:00", + "2024-01-05T05:25:00", + "2024-01-05T05:26:00", + "2024-01-05T05:27:00", + "2024-01-05T05:28:00", + "2024-01-05T05:29:00", + "2024-01-05T05:30:00", + "2024-01-05T05:31:00", + "2024-01-05T05:32:00", + "2024-01-05T05:33:00", + "2024-01-05T05:34:00", + "2024-01-05T05:35:00", + "2024-01-05T05:36:00", + "2024-01-05T05:37:00", + "2024-01-05T05:38:00", + "2024-01-05T05:39:00", + "2024-01-05T05:40:00", + "2024-01-05T05:41:00", + "2024-01-05T05:42:00", + "2024-01-05T05:43:00", + "2024-01-05T05:44:00", + "2024-01-05T05:45:00", + "2024-01-05T05:46:00", + "2024-01-05T05:47:00", + "2024-01-05T05:48:00", + "2024-01-05T05:49:00", + "2024-01-05T05:50:00", + "2024-01-05T05:51:00", + "2024-01-05T05:52:00", + "2024-01-05T05:53:00", + "2024-01-05T05:54:00", + "2024-01-05T05:55:00", + "2024-01-05T05:56:00", + "2024-01-05T05:57:00", + "2024-01-05T05:58:00", + "2024-01-05T05:59:00", + "2024-01-05T06:00:00", + "2024-01-05T06:01:00", + "2024-01-05T06:02:00", + "2024-01-05T06:03:00", + "2024-01-05T06:04:00", + "2024-01-05T06:05:00", + "2024-01-05T06:06:00", + "2024-01-05T06:07:00", + "2024-01-05T06:08:00", + "2024-01-05T06:09:00", + "2024-01-05T06:10:00", + "2024-01-05T06:11:00", + "2024-01-05T06:12:00", + "2024-01-05T06:13:00", + "2024-01-05T06:14:00", + "2024-01-05T06:15:00", + "2024-01-05T06:16:00", + "2024-01-05T06:17:00", + "2024-01-05T06:18:00", + "2024-01-05T06:19:00", + "2024-01-05T06:20:00", + "2024-01-05T06:21:00", + "2024-01-05T06:22:00", + "2024-01-05T06:23:00", + "2024-01-05T06:24:00", + "2024-01-05T06:25:00", + "2024-01-05T06:26:00", + "2024-01-05T06:27:00", + "2024-01-05T06:28:00", + "2024-01-05T06:29:00", + "2024-01-05T06:30:00", + "2024-01-05T06:31:00", + "2024-01-05T06:32:00", + "2024-01-05T06:33:00", + "2024-01-05T06:34:00", + "2024-01-05T06:35:00", + "2024-01-05T06:36:00", + "2024-01-05T06:37:00", + "2024-01-05T06:38:00", + "2024-01-05T06:39:00", + "2024-01-05T06:40:00", + "2024-01-05T06:41:00", + "2024-01-05T06:42:00", + "2024-01-05T06:43:00", + "2024-01-05T06:44:00", + "2024-01-05T06:45:00", + "2024-01-05T06:46:00", + "2024-01-05T06:47:00", + "2024-01-05T06:48:00", + "2024-01-05T06:49:00", + "2024-01-05T06:50:00", + "2024-01-05T06:51:00", + "2024-01-05T06:52:00", + "2024-01-05T06:53:00", + "2024-01-05T06:54:00", + "2024-01-05T06:55:00", + "2024-01-05T06:56:00", + "2024-01-05T06:57:00", + "2024-01-05T06:58:00", + "2024-01-05T06:59:00", + "2024-01-05T07:00:00", + "2024-01-05T07:01:00", + "2024-01-05T07:02:00", + "2024-01-05T07:03:00", + "2024-01-05T07:04:00", + "2024-01-05T07:05:00", + "2024-01-05T07:06:00", + "2024-01-05T07:07:00", + "2024-01-05T07:08:00", + "2024-01-05T07:09:00", + "2024-01-05T07:10:00", + "2024-01-05T07:11:00", + "2024-01-05T07:12:00", + "2024-01-05T07:13:00", + "2024-01-05T07:14:00", + "2024-01-05T07:15:00", + "2024-01-05T07:16:00", + "2024-01-05T07:17:00", + "2024-01-05T07:18:00", + "2024-01-05T07:19:00", + "2024-01-05T07:20:00", + "2024-01-05T07:21:00", + "2024-01-05T07:22:00", + "2024-01-05T07:23:00", + "2024-01-05T07:24:00", + "2024-01-05T07:25:00", + "2024-01-05T07:26:00", + "2024-01-05T07:27:00", + "2024-01-05T07:28:00", + "2024-01-05T07:29:00", + "2024-01-05T07:30:00", + "2024-01-05T07:31:00", + "2024-01-05T07:32:00", + "2024-01-05T07:33:00", + "2024-01-05T07:34:00", + "2024-01-05T07:35:00", + "2024-01-05T07:36:00", + "2024-01-05T07:37:00", + "2024-01-05T07:38:00", + "2024-01-05T07:39:00", + "2024-01-05T07:40:00", + "2024-01-05T07:41:00", + "2024-01-05T07:42:00", + "2024-01-05T07:43:00", + "2024-01-05T07:44:00", + "2024-01-05T07:45:00", + "2024-01-05T07:46:00", + "2024-01-05T07:47:00", + "2024-01-05T07:48:00", + "2024-01-05T07:49:00", + "2024-01-05T07:50:00", + "2024-01-05T07:51:00", + "2024-01-05T07:52:00", + "2024-01-05T07:53:00", + "2024-01-05T07:54:00", + "2024-01-05T07:55:00", + "2024-01-05T07:56:00", + "2024-01-05T07:57:00", + "2024-01-05T07:58:00", + "2024-01-05T07:59:00", + "2024-01-05T08:00:00", + "2024-01-05T08:01:00", + "2024-01-05T08:02:00", + "2024-01-05T08:03:00", + "2024-01-05T08:04:00", + "2024-01-05T08:05:00", + "2024-01-05T08:06:00", + "2024-01-05T08:07:00", + "2024-01-05T08:08:00", + "2024-01-05T08:09:00", + "2024-01-05T08:10:00", + "2024-01-05T08:11:00", + "2024-01-05T08:12:00", + "2024-01-05T08:13:00", + "2024-01-05T08:14:00", + "2024-01-05T08:15:00", + "2024-01-05T08:16:00", + "2024-01-05T08:17:00", + "2024-01-05T08:18:00", + "2024-01-05T08:19:00", + "2024-01-05T08:20:00", + "2024-01-05T08:21:00", + "2024-01-05T08:22:00", + "2024-01-05T08:23:00", + "2024-01-05T08:24:00", + "2024-01-05T08:25:00", + "2024-01-05T08:26:00", + "2024-01-05T08:27:00", + "2024-01-05T08:28:00", + "2024-01-05T08:29:00", + "2024-01-05T08:30:00", + "2024-01-05T08:31:00", + "2024-01-05T08:32:00", + "2024-01-05T08:33:00", + "2024-01-05T08:34:00", + "2024-01-05T08:35:00", + "2024-01-05T08:36:00", + "2024-01-05T08:37:00", + "2024-01-05T08:38:00", + "2024-01-05T08:39:00", + "2024-01-05T08:40:00", + "2024-01-05T08:41:00", + "2024-01-05T08:42:00", + "2024-01-05T08:43:00", + "2024-01-05T08:44:00", + "2024-01-05T08:45:00", + "2024-01-05T08:46:00", + "2024-01-05T08:47:00", + "2024-01-05T08:48:00", + "2024-01-05T08:49:00", + "2024-01-05T08:50:00", + "2024-01-05T08:51:00", + "2024-01-05T08:52:00", + "2024-01-05T08:53:00", + "2024-01-05T08:54:00", + "2024-01-05T08:55:00", + "2024-01-05T08:56:00", + "2024-01-05T08:57:00", + "2024-01-05T08:58:00", + "2024-01-05T08:59:00", + "2024-01-05T09:00:00", + "2024-01-05T09:01:00", + "2024-01-05T09:02:00", + "2024-01-05T09:03:00", + "2024-01-05T09:04:00", + "2024-01-05T09:05:00", + "2024-01-05T09:06:00", + "2024-01-05T09:07:00", + "2024-01-05T09:08:00", + "2024-01-05T09:09:00", + "2024-01-05T09:10:00", + "2024-01-05T09:11:00", + "2024-01-05T09:12:00", + "2024-01-05T09:13:00", + "2024-01-05T09:14:00", + "2024-01-05T09:15:00", + "2024-01-05T09:16:00", + "2024-01-05T09:17:00", + "2024-01-05T09:18:00", + "2024-01-05T09:19:00", + "2024-01-05T09:20:00", + "2024-01-05T09:21:00", + "2024-01-05T09:22:00", + "2024-01-05T09:23:00", + "2024-01-05T09:24:00", + "2024-01-05T09:25:00", + "2024-01-05T09:26:00", + "2024-01-05T09:27:00", + "2024-01-05T09:28:00", + "2024-01-05T09:29:00", + "2024-01-05T09:30:00", + "2024-01-05T09:31:00", + "2024-01-05T09:32:00", + "2024-01-05T09:33:00", + "2024-01-05T09:34:00", + "2024-01-05T09:35:00", + "2024-01-05T09:36:00", + "2024-01-05T09:37:00", + "2024-01-05T09:38:00", + "2024-01-05T09:39:00", + "2024-01-05T09:40:00", + "2024-01-05T09:41:00", + "2024-01-05T09:42:00", + "2024-01-05T09:43:00", + "2024-01-05T09:44:00", + "2024-01-05T09:45:00", + "2024-01-05T09:46:00", + "2024-01-05T09:47:00", + "2024-01-05T09:48:00", + "2024-01-05T09:49:00", + "2024-01-05T09:50:00", + "2024-01-05T09:51:00", + "2024-01-05T09:52:00", + "2024-01-05T09:53:00", + "2024-01-05T09:54:00", + "2024-01-05T09:55:00", + "2024-01-05T09:56:00", + "2024-01-05T09:57:00", + "2024-01-05T09:58:00", + "2024-01-05T09:59:00", + "2024-01-05T10:00:00", + "2024-01-05T10:01:00", + "2024-01-05T10:02:00", + "2024-01-05T10:03:00", + "2024-01-05T10:04:00", + "2024-01-05T10:05:00", + "2024-01-05T10:06:00", + "2024-01-05T10:07:00", + "2024-01-05T10:08:00", + "2024-01-05T10:09:00", + "2024-01-05T10:10:00", + "2024-01-05T10:11:00", + "2024-01-05T10:12:00", + "2024-01-05T10:13:00", + "2024-01-05T10:14:00", + "2024-01-05T10:15:00", + "2024-01-05T10:16:00", + "2024-01-05T10:17:00", + "2024-01-05T10:18:00", + "2024-01-05T10:19:00", + "2024-01-05T10:20:00", + "2024-01-05T10:21:00", + "2024-01-05T10:22:00", + "2024-01-05T10:23:00", + "2024-01-05T10:24:00", + "2024-01-05T10:25:00", + "2024-01-05T10:26:00", + "2024-01-05T10:27:00", + "2024-01-05T10:28:00", + "2024-01-05T10:29:00", + "2024-01-05T10:30:00", + "2024-01-05T10:31:00", + "2024-01-05T10:32:00", + "2024-01-05T10:33:00", + "2024-01-05T10:34:00", + "2024-01-05T10:35:00", + "2024-01-05T10:36:00", + "2024-01-05T10:37:00", + "2024-01-05T10:38:00", + "2024-01-05T10:39:00", + "2024-01-05T10:40:00", + "2024-01-05T10:41:00", + "2024-01-05T10:42:00", + "2024-01-05T10:43:00", + "2024-01-05T10:44:00", + "2024-01-05T10:45:00", + "2024-01-05T10:46:00", + "2024-01-05T10:47:00", + "2024-01-05T10:48:00", + "2024-01-05T10:49:00", + "2024-01-05T10:50:00", + "2024-01-05T10:51:00", + "2024-01-05T10:52:00", + "2024-01-05T10:53:00", + "2024-01-05T10:54:00", + "2024-01-05T10:55:00", + "2024-01-05T10:56:00", + "2024-01-05T10:57:00", + "2024-01-05T10:58:00", + "2024-01-05T10:59:00", + "2024-01-05T11:00:00", + "2024-01-05T11:01:00", + "2024-01-05T11:02:00", + "2024-01-05T11:03:00", + "2024-01-05T11:04:00", + "2024-01-05T11:05:00", + "2024-01-05T11:06:00", + "2024-01-05T11:07:00", + "2024-01-05T11:08:00", + "2024-01-05T11:09:00", + "2024-01-05T11:10:00", + "2024-01-05T11:11:00", + "2024-01-05T11:12:00", + "2024-01-05T11:13:00", + "2024-01-05T11:14:00", + "2024-01-05T11:15:00", + "2024-01-05T11:16:00", + "2024-01-05T11:17:00", + "2024-01-05T11:18:00", + "2024-01-05T11:19:00", + "2024-01-05T11:20:00", + "2024-01-05T11:21:00", + "2024-01-05T11:22:00", + "2024-01-05T11:23:00", + "2024-01-05T11:24:00", + "2024-01-05T11:25:00", + "2024-01-05T11:26:00", + "2024-01-05T11:27:00", + "2024-01-05T11:28:00", + "2024-01-05T11:29:00", + "2024-01-05T11:30:00", + "2024-01-05T11:31:00", + "2024-01-05T11:32:00", + "2024-01-05T11:33:00", + "2024-01-05T11:34:00", + "2024-01-05T11:35:00", + "2024-01-05T11:36:00", + "2024-01-05T11:37:00", + "2024-01-05T11:38:00", + "2024-01-05T11:39:00", + "2024-01-05T11:40:00", + "2024-01-05T11:41:00", + "2024-01-05T11:42:00", + "2024-01-05T11:43:00", + "2024-01-05T11:44:00", + "2024-01-05T11:45:00", + "2024-01-05T11:46:00", + "2024-01-05T11:47:00", + "2024-01-05T11:48:00", + "2024-01-05T11:49:00", + "2024-01-05T11:50:00", + "2024-01-05T11:51:00", + "2024-01-05T11:52:00", + "2024-01-05T11:53:00", + "2024-01-05T11:54:00", + "2024-01-05T11:55:00", + "2024-01-05T11:56:00", + "2024-01-05T11:57:00", + "2024-01-05T11:58:00", + "2024-01-05T11:59:00", + "2024-01-05T12:00:00", + "2024-01-05T12:01:00", + "2024-01-05T12:02:00", + "2024-01-05T12:03:00", + "2024-01-05T12:04:00", + "2024-01-05T12:05:00", + "2024-01-05T12:06:00", + "2024-01-05T12:07:00", + "2024-01-05T12:08:00", + "2024-01-05T12:09:00", + "2024-01-05T12:10:00", + "2024-01-05T12:11:00", + "2024-01-05T12:12:00", + "2024-01-05T12:13:00", + "2024-01-05T12:14:00", + "2024-01-05T12:15:00", + "2024-01-05T12:16:00", + "2024-01-05T12:17:00", + "2024-01-05T12:18:00", + "2024-01-05T12:19:00", + "2024-01-05T12:20:00", + "2024-01-05T12:21:00", + "2024-01-05T12:22:00", + "2024-01-05T12:23:00", + "2024-01-05T12:24:00", + "2024-01-05T12:25:00", + "2024-01-05T12:26:00", + "2024-01-05T12:27:00", + "2024-01-05T12:28:00", + "2024-01-05T12:29:00", + "2024-01-05T12:30:00", + "2024-01-05T12:31:00", + "2024-01-05T12:32:00", + "2024-01-05T12:33:00", + "2024-01-05T12:34:00", + "2024-01-05T12:35:00", + "2024-01-05T12:36:00", + "2024-01-05T12:37:00", + "2024-01-05T12:38:00", + "2024-01-05T12:39:00", + "2024-01-05T12:40:00", + "2024-01-05T12:41:00", + "2024-01-05T12:42:00", + "2024-01-05T12:43:00", + "2024-01-05T12:44:00", + "2024-01-05T12:45:00", + "2024-01-05T12:46:00", + "2024-01-05T12:47:00", + "2024-01-05T12:48:00", + "2024-01-05T12:49:00", + "2024-01-05T12:50:00", + "2024-01-05T12:51:00", + "2024-01-05T12:52:00", + "2024-01-05T12:53:00", + "2024-01-05T12:54:00", + "2024-01-05T12:55:00", + "2024-01-05T12:56:00", + "2024-01-05T12:57:00", + "2024-01-05T12:58:00", + "2024-01-05T12:59:00", + "2024-01-05T13:00:00", + "2024-01-05T13:01:00", + "2024-01-05T13:02:00", + "2024-01-05T13:03:00", + "2024-01-05T13:04:00", + "2024-01-05T13:05:00", + "2024-01-05T13:06:00", + "2024-01-05T13:07:00", + "2024-01-05T13:08:00", + "2024-01-05T13:09:00", + "2024-01-05T13:10:00", + "2024-01-05T13:11:00", + "2024-01-05T13:12:00", + "2024-01-05T13:13:00", + "2024-01-05T13:14:00", + "2024-01-05T13:15:00", + "2024-01-05T13:16:00", + "2024-01-05T13:17:00", + "2024-01-05T13:18:00", + "2024-01-05T13:19:00", + "2024-01-05T13:20:00", + "2024-01-05T13:21:00", + "2024-01-05T13:22:00", + "2024-01-05T13:23:00", + "2024-01-05T13:24:00", + "2024-01-05T13:25:00", + "2024-01-05T13:26:00", + "2024-01-05T13:27:00", + "2024-01-05T13:28:00", + "2024-01-05T13:29:00", + "2024-01-05T13:30:00", + "2024-01-05T13:31:00", + "2024-01-05T13:32:00", + "2024-01-05T13:33:00", + "2024-01-05T13:34:00", + "2024-01-05T13:35:00", + "2024-01-05T13:36:00", + "2024-01-05T13:37:00", + "2024-01-05T13:38:00", + "2024-01-05T13:39:00", + "2024-01-05T13:40:00", + "2024-01-05T13:41:00", + "2024-01-05T13:42:00", + "2024-01-05T13:43:00", + "2024-01-05T13:44:00", + "2024-01-05T13:45:00", + "2024-01-05T13:46:00", + "2024-01-05T13:47:00", + "2024-01-05T13:48:00", + "2024-01-05T13:49:00", + "2024-01-05T13:50:00", + "2024-01-05T13:51:00", + "2024-01-05T13:52:00", + "2024-01-05T13:53:00", + "2024-01-05T13:54:00", + "2024-01-05T13:55:00", + "2024-01-05T13:56:00", + "2024-01-05T13:57:00", + "2024-01-05T13:58:00", + "2024-01-05T13:59:00", + "2024-01-05T14:00:00", + "2024-01-05T14:01:00", + "2024-01-05T14:02:00", + "2024-01-05T14:03:00", + "2024-01-05T14:04:00", + "2024-01-05T14:05:00", + "2024-01-05T14:06:00", + "2024-01-05T14:07:00", + "2024-01-05T14:08:00", + "2024-01-05T14:09:00", + "2024-01-05T14:10:00", + "2024-01-05T14:11:00", + "2024-01-05T14:12:00", + "2024-01-05T14:13:00", + "2024-01-05T14:14:00", + "2024-01-05T14:15:00", + "2024-01-05T14:16:00", + "2024-01-05T14:17:00", + "2024-01-05T14:18:00", + "2024-01-05T14:19:00", + "2024-01-05T14:20:00", + "2024-01-05T14:21:00", + "2024-01-05T14:22:00", + "2024-01-05T14:23:00", + "2024-01-05T14:24:00", + "2024-01-05T14:25:00", + "2024-01-05T14:26:00", + "2024-01-05T14:27:00", + "2024-01-05T14:28:00", + "2024-01-05T14:29:00", + "2024-01-05T14:30:00", + "2024-01-05T14:31:00", + "2024-01-05T14:32:00", + "2024-01-05T14:33:00", + "2024-01-05T14:34:00", + "2024-01-05T14:35:00", + "2024-01-05T14:36:00", + "2024-01-05T14:37:00", + "2024-01-05T14:38:00", + "2024-01-05T14:39:00", + "2024-01-05T14:40:00", + "2024-01-05T14:41:00", + "2024-01-05T14:42:00", + "2024-01-05T14:43:00", + "2024-01-05T14:44:00", + "2024-01-05T14:45:00", + "2024-01-05T14:46:00", + "2024-01-05T14:47:00", + "2024-01-05T14:48:00", + "2024-01-05T14:49:00", + "2024-01-05T14:50:00", + "2024-01-05T14:51:00", + "2024-01-05T14:52:00", + "2024-01-05T14:53:00", + "2024-01-05T14:54:00", + "2024-01-05T14:55:00", + "2024-01-05T14:56:00", + "2024-01-05T14:57:00", + "2024-01-05T14:58:00", + "2024-01-05T14:59:00", + "2024-01-05T15:00:00", + "2024-01-05T15:01:00", + "2024-01-05T15:02:00", + "2024-01-05T15:03:00", + "2024-01-05T15:04:00", + "2024-01-05T15:05:00", + "2024-01-05T15:06:00", + "2024-01-05T15:07:00", + "2024-01-05T15:08:00", + "2024-01-05T15:09:00", + "2024-01-05T15:10:00", + "2024-01-05T15:11:00", + "2024-01-05T15:12:00", + "2024-01-05T15:13:00", + "2024-01-05T15:14:00", + "2024-01-05T15:15:00", + "2024-01-05T15:16:00", + "2024-01-05T15:17:00", + "2024-01-05T15:18:00", + "2024-01-05T15:19:00", + "2024-01-05T15:20:00", + "2024-01-05T15:21:00", + "2024-01-05T15:22:00", + "2024-01-05T15:23:00", + "2024-01-05T15:24:00", + "2024-01-05T15:25:00", + "2024-01-05T15:26:00", + "2024-01-05T15:27:00", + "2024-01-05T15:28:00", + "2024-01-05T15:29:00", + "2024-01-05T15:30:00", + "2024-01-05T15:31:00", + "2024-01-05T15:32:00", + "2024-01-05T15:33:00", + "2024-01-05T15:34:00", + "2024-01-05T15:35:00", + "2024-01-05T15:36:00", + "2024-01-05T15:37:00", + "2024-01-05T15:38:00", + "2024-01-05T15:39:00", + "2024-01-05T15:40:00", + "2024-01-05T15:41:00", + "2024-01-05T15:42:00", + "2024-01-05T15:43:00", + "2024-01-05T15:44:00", + "2024-01-05T15:45:00", + "2024-01-05T15:46:00", + "2024-01-05T15:47:00", + "2024-01-05T15:48:00", + "2024-01-05T15:49:00", + "2024-01-05T15:50:00", + "2024-01-05T15:51:00", + "2024-01-05T15:52:00", + "2024-01-05T15:53:00", + "2024-01-05T15:54:00", + "2024-01-05T15:55:00", + "2024-01-05T15:56:00", + "2024-01-05T15:57:00", + "2024-01-05T15:58:00", + "2024-01-05T15:59:00", + "2024-01-05T16:00:00", + "2024-01-05T16:01:00", + "2024-01-05T16:02:00", + "2024-01-05T16:03:00", + "2024-01-05T16:04:00", + "2024-01-05T16:05:00", + "2024-01-05T16:06:00", + "2024-01-05T16:07:00", + "2024-01-05T16:08:00", + "2024-01-05T16:09:00", + "2024-01-05T16:10:00", + "2024-01-05T16:11:00", + "2024-01-05T16:12:00", + "2024-01-05T16:13:00", + "2024-01-05T16:14:00", + "2024-01-05T16:15:00", + "2024-01-05T16:16:00", + "2024-01-05T16:17:00", + "2024-01-05T16:18:00", + "2024-01-05T16:19:00", + "2024-01-05T16:20:00", + "2024-01-05T16:21:00", + "2024-01-05T16:22:00", + "2024-01-05T16:23:00", + "2024-01-05T16:24:00", + "2024-01-05T16:25:00", + "2024-01-05T16:26:00", + "2024-01-05T16:27:00", + "2024-01-05T16:28:00", + "2024-01-05T16:29:00", + "2024-01-05T16:30:00", + "2024-01-05T16:31:00", + "2024-01-05T16:32:00", + "2024-01-05T16:33:00", + "2024-01-05T16:34:00", + "2024-01-05T16:35:00", + "2024-01-05T16:36:00", + "2024-01-05T16:37:00", + "2024-01-05T16:38:00", + "2024-01-05T16:39:00", + "2024-01-05T16:40:00", + "2024-01-05T16:41:00", + "2024-01-05T16:42:00", + "2024-01-05T16:43:00", + "2024-01-05T16:44:00", + "2024-01-05T16:45:00", + "2024-01-05T16:46:00", + "2024-01-05T16:47:00", + "2024-01-05T16:48:00", + "2024-01-05T16:49:00", + "2024-01-05T16:50:00", + "2024-01-05T16:51:00", + "2024-01-05T16:52:00", + "2024-01-05T16:53:00", + "2024-01-05T16:54:00", + "2024-01-05T16:55:00", + "2024-01-05T16:56:00", + "2024-01-05T16:57:00", + "2024-01-05T16:58:00", + "2024-01-05T16:59:00", + "2024-01-05T17:00:00", + "2024-01-05T17:01:00", + "2024-01-05T17:02:00", + "2024-01-05T17:03:00", + "2024-01-05T17:04:00", + "2024-01-05T17:05:00", + "2024-01-05T17:06:00", + "2024-01-05T17:07:00", + "2024-01-05T17:08:00", + "2024-01-05T17:09:00", + "2024-01-05T17:10:00", + "2024-01-05T17:11:00", + "2024-01-05T17:12:00", + "2024-01-05T17:13:00", + "2024-01-05T17:14:00", + "2024-01-05T17:15:00", + "2024-01-05T17:16:00", + "2024-01-05T17:17:00", + "2024-01-05T17:18:00", + "2024-01-05T17:19:00", + "2024-01-05T17:20:00", + "2024-01-05T17:21:00", + "2024-01-05T17:22:00", + "2024-01-05T17:23:00", + "2024-01-05T17:24:00", + "2024-01-05T17:25:00", + "2024-01-05T17:26:00", + "2024-01-05T17:27:00", + "2024-01-05T17:28:00", + "2024-01-05T17:29:00", + "2024-01-05T17:30:00", + "2024-01-05T17:31:00", + "2024-01-05T17:32:00", + "2024-01-05T17:33:00", + "2024-01-05T17:34:00", + "2024-01-05T17:35:00", + "2024-01-05T17:36:00", + "2024-01-05T17:37:00", + "2024-01-05T17:38:00", + "2024-01-05T17:39:00", + "2024-01-05T17:40:00", + "2024-01-05T17:41:00", + "2024-01-05T17:42:00", + "2024-01-05T17:43:00", + "2024-01-05T17:44:00", + "2024-01-05T17:45:00", + "2024-01-05T17:46:00", + "2024-01-05T17:47:00", + "2024-01-05T17:48:00", + "2024-01-05T17:49:00", + "2024-01-05T17:50:00", + "2024-01-05T17:51:00", + "2024-01-05T17:52:00", + "2024-01-05T17:53:00", + "2024-01-05T17:54:00", + "2024-01-05T17:55:00", + "2024-01-05T17:56:00", + "2024-01-05T17:57:00", + "2024-01-05T17:58:00", + "2024-01-05T17:59:00", + "2024-01-05T18:00:00", + "2024-01-05T18:01:00", + "2024-01-05T18:02:00", + "2024-01-05T18:03:00", + "2024-01-05T18:04:00", + "2024-01-05T18:05:00", + "2024-01-05T18:06:00", + "2024-01-05T18:07:00", + "2024-01-05T18:08:00", + "2024-01-05T18:09:00", + "2024-01-05T18:10:00", + "2024-01-05T18:11:00", + "2024-01-05T18:12:00", + "2024-01-05T18:13:00", + "2024-01-05T18:14:00", + "2024-01-05T18:15:00", + "2024-01-05T18:16:00", + "2024-01-05T18:17:00", + "2024-01-05T18:18:00", + "2024-01-05T18:19:00", + "2024-01-05T18:20:00", + "2024-01-05T18:21:00", + "2024-01-05T18:22:00", + "2024-01-05T18:23:00", + "2024-01-05T18:24:00", + "2024-01-05T18:25:00", + "2024-01-05T18:26:00", + "2024-01-05T18:27:00", + "2024-01-05T18:28:00", + "2024-01-05T18:29:00", + "2024-01-05T18:30:00", + "2024-01-05T18:31:00", + "2024-01-05T18:32:00", + "2024-01-05T18:33:00", + "2024-01-05T18:34:00", + "2024-01-05T18:35:00", + "2024-01-05T18:36:00", + "2024-01-05T18:37:00", + "2024-01-05T18:38:00", + "2024-01-05T18:39:00", + "2024-01-05T18:40:00", + "2024-01-05T18:41:00", + "2024-01-05T18:42:00", + "2024-01-05T18:43:00", + "2024-01-05T18:44:00", + "2024-01-05T18:45:00", + "2024-01-05T18:46:00", + "2024-01-05T18:47:00", + "2024-01-05T18:48:00", + "2024-01-05T18:49:00", + "2024-01-05T18:50:00", + "2024-01-05T18:51:00", + "2024-01-05T18:52:00", + "2024-01-05T18:53:00", + "2024-01-05T18:54:00", + "2024-01-05T18:55:00", + "2024-01-05T18:56:00", + "2024-01-05T18:57:00", + "2024-01-05T18:58:00", + "2024-01-05T18:59:00", + "2024-01-05T19:00:00", + "2024-01-05T19:01:00", + "2024-01-05T19:02:00", + "2024-01-05T19:03:00", + "2024-01-05T19:04:00", + "2024-01-05T19:05:00", + "2024-01-05T19:06:00", + "2024-01-05T19:07:00", + "2024-01-05T19:08:00", + "2024-01-05T19:09:00", + "2024-01-05T19:10:00", + "2024-01-05T19:11:00", + "2024-01-05T19:12:00", + "2024-01-05T19:13:00", + "2024-01-05T19:14:00", + "2024-01-05T19:15:00", + "2024-01-05T19:16:00", + "2024-01-05T19:17:00", + "2024-01-05T19:18:00", + "2024-01-05T19:19:00", + "2024-01-05T19:20:00", + "2024-01-05T19:21:00", + "2024-01-05T19:22:00", + "2024-01-05T19:23:00", + "2024-01-05T19:24:00", + "2024-01-05T19:25:00", + "2024-01-05T19:26:00", + "2024-01-05T19:27:00", + "2024-01-05T19:28:00", + "2024-01-05T19:29:00", + "2024-01-05T19:30:00", + "2024-01-05T19:31:00", + "2024-01-05T19:32:00", + "2024-01-05T19:33:00", + "2024-01-05T19:34:00", + "2024-01-05T19:35:00", + "2024-01-05T19:36:00", + "2024-01-05T19:37:00", + "2024-01-05T19:38:00", + "2024-01-05T19:39:00", + "2024-01-05T19:40:00", + "2024-01-05T19:41:00", + "2024-01-05T19:42:00", + "2024-01-05T19:43:00", + "2024-01-05T19:44:00", + "2024-01-05T19:45:00", + "2024-01-05T19:46:00", + "2024-01-05T19:47:00", + "2024-01-05T19:48:00", + "2024-01-05T19:49:00", + "2024-01-05T19:50:00", + "2024-01-05T19:51:00", + "2024-01-05T19:52:00", + "2024-01-05T19:53:00", + "2024-01-05T19:54:00", + "2024-01-05T19:55:00", + "2024-01-05T19:56:00", + "2024-01-05T19:57:00", + "2024-01-05T19:58:00", + "2024-01-05T19:59:00", + "2024-01-05T20:00:00", + "2024-01-05T20:01:00", + "2024-01-05T20:02:00", + "2024-01-05T20:03:00", + "2024-01-05T20:04:00", + "2024-01-05T20:05:00", + "2024-01-05T20:06:00", + "2024-01-05T20:07:00", + "2024-01-05T20:08:00", + "2024-01-05T20:09:00", + "2024-01-05T20:10:00", + "2024-01-05T20:11:00", + "2024-01-05T20:12:00", + "2024-01-05T20:13:00", + "2024-01-05T20:14:00", + "2024-01-05T20:15:00", + "2024-01-05T20:16:00", + "2024-01-05T20:17:00", + "2024-01-05T20:18:00", + "2024-01-05T20:19:00", + "2024-01-05T20:20:00", + "2024-01-05T20:21:00", + "2024-01-05T20:22:00", + "2024-01-05T20:23:00", + "2024-01-05T20:24:00", + "2024-01-05T20:25:00", + "2024-01-05T20:26:00", + "2024-01-05T20:27:00", + "2024-01-05T20:28:00", + "2024-01-05T20:29:00", + "2024-01-05T20:30:00", + "2024-01-05T20:31:00", + "2024-01-05T20:32:00", + "2024-01-05T20:33:00", + "2024-01-05T20:34:00", + "2024-01-05T20:35:00", + "2024-01-05T20:36:00", + "2024-01-05T20:37:00", + "2024-01-05T20:38:00", + "2024-01-05T20:39:00", + "2024-01-05T20:40:00", + "2024-01-05T20:41:00", + "2024-01-05T20:42:00", + "2024-01-05T20:43:00", + "2024-01-05T20:44:00", + "2024-01-05T20:45:00", + "2024-01-05T20:46:00", + "2024-01-05T20:47:00", + "2024-01-05T20:48:00", + "2024-01-05T20:49:00", + "2024-01-05T20:50:00", + "2024-01-05T20:51:00", + "2024-01-05T20:52:00", + "2024-01-05T20:53:00", + "2024-01-05T20:54:00", + "2024-01-05T20:55:00", + "2024-01-05T20:56:00", + "2024-01-05T20:57:00", + "2024-01-05T20:58:00", + "2024-01-05T20:59:00", + "2024-01-05T21:00:00", + "2024-01-05T21:01:00", + "2024-01-05T21:02:00", + "2024-01-05T21:03:00", + "2024-01-05T21:04:00", + "2024-01-05T21:05:00", + "2024-01-05T21:06:00", + "2024-01-05T21:07:00", + "2024-01-05T21:08:00", + "2024-01-05T21:09:00", + "2024-01-05T21:10:00", + "2024-01-05T21:11:00", + "2024-01-05T21:12:00", + "2024-01-05T21:13:00", + "2024-01-05T21:14:00", + "2024-01-05T21:15:00", + "2024-01-05T21:16:00", + "2024-01-05T21:17:00", + "2024-01-05T21:18:00", + "2024-01-05T21:19:00", + "2024-01-05T21:20:00", + "2024-01-05T21:21:00", + "2024-01-05T21:22:00", + "2024-01-05T21:23:00", + "2024-01-05T21:24:00", + "2024-01-05T21:25:00", + "2024-01-05T21:26:00", + "2024-01-05T21:27:00", + "2024-01-05T21:28:00", + "2024-01-05T21:29:00", + "2024-01-05T21:30:00", + "2024-01-05T21:31:00", + "2024-01-05T21:32:00", + "2024-01-05T21:33:00", + "2024-01-05T21:34:00", + "2024-01-05T21:35:00", + "2024-01-05T21:36:00", + "2024-01-05T21:37:00", + "2024-01-05T21:38:00", + "2024-01-05T21:39:00", + "2024-01-05T21:40:00", + "2024-01-05T21:41:00", + "2024-01-05T21:42:00", + "2024-01-05T21:43:00", + "2024-01-05T21:44:00", + "2024-01-05T21:45:00", + "2024-01-05T21:46:00", + "2024-01-05T21:47:00", + "2024-01-05T21:48:00", + "2024-01-05T21:49:00", + "2024-01-05T21:50:00", + "2024-01-05T21:51:00", + "2024-01-05T21:52:00", + "2024-01-05T21:53:00", + "2024-01-05T21:54:00", + "2024-01-05T21:55:00", + "2024-01-05T21:56:00", + "2024-01-05T21:57:00", + "2024-01-05T21:58:00", + "2024-01-05T21:59:00", + "2024-01-05T22:00:00", + "2024-01-05T22:01:00", + "2024-01-05T22:02:00", + "2024-01-05T22:03:00", + "2024-01-05T22:04:00", + "2024-01-05T22:05:00", + "2024-01-05T22:06:00", + "2024-01-05T22:07:00", + "2024-01-05T22:08:00", + "2024-01-05T22:09:00", + "2024-01-05T22:10:00", + "2024-01-05T22:11:00", + "2024-01-05T22:12:00", + "2024-01-05T22:13:00", + "2024-01-05T22:14:00", + "2024-01-05T22:15:00", + "2024-01-05T22:16:00", + "2024-01-05T22:17:00", + "2024-01-05T22:18:00", + "2024-01-05T22:19:00", + "2024-01-05T22:20:00", + "2024-01-05T22:21:00", + "2024-01-05T22:22:00", + "2024-01-05T22:23:00", + "2024-01-05T22:24:00", + "2024-01-05T22:25:00", + "2024-01-05T22:26:00", + "2024-01-05T22:27:00", + "2024-01-05T22:28:00", + "2024-01-05T22:29:00", + "2024-01-05T22:30:00", + "2024-01-05T22:31:00", + "2024-01-05T22:32:00", + "2024-01-05T22:33:00", + "2024-01-05T22:34:00", + "2024-01-05T22:35:00", + "2024-01-05T22:36:00", + "2024-01-05T22:37:00", + "2024-01-05T22:38:00", + "2024-01-05T22:39:00", + "2024-01-05T22:40:00", + "2024-01-05T22:41:00", + "2024-01-05T22:42:00", + "2024-01-05T22:43:00", + "2024-01-05T22:44:00", + "2024-01-05T22:45:00", + "2024-01-05T22:46:00", + "2024-01-05T22:47:00", + "2024-01-05T22:48:00", + "2024-01-05T22:49:00", + "2024-01-05T22:50:00", + "2024-01-05T22:51:00", + "2024-01-05T22:52:00", + "2024-01-05T22:53:00", + "2024-01-05T22:54:00", + "2024-01-05T22:55:00", + "2024-01-05T22:56:00", + "2024-01-05T22:57:00", + "2024-01-05T22:58:00", + "2024-01-05T22:59:00", + "2024-01-05T23:00:00", + "2024-01-05T23:01:00", + "2024-01-05T23:02:00", + "2024-01-05T23:03:00", + "2024-01-05T23:04:00", + "2024-01-05T23:05:00", + "2024-01-05T23:06:00", + "2024-01-05T23:07:00", + "2024-01-05T23:08:00", + "2024-01-05T23:09:00", + "2024-01-05T23:10:00", + "2024-01-05T23:11:00", + "2024-01-05T23:12:00", + "2024-01-05T23:13:00", + "2024-01-05T23:14:00", + "2024-01-05T23:15:00", + "2024-01-05T23:16:00", + "2024-01-05T23:17:00", + "2024-01-05T23:18:00", + "2024-01-05T23:19:00", + "2024-01-05T23:20:00", + "2024-01-05T23:21:00", + "2024-01-05T23:22:00", + "2024-01-05T23:23:00", + "2024-01-05T23:24:00", + "2024-01-05T23:25:00", + "2024-01-05T23:26:00", + "2024-01-05T23:27:00", + "2024-01-05T23:28:00", + "2024-01-05T23:29:00", + "2024-01-05T23:30:00", + "2024-01-05T23:31:00", + "2024-01-05T23:32:00", + "2024-01-05T23:33:00", + "2024-01-05T23:34:00", + "2024-01-05T23:35:00", + "2024-01-05T23:36:00", + "2024-01-05T23:37:00", + "2024-01-05T23:38:00", + "2024-01-05T23:39:00", + "2024-01-05T23:40:00", + "2024-01-05T23:41:00", + "2024-01-05T23:42:00", + "2024-01-05T23:43:00", + "2024-01-05T23:44:00", + "2024-01-05T23:45:00", + "2024-01-05T23:46:00", + "2024-01-05T23:47:00", + "2024-01-05T23:48:00", + "2024-01-05T23:49:00", + "2024-01-05T23:50:00", + "2024-01-05T23:51:00", + "2024-01-05T23:52:00", + "2024-01-05T23:53:00", + "2024-01-05T23:54:00", + "2024-01-05T23:55:00", + "2024-01-05T23:56:00", + "2024-01-05T23:57:00", + "2024-01-05T23:58:00", + "2024-01-05T23:59:00", + "2024-01-06T00:00:00", + "2024-01-06T00:01:00", + "2024-01-06T00:02:00", + "2024-01-06T00:03:00", + "2024-01-06T00:04:00", + "2024-01-06T00:05:00", + "2024-01-06T00:06:00", + "2024-01-06T00:07:00", + "2024-01-06T00:08:00", + "2024-01-06T00:09:00", + "2024-01-06T00:10:00", + "2024-01-06T00:11:00", + "2024-01-06T00:12:00", + "2024-01-06T00:13:00", + "2024-01-06T00:14:00", + "2024-01-06T00:15:00", + "2024-01-06T00:16:00", + "2024-01-06T00:17:00", + "2024-01-06T00:18:00", + "2024-01-06T00:19:00", + "2024-01-06T00:20:00", + "2024-01-06T00:21:00", + "2024-01-06T00:22:00", + "2024-01-06T00:23:00", + "2024-01-06T00:24:00", + "2024-01-06T00:25:00", + "2024-01-06T00:26:00", + "2024-01-06T00:27:00", + "2024-01-06T00:28:00", + "2024-01-06T00:29:00", + "2024-01-06T00:30:00", + "2024-01-06T00:31:00", + "2024-01-06T00:32:00", + "2024-01-06T00:33:00", + "2024-01-06T00:34:00", + "2024-01-06T00:35:00", + "2024-01-06T00:36:00", + "2024-01-06T00:37:00", + "2024-01-06T00:38:00", + "2024-01-06T00:39:00", + "2024-01-06T00:40:00", + "2024-01-06T00:41:00", + "2024-01-06T00:42:00", + "2024-01-06T00:43:00", + "2024-01-06T00:44:00", + "2024-01-06T00:45:00", + "2024-01-06T00:46:00", + "2024-01-06T00:47:00", + "2024-01-06T00:48:00", + "2024-01-06T00:49:00", + "2024-01-06T00:50:00", + "2024-01-06T00:51:00", + "2024-01-06T00:52:00", + "2024-01-06T00:53:00", + "2024-01-06T00:54:00", + "2024-01-06T00:55:00", + "2024-01-06T00:56:00", + "2024-01-06T00:57:00", + "2024-01-06T00:58:00", + "2024-01-06T00:59:00", + "2024-01-06T01:00:00", + "2024-01-06T01:01:00", + "2024-01-06T01:02:00", + "2024-01-06T01:03:00", + "2024-01-06T01:04:00", + "2024-01-06T01:05:00", + "2024-01-06T01:06:00", + "2024-01-06T01:07:00", + "2024-01-06T01:08:00", + "2024-01-06T01:09:00", + "2024-01-06T01:10:00", + "2024-01-06T01:11:00", + "2024-01-06T01:12:00", + "2024-01-06T01:13:00", + "2024-01-06T01:14:00", + "2024-01-06T01:15:00", + "2024-01-06T01:16:00", + "2024-01-06T01:17:00", + "2024-01-06T01:18:00", + "2024-01-06T01:19:00", + "2024-01-06T01:20:00", + "2024-01-06T01:21:00", + "2024-01-06T01:22:00", + "2024-01-06T01:23:00", + "2024-01-06T01:24:00", + "2024-01-06T01:25:00", + "2024-01-06T01:26:00", + "2024-01-06T01:27:00", + "2024-01-06T01:28:00", + "2024-01-06T01:29:00", + "2024-01-06T01:30:00", + "2024-01-06T01:31:00", + "2024-01-06T01:32:00", + "2024-01-06T01:33:00", + "2024-01-06T01:34:00", + "2024-01-06T01:35:00", + "2024-01-06T01:36:00", + "2024-01-06T01:37:00", + "2024-01-06T01:38:00", + "2024-01-06T01:39:00", + "2024-01-06T01:40:00", + "2024-01-06T01:41:00", + "2024-01-06T01:42:00", + "2024-01-06T01:43:00", + "2024-01-06T01:44:00", + "2024-01-06T01:45:00", + "2024-01-06T01:46:00", + "2024-01-06T01:47:00", + "2024-01-06T01:48:00", + "2024-01-06T01:49:00", + "2024-01-06T01:50:00", + "2024-01-06T01:51:00", + "2024-01-06T01:52:00", + "2024-01-06T01:53:00", + "2024-01-06T01:54:00", + "2024-01-06T01:55:00", + "2024-01-06T01:56:00", + "2024-01-06T01:57:00", + "2024-01-06T01:58:00", + "2024-01-06T01:59:00", + "2024-01-06T02:00:00", + "2024-01-06T02:01:00", + "2024-01-06T02:02:00", + "2024-01-06T02:03:00", + "2024-01-06T02:04:00", + "2024-01-06T02:05:00", + "2024-01-06T02:06:00", + "2024-01-06T02:07:00", + "2024-01-06T02:08:00", + "2024-01-06T02:09:00", + "2024-01-06T02:10:00", + "2024-01-06T02:11:00", + "2024-01-06T02:12:00", + "2024-01-06T02:13:00", + "2024-01-06T02:14:00", + "2024-01-06T02:15:00", + "2024-01-06T02:16:00", + "2024-01-06T02:17:00", + "2024-01-06T02:18:00", + "2024-01-06T02:19:00", + "2024-01-06T02:20:00", + "2024-01-06T02:21:00", + "2024-01-06T02:22:00", + "2024-01-06T02:23:00", + "2024-01-06T02:24:00", + "2024-01-06T02:25:00", + "2024-01-06T02:26:00", + "2024-01-06T02:27:00", + "2024-01-06T02:28:00", + "2024-01-06T02:29:00", + "2024-01-06T02:30:00", + "2024-01-06T02:31:00", + "2024-01-06T02:32:00", + "2024-01-06T02:33:00", + "2024-01-06T02:34:00", + "2024-01-06T02:35:00", + "2024-01-06T02:36:00", + "2024-01-06T02:37:00", + "2024-01-06T02:38:00", + "2024-01-06T02:39:00", + "2024-01-06T02:40:00", + "2024-01-06T02:41:00", + "2024-01-06T02:42:00", + "2024-01-06T02:43:00", + "2024-01-06T02:44:00", + "2024-01-06T02:45:00", + "2024-01-06T02:46:00", + "2024-01-06T02:47:00", + "2024-01-06T02:48:00", + "2024-01-06T02:49:00", + "2024-01-06T02:50:00", + "2024-01-06T02:51:00", + "2024-01-06T02:52:00", + "2024-01-06T02:53:00", + "2024-01-06T02:54:00", + "2024-01-06T02:55:00", + "2024-01-06T02:56:00", + "2024-01-06T02:57:00", + "2024-01-06T02:58:00", + "2024-01-06T02:59:00", + "2024-01-06T03:00:00", + "2024-01-06T03:01:00", + "2024-01-06T03:02:00", + "2024-01-06T03:03:00", + "2024-01-06T03:04:00", + "2024-01-06T03:05:00", + "2024-01-06T03:06:00", + "2024-01-06T03:07:00", + "2024-01-06T03:08:00", + "2024-01-06T03:09:00", + "2024-01-06T03:10:00", + "2024-01-06T03:11:00", + "2024-01-06T03:12:00", + "2024-01-06T03:13:00", + "2024-01-06T03:14:00", + "2024-01-06T03:15:00", + "2024-01-06T03:16:00", + "2024-01-06T03:17:00", + "2024-01-06T03:18:00", + "2024-01-06T03:19:00", + "2024-01-06T03:20:00", + "2024-01-06T03:21:00", + "2024-01-06T03:22:00", + "2024-01-06T03:23:00", + "2024-01-06T03:24:00", + "2024-01-06T03:25:00", + "2024-01-06T03:26:00", + "2024-01-06T03:27:00", + "2024-01-06T03:28:00", + "2024-01-06T03:29:00", + "2024-01-06T03:30:00", + "2024-01-06T03:31:00", + "2024-01-06T03:32:00", + "2024-01-06T03:33:00", + "2024-01-06T03:34:00", + "2024-01-06T03:35:00", + "2024-01-06T03:36:00", + "2024-01-06T03:37:00", + "2024-01-06T03:38:00", + "2024-01-06T03:39:00", + "2024-01-06T03:40:00", + "2024-01-06T03:41:00", + "2024-01-06T03:42:00", + "2024-01-06T03:43:00", + "2024-01-06T03:44:00", + "2024-01-06T03:45:00", + "2024-01-06T03:46:00", + "2024-01-06T03:47:00", + "2024-01-06T03:48:00", + "2024-01-06T03:49:00", + "2024-01-06T03:50:00", + "2024-01-06T03:51:00", + "2024-01-06T03:52:00", + "2024-01-06T03:53:00", + "2024-01-06T03:54:00", + "2024-01-06T03:55:00", + "2024-01-06T03:56:00", + "2024-01-06T03:57:00", + "2024-01-06T03:58:00", + "2024-01-06T03:59:00", + "2024-01-06T04:00:00", + "2024-01-06T04:01:00", + "2024-01-06T04:02:00", + "2024-01-06T04:03:00", + "2024-01-06T04:04:00", + "2024-01-06T04:05:00", + "2024-01-06T04:06:00", + "2024-01-06T04:07:00", + "2024-01-06T04:08:00", + "2024-01-06T04:09:00", + "2024-01-06T04:10:00", + "2024-01-06T04:11:00", + "2024-01-06T04:12:00", + "2024-01-06T04:13:00", + "2024-01-06T04:14:00", + "2024-01-06T04:15:00", + "2024-01-06T04:16:00", + "2024-01-06T04:17:00", + "2024-01-06T04:18:00", + "2024-01-06T04:19:00", + "2024-01-06T04:20:00", + "2024-01-06T04:21:00", + "2024-01-06T04:22:00", + "2024-01-06T04:23:00", + "2024-01-06T04:24:00", + "2024-01-06T04:25:00", + "2024-01-06T04:26:00", + "2024-01-06T04:27:00", + "2024-01-06T04:28:00", + "2024-01-06T04:29:00", + "2024-01-06T04:30:00", + "2024-01-06T04:31:00", + "2024-01-06T04:32:00", + "2024-01-06T04:33:00", + "2024-01-06T04:34:00", + "2024-01-06T04:35:00", + "2024-01-06T04:36:00", + "2024-01-06T04:37:00", + "2024-01-06T04:38:00", + "2024-01-06T04:39:00", + "2024-01-06T04:40:00", + "2024-01-06T04:41:00", + "2024-01-06T04:42:00", + "2024-01-06T04:43:00", + "2024-01-06T04:44:00", + "2024-01-06T04:45:00", + "2024-01-06T04:46:00", + "2024-01-06T04:47:00", + "2024-01-06T04:48:00", + "2024-01-06T04:49:00", + "2024-01-06T04:50:00", + "2024-01-06T04:51:00", + "2024-01-06T04:52:00", + "2024-01-06T04:53:00", + "2024-01-06T04:54:00", + "2024-01-06T04:55:00", + "2024-01-06T04:56:00", + "2024-01-06T04:57:00", + "2024-01-06T04:58:00", + "2024-01-06T04:59:00", + "2024-01-06T05:00:00", + "2024-01-06T05:01:00", + "2024-01-06T05:02:00", + "2024-01-06T05:03:00", + "2024-01-06T05:04:00", + "2024-01-06T05:05:00", + "2024-01-06T05:06:00", + "2024-01-06T05:07:00", + "2024-01-06T05:08:00", + "2024-01-06T05:09:00", + "2024-01-06T05:10:00", + "2024-01-06T05:11:00", + "2024-01-06T05:12:00", + "2024-01-06T05:13:00", + "2024-01-06T05:14:00", + "2024-01-06T05:15:00", + "2024-01-06T05:16:00", + "2024-01-06T05:17:00", + "2024-01-06T05:18:00", + "2024-01-06T05:19:00", + "2024-01-06T05:20:00", + "2024-01-06T05:21:00", + "2024-01-06T05:22:00", + "2024-01-06T05:23:00", + "2024-01-06T05:24:00", + "2024-01-06T05:25:00", + "2024-01-06T05:26:00", + "2024-01-06T05:27:00", + "2024-01-06T05:28:00", + "2024-01-06T05:29:00", + "2024-01-06T05:30:00", + "2024-01-06T05:31:00", + "2024-01-06T05:32:00", + "2024-01-06T05:33:00", + "2024-01-06T05:34:00", + "2024-01-06T05:35:00", + "2024-01-06T05:36:00", + "2024-01-06T05:37:00", + "2024-01-06T05:38:00", + "2024-01-06T05:39:00", + "2024-01-06T05:40:00", + "2024-01-06T05:41:00", + "2024-01-06T05:42:00", + "2024-01-06T05:43:00", + "2024-01-06T05:44:00", + "2024-01-06T05:45:00", + "2024-01-06T05:46:00", + "2024-01-06T05:47:00", + "2024-01-06T05:48:00", + "2024-01-06T05:49:00", + "2024-01-06T05:50:00", + "2024-01-06T05:51:00", + "2024-01-06T05:52:00", + "2024-01-06T05:53:00", + "2024-01-06T05:54:00", + "2024-01-06T05:55:00", + "2024-01-06T05:56:00", + "2024-01-06T05:57:00", + "2024-01-06T05:58:00", + "2024-01-06T05:59:00", + "2024-01-06T06:00:00", + "2024-01-06T06:01:00", + "2024-01-06T06:02:00", + "2024-01-06T06:03:00", + "2024-01-06T06:04:00", + "2024-01-06T06:05:00", + "2024-01-06T06:06:00", + "2024-01-06T06:07:00", + "2024-01-06T06:08:00", + "2024-01-06T06:09:00", + "2024-01-06T06:10:00", + "2024-01-06T06:11:00", + "2024-01-06T06:12:00", + "2024-01-06T06:13:00", + "2024-01-06T06:14:00", + "2024-01-06T06:15:00", + "2024-01-06T06:16:00", + "2024-01-06T06:17:00", + "2024-01-06T06:18:00", + "2024-01-06T06:19:00", + "2024-01-06T06:20:00", + "2024-01-06T06:21:00", + "2024-01-06T06:22:00", + "2024-01-06T06:23:00", + "2024-01-06T06:24:00", + "2024-01-06T06:25:00", + "2024-01-06T06:26:00", + "2024-01-06T06:27:00", + "2024-01-06T06:28:00", + "2024-01-06T06:29:00", + "2024-01-06T06:30:00", + "2024-01-06T06:31:00", + "2024-01-06T06:32:00", + "2024-01-06T06:33:00", + "2024-01-06T06:34:00", + "2024-01-06T06:35:00", + "2024-01-06T06:36:00", + "2024-01-06T06:37:00", + "2024-01-06T06:38:00", + "2024-01-06T06:39:00", + "2024-01-06T06:40:00", + "2024-01-06T06:41:00", + "2024-01-06T06:42:00", + "2024-01-06T06:43:00", + "2024-01-06T06:44:00", + "2024-01-06T06:45:00", + "2024-01-06T06:46:00", + "2024-01-06T06:47:00", + "2024-01-06T06:48:00", + "2024-01-06T06:49:00", + "2024-01-06T06:50:00", + "2024-01-06T06:51:00", + "2024-01-06T06:52:00", + "2024-01-06T06:53:00", + "2024-01-06T06:54:00", + "2024-01-06T06:55:00", + "2024-01-06T06:56:00", + "2024-01-06T06:57:00", + "2024-01-06T06:58:00", + "2024-01-06T06:59:00", + "2024-01-06T07:00:00", + "2024-01-06T07:01:00", + "2024-01-06T07:02:00", + "2024-01-06T07:03:00", + "2024-01-06T07:04:00", + "2024-01-06T07:05:00", + "2024-01-06T07:06:00", + "2024-01-06T07:07:00", + "2024-01-06T07:08:00", + "2024-01-06T07:09:00", + "2024-01-06T07:10:00", + "2024-01-06T07:11:00", + "2024-01-06T07:12:00", + "2024-01-06T07:13:00", + "2024-01-06T07:14:00", + "2024-01-06T07:15:00", + "2024-01-06T07:16:00", + "2024-01-06T07:17:00", + "2024-01-06T07:18:00", + "2024-01-06T07:19:00", + "2024-01-06T07:20:00", + "2024-01-06T07:21:00", + "2024-01-06T07:22:00", + "2024-01-06T07:23:00", + "2024-01-06T07:24:00", + "2024-01-06T07:25:00", + "2024-01-06T07:26:00", + "2024-01-06T07:27:00", + "2024-01-06T07:28:00", + "2024-01-06T07:29:00", + "2024-01-06T07:30:00", + "2024-01-06T07:31:00", + "2024-01-06T07:32:00", + "2024-01-06T07:33:00", + "2024-01-06T07:34:00", + "2024-01-06T07:35:00", + "2024-01-06T07:36:00", + "2024-01-06T07:37:00", + "2024-01-06T07:38:00", + "2024-01-06T07:39:00", + "2024-01-06T07:40:00", + "2024-01-06T07:41:00", + "2024-01-06T07:42:00", + "2024-01-06T07:43:00", + "2024-01-06T07:44:00", + "2024-01-06T07:45:00", + "2024-01-06T07:46:00", + "2024-01-06T07:47:00", + "2024-01-06T07:48:00", + "2024-01-06T07:49:00", + "2024-01-06T07:50:00", + "2024-01-06T07:51:00", + "2024-01-06T07:52:00", + "2024-01-06T07:53:00", + "2024-01-06T07:54:00", + "2024-01-06T07:55:00", + "2024-01-06T07:56:00", + "2024-01-06T07:57:00", + "2024-01-06T07:58:00", + "2024-01-06T07:59:00", + "2024-01-06T08:00:00", + "2024-01-06T08:01:00", + "2024-01-06T08:02:00", + "2024-01-06T08:03:00", + "2024-01-06T08:04:00", + "2024-01-06T08:05:00", + "2024-01-06T08:06:00", + "2024-01-06T08:07:00", + "2024-01-06T08:08:00", + "2024-01-06T08:09:00", + "2024-01-06T08:10:00", + "2024-01-06T08:11:00", + "2024-01-06T08:12:00", + "2024-01-06T08:13:00", + "2024-01-06T08:14:00", + "2024-01-06T08:15:00", + "2024-01-06T08:16:00", + "2024-01-06T08:17:00", + "2024-01-06T08:18:00", + "2024-01-06T08:19:00", + "2024-01-06T08:20:00", + "2024-01-06T08:21:00", + "2024-01-06T08:22:00", + "2024-01-06T08:23:00", + "2024-01-06T08:24:00", + "2024-01-06T08:25:00", + "2024-01-06T08:26:00", + "2024-01-06T08:27:00", + "2024-01-06T08:28:00", + "2024-01-06T08:29:00", + "2024-01-06T08:30:00", + "2024-01-06T08:31:00", + "2024-01-06T08:32:00", + "2024-01-06T08:33:00", + "2024-01-06T08:34:00", + "2024-01-06T08:35:00", + "2024-01-06T08:36:00", + "2024-01-06T08:37:00", + "2024-01-06T08:38:00", + "2024-01-06T08:39:00", + "2024-01-06T08:40:00", + "2024-01-06T08:41:00", + "2024-01-06T08:42:00", + "2024-01-06T08:43:00", + "2024-01-06T08:44:00", + "2024-01-06T08:45:00", + "2024-01-06T08:46:00", + "2024-01-06T08:47:00", + "2024-01-06T08:48:00", + "2024-01-06T08:49:00", + "2024-01-06T08:50:00", + "2024-01-06T08:51:00", + "2024-01-06T08:52:00", + "2024-01-06T08:53:00", + "2024-01-06T08:54:00", + "2024-01-06T08:55:00", + "2024-01-06T08:56:00", + "2024-01-06T08:57:00", + "2024-01-06T08:58:00", + "2024-01-06T08:59:00", + "2024-01-06T09:00:00", + "2024-01-06T09:01:00", + "2024-01-06T09:02:00", + "2024-01-06T09:03:00", + "2024-01-06T09:04:00", + "2024-01-06T09:05:00", + "2024-01-06T09:06:00", + "2024-01-06T09:07:00", + "2024-01-06T09:08:00", + "2024-01-06T09:09:00", + "2024-01-06T09:10:00", + "2024-01-06T09:11:00", + "2024-01-06T09:12:00", + "2024-01-06T09:13:00", + "2024-01-06T09:14:00", + "2024-01-06T09:15:00", + "2024-01-06T09:16:00", + "2024-01-06T09:17:00", + "2024-01-06T09:18:00", + "2024-01-06T09:19:00", + "2024-01-06T09:20:00", + "2024-01-06T09:21:00", + "2024-01-06T09:22:00", + "2024-01-06T09:23:00", + "2024-01-06T09:24:00", + "2024-01-06T09:25:00", + "2024-01-06T09:26:00", + "2024-01-06T09:27:00", + "2024-01-06T09:28:00", + "2024-01-06T09:29:00", + "2024-01-06T09:30:00", + "2024-01-06T09:31:00", + "2024-01-06T09:32:00", + "2024-01-06T09:33:00", + "2024-01-06T09:34:00", + "2024-01-06T09:35:00", + "2024-01-06T09:36:00", + "2024-01-06T09:37:00", + "2024-01-06T09:38:00", + "2024-01-06T09:39:00", + "2024-01-06T09:40:00", + "2024-01-06T09:41:00", + "2024-01-06T09:42:00", + "2024-01-06T09:43:00", + "2024-01-06T09:44:00", + "2024-01-06T09:45:00", + "2024-01-06T09:46:00", + "2024-01-06T09:47:00", + "2024-01-06T09:48:00", + "2024-01-06T09:49:00", + "2024-01-06T09:50:00", + "2024-01-06T09:51:00", + "2024-01-06T09:52:00", + "2024-01-06T09:53:00", + "2024-01-06T09:54:00", + "2024-01-06T09:55:00", + "2024-01-06T09:56:00", + "2024-01-06T09:57:00", + "2024-01-06T09:58:00", + "2024-01-06T09:59:00", + "2024-01-06T10:00:00", + "2024-01-06T10:01:00", + "2024-01-06T10:02:00", + "2024-01-06T10:03:00", + "2024-01-06T10:04:00", + "2024-01-06T10:05:00", + "2024-01-06T10:06:00", + "2024-01-06T10:07:00", + "2024-01-06T10:08:00", + "2024-01-06T10:09:00", + "2024-01-06T10:10:00", + "2024-01-06T10:11:00", + "2024-01-06T10:12:00", + "2024-01-06T10:13:00", + "2024-01-06T10:14:00", + "2024-01-06T10:15:00", + "2024-01-06T10:16:00", + "2024-01-06T10:17:00", + "2024-01-06T10:18:00", + "2024-01-06T10:19:00", + "2024-01-06T10:20:00", + "2024-01-06T10:21:00", + "2024-01-06T10:22:00", + "2024-01-06T10:23:00", + "2024-01-06T10:24:00", + "2024-01-06T10:25:00", + "2024-01-06T10:26:00", + "2024-01-06T10:27:00", + "2024-01-06T10:28:00", + "2024-01-06T10:29:00", + "2024-01-06T10:30:00", + "2024-01-06T10:31:00", + "2024-01-06T10:32:00", + "2024-01-06T10:33:00", + "2024-01-06T10:34:00", + "2024-01-06T10:35:00", + "2024-01-06T10:36:00", + "2024-01-06T10:37:00", + "2024-01-06T10:38:00", + "2024-01-06T10:39:00", + "2024-01-06T10:40:00", + "2024-01-06T10:41:00", + "2024-01-06T10:42:00", + "2024-01-06T10:43:00", + "2024-01-06T10:44:00", + "2024-01-06T10:45:00", + "2024-01-06T10:46:00", + "2024-01-06T10:47:00", + "2024-01-06T10:48:00", + "2024-01-06T10:49:00", + "2024-01-06T10:50:00", + "2024-01-06T10:51:00", + "2024-01-06T10:52:00", + "2024-01-06T10:53:00", + "2024-01-06T10:54:00", + "2024-01-06T10:55:00", + "2024-01-06T10:56:00", + "2024-01-06T10:57:00", + "2024-01-06T10:58:00", + "2024-01-06T10:59:00", + "2024-01-06T11:00:00", + "2024-01-06T11:01:00", + "2024-01-06T11:02:00", + "2024-01-06T11:03:00", + "2024-01-06T11:04:00", + "2024-01-06T11:05:00", + "2024-01-06T11:06:00", + "2024-01-06T11:07:00", + "2024-01-06T11:08:00", + "2024-01-06T11:09:00", + "2024-01-06T11:10:00", + "2024-01-06T11:11:00", + "2024-01-06T11:12:00", + "2024-01-06T11:13:00", + "2024-01-06T11:14:00", + "2024-01-06T11:15:00", + "2024-01-06T11:16:00", + "2024-01-06T11:17:00", + "2024-01-06T11:18:00", + "2024-01-06T11:19:00", + "2024-01-06T11:20:00", + "2024-01-06T11:21:00", + "2024-01-06T11:22:00", + "2024-01-06T11:23:00", + "2024-01-06T11:24:00", + "2024-01-06T11:25:00", + "2024-01-06T11:26:00", + "2024-01-06T11:27:00", + "2024-01-06T11:28:00", + "2024-01-06T11:29:00", + "2024-01-06T11:30:00", + "2024-01-06T11:31:00", + "2024-01-06T11:32:00", + "2024-01-06T11:33:00", + "2024-01-06T11:34:00", + "2024-01-06T11:35:00", + "2024-01-06T11:36:00", + "2024-01-06T11:37:00", + "2024-01-06T11:38:00", + "2024-01-06T11:39:00", + "2024-01-06T11:40:00", + "2024-01-06T11:41:00", + "2024-01-06T11:42:00", + "2024-01-06T11:43:00", + "2024-01-06T11:44:00", + "2024-01-06T11:45:00", + "2024-01-06T11:46:00", + "2024-01-06T11:47:00", + "2024-01-06T11:48:00", + "2024-01-06T11:49:00", + "2024-01-06T11:50:00", + "2024-01-06T11:51:00", + "2024-01-06T11:52:00", + "2024-01-06T11:53:00", + "2024-01-06T11:54:00", + "2024-01-06T11:55:00", + "2024-01-06T11:56:00", + "2024-01-06T11:57:00", + "2024-01-06T11:58:00", + "2024-01-06T11:59:00", + "2024-01-06T12:00:00", + "2024-01-06T12:01:00", + "2024-01-06T12:02:00", + "2024-01-06T12:03:00", + "2024-01-06T12:04:00", + "2024-01-06T12:05:00", + "2024-01-06T12:06:00", + "2024-01-06T12:07:00", + "2024-01-06T12:08:00", + "2024-01-06T12:09:00", + "2024-01-06T12:10:00", + "2024-01-06T12:11:00", + "2024-01-06T12:12:00", + "2024-01-06T12:13:00", + "2024-01-06T12:14:00", + "2024-01-06T12:15:00", + "2024-01-06T12:16:00", + "2024-01-06T12:17:00", + "2024-01-06T12:18:00", + "2024-01-06T12:19:00", + "2024-01-06T12:20:00", + "2024-01-06T12:21:00", + "2024-01-06T12:22:00", + "2024-01-06T12:23:00", + "2024-01-06T12:24:00", + "2024-01-06T12:25:00", + "2024-01-06T12:26:00", + "2024-01-06T12:27:00", + "2024-01-06T12:28:00", + "2024-01-06T12:29:00", + "2024-01-06T12:30:00", + "2024-01-06T12:31:00", + "2024-01-06T12:32:00", + "2024-01-06T12:33:00", + "2024-01-06T12:34:00", + "2024-01-06T12:35:00", + "2024-01-06T12:36:00", + "2024-01-06T12:37:00", + "2024-01-06T12:38:00", + "2024-01-06T12:39:00", + "2024-01-06T12:40:00", + "2024-01-06T12:41:00", + "2024-01-06T12:42:00", + "2024-01-06T12:43:00", + "2024-01-06T12:44:00", + "2024-01-06T12:45:00", + "2024-01-06T12:46:00", + "2024-01-06T12:47:00", + "2024-01-06T12:48:00", + "2024-01-06T12:49:00", + "2024-01-06T12:50:00", + "2024-01-06T12:51:00", + "2024-01-06T12:52:00", + "2024-01-06T12:53:00", + "2024-01-06T12:54:00", + "2024-01-06T12:55:00", + "2024-01-06T12:56:00", + "2024-01-06T12:57:00", + "2024-01-06T12:58:00", + "2024-01-06T12:59:00", + "2024-01-06T13:00:00", + "2024-01-06T13:01:00", + "2024-01-06T13:02:00", + "2024-01-06T13:03:00", + "2024-01-06T13:04:00", + "2024-01-06T13:05:00", + "2024-01-06T13:06:00", + "2024-01-06T13:07:00", + "2024-01-06T13:08:00", + "2024-01-06T13:09:00", + "2024-01-06T13:10:00", + "2024-01-06T13:11:00", + "2024-01-06T13:12:00", + "2024-01-06T13:13:00", + "2024-01-06T13:14:00", + "2024-01-06T13:15:00", + "2024-01-06T13:16:00", + "2024-01-06T13:17:00", + "2024-01-06T13:18:00", + "2024-01-06T13:19:00", + "2024-01-06T13:20:00", + "2024-01-06T13:21:00", + "2024-01-06T13:22:00", + "2024-01-06T13:23:00", + "2024-01-06T13:24:00", + "2024-01-06T13:25:00", + "2024-01-06T13:26:00", + "2024-01-06T13:27:00", + "2024-01-06T13:28:00", + "2024-01-06T13:29:00", + "2024-01-06T13:30:00", + "2024-01-06T13:31:00", + "2024-01-06T13:32:00", + "2024-01-06T13:33:00", + "2024-01-06T13:34:00", + "2024-01-06T13:35:00", + "2024-01-06T13:36:00", + "2024-01-06T13:37:00", + "2024-01-06T13:38:00", + "2024-01-06T13:39:00", + "2024-01-06T13:40:00", + "2024-01-06T13:41:00", + "2024-01-06T13:42:00", + "2024-01-06T13:43:00", + "2024-01-06T13:44:00", + "2024-01-06T13:45:00", + "2024-01-06T13:46:00", + "2024-01-06T13:47:00", + "2024-01-06T13:48:00", + "2024-01-06T13:49:00", + "2024-01-06T13:50:00", + "2024-01-06T13:51:00", + "2024-01-06T13:52:00", + "2024-01-06T13:53:00", + "2024-01-06T13:54:00", + "2024-01-06T13:55:00", + "2024-01-06T13:56:00", + "2024-01-06T13:57:00", + "2024-01-06T13:58:00", + "2024-01-06T13:59:00", + "2024-01-06T14:00:00", + "2024-01-06T14:01:00", + "2024-01-06T14:02:00", + "2024-01-06T14:03:00", + "2024-01-06T14:04:00", + "2024-01-06T14:05:00", + "2024-01-06T14:06:00", + "2024-01-06T14:07:00", + "2024-01-06T14:08:00", + "2024-01-06T14:09:00", + "2024-01-06T14:10:00", + "2024-01-06T14:11:00", + "2024-01-06T14:12:00", + "2024-01-06T14:13:00", + "2024-01-06T14:14:00", + "2024-01-06T14:15:00", + "2024-01-06T14:16:00", + "2024-01-06T14:17:00", + "2024-01-06T14:18:00", + "2024-01-06T14:19:00", + "2024-01-06T14:20:00", + "2024-01-06T14:21:00", + "2024-01-06T14:22:00", + "2024-01-06T14:23:00", + "2024-01-06T14:24:00", + "2024-01-06T14:25:00", + "2024-01-06T14:26:00", + "2024-01-06T14:27:00", + "2024-01-06T14:28:00", + "2024-01-06T14:29:00", + "2024-01-06T14:30:00", + "2024-01-06T14:31:00", + "2024-01-06T14:32:00", + "2024-01-06T14:33:00", + "2024-01-06T14:34:00", + "2024-01-06T14:35:00", + "2024-01-06T14:36:00", + "2024-01-06T14:37:00", + "2024-01-06T14:38:00", + "2024-01-06T14:39:00", + "2024-01-06T14:40:00", + "2024-01-06T14:41:00", + "2024-01-06T14:42:00", + "2024-01-06T14:43:00", + "2024-01-06T14:44:00", + "2024-01-06T14:45:00", + "2024-01-06T14:46:00", + "2024-01-06T14:47:00", + "2024-01-06T14:48:00", + "2024-01-06T14:49:00", + "2024-01-06T14:50:00", + "2024-01-06T14:51:00", + "2024-01-06T14:52:00", + "2024-01-06T14:53:00", + "2024-01-06T14:54:00", + "2024-01-06T14:55:00", + "2024-01-06T14:56:00", + "2024-01-06T14:57:00", + "2024-01-06T14:58:00", + "2024-01-06T14:59:00", + "2024-01-06T15:00:00", + "2024-01-06T15:01:00", + "2024-01-06T15:02:00", + "2024-01-06T15:03:00", + "2024-01-06T15:04:00", + "2024-01-06T15:05:00", + "2024-01-06T15:06:00", + "2024-01-06T15:07:00", + "2024-01-06T15:08:00", + "2024-01-06T15:09:00", + "2024-01-06T15:10:00", + "2024-01-06T15:11:00", + "2024-01-06T15:12:00", + "2024-01-06T15:13:00", + "2024-01-06T15:14:00", + "2024-01-06T15:15:00", + "2024-01-06T15:16:00", + "2024-01-06T15:17:00", + "2024-01-06T15:18:00", + "2024-01-06T15:19:00", + "2024-01-06T15:20:00", + "2024-01-06T15:21:00", + "2024-01-06T15:22:00", + "2024-01-06T15:23:00", + "2024-01-06T15:24:00", + "2024-01-06T15:25:00", + "2024-01-06T15:26:00", + "2024-01-06T15:27:00", + "2024-01-06T15:28:00", + "2024-01-06T15:29:00", + "2024-01-06T15:30:00", + "2024-01-06T15:31:00", + "2024-01-06T15:32:00", + "2024-01-06T15:33:00", + "2024-01-06T15:34:00", + "2024-01-06T15:35:00", + "2024-01-06T15:36:00", + "2024-01-06T15:37:00", + "2024-01-06T15:38:00", + "2024-01-06T15:39:00", + "2024-01-06T15:40:00", + "2024-01-06T15:41:00", + "2024-01-06T15:42:00", + "2024-01-06T15:43:00", + "2024-01-06T15:44:00", + "2024-01-06T15:45:00", + "2024-01-06T15:46:00", + "2024-01-06T15:47:00", + "2024-01-06T15:48:00", + "2024-01-06T15:49:00", + "2024-01-06T15:50:00", + "2024-01-06T15:51:00", + "2024-01-06T15:52:00", + "2024-01-06T15:53:00", + "2024-01-06T15:54:00", + "2024-01-06T15:55:00", + "2024-01-06T15:56:00", + "2024-01-06T15:57:00", + "2024-01-06T15:58:00", + "2024-01-06T15:59:00", + "2024-01-06T16:00:00", + "2024-01-06T16:01:00", + "2024-01-06T16:02:00", + "2024-01-06T16:03:00", + "2024-01-06T16:04:00", + "2024-01-06T16:05:00", + "2024-01-06T16:06:00", + "2024-01-06T16:07:00", + "2024-01-06T16:08:00", + "2024-01-06T16:09:00", + "2024-01-06T16:10:00", + "2024-01-06T16:11:00", + "2024-01-06T16:12:00", + "2024-01-06T16:13:00", + "2024-01-06T16:14:00", + "2024-01-06T16:15:00", + "2024-01-06T16:16:00", + "2024-01-06T16:17:00", + "2024-01-06T16:18:00", + "2024-01-06T16:19:00", + "2024-01-06T16:20:00", + "2024-01-06T16:21:00", + "2024-01-06T16:22:00", + "2024-01-06T16:23:00", + "2024-01-06T16:24:00", + "2024-01-06T16:25:00", + "2024-01-06T16:26:00", + "2024-01-06T16:27:00", + "2024-01-06T16:28:00", + "2024-01-06T16:29:00", + "2024-01-06T16:30:00", + "2024-01-06T16:31:00", + "2024-01-06T16:32:00", + "2024-01-06T16:33:00", + "2024-01-06T16:34:00", + "2024-01-06T16:35:00", + "2024-01-06T16:36:00", + "2024-01-06T16:37:00", + "2024-01-06T16:38:00", + "2024-01-06T16:39:00", + "2024-01-06T16:40:00", + "2024-01-06T16:41:00", + "2024-01-06T16:42:00", + "2024-01-06T16:43:00", + "2024-01-06T16:44:00", + "2024-01-06T16:45:00", + "2024-01-06T16:46:00", + "2024-01-06T16:47:00", + "2024-01-06T16:48:00", + "2024-01-06T16:49:00", + "2024-01-06T16:50:00", + "2024-01-06T16:51:00", + "2024-01-06T16:52:00", + "2024-01-06T16:53:00", + "2024-01-06T16:54:00", + "2024-01-06T16:55:00", + "2024-01-06T16:56:00", + "2024-01-06T16:57:00", + "2024-01-06T16:58:00", + "2024-01-06T16:59:00", + "2024-01-06T17:00:00", + "2024-01-06T17:01:00", + "2024-01-06T17:02:00", + "2024-01-06T17:03:00", + "2024-01-06T17:04:00", + "2024-01-06T17:05:00", + "2024-01-06T17:06:00", + "2024-01-06T17:07:00", + "2024-01-06T17:08:00", + "2024-01-06T17:09:00", + "2024-01-06T17:10:00", + "2024-01-06T17:11:00", + "2024-01-06T17:12:00", + "2024-01-06T17:13:00", + "2024-01-06T17:14:00", + "2024-01-06T17:15:00", + "2024-01-06T17:16:00", + "2024-01-06T17:17:00", + "2024-01-06T17:18:00", + "2024-01-06T17:19:00", + "2024-01-06T17:20:00", + "2024-01-06T17:21:00", + "2024-01-06T17:22:00", + "2024-01-06T17:23:00", + "2024-01-06T17:24:00", + "2024-01-06T17:25:00", + "2024-01-06T17:26:00", + "2024-01-06T17:27:00", + "2024-01-06T17:28:00", + "2024-01-06T17:29:00", + "2024-01-06T17:30:00", + "2024-01-06T17:31:00", + "2024-01-06T17:32:00", + "2024-01-06T17:33:00", + "2024-01-06T17:34:00", + "2024-01-06T17:35:00", + "2024-01-06T17:36:00", + "2024-01-06T17:37:00", + "2024-01-06T17:38:00", + "2024-01-06T17:39:00", + "2024-01-06T17:40:00", + "2024-01-06T17:41:00", + "2024-01-06T17:42:00", + "2024-01-06T17:43:00", + "2024-01-06T17:44:00", + "2024-01-06T17:45:00", + "2024-01-06T17:46:00", + "2024-01-06T17:47:00", + "2024-01-06T17:48:00", + "2024-01-06T17:49:00", + "2024-01-06T17:50:00", + "2024-01-06T17:51:00", + "2024-01-06T17:52:00", + "2024-01-06T17:53:00", + "2024-01-06T17:54:00", + "2024-01-06T17:55:00", + "2024-01-06T17:56:00", + "2024-01-06T17:57:00", + "2024-01-06T17:58:00", + "2024-01-06T17:59:00", + "2024-01-06T18:00:00", + "2024-01-06T18:01:00", + "2024-01-06T18:02:00", + "2024-01-06T18:03:00", + "2024-01-06T18:04:00", + "2024-01-06T18:05:00", + "2024-01-06T18:06:00", + "2024-01-06T18:07:00", + "2024-01-06T18:08:00", + "2024-01-06T18:09:00", + "2024-01-06T18:10:00", + "2024-01-06T18:11:00", + "2024-01-06T18:12:00", + "2024-01-06T18:13:00", + "2024-01-06T18:14:00", + "2024-01-06T18:15:00", + "2024-01-06T18:16:00", + "2024-01-06T18:17:00", + "2024-01-06T18:18:00", + "2024-01-06T18:19:00", + "2024-01-06T18:20:00", + "2024-01-06T18:21:00", + "2024-01-06T18:22:00", + "2024-01-06T18:23:00", + "2024-01-06T18:24:00", + "2024-01-06T18:25:00", + "2024-01-06T18:26:00", + "2024-01-06T18:27:00", + "2024-01-06T18:28:00", + "2024-01-06T18:29:00", + "2024-01-06T18:30:00", + "2024-01-06T18:31:00", + "2024-01-06T18:32:00", + "2024-01-06T18:33:00", + "2024-01-06T18:34:00", + "2024-01-06T18:35:00", + "2024-01-06T18:36:00", + "2024-01-06T18:37:00", + "2024-01-06T18:38:00", + "2024-01-06T18:39:00", + "2024-01-06T18:40:00", + "2024-01-06T18:41:00", + "2024-01-06T18:42:00", + "2024-01-06T18:43:00", + "2024-01-06T18:44:00", + "2024-01-06T18:45:00", + "2024-01-06T18:46:00", + "2024-01-06T18:47:00", + "2024-01-06T18:48:00", + "2024-01-06T18:49:00", + "2024-01-06T18:50:00", + "2024-01-06T18:51:00", + "2024-01-06T18:52:00", + "2024-01-06T18:53:00", + "2024-01-06T18:54:00", + "2024-01-06T18:55:00", + "2024-01-06T18:56:00", + "2024-01-06T18:57:00", + "2024-01-06T18:58:00", + "2024-01-06T18:59:00", + "2024-01-06T19:00:00", + "2024-01-06T19:01:00", + "2024-01-06T19:02:00", + "2024-01-06T19:03:00", + "2024-01-06T19:04:00", + "2024-01-06T19:05:00", + "2024-01-06T19:06:00", + "2024-01-06T19:07:00", + "2024-01-06T19:08:00", + "2024-01-06T19:09:00", + "2024-01-06T19:10:00", + "2024-01-06T19:11:00", + "2024-01-06T19:12:00", + "2024-01-06T19:13:00", + "2024-01-06T19:14:00", + "2024-01-06T19:15:00", + "2024-01-06T19:16:00", + "2024-01-06T19:17:00", + "2024-01-06T19:18:00", + "2024-01-06T19:19:00", + "2024-01-06T19:20:00", + "2024-01-06T19:21:00", + "2024-01-06T19:22:00", + "2024-01-06T19:23:00", + "2024-01-06T19:24:00", + "2024-01-06T19:25:00", + "2024-01-06T19:26:00", + "2024-01-06T19:27:00", + "2024-01-06T19:28:00", + "2024-01-06T19:29:00", + "2024-01-06T19:30:00", + "2024-01-06T19:31:00", + "2024-01-06T19:32:00", + "2024-01-06T19:33:00", + "2024-01-06T19:34:00", + "2024-01-06T19:35:00", + "2024-01-06T19:36:00", + "2024-01-06T19:37:00", + "2024-01-06T19:38:00", + "2024-01-06T19:39:00", + "2024-01-06T19:40:00", + "2024-01-06T19:41:00", + "2024-01-06T19:42:00", + "2024-01-06T19:43:00", + "2024-01-06T19:44:00", + "2024-01-06T19:45:00", + "2024-01-06T19:46:00", + "2024-01-06T19:47:00", + "2024-01-06T19:48:00", + "2024-01-06T19:49:00", + "2024-01-06T19:50:00", + "2024-01-06T19:51:00", + "2024-01-06T19:52:00", + "2024-01-06T19:53:00", + "2024-01-06T19:54:00", + "2024-01-06T19:55:00", + "2024-01-06T19:56:00", + "2024-01-06T19:57:00", + "2024-01-06T19:58:00", + "2024-01-06T19:59:00", + "2024-01-06T20:00:00", + "2024-01-06T20:01:00", + "2024-01-06T20:02:00", + "2024-01-06T20:03:00", + "2024-01-06T20:04:00", + "2024-01-06T20:05:00", + "2024-01-06T20:06:00", + "2024-01-06T20:07:00", + "2024-01-06T20:08:00", + "2024-01-06T20:09:00", + "2024-01-06T20:10:00", + "2024-01-06T20:11:00", + "2024-01-06T20:12:00", + "2024-01-06T20:13:00", + "2024-01-06T20:14:00", + "2024-01-06T20:15:00", + "2024-01-06T20:16:00", + "2024-01-06T20:17:00", + "2024-01-06T20:18:00", + "2024-01-06T20:19:00", + "2024-01-06T20:20:00", + "2024-01-06T20:21:00", + "2024-01-06T20:22:00", + "2024-01-06T20:23:00", + "2024-01-06T20:24:00", + "2024-01-06T20:25:00", + "2024-01-06T20:26:00", + "2024-01-06T20:27:00", + "2024-01-06T20:28:00", + "2024-01-06T20:29:00", + "2024-01-06T20:30:00", + "2024-01-06T20:31:00", + "2024-01-06T20:32:00", + "2024-01-06T20:33:00", + "2024-01-06T20:34:00", + "2024-01-06T20:35:00", + "2024-01-06T20:36:00", + "2024-01-06T20:37:00", + "2024-01-06T20:38:00", + "2024-01-06T20:39:00", + "2024-01-06T20:40:00", + "2024-01-06T20:41:00", + "2024-01-06T20:42:00", + "2024-01-06T20:43:00", + "2024-01-06T20:44:00", + "2024-01-06T20:45:00", + "2024-01-06T20:46:00", + "2024-01-06T20:47:00", + "2024-01-06T20:48:00", + "2024-01-06T20:49:00", + "2024-01-06T20:50:00", + "2024-01-06T20:51:00", + "2024-01-06T20:52:00", + "2024-01-06T20:53:00", + "2024-01-06T20:54:00", + "2024-01-06T20:55:00", + "2024-01-06T20:56:00", + "2024-01-06T20:57:00", + "2024-01-06T20:58:00", + "2024-01-06T20:59:00", + "2024-01-06T21:00:00", + "2024-01-06T21:01:00", + "2024-01-06T21:02:00", + "2024-01-06T21:03:00", + "2024-01-06T21:04:00", + "2024-01-06T21:05:00", + "2024-01-06T21:06:00", + "2024-01-06T21:07:00", + "2024-01-06T21:08:00", + "2024-01-06T21:09:00", + "2024-01-06T21:10:00", + "2024-01-06T21:11:00", + "2024-01-06T21:12:00", + "2024-01-06T21:13:00", + "2024-01-06T21:14:00", + "2024-01-06T21:15:00", + "2024-01-06T21:16:00", + "2024-01-06T21:17:00", + "2024-01-06T21:18:00", + "2024-01-06T21:19:00", + "2024-01-06T21:20:00", + "2024-01-06T21:21:00", + "2024-01-06T21:22:00", + "2024-01-06T21:23:00", + "2024-01-06T21:24:00", + "2024-01-06T21:25:00", + "2024-01-06T21:26:00", + "2024-01-06T21:27:00", + "2024-01-06T21:28:00", + "2024-01-06T21:29:00", + "2024-01-06T21:30:00", + "2024-01-06T21:31:00", + "2024-01-06T21:32:00", + "2024-01-06T21:33:00", + "2024-01-06T21:34:00", + "2024-01-06T21:35:00", + "2024-01-06T21:36:00", + "2024-01-06T21:37:00", + "2024-01-06T21:38:00", + "2024-01-06T21:39:00", + "2024-01-06T21:40:00", + "2024-01-06T21:41:00", + "2024-01-06T21:42:00", + "2024-01-06T21:43:00", + "2024-01-06T21:44:00", + "2024-01-06T21:45:00", + "2024-01-06T21:46:00", + "2024-01-06T21:47:00", + "2024-01-06T21:48:00", + "2024-01-06T21:49:00", + "2024-01-06T21:50:00", + "2024-01-06T21:51:00", + "2024-01-06T21:52:00", + "2024-01-06T21:53:00", + "2024-01-06T21:54:00", + "2024-01-06T21:55:00", + "2024-01-06T21:56:00", + "2024-01-06T21:57:00", + "2024-01-06T21:58:00", + "2024-01-06T21:59:00", + "2024-01-06T22:00:00", + "2024-01-06T22:01:00", + "2024-01-06T22:02:00", + "2024-01-06T22:03:00", + "2024-01-06T22:04:00", + "2024-01-06T22:05:00", + "2024-01-06T22:06:00", + "2024-01-06T22:07:00", + "2024-01-06T22:08:00", + "2024-01-06T22:09:00", + "2024-01-06T22:10:00", + "2024-01-06T22:11:00", + "2024-01-06T22:12:00", + "2024-01-06T22:13:00", + "2024-01-06T22:14:00", + "2024-01-06T22:15:00", + "2024-01-06T22:16:00", + "2024-01-06T22:17:00", + "2024-01-06T22:18:00", + "2024-01-06T22:19:00", + "2024-01-06T22:20:00", + "2024-01-06T22:21:00", + "2024-01-06T22:22:00", + "2024-01-06T22:23:00", + "2024-01-06T22:24:00", + "2024-01-06T22:25:00", + "2024-01-06T22:26:00", + "2024-01-06T22:27:00", + "2024-01-06T22:28:00", + "2024-01-06T22:29:00", + "2024-01-06T22:30:00", + "2024-01-06T22:31:00", + "2024-01-06T22:32:00", + "2024-01-06T22:33:00", + "2024-01-06T22:34:00", + "2024-01-06T22:35:00", + "2024-01-06T22:36:00", + "2024-01-06T22:37:00", + "2024-01-06T22:38:00", + "2024-01-06T22:39:00", + "2024-01-06T22:40:00", + "2024-01-06T22:41:00", + "2024-01-06T22:42:00", + "2024-01-06T22:43:00", + "2024-01-06T22:44:00", + "2024-01-06T22:45:00", + "2024-01-06T22:46:00", + "2024-01-06T22:47:00", + "2024-01-06T22:48:00", + "2024-01-06T22:49:00", + "2024-01-06T22:50:00", + "2024-01-06T22:51:00", + "2024-01-06T22:52:00", + "2024-01-06T22:53:00", + "2024-01-06T22:54:00", + "2024-01-06T22:55:00", + "2024-01-06T22:56:00", + "2024-01-06T22:57:00", + "2024-01-06T22:58:00", + "2024-01-06T22:59:00", + "2024-01-06T23:00:00", + "2024-01-06T23:01:00", + "2024-01-06T23:02:00", + "2024-01-06T23:03:00", + "2024-01-06T23:04:00", + "2024-01-06T23:05:00", + "2024-01-06T23:06:00", + "2024-01-06T23:07:00", + "2024-01-06T23:08:00", + "2024-01-06T23:09:00", + "2024-01-06T23:10:00", + "2024-01-06T23:11:00", + "2024-01-06T23:12:00", + "2024-01-06T23:13:00", + "2024-01-06T23:14:00", + "2024-01-06T23:15:00", + "2024-01-06T23:16:00", + "2024-01-06T23:17:00", + "2024-01-06T23:18:00", + "2024-01-06T23:19:00", + "2024-01-06T23:20:00", + "2024-01-06T23:21:00", + "2024-01-06T23:22:00", + "2024-01-06T23:23:00", + "2024-01-06T23:24:00", + "2024-01-06T23:25:00", + "2024-01-06T23:26:00", + "2024-01-06T23:27:00", + "2024-01-06T23:28:00", + "2024-01-06T23:29:00", + "2024-01-06T23:30:00", + "2024-01-06T23:31:00", + "2024-01-06T23:32:00", + "2024-01-06T23:33:00", + "2024-01-06T23:34:00", + "2024-01-06T23:35:00", + "2024-01-06T23:36:00", + "2024-01-06T23:37:00", + "2024-01-06T23:38:00", + "2024-01-06T23:39:00", + "2024-01-06T23:40:00", + "2024-01-06T23:41:00", + "2024-01-06T23:42:00", + "2024-01-06T23:43:00", + "2024-01-06T23:44:00", + "2024-01-06T23:45:00", + "2024-01-06T23:46:00", + "2024-01-06T23:47:00", + "2024-01-06T23:48:00", + "2024-01-06T23:49:00", + "2024-01-06T23:50:00", + "2024-01-06T23:51:00", + "2024-01-06T23:52:00", + "2024-01-06T23:53:00", + "2024-01-06T23:54:00", + "2024-01-06T23:55:00", + "2024-01-06T23:56:00", + "2024-01-06T23:57:00", + "2024-01-06T23:58:00", + "2024-01-06T23:59:00", + "2024-01-07T00:00:00", + "2024-01-07T00:01:00", + "2024-01-07T00:02:00", + "2024-01-07T00:03:00", + "2024-01-07T00:04:00", + "2024-01-07T00:05:00", + "2024-01-07T00:06:00", + "2024-01-07T00:07:00", + "2024-01-07T00:08:00", + "2024-01-07T00:09:00", + "2024-01-07T00:10:00", + "2024-01-07T00:11:00", + "2024-01-07T00:12:00", + "2024-01-07T00:13:00", + "2024-01-07T00:14:00", + "2024-01-07T00:15:00", + "2024-01-07T00:16:00", + "2024-01-07T00:17:00", + "2024-01-07T00:18:00", + "2024-01-07T00:19:00", + "2024-01-07T00:20:00", + "2024-01-07T00:21:00", + "2024-01-07T00:22:00", + "2024-01-07T00:23:00", + "2024-01-07T00:24:00", + "2024-01-07T00:25:00", + "2024-01-07T00:26:00", + "2024-01-07T00:27:00", + "2024-01-07T00:28:00", + "2024-01-07T00:29:00", + "2024-01-07T00:30:00", + "2024-01-07T00:31:00", + "2024-01-07T00:32:00", + "2024-01-07T00:33:00", + "2024-01-07T00:34:00", + "2024-01-07T00:35:00", + "2024-01-07T00:36:00", + "2024-01-07T00:37:00", + "2024-01-07T00:38:00", + "2024-01-07T00:39:00", + "2024-01-07T00:40:00", + "2024-01-07T00:41:00", + "2024-01-07T00:42:00", + "2024-01-07T00:43:00", + "2024-01-07T00:44:00", + "2024-01-07T00:45:00", + "2024-01-07T00:46:00", + "2024-01-07T00:47:00", + "2024-01-07T00:48:00", + "2024-01-07T00:49:00", + "2024-01-07T00:50:00", + "2024-01-07T00:51:00", + "2024-01-07T00:52:00", + "2024-01-07T00:53:00", + "2024-01-07T00:54:00", + "2024-01-07T00:55:00", + "2024-01-07T00:56:00", + "2024-01-07T00:57:00", + "2024-01-07T00:58:00", + "2024-01-07T00:59:00", + "2024-01-07T01:00:00", + "2024-01-07T01:01:00", + "2024-01-07T01:02:00", + "2024-01-07T01:03:00", + "2024-01-07T01:04:00", + "2024-01-07T01:05:00", + "2024-01-07T01:06:00", + "2024-01-07T01:07:00", + "2024-01-07T01:08:00", + "2024-01-07T01:09:00", + "2024-01-07T01:10:00", + "2024-01-07T01:11:00", + "2024-01-07T01:12:00", + "2024-01-07T01:13:00", + "2024-01-07T01:14:00", + "2024-01-07T01:15:00", + "2024-01-07T01:16:00", + "2024-01-07T01:17:00", + "2024-01-07T01:18:00", + "2024-01-07T01:19:00", + "2024-01-07T01:20:00", + "2024-01-07T01:21:00", + "2024-01-07T01:22:00", + "2024-01-07T01:23:00", + "2024-01-07T01:24:00", + "2024-01-07T01:25:00", + "2024-01-07T01:26:00", + "2024-01-07T01:27:00", + "2024-01-07T01:28:00", + "2024-01-07T01:29:00", + "2024-01-07T01:30:00", + "2024-01-07T01:31:00", + "2024-01-07T01:32:00", + "2024-01-07T01:33:00", + "2024-01-07T01:34:00", + "2024-01-07T01:35:00", + "2024-01-07T01:36:00", + "2024-01-07T01:37:00", + "2024-01-07T01:38:00", + "2024-01-07T01:39:00", + "2024-01-07T01:40:00", + "2024-01-07T01:41:00", + "2024-01-07T01:42:00", + "2024-01-07T01:43:00", + "2024-01-07T01:44:00", + "2024-01-07T01:45:00", + "2024-01-07T01:46:00", + "2024-01-07T01:47:00", + "2024-01-07T01:48:00", + "2024-01-07T01:49:00", + "2024-01-07T01:50:00", + "2024-01-07T01:51:00", + "2024-01-07T01:52:00", + "2024-01-07T01:53:00", + "2024-01-07T01:54:00", + "2024-01-07T01:55:00", + "2024-01-07T01:56:00", + "2024-01-07T01:57:00", + "2024-01-07T01:58:00", + "2024-01-07T01:59:00", + "2024-01-07T02:00:00", + "2024-01-07T02:01:00", + "2024-01-07T02:02:00", + "2024-01-07T02:03:00", + "2024-01-07T02:04:00", + "2024-01-07T02:05:00", + "2024-01-07T02:06:00", + "2024-01-07T02:07:00", + "2024-01-07T02:08:00", + "2024-01-07T02:09:00", + "2024-01-07T02:10:00", + "2024-01-07T02:11:00", + "2024-01-07T02:12:00", + "2024-01-07T02:13:00", + "2024-01-07T02:14:00", + "2024-01-07T02:15:00", + "2024-01-07T02:16:00", + "2024-01-07T02:17:00", + "2024-01-07T02:18:00", + "2024-01-07T02:19:00", + "2024-01-07T02:20:00", + "2024-01-07T02:21:00", + "2024-01-07T02:22:00", + "2024-01-07T02:23:00", + "2024-01-07T02:24:00", + "2024-01-07T02:25:00", + "2024-01-07T02:26:00", + "2024-01-07T02:27:00", + "2024-01-07T02:28:00", + "2024-01-07T02:29:00", + "2024-01-07T02:30:00", + "2024-01-07T02:31:00", + "2024-01-07T02:32:00", + "2024-01-07T02:33:00", + "2024-01-07T02:34:00", + "2024-01-07T02:35:00", + "2024-01-07T02:36:00", + "2024-01-07T02:37:00", + "2024-01-07T02:38:00", + "2024-01-07T02:39:00", + "2024-01-07T02:40:00", + "2024-01-07T02:41:00", + "2024-01-07T02:42:00", + "2024-01-07T02:43:00", + "2024-01-07T02:44:00", + "2024-01-07T02:45:00", + "2024-01-07T02:46:00", + "2024-01-07T02:47:00", + "2024-01-07T02:48:00", + "2024-01-07T02:49:00", + "2024-01-07T02:50:00", + "2024-01-07T02:51:00", + "2024-01-07T02:52:00", + "2024-01-07T02:53:00", + "2024-01-07T02:54:00", + "2024-01-07T02:55:00", + "2024-01-07T02:56:00", + "2024-01-07T02:57:00", + "2024-01-07T02:58:00", + "2024-01-07T02:59:00", + "2024-01-07T03:00:00", + "2024-01-07T03:01:00", + "2024-01-07T03:02:00", + "2024-01-07T03:03:00", + "2024-01-07T03:04:00", + "2024-01-07T03:05:00", + "2024-01-07T03:06:00", + "2024-01-07T03:07:00", + "2024-01-07T03:08:00", + "2024-01-07T03:09:00", + "2024-01-07T03:10:00", + "2024-01-07T03:11:00", + "2024-01-07T03:12:00", + "2024-01-07T03:13:00", + "2024-01-07T03:14:00", + "2024-01-07T03:15:00", + "2024-01-07T03:16:00", + "2024-01-07T03:17:00", + "2024-01-07T03:18:00", + "2024-01-07T03:19:00", + "2024-01-07T03:20:00", + "2024-01-07T03:21:00", + "2024-01-07T03:22:00", + "2024-01-07T03:23:00", + "2024-01-07T03:24:00", + "2024-01-07T03:25:00", + "2024-01-07T03:26:00", + "2024-01-07T03:27:00", + "2024-01-07T03:28:00", + "2024-01-07T03:29:00", + "2024-01-07T03:30:00", + "2024-01-07T03:31:00", + "2024-01-07T03:32:00", + "2024-01-07T03:33:00", + "2024-01-07T03:34:00", + "2024-01-07T03:35:00", + "2024-01-07T03:36:00", + "2024-01-07T03:37:00", + "2024-01-07T03:38:00", + "2024-01-07T03:39:00", + "2024-01-07T03:40:00", + "2024-01-07T03:41:00", + "2024-01-07T03:42:00", + "2024-01-07T03:43:00", + "2024-01-07T03:44:00", + "2024-01-07T03:45:00", + "2024-01-07T03:46:00", + "2024-01-07T03:47:00", + "2024-01-07T03:48:00", + "2024-01-07T03:49:00", + "2024-01-07T03:50:00", + "2024-01-07T03:51:00", + "2024-01-07T03:52:00", + "2024-01-07T03:53:00", + "2024-01-07T03:54:00", + "2024-01-07T03:55:00", + "2024-01-07T03:56:00", + "2024-01-07T03:57:00", + "2024-01-07T03:58:00", + "2024-01-07T03:59:00", + "2024-01-07T04:00:00", + "2024-01-07T04:01:00", + "2024-01-07T04:02:00", + "2024-01-07T04:03:00", + "2024-01-07T04:04:00", + "2024-01-07T04:05:00", + "2024-01-07T04:06:00", + "2024-01-07T04:07:00", + "2024-01-07T04:08:00", + "2024-01-07T04:09:00", + "2024-01-07T04:10:00", + "2024-01-07T04:11:00", + "2024-01-07T04:12:00", + "2024-01-07T04:13:00", + "2024-01-07T04:14:00", + "2024-01-07T04:15:00", + "2024-01-07T04:16:00", + "2024-01-07T04:17:00", + "2024-01-07T04:18:00", + "2024-01-07T04:19:00", + "2024-01-07T04:20:00", + "2024-01-07T04:21:00", + "2024-01-07T04:22:00", + "2024-01-07T04:23:00", + "2024-01-07T04:24:00", + "2024-01-07T04:25:00", + "2024-01-07T04:26:00", + "2024-01-07T04:27:00", + "2024-01-07T04:28:00", + "2024-01-07T04:29:00", + "2024-01-07T04:30:00", + "2024-01-07T04:31:00", + "2024-01-07T04:32:00", + "2024-01-07T04:33:00", + "2024-01-07T04:34:00", + "2024-01-07T04:35:00", + "2024-01-07T04:36:00", + "2024-01-07T04:37:00", + "2024-01-07T04:38:00", + "2024-01-07T04:39:00", + "2024-01-07T04:40:00", + "2024-01-07T04:41:00", + "2024-01-07T04:42:00", + "2024-01-07T04:43:00", + "2024-01-07T04:44:00", + "2024-01-07T04:45:00", + "2024-01-07T04:46:00", + "2024-01-07T04:47:00", + "2024-01-07T04:48:00", + "2024-01-07T04:49:00", + "2024-01-07T04:50:00", + "2024-01-07T04:51:00", + "2024-01-07T04:52:00", + "2024-01-07T04:53:00", + "2024-01-07T04:54:00", + "2024-01-07T04:55:00", + "2024-01-07T04:56:00", + "2024-01-07T04:57:00", + "2024-01-07T04:58:00", + "2024-01-07T04:59:00", + "2024-01-07T05:00:00", + "2024-01-07T05:01:00", + "2024-01-07T05:02:00", + "2024-01-07T05:03:00", + "2024-01-07T05:04:00", + "2024-01-07T05:05:00", + "2024-01-07T05:06:00", + "2024-01-07T05:07:00", + "2024-01-07T05:08:00", + "2024-01-07T05:09:00", + "2024-01-07T05:10:00", + "2024-01-07T05:11:00", + "2024-01-07T05:12:00", + "2024-01-07T05:13:00", + "2024-01-07T05:14:00", + "2024-01-07T05:15:00", + "2024-01-07T05:16:00", + "2024-01-07T05:17:00", + "2024-01-07T05:18:00", + "2024-01-07T05:19:00", + "2024-01-07T05:20:00", + "2024-01-07T05:21:00", + "2024-01-07T05:22:00", + "2024-01-07T05:23:00", + "2024-01-07T05:24:00", + "2024-01-07T05:25:00", + "2024-01-07T05:26:00", + "2024-01-07T05:27:00", + "2024-01-07T05:28:00", + "2024-01-07T05:29:00", + "2024-01-07T05:30:00", + "2024-01-07T05:31:00", + "2024-01-07T05:32:00", + "2024-01-07T05:33:00", + "2024-01-07T05:34:00", + "2024-01-07T05:35:00", + "2024-01-07T05:36:00", + "2024-01-07T05:37:00", + "2024-01-07T05:38:00", + "2024-01-07T05:39:00", + "2024-01-07T05:40:00", + "2024-01-07T05:41:00", + "2024-01-07T05:42:00", + "2024-01-07T05:43:00", + "2024-01-07T05:44:00", + "2024-01-07T05:45:00", + "2024-01-07T05:46:00", + "2024-01-07T05:47:00", + "2024-01-07T05:48:00", + "2024-01-07T05:49:00", + "2024-01-07T05:50:00", + "2024-01-07T05:51:00", + "2024-01-07T05:52:00", + "2024-01-07T05:53:00", + "2024-01-07T05:54:00", + "2024-01-07T05:55:00", + "2024-01-07T05:56:00", + "2024-01-07T05:57:00", + "2024-01-07T05:58:00", + "2024-01-07T05:59:00", + "2024-01-07T06:00:00", + "2024-01-07T06:01:00", + "2024-01-07T06:02:00", + "2024-01-07T06:03:00", + "2024-01-07T06:04:00", + "2024-01-07T06:05:00", + "2024-01-07T06:06:00", + "2024-01-07T06:07:00", + "2024-01-07T06:08:00", + "2024-01-07T06:09:00", + "2024-01-07T06:10:00", + "2024-01-07T06:11:00", + "2024-01-07T06:12:00", + "2024-01-07T06:13:00", + "2024-01-07T06:14:00", + "2024-01-07T06:15:00", + "2024-01-07T06:16:00", + "2024-01-07T06:17:00", + "2024-01-07T06:18:00", + "2024-01-07T06:19:00", + "2024-01-07T06:20:00", + "2024-01-07T06:21:00", + "2024-01-07T06:22:00", + "2024-01-07T06:23:00", + "2024-01-07T06:24:00", + "2024-01-07T06:25:00", + "2024-01-07T06:26:00", + "2024-01-07T06:27:00", + "2024-01-07T06:28:00", + "2024-01-07T06:29:00", + "2024-01-07T06:30:00", + "2024-01-07T06:31:00", + "2024-01-07T06:32:00", + "2024-01-07T06:33:00", + "2024-01-07T06:34:00", + "2024-01-07T06:35:00", + "2024-01-07T06:36:00", + "2024-01-07T06:37:00", + "2024-01-07T06:38:00", + "2024-01-07T06:39:00", + "2024-01-07T06:40:00", + "2024-01-07T06:41:00", + "2024-01-07T06:42:00", + "2024-01-07T06:43:00", + "2024-01-07T06:44:00", + "2024-01-07T06:45:00", + "2024-01-07T06:46:00", + "2024-01-07T06:47:00", + "2024-01-07T06:48:00", + "2024-01-07T06:49:00", + "2024-01-07T06:50:00", + "2024-01-07T06:51:00", + "2024-01-07T06:52:00", + "2024-01-07T06:53:00", + "2024-01-07T06:54:00", + "2024-01-07T06:55:00", + "2024-01-07T06:56:00", + "2024-01-07T06:57:00", + "2024-01-07T06:58:00", + "2024-01-07T06:59:00", + "2024-01-07T07:00:00", + "2024-01-07T07:01:00", + "2024-01-07T07:02:00", + "2024-01-07T07:03:00", + "2024-01-07T07:04:00", + "2024-01-07T07:05:00", + "2024-01-07T07:06:00", + "2024-01-07T07:07:00", + "2024-01-07T07:08:00", + "2024-01-07T07:09:00", + "2024-01-07T07:10:00", + "2024-01-07T07:11:00", + "2024-01-07T07:12:00", + "2024-01-07T07:13:00", + "2024-01-07T07:14:00", + "2024-01-07T07:15:00", + "2024-01-07T07:16:00", + "2024-01-07T07:17:00", + "2024-01-07T07:18:00", + "2024-01-07T07:19:00", + "2024-01-07T07:20:00", + "2024-01-07T07:21:00", + "2024-01-07T07:22:00", + "2024-01-07T07:23:00", + "2024-01-07T07:24:00", + "2024-01-07T07:25:00", + "2024-01-07T07:26:00", + "2024-01-07T07:27:00", + "2024-01-07T07:28:00", + "2024-01-07T07:29:00", + "2024-01-07T07:30:00", + "2024-01-07T07:31:00", + "2024-01-07T07:32:00", + "2024-01-07T07:33:00", + "2024-01-07T07:34:00", + "2024-01-07T07:35:00", + "2024-01-07T07:36:00", + "2024-01-07T07:37:00", + "2024-01-07T07:38:00", + "2024-01-07T07:39:00", + "2024-01-07T07:40:00", + "2024-01-07T07:41:00", + "2024-01-07T07:42:00", + "2024-01-07T07:43:00", + "2024-01-07T07:44:00", + "2024-01-07T07:45:00", + "2024-01-07T07:46:00", + "2024-01-07T07:47:00", + "2024-01-07T07:48:00", + "2024-01-07T07:49:00", + "2024-01-07T07:50:00", + "2024-01-07T07:51:00", + "2024-01-07T07:52:00", + "2024-01-07T07:53:00", + "2024-01-07T07:54:00", + "2024-01-07T07:55:00", + "2024-01-07T07:56:00", + "2024-01-07T07:57:00", + "2024-01-07T07:58:00", + "2024-01-07T07:59:00", + "2024-01-07T08:00:00", + "2024-01-07T08:01:00", + "2024-01-07T08:02:00", + "2024-01-07T08:03:00", + "2024-01-07T08:04:00", + "2024-01-07T08:05:00", + "2024-01-07T08:06:00", + "2024-01-07T08:07:00", + "2024-01-07T08:08:00", + "2024-01-07T08:09:00", + "2024-01-07T08:10:00", + "2024-01-07T08:11:00", + "2024-01-07T08:12:00", + "2024-01-07T08:13:00", + "2024-01-07T08:14:00", + "2024-01-07T08:15:00", + "2024-01-07T08:16:00", + "2024-01-07T08:17:00", + "2024-01-07T08:18:00", + "2024-01-07T08:19:00", + "2024-01-07T08:20:00", + "2024-01-07T08:21:00", + "2024-01-07T08:22:00", + "2024-01-07T08:23:00", + "2024-01-07T08:24:00", + "2024-01-07T08:25:00", + "2024-01-07T08:26:00", + "2024-01-07T08:27:00", + "2024-01-07T08:28:00", + "2024-01-07T08:29:00", + "2024-01-07T08:30:00", + "2024-01-07T08:31:00", + "2024-01-07T08:32:00", + "2024-01-07T08:33:00", + "2024-01-07T08:34:00", + "2024-01-07T08:35:00", + "2024-01-07T08:36:00", + "2024-01-07T08:37:00", + "2024-01-07T08:38:00", + "2024-01-07T08:39:00", + "2024-01-07T08:40:00", + "2024-01-07T08:41:00", + "2024-01-07T08:42:00", + "2024-01-07T08:43:00", + "2024-01-07T08:44:00", + "2024-01-07T08:45:00", + "2024-01-07T08:46:00", + "2024-01-07T08:47:00", + "2024-01-07T08:48:00", + "2024-01-07T08:49:00", + "2024-01-07T08:50:00", + "2024-01-07T08:51:00", + "2024-01-07T08:52:00", + "2024-01-07T08:53:00", + "2024-01-07T08:54:00", + "2024-01-07T08:55:00", + "2024-01-07T08:56:00", + "2024-01-07T08:57:00", + "2024-01-07T08:58:00", + "2024-01-07T08:59:00", + "2024-01-07T09:00:00", + "2024-01-07T09:01:00", + "2024-01-07T09:02:00", + "2024-01-07T09:03:00", + "2024-01-07T09:04:00", + "2024-01-07T09:05:00", + "2024-01-07T09:06:00", + "2024-01-07T09:07:00", + "2024-01-07T09:08:00", + "2024-01-07T09:09:00", + "2024-01-07T09:10:00", + "2024-01-07T09:11:00", + "2024-01-07T09:12:00", + "2024-01-07T09:13:00", + "2024-01-07T09:14:00", + "2024-01-07T09:15:00", + "2024-01-07T09:16:00", + "2024-01-07T09:17:00", + "2024-01-07T09:18:00", + "2024-01-07T09:19:00", + "2024-01-07T09:20:00", + "2024-01-07T09:21:00", + "2024-01-07T09:22:00", + "2024-01-07T09:23:00", + "2024-01-07T09:24:00", + "2024-01-07T09:25:00", + "2024-01-07T09:26:00", + "2024-01-07T09:27:00", + "2024-01-07T09:28:00", + "2024-01-07T09:29:00", + "2024-01-07T09:30:00", + "2024-01-07T09:31:00", + "2024-01-07T09:32:00", + "2024-01-07T09:33:00", + "2024-01-07T09:34:00", + "2024-01-07T09:35:00", + "2024-01-07T09:36:00", + "2024-01-07T09:37:00", + "2024-01-07T09:38:00", + "2024-01-07T09:39:00", + "2024-01-07T09:40:00", + "2024-01-07T09:41:00", + "2024-01-07T09:42:00", + "2024-01-07T09:43:00", + "2024-01-07T09:44:00", + "2024-01-07T09:45:00", + "2024-01-07T09:46:00", + "2024-01-07T09:47:00", + "2024-01-07T09:48:00", + "2024-01-07T09:49:00", + "2024-01-07T09:50:00", + "2024-01-07T09:51:00", + "2024-01-07T09:52:00", + "2024-01-07T09:53:00", + "2024-01-07T09:54:00", + "2024-01-07T09:55:00", + "2024-01-07T09:56:00", + "2024-01-07T09:57:00", + "2024-01-07T09:58:00", + "2024-01-07T09:59:00", + "2024-01-07T10:00:00", + "2024-01-07T10:01:00", + "2024-01-07T10:02:00", + "2024-01-07T10:03:00", + "2024-01-07T10:04:00", + "2024-01-07T10:05:00", + "2024-01-07T10:06:00", + "2024-01-07T10:07:00", + "2024-01-07T10:08:00", + "2024-01-07T10:09:00", + "2024-01-07T10:10:00", + "2024-01-07T10:11:00", + "2024-01-07T10:12:00", + "2024-01-07T10:13:00", + "2024-01-07T10:14:00", + "2024-01-07T10:15:00", + "2024-01-07T10:16:00", + "2024-01-07T10:17:00", + "2024-01-07T10:18:00", + "2024-01-07T10:19:00", + "2024-01-07T10:20:00", + "2024-01-07T10:21:00", + "2024-01-07T10:22:00", + "2024-01-07T10:23:00", + "2024-01-07T10:24:00", + "2024-01-07T10:25:00", + "2024-01-07T10:26:00", + "2024-01-07T10:27:00", + "2024-01-07T10:28:00", + "2024-01-07T10:29:00", + "2024-01-07T10:30:00", + "2024-01-07T10:31:00", + "2024-01-07T10:32:00", + "2024-01-07T10:33:00", + "2024-01-07T10:34:00", + "2024-01-07T10:35:00", + "2024-01-07T10:36:00", + "2024-01-07T10:37:00", + "2024-01-07T10:38:00", + "2024-01-07T10:39:00", + "2024-01-07T10:40:00", + "2024-01-07T10:41:00", + "2024-01-07T10:42:00", + "2024-01-07T10:43:00", + "2024-01-07T10:44:00", + "2024-01-07T10:45:00", + "2024-01-07T10:46:00", + "2024-01-07T10:47:00", + "2024-01-07T10:48:00", + "2024-01-07T10:49:00", + "2024-01-07T10:50:00", + "2024-01-07T10:51:00", + "2024-01-07T10:52:00", + "2024-01-07T10:53:00", + "2024-01-07T10:54:00", + "2024-01-07T10:55:00", + "2024-01-07T10:56:00", + "2024-01-07T10:57:00", + "2024-01-07T10:58:00", + "2024-01-07T10:59:00", + "2024-01-07T11:00:00", + "2024-01-07T11:01:00", + "2024-01-07T11:02:00", + "2024-01-07T11:03:00", + "2024-01-07T11:04:00", + "2024-01-07T11:05:00", + "2024-01-07T11:06:00", + "2024-01-07T11:07:00", + "2024-01-07T11:08:00", + "2024-01-07T11:09:00", + "2024-01-07T11:10:00", + "2024-01-07T11:11:00", + "2024-01-07T11:12:00", + "2024-01-07T11:13:00", + "2024-01-07T11:14:00", + "2024-01-07T11:15:00", + "2024-01-07T11:16:00", + "2024-01-07T11:17:00", + "2024-01-07T11:18:00", + "2024-01-07T11:19:00", + "2024-01-07T11:20:00", + "2024-01-07T11:21:00", + "2024-01-07T11:22:00", + "2024-01-07T11:23:00", + "2024-01-07T11:24:00", + "2024-01-07T11:25:00", + "2024-01-07T11:26:00", + "2024-01-07T11:27:00", + "2024-01-07T11:28:00", + "2024-01-07T11:29:00", + "2024-01-07T11:30:00", + "2024-01-07T11:31:00", + "2024-01-07T11:32:00", + "2024-01-07T11:33:00", + "2024-01-07T11:34:00", + "2024-01-07T11:35:00", + "2024-01-07T11:36:00", + "2024-01-07T11:37:00", + "2024-01-07T11:38:00", + "2024-01-07T11:39:00", + "2024-01-07T11:40:00", + "2024-01-07T11:41:00", + "2024-01-07T11:42:00", + "2024-01-07T11:43:00", + "2024-01-07T11:44:00", + "2024-01-07T11:45:00", + "2024-01-07T11:46:00", + "2024-01-07T11:47:00", + "2024-01-07T11:48:00", + "2024-01-07T11:49:00", + "2024-01-07T11:50:00", + "2024-01-07T11:51:00", + "2024-01-07T11:52:00", + "2024-01-07T11:53:00", + "2024-01-07T11:54:00", + "2024-01-07T11:55:00", + "2024-01-07T11:56:00", + "2024-01-07T11:57:00", + "2024-01-07T11:58:00", + "2024-01-07T11:59:00", + "2024-01-07T12:00:00", + "2024-01-07T12:01:00", + "2024-01-07T12:02:00", + "2024-01-07T12:03:00", + "2024-01-07T12:04:00", + "2024-01-07T12:05:00", + "2024-01-07T12:06:00", + "2024-01-07T12:07:00", + "2024-01-07T12:08:00", + "2024-01-07T12:09:00", + "2024-01-07T12:10:00", + "2024-01-07T12:11:00", + "2024-01-07T12:12:00", + "2024-01-07T12:13:00", + "2024-01-07T12:14:00", + "2024-01-07T12:15:00", + "2024-01-07T12:16:00", + "2024-01-07T12:17:00", + "2024-01-07T12:18:00", + "2024-01-07T12:19:00", + "2024-01-07T12:20:00", + "2024-01-07T12:21:00", + "2024-01-07T12:22:00", + "2024-01-07T12:23:00", + "2024-01-07T12:24:00", + "2024-01-07T12:25:00", + "2024-01-07T12:26:00", + "2024-01-07T12:27:00", + "2024-01-07T12:28:00", + "2024-01-07T12:29:00", + "2024-01-07T12:30:00", + "2024-01-07T12:31:00", + "2024-01-07T12:32:00", + "2024-01-07T12:33:00", + "2024-01-07T12:34:00", + "2024-01-07T12:35:00", + "2024-01-07T12:36:00", + "2024-01-07T12:37:00", + "2024-01-07T12:38:00", + "2024-01-07T12:39:00", + "2024-01-07T12:40:00", + "2024-01-07T12:41:00", + "2024-01-07T12:42:00", + "2024-01-07T12:43:00", + "2024-01-07T12:44:00", + "2024-01-07T12:45:00", + "2024-01-07T12:46:00", + "2024-01-07T12:47:00", + "2024-01-07T12:48:00", + "2024-01-07T12:49:00", + "2024-01-07T12:50:00", + "2024-01-07T12:51:00", + "2024-01-07T12:52:00", + "2024-01-07T12:53:00", + "2024-01-07T12:54:00", + "2024-01-07T12:55:00", + "2024-01-07T12:56:00", + "2024-01-07T12:57:00", + "2024-01-07T12:58:00", + "2024-01-07T12:59:00", + "2024-01-07T13:00:00", + "2024-01-07T13:01:00", + "2024-01-07T13:02:00", + "2024-01-07T13:03:00", + "2024-01-07T13:04:00", + "2024-01-07T13:05:00", + "2024-01-07T13:06:00", + "2024-01-07T13:07:00", + "2024-01-07T13:08:00", + "2024-01-07T13:09:00", + "2024-01-07T13:10:00", + "2024-01-07T13:11:00", + "2024-01-07T13:12:00", + "2024-01-07T13:13:00", + "2024-01-07T13:14:00", + "2024-01-07T13:15:00", + "2024-01-07T13:16:00", + "2024-01-07T13:17:00", + "2024-01-07T13:18:00", + "2024-01-07T13:19:00", + "2024-01-07T13:20:00", + "2024-01-07T13:21:00", + "2024-01-07T13:22:00", + "2024-01-07T13:23:00", + "2024-01-07T13:24:00", + "2024-01-07T13:25:00", + "2024-01-07T13:26:00", + "2024-01-07T13:27:00", + "2024-01-07T13:28:00", + "2024-01-07T13:29:00", + "2024-01-07T13:30:00", + "2024-01-07T13:31:00", + "2024-01-07T13:32:00", + "2024-01-07T13:33:00", + "2024-01-07T13:34:00", + "2024-01-07T13:35:00", + "2024-01-07T13:36:00", + "2024-01-07T13:37:00", + "2024-01-07T13:38:00", + "2024-01-07T13:39:00", + "2024-01-07T13:40:00", + "2024-01-07T13:41:00", + "2024-01-07T13:42:00", + "2024-01-07T13:43:00", + "2024-01-07T13:44:00", + "2024-01-07T13:45:00", + "2024-01-07T13:46:00", + "2024-01-07T13:47:00", + "2024-01-07T13:48:00", + "2024-01-07T13:49:00", + "2024-01-07T13:50:00", + "2024-01-07T13:51:00", + "2024-01-07T13:52:00", + "2024-01-07T13:53:00", + "2024-01-07T13:54:00", + "2024-01-07T13:55:00", + "2024-01-07T13:56:00", + "2024-01-07T13:57:00", + "2024-01-07T13:58:00", + "2024-01-07T13:59:00", + "2024-01-07T14:00:00", + "2024-01-07T14:01:00", + "2024-01-07T14:02:00", + "2024-01-07T14:03:00", + "2024-01-07T14:04:00", + "2024-01-07T14:05:00", + "2024-01-07T14:06:00", + "2024-01-07T14:07:00", + "2024-01-07T14:08:00", + "2024-01-07T14:09:00", + "2024-01-07T14:10:00", + "2024-01-07T14:11:00", + "2024-01-07T14:12:00", + "2024-01-07T14:13:00", + "2024-01-07T14:14:00", + "2024-01-07T14:15:00", + "2024-01-07T14:16:00", + "2024-01-07T14:17:00", + "2024-01-07T14:18:00", + "2024-01-07T14:19:00", + "2024-01-07T14:20:00", + "2024-01-07T14:21:00", + "2024-01-07T14:22:00", + "2024-01-07T14:23:00", + "2024-01-07T14:24:00", + "2024-01-07T14:25:00", + "2024-01-07T14:26:00", + "2024-01-07T14:27:00", + "2024-01-07T14:28:00", + "2024-01-07T14:29:00", + "2024-01-07T14:30:00", + "2024-01-07T14:31:00", + "2024-01-07T14:32:00", + "2024-01-07T14:33:00", + "2024-01-07T14:34:00", + "2024-01-07T14:35:00", + "2024-01-07T14:36:00", + "2024-01-07T14:37:00", + "2024-01-07T14:38:00", + "2024-01-07T14:39:00", + "2024-01-07T14:40:00", + "2024-01-07T14:41:00", + "2024-01-07T14:42:00", + "2024-01-07T14:43:00", + "2024-01-07T14:44:00", + "2024-01-07T14:45:00", + "2024-01-07T14:46:00", + "2024-01-07T14:47:00", + "2024-01-07T14:48:00", + "2024-01-07T14:49:00", + "2024-01-07T14:50:00", + "2024-01-07T14:51:00", + "2024-01-07T14:52:00", + "2024-01-07T14:53:00", + "2024-01-07T14:54:00", + "2024-01-07T14:55:00", + "2024-01-07T14:56:00", + "2024-01-07T14:57:00", + "2024-01-07T14:58:00", + "2024-01-07T14:59:00", + "2024-01-07T15:00:00", + "2024-01-07T15:01:00", + "2024-01-07T15:02:00", + "2024-01-07T15:03:00", + "2024-01-07T15:04:00", + "2024-01-07T15:05:00", + "2024-01-07T15:06:00", + "2024-01-07T15:07:00", + "2024-01-07T15:08:00", + "2024-01-07T15:09:00", + "2024-01-07T15:10:00", + "2024-01-07T15:11:00", + "2024-01-07T15:12:00", + "2024-01-07T15:13:00", + "2024-01-07T15:14:00", + "2024-01-07T15:15:00", + "2024-01-07T15:16:00", + "2024-01-07T15:17:00", + "2024-01-07T15:18:00", + "2024-01-07T15:19:00", + "2024-01-07T15:20:00", + "2024-01-07T15:21:00", + "2024-01-07T15:22:00", + "2024-01-07T15:23:00", + "2024-01-07T15:24:00", + "2024-01-07T15:25:00", + "2024-01-07T15:26:00", + "2024-01-07T15:27:00", + "2024-01-07T15:28:00", + "2024-01-07T15:29:00", + "2024-01-07T15:30:00", + "2024-01-07T15:31:00", + "2024-01-07T15:32:00", + "2024-01-07T15:33:00", + "2024-01-07T15:34:00", + "2024-01-07T15:35:00", + "2024-01-07T15:36:00", + "2024-01-07T15:37:00", + "2024-01-07T15:38:00", + "2024-01-07T15:39:00", + "2024-01-07T15:40:00", + "2024-01-07T15:41:00", + "2024-01-07T15:42:00", + "2024-01-07T15:43:00", + "2024-01-07T15:44:00", + "2024-01-07T15:45:00", + "2024-01-07T15:46:00", + "2024-01-07T15:47:00", + "2024-01-07T15:48:00", + "2024-01-07T15:49:00", + "2024-01-07T15:50:00", + "2024-01-07T15:51:00", + "2024-01-07T15:52:00", + "2024-01-07T15:53:00", + "2024-01-07T15:54:00", + "2024-01-07T15:55:00", + "2024-01-07T15:56:00", + "2024-01-07T15:57:00", + "2024-01-07T15:58:00", + "2024-01-07T15:59:00", + "2024-01-07T16:00:00", + "2024-01-07T16:01:00", + "2024-01-07T16:02:00", + "2024-01-07T16:03:00", + "2024-01-07T16:04:00", + "2024-01-07T16:05:00", + "2024-01-07T16:06:00", + "2024-01-07T16:07:00", + "2024-01-07T16:08:00", + "2024-01-07T16:09:00", + "2024-01-07T16:10:00", + "2024-01-07T16:11:00", + "2024-01-07T16:12:00", + "2024-01-07T16:13:00", + "2024-01-07T16:14:00", + "2024-01-07T16:15:00", + "2024-01-07T16:16:00", + "2024-01-07T16:17:00", + "2024-01-07T16:18:00", + "2024-01-07T16:19:00", + "2024-01-07T16:20:00", + "2024-01-07T16:21:00", + "2024-01-07T16:22:00", + "2024-01-07T16:23:00", + "2024-01-07T16:24:00", + "2024-01-07T16:25:00", + "2024-01-07T16:26:00", + "2024-01-07T16:27:00", + "2024-01-07T16:28:00", + "2024-01-07T16:29:00", + "2024-01-07T16:30:00", + "2024-01-07T16:31:00", + "2024-01-07T16:32:00", + "2024-01-07T16:33:00", + "2024-01-07T16:34:00", + "2024-01-07T16:35:00", + "2024-01-07T16:36:00", + "2024-01-07T16:37:00", + "2024-01-07T16:38:00", + "2024-01-07T16:39:00", + "2024-01-07T16:40:00", + "2024-01-07T16:41:00", + "2024-01-07T16:42:00", + "2024-01-07T16:43:00", + "2024-01-07T16:44:00", + "2024-01-07T16:45:00", + "2024-01-07T16:46:00", + "2024-01-07T16:47:00", + "2024-01-07T16:48:00", + "2024-01-07T16:49:00", + "2024-01-07T16:50:00", + "2024-01-07T16:51:00", + "2024-01-07T16:52:00", + "2024-01-07T16:53:00", + "2024-01-07T16:54:00", + "2024-01-07T16:55:00", + "2024-01-07T16:56:00", + "2024-01-07T16:57:00", + "2024-01-07T16:58:00", + "2024-01-07T16:59:00", + "2024-01-07T17:00:00", + "2024-01-07T17:01:00", + "2024-01-07T17:02:00", + "2024-01-07T17:03:00", + "2024-01-07T17:04:00", + "2024-01-07T17:05:00", + "2024-01-07T17:06:00", + "2024-01-07T17:07:00", + "2024-01-07T17:08:00", + "2024-01-07T17:09:00", + "2024-01-07T17:10:00", + "2024-01-07T17:11:00", + "2024-01-07T17:12:00", + "2024-01-07T17:13:00", + "2024-01-07T17:14:00", + "2024-01-07T17:15:00", + "2024-01-07T17:16:00", + "2024-01-07T17:17:00", + "2024-01-07T17:18:00", + "2024-01-07T17:19:00", + "2024-01-07T17:20:00", + "2024-01-07T17:21:00", + "2024-01-07T17:22:00", + "2024-01-07T17:23:00", + "2024-01-07T17:24:00", + "2024-01-07T17:25:00", + "2024-01-07T17:26:00", + "2024-01-07T17:27:00", + "2024-01-07T17:28:00", + "2024-01-07T17:29:00", + "2024-01-07T17:30:00", + "2024-01-07T17:31:00", + "2024-01-07T17:32:00", + "2024-01-07T17:33:00", + "2024-01-07T17:34:00", + "2024-01-07T17:35:00", + "2024-01-07T17:36:00", + "2024-01-07T17:37:00", + "2024-01-07T17:38:00", + "2024-01-07T17:39:00", + "2024-01-07T17:40:00", + "2024-01-07T17:41:00", + "2024-01-07T17:42:00", + "2024-01-07T17:43:00", + "2024-01-07T17:44:00", + "2024-01-07T17:45:00", + "2024-01-07T17:46:00", + "2024-01-07T17:47:00", + "2024-01-07T17:48:00", + "2024-01-07T17:49:00", + "2024-01-07T17:50:00", + "2024-01-07T17:51:00", + "2024-01-07T17:52:00", + "2024-01-07T17:53:00", + "2024-01-07T17:54:00", + "2024-01-07T17:55:00", + "2024-01-07T17:56:00", + "2024-01-07T17:57:00", + "2024-01-07T17:58:00", + "2024-01-07T17:59:00", + "2024-01-07T18:00:00", + "2024-01-07T18:01:00", + "2024-01-07T18:02:00", + "2024-01-07T18:03:00", + "2024-01-07T18:04:00", + "2024-01-07T18:05:00", + "2024-01-07T18:06:00", + "2024-01-07T18:07:00", + "2024-01-07T18:08:00", + "2024-01-07T18:09:00", + "2024-01-07T18:10:00", + "2024-01-07T18:11:00", + "2024-01-07T18:12:00", + "2024-01-07T18:13:00", + "2024-01-07T18:14:00", + "2024-01-07T18:15:00", + "2024-01-07T18:16:00", + "2024-01-07T18:17:00", + "2024-01-07T18:18:00", + "2024-01-07T18:19:00", + "2024-01-07T18:20:00", + "2024-01-07T18:21:00", + "2024-01-07T18:22:00", + "2024-01-07T18:23:00", + "2024-01-07T18:24:00", + "2024-01-07T18:25:00", + "2024-01-07T18:26:00", + "2024-01-07T18:27:00", + "2024-01-07T18:28:00", + "2024-01-07T18:29:00", + "2024-01-07T18:30:00", + "2024-01-07T18:31:00", + "2024-01-07T18:32:00", + "2024-01-07T18:33:00", + "2024-01-07T18:34:00", + "2024-01-07T18:35:00", + "2024-01-07T18:36:00", + "2024-01-07T18:37:00", + "2024-01-07T18:38:00", + "2024-01-07T18:39:00", + "2024-01-07T18:40:00", + "2024-01-07T18:41:00", + "2024-01-07T18:42:00", + "2024-01-07T18:43:00", + "2024-01-07T18:44:00", + "2024-01-07T18:45:00", + "2024-01-07T18:46:00", + "2024-01-07T18:47:00", + "2024-01-07T18:48:00", + "2024-01-07T18:49:00", + "2024-01-07T18:50:00", + "2024-01-07T18:51:00", + "2024-01-07T18:52:00", + "2024-01-07T18:53:00", + "2024-01-07T18:54:00", + "2024-01-07T18:55:00", + "2024-01-07T18:56:00", + "2024-01-07T18:57:00", + "2024-01-07T18:58:00", + "2024-01-07T18:59:00", + "2024-01-07T19:00:00", + "2024-01-07T19:01:00", + "2024-01-07T19:02:00", + "2024-01-07T19:03:00", + "2024-01-07T19:04:00", + "2024-01-07T19:05:00", + "2024-01-07T19:06:00", + "2024-01-07T19:07:00", + "2024-01-07T19:08:00", + "2024-01-07T19:09:00", + "2024-01-07T19:10:00", + "2024-01-07T19:11:00", + "2024-01-07T19:12:00", + "2024-01-07T19:13:00", + "2024-01-07T19:14:00", + "2024-01-07T19:15:00", + "2024-01-07T19:16:00", + "2024-01-07T19:17:00", + "2024-01-07T19:18:00", + "2024-01-07T19:19:00", + "2024-01-07T19:20:00", + "2024-01-07T19:21:00", + "2024-01-07T19:22:00", + "2024-01-07T19:23:00", + "2024-01-07T19:24:00", + "2024-01-07T19:25:00", + "2024-01-07T19:26:00", + "2024-01-07T19:27:00", + "2024-01-07T19:28:00", + "2024-01-07T19:29:00", + "2024-01-07T19:30:00", + "2024-01-07T19:31:00", + "2024-01-07T19:32:00", + "2024-01-07T19:33:00", + "2024-01-07T19:34:00", + "2024-01-07T19:35:00", + "2024-01-07T19:36:00", + "2024-01-07T19:37:00", + "2024-01-07T19:38:00", + "2024-01-07T19:39:00", + "2024-01-07T19:40:00", + "2024-01-07T19:41:00", + "2024-01-07T19:42:00", + "2024-01-07T19:43:00", + "2024-01-07T19:44:00", + "2024-01-07T19:45:00", + "2024-01-07T19:46:00", + "2024-01-07T19:47:00", + "2024-01-07T19:48:00", + "2024-01-07T19:49:00", + "2024-01-07T19:50:00", + "2024-01-07T19:51:00", + "2024-01-07T19:52:00", + "2024-01-07T19:53:00", + "2024-01-07T19:54:00", + "2024-01-07T19:55:00", + "2024-01-07T19:56:00", + "2024-01-07T19:57:00", + "2024-01-07T19:58:00", + "2024-01-07T19:59:00", + "2024-01-07T20:00:00", + "2024-01-07T20:01:00", + "2024-01-07T20:02:00", + "2024-01-07T20:03:00", + "2024-01-07T20:04:00", + "2024-01-07T20:05:00", + "2024-01-07T20:06:00", + "2024-01-07T20:07:00", + "2024-01-07T20:08:00", + "2024-01-07T20:09:00", + "2024-01-07T20:10:00", + "2024-01-07T20:11:00", + "2024-01-07T20:12:00", + "2024-01-07T20:13:00", + "2024-01-07T20:14:00", + "2024-01-07T20:15:00", + "2024-01-07T20:16:00", + "2024-01-07T20:17:00", + "2024-01-07T20:18:00", + "2024-01-07T20:19:00", + "2024-01-07T20:20:00", + "2024-01-07T20:21:00", + "2024-01-07T20:22:00", + "2024-01-07T20:23:00", + "2024-01-07T20:24:00", + "2024-01-07T20:25:00", + "2024-01-07T20:26:00", + "2024-01-07T20:27:00", + "2024-01-07T20:28:00", + "2024-01-07T20:29:00", + "2024-01-07T20:30:00", + "2024-01-07T20:31:00", + "2024-01-07T20:32:00", + "2024-01-07T20:33:00", + "2024-01-07T20:34:00", + "2024-01-07T20:35:00", + "2024-01-07T20:36:00", + "2024-01-07T20:37:00", + "2024-01-07T20:38:00", + "2024-01-07T20:39:00", + "2024-01-07T20:40:00", + "2024-01-07T20:41:00", + "2024-01-07T20:42:00", + "2024-01-07T20:43:00", + "2024-01-07T20:44:00", + "2024-01-07T20:45:00", + "2024-01-07T20:46:00", + "2024-01-07T20:47:00", + "2024-01-07T20:48:00", + "2024-01-07T20:49:00", + "2024-01-07T20:50:00", + "2024-01-07T20:51:00", + "2024-01-07T20:52:00", + "2024-01-07T20:53:00", + "2024-01-07T20:54:00", + "2024-01-07T20:55:00", + "2024-01-07T20:56:00", + "2024-01-07T20:57:00", + "2024-01-07T20:58:00", + "2024-01-07T20:59:00", + "2024-01-07T21:00:00", + "2024-01-07T21:01:00", + "2024-01-07T21:02:00", + "2024-01-07T21:03:00", + "2024-01-07T21:04:00", + "2024-01-07T21:05:00", + "2024-01-07T21:06:00", + "2024-01-07T21:07:00", + "2024-01-07T21:08:00", + "2024-01-07T21:09:00", + "2024-01-07T21:10:00", + "2024-01-07T21:11:00", + "2024-01-07T21:12:00", + "2024-01-07T21:13:00", + "2024-01-07T21:14:00", + "2024-01-07T21:15:00", + "2024-01-07T21:16:00", + "2024-01-07T21:17:00", + "2024-01-07T21:18:00", + "2024-01-07T21:19:00", + "2024-01-07T21:20:00", + "2024-01-07T21:21:00", + "2024-01-07T21:22:00", + "2024-01-07T21:23:00", + "2024-01-07T21:24:00", + "2024-01-07T21:25:00", + "2024-01-07T21:26:00", + "2024-01-07T21:27:00", + "2024-01-07T21:28:00", + "2024-01-07T21:29:00", + "2024-01-07T21:30:00", + "2024-01-07T21:31:00", + "2024-01-07T21:32:00", + "2024-01-07T21:33:00", + "2024-01-07T21:34:00", + "2024-01-07T21:35:00", + "2024-01-07T21:36:00", + "2024-01-07T21:37:00", + "2024-01-07T21:38:00", + "2024-01-07T21:39:00", + "2024-01-07T21:40:00", + "2024-01-07T21:41:00", + "2024-01-07T21:42:00", + "2024-01-07T21:43:00", + "2024-01-07T21:44:00", + "2024-01-07T21:45:00", + "2024-01-07T21:46:00", + "2024-01-07T21:47:00", + "2024-01-07T21:48:00", + "2024-01-07T21:49:00", + "2024-01-07T21:50:00", + "2024-01-07T21:51:00", + "2024-01-07T21:52:00", + "2024-01-07T21:53:00", + "2024-01-07T21:54:00", + "2024-01-07T21:55:00", + "2024-01-07T21:56:00", + "2024-01-07T21:57:00", + "2024-01-07T21:58:00", + "2024-01-07T21:59:00", + "2024-01-07T22:00:00", + "2024-01-07T22:01:00", + "2024-01-07T22:02:00", + "2024-01-07T22:03:00", + "2024-01-07T22:04:00", + "2024-01-07T22:05:00", + "2024-01-07T22:06:00", + "2024-01-07T22:07:00", + "2024-01-07T22:08:00", + "2024-01-07T22:09:00", + "2024-01-07T22:10:00", + "2024-01-07T22:11:00", + "2024-01-07T22:12:00", + "2024-01-07T22:13:00", + "2024-01-07T22:14:00", + "2024-01-07T22:15:00", + "2024-01-07T22:16:00", + "2024-01-07T22:17:00", + "2024-01-07T22:18:00", + "2024-01-07T22:19:00", + "2024-01-07T22:20:00", + "2024-01-07T22:21:00", + "2024-01-07T22:22:00", + "2024-01-07T22:23:00", + "2024-01-07T22:24:00", + "2024-01-07T22:25:00", + "2024-01-07T22:26:00", + "2024-01-07T22:27:00", + "2024-01-07T22:28:00", + "2024-01-07T22:29:00", + "2024-01-07T22:30:00", + "2024-01-07T22:31:00", + "2024-01-07T22:32:00", + "2024-01-07T22:33:00", + "2024-01-07T22:34:00", + "2024-01-07T22:35:00", + "2024-01-07T22:36:00", + "2024-01-07T22:37:00", + "2024-01-07T22:38:00", + "2024-01-07T22:39:00", + "2024-01-07T22:40:00", + "2024-01-07T22:41:00", + "2024-01-07T22:42:00", + "2024-01-07T22:43:00", + "2024-01-07T22:44:00", + "2024-01-07T22:45:00", + "2024-01-07T22:46:00", + "2024-01-07T22:47:00", + "2024-01-07T22:48:00", + "2024-01-07T22:49:00", + "2024-01-07T22:50:00", + "2024-01-07T22:51:00", + "2024-01-07T22:52:00", + "2024-01-07T22:53:00", + "2024-01-07T22:54:00", + "2024-01-07T22:55:00", + "2024-01-07T22:56:00", + "2024-01-07T22:57:00", + "2024-01-07T22:58:00", + "2024-01-07T22:59:00", + "2024-01-07T23:00:00", + "2024-01-07T23:01:00", + "2024-01-07T23:02:00", + "2024-01-07T23:03:00", + "2024-01-07T23:04:00", + "2024-01-07T23:05:00", + "2024-01-07T23:06:00", + "2024-01-07T23:07:00", + "2024-01-07T23:08:00", + "2024-01-07T23:09:00", + "2024-01-07T23:10:00", + "2024-01-07T23:11:00", + "2024-01-07T23:12:00", + "2024-01-07T23:13:00", + "2024-01-07T23:14:00", + "2024-01-07T23:15:00", + "2024-01-07T23:16:00", + "2024-01-07T23:17:00", + "2024-01-07T23:18:00", + "2024-01-07T23:19:00", + "2024-01-07T23:20:00", + "2024-01-07T23:21:00", + "2024-01-07T23:22:00", + "2024-01-07T23:23:00", + "2024-01-07T23:24:00", + "2024-01-07T23:25:00", + "2024-01-07T23:26:00", + "2024-01-07T23:27:00", + "2024-01-07T23:28:00", + "2024-01-07T23:29:00", + "2024-01-07T23:30:00", + "2024-01-07T23:31:00", + "2024-01-07T23:32:00", + "2024-01-07T23:33:00", + "2024-01-07T23:34:00", + "2024-01-07T23:35:00", + "2024-01-07T23:36:00", + "2024-01-07T23:37:00", + "2024-01-07T23:38:00", + "2024-01-07T23:39:00", + "2024-01-07T23:40:00", + "2024-01-07T23:41:00", + "2024-01-07T23:42:00", + "2024-01-07T23:43:00", + "2024-01-07T23:44:00", + "2024-01-07T23:45:00", + "2024-01-07T23:46:00", + "2024-01-07T23:47:00", + "2024-01-07T23:48:00", + "2024-01-07T23:49:00", + "2024-01-07T23:50:00", + "2024-01-07T23:51:00", + "2024-01-07T23:52:00", + "2024-01-07T23:53:00", + "2024-01-07T23:54:00", + "2024-01-07T23:55:00", + "2024-01-07T23:56:00", + "2024-01-07T23:57:00", + "2024-01-07T23:58:00", + "2024-01-07T23:59:00", + "2024-01-08T00:00:00", + "2024-01-08T00:01:00", + "2024-01-08T00:02:00", + "2024-01-08T00:03:00", + "2024-01-08T00:04:00", + "2024-01-08T00:05:00", + "2024-01-08T00:06:00", + "2024-01-08T00:07:00", + "2024-01-08T00:08:00", + "2024-01-08T00:09:00", + "2024-01-08T00:10:00", + "2024-01-08T00:11:00", + "2024-01-08T00:12:00", + "2024-01-08T00:13:00", + "2024-01-08T00:14:00", + "2024-01-08T00:15:00", + "2024-01-08T00:16:00", + "2024-01-08T00:17:00", + "2024-01-08T00:18:00", + "2024-01-08T00:19:00", + "2024-01-08T00:20:00", + "2024-01-08T00:21:00", + "2024-01-08T00:22:00", + "2024-01-08T00:23:00", + "2024-01-08T00:24:00", + "2024-01-08T00:25:00", + "2024-01-08T00:26:00", + "2024-01-08T00:27:00", + "2024-01-08T00:28:00", + "2024-01-08T00:29:00", + "2024-01-08T00:30:00", + "2024-01-08T00:31:00", + "2024-01-08T00:32:00", + "2024-01-08T00:33:00", + "2024-01-08T00:34:00", + "2024-01-08T00:35:00", + "2024-01-08T00:36:00", + "2024-01-08T00:37:00", + "2024-01-08T00:38:00", + "2024-01-08T00:39:00", + "2024-01-08T00:40:00", + "2024-01-08T00:41:00", + "2024-01-08T00:42:00", + "2024-01-08T00:43:00", + "2024-01-08T00:44:00", + "2024-01-08T00:45:00", + "2024-01-08T00:46:00", + "2024-01-08T00:47:00", + "2024-01-08T00:48:00", + "2024-01-08T00:49:00", + "2024-01-08T00:50:00", + "2024-01-08T00:51:00", + "2024-01-08T00:52:00", + "2024-01-08T00:53:00", + "2024-01-08T00:54:00", + "2024-01-08T00:55:00", + "2024-01-08T00:56:00", + "2024-01-08T00:57:00", + "2024-01-08T00:58:00", + "2024-01-08T00:59:00", + "2024-01-08T01:00:00", + "2024-01-08T01:01:00", + "2024-01-08T01:02:00", + "2024-01-08T01:03:00", + "2024-01-08T01:04:00", + "2024-01-08T01:05:00", + "2024-01-08T01:06:00", + "2024-01-08T01:07:00", + "2024-01-08T01:08:00", + "2024-01-08T01:09:00", + "2024-01-08T01:10:00", + "2024-01-08T01:11:00", + "2024-01-08T01:12:00", + "2024-01-08T01:13:00", + "2024-01-08T01:14:00", + "2024-01-08T01:15:00", + "2024-01-08T01:16:00", + "2024-01-08T01:17:00", + "2024-01-08T01:18:00", + "2024-01-08T01:19:00", + "2024-01-08T01:20:00", + "2024-01-08T01:21:00", + "2024-01-08T01:22:00", + "2024-01-08T01:23:00", + "2024-01-08T01:24:00", + "2024-01-08T01:25:00", + "2024-01-08T01:26:00", + "2024-01-08T01:27:00", + "2024-01-08T01:28:00", + "2024-01-08T01:29:00", + "2024-01-08T01:30:00", + "2024-01-08T01:31:00", + "2024-01-08T01:32:00", + "2024-01-08T01:33:00", + "2024-01-08T01:34:00", + "2024-01-08T01:35:00", + "2024-01-08T01:36:00", + "2024-01-08T01:37:00", + "2024-01-08T01:38:00", + "2024-01-08T01:39:00", + "2024-01-08T01:40:00", + "2024-01-08T01:41:00", + "2024-01-08T01:42:00", + "2024-01-08T01:43:00", + "2024-01-08T01:44:00", + "2024-01-08T01:45:00", + "2024-01-08T01:46:00", + "2024-01-08T01:47:00", + "2024-01-08T01:48:00", + "2024-01-08T01:49:00", + "2024-01-08T01:50:00", + "2024-01-08T01:51:00", + "2024-01-08T01:52:00", + "2024-01-08T01:53:00", + "2024-01-08T01:54:00", + "2024-01-08T01:55:00", + "2024-01-08T01:56:00", + "2024-01-08T01:57:00", + "2024-01-08T01:58:00", + "2024-01-08T01:59:00", + "2024-01-08T02:00:00", + "2024-01-08T02:01:00", + "2024-01-08T02:02:00", + "2024-01-08T02:03:00", + "2024-01-08T02:04:00", + "2024-01-08T02:05:00", + "2024-01-08T02:06:00", + "2024-01-08T02:07:00", + "2024-01-08T02:08:00", + "2024-01-08T02:09:00", + "2024-01-08T02:10:00", + "2024-01-08T02:11:00", + "2024-01-08T02:12:00", + "2024-01-08T02:13:00", + "2024-01-08T02:14:00", + "2024-01-08T02:15:00", + "2024-01-08T02:16:00", + "2024-01-08T02:17:00", + "2024-01-08T02:18:00", + "2024-01-08T02:19:00", + "2024-01-08T02:20:00", + "2024-01-08T02:21:00", + "2024-01-08T02:22:00", + "2024-01-08T02:23:00", + "2024-01-08T02:24:00", + "2024-01-08T02:25:00", + "2024-01-08T02:26:00", + "2024-01-08T02:27:00", + "2024-01-08T02:28:00", + "2024-01-08T02:29:00", + "2024-01-08T02:30:00", + "2024-01-08T02:31:00", + "2024-01-08T02:32:00", + "2024-01-08T02:33:00", + "2024-01-08T02:34:00", + "2024-01-08T02:35:00", + "2024-01-08T02:36:00", + "2024-01-08T02:37:00", + "2024-01-08T02:38:00", + "2024-01-08T02:39:00", + "2024-01-08T02:40:00", + "2024-01-08T02:41:00", + "2024-01-08T02:42:00", + "2024-01-08T02:43:00", + "2024-01-08T02:44:00", + "2024-01-08T02:45:00", + "2024-01-08T02:46:00", + "2024-01-08T02:47:00", + "2024-01-08T02:48:00", + "2024-01-08T02:49:00", + "2024-01-08T02:50:00", + "2024-01-08T02:51:00", + "2024-01-08T02:52:00", + "2024-01-08T02:53:00", + "2024-01-08T02:54:00", + "2024-01-08T02:55:00", + "2024-01-08T02:56:00", + "2024-01-08T02:57:00", + "2024-01-08T02:58:00", + "2024-01-08T02:59:00", + "2024-01-08T03:00:00", + "2024-01-08T03:01:00", + "2024-01-08T03:02:00", + "2024-01-08T03:03:00", + "2024-01-08T03:04:00", + "2024-01-08T03:05:00", + "2024-01-08T03:06:00", + "2024-01-08T03:07:00", + "2024-01-08T03:08:00", + "2024-01-08T03:09:00", + "2024-01-08T03:10:00", + "2024-01-08T03:11:00", + "2024-01-08T03:12:00", + "2024-01-08T03:13:00", + "2024-01-08T03:14:00", + "2024-01-08T03:15:00", + "2024-01-08T03:16:00", + "2024-01-08T03:17:00", + "2024-01-08T03:18:00", + "2024-01-08T03:19:00", + "2024-01-08T03:20:00", + "2024-01-08T03:21:00", + "2024-01-08T03:22:00", + "2024-01-08T03:23:00", + "2024-01-08T03:24:00", + "2024-01-08T03:25:00", + "2024-01-08T03:26:00", + "2024-01-08T03:27:00", + "2024-01-08T03:28:00", + "2024-01-08T03:29:00", + "2024-01-08T03:30:00", + "2024-01-08T03:31:00", + "2024-01-08T03:32:00", + "2024-01-08T03:33:00", + "2024-01-08T03:34:00", + "2024-01-08T03:35:00", + "2024-01-08T03:36:00", + "2024-01-08T03:37:00", + "2024-01-08T03:38:00", + "2024-01-08T03:39:00", + "2024-01-08T03:40:00", + "2024-01-08T03:41:00", + "2024-01-08T03:42:00", + "2024-01-08T03:43:00", + "2024-01-08T03:44:00", + "2024-01-08T03:45:00", + "2024-01-08T03:46:00", + "2024-01-08T03:47:00", + "2024-01-08T03:48:00", + "2024-01-08T03:49:00", + "2024-01-08T03:50:00", + "2024-01-08T03:51:00", + "2024-01-08T03:52:00", + "2024-01-08T03:53:00", + "2024-01-08T03:54:00", + "2024-01-08T03:55:00", + "2024-01-08T03:56:00", + "2024-01-08T03:57:00", + "2024-01-08T03:58:00", + "2024-01-08T03:59:00", + "2024-01-08T04:00:00", + "2024-01-08T04:01:00", + "2024-01-08T04:02:00", + "2024-01-08T04:03:00", + "2024-01-08T04:04:00", + "2024-01-08T04:05:00", + "2024-01-08T04:06:00", + "2024-01-08T04:07:00", + "2024-01-08T04:08:00", + "2024-01-08T04:09:00", + "2024-01-08T04:10:00", + "2024-01-08T04:11:00", + "2024-01-08T04:12:00", + "2024-01-08T04:13:00", + "2024-01-08T04:14:00", + "2024-01-08T04:15:00", + "2024-01-08T04:16:00", + "2024-01-08T04:17:00", + "2024-01-08T04:18:00", + "2024-01-08T04:19:00", + "2024-01-08T04:20:00", + "2024-01-08T04:21:00", + "2024-01-08T04:22:00", + "2024-01-08T04:23:00", + "2024-01-08T04:24:00", + "2024-01-08T04:25:00", + "2024-01-08T04:26:00", + "2024-01-08T04:27:00", + "2024-01-08T04:28:00", + "2024-01-08T04:29:00", + "2024-01-08T04:30:00", + "2024-01-08T04:31:00", + "2024-01-08T04:32:00", + "2024-01-08T04:33:00", + "2024-01-08T04:34:00", + "2024-01-08T04:35:00", + "2024-01-08T04:36:00", + "2024-01-08T04:37:00", + "2024-01-08T04:38:00", + "2024-01-08T04:39:00", + "2024-01-08T04:40:00", + "2024-01-08T04:41:00", + "2024-01-08T04:42:00", + "2024-01-08T04:43:00", + "2024-01-08T04:44:00", + "2024-01-08T04:45:00", + "2024-01-08T04:46:00", + "2024-01-08T04:47:00", + "2024-01-08T04:48:00", + "2024-01-08T04:49:00", + "2024-01-08T04:50:00", + "2024-01-08T04:51:00", + "2024-01-08T04:52:00", + "2024-01-08T04:53:00", + "2024-01-08T04:54:00", + "2024-01-08T04:55:00", + "2024-01-08T04:56:00", + "2024-01-08T04:57:00", + "2024-01-08T04:58:00", + "2024-01-08T04:59:00", + "2024-01-08T05:00:00", + "2024-01-08T05:01:00", + "2024-01-08T05:02:00", + "2024-01-08T05:03:00", + "2024-01-08T05:04:00", + "2024-01-08T05:05:00", + "2024-01-08T05:06:00", + "2024-01-08T05:07:00", + "2024-01-08T05:08:00", + "2024-01-08T05:09:00", + "2024-01-08T05:10:00", + "2024-01-08T05:11:00", + "2024-01-08T05:12:00", + "2024-01-08T05:13:00", + "2024-01-08T05:14:00", + "2024-01-08T05:15:00", + "2024-01-08T05:16:00", + "2024-01-08T05:17:00", + "2024-01-08T05:18:00", + "2024-01-08T05:19:00", + "2024-01-08T05:20:00", + "2024-01-08T05:21:00", + "2024-01-08T05:22:00", + "2024-01-08T05:23:00", + "2024-01-08T05:24:00", + "2024-01-08T05:25:00", + "2024-01-08T05:26:00", + "2024-01-08T05:27:00", + "2024-01-08T05:28:00", + "2024-01-08T05:29:00", + "2024-01-08T05:30:00", + "2024-01-08T05:31:00", + "2024-01-08T05:32:00", + "2024-01-08T05:33:00", + "2024-01-08T05:34:00", + "2024-01-08T05:35:00", + "2024-01-08T05:36:00", + "2024-01-08T05:37:00", + "2024-01-08T05:38:00", + "2024-01-08T05:39:00", + "2024-01-08T05:40:00", + "2024-01-08T05:41:00", + "2024-01-08T05:42:00", + "2024-01-08T05:43:00", + "2024-01-08T05:44:00", + "2024-01-08T05:45:00", + "2024-01-08T05:46:00", + "2024-01-08T05:47:00", + "2024-01-08T05:48:00", + "2024-01-08T05:49:00", + "2024-01-08T05:50:00", + "2024-01-08T05:51:00", + "2024-01-08T05:52:00", + "2024-01-08T05:53:00", + "2024-01-08T05:54:00", + "2024-01-08T05:55:00", + "2024-01-08T05:56:00", + "2024-01-08T05:57:00", + "2024-01-08T05:58:00", + "2024-01-08T05:59:00", + "2024-01-08T06:00:00", + "2024-01-08T06:01:00", + "2024-01-08T06:02:00", + "2024-01-08T06:03:00", + "2024-01-08T06:04:00", + "2024-01-08T06:05:00", + "2024-01-08T06:06:00", + "2024-01-08T06:07:00", + "2024-01-08T06:08:00", + "2024-01-08T06:09:00", + "2024-01-08T06:10:00", + "2024-01-08T06:11:00", + "2024-01-08T06:12:00", + "2024-01-08T06:13:00", + "2024-01-08T06:14:00", + "2024-01-08T06:15:00", + "2024-01-08T06:16:00", + "2024-01-08T06:17:00", + "2024-01-08T06:18:00", + "2024-01-08T06:19:00", + "2024-01-08T06:20:00", + "2024-01-08T06:21:00", + "2024-01-08T06:22:00", + "2024-01-08T06:23:00", + "2024-01-08T06:24:00", + "2024-01-08T06:25:00", + "2024-01-08T06:26:00", + "2024-01-08T06:27:00", + "2024-01-08T06:28:00", + "2024-01-08T06:29:00", + "2024-01-08T06:30:00", + "2024-01-08T06:31:00", + "2024-01-08T06:32:00", + "2024-01-08T06:33:00", + "2024-01-08T06:34:00", + "2024-01-08T06:35:00", + "2024-01-08T06:36:00", + "2024-01-08T06:37:00", + "2024-01-08T06:38:00", + "2024-01-08T06:39:00", + "2024-01-08T06:40:00", + "2024-01-08T06:41:00", + "2024-01-08T06:42:00", + "2024-01-08T06:43:00", + "2024-01-08T06:44:00", + "2024-01-08T06:45:00", + "2024-01-08T06:46:00", + "2024-01-08T06:47:00", + "2024-01-08T06:48:00", + "2024-01-08T06:49:00", + "2024-01-08T06:50:00", + "2024-01-08T06:51:00", + "2024-01-08T06:52:00", + "2024-01-08T06:53:00", + "2024-01-08T06:54:00", + "2024-01-08T06:55:00", + "2024-01-08T06:56:00", + "2024-01-08T06:57:00", + "2024-01-08T06:58:00", + "2024-01-08T06:59:00", + "2024-01-08T07:00:00", + "2024-01-08T07:01:00", + "2024-01-08T07:02:00", + "2024-01-08T07:03:00", + "2024-01-08T07:04:00", + "2024-01-08T07:05:00", + "2024-01-08T07:06:00", + "2024-01-08T07:07:00", + "2024-01-08T07:08:00", + "2024-01-08T07:09:00", + "2024-01-08T07:10:00", + "2024-01-08T07:11:00", + "2024-01-08T07:12:00", + "2024-01-08T07:13:00", + "2024-01-08T07:14:00", + "2024-01-08T07:15:00", + "2024-01-08T07:16:00", + "2024-01-08T07:17:00", + "2024-01-08T07:18:00", + "2024-01-08T07:19:00", + "2024-01-08T07:20:00", + "2024-01-08T07:21:00", + "2024-01-08T07:22:00", + "2024-01-08T07:23:00", + "2024-01-08T07:24:00", + "2024-01-08T07:25:00", + "2024-01-08T07:26:00", + "2024-01-08T07:27:00", + "2024-01-08T07:28:00", + "2024-01-08T07:29:00", + "2024-01-08T07:30:00", + "2024-01-08T07:31:00", + "2024-01-08T07:32:00", + "2024-01-08T07:33:00", + "2024-01-08T07:34:00", + "2024-01-08T07:35:00", + "2024-01-08T07:36:00", + "2024-01-08T07:37:00", + "2024-01-08T07:38:00", + "2024-01-08T07:39:00", + "2024-01-08T07:40:00", + "2024-01-08T07:41:00", + "2024-01-08T07:42:00", + "2024-01-08T07:43:00", + "2024-01-08T07:44:00", + "2024-01-08T07:45:00", + "2024-01-08T07:46:00", + "2024-01-08T07:47:00", + "2024-01-08T07:48:00", + "2024-01-08T07:49:00", + "2024-01-08T07:50:00", + "2024-01-08T07:51:00", + "2024-01-08T07:52:00", + "2024-01-08T07:53:00", + "2024-01-08T07:54:00", + "2024-01-08T07:55:00", + "2024-01-08T07:56:00", + "2024-01-08T07:57:00", + "2024-01-08T07:58:00", + "2024-01-08T07:59:00", + "2024-01-08T08:00:00", + "2024-01-08T08:01:00", + "2024-01-08T08:02:00", + "2024-01-08T08:03:00", + "2024-01-08T08:04:00", + "2024-01-08T08:05:00", + "2024-01-08T08:06:00", + "2024-01-08T08:07:00", + "2024-01-08T08:08:00", + "2024-01-08T08:09:00", + "2024-01-08T08:10:00", + "2024-01-08T08:11:00", + "2024-01-08T08:12:00", + "2024-01-08T08:13:00", + "2024-01-08T08:14:00", + "2024-01-08T08:15:00", + "2024-01-08T08:16:00", + "2024-01-08T08:17:00", + "2024-01-08T08:18:00", + "2024-01-08T08:19:00", + "2024-01-08T08:20:00", + "2024-01-08T08:21:00", + "2024-01-08T08:22:00", + "2024-01-08T08:23:00", + "2024-01-08T08:24:00", + "2024-01-08T08:25:00", + "2024-01-08T08:26:00", + "2024-01-08T08:27:00", + "2024-01-08T08:28:00", + "2024-01-08T08:29:00", + "2024-01-08T08:30:00", + "2024-01-08T08:31:00", + "2024-01-08T08:32:00", + "2024-01-08T08:33:00", + "2024-01-08T08:34:00", + "2024-01-08T08:35:00", + "2024-01-08T08:36:00", + "2024-01-08T08:37:00", + "2024-01-08T08:38:00", + "2024-01-08T08:39:00", + "2024-01-08T08:40:00", + "2024-01-08T08:41:00", + "2024-01-08T08:42:00", + "2024-01-08T08:43:00", + "2024-01-08T08:44:00", + "2024-01-08T08:45:00", + "2024-01-08T08:46:00", + "2024-01-08T08:47:00", + "2024-01-08T08:48:00", + "2024-01-08T08:49:00", + "2024-01-08T08:50:00", + "2024-01-08T08:51:00", + "2024-01-08T08:52:00", + "2024-01-08T08:53:00", + "2024-01-08T08:54:00", + "2024-01-08T08:55:00", + "2024-01-08T08:56:00", + "2024-01-08T08:57:00", + "2024-01-08T08:58:00", + "2024-01-08T08:59:00", + "2024-01-08T09:00:00", + "2024-01-08T09:01:00", + "2024-01-08T09:02:00", + "2024-01-08T09:03:00", + "2024-01-08T09:04:00", + "2024-01-08T09:05:00", + "2024-01-08T09:06:00", + "2024-01-08T09:07:00", + "2024-01-08T09:08:00", + "2024-01-08T09:09:00", + "2024-01-08T09:10:00", + "2024-01-08T09:11:00", + "2024-01-08T09:12:00", + "2024-01-08T09:13:00", + "2024-01-08T09:14:00", + "2024-01-08T09:15:00", + "2024-01-08T09:16:00", + "2024-01-08T09:17:00", + "2024-01-08T09:18:00", + "2024-01-08T09:19:00", + "2024-01-08T09:20:00", + "2024-01-08T09:21:00", + "2024-01-08T09:22:00", + "2024-01-08T09:23:00", + "2024-01-08T09:24:00", + "2024-01-08T09:25:00", + "2024-01-08T09:26:00", + "2024-01-08T09:27:00", + "2024-01-08T09:28:00", + "2024-01-08T09:29:00", + "2024-01-08T09:30:00", + "2024-01-08T09:31:00", + "2024-01-08T09:32:00", + "2024-01-08T09:33:00", + "2024-01-08T09:34:00", + "2024-01-08T09:35:00", + "2024-01-08T09:36:00", + "2024-01-08T09:37:00", + "2024-01-08T09:38:00", + "2024-01-08T09:39:00", + "2024-01-08T09:40:00", + "2024-01-08T09:41:00", + "2024-01-08T09:42:00", + "2024-01-08T09:43:00", + "2024-01-08T09:44:00", + "2024-01-08T09:45:00", + "2024-01-08T09:46:00", + "2024-01-08T09:47:00", + "2024-01-08T09:48:00", + "2024-01-08T09:49:00", + "2024-01-08T09:50:00", + "2024-01-08T09:51:00", + "2024-01-08T09:52:00", + "2024-01-08T09:53:00", + "2024-01-08T09:54:00", + "2024-01-08T09:55:00", + "2024-01-08T09:56:00", + "2024-01-08T09:57:00", + "2024-01-08T09:58:00", + "2024-01-08T09:59:00", + "2024-01-08T10:00:00", + "2024-01-08T10:01:00", + "2024-01-08T10:02:00", + "2024-01-08T10:03:00", + "2024-01-08T10:04:00", + "2024-01-08T10:05:00", + "2024-01-08T10:06:00", + "2024-01-08T10:07:00", + "2024-01-08T10:08:00", + "2024-01-08T10:09:00", + "2024-01-08T10:10:00", + "2024-01-08T10:11:00", + "2024-01-08T10:12:00", + "2024-01-08T10:13:00", + "2024-01-08T10:14:00", + "2024-01-08T10:15:00", + "2024-01-08T10:16:00", + "2024-01-08T10:17:00", + "2024-01-08T10:18:00", + "2024-01-08T10:19:00", + "2024-01-08T10:20:00", + "2024-01-08T10:21:00", + "2024-01-08T10:22:00", + "2024-01-08T10:23:00", + "2024-01-08T10:24:00", + "2024-01-08T10:25:00", + "2024-01-08T10:26:00", + "2024-01-08T10:27:00", + "2024-01-08T10:28:00", + "2024-01-08T10:29:00", + "2024-01-08T10:30:00", + "2024-01-08T10:31:00", + "2024-01-08T10:32:00", + "2024-01-08T10:33:00", + "2024-01-08T10:34:00", + "2024-01-08T10:35:00", + "2024-01-08T10:36:00", + "2024-01-08T10:37:00", + "2024-01-08T10:38:00", + "2024-01-08T10:39:00", + "2024-01-08T10:40:00", + "2024-01-08T10:41:00", + "2024-01-08T10:42:00", + "2024-01-08T10:43:00", + "2024-01-08T10:44:00", + "2024-01-08T10:45:00", + "2024-01-08T10:46:00", + "2024-01-08T10:47:00", + "2024-01-08T10:48:00", + "2024-01-08T10:49:00", + "2024-01-08T10:50:00", + "2024-01-08T10:51:00", + "2024-01-08T10:52:00", + "2024-01-08T10:53:00", + "2024-01-08T10:54:00", + "2024-01-08T10:55:00", + "2024-01-08T10:56:00", + "2024-01-08T10:57:00", + "2024-01-08T10:58:00", + "2024-01-08T10:59:00", + "2024-01-08T11:00:00", + "2024-01-08T11:01:00", + "2024-01-08T11:02:00", + "2024-01-08T11:03:00", + "2024-01-08T11:04:00", + "2024-01-08T11:05:00", + "2024-01-08T11:06:00", + "2024-01-08T11:07:00", + "2024-01-08T11:08:00", + "2024-01-08T11:09:00", + "2024-01-08T11:10:00", + "2024-01-08T11:11:00", + "2024-01-08T11:12:00", + "2024-01-08T11:13:00", + "2024-01-08T11:14:00", + "2024-01-08T11:15:00", + "2024-01-08T11:16:00", + "2024-01-08T11:17:00", + "2024-01-08T11:18:00", + "2024-01-08T11:19:00", + "2024-01-08T11:20:00", + "2024-01-08T11:21:00", + "2024-01-08T11:22:00", + "2024-01-08T11:23:00", + "2024-01-08T11:24:00", + "2024-01-08T11:25:00", + "2024-01-08T11:26:00", + "2024-01-08T11:27:00", + "2024-01-08T11:28:00", + "2024-01-08T11:29:00", + "2024-01-08T11:30:00", + "2024-01-08T11:31:00", + "2024-01-08T11:32:00", + "2024-01-08T11:33:00", + "2024-01-08T11:34:00", + "2024-01-08T11:35:00", + "2024-01-08T11:36:00", + "2024-01-08T11:37:00", + "2024-01-08T11:38:00", + "2024-01-08T11:39:00", + "2024-01-08T11:40:00", + "2024-01-08T11:41:00", + "2024-01-08T11:42:00", + "2024-01-08T11:43:00", + "2024-01-08T11:44:00", + "2024-01-08T11:45:00", + "2024-01-08T11:46:00", + "2024-01-08T11:47:00", + "2024-01-08T11:48:00", + "2024-01-08T11:49:00", + "2024-01-08T11:50:00", + "2024-01-08T11:51:00", + "2024-01-08T11:52:00", + "2024-01-08T11:53:00", + "2024-01-08T11:54:00", + "2024-01-08T11:55:00", + "2024-01-08T11:56:00", + "2024-01-08T11:57:00", + "2024-01-08T11:58:00", + "2024-01-08T11:59:00", + "2024-01-08T12:00:00", + "2024-01-08T12:01:00", + "2024-01-08T12:02:00", + "2024-01-08T12:03:00", + "2024-01-08T12:04:00", + "2024-01-08T12:05:00", + "2024-01-08T12:06:00", + "2024-01-08T12:07:00", + "2024-01-08T12:08:00", + "2024-01-08T12:09:00", + "2024-01-08T12:10:00", + "2024-01-08T12:11:00", + "2024-01-08T12:12:00", + "2024-01-08T12:13:00", + "2024-01-08T12:14:00", + "2024-01-08T12:15:00", + "2024-01-08T12:16:00", + "2024-01-08T12:17:00", + "2024-01-08T12:18:00", + "2024-01-08T12:19:00", + "2024-01-08T12:20:00", + "2024-01-08T12:21:00", + "2024-01-08T12:22:00", + "2024-01-08T12:23:00", + "2024-01-08T12:24:00", + "2024-01-08T12:25:00", + "2024-01-08T12:26:00", + "2024-01-08T12:27:00", + "2024-01-08T12:28:00", + "2024-01-08T12:29:00", + "2024-01-08T12:30:00", + "2024-01-08T12:31:00", + "2024-01-08T12:32:00", + "2024-01-08T12:33:00", + "2024-01-08T12:34:00", + "2024-01-08T12:35:00", + "2024-01-08T12:36:00", + "2024-01-08T12:37:00", + "2024-01-08T12:38:00", + "2024-01-08T12:39:00", + "2024-01-08T12:40:00", + "2024-01-08T12:41:00", + "2024-01-08T12:42:00", + "2024-01-08T12:43:00", + "2024-01-08T12:44:00", + "2024-01-08T12:45:00", + "2024-01-08T12:46:00", + "2024-01-08T12:47:00", + "2024-01-08T12:48:00", + "2024-01-08T12:49:00", + "2024-01-08T12:50:00", + "2024-01-08T12:51:00", + "2024-01-08T12:52:00", + "2024-01-08T12:53:00", + "2024-01-08T12:54:00", + "2024-01-08T12:55:00", + "2024-01-08T12:56:00", + "2024-01-08T12:57:00", + "2024-01-08T12:58:00", + "2024-01-08T12:59:00", + "2024-01-08T13:00:00", + "2024-01-08T13:01:00", + "2024-01-08T13:02:00", + "2024-01-08T13:03:00", + "2024-01-08T13:04:00", + "2024-01-08T13:05:00", + "2024-01-08T13:06:00", + "2024-01-08T13:07:00", + "2024-01-08T13:08:00", + "2024-01-08T13:09:00", + "2024-01-08T13:10:00", + "2024-01-08T13:11:00", + "2024-01-08T13:12:00", + "2024-01-08T13:13:00", + "2024-01-08T13:14:00", + "2024-01-08T13:15:00", + "2024-01-08T13:16:00", + "2024-01-08T13:17:00", + "2024-01-08T13:18:00", + "2024-01-08T13:19:00", + "2024-01-08T13:20:00", + "2024-01-08T13:21:00", + "2024-01-08T13:22:00", + "2024-01-08T13:23:00", + "2024-01-08T13:24:00", + "2024-01-08T13:25:00", + "2024-01-08T13:26:00", + "2024-01-08T13:27:00", + "2024-01-08T13:28:00", + "2024-01-08T13:29:00", + "2024-01-08T13:30:00", + "2024-01-08T13:31:00", + "2024-01-08T13:32:00", + "2024-01-08T13:33:00", + "2024-01-08T13:34:00", + "2024-01-08T13:35:00", + "2024-01-08T13:36:00", + "2024-01-08T13:37:00", + "2024-01-08T13:38:00", + "2024-01-08T13:39:00", + "2024-01-08T13:40:00", + "2024-01-08T13:41:00", + "2024-01-08T13:42:00", + "2024-01-08T13:43:00", + "2024-01-08T13:44:00", + "2024-01-08T13:45:00", + "2024-01-08T13:46:00", + "2024-01-08T13:47:00", + "2024-01-08T13:48:00", + "2024-01-08T13:49:00", + "2024-01-08T13:50:00", + "2024-01-08T13:51:00", + "2024-01-08T13:52:00", + "2024-01-08T13:53:00", + "2024-01-08T13:54:00", + "2024-01-08T13:55:00", + "2024-01-08T13:56:00", + "2024-01-08T13:57:00", + "2024-01-08T13:58:00", + "2024-01-08T13:59:00", + "2024-01-08T14:00:00", + "2024-01-08T14:01:00", + "2024-01-08T14:02:00", + "2024-01-08T14:03:00", + "2024-01-08T14:04:00", + "2024-01-08T14:05:00", + "2024-01-08T14:06:00", + "2024-01-08T14:07:00", + "2024-01-08T14:08:00", + "2024-01-08T14:09:00", + "2024-01-08T14:10:00", + "2024-01-08T14:11:00", + "2024-01-08T14:12:00", + "2024-01-08T14:13:00", + "2024-01-08T14:14:00", + "2024-01-08T14:15:00", + "2024-01-08T14:16:00", + "2024-01-08T14:17:00", + "2024-01-08T14:18:00", + "2024-01-08T14:19:00", + "2024-01-08T14:20:00", + "2024-01-08T14:21:00", + "2024-01-08T14:22:00", + "2024-01-08T14:23:00", + "2024-01-08T14:24:00", + "2024-01-08T14:25:00", + "2024-01-08T14:26:00", + "2024-01-08T14:27:00", + "2024-01-08T14:28:00", + "2024-01-08T14:29:00", + "2024-01-08T14:30:00", + "2024-01-08T14:31:00", + "2024-01-08T14:32:00", + "2024-01-08T14:33:00", + "2024-01-08T14:34:00", + "2024-01-08T14:35:00", + "2024-01-08T14:36:00", + "2024-01-08T14:37:00", + "2024-01-08T14:38:00", + "2024-01-08T14:39:00", + "2024-01-08T14:40:00", + "2024-01-08T14:41:00", + "2024-01-08T14:42:00", + "2024-01-08T14:43:00", + "2024-01-08T14:44:00", + "2024-01-08T14:45:00", + "2024-01-08T14:46:00", + "2024-01-08T14:47:00", + "2024-01-08T14:48:00", + "2024-01-08T14:49:00", + "2024-01-08T14:50:00", + "2024-01-08T14:51:00", + "2024-01-08T14:52:00", + "2024-01-08T14:53:00", + "2024-01-08T14:54:00", + "2024-01-08T14:55:00", + "2024-01-08T14:56:00", + "2024-01-08T14:57:00", + "2024-01-08T14:58:00", + "2024-01-08T14:59:00", + "2024-01-08T15:00:00", + "2024-01-08T15:01:00", + "2024-01-08T15:02:00", + "2024-01-08T15:03:00", + "2024-01-08T15:04:00", + "2024-01-08T15:05:00", + "2024-01-08T15:06:00", + "2024-01-08T15:07:00", + "2024-01-08T15:08:00", + "2024-01-08T15:09:00", + "2024-01-08T15:10:00", + "2024-01-08T15:11:00", + "2024-01-08T15:12:00", + "2024-01-08T15:13:00", + "2024-01-08T15:14:00", + "2024-01-08T15:15:00", + "2024-01-08T15:16:00", + "2024-01-08T15:17:00", + "2024-01-08T15:18:00", + "2024-01-08T15:19:00", + "2024-01-08T15:20:00", + "2024-01-08T15:21:00", + "2024-01-08T15:22:00", + "2024-01-08T15:23:00", + "2024-01-08T15:24:00", + "2024-01-08T15:25:00", + "2024-01-08T15:26:00", + "2024-01-08T15:27:00", + "2024-01-08T15:28:00", + "2024-01-08T15:29:00", + "2024-01-08T15:30:00", + "2024-01-08T15:31:00", + "2024-01-08T15:32:00", + "2024-01-08T15:33:00", + "2024-01-08T15:34:00", + "2024-01-08T15:35:00", + "2024-01-08T15:36:00", + "2024-01-08T15:37:00", + "2024-01-08T15:38:00", + "2024-01-08T15:39:00", + "2024-01-08T15:40:00", + "2024-01-08T15:41:00", + "2024-01-08T15:42:00", + "2024-01-08T15:43:00", + "2024-01-08T15:44:00", + "2024-01-08T15:45:00", + "2024-01-08T15:46:00", + "2024-01-08T15:47:00", + "2024-01-08T15:48:00", + "2024-01-08T15:49:00", + "2024-01-08T15:50:00", + "2024-01-08T15:51:00", + "2024-01-08T15:52:00", + "2024-01-08T15:53:00", + "2024-01-08T15:54:00", + "2024-01-08T15:55:00", + "2024-01-08T15:56:00", + "2024-01-08T15:57:00", + "2024-01-08T15:58:00", + "2024-01-08T15:59:00", + "2024-01-08T16:00:00", + "2024-01-08T16:01:00", + "2024-01-08T16:02:00", + "2024-01-08T16:03:00", + "2024-01-08T16:04:00", + "2024-01-08T16:05:00", + "2024-01-08T16:06:00", + "2024-01-08T16:07:00", + "2024-01-08T16:08:00", + "2024-01-08T16:09:00", + "2024-01-08T16:10:00", + "2024-01-08T16:11:00", + "2024-01-08T16:12:00", + "2024-01-08T16:13:00", + "2024-01-08T16:14:00", + "2024-01-08T16:15:00", + "2024-01-08T16:16:00", + "2024-01-08T16:17:00", + "2024-01-08T16:18:00", + "2024-01-08T16:19:00", + "2024-01-08T16:20:00", + "2024-01-08T16:21:00", + "2024-01-08T16:22:00", + "2024-01-08T16:23:00", + "2024-01-08T16:24:00", + "2024-01-08T16:25:00", + "2024-01-08T16:26:00", + "2024-01-08T16:27:00", + "2024-01-08T16:28:00", + "2024-01-08T16:29:00", + "2024-01-08T16:30:00", + "2024-01-08T16:31:00", + "2024-01-08T16:32:00", + "2024-01-08T16:33:00", + "2024-01-08T16:34:00", + "2024-01-08T16:35:00", + "2024-01-08T16:36:00", + "2024-01-08T16:37:00", + "2024-01-08T16:38:00", + "2024-01-08T16:39:00", + "2024-01-08T16:40:00", + "2024-01-08T16:41:00", + "2024-01-08T16:42:00", + "2024-01-08T16:43:00", + "2024-01-08T16:44:00", + "2024-01-08T16:45:00", + "2024-01-08T16:46:00", + "2024-01-08T16:47:00", + "2024-01-08T16:48:00", + "2024-01-08T16:49:00", + "2024-01-08T16:50:00", + "2024-01-08T16:51:00", + "2024-01-08T16:52:00", + "2024-01-08T16:53:00", + "2024-01-08T16:54:00", + "2024-01-08T16:55:00", + "2024-01-08T16:56:00", + "2024-01-08T16:57:00", + "2024-01-08T16:58:00", + "2024-01-08T16:59:00", + "2024-01-08T17:00:00", + "2024-01-08T17:01:00", + "2024-01-08T17:02:00", + "2024-01-08T17:03:00", + "2024-01-08T17:04:00", + "2024-01-08T17:05:00", + "2024-01-08T17:06:00", + "2024-01-08T17:07:00", + "2024-01-08T17:08:00", + "2024-01-08T17:09:00", + "2024-01-08T17:10:00", + "2024-01-08T17:11:00", + "2024-01-08T17:12:00", + "2024-01-08T17:13:00", + "2024-01-08T17:14:00", + "2024-01-08T17:15:00", + "2024-01-08T17:16:00", + "2024-01-08T17:17:00", + "2024-01-08T17:18:00", + "2024-01-08T17:19:00", + "2024-01-08T17:20:00", + "2024-01-08T17:21:00", + "2024-01-08T17:22:00", + "2024-01-08T17:23:00", + "2024-01-08T17:24:00", + "2024-01-08T17:25:00", + "2024-01-08T17:26:00", + "2024-01-08T17:27:00", + "2024-01-08T17:28:00", + "2024-01-08T17:29:00", + "2024-01-08T17:30:00", + "2024-01-08T17:31:00", + "2024-01-08T17:32:00", + "2024-01-08T17:33:00", + "2024-01-08T17:34:00", + "2024-01-08T17:35:00", + "2024-01-08T17:36:00", + "2024-01-08T17:37:00", + "2024-01-08T17:38:00", + "2024-01-08T17:39:00", + "2024-01-08T17:40:00", + "2024-01-08T17:41:00", + "2024-01-08T17:42:00", + "2024-01-08T17:43:00", + "2024-01-08T17:44:00", + "2024-01-08T17:45:00", + "2024-01-08T17:46:00", + "2024-01-08T17:47:00", + "2024-01-08T17:48:00", + "2024-01-08T17:49:00", + "2024-01-08T17:50:00", + "2024-01-08T17:51:00", + "2024-01-08T17:52:00", + "2024-01-08T17:53:00", + "2024-01-08T17:54:00", + "2024-01-08T17:55:00", + "2024-01-08T17:56:00", + "2024-01-08T17:57:00", + "2024-01-08T17:58:00", + "2024-01-08T17:59:00", + "2024-01-08T18:00:00", + "2024-01-08T18:01:00", + "2024-01-08T18:02:00", + "2024-01-08T18:03:00", + "2024-01-08T18:04:00", + "2024-01-08T18:05:00", + "2024-01-08T18:06:00", + "2024-01-08T18:07:00", + "2024-01-08T18:08:00", + "2024-01-08T18:09:00", + "2024-01-08T18:10:00", + "2024-01-08T18:11:00", + "2024-01-08T18:12:00", + "2024-01-08T18:13:00", + "2024-01-08T18:14:00", + "2024-01-08T18:15:00", + "2024-01-08T18:16:00", + "2024-01-08T18:17:00", + "2024-01-08T18:18:00", + "2024-01-08T18:19:00", + "2024-01-08T18:20:00", + "2024-01-08T18:21:00", + "2024-01-08T18:22:00", + "2024-01-08T18:23:00", + "2024-01-08T18:24:00", + "2024-01-08T18:25:00", + "2024-01-08T18:26:00", + "2024-01-08T18:27:00", + "2024-01-08T18:28:00", + "2024-01-08T18:29:00", + "2024-01-08T18:30:00", + "2024-01-08T18:31:00", + "2024-01-08T18:32:00", + "2024-01-08T18:33:00", + "2024-01-08T18:34:00", + "2024-01-08T18:35:00", + "2024-01-08T18:36:00", + "2024-01-08T18:37:00", + "2024-01-08T18:38:00", + "2024-01-08T18:39:00", + "2024-01-08T18:40:00", + "2024-01-08T18:41:00", + "2024-01-08T18:42:00", + "2024-01-08T18:43:00", + "2024-01-08T18:44:00", + "2024-01-08T18:45:00", + "2024-01-08T18:46:00", + "2024-01-08T18:47:00", + "2024-01-08T18:48:00", + "2024-01-08T18:49:00", + "2024-01-08T18:50:00", + "2024-01-08T18:51:00", + "2024-01-08T18:52:00", + "2024-01-08T18:53:00", + "2024-01-08T18:54:00", + "2024-01-08T18:55:00", + "2024-01-08T18:56:00", + "2024-01-08T18:57:00", + "2024-01-08T18:58:00", + "2024-01-08T18:59:00", + "2024-01-08T19:00:00", + "2024-01-08T19:01:00", + "2024-01-08T19:02:00", + "2024-01-08T19:03:00", + "2024-01-08T19:04:00", + "2024-01-08T19:05:00", + "2024-01-08T19:06:00", + "2024-01-08T19:07:00", + "2024-01-08T19:08:00", + "2024-01-08T19:09:00", + "2024-01-08T19:10:00", + "2024-01-08T19:11:00", + "2024-01-08T19:12:00", + "2024-01-08T19:13:00", + "2024-01-08T19:14:00", + "2024-01-08T19:15:00", + "2024-01-08T19:16:00", + "2024-01-08T19:17:00", + "2024-01-08T19:18:00", + "2024-01-08T19:19:00", + "2024-01-08T19:20:00", + "2024-01-08T19:21:00", + "2024-01-08T19:22:00", + "2024-01-08T19:23:00", + "2024-01-08T19:24:00", + "2024-01-08T19:25:00", + "2024-01-08T19:26:00", + "2024-01-08T19:27:00", + "2024-01-08T19:28:00", + "2024-01-08T19:29:00", + "2024-01-08T19:30:00", + "2024-01-08T19:31:00", + "2024-01-08T19:32:00", + "2024-01-08T19:33:00", + "2024-01-08T19:34:00", + "2024-01-08T19:35:00", + "2024-01-08T19:36:00", + "2024-01-08T19:37:00", + "2024-01-08T19:38:00", + "2024-01-08T19:39:00", + "2024-01-08T19:40:00", + "2024-01-08T19:41:00", + "2024-01-08T19:42:00", + "2024-01-08T19:43:00", + "2024-01-08T19:44:00", + "2024-01-08T19:45:00", + "2024-01-08T19:46:00", + "2024-01-08T19:47:00", + "2024-01-08T19:48:00", + "2024-01-08T19:49:00", + "2024-01-08T19:50:00", + "2024-01-08T19:51:00", + "2024-01-08T19:52:00", + "2024-01-08T19:53:00", + "2024-01-08T19:54:00", + "2024-01-08T19:55:00", + "2024-01-08T19:56:00", + "2024-01-08T19:57:00", + "2024-01-08T19:58:00", + "2024-01-08T19:59:00", + "2024-01-08T20:00:00", + "2024-01-08T20:01:00", + "2024-01-08T20:02:00", + "2024-01-08T20:03:00", + "2024-01-08T20:04:00", + "2024-01-08T20:05:00", + "2024-01-08T20:06:00", + "2024-01-08T20:07:00", + "2024-01-08T20:08:00", + "2024-01-08T20:09:00", + "2024-01-08T20:10:00", + "2024-01-08T20:11:00", + "2024-01-08T20:12:00", + "2024-01-08T20:13:00", + "2024-01-08T20:14:00", + "2024-01-08T20:15:00", + "2024-01-08T20:16:00", + "2024-01-08T20:17:00", + "2024-01-08T20:18:00", + "2024-01-08T20:19:00", + "2024-01-08T20:20:00", + "2024-01-08T20:21:00", + "2024-01-08T20:22:00", + "2024-01-08T20:23:00", + "2024-01-08T20:24:00", + "2024-01-08T20:25:00", + "2024-01-08T20:26:00", + "2024-01-08T20:27:00", + "2024-01-08T20:28:00", + "2024-01-08T20:29:00", + "2024-01-08T20:30:00", + "2024-01-08T20:31:00", + "2024-01-08T20:32:00", + "2024-01-08T20:33:00", + "2024-01-08T20:34:00", + "2024-01-08T20:35:00", + "2024-01-08T20:36:00", + "2024-01-08T20:37:00", + "2024-01-08T20:38:00", + "2024-01-08T20:39:00", + "2024-01-08T20:40:00", + "2024-01-08T20:41:00", + "2024-01-08T20:42:00", + "2024-01-08T20:43:00", + "2024-01-08T20:44:00", + "2024-01-08T20:45:00", + "2024-01-08T20:46:00", + "2024-01-08T20:47:00", + "2024-01-08T20:48:00", + "2024-01-08T20:49:00", + "2024-01-08T20:50:00", + "2024-01-08T20:51:00", + "2024-01-08T20:52:00", + "2024-01-08T20:53:00", + "2024-01-08T20:54:00", + "2024-01-08T20:55:00", + "2024-01-08T20:56:00", + "2024-01-08T20:57:00", + "2024-01-08T20:58:00", + "2024-01-08T20:59:00", + "2024-01-08T21:00:00", + "2024-01-08T21:01:00", + "2024-01-08T21:02:00", + "2024-01-08T21:03:00", + "2024-01-08T21:04:00", + "2024-01-08T21:05:00", + "2024-01-08T21:06:00", + "2024-01-08T21:07:00", + "2024-01-08T21:08:00", + "2024-01-08T21:09:00", + "2024-01-08T21:10:00", + "2024-01-08T21:11:00", + "2024-01-08T21:12:00", + "2024-01-08T21:13:00", + "2024-01-08T21:14:00", + "2024-01-08T21:15:00", + "2024-01-08T21:16:00", + "2024-01-08T21:17:00", + "2024-01-08T21:18:00", + "2024-01-08T21:19:00", + "2024-01-08T21:20:00", + "2024-01-08T21:21:00", + "2024-01-08T21:22:00", + "2024-01-08T21:23:00", + "2024-01-08T21:24:00", + "2024-01-08T21:25:00", + "2024-01-08T21:26:00", + "2024-01-08T21:27:00", + "2024-01-08T21:28:00", + "2024-01-08T21:29:00", + "2024-01-08T21:30:00", + "2024-01-08T21:31:00", + "2024-01-08T21:32:00", + "2024-01-08T21:33:00", + "2024-01-08T21:34:00", + "2024-01-08T21:35:00", + "2024-01-08T21:36:00", + "2024-01-08T21:37:00", + "2024-01-08T21:38:00", + "2024-01-08T21:39:00", + "2024-01-08T21:40:00", + "2024-01-08T21:41:00", + "2024-01-08T21:42:00", + "2024-01-08T21:43:00", + "2024-01-08T21:44:00", + "2024-01-08T21:45:00", + "2024-01-08T21:46:00", + "2024-01-08T21:47:00", + "2024-01-08T21:48:00", + "2024-01-08T21:49:00", + "2024-01-08T21:50:00", + "2024-01-08T21:51:00", + "2024-01-08T21:52:00", + "2024-01-08T21:53:00", + "2024-01-08T21:54:00", + "2024-01-08T21:55:00", + "2024-01-08T21:56:00", + "2024-01-08T21:57:00", + "2024-01-08T21:58:00", + "2024-01-08T21:59:00", + "2024-01-08T22:00:00", + "2024-01-08T22:01:00", + "2024-01-08T22:02:00", + "2024-01-08T22:03:00", + "2024-01-08T22:04:00", + "2024-01-08T22:05:00", + "2024-01-08T22:06:00", + "2024-01-08T22:07:00", + "2024-01-08T22:08:00", + "2024-01-08T22:09:00", + "2024-01-08T22:10:00", + "2024-01-08T22:11:00", + "2024-01-08T22:12:00", + "2024-01-08T22:13:00", + "2024-01-08T22:14:00", + "2024-01-08T22:15:00", + "2024-01-08T22:16:00", + "2024-01-08T22:17:00", + "2024-01-08T22:18:00", + "2024-01-08T22:19:00", + "2024-01-08T22:20:00", + "2024-01-08T22:21:00", + "2024-01-08T22:22:00", + "2024-01-08T22:23:00", + "2024-01-08T22:24:00", + "2024-01-08T22:25:00", + "2024-01-08T22:26:00", + "2024-01-08T22:27:00", + "2024-01-08T22:28:00", + "2024-01-08T22:29:00", + "2024-01-08T22:30:00", + "2024-01-08T22:31:00", + "2024-01-08T22:32:00", + "2024-01-08T22:33:00", + "2024-01-08T22:34:00", + "2024-01-08T22:35:00", + "2024-01-08T22:36:00", + "2024-01-08T22:37:00", + "2024-01-08T22:38:00", + "2024-01-08T22:39:00", + "2024-01-08T22:40:00", + "2024-01-08T22:41:00", + "2024-01-08T22:42:00", + "2024-01-08T22:43:00", + "2024-01-08T22:44:00", + "2024-01-08T22:45:00", + "2024-01-08T22:46:00", + "2024-01-08T22:47:00", + "2024-01-08T22:48:00", + "2024-01-08T22:49:00", + "2024-01-08T22:50:00", + "2024-01-08T22:51:00", + "2024-01-08T22:52:00", + "2024-01-08T22:53:00", + "2024-01-08T22:54:00", + "2024-01-08T22:55:00", + "2024-01-08T22:56:00", + "2024-01-08T22:57:00", + "2024-01-08T22:58:00", + "2024-01-08T22:59:00", + "2024-01-08T23:00:00", + "2024-01-08T23:01:00", + "2024-01-08T23:02:00", + "2024-01-08T23:03:00", + "2024-01-08T23:04:00", + "2024-01-08T23:05:00", + "2024-01-08T23:06:00", + "2024-01-08T23:07:00", + "2024-01-08T23:08:00", + "2024-01-08T23:09:00", + "2024-01-08T23:10:00", + "2024-01-08T23:11:00", + "2024-01-08T23:12:00", + "2024-01-08T23:13:00", + "2024-01-08T23:14:00", + "2024-01-08T23:15:00", + "2024-01-08T23:16:00", + "2024-01-08T23:17:00", + "2024-01-08T23:18:00", + "2024-01-08T23:19:00", + "2024-01-08T23:20:00", + "2024-01-08T23:21:00", + "2024-01-08T23:22:00", + "2024-01-08T23:23:00", + "2024-01-08T23:24:00", + "2024-01-08T23:25:00", + "2024-01-08T23:26:00", + "2024-01-08T23:27:00", + "2024-01-08T23:28:00", + "2024-01-08T23:29:00", + "2024-01-08T23:30:00", + "2024-01-08T23:31:00", + "2024-01-08T23:32:00", + "2024-01-08T23:33:00", + "2024-01-08T23:34:00", + "2024-01-08T23:35:00", + "2024-01-08T23:36:00", + "2024-01-08T23:37:00", + "2024-01-08T23:38:00", + "2024-01-08T23:39:00", + "2024-01-08T23:40:00", + "2024-01-08T23:41:00", + "2024-01-08T23:42:00", + "2024-01-08T23:43:00", + "2024-01-08T23:44:00", + "2024-01-08T23:45:00", + "2024-01-08T23:46:00", + "2024-01-08T23:47:00", + "2024-01-08T23:48:00", + "2024-01-08T23:49:00", + "2024-01-08T23:50:00", + "2024-01-08T23:51:00", + "2024-01-08T23:52:00", + "2024-01-08T23:53:00", + "2024-01-08T23:54:00", + "2024-01-08T23:55:00", + "2024-01-08T23:56:00", + "2024-01-08T23:57:00", + "2024-01-08T23:58:00", + "2024-01-08T23:59:00", + "2024-01-09T00:00:00", + "2024-01-09T00:01:00", + "2024-01-09T00:02:00", + "2024-01-09T00:03:00", + "2024-01-09T00:04:00", + "2024-01-09T00:05:00", + "2024-01-09T00:06:00", + "2024-01-09T00:07:00", + "2024-01-09T00:08:00", + "2024-01-09T00:09:00", + "2024-01-09T00:10:00", + "2024-01-09T00:11:00", + "2024-01-09T00:12:00", + "2024-01-09T00:13:00", + "2024-01-09T00:14:00", + "2024-01-09T00:15:00", + "2024-01-09T00:16:00", + "2024-01-09T00:17:00", + "2024-01-09T00:18:00", + "2024-01-09T00:19:00", + "2024-01-09T00:20:00", + "2024-01-09T00:21:00", + "2024-01-09T00:22:00", + "2024-01-09T00:23:00", + "2024-01-09T00:24:00", + "2024-01-09T00:25:00", + "2024-01-09T00:26:00", + "2024-01-09T00:27:00", + "2024-01-09T00:28:00", + "2024-01-09T00:29:00", + "2024-01-09T00:30:00", + "2024-01-09T00:31:00", + "2024-01-09T00:32:00", + "2024-01-09T00:33:00", + "2024-01-09T00:34:00", + "2024-01-09T00:35:00", + "2024-01-09T00:36:00", + "2024-01-09T00:37:00", + "2024-01-09T00:38:00", + "2024-01-09T00:39:00", + "2024-01-09T00:40:00", + "2024-01-09T00:41:00", + "2024-01-09T00:42:00", + "2024-01-09T00:43:00", + "2024-01-09T00:44:00", + "2024-01-09T00:45:00", + "2024-01-09T00:46:00", + "2024-01-09T00:47:00", + "2024-01-09T00:48:00", + "2024-01-09T00:49:00", + "2024-01-09T00:50:00", + "2024-01-09T00:51:00", + "2024-01-09T00:52:00", + "2024-01-09T00:53:00", + "2024-01-09T00:54:00", + "2024-01-09T00:55:00", + "2024-01-09T00:56:00", + "2024-01-09T00:57:00", + "2024-01-09T00:58:00", + "2024-01-09T00:59:00", + "2024-01-09T01:00:00", + "2024-01-09T01:01:00", + "2024-01-09T01:02:00", + "2024-01-09T01:03:00", + "2024-01-09T01:04:00", + "2024-01-09T01:05:00", + "2024-01-09T01:06:00", + "2024-01-09T01:07:00", + "2024-01-09T01:08:00", + "2024-01-09T01:09:00", + "2024-01-09T01:10:00", + "2024-01-09T01:11:00", + "2024-01-09T01:12:00", + "2024-01-09T01:13:00", + "2024-01-09T01:14:00", + "2024-01-09T01:15:00", + "2024-01-09T01:16:00", + "2024-01-09T01:17:00", + "2024-01-09T01:18:00", + "2024-01-09T01:19:00", + "2024-01-09T01:20:00", + "2024-01-09T01:21:00", + "2024-01-09T01:22:00", + "2024-01-09T01:23:00", + "2024-01-09T01:24:00", + "2024-01-09T01:25:00", + "2024-01-09T01:26:00", + "2024-01-09T01:27:00", + "2024-01-09T01:28:00", + "2024-01-09T01:29:00", + "2024-01-09T01:30:00", + "2024-01-09T01:31:00", + "2024-01-09T01:32:00", + "2024-01-09T01:33:00", + "2024-01-09T01:34:00", + "2024-01-09T01:35:00", + "2024-01-09T01:36:00", + "2024-01-09T01:37:00", + "2024-01-09T01:38:00", + "2024-01-09T01:39:00", + "2024-01-09T01:40:00", + "2024-01-09T01:41:00", + "2024-01-09T01:42:00", + "2024-01-09T01:43:00", + "2024-01-09T01:44:00", + "2024-01-09T01:45:00", + "2024-01-09T01:46:00", + "2024-01-09T01:47:00", + "2024-01-09T01:48:00", + "2024-01-09T01:49:00", + "2024-01-09T01:50:00", + "2024-01-09T01:51:00", + "2024-01-09T01:52:00", + "2024-01-09T01:53:00", + "2024-01-09T01:54:00", + "2024-01-09T01:55:00", + "2024-01-09T01:56:00", + "2024-01-09T01:57:00", + "2024-01-09T01:58:00", + "2024-01-09T01:59:00", + "2024-01-09T02:00:00", + "2024-01-09T02:01:00", + "2024-01-09T02:02:00", + "2024-01-09T02:03:00", + "2024-01-09T02:04:00", + "2024-01-09T02:05:00", + "2024-01-09T02:06:00", + "2024-01-09T02:07:00", + "2024-01-09T02:08:00", + "2024-01-09T02:09:00", + "2024-01-09T02:10:00", + "2024-01-09T02:11:00", + "2024-01-09T02:12:00", + "2024-01-09T02:13:00", + "2024-01-09T02:14:00", + "2024-01-09T02:15:00", + "2024-01-09T02:16:00", + "2024-01-09T02:17:00", + "2024-01-09T02:18:00", + "2024-01-09T02:19:00", + "2024-01-09T02:20:00", + "2024-01-09T02:21:00", + "2024-01-09T02:22:00", + "2024-01-09T02:23:00", + "2024-01-09T02:24:00", + "2024-01-09T02:25:00", + "2024-01-09T02:26:00", + "2024-01-09T02:27:00", + "2024-01-09T02:28:00", + "2024-01-09T02:29:00", + "2024-01-09T02:30:00", + "2024-01-09T02:31:00", + "2024-01-09T02:32:00", + "2024-01-09T02:33:00", + "2024-01-09T02:34:00", + "2024-01-09T02:35:00", + "2024-01-09T02:36:00", + "2024-01-09T02:37:00", + "2024-01-09T02:38:00", + "2024-01-09T02:39:00", + "2024-01-09T02:40:00", + "2024-01-09T02:41:00", + "2024-01-09T02:42:00", + "2024-01-09T02:43:00", + "2024-01-09T02:44:00", + "2024-01-09T02:45:00", + "2024-01-09T02:46:00", + "2024-01-09T02:47:00", + "2024-01-09T02:48:00", + "2024-01-09T02:49:00", + "2024-01-09T02:50:00", + "2024-01-09T02:51:00", + "2024-01-09T02:52:00", + "2024-01-09T02:53:00", + "2024-01-09T02:54:00", + "2024-01-09T02:55:00", + "2024-01-09T02:56:00", + "2024-01-09T02:57:00", + "2024-01-09T02:58:00", + "2024-01-09T02:59:00", + "2024-01-09T03:00:00", + "2024-01-09T03:01:00", + "2024-01-09T03:02:00", + "2024-01-09T03:03:00", + "2024-01-09T03:04:00", + "2024-01-09T03:05:00", + "2024-01-09T03:06:00", + "2024-01-09T03:07:00", + "2024-01-09T03:08:00", + "2024-01-09T03:09:00", + "2024-01-09T03:10:00", + "2024-01-09T03:11:00", + "2024-01-09T03:12:00", + "2024-01-09T03:13:00", + "2024-01-09T03:14:00", + "2024-01-09T03:15:00", + "2024-01-09T03:16:00", + "2024-01-09T03:17:00", + "2024-01-09T03:18:00", + "2024-01-09T03:19:00", + "2024-01-09T03:20:00", + "2024-01-09T03:21:00", + "2024-01-09T03:22:00", + "2024-01-09T03:23:00", + "2024-01-09T03:24:00", + "2024-01-09T03:25:00", + "2024-01-09T03:26:00", + "2024-01-09T03:27:00", + "2024-01-09T03:28:00", + "2024-01-09T03:29:00", + "2024-01-09T03:30:00", + "2024-01-09T03:31:00", + "2024-01-09T03:32:00", + "2024-01-09T03:33:00", + "2024-01-09T03:34:00", + "2024-01-09T03:35:00", + "2024-01-09T03:36:00", + "2024-01-09T03:37:00", + "2024-01-09T03:38:00", + "2024-01-09T03:39:00", + "2024-01-09T03:40:00", + "2024-01-09T03:41:00", + "2024-01-09T03:42:00", + "2024-01-09T03:43:00", + "2024-01-09T03:44:00", + "2024-01-09T03:45:00", + "2024-01-09T03:46:00", + "2024-01-09T03:47:00", + "2024-01-09T03:48:00", + "2024-01-09T03:49:00", + "2024-01-09T03:50:00", + "2024-01-09T03:51:00", + "2024-01-09T03:52:00", + "2024-01-09T03:53:00", + "2024-01-09T03:54:00", + "2024-01-09T03:55:00", + "2024-01-09T03:56:00", + "2024-01-09T03:57:00", + "2024-01-09T03:58:00", + "2024-01-09T03:59:00", + "2024-01-09T04:00:00", + "2024-01-09T04:01:00", + "2024-01-09T04:02:00", + "2024-01-09T04:03:00", + "2024-01-09T04:04:00", + "2024-01-09T04:05:00", + "2024-01-09T04:06:00", + "2024-01-09T04:07:00", + "2024-01-09T04:08:00", + "2024-01-09T04:09:00", + "2024-01-09T04:10:00", + "2024-01-09T04:11:00", + "2024-01-09T04:12:00", + "2024-01-09T04:13:00", + "2024-01-09T04:14:00", + "2024-01-09T04:15:00", + "2024-01-09T04:16:00", + "2024-01-09T04:17:00", + "2024-01-09T04:18:00", + "2024-01-09T04:19:00", + "2024-01-09T04:20:00", + "2024-01-09T04:21:00", + "2024-01-09T04:22:00", + "2024-01-09T04:23:00", + "2024-01-09T04:24:00", + "2024-01-09T04:25:00", + "2024-01-09T04:26:00", + "2024-01-09T04:27:00", + "2024-01-09T04:28:00", + "2024-01-09T04:29:00", + "2024-01-09T04:30:00", + "2024-01-09T04:31:00", + "2024-01-09T04:32:00", + "2024-01-09T04:33:00", + "2024-01-09T04:34:00", + "2024-01-09T04:35:00", + "2024-01-09T04:36:00", + "2024-01-09T04:37:00", + "2024-01-09T04:38:00", + "2024-01-09T04:39:00", + "2024-01-09T04:40:00", + "2024-01-09T04:41:00", + "2024-01-09T04:42:00", + "2024-01-09T04:43:00", + "2024-01-09T04:44:00", + "2024-01-09T04:45:00", + "2024-01-09T04:46:00", + "2024-01-09T04:47:00", + "2024-01-09T04:48:00", + "2024-01-09T04:49:00", + "2024-01-09T04:50:00", + "2024-01-09T04:51:00", + "2024-01-09T04:52:00", + "2024-01-09T04:53:00", + "2024-01-09T04:54:00", + "2024-01-09T04:55:00", + "2024-01-09T04:56:00", + "2024-01-09T04:57:00", + "2024-01-09T04:58:00", + "2024-01-09T04:59:00", + "2024-01-09T05:00:00", + "2024-01-09T05:01:00", + "2024-01-09T05:02:00", + "2024-01-09T05:03:00", + "2024-01-09T05:04:00", + "2024-01-09T05:05:00", + "2024-01-09T05:06:00", + "2024-01-09T05:07:00", + "2024-01-09T05:08:00", + "2024-01-09T05:09:00", + "2024-01-09T05:10:00", + "2024-01-09T05:11:00", + "2024-01-09T05:12:00", + "2024-01-09T05:13:00", + "2024-01-09T05:14:00", + "2024-01-09T05:15:00", + "2024-01-09T05:16:00", + "2024-01-09T05:17:00", + "2024-01-09T05:18:00", + "2024-01-09T05:19:00", + "2024-01-09T05:20:00", + "2024-01-09T05:21:00", + "2024-01-09T05:22:00", + "2024-01-09T05:23:00", + "2024-01-09T05:24:00", + "2024-01-09T05:25:00", + "2024-01-09T05:26:00", + "2024-01-09T05:27:00", + "2024-01-09T05:28:00", + "2024-01-09T05:29:00", + "2024-01-09T05:30:00", + "2024-01-09T05:31:00", + "2024-01-09T05:32:00", + "2024-01-09T05:33:00", + "2024-01-09T05:34:00", + "2024-01-09T05:35:00", + "2024-01-09T05:36:00", + "2024-01-09T05:37:00", + "2024-01-09T05:38:00", + "2024-01-09T05:39:00", + "2024-01-09T05:40:00", + "2024-01-09T05:41:00", + "2024-01-09T05:42:00", + "2024-01-09T05:43:00", + "2024-01-09T05:44:00", + "2024-01-09T05:45:00", + "2024-01-09T05:46:00", + "2024-01-09T05:47:00", + "2024-01-09T05:48:00", + "2024-01-09T05:49:00", + "2024-01-09T05:50:00", + "2024-01-09T05:51:00", + "2024-01-09T05:52:00", + "2024-01-09T05:53:00", + "2024-01-09T05:54:00", + "2024-01-09T05:55:00", + "2024-01-09T05:56:00", + "2024-01-09T05:57:00", + "2024-01-09T05:58:00", + "2024-01-09T05:59:00", + "2024-01-09T06:00:00", + "2024-01-09T06:01:00", + "2024-01-09T06:02:00", + "2024-01-09T06:03:00", + "2024-01-09T06:04:00", + "2024-01-09T06:05:00", + "2024-01-09T06:06:00", + "2024-01-09T06:07:00", + "2024-01-09T06:08:00", + "2024-01-09T06:09:00", + "2024-01-09T06:10:00", + "2024-01-09T06:11:00", + "2024-01-09T06:12:00", + "2024-01-09T06:13:00", + "2024-01-09T06:14:00", + "2024-01-09T06:15:00", + "2024-01-09T06:16:00", + "2024-01-09T06:17:00", + "2024-01-09T06:18:00", + "2024-01-09T06:19:00", + "2024-01-09T06:20:00", + "2024-01-09T06:21:00", + "2024-01-09T06:22:00", + "2024-01-09T06:23:00", + "2024-01-09T06:24:00", + "2024-01-09T06:25:00", + "2024-01-09T06:26:00", + "2024-01-09T06:27:00", + "2024-01-09T06:28:00", + "2024-01-09T06:29:00", + "2024-01-09T06:30:00", + "2024-01-09T06:31:00", + "2024-01-09T06:32:00", + "2024-01-09T06:33:00", + "2024-01-09T06:34:00", + "2024-01-09T06:35:00", + "2024-01-09T06:36:00", + "2024-01-09T06:37:00", + "2024-01-09T06:38:00", + "2024-01-09T06:39:00", + "2024-01-09T06:40:00", + "2024-01-09T06:41:00", + "2024-01-09T06:42:00", + "2024-01-09T06:43:00", + "2024-01-09T06:44:00", + "2024-01-09T06:45:00", + "2024-01-09T06:46:00", + "2024-01-09T06:47:00", + "2024-01-09T06:48:00", + "2024-01-09T06:49:00", + "2024-01-09T06:50:00", + "2024-01-09T06:51:00", + "2024-01-09T06:52:00", + "2024-01-09T06:53:00", + "2024-01-09T06:54:00", + "2024-01-09T06:55:00", + "2024-01-09T06:56:00", + "2024-01-09T06:57:00", + "2024-01-09T06:58:00", + "2024-01-09T06:59:00", + "2024-01-09T07:00:00", + "2024-01-09T07:01:00", + "2024-01-09T07:02:00", + "2024-01-09T07:03:00", + "2024-01-09T07:04:00", + "2024-01-09T07:05:00", + "2024-01-09T07:06:00", + "2024-01-09T07:07:00", + "2024-01-09T07:08:00", + "2024-01-09T07:09:00", + "2024-01-09T07:10:00", + "2024-01-09T07:11:00", + "2024-01-09T07:12:00", + "2024-01-09T07:13:00", + "2024-01-09T07:14:00", + "2024-01-09T07:15:00", + "2024-01-09T07:16:00", + "2024-01-09T07:17:00", + "2024-01-09T07:18:00", + "2024-01-09T07:19:00", + "2024-01-09T07:20:00", + "2024-01-09T07:21:00", + "2024-01-09T07:22:00", + "2024-01-09T07:23:00", + "2024-01-09T07:24:00", + "2024-01-09T07:25:00", + "2024-01-09T07:26:00", + "2024-01-09T07:27:00", + "2024-01-09T07:28:00", + "2024-01-09T07:29:00", + "2024-01-09T07:30:00", + "2024-01-09T07:31:00", + "2024-01-09T07:32:00", + "2024-01-09T07:33:00", + "2024-01-09T07:34:00", + "2024-01-09T07:35:00", + "2024-01-09T07:36:00", + "2024-01-09T07:37:00", + "2024-01-09T07:38:00", + "2024-01-09T07:39:00", + "2024-01-09T07:40:00", + "2024-01-09T07:41:00", + "2024-01-09T07:42:00", + "2024-01-09T07:43:00", + "2024-01-09T07:44:00", + "2024-01-09T07:45:00", + "2024-01-09T07:46:00", + "2024-01-09T07:47:00", + "2024-01-09T07:48:00", + "2024-01-09T07:49:00", + "2024-01-09T07:50:00", + "2024-01-09T07:51:00", + "2024-01-09T07:52:00", + "2024-01-09T07:53:00", + "2024-01-09T07:54:00", + "2024-01-09T07:55:00", + "2024-01-09T07:56:00", + "2024-01-09T07:57:00", + "2024-01-09T07:58:00", + "2024-01-09T07:59:00", + "2024-01-09T08:00:00", + "2024-01-09T08:01:00", + "2024-01-09T08:02:00", + "2024-01-09T08:03:00", + "2024-01-09T08:04:00", + "2024-01-09T08:05:00", + "2024-01-09T08:06:00", + "2024-01-09T08:07:00", + "2024-01-09T08:08:00", + "2024-01-09T08:09:00", + "2024-01-09T08:10:00", + "2024-01-09T08:11:00", + "2024-01-09T08:12:00", + "2024-01-09T08:13:00", + "2024-01-09T08:14:00", + "2024-01-09T08:15:00", + "2024-01-09T08:16:00", + "2024-01-09T08:17:00", + "2024-01-09T08:18:00", + "2024-01-09T08:19:00", + "2024-01-09T08:20:00", + "2024-01-09T08:21:00", + "2024-01-09T08:22:00", + "2024-01-09T08:23:00", + "2024-01-09T08:24:00", + "2024-01-09T08:25:00", + "2024-01-09T08:26:00", + "2024-01-09T08:27:00", + "2024-01-09T08:28:00", + "2024-01-09T08:29:00", + "2024-01-09T08:30:00", + "2024-01-09T08:31:00", + "2024-01-09T08:32:00", + "2024-01-09T08:33:00", + "2024-01-09T08:34:00", + "2024-01-09T08:35:00", + "2024-01-09T08:36:00", + "2024-01-09T08:37:00", + "2024-01-09T08:38:00", + "2024-01-09T08:39:00", + "2024-01-09T08:40:00", + "2024-01-09T08:41:00", + "2024-01-09T08:42:00", + "2024-01-09T08:43:00", + "2024-01-09T08:44:00", + "2024-01-09T08:45:00", + "2024-01-09T08:46:00", + "2024-01-09T08:47:00", + "2024-01-09T08:48:00", + "2024-01-09T08:49:00", + "2024-01-09T08:50:00", + "2024-01-09T08:51:00", + "2024-01-09T08:52:00", + "2024-01-09T08:53:00", + "2024-01-09T08:54:00", + "2024-01-09T08:55:00", + "2024-01-09T08:56:00", + "2024-01-09T08:57:00", + "2024-01-09T08:58:00", + "2024-01-09T08:59:00", + "2024-01-09T09:00:00", + "2024-01-09T09:01:00", + "2024-01-09T09:02:00", + "2024-01-09T09:03:00", + "2024-01-09T09:04:00", + "2024-01-09T09:05:00", + "2024-01-09T09:06:00", + "2024-01-09T09:07:00", + "2024-01-09T09:08:00", + "2024-01-09T09:09:00", + "2024-01-09T09:10:00", + "2024-01-09T09:11:00", + "2024-01-09T09:12:00", + "2024-01-09T09:13:00", + "2024-01-09T09:14:00", + "2024-01-09T09:15:00", + "2024-01-09T09:16:00", + "2024-01-09T09:17:00", + "2024-01-09T09:18:00", + "2024-01-09T09:19:00", + "2024-01-09T09:20:00", + "2024-01-09T09:21:00", + "2024-01-09T09:22:00", + "2024-01-09T09:23:00", + "2024-01-09T09:24:00", + "2024-01-09T09:25:00", + "2024-01-09T09:26:00", + "2024-01-09T09:27:00", + "2024-01-09T09:28:00", + "2024-01-09T09:29:00", + "2024-01-09T09:30:00", + "2024-01-09T09:31:00", + "2024-01-09T09:32:00", + "2024-01-09T09:33:00", + "2024-01-09T09:34:00", + "2024-01-09T09:35:00", + "2024-01-09T09:36:00", + "2024-01-09T09:37:00", + "2024-01-09T09:38:00", + "2024-01-09T09:39:00", + "2024-01-09T09:40:00", + "2024-01-09T09:41:00", + "2024-01-09T09:42:00", + "2024-01-09T09:43:00", + "2024-01-09T09:44:00", + "2024-01-09T09:45:00", + "2024-01-09T09:46:00", + "2024-01-09T09:47:00", + "2024-01-09T09:48:00", + "2024-01-09T09:49:00", + "2024-01-09T09:50:00", + "2024-01-09T09:51:00", + "2024-01-09T09:52:00", + "2024-01-09T09:53:00", + "2024-01-09T09:54:00", + "2024-01-09T09:55:00", + "2024-01-09T09:56:00", + "2024-01-09T09:57:00", + "2024-01-09T09:58:00", + "2024-01-09T09:59:00", + "2024-01-09T10:00:00", + "2024-01-09T10:01:00", + "2024-01-09T10:02:00", + "2024-01-09T10:03:00", + "2024-01-09T10:04:00", + "2024-01-09T10:05:00", + "2024-01-09T10:06:00", + "2024-01-09T10:07:00", + "2024-01-09T10:08:00", + "2024-01-09T10:09:00", + "2024-01-09T10:10:00", + "2024-01-09T10:11:00", + "2024-01-09T10:12:00", + "2024-01-09T10:13:00", + "2024-01-09T10:14:00", + "2024-01-09T10:15:00", + "2024-01-09T10:16:00", + "2024-01-09T10:17:00", + "2024-01-09T10:18:00", + "2024-01-09T10:19:00", + "2024-01-09T10:20:00", + "2024-01-09T10:21:00", + "2024-01-09T10:22:00", + "2024-01-09T10:23:00", + "2024-01-09T10:24:00", + "2024-01-09T10:25:00", + "2024-01-09T10:26:00", + "2024-01-09T10:27:00", + "2024-01-09T10:28:00", + "2024-01-09T10:29:00", + "2024-01-09T10:30:00", + "2024-01-09T10:31:00", + "2024-01-09T10:32:00", + "2024-01-09T10:33:00", + "2024-01-09T10:34:00", + "2024-01-09T10:35:00", + "2024-01-09T10:36:00", + "2024-01-09T10:37:00", + "2024-01-09T10:38:00", + "2024-01-09T10:39:00", + "2024-01-09T10:40:00", + "2024-01-09T10:41:00", + "2024-01-09T10:42:00", + "2024-01-09T10:43:00", + "2024-01-09T10:44:00", + "2024-01-09T10:45:00", + "2024-01-09T10:46:00", + "2024-01-09T10:47:00", + "2024-01-09T10:48:00", + "2024-01-09T10:49:00", + "2024-01-09T10:50:00", + "2024-01-09T10:51:00", + "2024-01-09T10:52:00", + "2024-01-09T10:53:00", + "2024-01-09T10:54:00", + "2024-01-09T10:55:00", + "2024-01-09T10:56:00", + "2024-01-09T10:57:00", + "2024-01-09T10:58:00", + "2024-01-09T10:59:00", + "2024-01-09T11:00:00", + "2024-01-09T11:01:00", + "2024-01-09T11:02:00", + "2024-01-09T11:03:00", + "2024-01-09T11:04:00", + "2024-01-09T11:05:00", + "2024-01-09T11:06:00", + "2024-01-09T11:07:00", + "2024-01-09T11:08:00", + "2024-01-09T11:09:00", + "2024-01-09T11:10:00", + "2024-01-09T11:11:00", + "2024-01-09T11:12:00", + "2024-01-09T11:13:00", + "2024-01-09T11:14:00", + "2024-01-09T11:15:00", + "2024-01-09T11:16:00", + "2024-01-09T11:17:00", + "2024-01-09T11:18:00", + "2024-01-09T11:19:00", + "2024-01-09T11:20:00", + "2024-01-09T11:21:00", + "2024-01-09T11:22:00", + "2024-01-09T11:23:00", + "2024-01-09T11:24:00", + "2024-01-09T11:25:00", + "2024-01-09T11:26:00", + "2024-01-09T11:27:00", + "2024-01-09T11:28:00", + "2024-01-09T11:29:00", + "2024-01-09T11:30:00", + "2024-01-09T11:31:00", + "2024-01-09T11:32:00", + "2024-01-09T11:33:00", + "2024-01-09T11:34:00", + "2024-01-09T11:35:00", + "2024-01-09T11:36:00", + "2024-01-09T11:37:00", + "2024-01-09T11:38:00", + "2024-01-09T11:39:00", + "2024-01-09T11:40:00", + "2024-01-09T11:41:00", + "2024-01-09T11:42:00", + "2024-01-09T11:43:00", + "2024-01-09T11:44:00", + "2024-01-09T11:45:00", + "2024-01-09T11:46:00", + "2024-01-09T11:47:00", + "2024-01-09T11:48:00", + "2024-01-09T11:49:00", + "2024-01-09T11:50:00", + "2024-01-09T11:51:00", + "2024-01-09T11:52:00", + "2024-01-09T11:53:00", + "2024-01-09T11:54:00", + "2024-01-09T11:55:00", + "2024-01-09T11:56:00", + "2024-01-09T11:57:00", + "2024-01-09T11:58:00", + "2024-01-09T11:59:00", + "2024-01-09T12:00:00", + "2024-01-09T12:01:00", + "2024-01-09T12:02:00", + "2024-01-09T12:03:00", + "2024-01-09T12:04:00", + "2024-01-09T12:05:00", + "2024-01-09T12:06:00", + "2024-01-09T12:07:00", + "2024-01-09T12:08:00", + "2024-01-09T12:09:00", + "2024-01-09T12:10:00", + "2024-01-09T12:11:00", + "2024-01-09T12:12:00", + "2024-01-09T12:13:00", + "2024-01-09T12:14:00", + "2024-01-09T12:15:00", + "2024-01-09T12:16:00", + "2024-01-09T12:17:00", + "2024-01-09T12:18:00", + "2024-01-09T12:19:00", + "2024-01-09T12:20:00", + "2024-01-09T12:21:00", + "2024-01-09T12:22:00", + "2024-01-09T12:23:00", + "2024-01-09T12:24:00", + "2024-01-09T12:25:00", + "2024-01-09T12:26:00", + "2024-01-09T12:27:00", + "2024-01-09T12:28:00", + "2024-01-09T12:29:00", + "2024-01-09T12:30:00", + "2024-01-09T12:31:00", + "2024-01-09T12:32:00", + "2024-01-09T12:33:00", + "2024-01-09T12:34:00", + "2024-01-09T12:35:00", + "2024-01-09T12:36:00", + "2024-01-09T12:37:00", + "2024-01-09T12:38:00", + "2024-01-09T12:39:00", + "2024-01-09T12:40:00", + "2024-01-09T12:41:00", + "2024-01-09T12:42:00", + "2024-01-09T12:43:00", + "2024-01-09T12:44:00", + "2024-01-09T12:45:00", + "2024-01-09T12:46:00", + "2024-01-09T12:47:00", + "2024-01-09T12:48:00", + "2024-01-09T12:49:00", + "2024-01-09T12:50:00", + "2024-01-09T12:51:00", + "2024-01-09T12:52:00", + "2024-01-09T12:53:00", + "2024-01-09T12:54:00", + "2024-01-09T12:55:00", + "2024-01-09T12:56:00", + "2024-01-09T12:57:00", + "2024-01-09T12:58:00", + "2024-01-09T12:59:00", + "2024-01-09T13:00:00", + "2024-01-09T13:01:00", + "2024-01-09T13:02:00", + "2024-01-09T13:03:00", + "2024-01-09T13:04:00", + "2024-01-09T13:05:00", + "2024-01-09T13:06:00", + "2024-01-09T13:07:00", + "2024-01-09T13:08:00", + "2024-01-09T13:09:00", + "2024-01-09T13:10:00", + "2024-01-09T13:11:00", + "2024-01-09T13:12:00", + "2024-01-09T13:13:00", + "2024-01-09T13:14:00", + "2024-01-09T13:15:00", + "2024-01-09T13:16:00", + "2024-01-09T13:17:00", + "2024-01-09T13:18:00", + "2024-01-09T13:19:00", + "2024-01-09T13:20:00", + "2024-01-09T13:21:00", + "2024-01-09T13:22:00", + "2024-01-09T13:23:00", + "2024-01-09T13:24:00", + "2024-01-09T13:25:00", + "2024-01-09T13:26:00", + "2024-01-09T13:27:00", + "2024-01-09T13:28:00", + "2024-01-09T13:29:00", + "2024-01-09T13:30:00", + "2024-01-09T13:31:00", + "2024-01-09T13:32:00", + "2024-01-09T13:33:00", + "2024-01-09T13:34:00", + "2024-01-09T13:35:00", + "2024-01-09T13:36:00", + "2024-01-09T13:37:00", + "2024-01-09T13:38:00", + "2024-01-09T13:39:00", + "2024-01-09T13:40:00", + "2024-01-09T13:41:00", + "2024-01-09T13:42:00", + "2024-01-09T13:43:00", + "2024-01-09T13:44:00", + "2024-01-09T13:45:00", + "2024-01-09T13:46:00", + "2024-01-09T13:47:00", + "2024-01-09T13:48:00", + "2024-01-09T13:49:00", + "2024-01-09T13:50:00", + "2024-01-09T13:51:00", + "2024-01-09T13:52:00", + "2024-01-09T13:53:00", + "2024-01-09T13:54:00", + "2024-01-09T13:55:00", + "2024-01-09T13:56:00", + "2024-01-09T13:57:00", + "2024-01-09T13:58:00", + "2024-01-09T13:59:00", + "2024-01-09T14:00:00", + "2024-01-09T14:01:00", + "2024-01-09T14:02:00", + "2024-01-09T14:03:00", + "2024-01-09T14:04:00", + "2024-01-09T14:05:00", + "2024-01-09T14:06:00", + "2024-01-09T14:07:00", + "2024-01-09T14:08:00", + "2024-01-09T14:09:00", + "2024-01-09T14:10:00", + "2024-01-09T14:11:00", + "2024-01-09T14:12:00", + "2024-01-09T14:13:00", + "2024-01-09T14:14:00", + "2024-01-09T14:15:00", + "2024-01-09T14:16:00", + "2024-01-09T14:17:00", + "2024-01-09T14:18:00", + "2024-01-09T14:19:00", + "2024-01-09T14:20:00", + "2024-01-09T14:21:00", + "2024-01-09T14:22:00", + "2024-01-09T14:23:00", + "2024-01-09T14:24:00", + "2024-01-09T14:25:00", + "2024-01-09T14:26:00", + "2024-01-09T14:27:00", + "2024-01-09T14:28:00", + "2024-01-09T14:29:00", + "2024-01-09T14:30:00", + "2024-01-09T14:31:00", + "2024-01-09T14:32:00", + "2024-01-09T14:33:00", + "2024-01-09T14:34:00", + "2024-01-09T14:35:00", + "2024-01-09T14:36:00", + "2024-01-09T14:37:00", + "2024-01-09T14:38:00", + "2024-01-09T14:39:00", + "2024-01-09T14:40:00", + "2024-01-09T14:41:00", + "2024-01-09T14:42:00", + "2024-01-09T14:43:00", + "2024-01-09T14:44:00", + "2024-01-09T14:45:00", + "2024-01-09T14:46:00", + "2024-01-09T14:47:00", + "2024-01-09T14:48:00", + "2024-01-09T14:49:00", + "2024-01-09T14:50:00", + "2024-01-09T14:51:00", + "2024-01-09T14:52:00", + "2024-01-09T14:53:00", + "2024-01-09T14:54:00", + "2024-01-09T14:55:00", + "2024-01-09T14:56:00", + "2024-01-09T14:57:00", + "2024-01-09T14:58:00", + "2024-01-09T14:59:00", + "2024-01-09T15:00:00", + "2024-01-09T15:01:00", + "2024-01-09T15:02:00", + "2024-01-09T15:03:00", + "2024-01-09T15:04:00", + "2024-01-09T15:05:00", + "2024-01-09T15:06:00", + "2024-01-09T15:07:00", + "2024-01-09T15:08:00", + "2024-01-09T15:09:00", + "2024-01-09T15:10:00", + "2024-01-09T15:11:00", + "2024-01-09T15:12:00", + "2024-01-09T15:13:00", + "2024-01-09T15:14:00", + "2024-01-09T15:15:00", + "2024-01-09T15:16:00", + "2024-01-09T15:17:00", + "2024-01-09T15:18:00", + "2024-01-09T15:19:00", + "2024-01-09T15:20:00", + "2024-01-09T15:21:00", + "2024-01-09T15:22:00", + "2024-01-09T15:23:00", + "2024-01-09T15:24:00", + "2024-01-09T15:25:00", + "2024-01-09T15:26:00", + "2024-01-09T15:27:00", + "2024-01-09T15:28:00", + "2024-01-09T15:29:00", + "2024-01-09T15:30:00", + "2024-01-09T15:31:00", + "2024-01-09T15:32:00", + "2024-01-09T15:33:00", + "2024-01-09T15:34:00", + "2024-01-09T15:35:00", + "2024-01-09T15:36:00", + "2024-01-09T15:37:00", + "2024-01-09T15:38:00", + "2024-01-09T15:39:00", + "2024-01-09T15:40:00", + "2024-01-09T15:41:00", + "2024-01-09T15:42:00", + "2024-01-09T15:43:00", + "2024-01-09T15:44:00", + "2024-01-09T15:45:00", + "2024-01-09T15:46:00", + "2024-01-09T15:47:00", + "2024-01-09T15:48:00", + "2024-01-09T15:49:00", + "2024-01-09T15:50:00", + "2024-01-09T15:51:00", + "2024-01-09T15:52:00", + "2024-01-09T15:53:00", + "2024-01-09T15:54:00", + "2024-01-09T15:55:00", + "2024-01-09T15:56:00", + "2024-01-09T15:57:00", + "2024-01-09T15:58:00", + "2024-01-09T15:59:00", + "2024-01-09T16:00:00", + "2024-01-09T16:01:00", + "2024-01-09T16:02:00", + "2024-01-09T16:03:00", + "2024-01-09T16:04:00", + "2024-01-09T16:05:00", + "2024-01-09T16:06:00", + "2024-01-09T16:07:00", + "2024-01-09T16:08:00", + "2024-01-09T16:09:00", + "2024-01-09T16:10:00", + "2024-01-09T16:11:00", + "2024-01-09T16:12:00", + "2024-01-09T16:13:00", + "2024-01-09T16:14:00", + "2024-01-09T16:15:00", + "2024-01-09T16:16:00", + "2024-01-09T16:17:00", + "2024-01-09T16:18:00", + "2024-01-09T16:19:00", + "2024-01-09T16:20:00", + "2024-01-09T16:21:00", + "2024-01-09T16:22:00", + "2024-01-09T16:23:00", + "2024-01-09T16:24:00", + "2024-01-09T16:25:00", + "2024-01-09T16:26:00", + "2024-01-09T16:27:00", + "2024-01-09T16:28:00", + "2024-01-09T16:29:00", + "2024-01-09T16:30:00", + "2024-01-09T16:31:00", + "2024-01-09T16:32:00", + "2024-01-09T16:33:00", + "2024-01-09T16:34:00", + "2024-01-09T16:35:00", + "2024-01-09T16:36:00", + "2024-01-09T16:37:00", + "2024-01-09T16:38:00", + "2024-01-09T16:39:00", + "2024-01-09T16:40:00", + "2024-01-09T16:41:00", + "2024-01-09T16:42:00", + "2024-01-09T16:43:00", + "2024-01-09T16:44:00", + "2024-01-09T16:45:00", + "2024-01-09T16:46:00", + "2024-01-09T16:47:00", + "2024-01-09T16:48:00", + "2024-01-09T16:49:00", + "2024-01-09T16:50:00", + "2024-01-09T16:51:00", + "2024-01-09T16:52:00", + "2024-01-09T16:53:00", + "2024-01-09T16:54:00", + "2024-01-09T16:55:00", + "2024-01-09T16:56:00", + "2024-01-09T16:57:00", + "2024-01-09T16:58:00", + "2024-01-09T16:59:00", + "2024-01-09T17:00:00", + "2024-01-09T17:01:00", + "2024-01-09T17:02:00", + "2024-01-09T17:03:00", + "2024-01-09T17:04:00", + "2024-01-09T17:05:00", + "2024-01-09T17:06:00", + "2024-01-09T17:07:00", + "2024-01-09T17:08:00", + "2024-01-09T17:09:00", + "2024-01-09T17:10:00", + "2024-01-09T17:11:00", + "2024-01-09T17:12:00", + "2024-01-09T17:13:00", + "2024-01-09T17:14:00", + "2024-01-09T17:15:00", + "2024-01-09T17:16:00", + "2024-01-09T17:17:00", + "2024-01-09T17:18:00", + "2024-01-09T17:19:00", + "2024-01-09T17:20:00", + "2024-01-09T17:21:00", + "2024-01-09T17:22:00", + "2024-01-09T17:23:00", + "2024-01-09T17:24:00", + "2024-01-09T17:25:00", + "2024-01-09T17:26:00", + "2024-01-09T17:27:00", + "2024-01-09T17:28:00", + "2024-01-09T17:29:00", + "2024-01-09T17:30:00", + "2024-01-09T17:31:00", + "2024-01-09T17:32:00", + "2024-01-09T17:33:00", + "2024-01-09T17:34:00", + "2024-01-09T17:35:00", + "2024-01-09T17:36:00", + "2024-01-09T17:37:00", + "2024-01-09T17:38:00", + "2024-01-09T17:39:00", + "2024-01-09T17:40:00", + "2024-01-09T17:41:00", + "2024-01-09T17:42:00", + "2024-01-09T17:43:00", + "2024-01-09T17:44:00", + "2024-01-09T17:45:00", + "2024-01-09T17:46:00", + "2024-01-09T17:47:00", + "2024-01-09T17:48:00", + "2024-01-09T17:49:00", + "2024-01-09T17:50:00", + "2024-01-09T17:51:00", + "2024-01-09T17:52:00", + "2024-01-09T17:53:00", + "2024-01-09T17:54:00", + "2024-01-09T17:55:00", + "2024-01-09T17:56:00", + "2024-01-09T17:57:00", + "2024-01-09T17:58:00", + "2024-01-09T17:59:00", + "2024-01-09T18:00:00", + "2024-01-09T18:01:00", + "2024-01-09T18:02:00", + "2024-01-09T18:03:00", + "2024-01-09T18:04:00", + "2024-01-09T18:05:00", + "2024-01-09T18:06:00", + "2024-01-09T18:07:00", + "2024-01-09T18:08:00", + "2024-01-09T18:09:00", + "2024-01-09T18:10:00", + "2024-01-09T18:11:00", + "2024-01-09T18:12:00", + "2024-01-09T18:13:00", + "2024-01-09T18:14:00", + "2024-01-09T18:15:00", + "2024-01-09T18:16:00", + "2024-01-09T18:17:00", + "2024-01-09T18:18:00", + "2024-01-09T18:19:00", + "2024-01-09T18:20:00", + "2024-01-09T18:21:00", + "2024-01-09T18:22:00", + "2024-01-09T18:23:00", + "2024-01-09T18:24:00", + "2024-01-09T18:25:00", + "2024-01-09T18:26:00", + "2024-01-09T18:27:00", + "2024-01-09T18:28:00", + "2024-01-09T18:29:00", + "2024-01-09T18:30:00", + "2024-01-09T18:31:00", + "2024-01-09T18:32:00", + "2024-01-09T18:33:00", + "2024-01-09T18:34:00", + "2024-01-09T18:35:00", + "2024-01-09T18:36:00", + "2024-01-09T18:37:00", + "2024-01-09T18:38:00", + "2024-01-09T18:39:00", + "2024-01-09T18:40:00", + "2024-01-09T18:41:00", + "2024-01-09T18:42:00", + "2024-01-09T18:43:00", + "2024-01-09T18:44:00", + "2024-01-09T18:45:00", + "2024-01-09T18:46:00", + "2024-01-09T18:47:00", + "2024-01-09T18:48:00", + "2024-01-09T18:49:00", + "2024-01-09T18:50:00", + "2024-01-09T18:51:00", + "2024-01-09T18:52:00", + "2024-01-09T18:53:00", + "2024-01-09T18:54:00", + "2024-01-09T18:55:00", + "2024-01-09T18:56:00", + "2024-01-09T18:57:00", + "2024-01-09T18:58:00", + "2024-01-09T18:59:00", + "2024-01-09T19:00:00", + "2024-01-09T19:01:00", + "2024-01-09T19:02:00", + "2024-01-09T19:03:00", + "2024-01-09T19:04:00", + "2024-01-09T19:05:00", + "2024-01-09T19:06:00", + "2024-01-09T19:07:00", + "2024-01-09T19:08:00", + "2024-01-09T19:09:00", + "2024-01-09T19:10:00", + "2024-01-09T19:11:00", + "2024-01-09T19:12:00", + "2024-01-09T19:13:00", + "2024-01-09T19:14:00", + "2024-01-09T19:15:00", + "2024-01-09T19:16:00", + "2024-01-09T19:17:00", + "2024-01-09T19:18:00", + "2024-01-09T19:19:00", + "2024-01-09T19:20:00", + "2024-01-09T19:21:00", + "2024-01-09T19:22:00", + "2024-01-09T19:23:00", + "2024-01-09T19:24:00", + "2024-01-09T19:25:00", + "2024-01-09T19:26:00", + "2024-01-09T19:27:00", + "2024-01-09T19:28:00", + "2024-01-09T19:29:00", + "2024-01-09T19:30:00", + "2024-01-09T19:31:00", + "2024-01-09T19:32:00", + "2024-01-09T19:33:00", + "2024-01-09T19:34:00", + "2024-01-09T19:35:00", + "2024-01-09T19:36:00", + "2024-01-09T19:37:00", + "2024-01-09T19:38:00", + "2024-01-09T19:39:00", + "2024-01-09T19:40:00", + "2024-01-09T19:41:00", + "2024-01-09T19:42:00", + "2024-01-09T19:43:00", + "2024-01-09T19:44:00", + "2024-01-09T19:45:00", + "2024-01-09T19:46:00", + "2024-01-09T19:47:00", + "2024-01-09T19:48:00", + "2024-01-09T19:49:00", + "2024-01-09T19:50:00", + "2024-01-09T19:51:00", + "2024-01-09T19:52:00", + "2024-01-09T19:53:00", + "2024-01-09T19:54:00", + "2024-01-09T19:55:00", + "2024-01-09T19:56:00", + "2024-01-09T19:57:00", + "2024-01-09T19:58:00", + "2024-01-09T19:59:00", + "2024-01-09T20:00:00", + "2024-01-09T20:01:00", + "2024-01-09T20:02:00", + "2024-01-09T20:03:00", + "2024-01-09T20:04:00", + "2024-01-09T20:05:00", + "2024-01-09T20:06:00", + "2024-01-09T20:07:00", + "2024-01-09T20:08:00", + "2024-01-09T20:09:00", + "2024-01-09T20:10:00", + "2024-01-09T20:11:00", + "2024-01-09T20:12:00", + "2024-01-09T20:13:00", + "2024-01-09T20:14:00", + "2024-01-09T20:15:00", + "2024-01-09T20:16:00", + "2024-01-09T20:17:00", + "2024-01-09T20:18:00", + "2024-01-09T20:19:00", + "2024-01-09T20:20:00", + "2024-01-09T20:21:00", + "2024-01-09T20:22:00", + "2024-01-09T20:23:00", + "2024-01-09T20:24:00", + "2024-01-09T20:25:00", + "2024-01-09T20:26:00", + "2024-01-09T20:27:00", + "2024-01-09T20:28:00", + "2024-01-09T20:29:00", + "2024-01-09T20:30:00", + "2024-01-09T20:31:00", + "2024-01-09T20:32:00", + "2024-01-09T20:33:00", + "2024-01-09T20:34:00", + "2024-01-09T20:35:00", + "2024-01-09T20:36:00", + "2024-01-09T20:37:00", + "2024-01-09T20:38:00", + "2024-01-09T20:39:00", + "2024-01-09T20:40:00", + "2024-01-09T20:41:00", + "2024-01-09T20:42:00", + "2024-01-09T20:43:00", + "2024-01-09T20:44:00", + "2024-01-09T20:45:00", + "2024-01-09T20:46:00", + "2024-01-09T20:47:00", + "2024-01-09T20:48:00", + "2024-01-09T20:49:00", + "2024-01-09T20:50:00", + "2024-01-09T20:51:00", + "2024-01-09T20:52:00", + "2024-01-09T20:53:00", + "2024-01-09T20:54:00", + "2024-01-09T20:55:00", + "2024-01-09T20:56:00", + "2024-01-09T20:57:00", + "2024-01-09T20:58:00", + "2024-01-09T20:59:00", + "2024-01-09T21:00:00", + "2024-01-09T21:01:00", + "2024-01-09T21:02:00", + "2024-01-09T21:03:00", + "2024-01-09T21:04:00", + "2024-01-09T21:05:00", + "2024-01-09T21:06:00", + "2024-01-09T21:07:00", + "2024-01-09T21:08:00", + "2024-01-09T21:09:00", + "2024-01-09T21:10:00", + "2024-01-09T21:11:00", + "2024-01-09T21:12:00", + "2024-01-09T21:13:00", + "2024-01-09T21:14:00", + "2024-01-09T21:15:00", + "2024-01-09T21:16:00", + "2024-01-09T21:17:00", + "2024-01-09T21:18:00", + "2024-01-09T21:19:00", + "2024-01-09T21:20:00", + "2024-01-09T21:21:00", + "2024-01-09T21:22:00", + "2024-01-09T21:23:00", + "2024-01-09T21:24:00", + "2024-01-09T21:25:00", + "2024-01-09T21:26:00", + "2024-01-09T21:27:00", + "2024-01-09T21:28:00", + "2024-01-09T21:29:00", + "2024-01-09T21:30:00", + "2024-01-09T21:31:00", + "2024-01-09T21:32:00", + "2024-01-09T21:33:00", + "2024-01-09T21:34:00", + "2024-01-09T21:35:00", + "2024-01-09T21:36:00", + "2024-01-09T21:37:00", + "2024-01-09T21:38:00", + "2024-01-09T21:39:00", + "2024-01-09T21:40:00", + "2024-01-09T21:41:00", + "2024-01-09T21:42:00", + "2024-01-09T21:43:00", + "2024-01-09T21:44:00", + "2024-01-09T21:45:00", + "2024-01-09T21:46:00", + "2024-01-09T21:47:00", + "2024-01-09T21:48:00", + "2024-01-09T21:49:00", + "2024-01-09T21:50:00", + "2024-01-09T21:51:00", + "2024-01-09T21:52:00", + "2024-01-09T21:53:00", + "2024-01-09T21:54:00", + "2024-01-09T21:55:00", + "2024-01-09T21:56:00", + "2024-01-09T21:57:00", + "2024-01-09T21:58:00", + "2024-01-09T21:59:00", + "2024-01-09T22:00:00", + "2024-01-09T22:01:00", + "2024-01-09T22:02:00", + "2024-01-09T22:03:00", + "2024-01-09T22:04:00", + "2024-01-09T22:05:00", + "2024-01-09T22:06:00", + "2024-01-09T22:07:00", + "2024-01-09T22:08:00", + "2024-01-09T22:09:00", + "2024-01-09T22:10:00", + "2024-01-09T22:11:00", + "2024-01-09T22:12:00", + "2024-01-09T22:13:00", + "2024-01-09T22:14:00", + "2024-01-09T22:15:00", + "2024-01-09T22:16:00", + "2024-01-09T22:17:00", + "2024-01-09T22:18:00", + "2024-01-09T22:19:00", + "2024-01-09T22:20:00", + "2024-01-09T22:21:00", + "2024-01-09T22:22:00", + "2024-01-09T22:23:00", + "2024-01-09T22:24:00", + "2024-01-09T22:25:00", + "2024-01-09T22:26:00", + "2024-01-09T22:27:00", + "2024-01-09T22:28:00", + "2024-01-09T22:29:00", + "2024-01-09T22:30:00", + "2024-01-09T22:31:00", + "2024-01-09T22:32:00", + "2024-01-09T22:33:00", + "2024-01-09T22:34:00", + "2024-01-09T22:35:00", + "2024-01-09T22:36:00", + "2024-01-09T22:37:00", + "2024-01-09T22:38:00", + "2024-01-09T22:39:00", + "2024-01-09T22:40:00", + "2024-01-09T22:41:00", + "2024-01-09T22:42:00", + "2024-01-09T22:43:00", + "2024-01-09T22:44:00", + "2024-01-09T22:45:00", + "2024-01-09T22:46:00", + "2024-01-09T22:47:00", + "2024-01-09T22:48:00", + "2024-01-09T22:49:00", + "2024-01-09T22:50:00", + "2024-01-09T22:51:00", + "2024-01-09T22:52:00", + "2024-01-09T22:53:00", + "2024-01-09T22:54:00", + "2024-01-09T22:55:00", + "2024-01-09T22:56:00", + "2024-01-09T22:57:00", + "2024-01-09T22:58:00", + "2024-01-09T22:59:00", + "2024-01-09T23:00:00", + "2024-01-09T23:01:00", + "2024-01-09T23:02:00", + "2024-01-09T23:03:00", + "2024-01-09T23:04:00", + "2024-01-09T23:05:00", + "2024-01-09T23:06:00", + "2024-01-09T23:07:00", + "2024-01-09T23:08:00", + "2024-01-09T23:09:00", + "2024-01-09T23:10:00", + "2024-01-09T23:11:00", + "2024-01-09T23:12:00", + "2024-01-09T23:13:00", + "2024-01-09T23:14:00", + "2024-01-09T23:15:00", + "2024-01-09T23:16:00", + "2024-01-09T23:17:00", + "2024-01-09T23:18:00", + "2024-01-09T23:19:00", + "2024-01-09T23:20:00", + "2024-01-09T23:21:00", + "2024-01-09T23:22:00", + "2024-01-09T23:23:00", + "2024-01-09T23:24:00", + "2024-01-09T23:25:00", + "2024-01-09T23:26:00", + "2024-01-09T23:27:00", + "2024-01-09T23:28:00", + "2024-01-09T23:29:00", + "2024-01-09T23:30:00", + "2024-01-09T23:31:00", + "2024-01-09T23:32:00", + "2024-01-09T23:33:00", + "2024-01-09T23:34:00", + "2024-01-09T23:35:00", + "2024-01-09T23:36:00", + "2024-01-09T23:37:00", + "2024-01-09T23:38:00", + "2024-01-09T23:39:00", + "2024-01-09T23:40:00", + "2024-01-09T23:41:00", + "2024-01-09T23:42:00", + "2024-01-09T23:43:00", + "2024-01-09T23:44:00", + "2024-01-09T23:45:00", + "2024-01-09T23:46:00", + "2024-01-09T23:47:00", + "2024-01-09T23:48:00", + "2024-01-09T23:49:00", + "2024-01-09T23:50:00", + "2024-01-09T23:51:00", + "2024-01-09T23:52:00", + "2024-01-09T23:53:00", + "2024-01-09T23:54:00", + "2024-01-09T23:55:00", + "2024-01-09T23:56:00", + "2024-01-09T23:57:00", + "2024-01-09T23:58:00", + "2024-01-09T23:59:00", + "2024-01-10T00:00:00" + ], + "y": [ + 3.6335, + 3.639, + 3.657, + 3.6622, + 3.6622, + 3.6512, + 3.6506, + 3.6519, + 3.6519, + 3.6501, + 3.6501, + 3.6666, + 3.6666, + 3.6629, + 3.6629, + 3.6519, + 3.6551, + 3.6583, + 3.6603, + 3.6532, + 3.6532, + 3.6452, + 3.6482, + 3.6619, + 3.667, + 3.6738, + 3.6562, + 3.6428, + 3.6428, + 3.6472, + 3.6657, + 3.657, + 3.6507, + 3.6516, + 3.6539, + 3.647, + 3.6496, + 3.6601, + 3.6601, + 3.6595, + 3.6595, + 3.6516, + 3.6516, + 3.6581, + 3.6618, + 3.6603, + 3.6597, + 3.6597, + 3.6597, + 3.6684, + 3.6627, + 3.6627, + 3.6697, + 3.6697, + 3.68, + 3.68, + 3.6822, + 3.6825, + 3.6861, + 3.6861, + 3.6861, + 3.6861, + 3.6762, + 3.6762, + 3.6762, + 3.6817, + 3.6874, + 3.6874, + 3.6874, + 3.6874, + 3.6822, + 3.7089, + 3.7133, + 3.7085, + 3.6986, + 3.6986, + 3.7181, + 3.7106, + 3.7249, + 3.7281, + 3.7347, + 3.7349, + 3.7354, + 3.7391, + 3.7416, + 3.7496, + 3.7679, + 3.7638, + 3.7545, + 3.7673, + 3.7699, + 3.7592, + 3.758, + 3.7564, + 3.7578, + 3.74, + 3.7367, + 3.7241, + 3.7282, + 3.7215, + 3.7315, + 3.7377, + 3.7366, + 3.7247, + 3.7247, + 3.7415, + 3.7367, + 3.7362, + 3.7369, + 3.7324, + 3.7324, + 3.7266, + 3.7245, + 3.7245, + 3.7245, + 3.7206, + 3.7206, + 3.7206, + 3.728, + 3.7308, + 3.7368, + 3.7252, + 3.7252, + 3.7291, + 3.7291, + 3.7291, + 3.7291, + 3.7288, + 3.7328, + 3.7328, + 3.7328, + 3.7358, + 3.7219, + 3.7219, + 3.7219, + 3.7166, + 3.711, + 3.7015, + 3.7006, + 3.6957, + 3.6998, + 3.6929, + 3.6963, + 3.6908, + 3.6864, + 3.6814, + 3.6849, + 3.6849, + 3.6864, + 3.6831, + 3.6843, + 3.681, + 3.6795, + 3.6783, + 3.6786, + 3.6786, + 3.6786, + 3.6732, + 3.679, + 3.6715, + 3.6688, + 3.6552, + 3.6576, + 3.6477, + 3.6477, + 3.6333, + 3.6321, + 3.6299, + 3.6304, + 3.6298, + 3.6262, + 3.6374, + 3.6441, + 3.6485, + 3.651, + 3.6544, + 3.6563, + 3.6564, + 3.653, + 3.653, + 3.6512, + 3.6512, + 3.6512, + 3.6512, + 3.6413, + 3.6442, + 3.6442, + 3.6438, + 3.6303, + 3.6327, + 3.6297, + 3.6196, + 3.6153, + 3.6004, + 3.5942, + 3.6018, + 3.6011, + 3.5925, + 3.5886, + 3.5883, + 3.6052, + 3.6137, + 3.6142, + 3.5937, + 3.5937, + 3.6067, + 3.5973, + 3.5952, + 3.5952, + 3.5958, + 3.5851, + 3.5935, + 3.5925, + 3.5842, + 3.5842, + 3.5886, + 3.5983, + 3.5988, + 3.5988, + 3.5902, + 3.5806, + 3.5766, + 3.5699, + 3.5642, + 3.567, + 3.5513, + 3.5496, + 3.5576, + 3.5644, + 3.5644, + 3.5655, + 3.5655, + 3.5677, + 3.5636, + 3.5636, + 3.5636, + 3.5676, + 3.5676, + 3.5676, + 3.5713, + 3.5699, + 3.5847, + 3.5935, + 3.5943, + 3.5958, + 3.596, + 3.5939, + 3.5939, + 3.5962, + 3.593, + 3.5725, + 3.5665, + 3.5803, + 3.5562, + 3.5645, + 3.5616, + 3.5673, + 3.5618, + 3.5586, + 3.5428, + 3.5395, + 3.5343, + 3.5343, + 3.55, + 3.5563, + 3.5563, + 3.5369, + 3.5422, + 3.5477, + 3.5534, + 3.5499, + 3.5443, + 3.5397, + 3.5485, + 3.5392, + 3.543, + 3.543, + 3.543, + 3.5354, + 3.5402, + 3.5468, + 3.5468, + 3.538, + 3.5365, + 3.5484, + 3.553, + 3.5604, + 3.5604, + 3.5622, + 3.558, + 3.5614, + 3.5614, + 3.5714, + 3.57, + 3.5675, + 3.5713, + 3.5669, + 3.5706, + 3.5729, + 3.5757, + 3.5705, + 3.5657, + 3.5578, + 3.5607, + 3.5607, + 3.5816, + 3.5855, + 3.5858, + 3.5858, + 3.5858, + 3.5893, + 3.5939, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5893, + 3.5878, + 3.5954, + 3.5954, + 3.5967, + 3.5967, + 3.5934, + 3.5969, + 3.5978, + 3.5978, + 3.5978, + 3.5882, + 3.573, + 3.5802, + 3.5802, + 3.5802, + 3.5802, + 3.5766, + 3.5868, + 3.5792, + 3.5831, + 3.5831, + 3.5813, + 3.5813, + 3.5813, + 3.5736, + 3.5691, + 3.5691, + 3.5691, + 3.5607, + 3.5618, + 3.567, + 3.567, + 3.5631, + 3.5551, + 3.5594, + 3.5594, + 3.5594, + 3.5615, + 3.5586, + 3.5659, + 3.5647, + 3.5621, + 3.567, + 3.5691, + 3.5787, + 3.5813, + 3.5836, + 3.5821, + 3.5858, + 3.5858, + 3.5848, + 3.5872, + 3.5814, + 3.5814, + 3.5783, + 3.5786, + 3.5786, + 3.5908, + 3.5922, + 3.5793, + 3.5859, + 3.5924, + 3.5924, + 3.5924, + 3.5906, + 3.5924, + 3.6066, + 3.6021, + 3.6021, + 3.5996, + 3.6009, + 3.6009, + 3.6009, + 3.5936, + 3.5992, + 3.6038, + 3.6038, + 3.6038, + 3.5953, + 3.5972, + 3.6014, + 3.6064, + 3.6064, + 3.6012, + 3.6073, + 3.6082, + 3.6038, + 3.6091, + 3.6096, + 3.6131, + 3.6128, + 3.6164, + 3.6143, + 3.611, + 3.613, + 3.621, + 3.6124, + 3.6124, + 3.6113, + 3.6051, + 3.6, + 3.6037, + 3.6051, + 3.601, + 3.598, + 3.5982, + 3.595, + 3.5958, + 3.598, + 3.5972, + 3.5924, + 3.5943, + 3.5924, + 3.5854, + 3.5831, + 3.5787, + 3.5813, + 3.5862, + 3.5802, + 3.5784, + 3.5748, + 3.5696, + 3.5753, + 3.5739, + 3.5797, + 3.5836, + 3.5835, + 3.577, + 3.5732, + 3.5771, + 3.5771, + 3.5771, + 3.5771, + 3.579, + 3.579, + 3.5872, + 3.5872, + 3.5914, + 3.5899, + 3.5825, + 3.5825, + 3.5764, + 3.5756, + 3.5732, + 3.5723, + 3.5755, + 3.5737, + 3.5709, + 3.5727, + 3.5724, + 3.5724, + 3.5756, + 3.5741, + 3.576, + 3.5782, + 3.5782, + 3.5782, + 3.58, + 3.5807, + 3.5757, + 3.5819, + 3.5749, + 3.5733, + 3.5673, + 3.5698, + 3.5698, + 3.5679, + 3.5679, + 3.5679, + 3.5703, + 3.5703, + 3.574, + 3.5708, + 3.5708, + 3.568, + 3.5669, + 3.5669, + 3.5669, + 3.568, + 3.568, + 3.5712, + 3.57, + 3.5682, + 3.5702, + 3.561, + 3.5607, + 3.5601, + 3.5583, + 3.5608, + 3.567, + 3.567, + 3.5622, + 3.5606, + 3.5583, + 3.5581, + 3.5597, + 3.5606, + 3.561, + 3.5614, + 3.5611, + 3.572, + 3.5703, + 3.5698, + 3.5676, + 3.5733, + 3.5685, + 3.5685, + 3.5786, + 3.5784, + 3.5752, + 3.576, + 3.575, + 3.5749, + 3.5738, + 3.578, + 3.5789, + 3.5786, + 3.579, + 3.5738, + 3.572, + 3.5781, + 3.5816, + 3.5751, + 3.5751, + 3.5751, + 3.5759, + 3.5759, + 3.5615, + 3.5639, + 3.5615, + 3.5614, + 3.5601, + 3.5597, + 3.5588, + 3.5616, + 3.5616, + 3.5616, + 3.5616, + 3.5651, + 3.561, + 3.5594, + 3.5555, + 3.5496, + 3.5493, + 3.5494, + 3.5458, + 3.5478, + 3.548, + 3.5424, + 3.5482, + 3.5482, + 3.5517, + 3.5592, + 3.5615, + 3.5628, + 3.5689, + 3.569, + 3.5703, + 3.574, + 3.5715, + 3.5628, + 3.5628, + 3.5635, + 3.5635, + 3.5635, + 3.568, + 3.5704, + 3.5704, + 3.5662, + 3.5662, + 3.5666, + 3.5605, + 3.5592, + 3.5602, + 3.5678, + 3.5678, + 3.5639, + 3.5687, + 3.5702, + 3.5702, + 3.5709, + 3.576, + 3.5766, + 3.5794, + 3.583, + 3.5827, + 3.587, + 3.587, + 3.579, + 3.5805, + 3.579, + 3.579, + 3.579, + 3.5769, + 3.5808, + 3.574, + 3.5787, + 3.5787, + 3.5778, + 3.5791, + 3.5799, + 3.5751, + 3.5696, + 3.5696, + 3.5645, + 3.5685, + 3.5685, + 3.5617, + 3.5634, + 3.5592, + 3.5536, + 3.5505, + 3.5505, + 3.5523, + 3.5523, + 3.55, + 3.55, + 3.5501, + 3.549, + 3.5524, + 3.5567, + 3.5567, + 3.5567, + 3.5567, + 3.5512, + 3.5492, + 3.5543, + 3.5543, + 3.5543, + 3.5581, + 3.5581, + 3.5581, + 3.5581, + 3.5584, + 3.5584, + 3.5621, + 3.5621, + 3.5621, + 3.5599, + 3.5632, + 3.5632, + 3.5632, + 3.5632, + 3.5598, + 3.5602, + 3.5591, + 3.5562, + 3.5555, + 3.5555, + 3.5661, + 3.569, + 3.5695, + 3.5669, + 3.5669, + 3.5655, + 3.5655, + 3.5655, + 3.558, + 3.5537, + 3.5532, + 3.5531, + 3.544, + 3.5398, + 3.5443, + 3.5349, + 3.5362, + 3.5391, + 3.5433, + 3.5417, + 3.5417, + 3.5485, + 3.5439, + 3.5338, + 3.5407, + 3.5392, + 3.5431, + 3.5413, + 3.5349, + 3.5349, + 3.5401, + 3.5401, + 3.5315, + 3.5351, + 3.5385, + 3.5411, + 3.5404, + 3.5404, + 3.5335, + 3.5356, + 3.5366, + 3.5366, + 3.5366, + 3.539, + 3.539, + 3.5526, + 3.5568, + 3.5545, + 3.5545, + 3.5565, + 3.5578, + 3.5671, + 3.5639, + 3.5565, + 3.5565, + 3.555, + 3.5463, + 3.5419, + 3.5387, + 3.5382, + 3.5382, + 3.538, + 3.5373, + 3.5335, + 3.5324, + 3.5324, + 3.5324, + 3.5385, + 3.5492, + 3.5492, + 3.553, + 3.5496, + 3.5517, + 3.5521, + 3.5461, + 3.5502, + 3.5502, + 3.5452, + 3.5452, + 3.554, + 3.554, + 3.5555, + 3.5528, + 3.5502, + 3.5492, + 3.5492, + 3.5501, + 3.5501, + 3.5501, + 3.5544, + 3.5544, + 3.5568, + 3.5585, + 3.5539, + 3.5539, + 3.5539, + 3.5539, + 3.5539, + 3.5539, + 3.5539, + 3.5477, + 3.5443, + 3.5394, + 3.5369, + 3.5372, + 3.537, + 3.5401, + 3.5496, + 3.5401, + 3.537, + 3.5386, + 3.5321, + 3.5282, + 3.5246, + 3.5251, + 3.5296, + 3.5302, + 3.5302, + 3.5297, + 3.5319, + 3.5394, + 3.535, + 3.5397, + 3.5401, + 3.5401, + 3.5455, + 3.5455, + 3.5455, + 3.5453, + 3.5453, + 3.5453, + 3.5453, + 3.5453, + 3.5427, + 3.5427, + 3.5427, + 3.5431, + 3.5453, + 3.5456, + 3.5456, + 3.5508, + 3.5508, + 3.5375, + 3.5375, + 3.5375, + 3.538, + 3.533, + 3.5316, + 3.5314, + 3.5328, + 3.5312, + 3.5371, + 3.5425, + 3.5447, + 3.5447, + 3.5447, + 3.5476, + 3.5383, + 3.5388, + 3.5388, + 3.5387, + 3.5631, + 3.57, + 3.5686, + 3.5619, + 3.5667, + 3.5667, + 3.5726, + 3.5723, + 3.5723, + 3.5681, + 3.5502, + 3.5438, + 3.5456, + 3.5389, + 3.5345, + 3.5406, + 3.5406, + 3.5406, + 3.5406, + 3.5406, + 3.5406, + 3.5398, + 3.5424, + 3.5424, + 3.5394, + 3.5416, + 3.5416, + 3.5466, + 3.5457, + 3.5443, + 3.5423, + 3.5423, + 3.5383, + 3.5383, + 3.5445, + 3.5441, + 3.5362, + 3.5335, + 3.5334, + 3.5347, + 3.5347, + 3.5347, + 3.5347, + 3.5318, + 3.5283, + 3.525, + 3.524, + 3.5134, + 3.5031, + 3.5016, + 3.5016, + 3.4988, + 3.488, + 3.4964, + 3.4984, + 3.4993, + 3.4917, + 3.4849, + 3.4856, + 3.4856, + 3.4914, + 3.4914, + 3.5014, + 3.5014, + 3.5076, + 3.5076, + 3.5127, + 3.5235, + 3.5166, + 3.5188, + 3.5135, + 3.516, + 3.5143, + 3.5166, + 3.5142, + 3.5098, + 3.5098, + 3.5224, + 3.5224, + 3.5185, + 3.5185, + 3.5185, + 3.5185, + 3.5216, + 3.5192, + 3.5195, + 3.5257, + 3.5257, + 3.5166, + 3.5168, + 3.5168, + 3.5244, + 3.5244, + 3.5244, + 3.53, + 3.53, + 3.53, + 3.525, + 3.525, + 3.525, + 3.5242, + 3.531, + 3.5169, + 3.514, + 3.5134, + 3.5134, + 3.5217, + 3.5269, + 3.5164, + 3.5184, + 3.5185, + 3.5224, + 3.5228, + 3.5322, + 3.534, + 3.5363, + 3.5363, + 3.5246, + 3.5237, + 3.5262, + 3.5243, + 3.5309, + 3.5309, + 3.5309, + 3.5309, + 3.5382, + 3.5424, + 3.5373, + 3.5339, + 3.5365, + 3.5365, + 3.5364, + 3.5424, + 3.5424, + 3.5469, + 3.5475, + 3.5475, + 3.552, + 3.5533, + 3.5549, + 3.5549, + 3.5499, + 3.5499, + 3.546, + 3.5524, + 3.5568, + 3.5576, + 3.5557, + 3.5313, + 3.5367, + 3.5226, + 3.5168, + 3.5227, + 3.5209, + 3.5217, + 3.52, + 3.5227, + 3.5249, + 3.5308, + 3.5266, + 3.5229, + 3.5323, + 3.5323, + 3.5323, + 3.5323, + 3.5318, + 3.5247, + 3.5247, + 3.525, + 3.525, + 3.5208, + 3.5177, + 3.5177, + 3.5107, + 3.5122, + 3.5051, + 3.5051, + 3.5053, + 3.5061, + 3.5061, + 3.5061, + 3.5061, + 3.5052, + 3.5038, + 3.5061, + 3.5061, + 3.5061, + 3.5058, + 3.5035, + 3.5005, + 3.4937, + 3.4937, + 3.4937, + 3.505, + 3.5064, + 3.5064, + 3.5115, + 3.5142, + 3.513, + 3.5142, + 3.5171, + 3.5171, + 3.5208, + 3.5182, + 3.5239, + 3.5239, + 3.5222, + 3.5222, + 3.5222, + 3.5222, + 3.5134, + 3.5108, + 3.5108, + 3.5194, + 3.5256, + 3.5236, + 3.5237, + 3.5193, + 3.5193, + 3.5219, + 3.5168, + 3.5168, + 3.5168, + 3.5161, + 3.5115, + 3.5115, + 3.5115, + 3.5128, + 3.5128, + 3.5135, + 3.5135, + 3.5135, + 3.5187, + 3.5187, + 3.5187, + 3.5187, + 3.5187, + 3.5241, + 3.524, + 3.5253, + 3.5253, + 3.5264, + 3.526, + 3.526, + 3.526, + 3.5268, + 3.5289, + 3.5287, + 3.5323, + 3.5297, + 3.5278, + 3.5277, + 3.5285, + 3.5294, + 3.5279, + 3.5279, + 3.5363, + 3.5333, + 3.5366, + 3.5397, + 3.542, + 3.5441, + 3.551, + 3.552, + 3.5594, + 3.5558, + 3.5582, + 3.5597, + 3.5584, + 3.5597, + 3.564, + 3.5648, + 3.5674, + 3.5674, + 3.5635, + 3.5657, + 3.569, + 3.5703, + 3.574, + 3.565, + 3.5728, + 3.5728, + 3.5547, + 3.5548, + 3.5585, + 3.552, + 3.5516, + 3.5516, + 3.5516, + 3.5488, + 3.5488, + 3.5535, + 3.5535, + 3.5535, + 3.5573, + 3.5568, + 3.5568, + 3.5533, + 3.5522, + 3.5473, + 3.5516, + 3.5511, + 3.5511, + 3.5487, + 3.5487, + 3.5487, + 3.5487, + 3.547, + 3.5438, + 3.545, + 3.545, + 3.5486, + 3.5486, + 3.5616, + 3.5586, + 3.5553, + 3.5561, + 3.5593, + 3.5593, + 3.5562, + 3.5554, + 3.5554, + 3.5554, + 3.5554, + 3.5554, + 3.5572, + 3.5614, + 3.5573, + 3.5581, + 3.5605, + 3.5616, + 3.5658, + 3.5649, + 3.5664, + 3.5664, + 3.5664, + 3.5688, + 3.5739, + 3.5766, + 3.5766, + 3.5806, + 3.5816, + 3.5792, + 3.5798, + 3.5821, + 3.58, + 3.5795, + 3.5765, + 3.5765, + 3.5824, + 3.5824, + 3.5802, + 3.5802, + 3.573, + 3.5824, + 3.5858, + 3.5854, + 3.5914, + 3.5954, + 3.597, + 3.5938, + 3.5941, + 3.5925, + 3.5903, + 3.5937, + 3.585, + 3.5873, + 3.591, + 3.591, + 3.6062, + 3.6117, + 3.6136, + 3.6112, + 3.6061, + 3.605, + 3.6138, + 3.6151, + 3.616, + 3.6148, + 3.6146, + 3.6146, + 3.6124, + 3.614, + 3.6064, + 3.6037, + 3.6037, + 3.604, + 3.601, + 3.5888, + 3.5953, + 3.59, + 3.5884, + 3.5874, + 3.5874, + 3.5951, + 3.5951, + 3.6, + 3.5945, + 3.5986, + 3.6022, + 3.6022, + 3.6022, + 3.6022, + 3.6022, + 3.6034, + 3.6033, + 3.6061, + 3.5986, + 3.5957, + 3.5957, + 3.5957, + 3.5987, + 3.6002, + 3.6036, + 3.6044, + 3.6044, + 3.61, + 3.6158, + 3.6158, + 3.6033, + 3.6033, + 3.5997, + 3.5943, + 3.5943, + 3.5943, + 3.5921, + 3.5885, + 3.5885, + 3.5885, + 3.5858, + 3.5928, + 3.591, + 3.591, + 3.5922, + 3.5926, + 3.5926, + 3.5907, + 3.5907, + 3.5773, + 3.5812, + 3.5789, + 3.5804, + 3.5764, + 3.5764, + 3.5795, + 3.5754, + 3.5709, + 3.5743, + 3.5721, + 3.5716, + 3.5716, + 3.5711, + 3.5666, + 3.5648, + 3.5667, + 3.573, + 3.5601, + 3.561, + 3.5621, + 3.5592, + 3.5569, + 3.5615, + 3.5613, + 3.5599, + 3.5722, + 3.5725, + 3.5754, + 3.5744, + 3.5734, + 3.5766, + 3.5766, + 3.5762, + 3.5783, + 3.5802, + 3.5799, + 3.5768, + 3.584, + 3.584, + 3.584, + 3.584, + 3.588, + 3.5927, + 3.5927, + 3.5927, + 3.5948, + 3.5948, + 3.5996, + 3.5998, + 3.5973, + 3.6003, + 3.6038, + 3.6041, + 3.6041, + 3.6041, + 3.6003, + 3.6015, + 3.5926, + 3.5906, + 3.5904, + 3.5955, + 3.5971, + 3.5971, + 3.5971, + 3.605, + 3.6036, + 3.599, + 3.599, + 3.599, + 3.5867, + 3.576, + 3.576, + 3.576, + 3.576, + 3.5922, + 3.5997, + 3.6023, + 3.6004, + 3.6038, + 3.6016, + 3.6103, + 3.6194, + 3.6212, + 3.6196, + 3.6126, + 3.6126, + 3.6128, + 3.6128, + 3.6156, + 3.6156, + 3.6156, + 3.6081, + 3.6157, + 3.6126, + 3.6097, + 3.6096, + 3.6096, + 3.6131, + 3.6155, + 3.6171, + 3.627, + 3.6278, + 3.6299, + 3.6174, + 3.6232, + 3.6312, + 3.6328, + 3.6301, + 3.6336, + 3.6342, + 3.63, + 3.6263, + 3.6263, + 3.6263, + 3.6292, + 3.624, + 3.6131, + 3.6246, + 3.6248, + 3.6177, + 3.6184, + 3.6184, + 3.6216, + 3.6225, + 3.6225, + 3.6051, + 3.5957, + 3.6, + 3.6, + 3.5994, + 3.5973, + 3.6008, + 3.6042, + 3.6077, + 3.6046, + 3.6047, + 3.6068, + 3.6058, + 3.5998, + 3.6007, + 3.6051, + 3.6009, + 3.6029, + 3.6076, + 3.6076, + 3.6077, + 3.6095, + 3.6044, + 3.6171, + 3.6316, + 3.6273, + 3.6273, + 3.6219, + 3.6161, + 3.6175, + 3.6066, + 3.6098, + 3.6098, + 3.6211, + 3.6179, + 3.6179, + 3.6188, + 3.6273, + 3.6252, + 3.6245, + 3.6265, + 3.6174, + 3.6175, + 3.613, + 3.6119, + 3.6063, + 3.6063, + 3.599, + 3.5965, + 3.6068, + 3.6077, + 3.6043, + 3.6043, + 3.6031, + 3.5975, + 3.6001, + 3.6019, + 3.5977, + 3.5958, + 3.5981, + 3.5936, + 3.5937, + 3.616, + 3.6088, + 3.6027, + 3.616, + 3.623, + 3.6381, + 3.6249, + 3.6419, + 3.6417, + 3.6305, + 3.617, + 3.6152, + 3.6141, + 3.6226, + 3.6219, + 3.6297, + 3.6246, + 3.6186, + 3.62, + 3.6261, + 3.6336, + 3.6336, + 3.6298, + 3.6331, + 3.6368, + 3.6388, + 3.6388, + 3.6412, + 3.6431, + 3.6488, + 3.6601, + 3.6582, + 3.6582, + 3.6553, + 3.6508, + 3.6496, + 3.6501, + 3.6478, + 3.643, + 3.6341, + 3.6459, + 3.6464, + 3.6492, + 3.6473, + 3.6473, + 3.6487, + 3.6369, + 3.6392, + 3.6392, + 3.6392, + 3.6392, + 3.6518, + 3.6412, + 3.6356, + 3.634, + 3.6297, + 3.62, + 3.6196, + 3.6186, + 3.6204, + 3.6169, + 3.6169, + 3.6248, + 3.627, + 3.6394, + 3.6394, + 3.6394, + 3.6358, + 3.6437, + 3.6488, + 3.6688, + 3.6688, + 3.6598, + 3.6571, + 3.6571, + 3.6571, + 3.6721, + 3.665, + 3.6719, + 3.6732, + 3.6712, + 3.6609, + 3.6528, + 3.6766, + 3.6673, + 3.6742, + 3.6742, + 3.6742, + 3.6805, + 3.664, + 3.6611, + 3.6711, + 3.659, + 3.6665, + 3.6662, + 3.668, + 3.668, + 3.6687, + 3.6723, + 3.682, + 3.6908, + 3.6908, + 3.6798, + 3.684, + 3.6857, + 3.6857, + 3.6885, + 3.6865, + 3.6836, + 3.6768, + 3.672, + 3.6726, + 3.678, + 3.6714, + 3.6714, + 3.6912, + 3.6864, + 3.6856, + 3.6936, + 3.706, + 3.7044, + 3.7046, + 3.7055, + 3.712, + 3.7197, + 3.7265, + 3.7295, + 3.7362, + 3.7488, + 3.7473, + 3.7713, + 3.7589, + 3.7621, + 3.7704, + 3.7614, + 3.7521, + 3.7364, + 3.7489, + 3.7614, + 3.7487, + 3.7434, + 3.7378, + 3.745, + 3.7477, + 3.7477, + 3.7523, + 3.7554, + 3.7674, + 3.7619, + 3.751, + 3.7506, + 3.7479, + 3.7467, + 3.7475, + 3.7422, + 3.7422, + 3.7307, + 3.7374, + 3.7355, + 3.7384, + 3.7466, + 3.7452, + 3.7452, + 3.7398, + 3.7303, + 3.7292, + 3.7212, + 3.7178, + 3.7187, + 3.7086, + 3.705, + 3.7025, + 3.7079, + 3.7157, + 3.7056, + 3.7056, + 3.711, + 3.711, + 3.737, + 3.7358, + 3.7358, + 3.7443, + 3.739, + 3.7434, + 3.7434, + 3.7383, + 3.7327, + 3.744, + 3.7393, + 3.7393, + 3.7348, + 3.7336, + 3.7303, + 3.7355, + 3.7289, + 3.7189, + 3.7157, + 3.7139, + 3.709, + 3.7112, + 3.7146, + 3.7213, + 3.7281, + 3.734, + 3.734, + 3.7355, + 3.7355, + 3.7355, + 3.7361, + 3.7257, + 3.7257, + 3.7381, + 3.7314, + 3.74, + 3.74, + 3.7422, + 3.7483, + 3.7522, + 3.7617, + 3.7617, + 3.763, + 3.7685, + 3.7801, + 3.7682, + 3.7647, + 3.7626, + 3.7572, + 3.7581, + 3.7538, + 3.7639, + 3.7678, + 3.7564, + 3.7641, + 3.7884, + 3.7848, + 3.7848, + 3.7706, + 3.7736, + 3.7653, + 3.7581, + 3.7524, + 3.7446, + 3.7462, + 3.7438, + 3.7487, + 3.7487, + 3.7514, + 3.7425, + 3.735, + 3.7376, + 3.7412, + 3.7412, + 3.7514, + 3.7452, + 3.7452, + 3.7452, + 3.7434, + 3.7434, + 3.7332, + 3.7321, + 3.7321, + 3.7461, + 3.7427, + 3.7377, + 3.7337, + 3.7317, + 3.731, + 3.7325, + 3.7295, + 3.7277, + 3.7266, + 3.7257, + 3.7251, + 3.7268, + 3.7268, + 3.7393, + 3.7393, + 3.7393, + 3.7358, + 3.7397, + 3.7436, + 3.7436, + 3.7398, + 3.7402, + 3.7344, + 3.7238, + 3.7238, + 3.7253, + 3.7424, + 3.7292, + 3.7384, + 3.7384, + 3.7384, + 3.7384, + 3.7249, + 3.7277, + 3.7306, + 3.7341, + 3.7272, + 3.723, + 3.7236, + 3.7228, + 3.7228, + 3.7327, + 3.732, + 3.7365, + 3.7378, + 3.74, + 3.7454, + 3.756, + 3.75, + 3.7467, + 3.7405, + 3.7507, + 3.7621, + 3.7427, + 3.7416, + 3.7508, + 3.7508, + 3.7508, + 3.7587, + 3.7519, + 3.7489, + 3.7546, + 3.7546, + 3.7539, + 3.7539, + 3.7498, + 3.7481, + 3.7353, + 3.7448, + 3.7581, + 3.7559, + 3.7545, + 3.747, + 3.7495, + 3.7497, + 3.759, + 3.759, + 3.7606, + 3.7606, + 3.7606, + 3.7686, + 3.7682, + 3.769, + 3.7706, + 3.7518, + 3.753, + 3.7657, + 3.7612, + 3.7684, + 3.7744, + 3.7779, + 3.7779, + 3.7686, + 3.7701, + 3.7701, + 3.7718, + 3.7677, + 3.7646, + 3.763, + 3.757, + 3.7553, + 3.7544, + 3.7544, + 3.7566, + 3.7512, + 3.7481, + 3.7471, + 3.7471, + 3.7511, + 3.7491, + 3.7516, + 3.7486, + 3.7455, + 3.7443, + 3.74, + 3.74, + 3.7467, + 3.7423, + 3.7474, + 3.7474, + 3.7536, + 3.7539, + 3.7626, + 3.7747, + 3.7807, + 3.7873, + 3.7946, + 3.7881, + 3.7853, + 3.7886, + 3.7872, + 3.797, + 3.7893, + 3.7893, + 3.7988, + 3.805, + 3.8081, + 3.8135, + 3.803, + 3.8111, + 3.8022, + 3.7978, + 3.8093, + 3.813, + 3.8163, + 3.8133, + 3.8174, + 3.8207, + 3.8225, + 3.8219, + 3.8239, + 3.8159, + 3.8167, + 3.8217, + 3.815, + 3.8148, + 3.826, + 3.8197, + 3.8256, + 3.8225, + 3.8041, + 3.8041, + 3.8002, + 3.7911, + 3.7929, + 3.7893, + 3.8025, + 3.7994, + 3.8125, + 3.8118, + 3.8207, + 3.8125, + 3.8156, + 3.7992, + 3.7841, + 3.7956, + 3.8073, + 3.7793, + 3.7895, + 3.7951, + 3.7825, + 3.7796, + 3.7685, + 3.7704, + 3.7755, + 3.7843, + 3.7765, + 3.7847, + 3.7787, + 3.7865, + 3.795, + 3.7958, + 3.7987, + 3.8001, + 3.7984, + 3.8006, + 3.8103, + 3.8137, + 3.8222, + 3.8222, + 3.8054, + 3.825, + 3.8027, + 3.8018, + 3.8184, + 3.8209, + 3.8177, + 3.802, + 3.8174, + 3.82, + 3.8165, + 3.8208, + 3.83, + 3.8175, + 3.8238, + 3.8191, + 3.8029, + 3.7925, + 3.7919, + 3.7976, + 3.8032, + 3.7972, + 3.8061, + 3.8018, + 3.8013, + 3.7909, + 3.7828, + 3.7847, + 3.7887, + 3.7829, + 3.7862, + 3.782, + 3.7875, + 3.7865, + 3.7849, + 3.781, + 3.7847, + 3.7863, + 3.7883, + 3.7735, + 3.7726, + 3.7721, + 3.7703, + 3.7703, + 3.7647, + 3.7655, + 3.7601, + 3.7644, + 3.7784, + 3.7819, + 3.769, + 3.7643, + 3.7631, + 3.766, + 3.7655, + 3.7618, + 3.7579, + 3.7545, + 3.7638, + 3.7632, + 3.7694, + 3.7702, + 3.7736, + 3.7804, + 3.7952, + 3.794, + 3.7902, + 3.7895, + 3.7858, + 3.7812, + 3.7756, + 3.784, + 3.7845, + 3.7748, + 3.7789, + 3.7756, + 3.7838, + 3.7838, + 3.7976, + 3.8025, + 3.7932, + 3.7932, + 3.79, + 3.7888, + 3.7844, + 3.79, + 3.79, + 3.7839, + 3.7865, + 3.7865, + 3.7876, + 3.7943, + 3.7882, + 3.7849, + 3.7846, + 3.7895, + 3.8025, + 3.803, + 3.803, + 3.7814, + 3.7701, + 3.7632, + 3.7725, + 3.7737, + 3.7693, + 3.7809, + 3.7843, + 3.7722, + 3.7686, + 3.7694, + 3.8064, + 3.8476, + 3.8781, + 3.9043, + 3.8947, + 3.8697, + 3.8837, + 3.8611, + 3.873, + 3.8737, + 3.8919, + 3.9024, + 3.8956, + 3.8871, + 3.8697, + 3.8592, + 3.8422, + 3.8348, + 3.8216, + 3.8122, + 3.84, + 3.8405, + 3.8405, + 3.8355, + 3.8419, + 3.8386, + 3.8427, + 3.8533, + 3.848, + 3.8303, + 3.8523, + 3.8485, + 3.84, + 3.83, + 3.8326, + 3.8237, + 3.812, + 3.8016, + 3.8042, + 3.8055, + 3.8079, + 3.802, + 3.8077, + 3.8096, + 3.8118, + 3.8021, + 3.8032, + 3.8074, + 3.8012, + 3.7987, + 3.7888, + 3.7837, + 3.7711, + 3.7699, + 3.7778, + 3.7802, + 3.7676, + 3.7633, + 3.7658, + 3.77, + 3.7709, + 3.767, + 3.7524, + 3.7523, + 3.753, + 3.754, + 3.7623, + 3.7651, + 3.7627, + 3.7684, + 3.7717, + 3.7776, + 3.7872, + 3.788, + 3.7893, + 3.7861, + 3.7825, + 3.7796, + 3.7808, + 3.7821, + 3.7812, + 3.7763, + 3.7789, + 3.7789, + 3.7737, + 3.7695, + 3.7686, + 3.761, + 3.7585, + 3.7553, + 3.7573, + 3.7583, + 3.762, + 3.7537, + 3.7502, + 3.7508, + 3.7486, + 3.7439, + 3.7478, + 3.7478, + 3.76, + 3.7534, + 3.7508, + 3.7446, + 3.7361, + 3.7318, + 3.7443, + 3.7383, + 3.7274, + 3.7095, + 3.7191, + 3.7163, + 3.7206, + 3.7199, + 3.7205, + 3.7128, + 3.7266, + 3.7149, + 3.7259, + 3.7328, + 3.7235, + 3.715, + 3.7108, + 3.7149, + 3.7236, + 3.7275, + 3.7399, + 3.734, + 3.7377, + 3.7369, + 3.7297, + 3.7297, + 3.744, + 3.7452, + 3.7446, + 3.7465, + 3.7538, + 3.7516, + 3.7522, + 3.7419, + 3.7471, + 3.7473, + 3.7474, + 3.7393, + 3.7303, + 3.7335, + 3.7203, + 3.7152, + 3.7212, + 3.7186, + 3.7163, + 3.7122, + 3.7202, + 3.7202, + 3.7169, + 3.7169, + 3.7319, + 3.7402, + 3.7359, + 3.7303, + 3.7303, + 3.7303, + 3.7432, + 3.7494, + 3.7468, + 3.7526, + 3.75, + 3.748, + 3.748, + 3.7486, + 3.7422, + 3.7416, + 3.7448, + 3.7455, + 3.7536, + 3.7493, + 3.7475, + 3.7559, + 3.7665, + 3.732, + 3.732, + 3.7368, + 3.7279, + 3.7258, + 3.7221, + 3.7226, + 3.7271, + 3.7229, + 3.7262, + 3.7267, + 3.7229, + 3.7252, + 3.7324, + 3.744, + 3.749, + 3.7466, + 3.7604, + 3.7669, + 3.7619, + 3.766, + 3.7617, + 3.7617, + 3.7579, + 3.7579, + 3.7553, + 3.7492, + 3.7492, + 3.7511, + 3.7585, + 3.7629, + 3.7639, + 3.7505, + 3.7579, + 3.7579, + 3.7633, + 3.7598, + 3.761, + 3.761, + 3.7757, + 3.7757, + 3.7757, + 3.7909, + 3.7902, + 3.832, + 3.8131, + 3.8172, + 3.8147, + 3.8218, + 3.8218, + 3.822, + 3.8298, + 3.8397, + 3.8464, + 3.8284, + 3.8346, + 3.8337, + 3.849, + 3.8393, + 3.8255, + 3.8483, + 3.8348, + 3.8348, + 3.8298, + 3.8256, + 3.8146, + 3.8244, + 3.8244, + 3.8064, + 3.8046, + 3.804, + 3.8078, + 3.8079, + 3.8079, + 3.8048, + 3.8037, + 3.8046, + 3.8175, + 3.818, + 3.8075, + 3.8086, + 3.797, + 3.7864, + 3.7852, + 3.7816, + 3.7812, + 3.7698, + 3.7698, + 3.7627, + 3.7567, + 3.7485, + 3.7461, + 3.7507, + 3.7475, + 3.7509, + 3.7387, + 3.7296, + 3.714, + 3.7121, + 3.7164, + 3.7158, + 3.7176, + 3.7156, + 3.7, + 3.6854, + 3.6691, + 3.6769, + 3.6699, + 3.6843, + 3.6639, + 3.6798, + 3.6653, + 3.6691, + 3.6758, + 3.6859, + 3.6875, + 3.685, + 3.6912, + 3.6927, + 3.6696, + 3.6548, + 3.6356, + 3.6394, + 3.6488, + 3.6469, + 3.6296, + 3.6174, + 3.6227, + 3.6278, + 3.6303, + 3.6332, + 3.634, + 3.6414, + 3.6404, + 3.6417, + 3.6406, + 3.6261, + 3.6325, + 3.6253, + 3.6262, + 3.6314, + 3.6381, + 3.6435, + 3.6461, + 3.64, + 3.6388, + 3.6326, + 3.6292, + 3.6234, + 3.6035, + 3.6026, + 3.5856, + 3.6132, + 3.6221, + 3.6277, + 3.6075, + 3.6035, + 3.6037, + 3.5858, + 3.5824, + 3.5878, + 3.5921, + 3.5828, + 3.5843, + 3.5979, + 3.6143, + 3.605, + 3.6087, + 3.6094, + 3.6125, + 3.6101, + 3.6101, + 3.6194, + 3.6237, + 3.6227, + 3.6203, + 3.6269, + 3.6183, + 3.6183, + 3.6066, + 3.6078, + 3.6099, + 3.6062, + 3.6098, + 3.6095, + 3.6095, + 3.6078, + 3.6036, + 3.6007, + 3.5977, + 3.5942, + 3.6012, + 3.6144, + 3.6022, + 3.6094, + 3.6091, + 3.6096, + 3.61, + 3.5974, + 3.5871, + 3.5871, + 3.5875, + 3.58, + 3.5685, + 3.5706, + 3.5699, + 3.5713, + 3.5641, + 3.5759, + 3.5803, + 3.5747, + 3.5704, + 3.5729, + 3.5615, + 3.5572, + 3.568, + 3.5629, + 3.5744, + 3.5836, + 3.586, + 3.594, + 3.5955, + 3.592, + 3.6042, + 3.6052, + 3.6245, + 3.6248, + 3.6229, + 3.6192, + 3.6148, + 3.604, + 3.5982, + 3.6016, + 3.6063, + 3.6075, + 3.6138, + 3.6148, + 3.6104, + 3.6194, + 3.6279, + 3.6238, + 3.6234, + 3.6266, + 3.6251, + 3.6241, + 3.6345, + 3.632, + 3.6339, + 3.6386, + 3.644, + 3.6459, + 3.6508, + 3.6447, + 3.6544, + 3.6512, + 3.6551, + 3.6532, + 3.6389, + 3.6339, + 3.634, + 3.6288, + 3.6265, + 3.6186, + 3.6229, + 3.6153, + 3.6086, + 3.6094, + 3.6094, + 3.6109, + 3.6071, + 3.6099, + 3.6066, + 3.6061, + 3.607, + 3.61, + 3.6045, + 3.6022, + 3.6061, + 3.6061, + 3.6061, + 3.6081, + 3.6112, + 3.6098, + 3.5993, + 3.5883, + 3.5754, + 3.5913, + 3.5879, + 3.5952, + 3.5926, + 3.5839, + 3.5833, + 3.5785, + 3.5742, + 3.5755, + 3.5905, + 3.5906, + 3.5958, + 3.5975, + 3.5786, + 3.5726, + 3.5726, + 3.5886, + 3.5903, + 3.5913, + 3.5874, + 3.5971, + 3.594, + 3.6017, + 3.6022, + 3.6022, + 3.5906, + 3.5972, + 3.6052, + 3.6059, + 3.6118, + 3.6025, + 3.6014, + 3.5947, + 3.5947, + 3.5923, + 3.5901, + 3.5878, + 3.5817, + 3.587, + 3.5914, + 3.5914, + 3.5938, + 3.5898, + 3.5884, + 3.5822, + 3.5798, + 3.5798, + 3.5918, + 3.5927, + 3.5902, + 3.5902, + 3.5902, + 3.5894, + 3.5879, + 3.5819, + 3.5814, + 3.584, + 3.5884, + 3.6006, + 3.6022, + 3.5981, + 3.5977, + 3.5977, + 3.5933, + 3.5877, + 3.5877, + 3.5877, + 3.586, + 3.586, + 3.575, + 3.5711, + 3.5749, + 3.5749, + 3.5887, + 3.5868, + 3.5889, + 3.5839, + 3.5839, + 3.5773, + 3.5813, + 3.5797, + 3.5913, + 3.5902, + 3.5843, + 3.5758, + 3.576, + 3.571, + 3.575, + 3.577, + 3.5906, + 3.588, + 3.5984, + 3.597, + 3.5924, + 3.5924, + 3.589, + 3.5931, + 3.5925, + 3.5955, + 3.5976, + 3.5967, + 3.6017, + 3.6017, + 3.5994, + 3.6015, + 3.5996, + 3.5894, + 3.5802, + 3.5802, + 3.5678, + 3.5715, + 3.5678, + 3.564, + 3.5629, + 3.5669, + 3.564, + 3.5619, + 3.566, + 3.572, + 3.5785, + 3.5827, + 3.5791, + 3.5705, + 3.5705, + 3.5772, + 3.5772, + 3.5725, + 3.5645, + 3.5645, + 3.5611, + 3.5611, + 3.5656, + 3.5656, + 3.5651, + 3.5646, + 3.5646, + 3.5646, + 3.5643, + 3.5679, + 3.5641, + 3.5608, + 3.55, + 3.5569, + 3.5766, + 3.5697, + 3.5697, + 3.5605, + 3.5605, + 3.5565, + 3.5706, + 3.5747, + 3.558, + 3.5628, + 3.5704, + 3.5713, + 3.5696, + 3.5656, + 3.5629, + 3.567, + 3.5646, + 3.5657, + 3.5682, + 3.575, + 3.5751, + 3.5743, + 3.5767, + 3.5767, + 3.561, + 3.557, + 3.5544, + 3.56, + 3.5544, + 3.5524, + 3.548, + 3.548, + 3.5519, + 3.5485, + 3.538, + 3.5389, + 3.5388, + 3.543, + 3.5415, + 3.5367, + 3.5338, + 3.5363, + 3.5336, + 3.529, + 3.5359, + 3.5345, + 3.5345, + 3.533, + 3.5382, + 3.5377, + 3.5485, + 3.5397, + 3.5412, + 3.5384, + 3.5312, + 3.5251, + 3.5266, + 3.5266, + 3.5224, + 3.5224, + 3.5309, + 3.5313, + 3.5313, + 3.533, + 3.5292, + 3.5267, + 3.515, + 3.5309, + 3.5337, + 3.5394, + 3.5388, + 3.5347, + 3.5348, + 3.5381, + 3.5433, + 3.5493, + 3.545, + 3.5445, + 3.5422, + 3.5422, + 3.5472, + 3.5468, + 3.5491, + 3.5477, + 3.5439, + 3.5439, + 3.544, + 3.544, + 3.5432, + 3.545, + 3.5435, + 3.5422, + 3.536, + 3.5312, + 3.5358, + 3.5485, + 3.5532, + 3.5532, + 3.5532, + 3.5453, + 3.5371, + 3.5349, + 3.5353, + 3.541, + 3.541, + 3.5509, + 3.5506, + 3.5519, + 3.5489, + 3.5467, + 3.5515, + 3.5554, + 3.5591, + 3.5591, + 3.5553, + 3.5608, + 3.5601, + 3.5601, + 3.5601, + 3.5533, + 3.5533, + 3.5535, + 3.5556, + 3.558, + 3.558, + 3.561, + 3.565, + 3.5626, + 3.5691, + 3.5672, + 3.5641, + 3.574, + 3.5745, + 3.5785, + 3.5746, + 3.5746, + 3.5717, + 3.5715, + 3.5773, + 3.5751, + 3.5751, + 3.5751, + 3.5759, + 3.5816, + 3.5837, + 3.5837, + 3.5795, + 3.5763, + 3.5724, + 3.5704, + 3.5706, + 3.5706, + 3.5743, + 3.5706, + 3.5625, + 3.5597, + 3.56, + 3.56, + 3.56, + 3.5606, + 3.5606, + 3.5522, + 3.552, + 3.552, + 3.563, + 3.563, + 3.5642, + 3.567, + 3.5621, + 3.5621, + 3.5621, + 3.5739, + 3.575, + 3.5824, + 3.585, + 3.585, + 3.5804, + 3.578, + 3.5822, + 3.5881, + 3.5893, + 3.5842, + 3.583, + 3.5781, + 3.5821, + 3.5831, + 3.577, + 3.577, + 3.5716, + 3.5716, + 3.5695, + 3.5789, + 3.5844, + 3.5844, + 3.5807, + 3.5807, + 3.5807, + 3.5875, + 3.5817, + 3.5817, + 3.5817, + 3.5765, + 3.5734, + 3.5734, + 3.5687, + 3.5799, + 3.5832, + 3.5919, + 3.5919, + 3.5875, + 3.5832, + 3.5818, + 3.5855, + 3.5855, + 3.575, + 3.5733, + 3.5733, + 3.5733, + 3.5733, + 3.5845, + 3.5773, + 3.5773, + 3.5785, + 3.5777, + 3.5777, + 3.5777, + 3.5777, + 3.5759, + 3.5691, + 3.57, + 3.5648, + 3.5606, + 3.5584, + 3.5547, + 3.5681, + 3.5739, + 3.5766, + 3.5766, + 3.5735, + 3.5735, + 3.5735, + 3.5735, + 3.5735, + 3.5784, + 3.5838, + 3.5927, + 3.5861, + 3.5861, + 3.5865, + 3.5963, + 3.609, + 3.6083, + 3.6138, + 3.6202, + 3.6275, + 3.6382, + 3.6417, + 3.6367, + 3.6426, + 3.6505, + 3.6624, + 3.647, + 3.6573, + 3.6504, + 3.6504, + 3.6414, + 3.6372, + 3.6375, + 3.631, + 3.62, + 3.5974, + 3.6043, + 3.6272, + 3.6257, + 3.6466, + 3.6504, + 3.6485, + 3.6516, + 3.6608, + 3.6524, + 3.6543, + 3.6578, + 3.6596, + 3.6633, + 3.6596, + 3.6642, + 3.6762, + 3.6865, + 3.6818, + 3.6922, + 3.6856, + 3.6875, + 3.6894, + 3.6827, + 3.6831, + 3.6831, + 3.6907, + 3.6737, + 3.6856, + 3.6957, + 3.7012, + 3.6962, + 3.6992, + 3.7033, + 3.6982, + 3.6874, + 3.6869, + 3.6985, + 3.6999, + 3.6974, + 3.6932, + 3.6981, + 3.7046, + 3.7007, + 3.7007, + 3.7079, + 3.7031, + 3.7031, + 3.6655, + 3.6678, + 3.666, + 3.6768, + 3.6768, + 3.6822, + 3.6957, + 3.7092, + 3.7089, + 3.7252, + 3.718, + 3.7204, + 3.7295, + 3.7344, + 3.7312, + 3.7312, + 3.7174, + 3.7354, + 3.7373, + 3.7199, + 3.7126, + 3.7319, + 3.7272, + 3.7126, + 3.7191, + 3.7272, + 3.7361, + 3.7388, + 3.738, + 3.7371, + 3.7422, + 3.748, + 3.7464, + 3.7467, + 3.7555, + 3.7617, + 3.7731, + 3.7568, + 3.7619, + 3.7624, + 3.7722, + 3.7659, + 3.7622, + 3.7578, + 3.7513, + 3.7304, + 3.733, + 3.723, + 3.7276, + 3.7336, + 3.7378, + 3.7498, + 3.7339, + 3.7305, + 3.7296, + 3.7296, + 3.7385, + 3.7349, + 3.7358, + 3.7255, + 3.713, + 3.7066, + 3.6983, + 3.7051, + 3.701, + 3.6957, + 3.6999, + 3.6851, + 3.6873, + 3.6854, + 3.6854, + 3.6865, + 3.6865, + 3.68, + 3.6775, + 3.6767, + 3.6905, + 3.6894, + 3.6987, + 3.7088, + 3.7051, + 3.7051, + 3.697, + 3.6923, + 3.6921, + 3.6887, + 3.6783, + 3.6842, + 3.6924, + 3.6942, + 3.6987, + 3.6987, + 3.6978, + 3.7265, + 3.742, + 3.7317, + 3.716, + 3.727, + 3.7322, + 3.732, + 3.7308, + 3.7231, + 3.7216, + 3.7187, + 3.7178, + 3.7148, + 3.7148, + 3.7322, + 3.7327, + 3.7215, + 3.7215, + 3.7365, + 3.7443, + 3.728, + 3.7203, + 3.7425, + 3.7197, + 3.7197, + 3.7257, + 3.7329, + 3.7262, + 3.7235, + 3.7235, + 3.7195, + 3.7248, + 3.7228, + 3.7172, + 3.7294, + 3.73, + 3.7338, + 3.732, + 3.7217, + 3.7148, + 3.7147, + 3.7234, + 3.7022, + 3.7044, + 3.7067, + 3.7027, + 3.6992, + 3.6934, + 3.6951, + 3.6951, + 3.6951, + 3.6951, + 3.7059, + 3.7043, + 3.6999, + 3.6999, + 3.6967, + 3.6967, + 3.704, + 3.7061, + 3.7061, + 3.7022, + 3.7007, + 3.6954, + 3.7069, + 3.7031, + 3.7031, + 3.7148, + 3.7246, + 3.719, + 3.7136, + 3.7179, + 3.7179, + 3.7104, + 3.724, + 3.7379, + 3.7394, + 3.7416, + 3.7416, + 3.7426, + 3.7332, + 3.749, + 3.7558, + 3.7632, + 3.7656, + 3.766, + 3.764, + 3.7673, + 3.7725, + 3.7732, + 3.78, + 3.7751, + 3.7714, + 3.7815, + 3.7797, + 3.7822, + 3.7831, + 3.7857, + 3.7844, + 3.7875, + 3.7816, + 3.7681, + 3.7658, + 3.7822, + 3.7822, + 3.7789, + 3.753, + 3.7487, + 3.7509, + 3.7575, + 3.758, + 3.7479, + 3.7416, + 3.7468, + 3.7468, + 3.7468, + 3.7536, + 3.7612, + 3.7558, + 3.7731, + 3.7758, + 3.7731, + 3.7847, + 3.788, + 3.7857, + 3.7857, + 3.7821, + 3.7846, + 3.7846, + 3.7783, + 3.7837, + 3.7815, + 3.7734, + 3.7721, + 3.7681, + 3.7803, + 3.7933, + 3.7882, + 3.7774, + 3.7755, + 3.7723, + 3.7816, + 3.7903, + 3.7879, + 3.7866, + 3.7891, + 3.7896, + 3.7896, + 3.7895, + 3.7841, + 3.7879, + 3.7892, + 3.7747, + 3.7782, + 3.79, + 3.7876, + 3.7876, + 3.7783, + 3.7785, + 3.7817, + 3.7816, + 3.7979, + 3.8026, + 3.8164, + 3.828, + 3.8067, + 3.8253, + 3.8328, + 3.8278, + 3.8336, + 3.8506, + 3.838, + 3.843, + 3.8422, + 3.8204, + 3.8161, + 3.8071, + 3.8027, + 3.7998, + 3.7992, + 3.7943, + 3.7888, + 3.7888, + 3.7799, + 3.7747, + 3.7799, + 3.7799, + 3.7752, + 3.7783, + 3.7783, + 3.7879, + 3.7854, + 3.7886, + 3.7952, + 3.8145, + 3.8237, + 3.8184, + 3.8184, + 3.831, + 3.8315, + 3.8166, + 3.8194, + 3.81, + 3.8007, + 3.797, + 3.8149, + 3.8096, + 3.7936, + 3.8063, + 3.8204, + 3.8139, + 3.8215, + 3.8295, + 3.8234, + 3.8201, + 3.8246, + 3.8427, + 3.8408, + 3.8365, + 3.8379, + 3.8422, + 3.8435, + 3.8293, + 3.8147, + 3.812, + 3.8034, + 3.8012, + 3.7917, + 3.8012, + 3.8012, + 3.8016, + 3.7918, + 3.7921, + 3.7945, + 3.7945, + 3.7976, + 3.7779, + 3.7816, + 3.7816, + 3.7705, + 3.7691, + 3.7664, + 3.7777, + 3.7841, + 3.785, + 3.7838, + 3.7872, + 3.798, + 3.8001, + 3.804, + 3.8037, + 3.8041, + 3.7932, + 3.8043, + 3.797, + 3.7857, + 3.7836, + 3.7904, + 3.8021, + 3.8068, + 3.808, + 3.8128, + 3.8225, + 3.8177, + 3.8128, + 3.8188, + 3.823, + 3.8204, + 3.8237, + 3.8231, + 3.817, + 3.8254, + 3.8238, + 3.8219, + 3.831, + 3.8286, + 3.8243, + 3.8216, + 3.8169, + 3.8169, + 3.8118, + 3.8177, + 3.8075, + 3.8105, + 3.7946, + 3.788, + 3.7864, + 3.7807, + 3.78, + 3.7819, + 3.7812, + 3.7783, + 3.7751, + 3.7819, + 3.7819, + 3.7564, + 3.76, + 3.746, + 3.7406, + 3.747, + 3.7452, + 3.742, + 3.7416, + 3.7449, + 3.7437, + 3.7384, + 3.7182, + 3.7205, + 3.7347, + 3.7325, + 3.7388, + 3.721, + 3.7413, + 3.7369, + 3.7435, + 3.7419, + 3.7459, + 3.7438, + 3.7456, + 3.7463, + 3.7541, + 3.7679, + 3.7708, + 3.7542, + 3.7479, + 3.7367, + 3.734, + 3.7455, + 3.7439, + 3.7324, + 3.7425, + 3.7425, + 3.745, + 3.7445, + 3.748, + 3.7526, + 3.755, + 3.7637, + 3.7647, + 3.7658, + 3.7658, + 3.7658, + 3.7658, + 3.7626, + 3.7533, + 3.7537, + 3.7533, + 3.7542, + 3.7572, + 3.7491, + 3.7463, + 3.7507, + 3.7433, + 3.7421, + 3.7437, + 3.7495, + 3.745, + 3.7478, + 3.7466, + 3.7464, + 3.739, + 3.7508, + 3.7497, + 3.75, + 3.748, + 3.7438, + 3.7522, + 3.749, + 3.7604, + 3.766, + 3.7668, + 3.7711, + 3.7683, + 3.7683, + 3.7683, + 3.7708, + 3.7653, + 3.7701, + 3.7825, + 3.7878, + 3.7808, + 3.7811, + 3.7848, + 3.7792, + 3.7794, + 3.7776, + 3.7801, + 3.768, + 3.7694, + 3.7545, + 3.754, + 3.7567, + 3.7521, + 3.7603, + 3.7534, + 3.7507, + 3.7479, + 3.7507, + 3.7479, + 3.7569, + 3.7569, + 3.753, + 3.7553, + 3.75, + 3.7471, + 3.7483, + 3.7483, + 3.7504, + 3.7457, + 3.7589, + 3.7622, + 3.7627, + 3.7514, + 3.7484, + 3.7437, + 3.7516, + 3.7516, + 3.7555, + 3.7542, + 3.7471, + 3.7435, + 3.7557, + 3.7542, + 3.7485, + 3.7455, + 3.7497, + 3.7497, + 3.7474, + 3.7354, + 3.7397, + 3.7397, + 3.7396, + 3.7475, + 3.7446, + 3.7468, + 3.7682, + 3.7685, + 3.7704, + 3.767, + 3.7607, + 3.7716, + 3.7751, + 3.7778, + 3.7808, + 3.7816, + 3.7803, + 3.7942, + 3.8135, + 3.8314, + 3.841, + 3.8387, + 3.8367, + 3.8411, + 3.833, + 3.8396, + 3.8307, + 3.8154, + 3.838, + 3.8276, + 3.8245, + 3.8204, + 3.8343, + 3.8196, + 3.831, + 3.8156, + 3.825, + 3.8262, + 3.8094, + 3.8095, + 3.8146, + 3.7907, + 3.783, + 3.7904, + 3.7908, + 3.793, + 3.7723, + 3.7667, + 3.7674, + 3.7416, + 3.7567, + 3.7596, + 3.7592, + 3.7624, + 3.7599, + 3.7573, + 3.7531, + 3.7501, + 3.7521, + 3.7602, + 3.7544, + 3.7661, + 3.7661, + 3.7641, + 3.7702, + 3.7684, + 3.7687, + 3.7581, + 3.7501, + 3.7501, + 3.7433, + 3.7411, + 3.7322, + 3.7107, + 3.6995, + 3.699, + 3.6961, + 3.673, + 3.6916, + 3.6965, + 3.7003, + 3.7128, + 3.7065, + 3.7071, + 3.7097, + 3.7126, + 3.6912, + 3.6699, + 3.6814, + 3.685, + 3.685, + 3.6869, + 3.6567, + 3.6831, + 3.6763, + 3.6822, + 3.6923, + 3.6919, + 3.705, + 3.7089, + 3.713, + 3.7075, + 3.6887, + 3.6891, + 3.6891, + 3.687, + 3.6968, + 3.6928, + 3.6942, + 3.6952, + 3.6922, + 3.6768, + 3.6719, + 3.6782, + 3.6537, + 3.6611, + 3.6678, + 3.6485, + 3.6066, + 3.6148, + 3.6318, + 3.6129, + 3.6198, + 3.598, + 3.488, + 3.5265, + 3.5042, + 3.5013, + 3.4927, + 3.432, + 3.4501, + 3.0304, + 2.75, + 2.4556, + 2.7172, + 2.8168, + 2.9242, + 2.9823, + 3.0195, + 3.0258, + 3.0484, + 3.071, + 3.0398, + 3.0514, + 3.0782, + 3.0923, + 3.1287, + 3.14, + 3.1182, + 3.1416, + 3.0898, + 3.1435, + 3.2265, + 3.1454, + 3.1538, + 3.1118, + 3.14, + 3.1432, + 3.1361, + 3.1903, + 3.2023, + 3.1832, + 3.188, + 3.1662, + 3.161, + 3.1721, + 3.1796, + 3.164, + 3.1886, + 3.207, + 3.2206, + 3.2433, + 3.2504, + 3.2244, + 3.219, + 3.2302, + 3.2492, + 3.2607, + 3.2544, + 3.2503, + 3.2689, + 3.2492, + 3.2518, + 3.2244, + 3.2044, + 3.2188, + 3.2052, + 3.1919, + 3.2023, + 3.2142, + 3.1991, + 3.1811, + 3.187, + 3.1998, + 3.1972, + 3.2007, + 3.1947, + 3.181, + 3.2009, + 3.203, + 3.1951, + 3.1855, + 3.1863, + 3.1658, + 3.163, + 3.1206, + 3.1498, + 3.1488, + 3.1333, + 3.1378, + 3.137, + 3.131, + 3.1383, + 3.151, + 3.142, + 3.128, + 3.1378, + 3.1371, + 3.1684, + 3.1662, + 3.1631, + 3.1682, + 3.164, + 3.1803, + 3.1828, + 3.1675, + 3.1648, + 3.1627, + 3.1455, + 3.1529, + 3.1409, + 3.1247, + 3.1151, + 3.0943, + 3.1165, + 3.1089, + 3.1231, + 3.1296, + 3.123, + 3.1193, + 3.1246, + 3.1301, + 3.1482, + 3.1341, + 3.1309, + 3.1333, + 3.1298, + 3.1253, + 3.1332, + 3.1347, + 3.1416, + 3.1228, + 3.1251, + 3.1177, + 3.1218, + 3.1167, + 3.1189, + 3.1189, + 3.0967, + 3.0707, + 3.0876, + 3.1177, + 3.116, + 3.107, + 3.1068, + 3.1019, + 3.1045, + 3.0939, + 3.0944, + 3.0933, + 3.083, + 3.0814, + 3.0846, + 3.1153, + 3.1053, + 3.1023, + 3.1245, + 3.1182, + 3.1083, + 3.089, + 3.0764, + 3.0684, + 3.0761, + 3.0831, + 3.0782, + 3.0877, + 3.1123, + 3.1228, + 3.107, + 3.1043, + 3.0935, + 3.0959, + 3.102, + 3.0961, + 3.0893, + 3.0814, + 3.0728, + 3.0728, + 3.0808, + 3.0721, + 3.0613, + 3.0608, + 3.055, + 3.0743, + 3.0745, + 3.0933, + 3.1017, + 3.0941, + 3.0938, + 3.099, + 3.0805, + 3.0767, + 3.0861, + 3.0736, + 3.0807, + 3.0796, + 3.0639, + 3.0668, + 3.0724, + 3.0684, + 3.071, + 3.0628, + 3.0572, + 3.0488, + 3.0284, + 3.0398, + 3.041, + 3.0514, + 3.0647, + 3.0768, + 3.0753, + 3.0712, + 3.0697, + 3.0589, + 3.0494, + 3.054, + 3.0684, + 3.0694, + 3.0519, + 3.0604, + 3.06, + 3.0639, + 3.0536, + 3.0577, + 3.0496, + 3.0539, + 3.046, + 3.0467, + 3.0439, + 3.0345, + 3.0465, + 3.0524, + 3.0489, + 3.032, + 3.0374, + 3.0416, + 3.089, + 3.1014, + 3.1046, + 3.1098, + 3.1082, + 3.1255, + 3.1453, + 3.1254, + 3.1302, + 3.1475, + 3.1393, + 3.1487, + 3.136, + 3.135, + 3.1468, + 3.1551, + 3.1402, + 3.1571, + 3.1562, + 3.1632, + 3.169, + 3.1754, + 3.1634, + 3.1564, + 3.1448, + 3.1363, + 3.1463, + 3.1361, + 3.1465, + 3.1344, + 3.1324, + 3.1238, + 3.1259, + 3.1228, + 3.1136, + 3.14, + 3.1496, + 3.1236, + 3.1269, + 3.1167, + 3.1044, + 3.1013, + 3.1134, + 3.1083, + 3.0974, + 3.0878, + 3.0952, + 3.0941, + 3.0989, + 3.091, + 3.095, + 3.0975, + 3.0776, + 3.0718, + 3.089, + 3.0993, + 3.1018, + 3.099, + 3.095, + 3.0941, + 3.1001, + 3.1055, + 3.1144, + 3.1058, + 3.1014, + 3.1008, + 3.1008, + 3.1149, + 3.1151, + 3.1118, + 3.1041, + 3.1055, + 3.1087, + 3.096, + 3.1053, + 3.1053, + 3.109, + 3.1068, + 3.1051, + 3.097, + 3.0978, + 3.0969, + 3.096, + 3.0813, + 3.0875, + 3.0774, + 3.0784, + 3.0807, + 3.0766, + 3.0679, + 3.0615, + 3.0703, + 3.0619, + 3.0635, + 3.0783, + 3.0691, + 3.0667, + 3.0667, + 3.0678, + 3.0789, + 3.0841, + 3.083, + 3.0732, + 3.0738, + 3.076, + 3.0801, + 3.0801, + 3.0806, + 3.0761, + 3.0761, + 3.0787, + 3.0766, + 3.0767, + 3.0704, + 3.0671, + 3.0752, + 3.0752, + 3.0643, + 3.0609, + 3.0691, + 3.0697, + 3.0706, + 3.0814, + 3.0904, + 3.0982, + 3.1041, + 3.1134, + 3.1134, + 3.1069, + 3.1069, + 3.1185, + 3.1222, + 3.1222, + 3.1302, + 3.1237, + 3.12, + 3.1173, + 3.1143, + 3.119, + 3.1178, + 3.1165, + 3.1145, + 3.1115, + 3.1153, + 3.0979, + 3.1055, + 3.1017, + 3.1022, + 3.0935, + 3.0893, + 3.0897, + 3.0897, + 3.086, + 3.0792, + 3.0847, + 3.0786, + 3.0817, + 3.0827, + 3.0862, + 3.0766, + 3.078, + 3.0679, + 3.0647, + 3.0678, + 3.0768, + 3.0749, + 3.0708, + 3.0775, + 3.0775, + 3.0775, + 3.0743, + 3.0707, + 3.0708, + 3.066, + 3.0596, + 3.0598, + 3.0685, + 3.072, + 3.072, + 3.0664, + 3.0664, + 3.0614, + 3.0467, + 3.0389, + 3.0373, + 3.0509, + 3.0499, + 3.0508, + 3.0521, + 3.0436, + 3.0503, + 3.0496, + 3.0673, + 3.0718, + 3.0641, + 3.0662, + 3.0691, + 3.0689, + 3.0673, + 3.0699, + 3.0733, + 3.0755, + 3.076, + 3.0729, + 3.0658, + 3.0728, + 3.0751, + 3.076, + 3.0755, + 3.078, + 3.0843, + 3.0848, + 3.084, + 3.0867, + 3.0843, + 3.08, + 3.08, + 3.0771, + 3.0771, + 3.0771, + 3.0762, + 3.0877, + 3.0975, + 3.0798, + 3.0768, + 3.072, + 3.0743, + 3.0689, + 3.0704, + 3.0714, + 3.0691, + 3.0697, + 3.0674, + 3.0609, + 3.0609, + 3.0637, + 3.0615, + 3.0589, + 3.0585, + 3.0586, + 3.0598, + 3.0547, + 3.0591, + 3.0591, + 3.0563, + 3.0543, + 3.0518, + 3.0549, + 3.0578, + 3.0638, + 3.0638, + 3.0581, + 3.0503, + 3.0484, + 3.0521, + 3.0439, + 3.0473, + 3.0433, + 3.0433, + 3.0543, + 3.0534, + 3.0534, + 3.0598, + 3.0711, + 3.0727, + 3.084, + 3.0885, + 3.0878, + 3.0825, + 3.086, + 3.0782, + 3.0782, + 3.0781, + 3.0782, + 3.0782, + 3.0834, + 3.0827, + 3.081, + 3.0836, + 3.0888, + 3.0927, + 3.0935, + 3.0935, + 3.0916, + 3.0939, + 3.0967, + 3.0964, + 3.0956, + 3.0956, + 3.0931, + 3.0898, + 3.0863, + 3.0863, + 3.0918, + 3.0886, + 3.0886, + 3.0853, + 3.0853, + 3.0856, + 3.0918, + 3.0923, + 3.092, + 3.092, + 3.0919, + 3.092, + 3.0975, + 3.0936, + 3.0896, + 3.0982, + 3.1045, + 3.1033, + 3.1034, + 3.102, + 3.0962, + 3.0972, + 3.0875, + 3.0875, + 3.0911, + 3.0911, + 3.0849, + 3.0871, + 3.0911, + 3.1104, + 3.116, + 3.1165, + 3.1144, + 3.1139, + 3.1139, + 3.1171, + 3.1169, + 3.123, + 3.1273, + 3.1252, + 3.1252, + 3.1252, + 3.1281, + 3.1256, + 3.127, + 3.127, + 3.1407, + 3.137, + 3.1408, + 3.14, + 3.1423, + 3.1449, + 3.1414, + 3.1346, + 3.1397, + 3.1521, + 3.1499, + 3.1499, + 3.154, + 3.1489, + 3.1426, + 3.1598, + 3.1546, + 3.1475, + 3.1511, + 3.1558, + 3.1558, + 3.1568, + 3.149, + 3.1516, + 3.1578, + 3.149, + 3.1505, + 3.1505, + 3.1452, + 3.1428, + 3.1414, + 3.1409, + 3.1378, + 3.1332, + 3.1237, + 3.1245, + 3.1252, + 3.1278, + 3.1278, + 3.1291, + 3.1286, + 3.1286, + 3.1299, + 3.1276, + 3.116, + 3.1164, + 3.1162, + 3.1141, + 3.1146, + 3.1056, + 3.1023, + 3.1035, + 3.1008, + 3.1032, + 3.0963, + 3.0935, + 3.0935, + 3.0898, + 3.0785, + 3.0802, + 3.0885, + 3.083, + 3.0925, + 3.0857, + 3.0808, + 3.0794, + 3.0781, + 3.0747, + 3.0639, + 3.0675, + 3.0742, + 3.0687, + 3.063, + 3.057, + 3.0626, + 3.0676, + 3.0737, + 3.0668, + 3.0639, + 3.0733, + 3.0723, + 3.0723, + 3.0723, + 3.0723, + 3.0639, + 3.0588, + 3.0583, + 3.0608, + 3.0588, + 3.0582, + 3.0582, + 3.0561, + 3.0561, + 3.0561, + 3.0561, + 3.0558, + 3.0522, + 3.0522, + 3.0439, + 3.0448, + 3.0346, + 3.0391, + 3.0391, + 3.0495, + 3.0555, + 3.0563, + 3.0618, + 3.0584, + 3.0611, + 3.0611, + 3.0632, + 3.0673, + 3.0654, + 3.0633, + 3.0622, + 3.0622, + 3.066, + 3.066, + 3.0622, + 3.06, + 3.06, + 3.06, + 3.06, + 3.06, + 3.06, + 3.06, + 3.0592, + 3.0587, + 3.058, + 3.0583, + 3.0546, + 3.0541, + 3.0496, + 3.0573, + 3.0639, + 3.0664, + 3.0698, + 3.077, + 3.0728, + 3.0698, + 3.0698, + 3.0698, + 3.0698, + 3.0711, + 3.0737, + 3.0812, + 3.0812, + 3.0811, + 3.0743, + 3.0721, + 3.0721, + 3.0721, + 3.0635, + 3.0674, + 3.0592, + 3.0732, + 3.0761, + 3.0797, + 3.0814, + 3.087, + 3.0848, + 3.1056, + 3.1099, + 3.1201, + 3.1204, + 3.1189, + 3.1083, + 3.1079, + 3.1041, + 3.0915, + 3.085, + 3.0704, + 3.0702, + 3.0699, + 3.0749, + 3.0696, + 3.0696, + 3.0715, + 3.0702, + 3.0648, + 3.0617, + 3.0636, + 3.0594, + 3.0896, + 3.0872, + 3.089, + 3.09, + 3.0991, + 3.0974, + 3.1098, + 3.1025, + 3.0982, + 3.0966, + 3.0978, + 3.1006, + 3.0905, + 3.088, + 3.0888, + 3.0873, + 3.0873, + 3.0878, + 3.0794, + 3.0794, + 3.0796, + 3.0743, + 3.0743, + 3.0761, + 3.0761, + 3.075, + 3.0852, + 3.078, + 3.0761, + 3.0752, + 3.0741, + 3.0685, + 3.0632, + 3.0621, + 3.0608, + 3.0629, + 3.0599, + 3.062, + 3.0641, + 3.0665, + 3.051, + 3.0439, + 3.04, + 3.0343, + 3.0403, + 3.029, + 3.0417, + 3.029, + 3.0077, + 2.997, + 2.9983, + 3.0206, + 3.0151, + 3.0188, + 3.0188, + 3.0309, + 3.0217, + 3.0244, + 3.0363, + 3.0331, + 3.0329, + 3.0329, + 3.0361, + 3.0361, + 3.0292, + 3.0297, + 3.0164, + 3.0226, + 3.0194, + 3.0218, + 3.0225, + 3.0291, + 3.033, + 3.0456, + 3.0492, + 3.0483, + 3.0372, + 3.0252, + 3.0276, + 3.0281, + 3.0301, + 3.0357, + 3.0436, + 3.0431, + 3.0421, + 3.0389, + 3.0308, + 3.0411, + 3.0448, + 3.0479, + 3.0512, + 3.057, + 3.053, + 3.053, + 3.0454, + 3.0498, + 3.0598, + 3.0673, + 3.0713, + 3.0736, + 3.0681, + 3.0706, + 3.0797, + 3.0841, + 3.0844, + 3.0854, + 3.0854, + 3.0832, + 3.0757, + 3.0819, + 3.0824, + 3.0824, + 3.0824, + 3.075, + 3.079, + 3.0856, + 3.084, + 3.0722, + 3.072, + 3.0771, + 3.0795, + 3.0777, + 3.0992, + 3.1004, + 3.1022, + 3.0975, + 3.1025, + 3.1025, + 3.1064, + 3.1034, + 3.1108, + 3.1177, + 3.1393, + 3.1391, + 3.1342, + 3.1246, + 3.1246, + 3.1379, + 3.1376, + 3.1376, + 3.1328, + 3.1328, + 3.1468, + 3.16, + 3.1639, + 3.1538, + 3.1415, + 3.137, + 3.125, + 3.1256, + 3.116, + 3.1128, + 3.1215, + 3.1216, + 3.1198, + 3.1198, + 3.1144, + 3.1168, + 3.1168, + 3.1218, + 3.1218, + 3.1242, + 3.1187, + 3.1169, + 3.1132, + 3.1105, + 3.1092, + 3.1244, + 3.1217, + 3.1135, + 3.1041, + 3.1041, + 3.1033, + 3.1033, + 3.1078, + 3.1086, + 3.1086, + 3.0952, + 3.0869, + 3.0906, + 3.0907, + 3.0939, + 3.0907, + 3.0893, + 3.091, + 3.0933, + 3.0913, + 3.0913, + 3.0961, + 3.0928, + 3.0981, + 3.0966, + 3.1025, + 3.1118, + 3.1118, + 3.1118, + 3.1208, + 3.1208, + 3.1148, + 3.1118, + 3.12, + 3.1193, + 3.1235, + 3.1235, + 3.1231, + 3.1193, + 3.1149, + 3.1083, + 3.1083, + 3.1056, + 3.1041, + 3.1014, + 3.1042, + 3.1042, + 3.1179, + 3.1195, + 3.1195, + 3.1049, + 3.1033, + 3.1122, + 3.1122, + 3.1122, + 3.1136, + 3.116, + 3.1174, + 3.112, + 3.112, + 3.112, + 3.1185, + 3.1185, + 3.12, + 3.1147, + 3.1147, + 3.1121, + 3.1139, + 3.1139, + 3.1124, + 3.1121, + 3.1121, + 3.1121, + 3.1121, + 3.1121, + 3.1121, + 3.1111, + 3.1111, + 3.1123, + 3.1123, + 3.1123, + 3.1175, + 3.1175, + 3.1202, + 3.1331, + 3.1433, + 3.1389, + 3.1369, + 3.1367, + 3.1367, + 3.1447, + 3.1378, + 3.1356, + 3.1359, + 3.1506, + 3.15, + 3.1506, + 3.1466, + 3.1333, + 3.1293, + 3.1222, + 3.1261, + 3.1286, + 3.1286, + 3.1301, + 3.1247, + 3.1262, + 3.1262, + 3.1284, + 3.1303, + 3.1303, + 3.1319, + 3.1319, + 3.1355, + 3.1402, + 3.1352, + 3.1367, + 3.1375, + 3.1231, + 3.1318, + 3.1318, + 3.1318, + 3.1219, + 3.1219, + 3.1219, + 3.1219, + 3.1219, + 3.1253, + 3.1349, + 3.1349, + 3.1349, + 3.1278, + 3.1278, + 3.133, + 3.1468, + 3.1559, + 3.1574, + 3.1574, + 3.145, + 3.1428, + 3.1428, + 3.1411, + 3.1409, + 3.1393, + 3.1393, + 3.1377, + 3.1338, + 3.1329, + 3.1275, + 3.1229, + 3.1229, + 3.1257, + 3.1257, + 3.1379, + 3.1384, + 3.1384, + 3.1384, + 3.1328, + 3.128, + 3.1316, + 3.1248, + 3.1256, + 3.1284, + 3.1247, + 3.1251, + 3.1262, + 3.1212, + 3.1165, + 3.1219, + 3.1297, + 3.123, + 3.1184, + 3.1136, + 3.1192, + 3.1192, + 3.1192, + 3.1192, + 3.1189, + 3.1168, + 3.1093, + 3.1095, + 3.1065, + 3.1065, + 3.1096, + 3.1159, + 3.1188, + 3.1196, + 3.1264, + 3.127, + 3.1188, + 3.1281, + 3.127, + 3.127, + 3.129, + 3.1354, + 3.1374, + 3.1417, + 3.1417, + 3.1352, + 3.1352, + 3.1352, + 3.1352, + 3.1395, + 3.1347, + 3.1318, + 3.1288, + 3.1231, + 3.1204, + 3.1208, + 3.1191, + 3.1208, + 3.1186, + 3.1223, + 3.1252, + 3.1223, + 3.1223, + 3.1243, + 3.1243, + 3.1298, + 3.1266, + 3.1225, + 3.1233, + 3.1289, + 3.1279, + 3.124, + 3.116, + 3.12, + 3.1164, + 3.1231, + 3.1239, + 3.1196, + 3.1124, + 3.1152, + 3.1185, + 3.1133, + 3.1137, + 3.1088, + 3.1097, + 3.1041, + 3.098, + 3.0984, + 3.1063, + 3.1086, + 3.1097, + 3.1118, + 3.1132, + 3.1104, + 3.1124, + 3.116, + 3.118, + 3.1222, + 3.1113, + 3.1097, + 3.1097, + 3.1097, + 3.1091, + 3.1041, + 3.1041, + 3.0921, + 3.0867, + 3.0889, + 3.0952, + 3.0935, + 3.0836, + 3.0779, + 3.0841, + 3.0841, + 3.0814, + 3.0814, + 3.0787, + 3.087, + 3.0878, + 3.0807, + 3.0862, + 3.0871, + 3.0702, + 3.0676, + 3.0724, + 3.082, + 3.0877, + 3.0877, + 3.0896, + 3.0841, + 3.0863, + 3.0855, + 3.0906, + 3.0835, + 3.0873, + 3.0987, + 3.1047, + 3.1143, + 3.1071, + 3.1071, + 3.096, + 3.0821, + 3.0782, + 3.062, + 3.0724, + 3.082, + 3.0947, + 3.0993, + 3.0899, + 3.0926, + 3.0913, + 3.1011, + 3.1027, + 3.1027, + 3.0916, + 3.1027, + 3.1062, + 3.1131, + 3.1158, + 3.109, + 3.1086, + 3.1024, + 3.1027, + 3.1049, + 3.1034, + 3.1034, + 3.1009, + 3.1, + 3.1008, + 3.1042, + 3.096, + 3.0875, + 3.1054, + 3.1053, + 3.0867, + 3.0921, + 3.1001, + 3.1056, + 3.1077, + 3.1077, + 3.1083, + 3.107, + 3.1038, + 3.0976, + 3.0939, + 3.1017, + 3.1017, + 3.1009, + 3.0984, + 3.0959, + 3.0868, + 3.0861, + 3.0861, + 3.088, + 3.088, + 3.0842, + 3.0834, + 3.0829, + 3.0829, + 3.0829, + 3.0874, + 3.0969, + 3.098, + 3.0989, + 3.0949, + 3.0949, + 3.1018, + 3.1062, + 3.1128, + 3.1129, + 3.1165, + 3.1243, + 3.113, + 3.1116, + 3.1134, + 3.1176, + 3.1227, + 3.1301, + 3.128, + 3.125, + 3.1161, + 3.112, + 3.1187, + 3.1187, + 3.1163, + 3.1163, + 3.1086, + 3.1185, + 3.1174, + 3.1174, + 3.1201, + 3.1164, + 3.1205, + 3.1205, + 3.1205, + 3.1277, + 3.1236, + 3.1236, + 3.123, + 3.1344, + 3.1521, + 3.1691, + 3.1417, + 3.1206, + 3.1161, + 3.1068, + 3.1153, + 3.1167, + 3.1167, + 3.1164, + 3.1162, + 3.1043, + 3.0993, + 3.0898, + 3.0957, + 3.0968, + 3.0973, + 3.093, + 3.0937, + 3.0955, + 3.0894, + 3.0727, + 3.072, + 3.0745, + 3.0716, + 3.0639, + 3.0709, + 3.061, + 3.0519, + 3.0529, + 3.0594, + 3.0658, + 3.082, + 3.0743, + 3.0677, + 3.0677, + 3.0726, + 3.0754, + 3.0864, + 3.0835, + 3.073, + 3.0717, + 3.08, + 3.0875, + 3.1025, + 3.1284, + 3.1423, + 3.1342, + 3.1097, + 3.1194, + 3.1211, + 3.1304, + 3.1288, + 3.11, + 3.1031, + 3.092, + 3.0994, + 3.0914, + 3.1039, + 3.0953, + 3.1023, + 3.0912, + 3.0941, + 3.0892, + 3.0915, + 3.095, + 3.0959, + 3.0988, + 3.0929, + 3.0956, + 3.0922, + 3.0883, + 3.0738, + 3.0825, + 3.0825, + 3.089, + 3.0837, + 3.0837, + 3.0838, + 3.0861, + 3.0861, + 3.0786, + 3.0786, + 3.0967, + 3.0958, + 3.1062, + 3.1031, + 3.1031, + 3.1086, + 3.1059, + 3.1029, + 3.0992, + 3.0977, + 3.1004, + 3.1004, + 3.0975, + 3.1051, + 3.1128, + 3.1144, + 3.1017, + 3.1073, + 3.1069, + 3.1032, + 3.1032, + 3.1089, + 3.1083, + 3.1083, + 3.1014, + 3.1206, + 3.1103, + 3.1188, + 3.1158, + 3.125, + 3.125, + 3.1214, + 3.1214, + 3.1128, + 3.1141, + 3.1111, + 3.1111, + 3.1111, + 3.1134, + 3.1134, + 3.1098, + 3.1098, + 3.1095, + 3.1241, + 3.1071, + 3.1041, + 3.1112, + 3.1112, + 3.1015, + 3.1013, + 3.1013, + 3.1096, + 3.1128, + 3.1105, + 3.1109, + 3.1176, + 3.1181, + 3.1193, + 3.137, + 3.1431, + 3.1353, + 3.1368, + 3.1363, + 3.1413, + 3.1424, + 3.139, + 3.1413, + 3.1288, + 3.1129, + 3.111, + 3.1118, + 3.1173, + 3.1114, + 3.1151, + 3.1335, + 3.1327, + 3.1251, + 3.1218, + 3.1364, + 3.1384, + 3.1486, + 3.1519, + 3.1426, + 3.1292, + 3.1345, + 3.134, + 3.139, + 3.1409, + 3.1469, + 3.1402, + 3.1438, + 3.1488, + 3.1481, + 3.1448, + 3.1506, + 3.1518, + 3.1514, + 3.1564, + 3.1462, + 3.1483, + 3.1474, + 3.1482, + 3.1467, + 3.1399, + 3.1374, + 3.1292, + 3.1339, + 3.1339, + 3.1313, + 3.1277, + 3.129, + 3.1304, + 3.1267, + 3.1236, + 3.1283, + 3.1231, + 3.1302, + 3.1302, + 3.1313, + 3.1274, + 3.1222, + 3.1257, + 3.1249, + 3.124, + 3.1271, + 3.1231, + 3.1227, + 3.1215, + 3.1238, + 3.1208, + 3.121, + 3.1147, + 3.111, + 3.1, + 3.1034, + 3.1092, + 3.1081, + 3.1094, + 3.1124, + 3.1092, + 3.1092, + 3.1106, + 3.1128, + 3.1144, + 3.1222, + 3.1263, + 3.1199, + 3.115, + 3.115, + 3.115, + 3.1312, + 3.1312, + 3.1293, + 3.137, + 3.1484, + 3.1467, + 3.1466, + 3.1496, + 3.1463, + 3.1333, + 3.1314, + 3.1309, + 3.1268, + 3.1284, + 3.1269, + 3.1192, + 3.1173, + 3.1234, + 3.1232, + 3.1248, + 3.1248, + 3.1173, + 3.1173, + 3.1239, + 3.1276, + 3.1276, + 3.1199, + 3.1284, + 3.1344, + 3.132, + 3.1329, + 3.1361, + 3.1311, + 3.129, + 3.1389, + 3.1386, + 3.1321, + 3.131, + 3.1223, + 3.1192, + 3.1178, + 3.1229, + 3.1268, + 3.1268, + 3.116, + 3.115, + 3.115, + 3.1208, + 3.1208, + 3.1203, + 3.1217, + 3.1211, + 3.1284, + 3.1296, + 3.1379, + 3.1364, + 3.144, + 3.1522, + 3.1368, + 3.1394, + 3.1429, + 3.1478, + 3.1495, + 3.157, + 3.16, + 3.1616, + 3.1504, + 3.1632, + 3.1608, + 3.1609, + 3.1588, + 3.1637, + 3.1824, + 3.1784, + 3.1732, + 3.1569, + 3.1577, + 3.1465, + 3.1454, + 3.1507, + 3.1544, + 3.1583, + 3.1614, + 3.1704, + 3.1652, + 3.1663, + 3.1668, + 3.1801, + 3.1866, + 3.18, + 3.1834, + 3.1933, + 3.194, + 3.1861, + 3.1775, + 3.1737, + 3.1766, + 3.1911, + 3.1883, + 3.1987, + 3.1978, + 3.2092, + 3.2212, + 3.2187, + 3.2186, + 3.2025, + 3.2057, + 3.2143, + 3.2216, + 3.2259, + 3.2272, + 3.2272, + 3.2188, + 3.229, + 3.2121, + 3.219, + 3.2201, + 3.2192, + 3.1969, + 3.1971, + 3.2037, + 3.2055, + 3.1982, + 3.1931, + 3.1867, + 3.1823, + 3.1823, + 3.1944, + 3.1979, + 3.1927, + 3.1963, + 3.1952, + 3.1995, + 3.207, + 3.2017, + 3.1871, + 3.1759, + 3.1727, + 3.1627, + 3.1555, + 3.1568, + 3.1584, + 3.1719, + 3.1606, + 3.1576, + 3.1568, + 3.1632, + 3.1607, + 3.1607, + 3.1692, + 3.176, + 3.1815, + 3.1788, + 3.1788, + 3.1652, + 3.1563, + 3.157, + 3.158, + 3.1628, + 3.1639, + 3.156, + 3.1507, + 3.157, + 3.163, + 3.1572, + 3.1646, + 3.1641, + 3.1641, + 3.1563, + 3.1687, + 3.1646, + 3.1646, + 3.1646, + 3.1661, + 3.163, + 3.163, + 3.1649, + 3.176, + 3.176, + 3.176, + 3.1828, + 3.1831, + 3.1766, + 3.1775, + 3.1728, + 3.1659, + 3.1649, + 3.1715, + 3.1743, + 3.1741, + 3.1765, + 3.1769, + 3.1852, + 3.1715, + 3.1672, + 3.1629, + 3.1705, + 3.181, + 3.1821, + 3.1757, + 3.169, + 3.1688, + 3.1669, + 3.1652, + 3.1794, + 3.1878, + 3.2072, + 3.1901, + 3.1897, + 3.1816, + 3.1827, + 3.1809, + 3.184, + 3.185, + 3.1831, + 3.1865, + 3.1899, + 3.1882, + 3.187, + 3.1776, + 3.1715, + 3.1741, + 3.1806, + 3.1752, + 3.1676, + 3.1679, + 3.1713, + 3.1648, + 3.1662, + 3.1606, + 3.1595, + 3.1595, + 3.1561, + 3.151, + 3.1552, + 3.1536, + 3.1524, + 3.1575, + 3.1679, + 3.1691, + 3.1688, + 3.1688, + 3.1662, + 3.1721, + 3.1698, + 3.1698, + 3.1711, + 3.1711, + 3.1728, + 3.1682, + 3.1682, + 3.1682, + 3.1739, + 3.176, + 3.176, + 3.1699, + 3.1682, + 3.1717, + 3.1771, + 3.1759, + 3.1751, + 3.1746, + 3.1776, + 3.1833, + 3.1812, + 3.1731, + 3.1731, + 3.1775, + 3.1875, + 3.1911, + 3.1849, + 3.1825, + 3.1743, + 3.1677, + 3.1672, + 3.1862, + 3.1846, + 3.1846, + 3.1813, + 3.1813, + 3.1816, + 3.1804, + 3.1804, + 3.1888, + 3.1821, + 3.1774, + 3.1787, + 3.1787, + 3.1787, + 3.1726, + 3.1723, + 3.1805, + 3.1805, + 3.1805, + 3.1909, + 3.1898, + 3.1894, + 3.1911, + 3.1951, + 3.2012, + 3.1947, + 3.1952, + 3.1952, + 3.1952, + 3.192, + 3.194, + 3.1924, + 3.1922, + 3.1922, + 3.194, + 3.194, + 3.194, + 3.194, + 3.194, + 3.1838, + 3.186, + 3.1879, + 3.1823, + 3.1806, + 3.1806, + 3.1808, + 3.1817, + 3.1789, + 3.1854, + 3.1935, + 3.1923, + 3.1974, + 3.1999, + 3.1933, + 3.1986, + 3.2042, + 3.207, + 3.1982, + 3.2037, + 3.2047, + 3.2124, + 3.2055, + 3.2028, + 3.2083, + 3.2096, + 3.2124, + 3.2124, + 3.2093, + 3.2106, + 3.1944, + 3.1983, + 3.203, + 3.2022, + 3.2019, + 3.2078, + 3.1979, + 3.1983, + 3.1981, + 3.1903, + 3.1934, + 3.1886, + 3.1886, + 3.1816, + 3.1711, + 3.1711, + 3.1711, + 3.18, + 3.1801, + 3.1786, + 3.1786, + 3.1786, + 3.1786, + 3.176, + 3.176, + 3.176, + 3.1802, + 3.1805, + 3.1859, + 3.1863, + 3.1859, + 3.1865, + 3.1867, + 3.1911, + 3.1927, + 3.1816, + 3.1816, + 3.1823, + 3.1883, + 3.1883, + 3.1825, + 3.1805, + 3.1827, + 3.1775, + 3.1779, + 3.1868, + 3.1837, + 3.1893, + 3.1944, + 3.203, + 3.203, + 3.203, + 3.1978, + 3.1977, + 3.1938, + 3.1938, + 3.1938, + 3.1944, + 3.1967, + 3.202, + 3.1991, + 3.1995, + 3.1995, + 3.1993, + 3.1977, + 3.2138, + 3.2176, + 3.22, + 3.2103, + 3.2212, + 3.2214, + 3.2162, + 3.2176, + 3.2141, + 3.2027, + 3.2048, + 3.208, + 3.2116, + 3.2035, + 3.194, + 3.194, + 3.1958, + 3.2001, + 3.1876, + 3.1876, + 3.1876, + 3.1883, + 3.1897, + 3.1956, + 3.211, + 3.2118, + 3.208, + 3.2023, + 3.2097, + 3.2125, + 3.214, + 3.2065, + 3.2065, + 3.2065, + 3.194, + 3.1897, + 3.1882, + 3.1959, + 3.1917, + 3.1847, + 3.1847, + 3.1916, + 3.194, + 3.191, + 3.184, + 3.184, + 3.1933, + 3.1938, + 3.1905, + 3.1905, + 3.1871, + 3.1871, + 3.1906, + 3.1904, + 3.1904, + 3.1949, + 3.1949, + 3.1943, + 3.198, + 3.1919, + 3.1977, + 3.2038, + 3.203, + 3.1965, + 3.1956, + 3.193, + 3.193, + 3.1799, + 3.1809, + 3.1944, + 3.2119, + 3.2119, + 3.2063, + 3.2124, + 3.2124, + 3.2083, + 3.2092, + 3.2104, + 3.2138, + 3.2137, + 3.2139, + 3.2139, + 3.2092, + 3.2114, + 3.2161, + 3.2199, + 3.2183, + 3.2101, + 3.2101, + 3.2101, + 3.2089, + 3.2089, + 3.2089, + 3.2089, + 3.2269, + 3.2342, + 3.2323, + 3.2323, + 3.2305, + 3.2357, + 3.2456, + 3.2411, + 3.2423, + 3.2388, + 3.2488, + 3.2499, + 3.2499, + 3.2352, + 3.2402, + 3.2272, + 3.2249, + 3.2249, + 3.2212, + 3.2115, + 3.2047, + 3.2047, + 3.2078, + 3.2078, + 3.2127, + 3.2086, + 3.2131, + 3.2195, + 3.2158, + 3.2078, + 3.2078, + 3.2049, + 3.2049, + 3.2063, + 3.2, + 3.1879, + 3.1912, + 3.1879, + 3.19, + 3.19, + 3.2039, + 3.21, + 3.2151, + 3.21, + 3.2159, + 3.2044, + 3.2044, + 3.2043, + 3.2188, + 3.2188, + 3.2188, + 3.2188, + 3.2033, + 3.2033, + 3.2033, + 3.2033, + 3.2025, + 3.2, + 3.1987, + 3.1951, + 3.1905, + 3.1891, + 3.184, + 3.1975, + 3.2004, + 3.1949, + 3.2022, + 3.2054, + 3.2019, + 3.2029, + 3.1983, + 3.2087, + 3.216, + 3.216, + 3.2121, + 3.2121, + 3.2121, + 3.2168, + 3.2143, + 3.2139, + 3.2077, + 3.2012, + 3.202, + 3.2007, + 3.1951, + 3.1919, + 3.194, + 3.1919, + 3.1908, + 3.1852, + 3.1852, + 3.1852, + 3.1942, + 3.2, + 3.2002, + 3.2044, + 3.2076, + 3.2076, + 3.2076, + 3.2076, + 3.2065, + 3.201, + 3.2075, + 3.2085, + 3.208, + 3.2085, + 3.2094, + 3.206, + 3.2042, + 3.204, + 3.2138, + 3.208, + 3.1955, + 3.1939, + 3.1981, + 3.209, + 3.2152, + 3.2172, + 3.2148, + 3.2152, + 3.2152, + 3.2152, + 3.218, + 3.2303, + 3.2253, + 3.2179, + 3.2179, + 3.237, + 3.2374, + 3.2374, + 3.232, + 3.2346, + 3.2374, + 3.2374, + 3.2405, + 3.2388, + 3.2348, + 3.2304, + 3.2271, + 3.2228, + 3.2064, + 3.2054, + 3.2048, + 3.2078, + 3.1966, + 3.2068, + 3.2008, + 3.1931, + 3.208, + 3.2078, + 3.2138, + 3.2138, + 3.21, + 3.2075, + 3.2172, + 3.2228, + 3.2165, + 3.2292, + 3.226, + 3.2253, + 3.2244, + 3.2244, + 3.2165, + 3.2165, + 3.2165, + 3.2267, + 3.2179, + 3.2179, + 3.2179, + 3.2188, + 3.2222, + 3.2276, + 3.2306, + 3.2293, + 3.2307, + 3.2223, + 3.2223, + 3.2223, + 3.2223, + 3.2223, + 3.2223, + 3.2223, + 3.2223, + 3.2241, + 3.2133, + 3.2038, + 3.1944, + 3.1883, + 3.1991, + 3.2064, + 3.2045, + 3.2038, + 3.2062, + 3.2055, + 3.1991, + 3.1909, + 3.1906, + 3.184, + 3.16, + 3.1662, + 3.1802, + 3.1707, + 3.1721, + 3.1806, + 3.1869, + 3.176, + 3.1774, + 3.1774, + 3.1774, + 3.1718, + 3.167, + 3.1671, + 3.1661, + 3.1671, + 3.1563, + 3.1592, + 3.131, + 3.0041, + 3.0766, + 3.0633, + 3.0565, + 3.0623, + 3.0815, + 3.0977, + 3.0943, + 3.0961, + 3.0966, + 3.0867, + 3.0949, + 3.1254, + 3.1152, + 3.1312, + 3.1186, + 3.1118, + 3.1214, + 3.1162, + 3.1085, + 3.1041, + 3.0987, + 3.083, + 3.0846, + 3.0992, + 3.1018, + 3.1023, + 3.0958, + 3.0893, + 3.0903, + 3.0895, + 3.0896, + 3.0968, + 3.0987, + 3.0987, + 3.082, + 3.0806, + 3.0764, + 3.0779, + 3.0827, + 3.0644, + 3.0631, + 3.0656, + 3.0862, + 3.0834, + 3.0883, + 3.092, + 3.0849, + 3.0891, + 3.0894, + 3.1, + 3.0956, + 3.0926, + 3.0932, + 3.094, + 3.0895, + 3.0895, + 3.0847, + 3.0802, + 3.0845, + 3.0886, + 3.0886, + 3.0819, + 3.0779, + 3.0737, + 3.0789, + 3.0762, + 3.0689, + 3.0689, + 3.0689, + 3.0689, + 3.0732, + 3.072, + 3.072, + 3.0877, + 3.0954, + 3.09, + 3.0834, + 3.0839, + 3.0807, + 3.0807, + 3.072, + 3.072, + 3.078, + 3.078, + 3.0802, + 3.0758, + 3.0838, + 3.0839, + 3.079, + 3.0746, + 3.0673, + 3.0599, + 3.0599, + 3.0701, + 3.0701, + 3.0708, + 3.074, + 3.0814, + 3.0775, + 3.0775, + 3.0775, + 3.0822, + 3.0822, + 3.0784, + 3.0803, + 3.0759, + 3.0813, + 3.0856, + 3.081, + 3.0695, + 3.0697, + 3.0678, + 3.0667, + 3.0606, + 3.058, + 3.058, + 3.0622, + 3.0681, + 3.0638, + 3.0638, + 3.0577, + 3.063, + 3.063, + 3.0577, + 3.0577, + 3.0665, + 3.0673, + 3.0673, + 3.0645, + 3.0689, + 3.0669, + 3.0669, + 3.0692, + 3.0624, + 3.0679, + 3.0663, + 3.0663, + 3.0649, + 3.0595, + 3.0504, + 3.0427, + 3.0416, + 3.0388, + 3.0346, + 3.0338, + 3.0448, + 3.0408, + 3.0364, + 3.0484, + 3.035, + 3.0371, + 3.0426, + 3.0426, + 3.0395, + 3.0525, + 3.0525, + 3.0589, + 3.0657, + 3.0657, + 3.0554, + 3.0592, + 3.0592, + 3.0559, + 3.0587, + 3.0598, + 3.0658, + 3.073, + 3.0686, + 3.0735, + 3.0723, + 3.0739, + 3.0786, + 3.0752, + 3.0824, + 3.091, + 3.0862, + 3.083, + 3.087, + 3.087, + 3.083, + 3.0822, + 3.0817, + 3.0769, + 3.079, + 3.074, + 3.078, + 3.0815, + 3.0806, + 3.0806, + 3.0806, + 3.0823, + 3.0794, + 3.0797, + 3.084, + 3.0867, + 3.089, + 3.0976, + 3.1074, + 3.1054, + 3.1067, + 3.0975, + 3.0975, + 3.091, + 3.0951, + 3.094, + 3.092, + 3.0999, + 3.0996, + 3.0996, + 3.0992, + 3.0992, + 3.1051, + 3.1056, + 3.1104, + 3.1104, + 3.1152, + 3.1122, + 3.1122, + 3.1122, + 3.1122, + 3.1122, + 3.1036, + 3.1046, + 3.1082, + 3.1082, + 3.116, + 3.1108, + 3.1108, + 3.1108, + 3.1108, + 3.1129, + 3.1109, + 3.1109, + 3.1036, + 3.1036, + 3.1036, + 3.0974, + 3.0967, + 3.0967, + 3.0967, + 3.108, + 3.1106, + 3.0984, + 3.0935, + 3.0895, + 3.0813, + 3.0813, + 3.0916, + 3.0916, + 3.1007, + 3.0968, + 3.0939, + 3.0939, + 3.1, + 3.1018, + 3.1018, + 3.1018, + 3.1018, + 3.089, + 3.089, + 3.0947, + 3.0947, + 3.1024, + 3.1042, + 3.1042, + 3.1042, + 3.1075, + 3.1041, + 3.1041, + 3.1004, + 3.1028, + 3.1003, + 3.1003, + 3.1003, + 3.0978, + 3.0938, + 3.0947, + 3.0974, + 3.0974, + 3.091, + 3.0819, + 3.0831, + 3.0768, + 3.0747, + 3.0737, + 3.0734, + 3.0772, + 3.0772, + 3.0772, + 3.0777, + 3.0777, + 3.0843, + 3.0993, + 3.0921, + 3.0937, + 3.0937, + 3.0953, + 3.0953, + 3.0953, + 3.0926, + 3.0926, + 3.0977, + 3.0995, + 3.0995, + 3.1018, + 3.1025, + 3.1025, + 3.0959, + 3.0937, + 3.0937, + 3.0937, + 3.1019, + 3.1056, + 3.1011, + 3.1056, + 3.106, + 3.1003, + 3.1001, + 3.1023, + 3.091, + 3.0914, + 3.0807, + 3.0836, + 3.0856, + 3.0774, + 3.0857, + 3.0864, + 3.0764, + 3.0771, + 3.0727, + 3.0731, + 3.0745, + 3.0726, + 3.0679, + 3.0686, + 3.072, + 3.0736, + 3.075, + 3.0862, + 3.0867, + 3.0902, + 3.082, + 3.0841, + 3.0841, + 3.0841, + 3.0841, + 3.0738, + 3.072, + 3.072, + 3.0773, + 3.0796, + 3.0796, + 3.0824, + 3.0785, + 3.0785, + 3.0785, + 3.08, + 3.08, + 3.08, + 3.0781, + 3.0781, + 3.0768, + 3.0768, + 3.0768, + 3.0768, + 3.0768, + 3.0739, + 3.0739, + 3.0739, + 3.0747, + 3.0747, + 3.0777, + 3.0826, + 3.0799, + 3.0862, + 3.083, + 3.083, + 3.0831, + 3.0822, + 3.0742, + 3.0755, + 3.0755, + 3.0805, + 3.0751, + 3.0751, + 3.0751, + 3.0874, + 3.0875, + 3.0816, + 3.082, + 3.0853, + 3.0876, + 3.0895, + 3.086, + 3.0944, + 3.0976, + 3.0956, + 3.0956, + 3.0956, + 3.0913, + 3.0911, + 3.0909, + 3.0848, + 3.0872, + 3.0947, + 3.089, + 3.082, + 3.0866, + 3.0849, + 3.0845, + 3.0806, + 3.072, + 3.0729, + 3.0701, + 3.0705, + 3.0707, + 3.069, + 3.0745, + 3.0742, + 3.0798, + 3.0778, + 3.0743, + 3.071, + 3.0708, + 3.0707, + 3.0789, + 3.0746, + 3.0718, + 3.0732, + 3.0712, + 3.0726, + 3.0779, + 3.1036, + 3.1167, + 3.1111, + 3.1036, + 3.1112, + 3.1182, + 3.1073, + 3.107, + 3.0971, + 3.0995, + 3.0995, + 3.0978, + 3.0979, + 3.0935, + 3.0965, + 3.0905, + 3.09, + 3.094, + 3.094, + 3.094, + 3.094, + 3.0972, + 3.0929, + 3.0856, + 3.0934, + 3.0934, + 3.0934, + 3.0951, + 3.0951, + 3.0912, + 3.0958, + 3.0987, + 3.0992, + 3.0991, + 3.0991, + 3.1069, + 3.1031, + 3.1047, + 3.1047, + 3.1075, + 3.1036, + 3.1036, + 3.1036, + 3.1036, + 3.0955, + 3.1005, + 3.0966, + 3.099, + 3.099, + 3.0891, + 3.0902, + 3.0856, + 3.082, + 3.0875, + 3.0875, + 3.0877, + 3.092, + 3.092, + 3.0897, + 3.0889, + 3.0877, + 3.0939, + 3.0852, + 3.0852, + 3.0831, + 3.0833, + 3.0774, + 3.0719, + 3.0737, + 3.0708, + 3.0727, + 3.0727, + 3.0719, + 3.0703, + 3.077, + 3.0755, + 3.0755, + 3.0844, + 3.0801, + 3.079, + 3.0804, + 3.0747, + 3.0738, + 3.0757, + 3.0742, + 3.0742, + 3.0711, + 3.072, + 3.0742, + 3.0723, + 3.0683, + 3.0636, + 3.0549, + 3.0622, + 3.0586, + 3.0516, + 3.0468, + 3.0488, + 3.0517, + 3.0466, + 3.0466, + 3.0496, + 3.0519, + 3.0515, + 3.0545, + 3.0512, + 3.0473, + 3.0473, + 3.0525, + 3.0499, + 3.0462, + 3.0429, + 3.0408, + 3.0408, + 3.0408, + 3.0285, + 3.0338, + 3.028, + 3.0182, + 3.0312, + 3.0376, + 3.0319, + 3.0394, + 3.038, + 3.0418, + 3.0378, + 3.0392, + 3.0337, + 3.0344, + 3.0265, + 3.0212, + 3.019, + 3.0307, + 3.0307, + 3.0343, + 3.0327, + 3.0256, + 3.0228, + 3.0269, + 3.0232, + 3.0355, + 3.0318, + 3.0358, + 3.0315, + 3.038, + 3.0407, + 3.0391, + 3.0351, + 3.0327, + 3.0416, + 3.037, + 3.0343, + 3.0398, + 3.0423, + 3.0446, + 3.0428, + 3.0465, + 3.0454, + 3.04, + 3.038, + 3.0487, + 3.0459, + 3.0459, + 3.0469, + 3.0477, + 3.0454, + 3.0495, + 3.0418, + 3.0483, + 3.0517, + 3.054, + 3.05, + 3.0478, + 3.0478, + 3.0468, + 3.0373, + 3.0398, + 3.0421, + 3.0421, + 3.0421, + 3.0484, + 3.0555, + 3.0576, + 3.0468, + 3.0292, + 3.0318, + 3.025, + 3.0245, + 3.027, + 3.0235, + 3.0256, + 3.023, + 3.032, + 3.0378, + 3.0471, + 3.0466, + 3.044, + 3.0469, + 3.0467, + 3.05, + 3.0559, + 3.0529, + 3.0537, + 3.055, + 3.0541, + 3.0655, + 3.0631, + 3.0598, + 3.049, + 3.0529, + 3.049, + 3.0455, + 3.0506, + 3.0506, + 3.0494, + 3.0465, + 3.0438, + 3.0445, + 3.0505, + 3.0433, + 3.0456, + 3.04, + 3.039, + 3.0395, + 3.0358, + 3.0362, + 3.0359, + 3.0306, + 3.0281, + 3.0364, + 3.0335, + 3.0359, + 3.0392, + 3.0405, + 3.0381, + 3.0454, + 3.0471, + 3.0398, + 3.04, + 3.0367, + 3.0407, + 3.051, + 3.057, + 3.057, + 3.0485, + 3.0477, + 3.0505, + 3.0491, + 3.0514, + 3.0587, + 3.0508, + 3.056, + 3.0477, + 3.049, + 3.0558, + 3.0444, + 3.0572, + 3.0534, + 3.0574, + 3.0546, + 3.056, + 3.0552, + 3.0509, + 3.0453, + 3.0427, + 3.0414, + 3.04, + 3.0407, + 3.0407, + 3.0397, + 3.0324, + 3.056, + 3.0563, + 3.0501, + 3.0389, + 3.0362, + 3.0433, + 3.0414, + 3.0414, + 3.0462, + 3.0464, + 3.0449, + 3.0487, + 3.0466, + 3.0456, + 3.0456, + 3.036, + 3.0366, + 3.0366, + 3.0366, + 3.0366, + 3.0299, + 3.0446, + 3.0407, + 3.0337, + 3.0337, + 3.0337, + 3.0281, + 3.0396, + 3.0446, + 3.0414, + 3.0536, + 3.042, + 3.0456, + 3.0456, + 3.0453, + 3.039, + 3.0472, + 3.0376, + 3.0337, + 3.0222, + 3.0206, + 3.0258, + 3.0301, + 3.0201, + 3.0222, + 3.0222, + 3.0172, + 3.0123, + 2.9949, + 2.9982, + 3.0088, + 3.002, + 3.0014, + 3.0021, + 3.0036, + 2.9958, + 2.9989, + 2.9949, + 3.0064, + 3.0038, + 3.0043, + 2.9953, + 2.9943, + 2.9723, + 2.9888, + 2.9936, + 2.9918, + 2.9934, + 2.9838, + 2.9872, + 2.9598, + 2.9812, + 2.9883, + 3.0058, + 2.993, + 2.9878, + 2.9812, + 2.9837, + 2.9834, + 2.9786, + 2.9661, + 2.9686, + 2.9686, + 2.9694, + 2.9686, + 2.9627, + 2.9562, + 2.9511, + 2.9583, + 2.9666, + 2.9616, + 2.9693, + 2.9701, + 2.965, + 2.9814, + 2.9814, + 2.9764, + 2.9741, + 2.9737, + 2.9848, + 2.9827, + 2.9691, + 2.9734, + 2.9726, + 2.9677, + 2.9616, + 2.9519, + 2.9514, + 2.9342, + 2.9491, + 2.96, + 2.9616, + 2.971, + 2.9712, + 2.9597, + 2.9602, + 2.9621, + 2.9589, + 2.9553, + 2.9591, + 2.9591, + 2.9644, + 2.9682, + 2.9703, + 2.9652, + 2.9652, + 2.9667, + 2.9654, + 2.9663, + 2.9711, + 2.9705, + 2.9705, + 2.9531, + 2.9583, + 2.9635, + 2.9589, + 2.9589, + 2.9688, + 2.9605, + 2.958, + 2.9605, + 2.9521, + 2.9531, + 2.9482, + 2.9482, + 2.9582, + 2.9605, + 2.9641, + 2.9845, + 2.9853, + 2.9822, + 2.9758, + 2.9832, + 2.9817, + 2.9675, + 2.9663, + 2.9711, + 2.9726, + 2.971, + 2.9764, + 2.9822, + 2.9822, + 2.981, + 2.9892, + 2.9941, + 2.9941, + 2.9938, + 2.9938, + 2.9818, + 2.985, + 2.9722, + 2.9722, + 2.9728, + 2.9736, + 2.9731, + 2.9731, + 2.9748, + 2.9781, + 2.979, + 2.9727, + 2.9727, + 2.9784, + 2.9852, + 2.9851, + 2.9792, + 2.9818, + 2.989, + 2.9861, + 2.9844, + 2.9838, + 2.9852, + 2.9876, + 2.9834, + 2.9854, + 2.9817, + 2.98, + 2.9598, + 2.9545, + 2.9729, + 2.9729, + 2.9669, + 2.9642, + 2.9555, + 2.9471, + 2.9486, + 2.9419, + 2.9398, + 2.926, + 2.9372, + 2.9502, + 2.9445, + 2.9425, + 2.9308, + 2.899, + 2.9103, + 2.915, + 2.8949, + 2.8961, + 2.9124, + 2.9225, + 2.9129, + 2.9172, + 2.9181, + 2.9208, + 2.9197, + 2.9275, + 2.925, + 2.9232, + 2.9182, + 2.9078, + 2.9118, + 2.9149, + 2.9133, + 2.9011, + 2.9012, + 2.9018, + 2.8996, + 2.8996, + 2.9051, + 2.906, + 2.9046, + 2.9046, + 2.8944, + 2.8943, + 2.9025, + 2.9184, + 2.9158, + 2.9188, + 2.9131, + 2.9105, + 2.9124, + 2.9212, + 2.918, + 2.9172, + 2.9172, + 2.911, + 2.911, + 2.911, + 2.9063, + 2.9119, + 2.9123, + 2.9181, + 2.9188, + 2.9169, + 2.9191, + 2.9211, + 2.9168, + 2.9159, + 2.9198, + 2.9193, + 2.9147, + 2.9178, + 2.9207, + 2.9197, + 2.9197, + 2.9235, + 2.9272, + 2.928, + 2.9239, + 2.926, + 2.9285, + 2.9285, + 2.9327, + 2.9303, + 2.9303, + 2.9274, + 2.9274, + 2.9219, + 2.9194, + 2.9122, + 2.9164, + 2.9164, + 2.9123, + 2.9087, + 2.9078, + 2.9071, + 2.9071, + 2.9089, + 2.8932, + 2.9117, + 2.9143, + 2.9111, + 2.9151, + 2.9146, + 2.9192, + 2.9271, + 2.9275, + 2.932, + 2.9393, + 2.9381, + 2.9372, + 2.9339, + 2.9304, + 2.9296, + 2.9294, + 2.9294, + 2.9325, + 2.933, + 2.9325, + 2.9307, + 2.9271, + 2.9271, + 2.9246, + 2.926, + 2.9266, + 2.9255, + 2.9239, + 2.9257, + 2.9178, + 2.9119, + 2.9144, + 2.9149, + 2.9149, + 2.9129, + 2.9125, + 2.9125, + 2.9125, + 2.9234, + 2.9253, + 2.928, + 2.9279, + 2.9308, + 2.9316, + 2.9316, + 2.9362, + 2.941, + 2.941, + 2.9362, + 2.9361, + 2.9361, + 2.9356, + 2.9375, + 2.9375, + 2.9366, + 2.9366, + 2.925, + 2.9251, + 2.9238, + 2.9224, + 2.9202, + 2.9261, + 2.9235, + 2.9297, + 2.9297, + 2.9297, + 2.933, + 2.9278, + 2.9278, + 2.9263, + 2.9273, + 2.9273, + 2.9273, + 2.9283, + 2.9325, + 2.9397, + 2.9415, + 2.9421, + 2.9369, + 2.928, + 2.925, + 2.9195, + 2.9169, + 2.913, + 2.913, + 2.9157, + 2.9182, + 2.9182, + 2.9182, + 2.9255, + 2.9321, + 2.9321, + 2.9321, + 2.9321, + 2.9321, + 2.9321, + 2.9276, + 2.9276, + 2.9276, + 2.9296, + 2.9269, + 2.9269, + 2.9269, + 2.9269, + 2.9269, + 2.9228, + 2.917, + 2.917, + 2.917, + 2.922, + 2.9189, + 2.9189, + 2.9189, + 2.909, + 2.9112, + 2.9129, + 2.9129, + 2.9134, + 2.9061, + 2.9109, + 2.9148, + 2.9083, + 2.9094, + 2.9062, + 2.9085, + 2.9103, + 2.9183, + 2.9225, + 2.9225, + 2.9147, + 2.9147, + 2.9154, + 2.9143, + 2.9154, + 2.9168, + 2.9156, + 2.9147, + 2.911, + 2.9111, + 2.9073, + 2.9063, + 2.9054, + 2.9027, + 2.9246, + 2.9238, + 2.9057, + 2.8935, + 2.9036, + 2.9034, + 2.8926, + 2.8914, + 2.8812, + 2.8752, + 2.8713, + 2.8656, + 2.8595, + 2.8736, + 2.8727, + 2.8727, + 2.867, + 2.8741, + 2.8693, + 2.8678, + 2.87, + 2.8719, + 2.8683, + 2.8829, + 2.8842, + 2.8824, + 2.8833, + 2.8859, + 2.893, + 2.8967, + 2.8985, + 2.8985, + 2.8974, + 2.8913, + 2.8934, + 2.8934, + 2.8935, + 2.8935, + 2.8935, + 2.8932, + 2.8907, + 2.8895, + 2.8921, + 2.8921, + 2.8921, + 2.8849, + 2.8866, + 2.881, + 2.878, + 2.8791, + 2.8791, + 2.8815, + 2.8856, + 2.8934, + 2.8943, + 2.8965, + 2.8965, + 2.8965, + 2.8934, + 2.8934, + 2.8934, + 2.8946, + 2.8922, + 2.8977, + 2.9015, + 2.9006, + 2.9032, + 2.9053, + 2.908, + 2.9133, + 2.9136, + 2.9136, + 2.9104, + 2.9163, + 2.9154, + 2.9187, + 2.9143, + 2.9143, + 2.9143, + 2.9143, + 2.9141, + 2.9123, + 2.908, + 2.9002, + 2.9007, + 2.9053, + 2.897, + 2.8946, + 2.8992, + 2.8977, + 2.8989, + 2.9003, + 2.8964, + 2.8959, + 2.8959, + 2.894, + 2.8962, + 2.8962, + 2.8999, + 2.8999, + 2.8999, + 2.8939, + 2.8956, + 2.8858, + 2.8858, + 2.8858, + 2.8858, + 2.8858, + 2.895, + 2.895, + 2.895, + 2.8926, + 2.8872, + 2.8843, + 2.8849, + 2.8849, + 2.8842, + 2.8737, + 2.8737, + 2.8737, + 2.8854, + 2.8877, + 2.8918, + 2.8926, + 2.9027, + 2.9068, + 2.915, + 2.913, + 2.9123, + 2.9123, + 2.9227, + 2.9268, + 2.9276, + 2.9225, + 2.9266, + 2.9268, + 2.9285, + 2.9404, + 2.9418, + 2.9451, + 2.9481, + 2.942, + 2.941, + 2.9329, + 2.9329, + 2.9329, + 2.9379, + 2.9429, + 2.9436, + 2.9436, + 2.9452, + 2.948, + 2.9483, + 2.946, + 2.943, + 2.9484, + 2.9503, + 2.9515, + 2.9545, + 2.9493, + 2.9493, + 2.9585, + 2.9564, + 2.9564, + 2.9606, + 2.9558, + 2.959, + 2.9583, + 2.9621, + 2.9588, + 2.9558, + 2.9605, + 2.9646, + 2.9617, + 2.9617, + 2.961, + 2.9653, + 2.9666, + 2.9653, + 2.9662, + 2.9682, + 2.9718, + 2.9731, + 2.973, + 2.9773, + 2.9811, + 2.981, + 2.9748, + 2.9708, + 2.9798, + 2.9664, + 2.9629, + 2.96, + 2.9498, + 2.9532, + 2.9582, + 2.9582, + 2.9582, + 2.9544, + 2.9516, + 2.9511, + 2.9445, + 2.9432, + 2.9393, + 2.935, + 2.9408, + 2.9412, + 2.9429, + 2.9381, + 2.9381, + 2.9381, + 2.93, + 2.94, + 2.9397, + 2.9385, + 2.9349, + 2.9372, + 2.9388, + 2.936, + 2.9402, + 2.9432, + 2.9428, + 2.9456, + 2.9437, + 2.9376, + 2.9376, + 2.9404, + 2.94, + 2.935, + 2.935, + 2.9302, + 2.9206, + 2.9142, + 2.9169, + 2.9206, + 2.9229, + 2.9211, + 2.9298, + 2.9335, + 2.932, + 2.9333, + 2.9348, + 2.9368, + 2.9307, + 2.9342, + 2.9378, + 2.9348, + 2.9269, + 2.9287, + 2.9325, + 2.9348, + 2.9302, + 2.9269, + 2.9199, + 2.9159, + 2.9119, + 2.9062, + 2.9092, + 2.9159, + 2.9159, + 2.9089, + 2.915, + 2.915, + 2.8994, + 2.9053, + 2.8974, + 2.8906, + 2.8781, + 2.8802, + 2.8824, + 2.8857, + 2.8831, + 2.8798, + 2.8778, + 2.8672, + 2.8672, + 2.877, + 2.8817, + 2.8863, + 2.8863, + 2.8881, + 2.8838, + 2.8867, + 2.8911, + 2.9045, + 2.9096, + 2.9135, + 2.9098, + 2.9121, + 2.9164, + 2.9164, + 2.9169, + 2.9148, + 2.9148, + 2.9105, + 2.9109, + 2.9118, + 2.9099, + 2.9099, + 2.9099, + 2.8992, + 2.9035, + 2.8974, + 2.8957, + 2.8947, + 2.9004, + 2.9, + 2.9086, + 2.9038, + 2.8981, + 2.8981, + 2.9046, + 2.9046, + 2.8981, + 2.8981, + 2.8981, + 2.8955, + 2.8873, + 2.884, + 2.8727, + 2.8706, + 2.8623, + 2.8666, + 2.8652, + 2.8591, + 2.8647, + 2.8648, + 2.8675, + 2.8715, + 2.8752, + 2.8844, + 2.8844, + 2.8876, + 2.8896, + 2.8896, + 2.8884, + 2.8911, + 2.8932, + 2.8947, + 2.8947, + 2.8947, + 2.8947, + 2.8981, + 2.901, + 2.8983, + 2.8951, + 2.8951, + 2.8811, + 2.8831, + 2.8831, + 2.8831, + 2.8921, + 2.8921, + 2.8838, + 2.8857, + 2.8821, + 2.8822, + 2.8822, + 2.8822, + 2.8822, + 2.8857, + 2.8857, + 2.8804, + 2.8801, + 2.8833, + 2.878, + 2.8794, + 2.8799, + 2.8812, + 2.8812, + 2.8761, + 2.8761, + 2.8854, + 2.886, + 2.886, + 2.881, + 2.881, + 2.881, + 2.8809, + 2.8809, + 2.8788, + 2.8788, + 2.8788, + 2.8836, + 2.8846, + 2.8882, + 2.8893, + 2.8893, + 2.8893, + 2.8893, + 2.891, + 2.891, + 2.891, + 2.8892, + 2.8892, + 2.8892, + 2.8838, + 2.8838, + 2.887, + 2.8892, + 2.8892, + 2.8912, + 2.8912, + 2.892, + 2.9008, + 2.8993, + 2.8997, + 2.8909, + 2.8865, + 2.8865, + 2.8852, + 2.884, + 2.884, + 2.878, + 2.8747, + 2.871, + 2.8752, + 2.8742, + 2.88, + 2.88, + 2.8839, + 2.8793, + 2.8793, + 2.8729, + 2.8729, + 2.8716, + 2.8714, + 2.8681, + 2.8687, + 2.8748, + 2.8729, + 2.8802, + 2.8802, + 2.8802, + 2.8802, + 2.8802, + 2.8778, + 2.8778, + 2.8786, + 2.8786, + 2.8782, + 2.8782, + 2.8723, + 2.8699, + 2.8699, + 2.8656, + 2.8669, + 2.8688, + 2.8688, + 2.864, + 2.8638, + 2.8648, + 2.8683, + 2.8683, + 2.8697, + 2.8697, + 2.8687, + 2.8703, + 2.8767, + 2.867, + 2.8652, + 2.857, + 2.8614, + 2.8627, + 2.8652, + 2.8616, + 2.8453, + 2.8357, + 2.8481, + 2.8481, + 2.8482, + 2.8266, + 2.8272, + 2.83, + 2.8239, + 2.8239, + 2.8309, + 2.8307, + 2.8239, + 2.8225, + 2.8208, + 2.8171, + 2.8228, + 2.8228, + 2.8228, + 2.816, + 2.8202, + 2.8246, + 2.8274, + 2.8274, + 2.8253, + 2.8253, + 2.819, + 2.8, + 2.7926, + 2.8032, + 2.8003, + 2.7973, + 2.7972, + 2.793, + 2.7872, + 2.7819, + 2.789, + 2.7799, + 2.792, + 2.8009, + 2.7958, + 2.7983, + 2.7992, + 2.7992, + 2.7939, + 2.7941, + 2.795, + 2.7928, + 2.7804, + 2.7904, + 2.7838, + 2.7943, + 2.8038, + 2.8119, + 2.8127, + 2.8117, + 2.813, + 2.8154, + 2.8153, + 2.8153, + 2.8091, + 2.8111, + 2.8158, + 2.8158, + 2.8121, + 2.8117, + 2.7995, + 2.7972, + 2.8097, + 2.8112, + 2.781, + 2.7789, + 2.7817, + 2.7848, + 2.7879, + 2.8056, + 2.8134, + 2.8068, + 2.8056, + 2.8015, + 2.8, + 2.7962, + 2.7946, + 2.7902, + 2.798, + 2.7905, + 2.7912, + 2.795, + 2.7912, + 2.7799, + 2.7742, + 2.7617, + 2.7848, + 2.7925, + 2.79, + 2.7993, + 2.7956, + 2.791, + 2.7932, + 2.7914, + 2.7952, + 2.7952, + 2.7833, + 2.7736, + 2.7582, + 2.7672, + 2.7729, + 2.776, + 2.7827, + 2.7827, + 2.7778, + 2.7699, + 2.7745, + 2.7775, + 2.7775, + 2.785, + 2.7868, + 2.7904, + 2.7938, + 2.7858, + 2.7858, + 2.7746, + 2.7678, + 2.7538, + 2.7586, + 2.75, + 2.7656, + 2.749, + 2.755, + 2.748, + 2.735, + 2.739, + 2.739, + 2.7493, + 2.743, + 2.7072, + 2.695, + 2.7103, + 2.7153, + 2.7186, + 2.7265, + 2.7113, + 2.7107, + 2.7068, + 2.7056, + 2.6947, + 2.6977, + 2.7066, + 2.6974, + 2.6953, + 2.688, + 2.6809, + 2.6798, + 2.6783, + 2.6545, + 2.65, + 2.6414, + 2.6743, + 2.6686, + 2.6831, + 2.6911, + 2.7007, + 2.7118, + 2.7158, + 2.7237, + 2.7271, + 2.7329, + 2.7327, + 2.7403, + 2.7412, + 2.7376, + 2.74, + 2.7349, + 2.7327, + 2.7265, + 2.7325, + 2.7235, + 2.7218, + 2.728, + 2.7285, + 2.7263, + 2.7274, + 2.7274, + 2.7274, + 2.7341, + 2.7349, + 2.7349, + 2.7382, + 2.7389, + 2.7516, + 2.75, + 2.7562, + 2.7561, + 2.7558, + 2.7613, + 2.7636, + 2.7626, + 2.7659, + 2.7705, + 2.7698, + 2.7684, + 2.7642, + 2.7681, + 2.7643, + 2.77, + 2.76, + 2.7504, + 2.759, + 2.7633, + 2.7637, + 2.7656, + 2.7737, + 2.7775, + 2.7804, + 2.7804, + 2.7735, + 2.777, + 2.7946, + 2.7904, + 2.7903, + 2.7903, + 2.786, + 2.7928, + 2.7951, + 2.8002, + 2.7928, + 2.792, + 2.792, + 2.7938, + 2.7983, + 2.7986, + 2.795, + 2.7961, + 2.8002, + 2.7935, + 2.7844, + 2.7897, + 2.7822, + 2.776, + 2.7751, + 2.7717, + 2.7738, + 2.7705, + 2.7707, + 2.7731, + 2.773, + 2.7837, + 2.7864, + 2.784, + 2.7866, + 2.7818, + 2.7804, + 2.781, + 2.7888, + 2.7944, + 2.7945, + 2.7967, + 2.7942, + 2.795, + 2.7963, + 2.7943, + 2.7906, + 2.7861, + 2.7861, + 2.7932, + 2.7861, + 2.7851, + 2.7851, + 2.7918, + 2.7887, + 2.7899, + 2.7854, + 2.7854, + 2.7862, + 2.787, + 2.7882, + 2.7868, + 2.7885, + 2.7898, + 2.791, + 2.791, + 2.7892, + 2.7876, + 2.7767, + 2.7792, + 2.7792, + 2.7801, + 2.7834, + 2.7807, + 2.775, + 2.7757, + 2.7759, + 2.7846, + 2.776, + 2.7809, + 2.7742, + 2.7821, + 2.7805, + 2.7847, + 2.7847, + 2.7852, + 2.7791, + 2.7806, + 2.7874, + 2.7886, + 2.7945, + 2.7931, + 2.7905, + 2.7905, + 2.7905, + 2.7745, + 2.7775, + 2.7775, + 2.7767, + 2.7739, + 2.7774, + 2.7769, + 2.775, + 2.7731, + 2.7723, + 2.7762, + 2.7727, + 2.7601, + 2.7585, + 2.7611, + 2.7599, + 2.7492, + 2.7495, + 2.7494, + 2.7553, + 2.7531, + 2.7576, + 2.7576, + 2.7538, + 2.7538, + 2.7541, + 2.7557, + 2.7528, + 2.7552, + 2.7531, + 2.7558, + 2.7517, + 2.7557, + 2.7557, + 2.7557, + 2.757, + 2.7605, + 2.7633, + 2.7705, + 2.7714, + 2.7749, + 2.7749, + 2.7644, + 2.7644, + 2.7642, + 2.7642, + 2.764, + 2.764, + 2.764, + 2.7667, + 2.7675, + 2.7586, + 2.7571, + 2.7642, + 2.7594, + 2.7577, + 2.7543, + 2.752, + 2.7519, + 2.7509, + 2.7529, + 2.7564, + 2.7563, + 2.7571, + 2.7554, + 2.7559, + 2.7559, + 2.7559, + 2.7559, + 2.7559, + 2.7516, + 2.747, + 2.7513, + 2.7562, + 2.7562, + 2.7469, + 2.7417, + 2.7417, + 2.7447, + 2.7447, + 2.7515, + 2.7492, + 2.7488, + 2.7465, + 2.7465, + 2.7521, + 2.7554, + 2.7562, + 2.7611, + 2.7602, + 2.7673, + 2.7705, + 2.772, + 2.776, + 2.776, + 2.776, + 2.775, + 2.7715, + 2.7715, + 2.7745, + 2.7774, + 2.7763, + 2.776, + 2.7782, + 2.7782, + 2.777, + 2.7782, + 2.7782, + 2.7782, + 2.7698, + 2.7698, + 2.7741, + 2.784, + 2.7848, + 2.7868, + 2.7929, + 2.7884, + 2.7862, + 2.7853, + 2.7862, + 2.7907, + 2.7889, + 2.7861, + 2.7848, + 2.7848, + 2.7811, + 2.781, + 2.787, + 2.787, + 2.7831, + 2.7802, + 2.7721, + 2.7804, + 2.785, + 2.7908, + 2.7839, + 2.7801, + 2.7782, + 2.7791, + 2.7791, + 2.7781, + 2.773, + 2.7689, + 2.7698, + 2.7698, + 2.7716, + 2.7716, + 2.7716, + 2.7699, + 2.7656, + 2.761, + 2.765, + 2.7685, + 2.7692, + 2.7692, + 2.7569, + 2.7617, + 2.76, + 2.762, + 2.762, + 2.7593, + 2.7617, + 2.7754, + 2.7762, + 2.7773, + 2.7707, + 2.7709, + 2.7662, + 2.7662, + 2.7641, + 2.767, + 2.767, + 2.7665, + 2.7665, + 2.7614, + 2.765, + 2.7595, + 2.754, + 2.7548, + 2.7548, + 2.7488, + 2.7488, + 2.7448, + 2.7527, + 2.7474, + 2.7508, + 2.7508, + 2.7508, + 2.748, + 2.748, + 2.7626, + 2.7637, + 2.7572, + 2.7572, + 2.7484, + 2.7456, + 2.7473, + 2.7473, + 2.7474, + 2.7518, + 2.7572, + 2.7626, + 2.7656, + 2.7651, + 2.7658, + 2.7691, + 2.776, + 2.7806, + 2.7778, + 2.7729, + 2.7729, + 2.7729, + 2.7697, + 2.7717, + 2.7713, + 2.7719, + 2.7719, + 2.7719, + 2.7684, + 2.7735, + 2.7751, + 2.7741, + 2.777, + 2.7733, + 2.7719, + 2.7729, + 2.7729, + 2.7667, + 2.7656, + 2.7656, + 2.7742, + 2.7726, + 2.78, + 2.7836, + 2.7833, + 2.7833, + 2.7833, + 2.7792, + 2.7789, + 2.7892, + 2.7892, + 2.7914, + 2.7827, + 2.7828, + 2.783, + 2.783, + 2.7804, + 2.7765, + 2.7742, + 2.7771, + 2.776, + 2.7732, + 2.7771, + 2.7816, + 2.7904, + 2.7923, + 2.7929, + 2.7905, + 2.7862, + 2.7977, + 2.7966, + 2.7958, + 2.801, + 2.8087, + 2.8101, + 2.8176, + 2.8235, + 2.8237, + 2.8275, + 2.8265, + 2.8265, + 2.819, + 2.8174, + 2.8174, + 2.8174, + 2.815, + 2.8035, + 2.8071, + 2.8142, + 2.8112, + 2.8228, + 2.8228, + 2.82, + 2.8231, + 2.8247, + 2.8241, + 2.8262, + 2.8309, + 2.8299, + 2.8312, + 2.8307, + 2.8228, + 2.8168, + 2.8198, + 2.8198, + 2.8086, + 2.8083, + 2.8079, + 2.8038, + 2.8025, + 2.799, + 2.799, + 2.803, + 2.801, + 2.801, + 2.8038, + 2.8039, + 2.8067, + 2.8067, + 2.8056, + 2.804, + 2.8015, + 2.7991, + 2.7904, + 2.791, + 2.7971, + 2.808, + 2.8066, + 2.8066, + 2.801, + 2.8053, + 2.8082, + 2.8147, + 2.8123, + 2.8123, + 2.816, + 2.813, + 2.8, + 2.7975, + 2.8043, + 2.8008, + 2.7966, + 2.7966, + 2.8005, + 2.8003, + 2.8038, + 2.8034, + 2.8061, + 2.8061, + 2.81, + 2.8078, + 2.8058, + 2.7996, + 2.7996, + 2.8082, + 2.8057, + 2.8093, + 2.8066, + 2.8066, + 2.796, + 2.7917, + 2.7902, + 2.7923, + 2.7953, + 2.7953, + 2.795, + 2.795, + 2.7992, + 2.7989, + 2.8007, + 2.8037, + 2.8065, + 2.8101, + 2.8167, + 2.8218, + 2.8143, + 2.8113, + 2.8096, + 2.8113, + 2.8113, + 2.8091, + 2.8106, + 2.7975, + 2.8047, + 2.8135, + 2.82, + 2.8209, + 2.8156, + 2.8112, + 2.8097, + 2.8136, + 2.8237, + 2.8268, + 2.833, + 2.8283, + 2.8329, + 2.8301, + 2.8344, + 2.8344, + 2.8257, + 2.8257, + 2.8271, + 2.8486, + 2.8484, + 2.8486, + 2.8399, + 2.8399, + 2.8333, + 2.8273, + 2.8255, + 2.816, + 2.8141, + 2.8038, + 2.8183, + 2.8183, + 2.8183, + 2.8183, + 2.8182, + 2.8228, + 2.8243, + 2.8336, + 2.8368, + 2.8368, + 2.8364, + 2.8364, + 2.8307, + 2.8361, + 2.8361, + 2.8359, + 2.8359, + 2.8366, + 2.8366, + 2.834, + 2.8332, + 2.8336, + 2.8354, + 2.8521, + 2.8486, + 2.8404, + 2.8404, + 2.8409, + 2.8409, + 2.841, + 2.8385, + 2.8358, + 2.8282, + 2.8259, + 2.8228, + 2.8222, + 2.8249, + 2.8249, + 2.826, + 2.8271, + 2.8362, + 2.8349, + 2.8302, + 2.8319, + 2.8319, + 2.8254, + 2.8254, + 2.8254, + 2.824, + 2.82, + 2.8194, + 2.8142, + 2.8107, + 2.8098, + 2.81, + 2.8147, + 2.8147, + 2.8149, + 2.8177, + 2.824, + 2.824, + 2.8157, + 2.8183, + 2.8183, + 2.8262, + 2.8307, + 2.8255, + 2.8273, + 2.8198, + 2.8225, + 2.8247, + 2.8247, + 2.8171, + 2.8158, + 2.8227, + 2.8227, + 2.8271, + 2.8219, + 2.8191, + 2.8191, + 2.8159, + 2.8187, + 2.8187, + 2.8187, + 2.8187, + 2.8187, + 2.8279, + 2.8237, + 2.8237, + 2.8204, + 2.8147, + 2.8185, + 2.8142, + 2.8106, + 2.8102, + 2.8038, + 2.8057, + 2.8069, + 2.8069, + 2.805, + 2.805, + 2.811, + 2.8079, + 2.8067, + 2.8053, + 2.8085, + 2.8118, + 2.8151, + 2.8171, + 2.8137, + 2.8154, + 2.8154, + 2.8154, + 2.8141, + 2.8172, + 2.8172, + 2.8172, + 2.8141, + 2.8115, + 2.8117, + 2.8104, + 2.806, + 2.8124, + 2.8129, + 2.8129, + 2.8076, + 2.8076, + 2.8166, + 2.8138, + 2.8138, + 2.8117, + 2.8117, + 2.8166, + 2.817, + 2.817, + 2.8196, + 2.8236, + 2.8262, + 2.8291, + 2.8454, + 2.838, + 2.826, + 2.83, + 2.8255, + 2.8257, + 2.8257, + 2.8215, + 2.8251, + 2.815, + 2.8102, + 2.8085, + 2.8123, + 2.8098, + 2.8098, + 2.7983, + 2.7949, + 2.7924, + 2.7947, + 2.7966, + 2.7957, + 2.7957, + 2.7957, + 2.7851, + 2.777, + 2.7841, + 2.7797, + 2.7778, + 2.7809, + 2.7811, + 2.7814, + 2.7814, + 2.7814, + 2.7819, + 2.7796, + 2.7821, + 2.7799, + 2.7787, + 2.7777, + 2.7777, + 2.7809, + 2.7806, + 2.7806, + 2.7867, + 2.7782, + 2.781, + 2.781, + 2.7786, + 2.7774, + 2.7712, + 2.7729, + 2.7735, + 2.7781, + 2.7761, + 2.7792, + 2.7836, + 2.7846, + 2.7846, + 2.7855, + 2.7855, + 2.79, + 2.7916, + 2.791, + 2.791, + 2.791, + 2.7951, + 2.796, + 2.796, + 2.7975, + 2.798, + 2.8012, + 2.8032, + 2.8035, + 2.7956, + 2.795, + 2.7903, + 2.7866, + 2.7858, + 2.7865, + 2.7732, + 2.7802, + 2.7722, + 2.7722, + 2.7854, + 2.7874, + 2.7874, + 2.7855, + 2.7855, + 2.7826, + 2.7814, + 2.7814, + 2.7889, + 2.7904, + 2.7873, + 2.7906, + 2.7923, + 2.7944, + 2.7945, + 2.7992, + 2.7992, + 2.7972, + 2.7978, + 2.796, + 2.7937, + 2.795, + 2.7991, + 2.8075, + 2.8104, + 2.813, + 2.8078, + 2.8022, + 2.7952, + 2.7981, + 2.7981, + 2.7904, + 2.7885, + 2.7881, + 2.7881, + 2.7903, + 2.7903, + 2.7903, + 2.7877, + 2.7877, + 2.7874, + 2.7905, + 2.7905, + 2.7941, + 2.7941, + 2.7893, + 2.781, + 2.7778, + 2.7711, + 2.7796, + 2.769, + 2.7708, + 2.7731, + 2.7704, + 2.7682, + 2.7741, + 2.7745, + 2.7707, + 2.7707, + 2.7748, + 2.7762, + 2.7762, + 2.7836, + 2.7886, + 2.789, + 2.7889, + 2.787, + 2.7936, + 2.8007, + 2.8007, + 2.784, + 2.7819, + 2.7821, + 2.7821, + 2.786, + 2.7933, + 2.7975, + 2.7901, + 2.7894, + 2.7959, + 2.794, + 2.7892, + 2.786, + 2.7827, + 2.7827, + 2.7843, + 2.7831, + 2.7801, + 2.7801, + 2.7801, + 2.7801, + 2.7932, + 2.7894, + 2.7877, + 2.7862, + 2.7876, + 2.7812, + 2.7812, + 2.7777, + 2.776, + 2.7758, + 2.7758, + 2.7838, + 2.7817, + 2.7817, + 2.7841, + 2.7841, + 2.7841, + 2.7841, + 2.7741, + 2.7749, + 2.7746, + 2.7746, + 2.774, + 2.774, + 2.774, + 2.7739, + 2.7737, + 2.7761, + 2.7766, + 2.7801, + 2.7801, + 2.784, + 2.7884, + 2.7884, + 2.7884, + 2.7791, + 2.775, + 2.775, + 2.775, + 2.7722, + 2.7615, + 2.7615, + 2.7711, + 2.7725, + 2.7725, + 2.7725, + 2.7675, + 2.7614, + 2.7648, + 2.7573, + 2.7597, + 2.7608, + 2.7566, + 2.7585, + 2.7658, + 2.7659, + 2.7691, + 2.7691, + 2.7691, + 2.7659, + 2.7587, + 2.761, + 2.761, + 2.7625, + 2.7625, + 2.7625, + 2.7556, + 2.7566, + 2.7566, + 2.7566, + 2.7566, + 2.7678, + 2.7688, + 2.7695, + 2.7606, + 2.7531, + 2.7624, + 2.7538, + 2.7484, + 2.7533, + 2.7499, + 2.7343, + 2.7411, + 2.742, + 2.7452, + 2.741, + 2.7406, + 2.7406, + 2.74, + 2.7369, + 2.7299, + 2.7418, + 2.749, + 2.7559, + 2.7567, + 2.7605, + 2.7553, + 2.7553, + 2.7578, + 2.7618, + 2.7647, + 2.7653, + 2.7663, + 2.7707, + 2.7707, + 2.7759, + 2.7836, + 2.7845, + 2.7781, + 2.7779, + 2.7779, + 2.7779, + 2.7797, + 2.7797, + 2.7774, + 2.7774, + 2.7736, + 2.7736, + 2.7736, + 2.7751, + 2.7804, + 2.7804, + 2.7889, + 2.7855, + 2.778, + 2.7839, + 2.7769, + 2.7743, + 2.7769, + 2.7739, + 2.7807, + 2.7775, + 2.7748, + 2.7748, + 2.7722, + 2.7769, + 2.7769, + 2.7769, + 2.7638, + 2.7668, + 2.7693, + 2.773, + 2.768, + 2.768, + 2.7718, + 2.7738, + 2.7776, + 2.7762, + 2.7828, + 2.7791, + 2.7797, + 2.7848, + 2.7899, + 2.7927, + 2.7938, + 2.795, + 2.795, + 2.7952, + 2.7952, + 2.7961, + 2.7928, + 2.7932, + 2.7853, + 2.7806, + 2.7881, + 2.7922, + 2.7935, + 2.7891, + 2.7919, + 2.7886, + 2.794, + 2.7995, + 2.8066, + 2.8089, + 2.8118, + 2.8118, + 2.8037, + 2.8167, + 2.8207, + 2.8163, + 2.8159, + 2.8119, + 2.8164, + 2.825, + 2.8337, + 2.8301, + 2.83, + 2.8282, + 2.8228, + 2.821, + 2.8163, + 2.8099, + 2.8099, + 2.8099, + 2.8215, + 2.8059, + 2.7972, + 2.8009, + 2.8079, + 2.8047, + 2.8009, + 2.8009, + 2.8009, + 2.8009, + 2.8009, + 2.8134, + 2.8131, + 2.8118, + 2.8133, + 2.8191, + 2.817, + 2.8152, + 2.8129, + 2.8067, + 2.803, + 2.8024, + 2.8129, + 2.8128, + 2.8091, + 2.8091, + 2.8061, + 2.8139, + 2.8139, + 2.8139, + 2.8176, + 2.813, + 2.8107, + 2.805, + 2.8015, + 2.8062, + 2.8062, + 2.8062, + 2.8062, + 2.8095, + 2.8095, + 2.8183, + 2.8115, + 2.8115, + 2.8113, + 2.8095, + 2.804, + 2.802, + 2.7975, + 2.7984, + 2.7977, + 2.7988, + 2.7904, + 2.7954, + 2.7877, + 2.7873, + 2.7908, + 2.788, + 2.778, + 2.7779, + 2.7762, + 2.7798, + 2.7846, + 2.7771, + 2.7863, + 2.7904, + 2.7915, + 2.7902, + 2.7912, + 2.7968, + 2.7952, + 2.7952, + 2.7924, + 2.7924, + 2.7961, + 2.7973, + 2.7951, + 2.7907, + 2.7956, + 2.7965, + 2.7971, + 2.8012, + 2.8025, + 2.8116, + 2.8088, + 2.8085, + 2.8112, + 2.817, + 2.8169, + 2.8169, + 2.8145, + 2.8135, + 2.8142, + 2.8075, + 2.8046, + 2.8128, + 2.802, + 2.796, + 2.7898, + 2.7845, + 2.7918, + 2.7932, + 2.7932, + 2.7932, + 2.7932, + 2.7946, + 2.7822, + 2.7752, + 2.7752, + 2.7703, + 2.7831, + 2.7848, + 2.787, + 2.7872, + 2.7889, + 2.7904, + 2.7904, + 2.7909, + 2.7909, + 2.7929, + 2.7945, + 2.7945, + 2.7945, + 2.7884, + 2.7884, + 2.7884, + 2.7942, + 2.7992, + 2.7971, + 2.8079, + 2.8087, + 2.8087, + 2.8142, + 2.8142, + 2.8154, + 2.8078, + 2.8078, + 2.8155, + 2.8118, + 2.8152, + 2.8138, + 2.8055, + 2.804, + 2.8023, + 2.796, + 2.796, + 2.794, + 2.794, + 2.7893, + 2.787, + 2.7816, + 2.7799, + 2.7799, + 2.7799, + 2.7786, + 2.7786, + 2.7848, + 2.7809, + 2.7809, + 2.7815, + 2.7847, + 2.786, + 2.788, + 2.7845, + 2.7839, + 2.7864, + 2.7836, + 2.7847, + 2.7868, + 2.7879, + 2.7975, + 2.7992, + 2.7992, + 2.7992, + 2.8006, + 2.8021, + 2.8009, + 2.7989, + 2.7976, + 2.7976, + 2.7976, + 2.8066, + 2.8092, + 2.813, + 2.816, + 2.816, + 2.817, + 2.8189, + 2.8189, + 2.8189, + 2.8218, + 2.8218, + 2.8158, + 2.8178, + 2.8114, + 2.8012, + 2.8036, + 2.7917, + 2.7996, + 2.8076, + 2.8076, + 2.8076, + 2.7945, + 2.7945, + 2.7963, + 2.7963, + 2.7963, + 2.7963, + 2.8079, + 2.8071, + 2.816, + 2.82, + 2.82, + 2.8207, + 2.8288, + 2.8262, + 2.822, + 2.8224, + 2.8224, + 2.8241, + 2.8252, + 2.8252, + 2.8252, + 2.8252, + 2.8252, + 2.8293, + 2.8393, + 2.8462, + 2.8376, + 2.8356, + 2.8414, + 2.8414, + 2.8359, + 2.8413, + 2.8362, + 2.8362, + 2.8362, + 2.8362, + 2.8362, + 2.8321, + 2.8321, + 2.8391, + 2.8391, + 2.8391, + 2.8391, + 2.8391, + 2.8262, + 2.8262, + 2.8262, + 2.8319, + 2.8319, + 2.8319, + 2.8319, + 2.8289, + 2.8289, + 2.8355, + 2.8355, + 2.8323, + 2.8245, + 2.8278, + 2.829, + 2.8228, + 2.8228, + 2.8228, + 2.8228, + 2.8211, + 2.8211, + 2.8219, + 2.8183, + 2.8177, + 2.8147, + 2.8144, + 2.8117, + 2.8087, + 2.8066, + 2.8046, + 2.8015, + 2.7991, + 2.8028, + 2.8028, + 2.8071, + 2.8143, + 2.8183, + 2.8172, + 2.8172, + 2.8172, + 2.8172, + 2.8195, + 2.8228, + 2.822, + 2.8245, + 2.8245, + 2.8271, + 2.8259, + 2.8263, + 2.8263, + 2.8142, + 2.802, + 2.7979, + 2.7979, + 2.8114, + 2.8114, + 2.8114, + 2.7979, + 2.7993, + 2.8089, + 2.8051, + 2.8051, + 2.8051, + 2.8051, + 2.8094, + 2.8094, + 2.8207, + 2.8217, + 2.819, + 2.819, + 2.8259, + 2.8222, + 2.819, + 2.8216, + 2.8183, + 2.8238, + 2.8217, + 2.8217, + 2.8136, + 2.8083, + 2.8065, + 2.8031, + 2.8052, + 2.804, + 2.7949, + 2.8052, + 2.8052, + 2.8026, + 2.8017, + 2.7982, + 2.7963, + 2.7933, + 2.7961, + 2.796, + 2.7977, + 2.7963, + 2.7967, + 2.7943, + 2.7943, + 2.7943, + 2.793, + 2.7958, + 2.8022, + 2.8015, + 2.801, + 2.7971, + 2.7971, + 2.8046, + 2.8046, + 2.8046, + 2.8086, + 2.8102, + 2.8105, + 2.8154, + 2.816, + 2.8159, + 2.8062, + 2.8057, + 2.8012, + 2.7966, + 2.8005, + 2.7987, + 2.7965, + 2.8084, + 2.8091, + 2.8097, + 2.8143, + 2.8091, + 2.8125, + 2.8093, + 2.8086, + 2.8048, + 2.8104, + 2.8186, + 2.8149, + 2.8144, + 2.8119, + 2.8074, + 2.8078, + 2.8097, + 2.8098, + 2.8081, + 2.8061, + 2.8098, + 2.81, + 2.81, + 2.81, + 2.81, + 2.81, + 2.8075, + 2.8032, + 2.8057, + 2.8012, + 2.805, + 2.8058, + 2.8058, + 2.8058, + 2.8047, + 2.8067, + 2.8067, + 2.8055, + 2.8047, + 2.8047, + 2.8047, + 2.8047, + 2.8047, + 2.8053, + 2.8051, + 2.8081, + 2.8081, + 2.8081, + 2.8081, + 2.8081, + 2.8069, + 2.8059, + 2.8059, + 2.808, + 2.8142, + 2.8127, + 2.8083, + 2.8171, + 2.8151, + 2.8209, + 2.8058, + 2.8058, + 2.8058, + 2.8058, + 2.8058, + 2.812, + 2.816, + 2.8126, + 2.8067, + 2.8142, + 2.8109, + 2.8109, + 2.8093, + 2.8056, + 2.8119, + 2.8143, + 2.8187, + 2.8252, + 2.83, + 2.8336, + 2.8302, + 2.8302, + 2.8281, + 2.8304, + 2.8285, + 2.8254, + 2.82, + 2.8224, + 2.8183, + 2.8165, + 2.8181, + 2.8214, + 2.8102, + 2.8042, + 2.8012, + 2.79, + 2.7875, + 2.7776, + 2.7807, + 2.7778, + 2.7778, + 2.776, + 2.7589, + 2.7668, + 2.7687, + 2.7615, + 2.7614, + 2.7541, + 2.7575, + 2.7596, + 2.7574, + 2.7611, + 2.7653, + 2.7587, + 2.7614, + 2.7569, + 2.7649, + 2.763, + 2.763, + 2.7704, + 2.7689, + 2.7704, + 2.7556, + 2.7549, + 2.7549, + 2.7476, + 2.7439, + 2.7605, + 2.7546, + 2.76, + 2.761, + 2.7553, + 2.7417, + 2.7429, + 2.7505, + 2.7586, + 2.7551, + 2.7561, + 2.7561, + 2.7561, + 2.7568, + 2.7554, + 2.743, + 2.736, + 2.7262, + 2.7332, + 2.7282, + 2.7414, + 2.7325, + 2.7353, + 2.7353, + 2.7353, + 2.7321, + 2.7302, + 2.7397, + 2.7397, + 2.7444, + 2.749, + 2.7438, + 2.7438, + 2.7488, + 2.7488, + 2.742, + 2.7342, + 2.727, + 2.727, + 2.7367, + 2.7367, + 2.7295, + 2.7295, + 2.73, + 2.73, + 2.7418, + 2.7433, + 2.7481, + 2.7481, + 2.7481, + 2.7481, + 2.7481, + 2.7481, + 2.7484, + 2.7505, + 2.7502, + 2.7472, + 2.752, + 2.7523, + 2.7538, + 2.7486, + 2.7486, + 2.7536, + 2.7536, + 2.7536, + 2.7562, + 2.7568, + 2.7551, + 2.7561, + 2.755, + 2.755, + 2.7528, + 2.7528, + 2.7528, + 2.7528, + 2.7528, + 2.747, + 2.7502, + 2.75, + 2.75, + 2.7507, + 2.761, + 2.7578, + 2.7575, + 2.755, + 2.7558, + 2.7569, + 2.7562, + 2.7562, + 2.7544, + 2.75, + 2.7516, + 2.7527, + 2.7496, + 2.7521, + 2.7474, + 2.7474, + 2.7546, + 2.7558, + 2.7518, + 2.7505, + 2.7505, + 2.7523, + 2.7528, + 2.7545, + 2.7503, + 2.7503, + 2.7503, + 2.7503, + 2.7593, + 2.7559, + 2.7541, + 2.7503, + 2.7506, + 2.7498, + 2.7438, + 2.7459, + 2.7457, + 2.7468, + 2.7479, + 2.7461, + 2.748, + 2.7468, + 2.7466, + 2.748, + 2.7431, + 2.7467, + 2.7457, + 2.748, + 2.7591, + 2.7629, + 2.7626, + 2.759, + 2.759, + 2.7602, + 2.7634, + 2.7656, + 2.7627, + 2.7627, + 2.7624, + 2.7582, + 2.7582, + 2.7626, + 2.7629, + 2.7585, + 2.7585, + 2.7585, + 2.7547, + 2.7547, + 2.746, + 2.7473, + 2.7473, + 2.7494, + 2.75, + 2.7456, + 2.7427, + 2.7427, + 2.7403, + 2.7374, + 2.7388, + 2.7397, + 2.7401, + 2.7401, + 2.7401, + 2.7317, + 2.735, + 2.7327, + 2.7235, + 2.7252, + 2.7174, + 2.7262, + 2.7194, + 2.708, + 2.7107, + 2.7168, + 2.7179, + 2.724, + 2.7279, + 2.7282, + 2.7206, + 2.7226, + 2.7289, + 2.729, + 2.7293, + 2.7248, + 2.7222, + 2.7222, + 2.7221, + 2.7279, + 2.7304, + 2.7304, + 2.731, + 2.7377, + 2.7377, + 2.7417, + 2.7456, + 2.7503, + 2.7494, + 2.7454, + 2.7467, + 2.746, + 2.7457, + 2.747, + 2.7494, + 2.7531, + 2.7521, + 2.7517, + 2.7508, + 2.7489, + 2.7437, + 2.7493, + 2.7458, + 2.7438, + 2.7435, + 2.7427, + 2.74, + 2.7403, + 2.7403, + 2.741, + 2.7382, + 2.742, + 2.742, + 2.7446, + 2.7442, + 2.744, + 2.7436, + 2.751, + 2.757, + 2.757, + 2.7482, + 2.7482, + 2.7482, + 2.747, + 2.7469, + 2.747, + 2.7582, + 2.759, + 2.7613, + 2.7603, + 2.7583, + 2.7583, + 2.7546, + 2.7565, + 2.7671, + 2.7631, + 2.7665, + 2.77, + 2.7752, + 2.7767, + 2.7817, + 2.786, + 2.7872, + 2.7835, + 2.7858, + 2.7947, + 2.7965, + 2.7804, + 2.7869, + 2.7776, + 2.7751, + 2.781, + 2.781, + 2.781, + 2.7819, + 2.7899, + 2.7875, + 2.7875, + 2.78, + 2.7833, + 2.7833, + 2.7807, + 2.7741, + 2.7773, + 2.7773, + 2.7735, + 2.7722, + 2.7722, + 2.772, + 2.772, + 2.7699, + 2.7727, + 2.7707, + 2.7707, + 2.7726, + 2.7679, + 2.7684, + 2.766, + 2.7727, + 2.7783, + 2.7801, + 2.7801, + 2.7794, + 2.784, + 2.784, + 2.7748, + 2.7693, + 2.7543, + 2.7622, + 2.7609, + 2.7459, + 2.7486, + 2.7579, + 2.7532, + 2.7532, + 2.7532, + 2.7536, + 2.7486, + 2.7553, + 2.7556, + 2.7643, + 2.7593, + 2.7596, + 2.7622, + 2.7637, + 2.7631, + 2.7631, + 2.7579, + 2.7519, + 2.7586, + 2.7586, + 2.762, + 2.7636, + 2.7636, + 2.7636, + 2.7646, + 2.761, + 2.7582, + 2.7576, + 2.7524, + 2.7498, + 2.7468, + 2.7456, + 2.7458, + 2.7424, + 2.7472, + 2.7472, + 2.7465, + 2.7465, + 2.7465, + 2.755, + 2.7561, + 2.7571, + 2.7584, + 2.7566, + 2.7571, + 2.7557, + 2.7558, + 2.7564, + 2.7527, + 2.7527, + 2.7598, + 2.7626, + 2.7626, + 2.7626, + 2.7626, + 2.7547, + 2.7547, + 2.7537, + 2.7504, + 2.7504, + 2.7475, + 2.7458, + 2.7447, + 2.742, + 2.742, + 2.7363, + 2.7418, + 2.7324, + 2.7371, + 2.725, + 2.7294, + 2.7198, + 2.725, + 2.7257, + 2.73, + 2.7315, + 2.7364, + 2.7341, + 2.7341, + 2.7257, + 2.7266, + 2.7102, + 2.72, + 2.72, + 2.7265, + 2.7262, + 2.7218, + 2.7218, + 2.7245, + 2.716, + 2.7113, + 2.7022, + 2.7116, + 2.7168, + 2.7229, + 2.7147, + 2.7202, + 2.7305, + 2.729, + 2.7352, + 2.7352, + 2.7272, + 2.7289, + 2.7312, + 2.7291, + 2.7291, + 2.7291, + 2.7311, + 2.7317, + 2.7286, + 2.7305, + 2.729, + 2.7276, + 2.7276, + 2.7293, + 2.7343, + 2.7332, + 2.7332, + 2.7332, + 2.729, + 2.7294, + 2.7286, + 2.7299, + 2.7286, + 2.7267, + 2.7267, + 2.724, + 2.7221, + 2.7225, + 2.7206, + 2.7295, + 2.7244, + 2.7238, + 2.7217, + 2.7247, + 2.71, + 2.7189, + 2.714, + 2.7066, + 2.7084, + 2.7095, + 2.7095, + 2.7095, + 2.712, + 2.7131, + 2.7138, + 2.7159, + 2.7121, + 2.7121, + 2.6946, + 2.6869, + 2.693, + 2.678, + 2.6731, + 2.6793, + 2.6791, + 2.6781, + 2.6714, + 2.6756, + 2.6826, + 2.6814, + 2.6827, + 2.677, + 2.674, + 2.6787, + 2.6709, + 2.6757, + 2.6781, + 2.6839, + 2.6821, + 2.6923, + 2.6929, + 2.6852, + 2.6852, + 2.6917, + 2.6915, + 2.6959, + 2.6992, + 2.7007, + 2.6994, + 2.6994, + 2.6942, + 2.6969, + 2.6969, + 2.6969, + 2.6969, + 2.7015, + 2.7032, + 2.7032, + 2.7028, + 2.7028, + 2.697, + 2.6956, + 2.6956, + 2.6956, + 2.6956, + 2.7005, + 2.7089, + 2.7089, + 2.7089, + 2.7068, + 2.7068, + 2.7124, + 2.709, + 2.7115, + 2.712, + 2.7142, + 2.7176, + 2.715, + 2.7159, + 2.72, + 2.7253, + 2.7253, + 2.7256, + 2.725, + 2.7268, + 2.7182, + 2.7182, + 2.7246, + 2.7246, + 2.7219, + 2.7219, + 2.7254, + 2.7254, + 2.728, + 2.7274, + 2.7274, + 2.733, + 2.7332, + 2.7334, + 2.7341, + 2.7341, + 2.7341, + 2.7319, + 2.7363, + 2.7404, + 2.7402, + 2.735, + 2.735, + 2.7376, + 2.7376, + 2.7391, + 2.7361, + 2.7363, + 2.7363, + 2.7377, + 2.7393, + 2.7393, + 2.7393, + 2.7409, + 2.7409, + 2.735, + 2.735, + 2.735, + 2.735, + 2.735, + 2.735, + 2.739, + 2.743, + 2.7394, + 2.7468, + 2.7449, + 2.7449, + 2.7438, + 2.7438, + 2.75, + 2.7481, + 2.755, + 2.7536, + 2.7531, + 2.7517, + 2.7546, + 2.753, + 2.753, + 2.7582, + 2.7533, + 2.7529, + 2.7529, + 2.7529, + 2.7434, + 2.7433, + 2.744, + 2.747, + 2.747, + 2.747, + 2.7424, + 2.7424, + 2.7389, + 2.74, + 2.74, + 2.74, + 2.74, + 2.7418, + 2.7404, + 2.7404, + 2.7404, + 2.7404, + 2.744, + 2.744, + 2.7438, + 2.7438, + 2.7433, + 2.7397, + 2.7397, + 2.7397, + 2.7395, + 2.7346, + 2.7346, + 2.7322, + 2.7322, + 2.7396, + 2.7386, + 2.7386, + 2.7382, + 2.7373, + 2.7323, + 2.7344, + 2.7339, + 2.7339, + 2.7339, + 2.747, + 2.7489, + 2.7489, + 2.7479, + 2.7479, + 2.7543, + 2.7497, + 2.7497, + 2.7497, + 2.7497, + 2.7531, + 2.7541, + 2.7538, + 2.7558, + 2.7525, + 2.7552, + 2.7553, + 2.7558, + 2.7558, + 2.7535, + 2.7533, + 2.7533, + 2.7527, + 2.7533, + 2.7499, + 2.7499, + 2.7499, + 2.7534, + 2.7534, + 2.7534, + 2.7534, + 2.7534, + 2.7605, + 2.763, + 2.7652, + 2.7647, + 2.7674, + 2.7655, + 2.7642, + 2.7642, + 2.7549, + 2.7549, + 2.7534, + 2.752, + 2.7508, + 2.7508, + 2.756, + 2.756, + 2.756, + 2.7544, + 2.7544, + 2.755, + 2.755, + 2.755, + 2.755, + 2.755, + 2.755, + 2.755, + 2.755, + 2.7516, + 2.7465, + 2.7442, + 2.7404, + 2.7404, + 2.7404, + 2.7404, + 2.7404, + 2.7397, + 2.7397, + 2.741, + 2.741, + 2.7398, + 2.7383, + 2.7381, + 2.7414, + 2.735, + 2.7389, + 2.7389, + 2.7389, + 2.7389, + 2.7389, + 2.7389, + 2.735, + 2.735, + 2.7364, + 2.7296, + 2.7335, + 2.7335, + 2.7335, + 2.7335, + 2.7381, + 2.7381, + 2.7381, + 2.7428, + 2.7428, + 2.7428, + 2.7401, + 2.7401, + 2.7401, + 2.7401, + 2.7411, + 2.7411, + 2.7411, + 2.7338, + 2.7348, + 2.7348, + 2.7348, + 2.7338, + 2.7329, + 2.7317, + 2.7306, + 2.732, + 2.7313, + 2.7313, + 2.725, + 2.727, + 2.727, + 2.7253, + 2.7253, + 2.7235, + 2.7184, + 2.7167, + 2.7159, + 2.7159, + 2.7124, + 2.7079, + 2.7099, + 2.7127, + 2.7127, + 2.7065, + 2.705, + 2.7035, + 2.7035, + 2.7035, + 2.7081, + 2.7081, + 2.7081, + 2.7078, + 2.7087, + 2.7093, + 2.7103, + 2.7131, + 2.7132, + 2.71, + 2.7057, + 2.701, + 2.701, + 2.6987, + 2.6977, + 2.6894, + 2.6956, + 2.6792, + 2.6799, + 2.6799, + 2.68, + 2.6768, + 2.6768, + 2.6768, + 2.6801, + 2.6784, + 2.6568, + 2.657, + 2.6542, + 2.6472, + 2.6537, + 2.652, + 2.6505, + 2.651, + 2.6552, + 2.6577, + 2.6653, + 2.6653, + 2.6623, + 2.6604, + 2.652, + 2.6256, + 2.6315, + 2.6348, + 2.6206, + 2.6107, + 2.595, + 2.6209, + 2.63, + 2.632, + 2.6329, + 2.6414, + 2.6457, + 2.6479, + 2.6497, + 2.65, + 2.6435, + 2.6435, + 2.6513, + 2.6513, + 2.6518, + 2.6398, + 2.642, + 2.639, + 2.6368, + 2.6433, + 2.6328, + 2.6214, + 2.626, + 2.6239, + 2.6328, + 2.6456, + 2.6506, + 2.6492, + 2.655, + 2.662, + 2.6575, + 2.6575, + 2.6575, + 2.6575, + 2.6462, + 2.651, + 2.651, + 2.6446, + 2.6444, + 2.6351, + 2.6356, + 2.6304, + 2.6271, + 2.6176, + 2.6222, + 2.6222, + 2.6196, + 2.6086, + 2.6051, + 2.6144, + 2.6142, + 2.6047, + 2.6046, + 2.5967, + 2.5998, + 2.6047, + 2.6038, + 2.5987, + 2.605, + 2.6062, + 2.6065, + 2.6165, + 2.6124, + 2.5997, + 2.5941, + 2.6021, + 2.6046, + 2.6008, + 2.6086, + 2.6141, + 2.6193, + 2.6161, + 2.612, + 2.6158, + 2.6205, + 2.6164, + 2.6252, + 2.6271, + 2.6218, + 2.6225, + 2.6272, + 2.6304, + 2.6304, + 2.6332, + 2.6319, + 2.6356, + 2.6448, + 2.6447, + 2.6479, + 2.6571, + 2.6514, + 2.6506, + 2.6454, + 2.6345, + 2.6252, + 2.6115, + 2.6192, + 2.6192, + 2.6163, + 2.6102, + 2.596, + 2.6015, + 2.6081, + 2.6176, + 2.63, + 2.6206, + 2.6206, + 2.6206, + 2.6251, + 2.6251, + 2.6255, + 2.6311, + 2.6283, + 2.6199, + 2.6279, + 2.6152, + 2.6113, + 2.6046, + 2.6105, + 2.5983, + 2.6095, + 2.6192, + 2.6205, + 2.6318, + 2.6358, + 2.6336, + 2.6336, + 2.6273, + 2.6238, + 2.6159, + 2.6192, + 2.6111, + 2.6108, + 2.6013, + 2.6041, + 2.6069, + 2.596, + 2.5891, + 2.5951, + 2.5852, + 2.5989, + 2.5838, + 2.5758, + 2.5643, + 2.5794, + 2.589, + 2.5987, + 2.5968, + 2.5964, + 2.608, + 2.6082, + 2.6162, + 2.6071, + 2.611, + 2.615, + 2.6177, + 2.6108, + 2.6156, + 2.6156, + 2.6071, + 2.598, + 2.5917, + 2.5712, + 2.5839, + 2.5872, + 2.5918, + 2.5918, + 2.5936, + 2.5953, + 2.6, + 2.6, + 2.5897, + 2.5859, + 2.5886, + 2.5938, + 2.5968, + 2.608, + 2.587, + 2.5818, + 2.5858, + 2.5879, + 2.5792, + 2.5786, + 2.5855, + 2.5748, + 2.5856, + 2.57, + 2.5472, + 2.5531, + 2.5544, + 2.5579, + 2.5579, + 2.5675, + 2.5675, + 2.5581, + 2.56, + 2.5707, + 2.5697, + 2.5718, + 2.5738, + 2.5738, + 2.5732, + 2.5627, + 2.5606, + 2.5571, + 2.5629, + 2.5625, + 2.5804, + 2.5836, + 2.5817, + 2.5787, + 2.5879, + 2.5947, + 2.5928, + 2.5829, + 2.5784, + 2.5718, + 2.5637, + 2.5629, + 2.5551, + 2.5372, + 2.5358, + 2.5441, + 2.5461, + 2.5568, + 2.5391, + 2.5474, + 2.5474, + 2.5403, + 2.5309, + 2.5217, + 2.5294, + 2.5216, + 2.5206, + 2.5147, + 2.5251, + 2.5089, + 2.5216, + 2.5177, + 2.5167, + 2.5082, + 2.498, + 2.5156, + 2.5123, + 2.5188, + 2.5135, + 2.5112, + 2.5115, + 2.5091, + 2.5107, + 2.4887, + 2.4974, + 2.508, + 2.5114, + 2.5082, + 2.4826, + 2.4774, + 2.4605, + 2.4725, + 2.4821, + 2.4851, + 2.4871, + 2.4907, + 2.5017, + 2.4926, + 2.5152, + 2.5102, + 2.5089, + 2.5085, + 2.5106, + 2.5137, + 2.5137, + 2.509, + 2.4915, + 2.5016, + 2.4937, + 2.4916, + 2.4863, + 2.4852, + 2.464, + 2.4647, + 2.4524, + 2.4732, + 2.4633, + 2.4608, + 2.4621, + 2.4436, + 2.4477, + 2.435, + 2.4201, + 2.4174, + 2.4261, + 2.4222, + 2.4287, + 2.4385, + 2.4531, + 2.4547, + 2.4547, + 2.4574, + 2.4559, + 2.4686, + 2.4571, + 2.4614, + 2.4734, + 2.4851, + 2.4709, + 2.4757, + 2.4851, + 2.4794, + 2.4804, + 2.4917, + 2.4903, + 2.497, + 2.5008, + 2.4985, + 2.5017, + 2.5033, + 2.4988, + 2.4981, + 2.502, + 2.502, + 2.502, + 2.4967, + 2.4911, + 2.488, + 2.4856, + 2.4752, + 2.4894, + 2.4894, + 2.4998, + 2.5011, + 2.5114, + 2.5118, + 2.5036, + 2.5054, + 2.5049, + 2.5061, + 2.5074, + 2.5106, + 2.5122, + 2.5071, + 2.5104, + 2.5146, + 2.5144, + 2.5144, + 2.5076, + 2.5083, + 2.5071, + 2.5084, + 2.5157, + 2.5241, + 2.5188, + 2.528, + 2.5267, + 2.5297, + 2.5294, + 2.529, + 2.5301, + 2.5293, + 2.5356, + 2.5388, + 2.5388, + 2.5423, + 2.5423, + 2.5449, + 2.5456, + 2.5412, + 2.5459, + 2.5493, + 2.5497, + 2.5516, + 2.5482, + 2.5489, + 2.5489, + 2.5419, + 2.5406, + 2.5369, + 2.5379, + 2.5418, + 2.5402, + 2.5402, + 2.542, + 2.542, + 2.5497, + 2.5475, + 2.5392, + 2.5397, + 2.5365, + 2.5364, + 2.5364, + 2.5422, + 2.5395, + 2.5395, + 2.5395, + 2.5444, + 2.5444, + 2.5444, + 2.539, + 2.539, + 2.5432, + 2.5474, + 2.5474, + 2.552, + 2.5546, + 2.5546, + 2.5438, + 2.5431, + 2.539, + 2.539, + 2.5414, + 2.5351, + 2.5409, + 2.5327, + 2.528, + 2.5302, + 2.5343, + 2.5343, + 2.5334, + 2.5334, + 2.5343, + 2.5351, + 2.5441, + 2.5422, + 2.5422, + 2.5406, + 2.5404, + 2.5406, + 2.5406, + 2.5479, + 2.5498, + 2.5564, + 2.556, + 2.556, + 2.5502, + 2.5511, + 2.5518, + 2.5473, + 2.5473, + 2.5506, + 2.5517, + 2.5531, + 2.5531, + 2.5514, + 2.5497, + 2.5473, + 2.5419, + 2.5409, + 2.5418, + 2.5374, + 2.5399, + 2.5419, + 2.5419, + 2.5426, + 2.5534, + 2.5534, + 2.5478, + 2.5478, + 2.5478, + 2.5491, + 2.553, + 2.5562, + 2.5562, + 2.5579, + 2.5553, + 2.5548, + 2.5775, + 2.5918, + 2.5981, + 2.5868, + 2.5998, + 2.5934, + 2.598, + 2.6118, + 2.6162, + 2.6119, + 2.6148, + 2.6114, + 2.6051, + 2.6031, + 2.5935, + 2.5961, + 2.5924, + 2.5924, + 2.5911, + 2.5861, + 2.5861, + 2.5881, + 2.5898, + 2.5982, + 2.5826, + 2.5806, + 2.5884, + 2.5817, + 2.5804, + 2.5703, + 2.5709, + 2.5682, + 2.5712, + 2.5708, + 2.5746, + 2.5705, + 2.575, + 2.575, + 2.5785, + 2.5776, + 2.5799, + 2.5777, + 2.5804, + 2.5886, + 2.5887, + 2.592, + 2.5922, + 2.5911, + 2.5935, + 2.5947, + 2.5958, + 2.5944, + 2.594, + 2.597, + 2.6011, + 2.6079, + 2.6138, + 2.6157, + 2.6088, + 2.604, + 2.6008, + 2.5991, + 2.6026, + 2.605, + 2.5948, + 2.5826, + 2.5856, + 2.5871, + 2.587, + 2.5903, + 2.5903, + 2.5823, + 2.5823, + 2.577, + 2.5767, + 2.5708, + 2.5665, + 2.5629, + 2.5629, + 2.5731, + 2.5755, + 2.5742, + 2.5752, + 2.5785, + 2.5804, + 2.5804, + 2.5747, + 2.5723, + 2.5782, + 2.5806, + 2.5821, + 2.587, + 2.587, + 2.5793, + 2.5774, + 2.5735, + 2.5735, + 2.5776, + 2.5785, + 2.5785, + 2.5785, + 2.589, + 2.5861, + 2.5861, + 2.5852, + 2.5852, + 2.5806, + 2.583, + 2.5892, + 2.5916, + 2.599, + 2.6048, + 2.5963, + 2.5987, + 2.5981, + 2.6041, + 2.6086, + 2.6046, + 2.6052, + 2.6052, + 2.6193, + 2.6093, + 2.6083, + 2.6102, + 2.6123, + 2.6104, + 2.598, + 2.5984, + 2.5984, + 2.6046, + 2.6045, + 2.6012, + 2.6004, + 2.5999, + 2.5935, + 2.5954, + 2.5903, + 2.5929, + 2.5887, + 2.5861, + 2.585, + 2.5833, + 2.5784, + 2.5795, + 2.5822, + 2.583, + 2.5826, + 2.5838, + 2.5838, + 2.5867, + 2.5825, + 2.5822, + 2.5826, + 2.5816, + 2.5767, + 2.5677, + 2.5696, + 2.5671, + 2.5721, + 2.5804, + 2.5781, + 2.5839, + 2.5839, + 2.5773, + 2.575, + 2.5712, + 2.5712, + 2.5712, + 2.5743, + 2.5743, + 2.5841, + 2.5841, + 2.5841, + 2.5822, + 2.5872, + 2.5885, + 2.5885, + 2.5887, + 2.5896, + 2.5886, + 2.5887, + 2.584, + 2.584, + 2.5827, + 2.5827, + 2.5827, + 2.5925, + 2.5911, + 2.5964, + 2.594, + 2.5849, + 2.5798, + 2.5798, + 2.5798, + 2.5798, + 2.5735, + 2.5726, + 2.5791, + 2.5791, + 2.586, + 2.591, + 2.5844, + 2.5844, + 2.5844, + 2.5792, + 2.5793, + 2.5755, + 2.5755, + 2.5755, + 2.5787, + 2.5614, + 2.5636, + 2.5576, + 2.5526, + 2.5496, + 2.5595, + 2.5605, + 2.5624, + 2.5546, + 2.5546, + 2.5546, + 2.5546, + 2.5601, + 2.5592, + 2.5502, + 2.5534, + 2.5517, + 2.5607, + 2.5614, + 2.5568, + 2.55, + 2.5541, + 2.5541, + 2.5541, + 2.5664, + 2.5697, + 2.5728, + 2.5772, + 2.5762, + 2.5761, + 2.5845, + 2.5833, + 2.5787, + 2.5834, + 2.5834, + 2.5797, + 2.5872, + 2.5878, + 2.5852, + 2.5903, + 2.5903, + 2.5903, + 2.5828, + 2.5871, + 2.5872, + 2.5817, + 2.5798, + 2.5798, + 2.5843, + 2.5821, + 2.58, + 2.584, + 2.5784, + 2.5727, + 2.5724, + 2.5733, + 2.575, + 2.5774, + 2.5796, + 2.5775, + 2.5746, + 2.5785, + 2.5845, + 2.5891, + 2.5806, + 2.5811, + 2.5908, + 2.5952, + 2.5946, + 2.5911, + 2.5987, + 2.5987, + 2.6043, + 2.6067, + 2.6062, + 2.6093, + 2.608, + 2.6047, + 2.6063, + 2.603, + 2.6056, + 2.6089, + 2.6077, + 2.6054, + 2.6, + 2.5934, + 2.5948, + 2.5858, + 2.591, + 2.591, + 2.589, + 2.5876, + 2.591, + 2.5879, + 2.5865, + 2.5894, + 2.59, + 2.5964, + 2.5982, + 2.6059, + 2.6066, + 2.6037, + 2.5988, + 2.5991, + 2.5989, + 2.5981, + 2.6045, + 2.6045, + 2.5989, + 2.5989, + 2.598, + 2.5929, + 2.5915, + 2.5918, + 2.6012, + 2.6024, + 2.6129, + 2.6181, + 2.6172, + 2.612, + 2.6124, + 2.6131, + 2.6046, + 2.608, + 2.6287, + 2.6223, + 2.6282, + 2.6281, + 2.6229, + 2.6422, + 2.6533, + 2.6565, + 2.6591, + 2.6596, + 2.6544, + 2.6485, + 2.6701, + 2.68, + 2.6766, + 2.677, + 2.6752, + 2.6837, + 2.6932, + 2.6862, + 2.6994, + 2.683, + 2.6813, + 2.6734, + 2.67, + 2.6608, + 2.6584, + 2.656, + 2.6598, + 2.6594, + 2.6685, + 2.6766, + 2.6793, + 2.6672, + 2.6541, + 2.6736, + 2.6904, + 2.6765, + 2.6797, + 2.6765, + 2.68, + 2.68, + 2.6752, + 2.6789, + 2.6829, + 2.6832, + 2.688, + 2.6862, + 2.6927, + 2.6906, + 2.6862, + 2.6906, + 2.686, + 2.6765, + 2.6782, + 2.6782, + 2.6832, + 2.6881, + 2.6861, + 2.6938, + 2.6938, + 2.6866, + 2.6937, + 2.7007, + 2.6992, + 2.7024, + 2.6835, + 2.6852, + 2.6852, + 2.6852, + 2.6762, + 2.6749, + 2.6767, + 2.6784, + 2.6757, + 2.6754, + 2.688, + 2.688, + 2.6934, + 2.6934, + 2.7015, + 2.7, + 2.6955, + 2.6871, + 2.6916, + 2.695, + 2.695, + 2.6918, + 2.6946, + 2.6974, + 2.6947, + 2.7032, + 2.6994, + 2.6986, + 2.6986, + 2.6986, + 2.6959, + 2.6871, + 2.6831, + 2.687, + 2.6731, + 2.6814, + 2.6808, + 2.688, + 2.6908, + 2.6802, + 2.6826, + 2.6859, + 2.6846, + 2.6844, + 2.6813, + 2.6697, + 2.6761, + 2.663, + 2.6587, + 2.6603, + 2.6617, + 2.6658, + 2.6658, + 2.6599, + 2.6579, + 2.6579, + 2.6594, + 2.6621, + 2.6621, + 2.6608, + 2.6643, + 2.6523, + 2.6555, + 2.663, + 2.6737, + 2.6722, + 2.6774, + 2.6835, + 2.6878, + 2.6891, + 2.6981, + 2.6961, + 2.7063, + 2.715, + 2.7172, + 2.7166, + 2.7285, + 2.733, + 2.7246, + 2.7391, + 2.7335, + 2.7284, + 2.7299, + 2.7262, + 2.7174, + 2.7152, + 2.7124, + 2.7138, + 2.7154, + 2.7198, + 2.7246, + 2.7194, + 2.7271, + 2.7209, + 2.7251, + 2.7273, + 2.723, + 2.7199, + 2.7158, + 2.7237, + 2.72, + 2.7137, + 2.7108, + 2.7139, + 2.714, + 2.7141, + 2.7141, + 2.7007, + 2.7006, + 2.7032, + 2.7087, + 2.7026, + 2.6837, + 2.6884, + 2.6826, + 2.6798, + 2.6902, + 2.6943, + 2.6922, + 2.694, + 2.69, + 2.6789, + 2.6887, + 2.6885, + 2.6943, + 2.6959, + 2.704, + 2.6951, + 2.6952, + 2.6989, + 2.6974, + 2.6991, + 2.6991, + 2.6991, + 2.6983, + 2.699, + 2.699, + 2.7029, + 2.6974, + 2.6926, + 2.6956, + 2.6956, + 2.6956, + 2.6956, + 2.6956, + 2.7024, + 2.7015, + 2.7015, + 2.7024, + 2.7, + 2.71, + 2.707, + 2.7075, + 2.7136, + 2.7161, + 2.7161, + 2.7062, + 2.7062, + 2.7062, + 2.7054, + 2.7246, + 2.7278, + 2.7135, + 2.7181, + 2.7224, + 2.7233, + 2.7234, + 2.7277, + 2.7245, + 2.7169, + 2.733, + 2.7631, + 2.7561, + 2.7511, + 2.755, + 2.7547, + 2.7515, + 2.7566, + 2.752, + 2.7585, + 2.7678, + 2.7858, + 2.8034, + 2.8069, + 2.819, + 2.811, + 2.803, + 2.772, + 2.7776, + 2.7758, + 2.7656, + 2.7599, + 2.7614, + 2.7543, + 2.754, + 2.7329, + 2.7459, + 2.7437, + 2.7456, + 2.7547, + 2.7463, + 2.742, + 2.7463, + 2.7405, + 2.7432, + 2.7383, + 2.736, + 2.736, + 2.7411, + 2.7418, + 2.7401, + 2.7261, + 2.7339, + 2.7235, + 2.7229, + 2.7157, + 2.7157, + 2.7132, + 2.7124, + 2.7163, + 2.7212, + 2.7212, + 2.7199, + 2.7199, + 2.7199, + 2.7077, + 2.7077, + 2.7148, + 2.7167, + 2.7167, + 2.7167, + 2.7209, + 2.7182, + 2.7182, + 2.7191, + 2.7128, + 2.7206, + 2.7206, + 2.7179, + 2.7163, + 2.7163, + 2.7209, + 2.7209, + 2.7265, + 2.7312, + 2.7261, + 2.7336, + 2.7336, + 2.7268, + 2.7323, + 2.7316, + 2.7303, + 2.7418, + 2.7383, + 2.7409, + 2.7267, + 2.7317, + 2.7317, + 2.7317, + 2.7317, + 2.7322, + 2.7249, + 2.7249, + 2.7312, + 2.7312, + 2.7349, + 2.7407, + 2.7359, + 2.7444, + 2.7374, + 2.7322, + 2.7279, + 2.7279, + 2.7279, + 2.7326, + 2.7346, + 2.7377, + 2.7433, + 2.7399, + 2.735, + 2.7417, + 2.7504, + 2.7481, + 2.7507, + 2.751, + 2.7479, + 2.7446, + 2.755, + 2.7549, + 2.7549, + 2.7662, + 2.7639, + 2.7746, + 2.7621, + 2.7627, + 2.7627, + 2.7662, + 2.7707, + 2.7763, + 2.7808, + 2.7889, + 2.7678, + 2.788, + 2.787, + 2.7758, + 2.7758, + 2.7717, + 2.7726, + 2.7726, + 2.7726, + 2.784, + 2.7891, + 2.7863, + 2.7863, + 2.7873, + 2.7917, + 2.7926, + 2.7889, + 2.7928, + 2.7915, + 2.7913, + 2.7901, + 2.7914, + 2.7909, + 2.7876, + 2.7822, + 2.7912, + 2.7912, + 2.7988, + 2.8009, + 2.8024, + 2.8107, + 2.8094, + 2.8066, + 2.8183, + 2.82, + 2.8156, + 2.8231, + 2.8179, + 2.8128, + 2.8035, + 2.8114, + 2.815, + 2.8174, + 2.8078, + 2.8062, + 2.8087, + 2.8096, + 2.8046, + 2.8059, + 2.8121, + 2.7962, + 2.7998, + 2.7991, + 2.8081, + 2.81, + 2.8079, + 2.8112, + 2.8062, + 2.7966, + 2.7978, + 2.7914, + 2.7885, + 2.7867, + 2.7958, + 2.7837, + 2.7864, + 2.8015, + 2.8235, + 2.8078, + 2.813, + 2.8088, + 2.8146, + 2.813, + 2.811, + 2.8118, + 2.8174, + 2.8159, + 2.8174, + 2.8216, + 2.8216, + 2.822, + 2.8182, + 2.825, + 2.8259, + 2.8258, + 2.8216, + 2.8219, + 2.8336, + 2.826, + 2.8394, + 2.8401, + 2.8332, + 2.837, + 2.8389, + 2.8416, + 2.8418, + 2.8456, + 2.8355, + 2.8277, + 2.8353, + 2.8362, + 2.8384, + 2.8382, + 2.8474, + 2.8553, + 2.8596, + 2.866, + 2.8697, + 2.8739, + 2.87, + 2.8679, + 2.8607, + 2.8638, + 2.8594, + 2.8548, + 2.848, + 2.8494, + 2.8528, + 2.8558, + 2.8433, + 2.8445, + 2.8494, + 2.8494, + 2.8532, + 2.8486, + 2.8462, + 2.8417, + 2.839, + 2.847, + 2.847, + 2.8453, + 2.8388, + 2.8368, + 2.8405, + 2.8391, + 2.83, + 2.8323, + 2.835, + 2.835, + 2.8287, + 2.8287, + 2.836, + 2.8385, + 2.8426, + 2.846, + 2.8381, + 2.8381, + 2.8454, + 2.8417, + 2.8386, + 2.838, + 2.8362, + 2.8301, + 2.8282, + 2.8276, + 2.8285, + 2.8285, + 2.8294, + 2.8326, + 2.8268, + 2.8176, + 2.8158, + 2.8091, + 2.8172, + 2.8138, + 2.8038, + 2.8022, + 2.8065, + 2.8104, + 2.8064, + 2.8146, + 2.8149, + 2.8192, + 2.8174, + 2.8142, + 2.8174, + 2.8142, + 2.8161, + 2.814, + 2.81, + 2.8129, + 2.815, + 2.815, + 2.815, + 2.8126, + 2.8235, + 2.8271, + 2.8221, + 2.82, + 2.8159, + 2.8148, + 2.8193, + 2.8158, + 2.8187, + 2.8218, + 2.8237, + 2.8267, + 2.8267, + 2.8232, + 2.8286, + 2.8349, + 2.827, + 2.8287, + 2.8277, + 2.8223, + 2.8223, + 2.8224, + 2.825, + 2.825, + 2.8222, + 2.8222, + 2.8331, + 2.839, + 2.8405, + 2.8421, + 2.8378, + 2.8425, + 2.8425, + 2.8402, + 2.8402, + 2.8367, + 2.8316, + 2.8363, + 2.8249, + 2.8252, + 2.8289, + 2.8225, + 2.8225, + 2.8176, + 2.8124, + 2.8238, + 2.8216, + 2.8203, + 2.8203, + 2.8203, + 2.8165, + 2.8165, + 2.8165, + 2.8095, + 2.8095, + 2.8046, + 2.8094, + 2.8189, + 2.8207, + 2.8221, + 2.8221, + 2.8214, + 2.8214, + 2.8214, + 2.8183, + 2.8183, + 2.8096, + 2.8137, + 2.8166, + 2.8251, + 2.8282, + 2.8282, + 2.8297, + 2.8297, + 2.8297, + 2.8206, + 2.8231, + 2.8168, + 2.824, + 2.8174, + 2.8128, + 2.8099, + 2.8108, + 2.808, + 2.8121, + 2.8094, + 2.8046, + 2.8067, + 2.8101, + 2.8081, + 2.8081, + 2.8051, + 2.8052, + 2.8087, + 2.8087, + 2.8085, + 2.8085, + 2.818, + 2.818, + 2.8191, + 2.82, + 2.82, + 2.82, + 2.82, + 2.82, + 2.8155, + 2.8155, + 2.8155, + 2.8155, + 2.8207, + 2.8196, + 2.8196, + 2.8196, + 2.8196, + 2.8119, + 2.8123, + 2.8119, + 2.8119, + 2.8119, + 2.8139, + 2.8139, + 2.8139, + 2.8205, + 2.8205, + 2.8202, + 2.8202, + 2.8202, + 2.8202, + 2.8164, + 2.8178, + 2.8142, + 2.8079, + 2.8153, + 2.8168, + 2.8203, + 2.8178, + 2.8178, + 2.8178, + 2.8178, + 2.8072, + 2.8065, + 2.8016, + 2.8028, + 2.8028, + 2.8046, + 2.7955, + 2.8016, + 2.8023, + 2.7984, + 2.7988, + 2.7991, + 2.8023, + 2.8037, + 2.8061, + 2.8081, + 2.8088, + 2.8078, + 2.8078, + 2.8123, + 2.8115, + 2.8089, + 2.8111, + 2.8111, + 2.8165, + 2.814, + 2.8165, + 2.8169, + 2.8169, + 2.8169, + 2.8148, + 2.8146, + 2.8179, + 2.8179, + 2.8116, + 2.8012, + 2.8123, + 2.8154, + 2.8158, + 2.8187, + 2.8156, + 2.8156, + 2.816, + 2.816, + 2.8216, + 2.8354, + 2.8289, + 2.8289, + 2.8228, + 2.8279, + 2.823, + 2.823, + 2.8148, + 2.8157, + 2.8143, + 2.7996, + 2.797, + 2.8046, + 2.8079, + 2.7966, + 2.7943, + 2.791, + 2.7714, + 2.7888, + 2.7899, + 2.7732, + 2.7735, + 2.7637, + 2.7776, + 2.7692, + 2.7751, + 2.7495, + 2.747, + 2.7485, + 2.7607, + 2.7678, + 2.7701, + 2.7691, + 2.7715, + 2.7684, + 2.7693, + 2.7799, + 2.7833, + 2.7828, + 2.7899, + 2.7951, + 2.7965, + 2.7906, + 2.785, + 2.785, + 2.7861, + 2.7915, + 2.7877, + 2.7849, + 2.7886, + 2.7902, + 2.7902, + 2.7927, + 2.784, + 2.7838, + 2.7829, + 2.792, + 2.7932, + 2.7932, + 2.7932, + 2.796, + 2.7963, + 2.7963, + 2.7959, + 2.7984, + 2.7975, + 2.796, + 2.7866, + 2.7876, + 2.7827, + 2.7827, + 2.7799, + 2.7803, + 2.7803, + 2.7742, + 2.7657, + 2.7634, + 2.7614, + 2.7653, + 2.7635, + 2.7635, + 2.7635, + 2.7607, + 2.7619, + 2.7558, + 2.7594, + 2.762, + 2.7543, + 2.7563, + 2.7579, + 2.757, + 2.7459, + 2.7506, + 2.7498, + 2.7519, + 2.7505, + 2.7626, + 2.7561, + 2.7634, + 2.7623, + 2.764, + 2.7664, + 2.7664, + 2.7719, + 2.7687, + 2.7687, + 2.766, + 2.766, + 2.7722, + 2.7778, + 2.7755, + 2.77, + 2.767, + 2.7684, + 2.7684, + 2.7684, + 2.7586, + 2.7614, + 2.7587, + 2.7604, + 2.7604, + 2.7581, + 2.7576, + 2.7576, + 2.759, + 2.759, + 2.7619, + 2.7582, + 2.7582, + 2.7621, + 2.7647, + 2.7647, + 2.7692, + 2.7742, + 2.7742, + 2.7751, + 2.7741, + 2.7705, + 2.7705, + 2.7699, + 2.7668, + 2.7628, + 2.7598, + 2.7618, + 2.7618, + 2.7664, + 2.7654, + 2.7665, + 2.7665, + 2.7758, + 2.7735, + 2.769, + 2.769, + 2.7659, + 2.7668, + 2.7668, + 2.7663, + 2.7646, + 2.7646, + 2.7646, + 2.7646, + 2.7655, + 2.7608, + 2.7598, + 2.7614, + 2.7614, + 2.7693, + 2.7712, + 2.7732, + 2.7732, + 2.7698, + 2.7721, + 2.769, + 2.769, + 2.7642, + 2.7676, + 2.7719, + 2.758, + 2.7584, + 2.7479, + 2.7483, + 2.7483, + 2.7537, + 2.7537, + 2.75, + 2.75, + 2.75, + 2.7548, + 2.7548, + 2.7531, + 2.752, + 2.7456, + 2.7465, + 2.7465, + 2.7465, + 2.7572, + 2.7626, + 2.7626, + 2.761, + 2.7609, + 2.7574, + 2.7574, + 2.7534, + 2.7547, + 2.7499, + 2.7519, + 2.746, + 2.7459, + 2.7459, + 2.746, + 2.746, + 2.7464, + 2.7464, + 2.7465, + 2.7421, + 2.7421, + 2.7451, + 2.7439, + 2.7443, + 2.7507, + 2.746, + 2.745, + 2.745, + 2.745, + 2.7428, + 2.7428, + 2.7428, + 2.7482, + 2.7482, + 2.7477, + 2.7449, + 2.7477, + 2.7532, + 2.7543, + 2.7605, + 2.7521, + 2.7544, + 2.7539, + 2.7539, + 2.7539, + 2.7518, + 2.7486, + 2.7519, + 2.7564, + 2.75, + 2.7465, + 2.7433, + 2.7407, + 2.734, + 2.738, + 2.7343, + 2.7414, + 2.7433, + 2.7443, + 2.7464, + 2.7467, + 2.748, + 2.7453, + 2.746, + 2.7463, + 2.75, + 2.7579, + 2.7613, + 2.7626, + 2.7641, + 2.7641, + 2.7654, + 2.7702, + 2.7696, + 2.7802, + 2.7884, + 2.7847, + 2.784, + 2.7833, + 2.7897, + 2.7933, + 2.7914, + 2.7871, + 2.7848, + 2.782, + 2.7792, + 2.7834, + 2.7813, + 2.782, + 2.7839, + 2.7839, + 2.7832, + 2.7852, + 2.7831, + 2.7868, + 2.7882, + 2.7886, + 2.784, + 2.7835, + 2.7775, + 2.7775, + 2.7785, + 2.7802, + 2.778, + 2.7714, + 2.7674, + 2.7697, + 2.7683, + 2.7668, + 2.7626, + 2.7658, + 2.7614, + 2.7632, + 2.7644, + 2.7663, + 2.7653, + 2.7663, + 2.7619, + 2.7603, + 2.7549, + 2.7561, + 2.7531, + 2.7526, + 2.7546, + 2.7585, + 2.7585, + 2.7598, + 2.7601, + 2.7569, + 2.7566, + 2.7535, + 2.7535, + 2.7488, + 2.7478, + 2.7493, + 2.7512, + 2.7494, + 2.746, + 2.746, + 2.7525, + 2.7525, + 2.7455, + 2.7543, + 2.7579, + 2.7606, + 2.7606, + 2.7622, + 2.7622, + 2.7642, + 2.7642, + 2.7642, + 2.7642, + 2.7641, + 2.7631, + 2.7625, + 2.768, + 2.7649, + 2.7613, + 2.7627, + 2.7627, + 2.7627, + 2.7554, + 2.7642, + 2.7682, + 2.7682, + 2.7704, + 2.7722, + 2.7742, + 2.7727, + 2.7744, + 2.7718, + 2.7732, + 2.7732, + 2.7732, + 2.7615, + 2.7609, + 2.7609, + 2.7622, + 2.7578, + 2.7661, + 2.7683, + 2.7703, + 2.7626, + 2.7646, + 2.7678, + 2.771, + 2.7671, + 2.7658, + 2.7591, + 2.7615, + 2.7607, + 2.7613, + 2.7586, + 2.7698, + 2.7598, + 2.7619, + 2.7648, + 2.7623, + 2.7623, + 2.7593, + 2.752, + 2.7517, + 2.7569, + 2.7585, + 2.7609, + 2.7638, + 2.7665, + 2.7649, + 2.769, + 2.769, + 2.7691, + 2.7724, + 2.7724, + 2.771, + 2.7729, + 2.7735, + 2.7714, + 2.7639, + 2.7644, + 2.7608, + 2.7586, + 2.7574, + 2.7599, + 2.7592, + 2.7571, + 2.7577, + 2.7564, + 2.759, + 2.7627, + 2.7642, + 2.7631, + 2.7597, + 2.7729, + 2.7663, + 2.7712, + 2.7707, + 2.7751, + 2.7722, + 2.7717, + 2.774, + 2.779, + 2.7816, + 2.7819, + 2.7816, + 2.7798, + 2.777, + 2.7756, + 2.7769, + 2.7729, + 2.7725, + 2.7726, + 2.7729, + 2.772, + 2.772, + 2.7787, + 2.7751, + 2.7756, + 2.7756, + 2.7737, + 2.7756, + 2.7756, + 2.773, + 2.773, + 2.7743, + 2.7756, + 2.7756, + 2.7726, + 2.769, + 2.771, + 2.771, + 2.771, + 2.7697, + 2.7646, + 2.7646, + 2.7624, + 2.7549, + 2.7594, + 2.7648, + 2.7591, + 2.7497, + 2.7464, + 2.7444, + 2.7482, + 2.7466, + 2.7466, + 2.7466, + 2.7514, + 2.7492, + 2.7492, + 2.7516, + 2.7537, + 2.7598, + 2.7605, + 2.7625, + 2.768, + 2.7686, + 2.7644, + 2.763, + 2.7652, + 2.7644, + 2.7644, + 2.7627, + 2.7627, + 2.7642, + 2.7604, + 2.7608, + 2.7605, + 2.7527, + 2.7508, + 2.7405, + 2.74, + 2.7374, + 2.7393, + 2.7358, + 2.7262, + 2.7286, + 2.7274, + 2.7235, + 2.7249, + 2.7233, + 2.7287, + 2.7297, + 2.7228, + 2.7234, + 2.7251, + 2.7189, + 2.7177, + 2.7098, + 2.7058, + 2.6982, + 2.7112, + 2.7056, + 2.7023, + 2.6988, + 2.704, + 2.704, + 2.7081, + 2.7081, + 2.7042, + 2.7083, + 2.704, + 2.704, + 2.7048, + 2.7034, + 2.7017, + 2.6992, + 2.6975, + 2.6977, + 2.7001, + 2.686, + 2.6947, + 2.696, + 2.6907, + 2.6858, + 2.6789, + 2.6856, + 2.677, + 2.6806, + 2.6806, + 2.679, + 2.6781, + 2.6759, + 2.6848, + 2.683, + 2.6847, + 2.6857, + 2.6855, + 2.6855, + 2.6846, + 2.6923, + 2.6939, + 2.6939, + 2.6939, + 2.6876, + 2.6906, + 2.6877, + 2.6892, + 2.6892, + 2.6789, + 2.6646, + 2.6612, + 2.662, + 2.6651, + 2.6691, + 2.6757, + 2.6683, + 2.6771, + 2.6744, + 2.6761, + 2.6722, + 2.6765, + 2.6762, + 2.6723, + 2.6736, + 2.6739, + 2.6863, + 2.6863, + 2.6879, + 2.688, + 2.686, + 2.6872, + 2.6854, + 2.6854, + 2.692, + 2.6959, + 2.6959, + 2.6959, + 2.6915, + 2.6918, + 2.6938, + 2.6948, + 2.6948, + 2.6964, + 2.6964, + 2.6983, + 2.7, + 2.7007, + 2.7032, + 2.703, + 2.703, + 2.7057, + 2.7066, + 2.7026, + 2.6999, + 2.6995, + 2.6995, + 2.6995, + 2.6995, + 2.6995, + 2.6995, + 2.6954, + 2.697, + 2.6974, + 2.7009, + 2.7045, + 2.7045, + 2.7045, + 2.702, + 2.702, + 2.699, + 2.6982, + 2.694, + 2.6918, + 2.6905, + 2.6872, + 2.6901, + 2.6901, + 2.6878, + 2.6871, + 2.6871, + 2.6853, + 2.6853, + 2.6853, + 2.686, + 2.6884, + 2.6884, + 2.6934, + 2.6934, + 2.6934, + 2.6941, + 2.6914, + 2.6915, + 2.6921, + 2.6921, + 2.6921, + 2.6921, + 2.6921, + 2.6921, + 2.6921, + 2.6985, + 2.6971, + 2.7015, + 2.7032, + 2.702, + 2.7023, + 2.7023, + 2.7099, + 2.7112, + 2.7112, + 2.7114, + 2.7128, + 2.7121, + 2.7121, + 2.7121, + 2.7121, + 2.7121, + 2.7121, + 2.7128, + 2.7077, + 2.7078, + 2.7078, + 2.6959, + 2.6947, + 2.6878, + 2.6878, + 2.6875, + 2.6867, + 2.6846, + 2.688, + 2.6848, + 2.6894, + 2.6871, + 2.6875, + 2.6926, + 2.6943, + 2.6941, + 2.6938, + 2.6892, + 2.6914, + 2.6926, + 2.6936, + 2.6925, + 2.6931, + 2.6931, + 2.6984, + 2.6984, + 2.7025, + 2.7039, + 2.6986, + 2.6991, + 2.6991, + 2.7002, + 2.6974, + 2.7017, + 2.7012, + 2.7019, + 2.7019, + 2.7051, + 2.7001, + 2.6987, + 2.7001, + 2.7001, + 2.7001, + 2.6871, + 2.6883, + 2.6808, + 2.6877, + 2.6863, + 2.6873, + 2.6861, + 2.6868, + 2.6889, + 2.6889, + 2.6862, + 2.6862, + 2.6759, + 2.6801, + 2.6827, + 2.6791, + 2.6791, + 2.6791, + 2.6709, + 2.6772, + 2.6785, + 2.6781, + 2.6781, + 2.6781, + 2.6717, + 2.6717, + 2.6608, + 2.6576, + 2.6601, + 2.6609, + 2.6697, + 2.6849, + 2.6805, + 2.6838, + 2.6825, + 2.6727, + 2.6603, + 2.5736, + 2.6185, + 2.6093, + 2.6033, + 2.626, + 2.6277, + 2.626, + 2.627, + 2.6336, + 2.6364, + 2.6261, + 2.6325, + 2.6407, + 2.6369, + 2.6423, + 2.6383, + 2.6385, + 2.6464, + 2.6423, + 2.6413, + 2.6404, + 2.6521, + 2.6542, + 2.665, + 2.6659, + 2.6585, + 2.6513, + 2.6456, + 2.6498, + 2.65, + 2.6491, + 2.6477, + 2.6558, + 2.6576, + 2.6606, + 2.6672, + 2.67, + 2.6677, + 2.6655, + 2.6674, + 2.6674, + 2.6649, + 2.6633, + 2.6694, + 2.667, + 2.6628, + 2.6628, + 2.6628, + 2.6663, + 2.668, + 2.6726, + 2.677, + 2.6798, + 2.6787, + 2.6783, + 2.6863, + 2.6914, + 2.6942, + 2.6942, + 2.6794, + 2.677, + 2.6765, + 2.6825, + 2.6816, + 2.6783, + 2.691, + 2.691, + 2.6828, + 2.6789, + 2.6823, + 2.6754, + 2.6757, + 2.6672, + 2.6766, + 2.6645, + 2.6613, + 2.6685, + 2.6617, + 2.6596, + 2.6581, + 2.6494, + 2.6457, + 2.6457, + 2.6457, + 2.6545, + 2.6564, + 2.6534, + 2.6517, + 2.6517, + 2.6452, + 2.6462, + 2.6462, + 2.6471, + 2.6501, + 2.6385, + 2.6284, + 2.6295, + 2.6371, + 2.6371, + 2.6351, + 2.633, + 2.6339, + 2.6382, + 2.6382, + 2.6365, + 2.6383, + 2.6383, + 2.6383, + 2.6383, + 2.6283, + 2.6247, + 2.6307, + 2.6367, + 2.6367, + 2.6345, + 2.6318, + 2.6421, + 2.6427, + 2.643, + 2.6246, + 2.6232, + 2.6207, + 2.6253, + 2.6181, + 2.6175, + 2.6007, + 2.6108, + 2.6188, + 2.6195, + 2.6093, + 2.6087, + 2.6069, + 2.6112, + 2.612, + 2.6128, + 2.6263, + 2.6288, + 2.6288, + 2.6176, + 2.6203, + 2.622, + 2.6267, + 2.6342, + 2.628, + 2.6249, + 2.6275, + 2.6275, + 2.6317, + 2.6302, + 2.6302, + 2.6195, + 2.6195, + 2.6195, + 2.6195, + 2.6238, + 2.6287, + 2.6245, + 2.6354, + 2.6299, + 2.6299, + 2.627, + 2.6291, + 2.6291, + 2.6291, + 2.6291, + 2.6375, + 2.6413, + 2.6329, + 2.6329, + 2.624, + 2.6211, + 2.6211, + 2.616, + 2.6191, + 2.6191, + 2.6152, + 2.6175, + 2.6111, + 2.6111, + 2.6271, + 2.6257, + 2.6257, + 2.6194, + 2.6194, + 2.6208, + 2.6208, + 2.6208, + 2.6158, + 2.6203, + 2.6169, + 2.61, + 2.6127, + 2.615, + 2.613, + 2.6149, + 2.6218, + 2.6175, + 2.6175, + 2.6169, + 2.6153, + 2.6182, + 2.6099, + 2.6105, + 2.6072, + 2.6094, + 2.6097, + 2.61, + 2.6052, + 2.6072, + 2.6122, + 2.6049, + 2.5951, + 2.5873, + 2.5881, + 2.5898, + 2.5747, + 2.5828, + 2.5903, + 2.5832, + 2.5885, + 2.583, + 2.596, + 2.5878, + 2.5906, + 2.5884, + 2.5942, + 2.5909, + 2.5958, + 2.6009, + 2.6027, + 2.603, + 2.6013, + 2.6013, + 2.5911, + 2.592, + 2.592, + 2.5863, + 2.5863, + 2.5873, + 2.5922, + 2.6004, + 2.6009, + 2.6, + 2.6, + 2.5951, + 2.5866, + 2.5843, + 2.5917, + 2.5917, + 2.5849, + 2.5806, + 2.5709, + 2.572, + 2.576, + 2.576, + 2.5711, + 2.5711, + 2.58, + 2.58, + 2.5764, + 2.5762, + 2.578, + 2.5845, + 2.5868, + 2.5811, + 2.5811, + 2.5798, + 2.5908, + 2.5829, + 2.5829, + 2.5829, + 2.5902, + 2.593, + 2.5843, + 2.5901, + 2.5812, + 2.58, + 2.58, + 2.585, + 2.5791, + 2.5748, + 2.5767, + 2.5767, + 2.5718, + 2.5674, + 2.5609, + 2.5708, + 2.5706, + 2.5706, + 2.5673, + 2.582, + 2.581, + 2.5885, + 2.5854, + 2.5845, + 2.5845, + 2.5727, + 2.5727, + 2.5718, + 2.5629, + 2.5629, + 2.5673, + 2.5662, + 2.5662, + 2.5671, + 2.5642, + 2.5642, + 2.5697, + 2.5699, + 2.5706, + 2.5635, + 2.5661, + 2.5791, + 2.5821, + 2.5829, + 2.5784, + 2.5814, + 2.5814, + 2.5807, + 2.5826, + 2.5826, + 2.5826, + 2.5826, + 2.5826, + 2.5826, + 2.584, + 2.5867, + 2.5862, + 2.5826, + 2.5891, + 2.5892, + 2.5892, + 2.5892, + 2.5892, + 2.5892, + 2.5892, + 2.5892, + 2.5892, + 2.5765, + 2.5836, + 2.5836, + 2.5836, + 2.5877, + 2.5885, + 2.5917, + 2.5906, + 2.5951, + 2.5959, + 2.5959, + 2.5979, + 2.5979, + 2.5991, + 2.5955, + 2.5955, + 2.5984, + 2.595, + 2.6016, + 2.6036, + 2.6036, + 2.6038, + 2.598, + 2.6036, + 2.6061, + 2.6038, + 2.6023, + 2.6023, + 2.6018, + 2.5932, + 2.5934, + 2.5851, + 2.5841, + 2.583, + 2.583, + 2.5817, + 2.58, + 2.579, + 2.5787, + 2.577, + 2.5769, + 2.5775, + 2.5796, + 2.5796, + 2.5796, + 2.5796, + 2.5825, + 2.5826, + 2.5826, + 2.5687, + 2.5664, + 2.5602, + 2.5594, + 2.5529, + 2.5489, + 2.5494, + 2.5551, + 2.5584, + 2.565, + 2.5569, + 2.5503, + 2.5434, + 2.5479, + 2.54, + 2.5483, + 2.5438, + 2.5365, + 2.5328, + 2.4999, + 2.491, + 2.4933, + 2.4909, + 2.5034, + 2.5032, + 2.508, + 2.5101, + 2.5106, + 2.5096, + 2.515, + 2.5194, + 2.5162, + 2.5145, + 2.5145, + 2.5245, + 2.5292, + 2.5252, + 2.5274, + 2.5278, + 2.5211, + 2.5159, + 2.5159, + 2.5143, + 2.512, + 2.5156, + 2.4996, + 2.4997, + 2.4959, + 2.4923, + 2.4838, + 2.4761, + 2.4796, + 2.4022, + 2.395, + 2.4137, + 2.419, + 2.4254, + 2.4158, + 2.427, + 2.4143, + 2.4189, + 2.4262, + 2.4245, + 2.4239, + 2.4229, + 2.4192, + 2.4199, + 2.4137, + 2.3873, + 2.3936, + 2.3989, + 2.3975, + 2.395, + 2.4007, + 2.3924, + 2.3934, + 2.3993, + 2.4014, + 2.4012, + 2.412, + 2.4042, + 2.4015, + 2.4001, + 2.403, + 2.4013, + 2.3974, + 2.3921, + 2.3815, + 2.3747, + 2.3854, + 2.3822, + 2.3769, + 2.3866, + 2.3784, + 2.3796, + 2.3764, + 2.379, + 2.3842, + 2.382, + 2.3923, + 2.3966, + 2.4046, + 2.4014, + 2.4176, + 2.42, + 2.4226, + 2.4199, + 2.4221, + 2.4494, + 2.4622, + 2.46, + 2.4793, + 2.4469, + 2.4422, + 2.4344, + 2.4504, + 2.4528, + 2.4237, + 2.4488, + 2.4532, + 2.4667, + 2.4614, + 2.4637, + 2.4306, + 2.4326, + 2.4357, + 2.4416, + 2.4432, + 2.4384, + 2.413, + 2.4126, + 2.4296, + 2.4279, + 2.439, + 2.4353, + 2.4335, + 2.4455, + 2.4334, + 2.4542, + 2.455, + 2.4606, + 2.4515, + 2.4654, + 2.4547, + 2.4525, + 2.445, + 2.4535, + 2.4446, + 2.4408, + 2.443, + 2.431, + 2.4341, + 2.4245, + 2.4166, + 2.4297, + 2.432, + 2.4247, + 2.4238, + 2.4424, + 2.4455, + 2.4341, + 2.445, + 2.4504, + 2.46, + 2.4592, + 2.4632, + 2.4545, + 2.4607, + 2.4632, + 2.4667, + 2.4695, + 2.4664, + 2.476, + 2.4816, + 2.4845, + 2.487, + 2.4887, + 2.4838, + 2.4838, + 2.4828, + 2.4828, + 2.4852, + 2.4827, + 2.4915, + 2.4958, + 2.4971, + 2.4991, + 2.4935, + 2.4951, + 2.496, + 2.4969, + 2.4983, + 2.4982, + 2.5012, + 2.5068, + 2.5041, + 2.4999, + 2.4976, + 2.4954, + 2.4925, + 2.4803, + 2.4772, + 2.4719, + 2.4576, + 2.4545, + 2.4605, + 2.4605, + 2.4596, + 2.4626, + 2.4639, + 2.4565, + 2.4702, + 2.4772, + 2.4835, + 2.4876, + 2.4875, + 2.4852, + 2.4867, + 2.4883, + 2.489, + 2.4917, + 2.491, + 2.4824, + 2.4847, + 2.4885, + 2.4885, + 2.4899, + 2.4899, + 2.4847, + 2.4847, + 2.4764, + 2.4724, + 2.4709, + 2.4709, + 2.4606, + 2.473, + 2.4757, + 2.4774, + 2.4774, + 2.4847, + 2.4847, + 2.4816, + 2.4883, + 2.4993, + 2.4956, + 2.4925, + 2.4925, + 2.4952, + 2.4973, + 2.4973, + 2.4923, + 2.4923, + 2.4883, + 2.4861, + 2.4861, + 2.484, + 2.4809, + 2.483, + 2.4811, + 2.4811, + 2.4817, + 2.4817, + 2.4862, + 2.477, + 2.477, + 2.477, + 2.4829, + 2.4883, + 2.4888, + 2.4923, + 2.4964, + 2.4964, + 2.4965, + 2.495, + 2.5025, + 2.5025, + 2.5006, + 2.4976 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T00:00:00", + "2024-01-01T00:02:00" + ], + "y": [ + 3.626233, + 3.626233 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T00:00:00", + "2024-01-01T01:25:00" + ], + "y": [ + 3.686505601555519, + 3.7496 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T00:03:00", + "2024-01-01T01:26:00" + ], + "y": [ + 3.650005685649132, + 3.7679 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:25:00", + "2024-01-01T02:27:00" + ], + "y": [ + 3.6887628, + 3.6887628 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:28:00", + "2024-01-01T02:30:00" + ], + "y": [ + 3.6937728, + 3.6937728 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:31:00", + "2024-01-01T02:33:00" + ], + "y": [ + 3.688362, + 3.688362 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:34:00", + "2024-01-01T02:36:00" + ], + "y": [ + 3.6859572, + 3.6859572 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:37:00", + "2024-01-01T02:39:00" + ], + "y": [ + 3.6805464, + 3.6805464 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T01:26:00", + "2024-01-01T02:41:00" + ], + "y": [ + 3.713420848392969, + 3.6552 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:40:00", + "2024-01-01T02:42:00" + ], + "y": [ + 3.6761376, + 3.6761376 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:43:00", + "2024-01-01T02:45:00" + ], + "y": [ + 3.6549954, + 3.6549954 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:46:00", + "2024-01-01T02:48:00" + ], + "y": [ + 3.6393641999999997, + 3.6393641999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T02:49:00", + "2024-01-01T11:42:00" + ], + "y": [ + 3.641912241437017, + 3.5315 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T11:42:00", + "2024-01-01T11:44:00" + ], + "y": [ + 3.538563, + 3.538563 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T11:45:00", + "2024-01-01T11:47:00" + ], + "y": [ + 3.5481822000000003, + 3.5481822000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T11:48:00", + "2024-01-01T11:50:00" + ], + "y": [ + 3.5405670000000002, + 3.5405670000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T11:51:00", + "2024-01-01T11:53:00" + ], + "y": [ + 3.5436731999999997, + 3.5436731999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T03:41:00", + "2024-01-01T15:41:00" + ], + "y": [ + 3.524886808663259, + 3.5185 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T15:41:00", + "2024-01-01T15:43:00" + ], + "y": [ + 3.511463, + 3.511463 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T15:44:00", + "2024-01-01T15:46:00" + ], + "y": [ + 3.5251356, + 3.5251356 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T15:47:00", + "2024-01-01T20:01:00" + ], + "y": [ + 3.5052775483326624, + 3.6117 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T20:01:00", + "2024-01-01T20:03:00" + ], + "y": [ + 3.6044766, + 3.6044766 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T20:04:00", + "2024-01-01T20:06:00" + ], + "y": [ + 3.5988878, + 3.5988878 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T20:07:00", + "2024-01-01T20:09:00" + ], + "y": [ + 3.6078698, + 3.6078698 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T20:10:00", + "2024-01-01T20:12:00" + ], + "y": [ + 3.6073708, + 3.6073708 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T11:54:00", + "2024-01-01T23:54:00" + ], + "y": [ + 3.590627032862249, + 3.6245 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T23:54:00", + "2024-01-01T23:56:00" + ], + "y": [ + 3.6317489999999997, + 3.6317489999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-01T23:57:00", + "2024-01-01T23:59:00" + ], + "y": [ + 3.6247350000000003, + 3.6247350000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T00:00:00", + "2024-01-02T00:02:00" + ], + "y": [ + 3.6135126, + 3.6135126 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-01T20:13:00", + "2024-01-02T01:57:00" + ], + "y": [ + 3.5822959193717274, + 3.6908 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T00:03:00", + "2024-01-02T02:16:00" + ], + "y": [ + 3.648965844501011, + 3.706 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T01:57:00", + "2024-01-02T06:45:00" + ], + "y": [ + 3.678510453987717, + 3.7946 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T03:16:00", + "2024-01-02T09:50:00" + ], + "y": [ + 3.7902979568710906, + 3.8476 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T06:45:00", + "2024-01-02T09:52:00" + ], + "y": [ + 3.769382049122572, + 3.9043 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T09:52:00", + "2024-01-02T10:39:00" + ], + "y": [ + 3.847848673898105, + 3.7888 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T10:50:00", + "2024-01-02T10:52:00" + ], + "y": [ + 3.774534, + 3.774534 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T11:39:00", + "2024-01-02T11:41:00" + ], + "y": [ + 3.7116618, + 3.7116618 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T11:42:00", + "2024-01-02T11:44:00" + ], + "y": [ + 3.7124601999999998, + 3.7124601999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T11:45:00", + "2024-01-02T13:32:00" + ], + "y": [ + 3.714191247922029, + 3.832 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T13:32:00", + "2024-01-02T14:25:00" + ], + "y": [ + 3.776594042050441, + 3.714 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T10:53:00", + "2024-01-02T14:32:00" + ], + "y": [ + 3.8077488709612943, + 3.6854 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T14:32:00", + "2024-01-02T14:34:00" + ], + "y": [ + 3.6927708, + 3.6927708 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T14:35:00", + "2024-01-02T16:11:00" + ], + "y": [ + 3.6821460507051933, + 3.5685 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:11:00", + "2024-01-02T16:13:00" + ], + "y": [ + 3.575637, + 3.575637 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:14:00", + "2024-01-02T16:16:00" + ], + "y": [ + 3.5784426, + 3.5784426 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:17:00", + "2024-01-02T16:19:00" + ], + "y": [ + 3.5874606, + 3.5874606 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:20:00", + "2024-01-02T16:22:00" + ], + "y": [ + 3.5800458, + 3.5800458 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:23:00", + "2024-01-02T16:25:00" + ], + "y": [ + 3.575136, + 3.575136 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T16:26:00", + "2024-01-02T21:03:00" + ], + "y": [ + 3.625084956258337, + 3.515 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-02T21:03:00", + "2024-01-03T00:10:00" + ], + "y": [ + 3.566276920178244, + 3.6202 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-02T15:25:00", + "2024-01-03T00:17:00" + ], + "y": [ + 3.5515949763405987, + 3.6624 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T01:10:00", + "2024-01-03T01:12:00" + ], + "y": [ + 3.7081014, + 3.7081014 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T01:13:00", + "2024-01-03T01:15:00" + ], + "y": [ + 3.7105062, + 3.7105062 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T00:17:00", + "2024-01-03T01:27:00" + ], + "y": [ + 3.62031914232483, + 3.7295 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T01:27:00", + "2024-01-03T01:29:00" + ], + "y": [ + 3.722041, + 3.722041 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T01:16:00", + "2024-01-03T04:18:00" + ], + "y": [ + 3.7213059709330762, + 3.78 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T05:18:00", + "2024-01-03T05:20:00" + ], + "y": [ + 3.7954758, + 3.7954758 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T01:30:00", + "2024-01-03T05:31:00" + ], + "y": [ + 3.6984677737575513, + 3.8164 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T05:21:00", + "2024-01-03T11:11:00" + ], + "y": [ + 3.8219377111662154, + 3.6995 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T05:31:00", + "2024-01-03T11:11:00" + ], + "y": [ + 3.761219598664223, + 3.6995 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T11:11:00", + "2024-01-03T11:13:00" + ], + "y": [ + 3.706899, + 3.706899 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T11:14:00", + "2024-01-03T12:01:00" + ], + "y": [ + 3.697563278898474, + 3.488 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T12:01:00", + "2024-01-03T12:08:00" + ], + "y": [ + 3.511326086794957, + 3.0304 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T12:08:00", + "2024-01-03T12:10:00" + ], + "y": [ + 3.0364608000000004, + 3.0364608000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T12:11:00", + "2024-01-03T12:12:00" + ], + "y": [ + 2.756838591040775, + 2.8168 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T12:11:00", + "2024-01-03T12:13:00" + ], + "y": [ + 2.7117656, + 2.7117656 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T12:14:00", + "2024-01-03T12:16:00" + ], + "y": [ + 2.9763354, + 2.9763354 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T12:17:00", + "2024-01-03T12:24:00" + ], + "y": [ + 3.0423032, + 3.14 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T12:24:00", + "2024-01-03T12:29:00" + ], + "y": [ + 3.1124541192106325, + 3.2265 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T12:29:00", + "2024-01-03T12:32:00" + ], + "y": [ + 3.1798488195917924, + 3.1118 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:12:00", + "2024-01-03T13:14:00" + ], + "y": [ + 3.2071014, + 3.2071014 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:15:00", + "2024-01-03T13:17:00" + ], + "y": [ + 3.2073017999999998, + 3.2073017999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:18:00", + "2024-01-03T13:20:00" + ], + "y": [ + 3.1918710000000003, + 3.1918710000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:21:00", + "2024-01-03T13:23:00" + ], + "y": [ + 3.169326, + 3.169326 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:24:00", + "2024-01-03T13:26:00" + ], + "y": [ + 3.1550976, + 3.1550976 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:27:00", + "2024-01-03T13:29:00" + ], + "y": [ + 3.143274, + 3.143274 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:30:00", + "2024-01-03T13:32:00" + ], + "y": [ + 3.1573019999999996, + 3.1573019999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T13:32:00", + "2024-01-03T13:34:00" + ], + "y": [ + 3.121744, + 3.121744 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T13:35:00", + "2024-01-03T13:37:00" + ], + "y": [ + 3.1620632, + 3.1620632 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T13:38:00", + "2024-01-03T13:40:00" + ], + "y": [ + 3.1618636, + 3.1618636 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T13:41:00", + "2024-01-03T14:16:00" + ], + "y": [ + 3.136780667285528, + 3.0707 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T13:33:00", + "2024-01-03T14:38:00" + ], + "y": [ + 3.165498284362336, + 3.0684 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T15:16:00", + "2024-01-03T15:18:00" + ], + "y": [ + 3.0622632, + 3.0622632 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T15:19:00", + "2024-01-03T15:59:00" + ], + "y": [ + 3.0470202015636847, + 3.1453 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T14:38:00", + "2024-01-03T16:12:00" + ], + "y": [ + 3.1131619066500495, + 3.1632 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:12:00", + "2024-01-03T17:14:00" + ], + "y": [ + 3.103194, + 3.103194 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:15:00", + "2024-01-03T17:17:00" + ], + "y": [ + 3.102192, + 3.102192 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:18:00", + "2024-01-03T17:20:00" + ], + "y": [ + 3.0835548, + 3.0835548 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:21:00", + "2024-01-03T17:23:00" + ], + "y": [ + 3.0827532, + 3.0827532 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:24:00", + "2024-01-03T17:26:00" + ], + "y": [ + 3.0764406, + 3.0764406 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:27:00", + "2024-01-03T17:29:00" + ], + "y": [ + 3.0844566, + 3.0844566 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T15:59:00", + "2024-01-03T18:55:00" + ], + "y": [ + 3.099822870684043, + 3.0467 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T19:55:00", + "2024-01-03T19:57:00" + ], + "y": [ + 3.0529818, + 3.0529818 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T19:58:00", + "2024-01-03T20:00:00" + ], + "y": [ + 3.0481914, + 3.0481914 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T20:01:00", + "2024-01-03T20:03:00" + ], + "y": [ + 3.0516843999999996, + 3.0516843999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T20:04:00", + "2024-01-03T21:32:00" + ], + "y": [ + 3.047917204763151, + 3.1407 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T21:32:00", + "2024-01-03T21:34:00" + ], + "y": [ + 3.1344185999999996, + 3.1344185999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T21:35:00", + "2024-01-03T21:37:00" + ], + "y": [ + 3.1337200000000003, + 3.1337200000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T17:30:00", + "2024-01-03T21:47:00" + ], + "y": [ + 3.111437107001599, + 3.1598 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T22:47:00", + "2024-01-03T22:49:00" + ], + "y": [ + 3.0737352000000002, + 3.0737352000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-03T21:38:00", + "2024-01-03T23:10:00" + ], + "y": [ + 3.0959792598374887, + 3.0439 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T00:10:00", + "2024-01-04T00:12:00" + ], + "y": [ + 3.0752372, + 3.0752372 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T00:13:00", + "2024-01-04T00:15:00" + ], + "y": [ + 3.0993888, + 3.0993888 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T00:16:00", + "2024-01-04T01:20:00" + ], + "y": [ + 3.0752828937406567, + 3.029 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-03T22:50:00", + "2024-01-04T01:24:00" + ], + "y": [ + 3.0993687346467014, + 2.997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T02:20:00", + "2024-01-04T02:22:00" + ], + "y": [ + 3.0792292, + 3.0792292 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T02:23:00", + "2024-01-04T02:25:00" + ], + "y": [ + 3.0695486, + 3.0695486 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T01:24:00", + "2024-01-04T02:37:00" + ], + "y": [ + 3.0407203214151344, + 3.0992 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T03:37:00", + "2024-01-04T03:39:00" + ], + "y": [ + 3.0968814, + 3.0968814 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T03:40:00", + "2024-01-04T03:42:00" + ], + "y": [ + 3.0994866, + 3.0994866 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T03:43:00", + "2024-01-04T03:45:00" + ], + "y": [ + 3.1022922, + 3.1022922 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T02:26:00", + "2024-01-04T09:56:00" + ], + "y": [ + 3.0721362911487313, + 3.1691 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T09:56:00", + "2024-01-04T10:16:00" + ], + "y": [ + 3.123278752260452, + 3.0727 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:16:00", + "2024-01-04T11:18:00" + ], + "y": [ + 3.0724427999999997, + 3.0724427999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:19:00", + "2024-01-04T11:21:00" + ], + "y": [ + 3.0896084, + 3.0896084 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:22:00", + "2024-01-04T11:24:00" + ], + "y": [ + 3.0968938, + 3.0968938 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:25:00", + "2024-01-04T11:27:00" + ], + "y": [ + 3.0966942, + 3.0966942 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:28:00", + "2024-01-04T11:30:00" + ], + "y": [ + 3.0941992000000003, + 3.0941992000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:31:00", + "2024-01-04T11:33:00" + ], + "y": [ + 3.0988898000000002, + 3.0988898000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:34:00", + "2024-01-04T11:36:00" + ], + "y": [ + 3.0954966, + 3.0954966 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:37:00", + "2024-01-04T11:39:00" + ], + "y": [ + 3.0969936000000002, + 3.0969936000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T03:46:00", + "2024-01-04T15:19:00" + ], + "y": [ + 3.1417732890537553, + 3.1933 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T11:40:00", + "2024-01-04T15:27:00" + ], + "y": [ + 3.1020833999999997, + 3.1987 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T15:27:00", + "2024-01-04T15:29:00" + ], + "y": [ + 3.1923026, + 3.1923026 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T15:30:00", + "2024-01-04T15:32:00" + ], + "y": [ + 3.2147576, + 3.2147576 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T15:33:00", + "2024-01-04T15:35:00" + ], + "y": [ + 3.196095, + 3.196095 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T15:36:00", + "2024-01-04T15:38:00" + ], + "y": [ + 3.2151568, + 3.2151568 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:19:00", + "2024-01-04T16:21:00" + ], + "y": [ + 3.187863, + 3.187863 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:22:00", + "2024-01-04T16:24:00" + ], + "y": [ + 3.1715304, + 3.1715304 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:25:00", + "2024-01-04T16:27:00" + ], + "y": [ + 3.164316, + 3.164316 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:28:00", + "2024-01-04T16:30:00" + ], + "y": [ + 3.162312, + 3.162312 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:31:00", + "2024-01-04T16:33:00" + ], + "y": [ + 3.169326, + 3.169326 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:34:00", + "2024-01-04T16:36:00" + ], + "y": [ + 3.1704282, + 3.1704282 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:37:00", + "2024-01-04T16:39:00" + ], + "y": [ + 3.1750374, + 3.1750374 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:40:00", + "2024-01-04T16:42:00" + ], + "y": [ + 3.1709292000000002, + 3.1709292000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-04T15:39:00", + "2024-01-05T01:47:00" + ], + "y": [ + 3.1805386984616866, + 3.131 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-04T16:43:00", + "2024-01-05T01:48:00" + ], + "y": [ + 3.209141934146169, + 3.0041 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T01:48:00", + "2024-01-05T01:54:00" + ], + "y": [ + 3.047923896417486, + 3.0977 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T02:47:00", + "2024-01-05T02:49:00" + ], + "y": [ + 3.078331, + 3.078331 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T02:54:00", + "2024-01-05T02:56:00" + ], + "y": [ + 3.0823524, + 3.0823524 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T02:57:00", + "2024-01-05T02:59:00" + ], + "y": [ + 3.0750378, + 3.0750378 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T03:00:00", + "2024-01-05T14:20:00" + ], + "y": [ + 3.099117448390942, + 2.9949 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T02:50:00", + "2024-01-05T14:35:00" + ], + "y": [ + 3.0373395558964655, + 2.9723 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:35:00", + "2024-01-05T15:37:00" + ], + "y": [ + 2.9643593999999998, + 2.9643593999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:38:00", + "2024-01-05T15:40:00" + ], + "y": [ + 2.9607666, + 2.9607666 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:41:00", + "2024-01-05T15:43:00" + ], + "y": [ + 2.9651578, + 2.9651578 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:44:00", + "2024-01-05T15:46:00" + ], + "y": [ + 2.9471938, + 2.9471938 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:47:00", + "2024-01-05T15:49:00" + ], + "y": [ + 2.9529821999999997, + 2.9529821999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:50:00", + "2024-01-05T15:52:00" + ], + "y": [ + 2.9545790000000003, + 2.9545790000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:53:00", + "2024-01-05T15:55:00" + ], + "y": [ + 2.9461958000000004, + 2.9461958000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:56:00", + "2024-01-05T15:58:00" + ], + "y": [ + 2.9423036, + 2.9423036 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T15:59:00", + "2024-01-05T16:01:00" + ], + "y": [ + 2.9581718, + 2.9581718 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T14:20:00", + "2024-01-05T17:05:00" + ], + "y": [ + 3.0048936503057257, + 2.899 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T16:02:00", + "2024-01-05T17:08:00" + ], + "y": [ + 2.93908109399865, + 2.8949 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T18:08:00", + "2024-01-05T18:10:00" + ], + "y": [ + 2.9213456, + 2.9213456 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T18:11:00", + "2024-01-05T18:13:00" + ], + "y": [ + 2.920148, + 2.920148 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T18:14:00", + "2024-01-05T18:16:00" + ], + "y": [ + 2.9268346000000003, + 2.9268346000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T18:17:00", + "2024-01-05T18:19:00" + ], + "y": [ + 2.9215452, + 2.9215452 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T18:20:00", + "2024-01-05T23:56:00" + ], + "y": [ + 2.885856188319984, + 2.9731 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T23:56:00", + "2024-01-05T23:58:00" + ], + "y": [ + 2.9671538, + 2.9671538 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-05T23:59:00", + "2024-01-06T01:20:00" + ], + "y": [ + 2.937996998631673, + 2.8906 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:20:00", + "2024-01-06T02:22:00" + ], + "y": [ + 2.8590704, + 2.8590704 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:23:00", + "2024-01-06T02:25:00" + ], + "y": [ + 2.8694496, + 2.8694496 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:26:00", + "2024-01-06T02:28:00" + ], + "y": [ + 2.8818248, + 2.8818248 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:29:00", + "2024-01-06T02:31:00" + ], + "y": [ + 2.8826232, + 2.8826232 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:32:00", + "2024-01-06T02:34:00" + ], + "y": [ + 2.8889106, + 2.8889106 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:35:00", + "2024-01-06T02:37:00" + ], + "y": [ + 2.8889106, + 2.8889106 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:38:00", + "2024-01-06T02:40:00" + ], + "y": [ + 2.8925034, + 2.8925034 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:41:00", + "2024-01-06T02:43:00" + ], + "y": [ + 2.8753378, + 2.8753378 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:44:00", + "2024-01-06T02:46:00" + ], + "y": [ + 2.8773338, + 2.8773338 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:47:00", + "2024-01-06T02:49:00" + ], + "y": [ + 2.8780324, + 2.8780324 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:50:00", + "2024-01-06T02:52:00" + ], + "y": [ + 2.8764356, + 2.8764356 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:53:00", + "2024-01-06T02:55:00" + ], + "y": [ + 2.8764356, + 2.8764356 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:56:00", + "2024-01-06T02:58:00" + ], + "y": [ + 2.8746392, + 2.8746392 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T02:59:00", + "2024-01-06T03:01:00" + ], + "y": [ + 2.8722440000000002, + 2.8722440000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:02:00", + "2024-01-06T03:04:00" + ], + "y": [ + 2.8754376, + 2.8754376 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:05:00", + "2024-01-06T03:07:00" + ], + "y": [ + 2.8703478000000002, + 2.8703478000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:08:00", + "2024-01-06T03:10:00" + ], + "y": [ + 2.8802280000000002, + 2.8802280000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:11:00", + "2024-01-06T03:13:00" + ], + "y": [ + 2.875238, + 2.875238 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:14:00", + "2024-01-06T03:16:00" + ], + "y": [ + 2.8730424, + 2.8730424 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:17:00", + "2024-01-06T03:19:00" + ], + "y": [ + 2.8778327999999997, + 2.8778327999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:20:00", + "2024-01-06T03:22:00" + ], + "y": [ + 2.8835214, + 2.8835214 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:23:00", + "2024-01-06T03:25:00" + ], + "y": [ + 2.8835214, + 2.8835214 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:26:00", + "2024-01-06T03:28:00" + ], + "y": [ + 2.885218, + 2.885218 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:29:00", + "2024-01-06T03:31:00" + ], + "y": [ + 2.8834216, + 2.8834216 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:32:00", + "2024-01-06T03:34:00" + ], + "y": [ + 2.881226, + 2.881226 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:35:00", + "2024-01-06T03:37:00" + ], + "y": [ + 2.8854176, + 2.8854176 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:38:00", + "2024-01-06T03:40:00" + ], + "y": [ + 2.8949984, + 2.8949984 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:41:00", + "2024-01-06T03:43:00" + ], + "y": [ + 2.8851182, + 2.8851182 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:44:00", + "2024-01-06T03:46:00" + ], + "y": [ + 2.8794296000000004, + 2.8794296000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-05T17:05:00", + "2024-01-06T04:40:00" + ], + "y": [ + 2.9412906946221136, + 2.8453 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T04:40:00", + "2024-01-06T04:42:00" + ], + "y": [ + 2.8509906, + 2.8509906 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T04:43:00", + "2024-01-06T04:45:00" + ], + "y": [ + 2.8537962, + 2.8537962 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T04:46:00", + "2024-01-06T04:48:00" + ], + "y": [ + 2.8328544, + 2.8328544 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T03:47:00", + "2024-01-06T05:08:00" + ], + "y": [ + 2.8363876965086563, + 2.7926 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T04:49:00", + "2024-01-06T06:50:00" + ], + "y": [ + 2.8295478000000003, + 2.735 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T06:50:00", + "2024-01-06T06:52:00" + ], + "y": [ + 2.7404699999999997, + 2.7404699999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T06:08:00", + "2024-01-06T06:55:00" + ], + "y": [ + 2.7508427166417513, + 2.7072 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T06:53:00", + "2024-01-06T06:55:00" + ], + "y": [ + 2.7547986, + 2.7547986 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T07:55:00", + "2024-01-06T07:57:00" + ], + "y": [ + 2.7580728, + 2.7580728 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T07:58:00", + "2024-01-06T08:00:00" + ], + "y": [ + 2.764959, + 2.764959 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T08:01:00", + "2024-01-06T08:03:00" + ], + "y": [ + 2.7586716, + 2.7586716 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T06:56:00", + "2024-01-06T08:12:00" + ], + "y": [ + 2.73431473680807, + 2.7775 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:12:00", + "2024-01-06T09:14:00" + ], + "y": [ + 2.7909708, + 2.7909708 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:15:00", + "2024-01-06T09:17:00" + ], + "y": [ + 2.7937763999999996, + 2.7937763999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:18:00", + "2024-01-06T09:20:00" + ], + "y": [ + 2.7953796, + 2.7953796 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:21:00", + "2024-01-06T09:23:00" + ], + "y": [ + 2.7947784, + 2.7947784 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:24:00", + "2024-01-06T09:26:00" + ], + "y": [ + 2.7847584, + 2.7847584 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:27:00", + "2024-01-06T09:29:00" + ], + "y": [ + 2.7889668, + 2.7889668 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T08:04:00", + "2024-01-06T16:17:00" + ], + "y": [ + 2.751591281312793, + 2.8344 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T09:30:00", + "2024-01-06T21:30:00" + ], + "y": [ + 2.8161919907080373, + 2.7933 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T21:30:00", + "2024-01-06T21:32:00" + ], + "y": [ + 2.7988866, + 2.7988866 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-06T16:17:00", + "2024-01-06T23:04:00" + ], + "y": [ + 2.7934180983266623, + 2.7484 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:04:00", + "2024-01-07T00:06:00" + ], + "y": [ + 2.7582723999999996, + 2.7582723999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:07:00", + "2024-01-07T00:09:00" + ], + "y": [ + 2.7674540000000003, + 2.7674540000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:10:00", + "2024-01-07T00:12:00" + ], + "y": [ + 2.7662563999999996, + 2.7662563999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:13:00", + "2024-01-07T00:15:00" + ], + "y": [ + 2.7706475999999998, + 2.7706475999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:16:00", + "2024-01-07T00:18:00" + ], + "y": [ + 2.7741406, + 2.7741406 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:19:00", + "2024-01-07T00:21:00" + ], + "y": [ + 2.7871145999999998, + 2.7871145999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:22:00", + "2024-01-07T00:24:00" + ], + "y": [ + 2.7894099999999997, + 2.7894099999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:25:00", + "2024-01-07T00:27:00" + ], + "y": [ + 2.7905078, + 2.7905078 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:28:00", + "2024-01-07T00:30:00" + ], + "y": [ + 2.7797294, + 2.7797294 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:31:00", + "2024-01-07T00:33:00" + ], + "y": [ + 2.7866155999999997, + 2.7866155999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:34:00", + "2024-01-07T00:36:00" + ], + "y": [ + 2.7863162, + 2.7863162 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:37:00", + "2024-01-07T00:39:00" + ], + "y": [ + 2.793901, + 2.793901 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-06T21:33:00", + "2024-01-07T09:32:00" + ], + "y": [ + 2.8216910305243346, + 2.736 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T00:40:00", + "2024-01-07T09:33:00" + ], + "y": [ + 2.7711448662415004, + 2.7262 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T09:32:00", + "2024-01-07T09:34:00" + ], + "y": [ + 2.7414720000000004, + 2.7414720000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:33:00", + "2024-01-07T10:35:00" + ], + "y": [ + 2.7446996, + 2.7446996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:36:00", + "2024-01-07T10:38:00" + ], + "y": [ + 2.7451986, + 2.7451986 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:39:00", + "2024-01-07T10:41:00" + ], + "y": [ + 2.751985, + 2.751985 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:42:00", + "2024-01-07T10:44:00" + ], + "y": [ + 2.7513862, + 2.7513862 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:45:00", + "2024-01-07T10:47:00" + ], + "y": [ + 2.7488912, + 2.7488912 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:48:00", + "2024-01-07T10:50:00" + ], + "y": [ + 2.7471946, + 2.7471946 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:51:00", + "2024-01-07T10:53:00" + ], + "y": [ + 2.7419051999999997, + 2.7419051999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:54:00", + "2024-01-07T10:56:00" + ], + "y": [ + 2.7502883999999996, + 2.7502883999999996 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T10:57:00", + "2024-01-07T10:59:00" + ], + "y": [ + 2.744999, + 2.744999 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T11:00:00", + "2024-01-07T11:02:00" + ], + "y": [ + 2.748991, + 2.748991 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T11:03:00", + "2024-01-07T11:05:00" + ], + "y": [ + 2.7447994000000002, + 2.7447994000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T09:35:00", + "2024-01-07T16:48:00" + ], + "y": [ + 2.7679990593542776, + 2.678 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T11:06:00", + "2024-01-07T16:49:00" + ], + "y": [ + 2.71605311077422, + 2.6731 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T16:48:00", + "2024-01-07T16:50:00" + ], + "y": [ + 2.683356, + 2.683356 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T16:51:00", + "2024-01-07T16:53:00" + ], + "y": [ + 2.6844582, + 2.6844582 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T17:49:00", + "2024-01-07T17:51:00" + ], + "y": [ + 2.7201488, + 2.7201488 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T17:52:00", + "2024-01-07T17:54:00" + ], + "y": [ + 2.7127636, + 2.7127636 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T17:55:00", + "2024-01-07T17:57:00" + ], + "y": [ + 2.7191508, + 2.7191508 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T17:58:00", + "2024-01-07T18:00:00" + ], + "y": [ + 2.7199492, + 2.7199492 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:01:00", + "2024-01-07T18:03:00" + ], + "y": [ + 2.7219452, + 2.7219452 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:04:00", + "2024-01-07T18:06:00" + ], + "y": [ + 2.7277336, + 2.7277336 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:07:00", + "2024-01-07T18:09:00" + ], + "y": [ + 2.7286318, + 2.7286318 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:10:00", + "2024-01-07T18:12:00" + ], + "y": [ + 2.7308274, + 2.7308274 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:13:00", + "2024-01-07T18:15:00" + ], + "y": [ + 2.72953, + 2.72953 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:16:00", + "2024-01-07T18:18:00" + ], + "y": [ + 2.7321248000000002, + 2.7321248000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:19:00", + "2024-01-07T18:21:00" + ], + "y": [ + 2.7308274, + 2.7308274 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:22:00", + "2024-01-07T18:24:00" + ], + "y": [ + 2.7338214, + 2.7338214 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T16:54:00", + "2024-01-07T18:50:00" + ], + "y": [ + 2.7146317290551663, + 2.7582 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T19:50:00", + "2024-01-07T19:52:00" + ], + "y": [ + 2.758005, + 2.758005 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T19:53:00", + "2024-01-07T19:55:00" + ], + "y": [ + 2.7613116, + 2.7613116 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T19:56:00", + "2024-01-07T19:58:00" + ], + "y": [ + 2.7588065999999998, + 2.7588065999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T19:59:00", + "2024-01-07T20:01:00" + ], + "y": [ + 2.7588065999999998, + 2.7588065999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:02:00", + "2024-01-07T20:04:00" + ], + "y": [ + 2.7553997999999997, + 2.7553997999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:05:00", + "2024-01-07T20:07:00" + ], + "y": [ + 2.7589068, + 2.7589068 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:08:00", + "2024-01-07T20:10:00" + ], + "y": [ + 2.766021, + 2.766021 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:11:00", + "2024-01-07T20:13:00" + ], + "y": [ + 2.7702294, + 2.7702294 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:14:00", + "2024-01-07T20:16:00" + ], + "y": [ + 2.7697284000000004, + 2.7697284000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:17:00", + "2024-01-07T20:19:00" + ], + "y": [ + 2.7604098, + 2.7604098 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:20:00", + "2024-01-07T20:22:00" + ], + "y": [ + 2.7563016, + 2.7563016 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:23:00", + "2024-01-07T20:25:00" + ], + "y": [ + 2.7615119999999997, + 2.7615119999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:26:00", + "2024-01-07T20:28:00" + ], + "y": [ + 2.7599088, + 2.7599088 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:29:00", + "2024-01-07T20:31:00" + ], + "y": [ + 2.76051, + 2.76051 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:32:00", + "2024-01-07T20:34:00" + ], + "y": [ + 2.76051, + 2.76051 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:35:00", + "2024-01-07T20:37:00" + ], + "y": [ + 2.7571032, + 2.7571032 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:38:00", + "2024-01-07T20:40:00" + ], + "y": [ + 2.7458808, + 2.7458808 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:41:00", + "2024-01-07T20:43:00" + ], + "y": [ + 2.7458808, + 2.7458808 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:44:00", + "2024-01-07T20:46:00" + ], + "y": [ + 2.7451794, + 2.7451794 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:47:00", + "2024-01-07T20:49:00" + ], + "y": [ + 2.7452796, + 2.7452796 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:50:00", + "2024-01-07T20:52:00" + ], + "y": [ + 2.7468828, + 2.7468828 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:53:00", + "2024-01-07T20:55:00" + ], + "y": [ + 2.7443778, + 2.7443778 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:56:00", + "2024-01-07T20:58:00" + ], + "y": [ + 2.7443778, + 2.7443778 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T20:59:00", + "2024-01-07T21:01:00" + ], + "y": [ + 2.7404699999999997, + 2.7404699999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:02:00", + "2024-01-07T21:04:00" + ], + "y": [ + 2.7389669999999997, + 2.7389669999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:05:00", + "2024-01-07T21:07:00" + ], + "y": [ + 2.7389669999999997, + 2.7389669999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:08:00", + "2024-01-07T21:10:00" + ], + "y": [ + 2.7435762, + 2.7435762 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:11:00", + "2024-01-07T21:13:00" + ], + "y": [ + 2.7482856, + 2.7482856 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:14:00", + "2024-01-07T21:16:00" + ], + "y": [ + 2.7455802, + 2.7455802 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:17:00", + "2024-01-07T21:19:00" + ], + "y": [ + 2.7465821999999998, + 2.7465821999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:20:00", + "2024-01-07T21:22:00" + ], + "y": [ + 2.7402696, + 2.7402696 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:23:00", + "2024-01-07T21:25:00" + ], + "y": [ + 2.7392676, + 2.7392676 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:26:00", + "2024-01-07T21:28:00" + ], + "y": [ + 2.7360612, + 2.7360612 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:29:00", + "2024-01-07T21:31:00" + ], + "y": [ + 2.7367626, + 2.7367626 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:32:00", + "2024-01-07T21:34:00" + ], + "y": [ + 2.7324539999999997, + 2.7324539999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:35:00", + "2024-01-07T21:37:00" + ], + "y": [ + 2.7289470000000002, + 2.7289470000000002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:38:00", + "2024-01-07T21:40:00" + ], + "y": [ + 2.7213318, + 2.7213318 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:41:00", + "2024-01-07T21:43:00" + ], + "y": [ + 2.7133158, + 2.7133158 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:44:00", + "2024-01-07T21:46:00" + ], + "y": [ + 2.7181254, + 2.7181254 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:47:00", + "2024-01-07T21:49:00" + ], + "y": [ + 2.708907, + 2.708907 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:50:00", + "2024-01-07T21:52:00" + ], + "y": [ + 2.7135162, + 2.7135162 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:53:00", + "2024-01-07T21:55:00" + ], + "y": [ + 2.7132156000000003, + 2.7132156000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:56:00", + "2024-01-07T21:58:00" + ], + "y": [ + 2.7157206, + 2.7157206 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T21:59:00", + "2024-01-07T22:01:00" + ], + "y": [ + 2.71542, + 2.71542 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T22:02:00", + "2024-01-07T22:04:00" + ], + "y": [ + 2.706402, + 2.706402 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T18:25:00", + "2024-01-07T22:16:00" + ], + "y": [ + 2.701269992133626, + 2.6568 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T22:05:00", + "2024-01-07T22:35:00" + ], + "y": [ + 2.6947788, + 2.6107 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-07T22:35:00", + "2024-01-08T01:43:00" + ], + "y": [ + 2.6409223393525063, + 2.5472 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-07T23:16:00", + "2024-01-08T01:43:00" + ], + "y": [ + 2.5974924992766555, + 2.5472 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T01:43:00", + "2024-01-08T02:37:00" + ], + "y": [ + 2.5766872420418676, + 2.498 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T02:43:00", + "2024-01-08T02:45:00" + ], + "y": [ + 2.506477, + 2.506477 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T02:46:00", + "2024-01-08T02:48:00" + ], + "y": [ + 2.4837226, + 2.4837226 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T02:49:00", + "2024-01-08T03:24:00" + ], + "y": [ + 2.475088276932536, + 2.435 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T02:37:00", + "2024-01-08T03:25:00" + ], + "y": [ + 2.50633554992277, + 2.4201 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T03:25:00", + "2024-01-08T03:48:00" + ], + "y": [ + 2.4554044877733623, + 2.497 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:24:00", + "2024-01-08T04:26:00" + ], + "y": [ + 2.5033832, + 2.5033832 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:27:00", + "2024-01-08T04:29:00" + ], + "y": [ + 2.5137624, + 2.5137624 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:30:00", + "2024-01-08T04:32:00" + ], + "y": [ + 2.5246406, + 2.5246406 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:33:00", + "2024-01-08T04:35:00" + ], + "y": [ + 2.5250398, + 2.5250398 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:36:00", + "2024-01-08T04:38:00" + ], + "y": [ + 2.5337224000000003, + 2.5337224000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:39:00", + "2024-01-08T04:41:00" + ], + "y": [ + 2.5372154, + 2.5372154 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:42:00", + "2024-01-08T04:44:00" + ], + "y": [ + 2.5361176, + 2.5361176 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:45:00", + "2024-01-08T04:47:00" + ], + "y": [ + 2.5446006, + 2.5446006 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T04:48:00", + "2024-01-08T04:50:00" + ], + "y": [ + 2.5539978000000003, + 2.5539978000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T04:51:00", + "2024-01-08T04:53:00" + ], + "y": [ + 2.5456811999999998, + 2.5456811999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T04:54:00", + "2024-01-08T04:56:00" + ], + "y": [ + 2.5468835999999997, + 2.5468835999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T04:48:00", + "2024-01-08T08:21:00" + ], + "y": [ + 2.5404127279097466, + 2.6193 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T04:57:00", + "2024-01-08T08:21:00" + ], + "y": [ + 2.5790827684475377, + 2.6193 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:21:00", + "2024-01-08T09:23:00" + ], + "y": [ + 2.5938773999999998, + 2.5938773999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:24:00", + "2024-01-08T09:26:00" + ], + "y": [ + 2.5878654, + 2.5878654 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:27:00", + "2024-01-08T09:29:00" + ], + "y": [ + 2.597685, + 2.597685 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:30:00", + "2024-01-08T09:32:00" + ], + "y": [ + 2.599188, + 2.599188 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:33:00", + "2024-01-08T09:35:00" + ], + "y": [ + 2.5849596, + 2.5849596 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T09:36:00", + "2024-01-08T12:01:00" + ], + "y": [ + 2.611042291345295, + 2.6533 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T08:21:00", + "2024-01-08T12:03:00" + ], + "y": [ + 2.581428177020543, + 2.6591 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T12:03:00", + "2024-01-08T12:05:00" + ], + "y": [ + 2.6537818, + 2.6537818 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T12:06:00", + "2024-01-08T12:08:00" + ], + "y": [ + 2.6432029999999997, + 2.6432029999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T12:09:00", + "2024-01-08T12:11:00" + ], + "y": [ + 2.6712468, + 2.6712468 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T12:12:00", + "2024-01-08T12:14:00" + ], + "y": [ + 2.6783326, + 2.6783326 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T13:01:00", + "2024-01-08T13:03:00" + ], + "y": [ + 2.688867, + 2.688867 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T13:04:00", + "2024-01-08T13:06:00" + ], + "y": [ + 2.6905704, + 2.6905704 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T13:07:00", + "2024-01-08T13:09:00" + ], + "y": [ + 2.6820534, + 2.6820534 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T12:15:00", + "2024-01-08T15:52:00" + ], + "y": [ + 2.668383981212223, + 2.7631 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T13:10:00", + "2024-01-08T15:52:00" + ], + "y": [ + 2.714428811449466, + 2.7631 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T16:52:00", + "2024-01-08T16:54:00" + ], + "y": [ + 2.7263417999999997, + 2.7263417999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T16:55:00", + "2024-01-08T16:57:00" + ], + "y": [ + 2.7245382, + 2.7245382 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T16:58:00", + "2024-01-08T17:00:00" + ], + "y": [ + 2.7260412, + 2.7260412 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:01:00", + "2024-01-08T17:03:00" + ], + "y": [ + 2.7217325999999997, + 2.7217325999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:04:00", + "2024-01-08T17:06:00" + ], + "y": [ + 2.7319530000000003, + 2.7319530000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:07:00", + "2024-01-08T17:09:00" + ], + "y": [ + 2.7390672, + 2.7390672 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:10:00", + "2024-01-08T17:12:00" + ], + "y": [ + 2.7377645999999998, + 2.7377645999999998 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:13:00", + "2024-01-08T17:15:00" + ], + "y": [ + 2.7472836, + 2.7472836 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:16:00", + "2024-01-08T17:18:00" + ], + "y": [ + 2.7321534, + 2.7321534 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:19:00", + "2024-01-08T17:21:00" + ], + "y": [ + 2.7371634, + 2.7371634 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T17:22:00", + "2024-01-08T18:32:00" + ], + "y": [ + 2.7646509188602266, + 2.8107 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T15:52:00", + "2024-01-08T18:35:00" + ], + "y": [ + 2.731352070270243, + 2.8183 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T18:35:00", + "2024-01-08T18:37:00" + ], + "y": [ + 2.8126634, + 2.8126634 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T19:32:00", + "2024-01-08T19:34:00" + ], + "y": [ + 2.8457802, + 2.8457802 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T19:35:00", + "2024-01-08T19:37:00" + ], + "y": [ + 2.8445778, + 2.8445778 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T19:38:00", + "2024-01-08T19:40:00" + ], + "y": [ + 2.8512912, + 2.8512912 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T19:41:00", + "2024-01-08T19:43:00" + ], + "y": [ + 2.8409706000000003, + 2.8409706000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-08T19:44:00", + "2024-01-09T00:28:00" + ], + "y": [ + 2.8632536269606677, + 2.7714 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-08T18:38:00", + "2024-01-09T04:19:00" + ], + "y": [ + 2.782281482284082, + 2.734 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T05:19:00", + "2024-01-09T05:21:00" + ], + "y": [ + 2.7588712, + 2.7588712 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T00:28:00", + "2024-01-09T09:15:00" + ], + "y": [ + 2.780647855506791, + 2.686 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T05:22:00", + "2024-01-09T09:20:00" + ], + "y": [ + 2.7263027396983652, + 2.6789 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:20:00", + "2024-01-09T10:22:00" + ], + "y": [ + 2.6929034, + 2.6929034 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:23:00", + "2024-01-09T10:25:00" + ], + "y": [ + 2.6977936, + 2.6977936 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:26:00", + "2024-01-09T10:28:00" + ], + "y": [ + 2.7002886000000004, + 2.7002886000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:29:00", + "2024-01-09T10:31:00" + ], + "y": [ + 2.6945002, + 2.6945002 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:32:00", + "2024-01-09T10:34:00" + ], + "y": [ + 2.694101, + 2.694101 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:35:00", + "2024-01-09T10:37:00" + ], + "y": [ + 2.694101, + 2.694101 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:38:00", + "2024-01-09T10:40:00" + ], + "y": [ + 2.6920052, + 2.6920052 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:41:00", + "2024-01-09T10:43:00" + ], + "y": [ + 2.6990909999999997, + 2.6990909999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:44:00", + "2024-01-09T10:46:00" + ], + "y": [ + 2.696596, + 2.696596 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:47:00", + "2024-01-09T10:49:00" + ], + "y": [ + 2.688612, + 2.688612 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:50:00", + "2024-01-09T10:52:00" + ], + "y": [ + 2.6818256, + 2.6818256 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:53:00", + "2024-01-09T10:55:00" + ], + "y": [ + 2.6824244000000004, + 2.6824244000000004 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:56:00", + "2024-01-09T10:58:00" + ], + "y": [ + 2.6799294, + 2.6799294 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T10:59:00", + "2024-01-09T11:01:00" + ], + "y": [ + 2.680628, + 2.680628 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:02:00", + "2024-01-09T11:04:00" + ], + "y": [ + 2.6880132, + 2.6880132 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:05:00", + "2024-01-09T11:07:00" + ], + "y": [ + 2.6887118, + 2.6887118 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:08:00", + "2024-01-09T11:10:00" + ], + "y": [ + 2.6867158, + 2.6867158 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:11:00", + "2024-01-09T11:13:00" + ], + "y": [ + 2.6867158, + 2.6867158 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:14:00", + "2024-01-09T11:16:00" + ], + "y": [ + 2.6867158, + 2.6867158 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:17:00", + "2024-01-09T11:19:00" + ], + "y": [ + 2.696097, + 2.696097 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:20:00", + "2024-01-09T11:22:00" + ], + "y": [ + 2.6968954000000003, + 2.6968954000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:23:00", + "2024-01-09T11:25:00" + ], + "y": [ + 2.7057776, + 2.7057776 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:26:00", + "2024-01-09T11:28:00" + ], + "y": [ + 2.7073744, + 2.7073744 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:29:00", + "2024-01-09T11:31:00" + ], + "y": [ + 2.7066757999999997, + 2.7066757999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:32:00", + "2024-01-09T11:34:00" + ], + "y": [ + 2.7066757999999997, + 2.7066757999999997 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T09:15:00", + "2024-01-09T12:56:00" + ], + "y": [ + 2.6949628851451397, + 2.5736 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T11:35:00", + "2024-01-09T12:56:00" + ], + "y": [ + 2.6686485770000483, + 2.5736 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T12:56:00", + "2024-01-09T13:17:00" + ], + "y": [ + 2.6111437501481447, + 2.6521 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T13:56:00", + "2024-01-09T13:58:00" + ], + "y": [ + 2.671646, + 2.671646 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T13:59:00", + "2024-01-09T14:01:00" + ], + "y": [ + 2.6762368, + 2.6762368 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:17:00", + "2024-01-09T14:19:00" + ], + "y": [ + 2.6509914, + 2.6509914 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:20:00", + "2024-01-09T14:22:00" + ], + "y": [ + 2.659809, + 2.659809 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:23:00", + "2024-01-09T14:25:00" + ], + "y": [ + 2.6570034, + 2.6570034 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:26:00", + "2024-01-09T14:28:00" + ], + "y": [ + 2.6514924, + 2.6514924 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:29:00", + "2024-01-09T14:31:00" + ], + "y": [ + 2.6554002000000003, + 2.6554002000000003 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T14:02:00", + "2024-01-09T15:01:00" + ], + "y": [ + 2.6520914841225824, + 2.6007 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T16:01:00", + "2024-01-09T16:03:00" + ], + "y": [ + 2.6155584, + 2.6155584 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T14:32:00", + "2024-01-09T19:27:00" + ], + "y": [ + 2.638274350889481, + 2.5529 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T19:27:00", + "2024-01-09T19:29:00" + ], + "y": [ + 2.5580058, + 2.5580058 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T16:04:00", + "2024-01-09T19:35:00" + ], + "y": [ + 2.582413718263249, + 2.5434 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T19:30:00", + "2024-01-09T20:13:00" + ], + "y": [ + 2.5602101999999998, + 2.4761 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T20:13:00", + "2024-01-09T20:15:00" + ], + "y": [ + 2.4810522, + 2.4810522 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T20:35:00", + "2024-01-09T20:37:00" + ], + "y": [ + 2.39021, + 2.39021 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T20:38:00", + "2024-01-09T20:40:00" + ], + "y": [ + 2.3886132, + 2.3886132 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T20:41:00", + "2024-01-09T20:43:00" + ], + "y": [ + 2.3963976, + 2.3963976 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T20:44:00", + "2024-01-09T20:46:00" + ], + "y": [ + 2.396697, + 2.396697 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T20:47:00", + "2024-01-09T21:12:00" + ], + "y": [ + 2.385341568164769, + 2.4622 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T20:16:00", + "2024-01-09T21:14:00" + ], + "y": [ + 2.429938328258007, + 2.4793 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "blue", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T21:12:00", + "2024-01-09T21:14:00" + ], + "y": [ + 2.4572756, + 2.4572756 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T21:15:00", + "2024-01-09T22:29:00" + ], + "y": [ + 2.4254343899033426, + 2.4991 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "red", + "width": 3 + }, + "mode": "lines", + "name": "Sell Executor", + "x": [ + "2024-01-09T22:14:00", + "2024-01-10T00:00:00" + ], + "y": [ + 2.4881716389034567, + 2.4976 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "green", + "width": 3 + }, + "mode": "lines", + "name": "Buy Executor", + "x": [ + "2024-01-09T22:29:00", + "2024-01-10T00:00:00" + ], + "y": [ + 2.47038542174093, + 2.4976 + ], + "type": "scatter", + "xaxis": "x", + "yaxis": "y" + }, + { + "line": { + "color": "gold", + "dash": "dash", + "width": 2 + }, + "mode": "lines", + "name": "Cumulative PNL", + "x": [ + "2024-01-01T00:02:00", + "2024-01-01T01:25:00", + "2024-01-01T01:26:00", + "2024-01-01T02:27:00", + "2024-01-01T02:30:00", + "2024-01-01T02:33:00", + "2024-01-01T02:36:00", + "2024-01-01T02:39:00", + "2024-01-01T02:41:00", + "2024-01-01T02:42:00", + "2024-01-01T02:45:00", + "2024-01-01T02:48:00", + "2024-01-01T11:42:00", + "2024-01-01T11:44:00", + "2024-01-01T11:47:00", + "2024-01-01T11:50:00", + "2024-01-01T11:53:00", + "2024-01-01T15:41:00", + "2024-01-01T15:43:00", + "2024-01-01T15:46:00", + "2024-01-01T20:01:00", + "2024-01-01T20:03:00", + "2024-01-01T20:06:00", + "2024-01-01T20:09:00", + "2024-01-01T20:12:00", + "2024-01-01T23:54:00", + "2024-01-01T23:56:00", + "2024-01-01T23:59:00", + "2024-01-02T00:02:00", + "2024-01-02T01:57:00", + "2024-01-02T02:16:00", + "2024-01-02T06:45:00", + "2024-01-02T09:50:00", + "2024-01-02T09:52:00", + "2024-01-02T10:39:00", + "2024-01-02T10:52:00", + "2024-01-02T11:41:00", + "2024-01-02T11:44:00", + "2024-01-02T13:32:00", + "2024-01-02T14:25:00", + "2024-01-02T14:32:00", + "2024-01-02T14:34:00", + "2024-01-02T16:11:00", + "2024-01-02T16:13:00", + "2024-01-02T16:16:00", + "2024-01-02T16:19:00", + "2024-01-02T16:22:00", + "2024-01-02T16:25:00", + "2024-01-02T21:03:00", + "2024-01-03T00:10:00", + "2024-01-03T00:17:00", + "2024-01-03T01:12:00", + "2024-01-03T01:15:00", + "2024-01-03T01:27:00", + "2024-01-03T01:29:00", + "2024-01-03T04:18:00", + "2024-01-03T05:20:00", + "2024-01-03T05:31:00", + "2024-01-03T11:11:00", + "2024-01-03T11:11:00", + "2024-01-03T11:13:00", + "2024-01-03T12:01:00", + "2024-01-03T12:08:00", + "2024-01-03T12:10:00", + "2024-01-03T12:12:00", + "2024-01-03T12:13:00", + "2024-01-03T12:16:00", + "2024-01-03T12:24:00", + "2024-01-03T12:29:00", + "2024-01-03T12:32:00", + "2024-01-03T13:14:00", + "2024-01-03T13:17:00", + "2024-01-03T13:20:00", + "2024-01-03T13:23:00", + "2024-01-03T13:26:00", + "2024-01-03T13:29:00", + "2024-01-03T13:32:00", + "2024-01-03T13:34:00", + "2024-01-03T13:37:00", + "2024-01-03T13:40:00", + "2024-01-03T14:16:00", + "2024-01-03T14:38:00", + "2024-01-03T15:18:00", + "2024-01-03T15:59:00", + "2024-01-03T16:12:00", + "2024-01-03T17:14:00", + "2024-01-03T17:17:00", + "2024-01-03T17:20:00", + "2024-01-03T17:23:00", + "2024-01-03T17:26:00", + "2024-01-03T17:29:00", + "2024-01-03T18:55:00", + "2024-01-03T19:57:00", + "2024-01-03T20:00:00", + "2024-01-03T20:03:00", + "2024-01-03T21:32:00", + "2024-01-03T21:34:00", + "2024-01-03T21:37:00", + "2024-01-03T21:47:00", + "2024-01-03T22:49:00", + "2024-01-03T23:10:00", + "2024-01-04T00:12:00", + "2024-01-04T00:15:00", + "2024-01-04T01:20:00", + "2024-01-04T01:24:00", + "2024-01-04T02:22:00", + "2024-01-04T02:25:00", + "2024-01-04T02:37:00", + "2024-01-04T03:39:00", + "2024-01-04T03:42:00", + "2024-01-04T03:45:00", + "2024-01-04T09:56:00", + "2024-01-04T10:16:00", + "2024-01-04T11:18:00", + "2024-01-04T11:21:00", + "2024-01-04T11:24:00", + "2024-01-04T11:27:00", + "2024-01-04T11:30:00", + "2024-01-04T11:33:00", + "2024-01-04T11:36:00", + "2024-01-04T11:39:00", + "2024-01-04T15:19:00", + "2024-01-04T15:27:00", + "2024-01-04T15:29:00", + "2024-01-04T15:32:00", + "2024-01-04T15:35:00", + "2024-01-04T15:38:00", + "2024-01-04T16:21:00", + "2024-01-04T16:24:00", + "2024-01-04T16:27:00", + "2024-01-04T16:30:00", + "2024-01-04T16:33:00", + "2024-01-04T16:36:00", + "2024-01-04T16:39:00", + "2024-01-04T16:42:00", + "2024-01-05T01:47:00", + "2024-01-05T01:48:00", + "2024-01-05T01:54:00", + "2024-01-05T02:49:00", + "2024-01-05T02:56:00", + "2024-01-05T02:59:00", + "2024-01-05T14:20:00", + "2024-01-05T14:35:00", + "2024-01-05T15:37:00", + "2024-01-05T15:40:00", + "2024-01-05T15:43:00", + "2024-01-05T15:46:00", + "2024-01-05T15:49:00", + "2024-01-05T15:52:00", + "2024-01-05T15:55:00", + "2024-01-05T15:58:00", + "2024-01-05T16:01:00", + "2024-01-05T17:05:00", + "2024-01-05T17:08:00", + "2024-01-05T18:10:00", + "2024-01-05T18:13:00", + "2024-01-05T18:16:00", + "2024-01-05T18:19:00", + "2024-01-05T23:56:00", + "2024-01-05T23:58:00", + "2024-01-06T01:20:00", + "2024-01-06T02:22:00", + "2024-01-06T02:25:00", + "2024-01-06T02:28:00", + "2024-01-06T02:31:00", + "2024-01-06T02:34:00", + "2024-01-06T02:37:00", + "2024-01-06T02:40:00", + "2024-01-06T02:43:00", + "2024-01-06T02:46:00", + "2024-01-06T02:49:00", + "2024-01-06T02:52:00", + "2024-01-06T02:55:00", + "2024-01-06T02:58:00", + "2024-01-06T03:01:00", + "2024-01-06T03:04:00", + "2024-01-06T03:07:00", + "2024-01-06T03:10:00", + "2024-01-06T03:13:00", + "2024-01-06T03:16:00", + "2024-01-06T03:19:00", + "2024-01-06T03:22:00", + "2024-01-06T03:25:00", + "2024-01-06T03:28:00", + "2024-01-06T03:31:00", + "2024-01-06T03:34:00", + "2024-01-06T03:37:00", + "2024-01-06T03:40:00", + "2024-01-06T03:43:00", + "2024-01-06T03:46:00", + "2024-01-06T04:40:00", + "2024-01-06T04:42:00", + "2024-01-06T04:45:00", + "2024-01-06T04:48:00", + "2024-01-06T05:08:00", + "2024-01-06T06:50:00", + "2024-01-06T06:52:00", + "2024-01-06T06:55:00", + "2024-01-06T06:55:00", + "2024-01-06T07:57:00", + "2024-01-06T08:00:00", + "2024-01-06T08:03:00", + "2024-01-06T08:12:00", + "2024-01-06T09:14:00", + "2024-01-06T09:17:00", + "2024-01-06T09:20:00", + "2024-01-06T09:23:00", + "2024-01-06T09:26:00", + "2024-01-06T09:29:00", + "2024-01-06T16:17:00", + "2024-01-06T21:30:00", + "2024-01-06T21:32:00", + "2024-01-06T23:04:00", + "2024-01-07T00:06:00", + "2024-01-07T00:09:00", + "2024-01-07T00:12:00", + "2024-01-07T00:15:00", + "2024-01-07T00:18:00", + "2024-01-07T00:21:00", + "2024-01-07T00:24:00", + "2024-01-07T00:27:00", + "2024-01-07T00:30:00", + "2024-01-07T00:33:00", + "2024-01-07T00:36:00", + "2024-01-07T00:39:00", + "2024-01-07T09:32:00", + "2024-01-07T09:33:00", + "2024-01-07T09:34:00", + "2024-01-07T10:35:00", + "2024-01-07T10:38:00", + "2024-01-07T10:41:00", + "2024-01-07T10:44:00", + "2024-01-07T10:47:00", + "2024-01-07T10:50:00", + "2024-01-07T10:53:00", + "2024-01-07T10:56:00", + "2024-01-07T10:59:00", + "2024-01-07T11:02:00", + "2024-01-07T11:05:00", + "2024-01-07T16:48:00", + "2024-01-07T16:49:00", + "2024-01-07T16:50:00", + "2024-01-07T16:53:00", + "2024-01-07T17:51:00", + "2024-01-07T17:54:00", + "2024-01-07T17:57:00", + "2024-01-07T18:00:00", + "2024-01-07T18:03:00", + "2024-01-07T18:06:00", + "2024-01-07T18:09:00", + "2024-01-07T18:12:00", + "2024-01-07T18:15:00", + "2024-01-07T18:18:00", + "2024-01-07T18:21:00", + "2024-01-07T18:24:00", + "2024-01-07T18:50:00", + "2024-01-07T19:52:00", + "2024-01-07T19:55:00", + "2024-01-07T19:58:00", + "2024-01-07T20:01:00", + "2024-01-07T20:04:00", + "2024-01-07T20:07:00", + "2024-01-07T20:10:00", + "2024-01-07T20:13:00", + "2024-01-07T20:16:00", + "2024-01-07T20:19:00", + "2024-01-07T20:22:00", + "2024-01-07T20:25:00", + "2024-01-07T20:28:00", + "2024-01-07T20:31:00", + "2024-01-07T20:34:00", + "2024-01-07T20:37:00", + "2024-01-07T20:40:00", + "2024-01-07T20:43:00", + "2024-01-07T20:46:00", + "2024-01-07T20:49:00", + "2024-01-07T20:52:00", + "2024-01-07T20:55:00", + "2024-01-07T20:58:00", + "2024-01-07T21:01:00", + "2024-01-07T21:04:00", + "2024-01-07T21:07:00", + "2024-01-07T21:10:00", + "2024-01-07T21:13:00", + "2024-01-07T21:16:00", + "2024-01-07T21:19:00", + "2024-01-07T21:22:00", + "2024-01-07T21:25:00", + "2024-01-07T21:28:00", + "2024-01-07T21:31:00", + "2024-01-07T21:34:00", + "2024-01-07T21:37:00", + "2024-01-07T21:40:00", + "2024-01-07T21:43:00", + "2024-01-07T21:46:00", + "2024-01-07T21:49:00", + "2024-01-07T21:52:00", + "2024-01-07T21:55:00", + "2024-01-07T21:58:00", + "2024-01-07T22:01:00", + "2024-01-07T22:04:00", + "2024-01-07T22:16:00", + "2024-01-07T22:35:00", + "2024-01-08T01:43:00", + "2024-01-08T01:43:00", + "2024-01-08T02:37:00", + "2024-01-08T02:45:00", + "2024-01-08T02:48:00", + "2024-01-08T03:24:00", + "2024-01-08T03:25:00", + "2024-01-08T03:48:00", + "2024-01-08T04:26:00", + "2024-01-08T04:29:00", + "2024-01-08T04:32:00", + "2024-01-08T04:35:00", + "2024-01-08T04:38:00", + "2024-01-08T04:41:00", + "2024-01-08T04:44:00", + "2024-01-08T04:47:00", + "2024-01-08T04:50:00", + "2024-01-08T04:53:00", + "2024-01-08T04:56:00", + "2024-01-08T08:21:00", + "2024-01-08T08:21:00", + "2024-01-08T09:23:00", + "2024-01-08T09:26:00", + "2024-01-08T09:29:00", + "2024-01-08T09:32:00", + "2024-01-08T09:35:00", + "2024-01-08T12:01:00", + "2024-01-08T12:03:00", + "2024-01-08T12:05:00", + "2024-01-08T12:08:00", + "2024-01-08T12:11:00", + "2024-01-08T12:14:00", + "2024-01-08T13:03:00", + "2024-01-08T13:06:00", + "2024-01-08T13:09:00", + "2024-01-08T15:52:00", + "2024-01-08T15:52:00", + "2024-01-08T16:54:00", + "2024-01-08T16:57:00", + "2024-01-08T17:00:00", + "2024-01-08T17:03:00", + "2024-01-08T17:06:00", + "2024-01-08T17:09:00", + "2024-01-08T17:12:00", + "2024-01-08T17:15:00", + "2024-01-08T17:18:00", + "2024-01-08T17:21:00", + "2024-01-08T18:32:00", + "2024-01-08T18:35:00", + "2024-01-08T18:37:00", + "2024-01-08T19:34:00", + "2024-01-08T19:37:00", + "2024-01-08T19:40:00", + "2024-01-08T19:43:00", + "2024-01-09T00:28:00", + "2024-01-09T04:19:00", + "2024-01-09T05:21:00", + "2024-01-09T09:15:00", + "2024-01-09T09:20:00", + "2024-01-09T10:22:00", + "2024-01-09T10:25:00", + "2024-01-09T10:28:00", + "2024-01-09T10:31:00", + "2024-01-09T10:34:00", + "2024-01-09T10:37:00", + "2024-01-09T10:40:00", + "2024-01-09T10:43:00", + "2024-01-09T10:46:00", + "2024-01-09T10:49:00", + "2024-01-09T10:52:00", + "2024-01-09T10:55:00", + "2024-01-09T10:58:00", + "2024-01-09T11:01:00", + "2024-01-09T11:04:00", + "2024-01-09T11:07:00", + "2024-01-09T11:10:00", + "2024-01-09T11:13:00", + "2024-01-09T11:16:00", + "2024-01-09T11:19:00", + "2024-01-09T11:22:00", + "2024-01-09T11:25:00", + "2024-01-09T11:28:00", + "2024-01-09T11:31:00", + "2024-01-09T11:34:00", + "2024-01-09T12:56:00", + "2024-01-09T12:56:00", + "2024-01-09T13:17:00", + "2024-01-09T13:58:00", + "2024-01-09T14:01:00", + "2024-01-09T14:19:00", + "2024-01-09T14:22:00", + "2024-01-09T14:25:00", + "2024-01-09T14:28:00", + "2024-01-09T14:31:00", + "2024-01-09T15:01:00", + "2024-01-09T16:03:00", + "2024-01-09T19:27:00", + "2024-01-09T19:29:00", + "2024-01-09T19:35:00", + "2024-01-09T20:13:00", + "2024-01-09T20:15:00", + "2024-01-09T20:37:00", + "2024-01-09T20:40:00", + "2024-01-09T20:43:00", + "2024-01-09T20:46:00", + "2024-01-09T21:12:00", + "2024-01-09T21:14:00", + "2024-01-09T21:14:00", + "2024-01-09T22:29:00", + "2024-01-10T00:00:00", + "2024-01-10T00:00:00" + ], + "y": [ + 0.0, + -7.676355559456428, + -5.3156683972648455, + -5.3156683972648455, + -5.3156683972648455, + -5.3156683972648455, + -5.3156683972648455, + -5.3156683972648455, + -12.476731535812071, + -12.476731535812071, + -12.476731535812071, + -12.476731535812071, + -10.253435806665664, + -10.253435806665664, + -10.253435806665664, + -10.253435806665664, + -10.253435806665664, + -10.37071866740942, + -10.37071866740942, + -10.37071866740942, + -3.0644998832586796, + -3.0644998832586796, + -3.0644998832586796, + -3.0644998832586796, + -3.0644998832586796, + -7.389494193958972, + -7.389494193958972, + -7.389494193958972, + -7.389494193958972, + 0.04988951224795857, + -7.509869999885204, + -5.180360580546458, + -12.217927368089391, + -7.070458022783714, + -13.614568875319472, + -13.614568875319472, + -13.614568875319472, + -13.614568875319472, + -11.311856836652863, + -19.011500911278816, + -1.3119530794325072, + -1.3119530794325072, + 0.9561489739808366, + 0.9561489739808366, + 0.9561489739808366, + 0.9561489739808366, + 0.9561489739808366, + 0.9561489739808366, + 12.16734258389018, + 4.826038291487694, + 20.77998129126318, + 20.77998129126318, + 20.77998129126318, + 32.30519979747568, + 32.30519979747568, + 25.333128885744074, + 25.333128885744074, + 33.17995827415365, + 45.160699880957594, + 37.88899286770427, + 37.88899286770427, + 46.19309629891978, + 66.38369941951012, + 66.38369941951012, + 66.38369941951012, + 66.38369941951012, + 66.38369941951012, + 67.16852548889437, + 76.95235195970594, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 71.67811020984897, + 62.6115911288647, + 70.78684454167787, + 70.78684454167787, + 73.37493681838262, + 66.31797124898706, + 66.31797124898706, + 66.31797124898706, + 66.31797124898706, + 66.31797124898706, + 66.31797124898706, + 66.31797124898706, + 58.542072181924325, + 58.542072181924325, + 58.542072181924325, + 58.542072181924325, + 60.759020730489816, + 60.759020730489816, + 60.759020730489816, + 53.463343899803384, + 53.463343899803384, + 46.145889970598404, + 46.145889970598404, + 46.145889970598404, + 39.39519309653596, + 51.87565565147476, + 51.87565565147476, + 51.87565565147476, + 42.90251473235926, + 42.90251473235926, + 42.90251473235926, + 42.90251473235926, + 45.203581084583504, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 38.61341516132381, + 31.076292078750967, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 31.823266563392284, + 24.685544182339083, + 57.59234485225777, + 54.1202892745174, + 54.1202892745174, + 54.1202892745174, + 54.1202892745174, + 62.4484869806114, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 52.13634146984478, + 54.74645656081596, + 48.730781862248705, + 48.730781862248705, + 48.730781862248705, + 48.730781862248705, + 48.730781862248705, + 59.974048220841624, + 59.974048220841624, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 52.22720006305474, + 69.1107008383909, + 69.1107008383909, + 69.1107008383909, + 69.1107008383909, + 62.723921235346864, + 63.53049629392656, + 63.53049629392656, + 57.47925280132176, + 57.47925280132176, + 57.47925280132176, + 57.47925280132176, + 57.47925280132176, + 50.063087331452365, + 50.063087331452365, + 50.063087331452365, + 50.063087331452365, + 50.063087331452365, + 50.063087331452365, + 50.063087331452365, + 54.55209523640904, + 59.46773774060926, + 59.46773774060926, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 51.897136917994175, + 63.19611553261765, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 55.77210090919168, + 72.67748031139159, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 65.30797792973644, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 57.62861414607202, + 49.856749118202416, + 50.60664842500928, + 64.13827480050522, + 55.218707952319114, + 66.54526044979826, + 66.54526044979826, + 66.54526044979826, + 59.99672013105197, + 62.71198639338457, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 55.773731418511595, + 58.0423246179528, + 50.985256489869435, + 50.985256489869435, + 50.985256489869435, + 50.985256489869435, + 50.985256489869435, + 50.985256489869435, + 43.74749003547777, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 59.185097789719315, + 72.30907462501732, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 64.2021497174088, + 56.57617798982417, + 68.1076122226461, + 68.1076122226461, + 68.1076122226461, + 68.1076122226461, + 68.1076122226461, + 68.1076122226461, + 76.04217965199182, + 68.5255234333625, + 68.5255234333625, + 71.16201497777149, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 63.27215309240123, + 66.54723620684138, + 53.32459278261757, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 47.67454193386584, + 38.4610158523242, + 38.4610158523242, + 40.83211356378258, + 40.83211356378258, + 33.87733957079906, + 34.702550746887425, + 34.702550746887425, + 34.702550746887425, + 34.702550746887425, + 34.702550746887425, + 34.702550746887425, + 39.62154167575385, + 30.491442659539775, + 30.491442659539775, + 38.2798783801366, + 37.56899468096446, + 42.269981264627965 + ], + "type": "scatter", + "xaxis": "x2", + "yaxis": "y2" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0.0, + 1.0 + ], + "matches": "x2", + "showticklabels": false, + "rangeslider": { + "visible": false + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.314, + 1.0 + ], + "title": { + "text": "Price" + } + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.0, + 1.0 + ] + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.0, + 0.294 + ], + "title": { + "text": "PNL" + } + }, + "annotations": [ + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Candlestick", + "x": 0.5, + "xanchor": "center", + "xref": "paper", + "y": 1.0, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "PNL Quote", + "x": 0.5, + "xanchor": "center", + "xref": "paper", + "y": 0.294, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "white", + "size": 12 + }, + "margin": { + "l": 20, + "r": 20, + "t": 50, + "b": 20 + }, + "plot_bgcolor": "rgba(0, 0, 0, 0)", + "paper_bgcolor": "rgba(0, 0, 0, 0.1)", + "height": 800, + "width": 1800, + "hovermode": "x unified", + "showlegend": false, + "title": { + "text": "Trading Pair: WLD-USDT" + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" } - }, - "outputs": [], + ], "source": [ - "strategy_analysis.create_base_figure(volume=False, positions=False, trade_pnl=True)\n", - "fig = strategy_analysis.figure()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "fig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "# Now let's see how we can add the SuperTrend to the plot\n", - "\n", - "import plotly.graph_objects as go\n", - "\n", - "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", - "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", - "# Add the SuperTrend line\n", - "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", - " mode='markers',\n", - " name='SuperTrend Short',\n", - " line=dict(color=\"red\")),\n", - " row=1, col=1)\n", - "\n", - "fig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "# To see the trades we will need to select a lower timeframe due the restrictions and speed of the plotly library\n", - "start_time = \"2023-11-03\"\n", - "end_time = \"2023-11-05\"\n", - "\n", - "processed_data_filtered = backtesting_results[\"processed_data\"][\n", - " (backtesting_results[\"processed_data\"][\"timestamp\"] >= start_time) &\n", - " (backtesting_results[\"processed_data\"][\"timestamp\"] <= end_time)\n", - "]\n", - "\n", - "executors_filtered = backtesting_results[\"executors_df\"][\n", - " (backtesting_results[\"executors_df\"][\"timestamp\"] >= start_time) &\n", - " (backtesting_results[\"executors_df\"][\"timestamp\"] <= end_time)\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "executors_filtered" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "\n", - "strategy_analysis = StrategyAnalysis(\n", - " positions=executors_filtered,\n", - " candles_df=processed_data_filtered,\n", - ")\n", - "\n", - "strategy_analysis.create_base_figure(volume=False, positions=True, trade_pnl=True)\n", - "fig = strategy_analysis.figure()\n", - "super_trend_long = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == 1]\n", - "super_trend_short = strategy_analysis.candles_df[strategy_analysis.candles_df[f\"SUPERTd_{length}_{multiplier}\"] == -1]\n", - "# Add the SuperTrend line\n", - "fig.add_trace(go.Scatter(x=super_trend_long.index, y=super_trend_long[f'SUPERT_{length}_{multiplier}'],\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.index, y=super_trend_short[f'SUPERT_{length}_{multiplier}'],\n", - " mode='markers',\n", - " name='SuperTrend Short',\n", - " line=dict(color=\"red\")),\n", - " row=1, col=1)\n", "fig" ] }, @@ -463,18 +36204,1036 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:39:53.988526Z", + "start_time": "2024-05-21T23:39:53.910074Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "hovertemplate": "profitable=False
timestamp=%{x}
net_pnl_quote=%{y}", + "legendgroup": "False", + "marker": { + "color": "#636efa", + "symbol": "circle" + }, + "mode": "markers", + "name": "False", + "orientation": "h", + "showlegend": true, + "x": [ + 1.7040672E12, + 1.7040672E12, + 1.70407548E12, + 1.70407566E12, + 1.70407584E12, + 1.70407602E12, + 1.7040762E12, + 1.70407236E12, + 1.70407638E12, + 1.70407656E12, + 1.70407674E12, + 1.70407692E12, + 1.7040771E12, + 1.70407728E12, + 1.70407998E12, + 1.70408016E12, + 1.7040828E12, + 1.70408298E12, + 1.70408034E12, + 1.70412354E12, + 1.70412372E12, + 1.7041239E12, + 1.70412408E12, + 1.70412426E12, + 1.70412444E12, + 1.70412636E12, + 1.70412654E12, + 1.70412672E12, + 1.7041269E12, + 1.70412708E12, + 1.70412726E12, + 1.70412744E12, + 1.70412762E12, + 1.7041278E12, + 1.70412798E12, + 1.70412816E12, + 1.70412834E12, + 1.70412852E12, + 1.70414106E12, + 1.70414124E12, + 1.70414142E12, + 1.7041416E12, + 1.70414178E12, + 1.70414826E12, + 1.70414844E12, + 1.70414862E12 + ], + "xaxis": "x", + "y": [ + 0.0, + -7.61247867369609, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.014537137929758, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.0967233829772045, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.854400389172675, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "yaxis": "y", + "type": "scatter" + }, + { + "hovertemplate": "profitable=True
timestamp=%{x}
net_pnl_quote=%{y}", + "legendgroup": "True", + "marker": { + "color": "#EF553B", + "symbol": "circle" + }, + "mode": "markers", + "name": "True", + "orientation": "h", + "showlegend": true, + "x": [ + 1.70406738E12, + 1.70407746E12, + 1.70408316E12, + 1.70412462E12, + 1.70414196E12, + 1.7041488E12 + ], + "xaxis": "x", + "y": [ + 2.3606871621915824, + 2.211527553335368, + 7.0853167853100585, + 15.828155020906378, + 1.364377041510068, + 0.12822052055113842 + ], + "yaxis": "y", + "type": "scatter" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "timestamp" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "net_pnl_quote" + } + }, + "legend": { + "title": { + "text": "profitable" + }, + "tracegroupgap": 0 + }, + "title": { + "text": "PNL per Trade" + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import plotly.express as px\n", "\n", - "executors_df = backtesting_results[\"executors_df\"]\n", + "executors_df = pd.DataFrame(ei.to_dict() for ei in backtesting_results[\"executors\"])\n", + "executors_df[\"profitable\"] = executors_df[\"net_pnl_quote\"] > 0\n", "\n", "fig = px.scatter(executors_df, x=\"timestamp\", y='net_pnl_quote', title='PNL per Trade',\n", " color='profitable', color_continuous_scale=['red', 'green'])\n", @@ -496,16 +37255,1011 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:40:07.291321Z", + "start_time": "2024-05-21T23:40:07.285550Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "hovertemplate": "filled_amount_quote=%{x}
net_pnl_quote=%{y}", + "legendgroup": "", + "marker": { + "color": "#636efa", + "symbol": "circle" + }, + "mode": "markers", + "name": "", + "orientation": "v", + "showlegend": false, + "x": [ + 0.0, + 503.3333333333333, + 71.33333333333334, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 496.66666666666674, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 71.52380952380952, + 0.0, + 0.0, + 496.66666666666674, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 503.3333333333333, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 503.3333333333333, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 496.66666666666674, + 0.0, + 0.0, + 0.0, + 239.04761904761904, + 71.33333333333334 + ], + "xaxis": "x", + "y": [ + 0.0, + -7.61247867369609, + 2.3606871621915824, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.014537137929758, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.211527553335368, + 0.0, + 0.0, + -7.0967233829772045, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 7.0853167853100585, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.854400389172675, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 15.828155020906378, + 0.0, + 0.0, + 0.0, + 1.364377041510068, + 0.12822052055113842 + ], + "yaxis": "y", + "type": "scatter" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "filled_amount_quote" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "net_pnl_quote" + } + }, + "legend": { + "tracegroupgap": 0 + }, + "title": { + "text": "Trade Volume vs. PNL" + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "fig = px.scatter(executors_df, x='volume', y='net_pnl_quote', title='Trade Volume vs. PNL')\n", + "fig = px.scatter(executors_df, x='filled_amount_quote', y='net_pnl_quote', title='Trade Volume vs. PNL')\n", "fig.show()" ] }, @@ -524,47 +38278,964 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2024-05-21T23:40:47.289939Z", + "start_time": "2024-05-21T23:40:47.246917Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "alignmentgroup": "True", + "bingroup": "x", + "hovertemplate": "net_pnl_quote=%{x}
count=%{y}", + "legendgroup": "", + "marker": { + "color": "#636efa", + "pattern": { + "shape": "" + } + }, + "name": "", + "offsetgroup": "", + "orientation": "v", + "showlegend": false, + "x": [ + 0.0, + -7.61247867369609, + 2.3606871621915824, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.014537137929758, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.211527553335368, + 0.0, + 0.0, + -7.0967233829772045, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 7.0853167853100585, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.854400389172675, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 15.828155020906378, + 0.0, + 0.0, + 0.0, + 1.364377041510068, + 0.12822052055113842 + ], + "xaxis": "x", + "yaxis": "y", + "type": "histogram" + } + ], + "layout": { + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "net_pnl_quote" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.0, + 1.0 + ], + "title": { + "text": "count" + } + }, + "legend": { + "tracegroupgap": 0 + }, + "title": { + "text": "PNL Distribution" + }, + "barmode": "relative" + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "fig = px.histogram(executors_df, x='net_pnl_quote', title='PNL Distribution')\n", "fig.show()\n" ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "source": [ - "# Conclusion\n", - "We can see that the indicator has potential to bring good signals to trade and might be interesting to see how we can design a market maker that shifts the mid price based on this indicator.\n", - "A lot of the short signals are wrong but if we zoom in into the loss signals we can see that the losses are not that big and the wins are bigger and if we had implemented the trailing stop feature probably a lot of them are going to be profits." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "source": [ - "# Next steps\n", - "- Filter only the loss signals and understand what you can do to prevent them\n", - "- Try different configuration values for the indicator\n", - "- Test in multiple markets, pick mature markets like BTC-USDT or ETH-USDT and also volatile markets like DOGE-USDT or SHIB-USDT" - ] } ], "metadata": { diff --git a/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb b/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb index c09c9b7..24d3db5 100644 --- a/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb +++ b/quants_lab/research_notebooks/dman_maker/03_optimization_notebook.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "outputs": [], "source": [ "import os\n", @@ -12,95 +12,87 @@ "sys.path.append(root_path)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-05-21T23:41:37.862880Z", + "start_time": "2024-05-21T23:41:37.861203Z" + } } }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "outputs": [], "source": [ + "from quants_lab.controllers.market_making.dman_maker_v2 import DManMakerV2Config\n", + "from hummingbot.strategy_v2.backtesting import MarketMakingBacktesting\n", "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", + "import pandas as pd\n", "from optuna import TrialPruned\n", - "from hummingbot.smart_components.utils.order_level_builder import OrderLevelBuilder\n", "\n", - "from quants_lab.controllers.supertrend import SuperTrend, SuperTrendConfig\n", + "backtesting_engine = MarketMakingBacktesting()\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", + "async def objective(trial, start, end, backtesting_resolution):\n", " try:\n", " # Market configuration\n", - " exchange = \"binance_perpetual\"\n", + " connector_name = \"binance_perpetual\"\n", " trading_pair = \"WLD-USDT\"\n", - " interval = \"3m\"\n", "\n", " # Account configuration\n", - " initial_portfolio_usd = 1000\n", - " order_amount = Decimal(\"25\")\n", - " n_levels = 1\n", - " leverage = 20\n", + " total_amount_quote = 1000\n", " trade_cost = 0.0006\n", - "\n", - " # Backtest period\n", - " start = \"2023-01-01\"\n", - " end = \"2024-01-02\"\n", + " buy_spreads = [Decimal(\"0.002\")]\n", + " sell_spreads = [Decimal(\"0.002\")]\n", + " buy_amounts_pct = [Decimal(\"1\")]\n", + " sell_amounts_pct = [Decimal(\"1\")]\n", + " executor_refresh_time = 60\n", + " cooldown_time = 3600\n", + " top_executor_refresh_time = 60\n", + " executor_activation_bounds = [Decimal(\"0.01\")]\n", + " dca_spreads = [Decimal(\"0.\"), Decimal(\"0.002\"), Decimal(\"0.004\"), Decimal(\"0.006\"), Decimal(\"0.008\"), Decimal(\"0.01\")]\n", + " dca_amounts = [Decimal(\"0.1\"), Decimal(\"0.2\"), Decimal(\"0.3\"), Decimal(\"0.4\"), Decimal(\"0.5\"), Decimal(\"0.6\")]\n", "\n", " # Triple barrier configuration\n", - " stop_loss = trial.suggest_float('stop_loss', 0.01, 0.02, step=0.01)\n", + " stop_loss = trial.suggest_float('stop_loss', 0.01, 0.04, 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", + " config_dict = {\n", + " \"connector_name\": connector_name,\n", + " \"trading_pair\": trading_pair,\n", + " \"total_amount_quote\": total_amount_quote,\n", + " \"buy_spreads\": buy_spreads,\n", + " \"sell_spreads\": sell_spreads,\n", + " \"buy_amounts_pct\": buy_amounts_pct,\n", + " \"sell_amounts_pct\": sell_amounts_pct,\n", + " \"executor_refresh_time\": executor_refresh_time,\n", + " \"cooldown_time\": cooldown_time,\n", + " \"top_executor_refresh_time\": top_executor_refresh_time,\n", + " \"executor_activation_bounds\": executor_activation_bounds,\n", + " \"stop_loss\": stop_loss,\n", + " \"take_profit\": take_profit,\n", + " \"time_limit\": time_limit,\n", + " \"trailing_stop\": {\n", + " \"activation_price\": trailing_stop_activation_price_delta,\n", + " \"trailing_delta\": trailing_stop_trailing_delta\n", + " },\n", + " \"dca_spreads\": dca_spreads,\n", + " \"dca_amounts\": dca_amounts\n", + " }\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 = SuperTrendConfig(\n", - " exchange=exchange,\n", - " trading_pair=trading_pair,\n", - " strategy_name='supertrend',\n", - " candles_config=[\n", - " CandlesConfig(connector=exchange, trading_pair=trading_pair,\n", - " interval=interval, 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 = SuperTrend(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", + " config = DManMakerV2Config(**config_dict)\n", + " start_time = pd.to_datetime(start).timestamp() * 1e3\n", + " end_time = pd.to_datetime(end).timestamp() * 1e3\n", + " backtesting_results = await backtesting_engine.run_backtesting(\n", + " controller_config=config, trade_cost=trade_cost,\n", + " start=int(start_time), end=int(end_time),\n", + " backtesting_resolution=backtesting_resolution)\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", @@ -110,50 +102,489 @@ " 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", + " trial.set_user_attr(\"close_types\", strategy_analysis[\"close_types\"])\n", + " trial.set_user_attr(\"config\", config_dict)\n", " return strategy_analysis[\"net_pnl\"]\n", " except Exception as e:\n", " traceback.print_exc()\n", " raise TrialPruned()\n" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-05-22T00:12:09.348323Z", + "start_time": "2024-05-22T00:12:09.343772Z" + } } }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 6, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[I 2024-05-21 19:14:06,636] A new study created in RDB with name: dman_maker_v2_opt\n" + ] + } + ], "source": [ "import optuna\n", "\n", "# Now let's configure the parameters for the optimization\n", - "study_name = \"super_trend_optimization_1\"\n", + "study_name = \"dman_maker_v2_opt\"\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", - " )" + " load_if_exists=True) # If the study already exists, we load it)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-05-22T00:14:06.642119Z", + "start_time": "2024-05-22T00:14:06.258703Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[W 2024-05-21 19:14:32,364] Trial 0 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,365] Trial 0 failed with value .\n", + "/Users/dardonacci/anaconda3/envs/dashboard/lib/python3.10/site-packages/optuna/study/_optimize.py:159: RuntimeWarning: coroutine 'objective' was never awaited\n", + " frozen_trial = _run_trial(study, func, catch)\n", + "RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n", + "[W 2024-05-21 19:14:32,380] Trial 1 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,380] Trial 1 failed with value .\n", + "[W 2024-05-21 19:14:32,390] Trial 2 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,391] Trial 2 failed with value .\n", + "[W 2024-05-21 19:14:32,401] Trial 3 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,402] Trial 3 failed with value .\n", + "[W 2024-05-21 19:14:32,412] Trial 4 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,413] Trial 4 failed with value .\n", + "[W 2024-05-21 19:14:32,424] Trial 5 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,424] Trial 5 failed with value .\n", + "[W 2024-05-21 19:14:32,435] Trial 6 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,435] Trial 6 failed with value .\n", + "[W 2024-05-21 19:14:32,448] Trial 7 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,448] Trial 7 failed with value .\n", + "[W 2024-05-21 19:14:32,464] Trial 8 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,464] Trial 8 failed with value .\n", + "[W 2024-05-21 19:14:32,474] Trial 9 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,474] Trial 9 failed with value .\n", + "[W 2024-05-21 19:14:32,485] Trial 10 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,485] Trial 10 failed with value .\n", + "[W 2024-05-21 19:14:32,496] Trial 11 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,496] Trial 11 failed with value .\n", + "[W 2024-05-21 19:14:32,505] Trial 12 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,505] Trial 12 failed with value .\n", + "[W 2024-05-21 19:14:32,515] Trial 13 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,516] Trial 13 failed with value .\n", + "[W 2024-05-21 19:14:32,525] Trial 14 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,526] Trial 14 failed with value .\n", + "[W 2024-05-21 19:14:32,535] Trial 15 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,535] Trial 15 failed with value .\n", + "[W 2024-05-21 19:14:32,551] Trial 16 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,551] Trial 16 failed with value .\n", + "[W 2024-05-21 19:14:32,561] Trial 17 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,562] Trial 17 failed with value .\n", + "[W 2024-05-21 19:14:32,572] Trial 18 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,572] Trial 18 failed with value .\n", + "[W 2024-05-21 19:14:32,582] Trial 19 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,583] Trial 19 failed with value .\n", + "[W 2024-05-21 19:14:32,592] Trial 20 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,593] Trial 20 failed with value .\n", + "[W 2024-05-21 19:14:32,603] Trial 21 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,603] Trial 21 failed with value .\n", + "[W 2024-05-21 19:14:32,613] Trial 22 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,614] Trial 22 failed with value .\n", + "[W 2024-05-21 19:14:32,624] Trial 23 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,624] Trial 23 failed with value .\n", + "[W 2024-05-21 19:14:32,634] Trial 24 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,634] Trial 24 failed with value .\n", + "[W 2024-05-21 19:14:32,644] Trial 25 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,644] Trial 25 failed with value .\n", + "[W 2024-05-21 19:14:32,654] Trial 26 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,654] Trial 26 failed with value .\n", + "[W 2024-05-21 19:14:32,666] Trial 27 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,666] Trial 27 failed with value .\n", + "[W 2024-05-21 19:14:32,676] Trial 28 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,676] Trial 28 failed with value .\n", + "[W 2024-05-21 19:14:32,687] Trial 29 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,687] Trial 29 failed with value .\n", + "[W 2024-05-21 19:14:32,697] Trial 30 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,698] Trial 30 failed with value .\n", + "[W 2024-05-21 19:14:32,709] Trial 31 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,709] Trial 31 failed with value .\n", + "[W 2024-05-21 19:14:32,718] Trial 32 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,719] Trial 32 failed with value .\n", + "[W 2024-05-21 19:14:32,728] Trial 33 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,728] Trial 33 failed with value .\n", + "[W 2024-05-21 19:14:32,738] Trial 34 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,739] Trial 34 failed with value .\n", + "[W 2024-05-21 19:14:32,749] Trial 35 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,750] Trial 35 failed with value .\n", + "[W 2024-05-21 19:14:32,761] Trial 36 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,762] Trial 36 failed with value .\n", + "[W 2024-05-21 19:14:32,772] Trial 37 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,772] Trial 37 failed with value .\n", + "[W 2024-05-21 19:14:32,782] Trial 38 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,782] Trial 38 failed with value .\n", + "[W 2024-05-21 19:14:32,791] Trial 39 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,792] Trial 39 failed with value .\n", + "[W 2024-05-21 19:14:32,803] Trial 40 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,803] Trial 40 failed with value .\n", + "[W 2024-05-21 19:14:32,813] Trial 41 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,814] Trial 41 failed with value .\n", + "[W 2024-05-21 19:14:32,823] Trial 42 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,824] Trial 42 failed with value .\n", + "[W 2024-05-21 19:14:32,835] Trial 43 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,836] Trial 43 failed with value .\n", + "[W 2024-05-21 19:14:32,846] Trial 44 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,846] Trial 44 failed with value .\n", + "[W 2024-05-21 19:14:32,857] Trial 45 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,857] Trial 45 failed with value .\n", + "[W 2024-05-21 19:14:32,868] Trial 46 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,869] Trial 46 failed with value .\n", + "[W 2024-05-21 19:14:32,878] Trial 47 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,878] Trial 47 failed with value .\n", + "[W 2024-05-21 19:14:32,889] Trial 48 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,889] Trial 48 failed with value .\n", + "[W 2024-05-21 19:14:32,899] Trial 49 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,900] Trial 49 failed with value .\n", + "[W 2024-05-21 19:14:32,911] Trial 50 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,911] Trial 50 failed with value .\n", + "[W 2024-05-21 19:14:32,920] Trial 51 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,920] Trial 51 failed with value .\n", + "[W 2024-05-21 19:14:32,929] Trial 52 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,930] Trial 52 failed with value .\n", + "[W 2024-05-21 19:14:32,940] Trial 53 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,941] Trial 53 failed with value .\n", + "[W 2024-05-21 19:14:32,950] Trial 54 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,950] Trial 54 failed with value .\n", + "[W 2024-05-21 19:14:32,959] Trial 55 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,960] Trial 55 failed with value .\n", + "[W 2024-05-21 19:14:32,970] Trial 56 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,971] Trial 56 failed with value .\n", + "[W 2024-05-21 19:14:32,980] Trial 57 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,981] Trial 57 failed with value .\n", + "[W 2024-05-21 19:14:32,990] Trial 58 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:32,990] Trial 58 failed with value .\n", + "[W 2024-05-21 19:14:32,999] Trial 59 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,000] Trial 59 failed with value .\n", + "[W 2024-05-21 19:14:33,010] Trial 60 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,010] Trial 60 failed with value .\n", + "[W 2024-05-21 19:14:33,019] Trial 61 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,020] Trial 61 failed with value .\n", + "[W 2024-05-21 19:14:33,030] Trial 62 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,030] Trial 62 failed with value .\n", + "[W 2024-05-21 19:14:33,040] Trial 63 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,040] Trial 63 failed with value .\n", + "[W 2024-05-21 19:14:33,050] Trial 64 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,051] Trial 64 failed with value .\n", + "[W 2024-05-21 19:14:33,061] Trial 65 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,061] Trial 65 failed with value .\n", + "[W 2024-05-21 19:14:33,071] Trial 66 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,071] Trial 66 failed with value .\n", + "[W 2024-05-21 19:14:33,080] Trial 67 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,081] Trial 67 failed with value .\n", + "[W 2024-05-21 19:14:33,091] Trial 68 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,092] Trial 68 failed with value .\n", + "[W 2024-05-21 19:14:33,102] Trial 69 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,102] Trial 69 failed with value .\n", + "[W 2024-05-21 19:14:33,111] Trial 70 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,112] Trial 70 failed with value .\n", + "[W 2024-05-21 19:14:33,123] Trial 71 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,124] Trial 71 failed with value .\n", + "[W 2024-05-21 19:14:33,133] Trial 72 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,134] Trial 72 failed with value .\n", + "[W 2024-05-21 19:14:33,144] Trial 73 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,145] Trial 73 failed with value .\n", + "[W 2024-05-21 19:14:33,154] Trial 74 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,154] Trial 74 failed with value .\n", + "[W 2024-05-21 19:14:33,164] Trial 75 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,165] Trial 75 failed with value .\n", + "[W 2024-05-21 19:14:33,175] Trial 76 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,175] Trial 76 failed with value .\n", + "[W 2024-05-21 19:14:33,186] Trial 77 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,186] Trial 77 failed with value .\n", + "[W 2024-05-21 19:14:33,196] Trial 78 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,196] Trial 78 failed with value .\n", + "[W 2024-05-21 19:14:33,206] Trial 79 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,207] Trial 79 failed with value .\n", + "[W 2024-05-21 19:14:33,217] Trial 80 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,218] Trial 80 failed with value .\n", + "[W 2024-05-21 19:14:33,227] Trial 81 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,228] Trial 81 failed with value .\n", + "[W 2024-05-21 19:14:33,239] Trial 82 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,239] Trial 82 failed with value .\n", + "[W 2024-05-21 19:14:33,255] Trial 83 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,255] Trial 83 failed with value .\n", + "[W 2024-05-21 19:14:33,267] Trial 84 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,267] Trial 84 failed with value .\n", + "[W 2024-05-21 19:14:33,278] Trial 85 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,278] Trial 85 failed with value .\n", + "[W 2024-05-21 19:14:33,289] Trial 86 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,289] Trial 86 failed with value .\n", + "[W 2024-05-21 19:14:33,300] Trial 87 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,300] Trial 87 failed with value .\n", + "[W 2024-05-21 19:14:33,310] Trial 88 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,311] Trial 88 failed with value .\n", + "[W 2024-05-21 19:14:33,321] Trial 89 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,322] Trial 89 failed with value .\n", + "[W 2024-05-21 19:14:33,333] Trial 90 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,334] Trial 90 failed with value .\n", + "[W 2024-05-21 19:14:33,345] Trial 91 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,345] Trial 91 failed with value .\n", + "[W 2024-05-21 19:14:33,356] Trial 92 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,357] Trial 92 failed with value .\n", + "[W 2024-05-21 19:14:33,367] Trial 93 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,367] Trial 93 failed with value .\n", + "[W 2024-05-21 19:14:33,376] Trial 94 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,377] Trial 94 failed with value .\n", + "[W 2024-05-21 19:14:33,389] Trial 95 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,389] Trial 95 failed with value .\n", + "[W 2024-05-21 19:14:33,401] Trial 96 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,401] Trial 96 failed with value .\n", + "[W 2024-05-21 19:14:33,413] Trial 97 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,413] Trial 97 failed with value .\n", + "[W 2024-05-21 19:14:33,426] Trial 98 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,427] Trial 98 failed with value .\n", + "[W 2024-05-21 19:14:33,438] Trial 99 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,438] Trial 99 failed with value .\n", + "[W 2024-05-21 19:14:33,449] Trial 100 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,450] Trial 100 failed with value .\n", + "[W 2024-05-21 19:14:33,461] Trial 101 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,461] Trial 101 failed with value .\n", + "[W 2024-05-21 19:14:33,472] Trial 102 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,472] Trial 102 failed with value .\n", + "[W 2024-05-21 19:14:33,483] Trial 103 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,484] Trial 103 failed with value .\n", + "[W 2024-05-21 19:14:33,495] Trial 104 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,495] Trial 104 failed with value .\n", + "[W 2024-05-21 19:14:33,506] Trial 105 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,506] Trial 105 failed with value .\n", + "[W 2024-05-21 19:14:33,517] Trial 106 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,517] Trial 106 failed with value .\n", + "[W 2024-05-21 19:14:33,529] Trial 107 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,530] Trial 107 failed with value .\n", + "[W 2024-05-21 19:14:33,540] Trial 108 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,540] Trial 108 failed with value .\n", + "[W 2024-05-21 19:14:33,550] Trial 109 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,550] Trial 109 failed with value .\n", + "[W 2024-05-21 19:14:33,562] Trial 110 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,563] Trial 110 failed with value .\n", + "[W 2024-05-21 19:14:33,574] Trial 111 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,574] Trial 111 failed with value .\n", + "[W 2024-05-21 19:14:33,590] Trial 112 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,591] Trial 112 failed with value .\n", + "[W 2024-05-21 19:14:33,602] Trial 113 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,602] Trial 113 failed with value .\n", + "[W 2024-05-21 19:14:33,613] Trial 114 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,614] Trial 114 failed with value .\n", + "[W 2024-05-21 19:14:33,624] Trial 115 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,624] Trial 115 failed with value .\n", + "[W 2024-05-21 19:14:33,635] Trial 116 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,635] Trial 116 failed with value .\n", + "[W 2024-05-21 19:14:33,646] Trial 117 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,646] Trial 117 failed with value .\n", + "[W 2024-05-21 19:14:33,657] Trial 118 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,658] Trial 118 failed with value .\n", + "[W 2024-05-21 19:14:33,668] Trial 119 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,669] Trial 119 failed with value .\n", + "[W 2024-05-21 19:14:33,680] Trial 120 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,681] Trial 120 failed with value .\n", + "[W 2024-05-21 19:14:33,690] Trial 121 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,691] Trial 121 failed with value .\n", + "[W 2024-05-21 19:14:33,702] Trial 122 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,702] Trial 122 failed with value .\n", + "[W 2024-05-21 19:14:33,713] Trial 123 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,713] Trial 123 failed with value .\n", + "[W 2024-05-21 19:14:33,726] Trial 124 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,726] Trial 124 failed with value .\n", + "[W 2024-05-21 19:14:33,737] Trial 125 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,737] Trial 125 failed with value .\n", + "[W 2024-05-21 19:14:33,749] Trial 126 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,749] Trial 126 failed with value .\n", + "[W 2024-05-21 19:14:33,761] Trial 127 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,762] Trial 127 failed with value .\n", + "[W 2024-05-21 19:14:33,774] Trial 128 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,774] Trial 128 failed with value .\n", + "[W 2024-05-21 19:14:33,786] Trial 129 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,787] Trial 129 failed with value .\n", + "[W 2024-05-21 19:14:33,797] Trial 130 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,797] Trial 130 failed with value .\n", + "[W 2024-05-21 19:14:33,808] Trial 131 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,808] Trial 131 failed with value .\n", + "[W 2024-05-21 19:14:33,819] Trial 132 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,819] Trial 132 failed with value .\n", + "[W 2024-05-21 19:14:33,830] Trial 133 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,831] Trial 133 failed with value .\n", + "[W 2024-05-21 19:14:33,842] Trial 134 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,843] Trial 134 failed with value .\n", + "[W 2024-05-21 19:14:33,852] Trial 135 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,853] Trial 135 failed with value .\n", + "[W 2024-05-21 19:14:33,862] Trial 136 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,863] Trial 136 failed with value .\n", + "[W 2024-05-21 19:14:33,873] Trial 137 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,873] Trial 137 failed with value .\n", + "[W 2024-05-21 19:14:33,883] Trial 138 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,883] Trial 138 failed with value .\n", + "[W 2024-05-21 19:14:33,893] Trial 139 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,893] Trial 139 failed with value .\n", + "[W 2024-05-21 19:14:33,904] Trial 140 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,904] Trial 140 failed with value .\n", + "[W 2024-05-21 19:14:33,917] Trial 141 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,917] Trial 141 failed with value .\n", + "[W 2024-05-21 19:14:33,927] Trial 142 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,927] Trial 142 failed with value .\n", + "[W 2024-05-21 19:14:33,938] Trial 143 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,938] Trial 143 failed with value .\n", + "[W 2024-05-21 19:14:33,948] Trial 144 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,948] Trial 144 failed with value .\n", + "[W 2024-05-21 19:14:33,958] Trial 145 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,958] Trial 145 failed with value .\n", + "[W 2024-05-21 19:14:33,968] Trial 146 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,969] Trial 146 failed with value .\n", + "[W 2024-05-21 19:14:33,979] Trial 147 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,979] Trial 147 failed with value .\n", + "[W 2024-05-21 19:14:33,988] Trial 148 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,988] Trial 148 failed with value .\n", + "[W 2024-05-21 19:14:33,999] Trial 149 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:33,999] Trial 149 failed with value .\n", + "[W 2024-05-21 19:14:34,011] Trial 150 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,012] Trial 150 failed with value .\n", + "[W 2024-05-21 19:14:34,025] Trial 151 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,026] Trial 151 failed with value .\n", + "[W 2024-05-21 19:14:34,035] Trial 152 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,035] Trial 152 failed with value .\n", + "[W 2024-05-21 19:14:34,045] Trial 153 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,046] Trial 153 failed with value .\n", + "[W 2024-05-21 19:14:34,055] Trial 154 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,056] Trial 154 failed with value .\n", + "[W 2024-05-21 19:14:34,064] Trial 155 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,065] Trial 155 failed with value .\n", + "[W 2024-05-21 19:14:34,074] Trial 156 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,074] Trial 156 failed with value .\n", + "[W 2024-05-21 19:14:34,085] Trial 157 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,085] Trial 157 failed with value .\n", + "[W 2024-05-21 19:14:34,094] Trial 158 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,094] Trial 158 failed with value .\n", + "[W 2024-05-21 19:14:34,104] Trial 159 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,104] Trial 159 failed with value .\n", + "[W 2024-05-21 19:14:34,115] Trial 160 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,115] Trial 160 failed with value .\n", + "[W 2024-05-21 19:14:34,124] Trial 161 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,124] Trial 161 failed with value .\n", + "[W 2024-05-21 19:14:34,134] Trial 162 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,135] Trial 162 failed with value .\n", + "[W 2024-05-21 19:14:34,145] Trial 163 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,145] Trial 163 failed with value .\n", + "[W 2024-05-21 19:14:34,154] Trial 164 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,155] Trial 164 failed with value .\n", + "[W 2024-05-21 19:14:34,165] Trial 165 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,165] Trial 165 failed with value .\n", + "[W 2024-05-21 19:14:34,175] Trial 166 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,176] Trial 166 failed with value .\n", + "[W 2024-05-21 19:14:34,185] Trial 167 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,185] Trial 167 failed with value .\n", + "[W 2024-05-21 19:14:34,196] Trial 168 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,197] Trial 168 failed with value .\n", + "[W 2024-05-21 19:14:34,206] Trial 169 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,207] Trial 169 failed with value .\n", + "[W 2024-05-21 19:14:34,215] Trial 170 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,216] Trial 170 failed with value .\n", + "[W 2024-05-21 19:14:34,225] Trial 171 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,226] Trial 171 failed with value .\n", + "[W 2024-05-21 19:14:34,235] Trial 172 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,236] Trial 172 failed with value .\n", + "[W 2024-05-21 19:14:34,247] Trial 173 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,247] Trial 173 failed with value .\n", + "[W 2024-05-21 19:14:34,258] Trial 174 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,258] Trial 174 failed with value .\n", + "[W 2024-05-21 19:14:34,268] Trial 175 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,268] Trial 175 failed with value .\n", + "[W 2024-05-21 19:14:34,278] Trial 176 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,278] Trial 176 failed with value .\n", + "[W 2024-05-21 19:14:34,288] Trial 177 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,289] Trial 177 failed with value .\n", + "[W 2024-05-21 19:14:34,299] Trial 178 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,299] Trial 178 failed with value .\n", + "[W 2024-05-21 19:14:34,310] Trial 179 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,310] Trial 179 failed with value .\n", + "[W 2024-05-21 19:14:34,319] Trial 180 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,320] Trial 180 failed with value .\n", + "[W 2024-05-21 19:14:34,329] Trial 181 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,330] Trial 181 failed with value .\n", + "[W 2024-05-21 19:14:34,339] Trial 182 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,340] Trial 182 failed with value .\n", + "[W 2024-05-21 19:14:34,349] Trial 183 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,349] Trial 183 failed with value .\n", + "[W 2024-05-21 19:14:34,358] Trial 184 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,359] Trial 184 failed with value .\n", + "[W 2024-05-21 19:14:34,369] Trial 185 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,369] Trial 185 failed with value .\n", + "[W 2024-05-21 19:14:34,378] Trial 186 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,379] Trial 186 failed with value .\n", + "[W 2024-05-21 19:14:34,388] Trial 187 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,389] Trial 187 failed with value .\n", + "[W 2024-05-21 19:14:34,408] Trial 188 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,409] Trial 188 failed with value .\n", + "[W 2024-05-21 19:14:34,421] Trial 189 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,421] Trial 189 failed with value .\n", + "[W 2024-05-21 19:14:34,432] Trial 190 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,433] Trial 190 failed with value .\n", + "[W 2024-05-21 19:14:34,442] Trial 191 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,442] Trial 191 failed with value .\n", + "[W 2024-05-21 19:14:34,452] Trial 192 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,452] Trial 192 failed with value .\n", + "[W 2024-05-21 19:14:34,462] Trial 193 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,463] Trial 193 failed with value .\n", + "[W 2024-05-21 19:14:34,473] Trial 194 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,474] Trial 194 failed with value .\n", + "[W 2024-05-21 19:14:34,484] Trial 195 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,485] Trial 195 failed with value .\n", + "[W 2024-05-21 19:14:34,494] Trial 196 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,495] Trial 196 failed with value .\n", + "[W 2024-05-21 19:14:34,505] Trial 197 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,505] Trial 197 failed with value .\n", + "[W 2024-05-21 19:14:34,515] Trial 198 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,515] Trial 198 failed with value .\n", + "[W 2024-05-21 19:14:34,524] Trial 199 failed with parameters: {} because of the following error: The value could not be cast to float.\n", + "[W 2024-05-21 19:14:34,525] Trial 199 failed with value .\n" + ] + } + ], + "source": [ + "# Not let's run the optimization!\n", + "start = \"2024-05-01\"\n", + "end = \"2024-05-20\"\n", + "backtesting_resolution = \"1m\"\n", + "n_trials = 200\n", + "study.optimize(lambda trial: objective(trial, start, end, backtesting_resolution), n_trials=n_trials)\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-05-22T00:14:34.527783Z", + "start_time": "2024-05-22T00:14:32.359422Z" + } } }, { "cell_type": "code", "execution_count": null, "outputs": [], - "source": [ - "# Not let's run the optimization!\n", - "\n", - "n_trials = 200\n", - "study.optimize(objective, n_trials=n_trials)" - ], + "source": [], "metadata": { "collapsed": false } diff --git a/quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb b/quants_lab/research_notebooks/marroq (old)/01_strategy_design_marroq.ipynb similarity index 100% rename from quants_lab/research_notebooks/marroq/01_strategy_design_marroq.ipynb rename to quants_lab/research_notebooks/marroq (old)/01_strategy_design_marroq.ipynb diff --git a/quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb b/quants_lab/research_notebooks/marroq (old)/02_single_controller_backtest.ipynb similarity index 100% rename from quants_lab/research_notebooks/marroq/02_single_controller_backtest.ipynb rename to quants_lab/research_notebooks/marroq (old)/02_single_controller_backtest.ipynb diff --git a/quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb b/quants_lab/research_notebooks/marroq (old)/03_optimization_notebook.ipynb similarity index 100% rename from quants_lab/research_notebooks/marroq/03_optimization_notebook.ipynb rename to quants_lab/research_notebooks/marroq (old)/03_optimization_notebook.ipynb diff --git a/quants_lab/research_notebooks/supertrend_multitimeframe/01_strategy_design_supertrend_multitimeframe.ipynb b/quants_lab/research_notebooks/supertrend_multitimeframe(old)/01_strategy_design_supertrend_multitimeframe.ipynb similarity index 100% rename from quants_lab/research_notebooks/supertrend_multitimeframe/01_strategy_design_supertrend_multitimeframe.ipynb rename to quants_lab/research_notebooks/supertrend_multitimeframe(old)/01_strategy_design_supertrend_multitimeframe.ipynb diff --git a/quants_lab/research_notebooks/supertrend_multitimeframe/02_controller_backtest.ipynb b/quants_lab/research_notebooks/supertrend_multitimeframe(old)/02_controller_backtest.ipynb similarity index 100% rename from quants_lab/research_notebooks/supertrend_multitimeframe/02_controller_backtest.ipynb rename to quants_lab/research_notebooks/supertrend_multitimeframe(old)/02_controller_backtest.ipynb diff --git a/quants_lab/research_notebooks/supertrend_multitimeframe/03_optimization_notebook.ipynb b/quants_lab/research_notebooks/supertrend_multitimeframe(old)/03_optimization_notebook.ipynb similarity index 100% rename from quants_lab/research_notebooks/supertrend_multitimeframe/03_optimization_notebook.ipynb rename to quants_lab/research_notebooks/supertrend_multitimeframe(old)/03_optimization_notebook.ipynb From ca110ae86dd750dcd132a9bd2eb354715207963a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 22 May 2024 19:55:18 -0500 Subject: [PATCH 111/139] (feat) improve naming --- frontend/components/directional_trading_general_inputs.py | 2 +- frontend/components/market_making_general_inputs.py | 4 ++-- frontend/pages/config/pmm_dynamic/app.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py index 2edbeae..3f1689b 100644 --- a/frontend/components/directional_trading_general_inputs.py +++ b/frontend/components/directional_trading_general_inputs.py @@ -27,7 +27,7 @@ def get_directional_trading_general_inputs(): help="Enter the maximum number of executors per side (e.g., 5).") with c6: cooldown_time = st.number_input("Cooldown Time (minutes)", value=10, - help="Specify the cooldown time in minutes (e.g., 60).") * 60 + help="Time between accepting a new signal in minutes (e.g., 60).") * 60 with c7: position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0, help="Enter the position mode (HEDGE/ONEWAY).") diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py index 7cdc15f..7fa2c9a 100644 --- a/frontend/components/market_making_general_inputs.py +++ b/frontend/components/market_making_general_inputs.py @@ -23,8 +23,8 @@ def get_market_making_general_inputs(custom_candles=False): position_mode = st.selectbox("Position Mode", ("HEDGE", "ONEWAY"), index=0, help="Enter the position mode (HEDGE/ONEWAY).") with c6: - cooldown_time = st.number_input("Cooldown Time (minutes)", value=60, - help="Specify the cooldown time in minutes (e.g., 60).") * 60 + cooldown_time = st.number_input("Stop Loss Cooldown Time (minutes)", value=60, + 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=60, help="Enter the refresh time in minutes for executors (e.g., 60).") * 60 diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py index ecb8840..1fd4ead 100644 --- a/frontend/pages/config/pmm_dynamic/app.py +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -44,7 +44,7 @@ with st.expander("Visualizing PMM Dynamic Indicators", expanded=True): 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="Spreads Multiplier", line=dict(color="red"))], row=4, 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) From 779cfc9e5d9ce7f50ba2ef5eaabcaf14c76904eb Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 23 May 2024 09:56:29 -0500 Subject: [PATCH 112/139] (feat) minor changes --- frontend/pages/config/pmm_dynamic/app.py | 2 +- .../visualization/executors_distribution.py | 23 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py index 1fd4ead..d7cb95a 100644 --- a/frontend/pages/config/pmm_dynamic/app.py +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -81,4 +81,4 @@ if bt_results: st.write("---") render_close_types(bt_results["results"]) st.write("---") -render_save_config("pmm_simple", inputs) +render_save_config("pmm_dynamic", inputs) diff --git a/frontend/visualization/executors_distribution.py b/frontend/visualization/executors_distribution.py index 5f2220c..d5d9f81 100644 --- a/frontend/visualization/executors_distribution.py +++ b/frontend/visualization/executors_distribution.py @@ -78,29 +78,6 @@ def create_executors_distribution_traces(buy_spreads, sell_spreads, buy_amounts_ align='center' ) - # Draw circles around the total volume annotations - fig.add_shape( - type='circle', - xref='x', - yref='y', - x0=-np.mean(buy_spread_distributions) - 0.4, - y0=max_y - 0.05 * max_y, - x1=-np.mean(buy_spread_distributions) + 0.4, - y1=max_y + 0.05 * max_y, - line=dict(color=colors['buy']) - ) - - fig.add_shape( - type='circle', - xref='x', - yref='y', - x0=np.mean(sell_spread_distributions) - 0.4, - y0=max_y - 0.05 * max_y, - x1=np.mean(sell_spread_distributions) + 0.4, - y1=max_y + 0.05 * max_y, - line=dict(color=colors['sell']) - ) - # Apply the theme layout layout_settings = theme.get_default_layout("Market Maker Order Distribution") fig.update_layout(**layout_settings) From cfe5ee429828df4a0e4ed72618d1ca6196f6030e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:48:22 -0500 Subject: [PATCH 113/139] (feat) add backend api features to get configs data --- backend/services/backend_api_client.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py index ebdd8b9..285da80 100644 --- a/backend/services/backend_api_client.py +++ b/backend/services/backend_api_client.py @@ -108,7 +108,8 @@ class BackendAPIClient: """Get the status of a bot.""" url = f"{self.base_url}/get-bot-status/{bot_name}" response = requests.get(url) - return response.json() + if response.status_code == 200: + return response.json()["data"] def get_bot_history(self, bot_name: str): """Get the historical data of a bot.""" @@ -204,3 +205,23 @@ class BackendAPIClient: "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() From 91d02f3f0306df0bc68441355d9516ba85799a8f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:48:32 -0500 Subject: [PATCH 114/139] (feat) refactor perfromance card --- frontend/components/bot_performance_card.py | 296 ++++++++++++++------ 1 file changed, 209 insertions(+), 87 deletions(-) diff --git a/frontend/components/bot_performance_card.py b/frontend/components/bot_performance_card.py index 74d2fa7..d006017 100644 --- a/frontend/components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -2,13 +2,13 @@ from streamlit_elements import mui from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from frontend.components.dashboard import Dashboard -import pandas as pd from backend.services.backend_api_client import BackendAPIClient TRADES_TO_SHOW = 5 -WIDE_COL_WIDTH = 180 -MEDIUM_COL_WIDTH = 140 +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) @@ -21,91 +21,213 @@ def archive_bot(bot_name): backend_api_client.remove_container(bot_name) + class BotPerformanceCardV2(Dashboard.Item): - def __init__(self, board, x, y, w, h, **item_props): - super().__init__(board, x, y, w, h, **item_props) + 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 stop_controllers(self, bot_name): + for controller in self._active_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) + is_running = False + if len(bot_status) > 0: + # Controllers Table + active_controllers_list = [] + stopped_controllers_list = [] + total_global_pnl_quote = 0 + total_volume_traded = 0 + total_open_order_volume = 0 + total_imbalance = 0 + total_unrealized_pnl_quote = 0 + for controller, inner_dict in bot_status.items(): + 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 = inner_dict.get("realized_pnl_quote", 0) + unrealized_pnl_quote = inner_dict.get("unrealized_pnl_quote", 0) + global_pnl_quote = inner_dict.get("global_pnl_quote", 0) + volume_traded = inner_dict.get("volume_traded", 0) + open_order_volume = inner_dict.get("open_order_volume", 0) + imbalance = inner_dict.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 any([value != 0 for value in controller_info.values() if value != controller_info["id"]]): + is_running = True + 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 - def __call__(self, bot_config: dict): - bot_name = bot_config["bot_name"] - status = bot_config.get("status", {"running_status": "not available"}) - is_running = status["running_status"] == "running" - global_performance = status.get("global_performance") - with mui.Card(key=self._key, - sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"}, - elevation=2): - color = "green" if is_running else "grey" - mui.CardHeader( - title=bot_name, - subheader=status["running_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: + 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:.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("📊 NET PNL (%)", variant="h6") + mui.Typography(f"{total_global_pnl_pct:.2%}", 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_controllers(bot_name), + variant="outlined", + color="warning", + sx={"width": "100%", "height": "100%"}): + mui.icon.AddCircleOutline() + mui.Typography("Stop") + 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") + 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}): - # Balances Table - mui.Typography("Balances", variant="h6") - - balances = status.get("total_balances", {}) - if balances: - rows = [(exchange, symbol, round(float(value), 2)) for exchange, inner_dict in balances.items() for symbol, value - in inner_dict.items()] - df_balances = pd.DataFrame(rows, columns=["Exchange", "Currency", "Amount"]).reset_index().rename(columns={"index": "id"}) - - balances_rows = df_balances.to_dict(orient='records') - balances_cols = [{'field': col, 'headerName': col} for col in df_balances.columns] - - for column in balances_cols: - # Customize width for 'exchange' column - if column['field'] == 'Exchange': - column['width'] = WIDE_COL_WIDTH - mui.DataGrid( - rows=balances_rows, - columns=balances_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) - else: - mui.Typography(str(balances), sx={"fontSize": "0.75rem"}) - mui.Divider(sx={"margin": "1rem 0"}) - # Controllers Table - mui.Typography("Controllers", variant="h6", sx={"marginTop": 2}) - controllers = status.get("controllers") - if controllers: - controllers_list = [] - for controller, inner_dict in controllers.items(): - controllers_list.append({ - "Controller ID": controller, - "Realized PNL ($)": inner_dict.get("realized_pnl_quote", 0), - "Unrealized PNL ($)": inner_dict.get("unrealized_pnl_quote", 0), - "GLOBAL PNL ($)": inner_dict.get("global_pnl_quote", 0), - # "global_pnl_pct": inner_dict.get("global_pnl_pct", 0), - "Volume ($)": inner_dict.get("total_volume_traded", 0), - }) - - df_controllers = pd.DataFrame(controllers_list).reset_index().rename(columns={"index": "id"}) - controllers_rows = df_controllers.to_dict(orient='records') - controllers_cols = [{'field': col, 'headerName': col} for col in df_controllers.columns] - for column in controllers_cols: - # Customize width for 'exchange' column - column['width'] = WIDE_COL_WIDTH - mui.DataGrid( - rows=controllers_rows, - columns=controllers_cols, - autoHeight=True, - density="compact", - disableColumnSelector=True, - hideFooter=True, - initialState={"columns": {"columnVisibilityModel": {"id": False}}}) - else: - mui.Typography(str(controllers), sx={"fontSize": "0.75rem"}) - mui.Divider(sx={"margin": "1rem 0"}) - mui.Typography("Global Performance", variant="h6") - if global_performance: - global_pnl_quote = global_performance.get("global_pnl_quote", 0) - global_pnl_pct = global_performance.get("global_pnl_pct", 0) - total_volume_traded = global_performance.get("total_volume_traded", 0) - mui.Typography(f" Global PnL (Quote): {global_pnl_quote} | Global PnL %: {global_pnl_pct} | Total Volume Traded: {total_volume_traded}") - else: - mui.Typography("No global performance data available", sx={"fontSize": "0.75rem"}) + 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"}) From 0e2bfc03f27a03f50dc4ba9cab71c07ac64b8516 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:48:45 -0500 Subject: [PATCH 115/139] (feat) adapt components to support defaults from session state --- frontend/components/config_loader.py | 17 +++++ frontend/components/dca_distribution.py | 65 ++++++++++++------- .../directional_trading_general_inputs.py | 34 +++++++--- frontend/components/executors_distribution.py | 33 ++++++++-- frontend/components/launch_strategy_v2.py | 2 +- .../market_making_general_inputs.py | 33 +++++++--- frontend/components/risk_management.py | 21 ++++-- frontend/components/st_inputs.py | 11 +++- 8 files changed, 156 insertions(+), 60 deletions(-) create mode 100644 frontend/components/config_loader.py diff --git a/frontend/components/config_loader.py b/frontend/components/config_loader.py new file mode 100644 index 0000000..3f5ff1f --- /dev/null +++ b/frontend/components/config_loader.py @@ -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), {}) diff --git a/frontend/components/dca_distribution.py b/frontend/components/dca_distribution.py index c2f2fd0..a6cd060 100644 --- a/frontend/components/dca_distribution.py +++ b/frontend/components/dca_distribution.py @@ -1,22 +1,38 @@ import streamlit as st -from hummingbot.connector.connector_base import OrderType from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs def get_dca_distribution_inputs(): - with st.expander("DCA Distribution", expanded=True): - st.header("DCA Settings") - buy_order_levels = st.number_input("Number of Order Levels", min_value=1, value=3, - help="Enter the number of order levels (e.g., 2).") + 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).") - 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) - 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) + 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, @@ -31,26 +47,27 @@ def get_dca_distribution_inputs(): 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 c1: - sl = st.number_input("Stop Loss (%)", min_value=0.0, max_value=100.0, value=2.0, step=0.1, - help="Enter the stop loss as a percentage (e.g., 2.0 for 2%).") / 100 + # c1, c2, c3, c4, c5 = st.columns(5) with c2: - tp = st.number_input("Take Profit (%)", min_value=0.0, max_value=100.0, value=1.0, step=0.1, + 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=60 * 6, + # 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=1.8, + # 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=0.2, step=0.1, + # 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_spreads": [spread / 100 for spread in spreads_normalized], "dca_amounts": orders_amount_normalized, "stop_loss": sl, "take_profit": tp, diff --git a/frontend/components/directional_trading_general_inputs.py b/frontend/components/directional_trading_general_inputs.py index 3f1689b..5f7b03a 100644 --- a/frontend/components/directional_trading_general_inputs.py +++ b/frontend/components/directional_trading_general_inputs.py @@ -4,31 +4,45 @@ 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="binance", + 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="binance", + 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="WLD-USDT", + 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="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=20, + 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=1, + 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=1000, + 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=5, + 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=10, + 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=0, + 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 diff --git a/frontend/components/executors_distribution.py b/frontend/components/executors_distribution.py index 0dbebbb..0b77495 100644 --- a/frontend/components/executors_distribution.py +++ b/frontend/components/executors_distribution.py @@ -2,30 +2,49 @@ import streamlit as st from frontend.components.st_inputs import get_distribution, normalize, distribution_inputs -def get_executors_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=2, + 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=2, + 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) + 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) + 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) + 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) + 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, diff --git a/frontend/components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py index cd7f256..b372f42 100644 --- a/frontend/components/launch_strategy_v2.py +++ b/frontend/components/launch_strategy_v2.py @@ -58,7 +58,7 @@ class LaunchStrategyV2(Dashboard.Item): "markets": {}, "candles_config": [], "controllers_config": self._controller_config_selected, - "config_update_interval": 20, + "config_update_interval": 10, "script_file_name": "v2_with_controllers.py", "time_to_cash_out": None, } diff --git a/frontend/components/market_making_general_inputs.py b/frontend/components/market_making_general_inputs.py index 7fa2c9a..47b7f5e 100644 --- a/frontend/components/market_making_general_inputs.py +++ b/frontend/components/market_making_general_inputs.py @@ -4,38 +4,51 @@ 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="binance", + 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="WLD-USDT", + 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=20, + 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=1000, + 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=0, + 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=60, + 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=60, + 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="binance", + 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="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: - interval = st.selectbox("Candles Interval", ("1m", "3m", "5m", "15m", "1h", "4h", "1d"), index=1, + 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 diff --git a/frontend/components/risk_management.py b/frontend/components/risk_management.py index 3b0ed71..bcf3ee1 100644 --- a/frontend/components/risk_management.py +++ b/frontend/components/risk_management.py @@ -3,25 +3,36 @@ 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=5.0, step=0.1, + 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=2.0, step=0.1, + 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=60 * 12, + 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=1.8, + 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=0.2, step=0.1, + 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 diff --git a/frontend/components/st_inputs.py b/frontend/components/st_inputs.py index 920bc42..bd1d904 100644 --- a/frontend/components/st_inputs.py +++ b/frontend/components/st_inputs.py @@ -9,7 +9,7 @@ def normalize(values): return [val / total for val in values] -def distribution_inputs(column, dist_type_name, levels=3): +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", @@ -47,8 +47,13 @@ def distribution_inputs(column, dist_type_name, levels=3): step = column.number_input(f"{dist_type_name} End", value=1.0, key=f"{column}_{dist_type_name.lower()}_end") 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 in range(levels)] + 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 From 775b116984afacc7c31351817004977d1c86b50c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:02 -0500 Subject: [PATCH 116/139] (feat) refactor bollinger controller page --- frontend/pages/config/bollinger_v1/app.py | 4 +++- frontend/pages/config/bollinger_v1/user_inputs.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/pages/config/bollinger_v1/app.py b/frontend/pages/config/bollinger_v1/app.py index a8a8b26..9f6ec6d 100644 --- a/frontend/pages/config/bollinger_v1/app.py +++ b/frontend/pages/config/bollinger_v1/app.py @@ -8,6 +8,7 @@ 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 @@ -29,9 +30,10 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B st.text("This tool will let you create a config for Bollinger V1 and visualize the strategy.") -st.write("---") +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) diff --git a/frontend/pages/config/bollinger_v1/user_inputs.py b/frontend/pages/config/bollinger_v1/user_inputs.py index 31964c0..8bc8988 100644 --- a/frontend/pages/config/bollinger_v1/user_inputs.py +++ b/frontend/pages/config/bollinger_v1/user_inputs.py @@ -4,18 +4,23 @@ 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=20, max_value=200, value=100) + 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=5.0, value=2.0) + 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=0.0) + bb_long_threshold = st.number_input("Long Threshold", value=bb_long_threshold) with c4: - bb_short_threshold = st.number_input("Short Threshold", value=1.0) + bb_short_threshold = st.number_input("Short Threshold", value=bb_short_threshold) return { "controller_name": "bollinger_v1", "controller_type": "directional_trading", From 1b578d48a9da3723c19b2654369f30b516361b9d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:07 -0500 Subject: [PATCH 117/139] (feat) refactor dman maker controller page --- frontend/pages/config/dman_maker_v2/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/pages/config/dman_maker_v2/app.py b/frontend/pages/config/dman_maker_v2/app.py index e840b23..7fbd2b0 100644 --- a/frontend/pages/config/dman_maker_v2/app.py +++ b/frontend/pages/config/dman_maker_v2/app.py @@ -3,6 +3,7 @@ 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 @@ -20,7 +21,7 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B # Page content st.text("This tool will let you create a config for D-Man Maker V2 and upload it to the BackendAPI.") -st.write("---") +get_default_config_loader("dman_maker_v2") inputs = user_inputs() with st.expander("Executor Distribution:", expanded=True): @@ -51,6 +52,7 @@ 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( From bdf0be20affbf1f64b8fed8efcbe753b8c0bed23 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:12 -0500 Subject: [PATCH 118/139] (feat) refactor macd bb controller page --- frontend/pages/config/macd_bb_v1/app.py | 4 +++- .../pages/config/macd_bb_v1/user_inputs.py | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/frontend/pages/config/macd_bb_v1/app.py b/frontend/pages/config/macd_bb_v1/app.py index 961d4d5..f061fcc 100644 --- a/frontend/pages/config/macd_bb_v1/app.py +++ b/frontend/pages/config/macd_bb_v1/app.py @@ -7,6 +7,7 @@ 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 @@ -24,9 +25,10 @@ from frontend.visualization.utils import add_traces_to_fig 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) diff --git a/frontend/pages/config/macd_bb_v1/user_inputs.py b/frontend/pages/config/macd_bb_v1/user_inputs.py index aa5d9be..3e7e212 100644 --- a/frontend/pages/config/macd_bb_v1/user_inputs.py +++ b/frontend/pages/config/macd_bb_v1/user_inputs.py @@ -4,24 +4,32 @@ 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=20, max_value=200, value=100) + 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=5.0, value=2.0) + 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=0.2) + bb_long_threshold = st.number_input("Long Threshold", value=bb_long_threshold) with c4: - bb_short_threshold = st.number_input("Short Threshold", value=0.8) + 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=21) + 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=42) + 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=9) + macd_signal = st.number_input("MACD Signal", min_value=1, value=macd_signal) return { "controller_name": "macd_bb_v1", From 5468a16b8e34c2ac5b0051dec8b5056441a97f96 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:18 -0500 Subject: [PATCH 119/139] (feat) refactor pmm dynamic controller page --- frontend/pages/config/pmm_dynamic/app.py | 9 ++++++--- frontend/pages/config/pmm_dynamic/user_inputs.py | 14 +++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/pages/config/pmm_dynamic/app.py b/frontend/pages/config/pmm_dynamic/app.py index d7cb95a..d3e71e5 100644 --- a/frontend/pages/config/pmm_dynamic/app.py +++ b/frontend/pages/config/pmm_dynamic/app.py @@ -4,6 +4,7 @@ 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 @@ -28,6 +29,7 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B # 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") @@ -53,11 +55,12 @@ with st.expander("Visualizing PMM Dynamic Indicators", expanded=True): st.write("### Executors Distribution") st.write("The order distributions are affected by the average NATR. This means that if the first order has a spread of " "1 and the NATR is 0.005, the first order will have a spread of 0.5% of the mid price.") -buy_spread_distributions, sell_spread_distributions, buy_order_amounts_pct, sell_order_amounts_pct = get_executors_distribution_inputs() -inputs["buy_spreads"] = [spread * 100 for spread in buy_spread_distributions] -inputs["sell_spreads"] = [spread * 100 for spread in sell_spread_distributions] +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"]] diff --git a/frontend/pages/config/pmm_dynamic/user_inputs.py b/frontend/pages/config/pmm_dynamic/user_inputs.py index b0b1711..21e7736 100644 --- a/frontend/pages/config/pmm_dynamic/user_inputs.py +++ b/frontend/pages/config/pmm_dynamic/user_inputs.py @@ -1,23 +1,27 @@ 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(): + 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=21) + 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=42) + 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=9) + 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=14) + natr_length = st.number_input("NATR Length", min_value=1, max_value=200, value=natr_length) # Create the config config = { From 3cae3febf22a1781f3d137b1f49d8d0d907dc188 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:22 -0500 Subject: [PATCH 120/139] (feat) refactor pmm simple controller page --- frontend/pages/config/pmm_simple/app.py | 3 ++- frontend/pages/config/pmm_simple/user_inputs.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/pages/config/pmm_simple/app.py b/frontend/pages/config/pmm_simple/app.py index a7b76b5..28b9c75 100644 --- a/frontend/pages/config/pmm_simple/app.py +++ b/frontend/pages/config/pmm_simple/app.py @@ -1,6 +1,7 @@ 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 @@ -18,7 +19,7 @@ backend_api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=B # Page content st.text("This tool will let you create a config for PMM Simple, backtest and upload it to the Backend API.") -# Get user inputs +get_default_config_loader("pmm_simple") inputs = user_inputs() with st.expander("Executor Distribution:", expanded=True): diff --git a/frontend/pages/config/pmm_simple/user_inputs.py b/frontend/pages/config/pmm_simple/user_inputs.py index 3ae5431..69ff319 100644 --- a/frontend/pages/config/pmm_simple/user_inputs.py +++ b/frontend/pages/config/pmm_simple/user_inputs.py @@ -35,5 +35,5 @@ def user_inputs(): "trailing_delta": ts_delta } } - + st.session_state["default_config"] = config return config From 071c7477975128fae6efa47dc8c19313f2a17be0 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:49:39 -0500 Subject: [PATCH 121/139] (feat) improve performance of instances page and add running forever --- frontend/pages/orchestration/instances/app.py | 73 +++++++++++++------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/frontend/pages/orchestration/instances/app.py b/frontend/pages/orchestration/instances/app.py index 1689212..234f7eb 100644 --- a/frontend/pages/orchestration/instances/app.py +++ b/frontend/pages/orchestration/instances/app.py @@ -1,5 +1,8 @@ +import time + import streamlit as st from streamlit_elements import elements, mui +from types import SimpleNamespace from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT from frontend.components.bot_performance_card import BotPerformanceCardV2 @@ -9,42 +12,66 @@ from frontend.st_utils import initialize_st_page # Constants for UI layout CARD_WIDTH = 12 -CARD_HEIGHT = 3 +CARD_HEIGHT = 4 NUM_CARD_COLS = 1 - -def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_HEIGHT, card_height: int = CARD_WIDTH): +def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int = CARD_WIDTH, card_height: int = CARD_HEIGHT): rows = n_cards // cols + 1 x_y = [(x * card_width, y * card_height) for x in range(cols) for y in range(rows)] return sorted(x_y, key=lambda x: (x[1], x[0])) +def update_active_bots(api_client, active_instances_board): + active_bots_response = api_client.get_active_bots_status() + if active_bots_response.get("status") == "success": + current_active_bots = active_bots_response.get("data") + stored_bots = {card[1]: card for card in st.session_state.active_instances_board.bot_cards} + + new_bots = set(current_active_bots.keys()) - set(stored_bots.keys()) + removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys()) + + for bot in new_bots: + x, y = get_grid_positions(1)[0] # Get a new position + card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) + st.session_state.active_instances_board.bot_cards.append((card, bot)) + + for bot in removed_bots: + st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot] + + st.session_state.active_instances_board.bot_cards.sort(key=lambda x: x[1]) # Sort by bot name + initialize_st_page(title="Instances", icon="🦅") api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) - if not api_client.is_docker_running(): st.warning("Docker is not running. Please start Docker and refresh the page.") st.stop() +if "active_instances_board" not in st.session_state: + active_bots_response = api_client.get_active_bots_status() + active_bots = active_bots_response.get("data") + bot_cards = [] + active_instances_board = Dashboard() + if active_bots: + positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): + card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) + bot_cards.append((card, bot)) + st.session_state.active_instances_board = SimpleNamespace( + dashboard=active_instances_board, + bot_cards=bot_cards, + ) +else: + active_instances_board = st.session_state.active_instances_board + update_active_bots(api_client, active_instances_board) -active_bots_response = api_client.get_active_bots_status() -if active_bots_response.get("status") == "success": - with elements("active_instances_board"): - with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): - mui.Typography("🦅 Active Instances", variant="h5") - active_bots = active_bots_response.get("data") - if active_bots: - positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) - active_instances_board = Dashboard() - for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): - card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) - with active_instances_board(): - card(bot_info) - else: - mui.Alert("No active bots found. Please create a new bot.", severity="info", sx={"margin": "1rem"}) - - - - +with elements("active_instances_board"): + with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): + mui.Typography("🏠 Local Instances", variant="h5") + for card, bot in st.session_state.active_instances_board.bot_cards: + with st.session_state.active_instances_board.dashboard(): + card(bot) +while True: + time.sleep(2) + st.rerun() \ No newline at end of file From 75b4c8ec7f4ea703b6e9a8206167c22437400c7a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 01:51:19 -0500 Subject: [PATCH 122/139] (feat) refactor supertrend v1 --- frontend/pages/config/supertrend_v1/app.py | 9 +++++---- frontend/pages/config/supertrend_v1/user_inputs.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/frontend/pages/config/supertrend_v1/app.py b/frontend/pages/config/supertrend_v1/app.py index 0b38f63..af195d3 100644 --- a/frontend/pages/config/supertrend_v1/app.py +++ b/frontend/pages/config/supertrend_v1/app.py @@ -4,6 +4,7 @@ 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 @@ -13,18 +14,18 @@ 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, \ - get_supertrend_traces -from frontend.visualization.signals import get_macdbb_v1_signal_traces, get_supertrend_v1_signal_traces +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) diff --git a/frontend/pages/config/supertrend_v1/user_inputs.py b/frontend/pages/config/supertrend_v1/user_inputs.py index cb7b3b2..05245eb 100644 --- a/frontend/pages/config/supertrend_v1/user_inputs.py +++ b/frontend/pages/config/supertrend_v1/user_inputs.py @@ -4,16 +4,21 @@ 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=20) + 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=3.0) + 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=0.5) / 100 + percentage_threshold = st.number_input("Percentage Threshold (%)", value=percentage_threshold) / 100 return { "controller_name": "supertrend_v1", "controller_type": "directional_trading", From 3e5857b9d06a0aacb36d44a9744cba2a860fe77c Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 23:31:40 -0500 Subject: [PATCH 123/139] (feat) improve conditions in performance card --- frontend/components/bot_performance_card.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/components/bot_performance_card.py b/frontend/components/bot_performance_card.py index d006017..90bc826 100644 --- a/frontend/components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -21,7 +21,6 @@ def archive_bot(bot_name): backend_api_client.remove_container(bot_name) - class BotPerformanceCardV2(Dashboard.Item): DEFAULT_COLUMNS = [ {"field": 'id', "headerName": 'ID', "width": WIDE_COL_WIDTH}, @@ -58,7 +57,7 @@ class BotPerformanceCardV2(Dashboard.Item): controller_configs = backend_api_client.get_all_configs_from_bot(bot_name) bot_status = backend_api_client.get_bot_status(bot_name) is_running = False - if len(bot_status) > 0: + if bot_status and len(bot_status) > 0: # Controllers Table active_controllers_list = [] stopped_controllers_list = [] @@ -86,7 +85,7 @@ class BotPerformanceCardV2(Dashboard.Item): "open_order_volume": open_order_volume, "imbalance": imbalance, } - if any([value != 0 for value in controller_info.values() if value != controller_info["id"]]): + if len(controller_info) > 0: is_running = True if kill_switch_status: stopped_controllers_list.append(controller_info) From 1a5ad0b6ec1276d697c31f255a221420222f766d Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sat, 25 May 2024 23:33:07 -0500 Subject: [PATCH 124/139] (feat) improve active instances page --- frontend/pages/orchestration/instances/app.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/frontend/pages/orchestration/instances/app.py b/frontend/pages/orchestration/instances/app.py index 234f7eb..a163d91 100644 --- a/frontend/pages/orchestration/instances/app.py +++ b/frontend/pages/orchestration/instances/app.py @@ -40,6 +40,7 @@ def update_active_bots(api_client, active_instances_board): st.session_state.active_instances_board.bot_cards.sort(key=lambda x: x[1]) # Sort by bot name + initialize_st_page(title="Instances", icon="🦅") api_client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) @@ -48,19 +49,23 @@ if not api_client.is_docker_running(): st.stop() if "active_instances_board" not in st.session_state: - active_bots_response = api_client.get_active_bots_status() - active_bots = active_bots_response.get("data") - bot_cards = [] - active_instances_board = Dashboard() - if active_bots: - positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) - for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): - card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) - bot_cards.append((card, bot)) - st.session_state.active_instances_board = SimpleNamespace( - dashboard=active_instances_board, - bot_cards=bot_cards, - ) + try: + active_bots_response = api_client.get_active_bots_status() + active_bots = active_bots_response.get("data") + bot_cards = [] + board = Dashboard() + if active_bots: + positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): + card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT) + bot_cards.append((card, bot)) + st.session_state.active_instances_board = SimpleNamespace( + dashboard=board, + bot_cards=bot_cards, + ) + except Exception as e: + st.error(f"Error fetching active bots, reload the page if persists: {e}") + st.rerun() else: active_instances_board = st.session_state.active_instances_board update_active_bots(api_client, active_instances_board) From 06ce36d30483dc65f391c389809777c4044c9bb8 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Sun, 26 May 2024 17:51:47 -0500 Subject: [PATCH 125/139] (feat) add error handling for active instances --- backend/services/backend_api_client.py | 6 +- frontend/components/bot_performance_card.py | 369 ++++++++++-------- frontend/pages/orchestration/instances/app.py | 53 +-- 3 files changed, 236 insertions(+), 192 deletions(-) diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py index 285da80..024a259 100644 --- a/backend/services/backend_api_client.py +++ b/backend/services/backend_api_client.py @@ -109,7 +109,9 @@ class BackendAPIClient: url = f"{self.base_url}/get-bot-status/{bot_name}" response = requests.get(url) if response.status_code == 200: - return response.json()["data"] + 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.""" @@ -127,7 +129,7 @@ class BackendAPIClient: if response.status_code == 200: return response.json() # Successful request else: - return response.json() # Handle errors or no data found + return {"status": "error", "data": "No active bots found"} def get_all_controllers_config(self): """Get all controller configurations.""" diff --git a/frontend/components/bot_performance_card.py b/frontend/components/bot_performance_card.py index 90bc826..c045bc9 100644 --- a/frontend/components/bot_performance_card.py +++ b/frontend/components/bot_performance_card.py @@ -1,3 +1,5 @@ +import time + from streamlit_elements import mui from CONFIG import BACKEND_API_HOST, BACKEND_API_PORT @@ -44,10 +46,17 @@ class BotPerformanceCardV2(Dashboard.Item): def _handle_active_row_selection(self, params, _): self._active_controller_config_selected = params - def stop_controllers(self, bot_name): + 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) @@ -56,166 +65,210 @@ class BotPerformanceCardV2(Dashboard.Item): try: controller_configs = backend_api_client.get_all_configs_from_bot(bot_name) bot_status = backend_api_client.get_bot_status(bot_name) - is_running = False - if bot_status and len(bot_status) > 0: - # Controllers Table - active_controllers_list = [] - stopped_controllers_list = [] - total_global_pnl_quote = 0 - total_volume_traded = 0 - total_open_order_volume = 0 - total_imbalance = 0 - total_unrealized_pnl_quote = 0 - for controller, inner_dict in bot_status.items(): - 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 = inner_dict.get("realized_pnl_quote", 0) - unrealized_pnl_quote = inner_dict.get("unrealized_pnl_quote", 0) - global_pnl_quote = inner_dict.get("global_pnl_quote", 0) - volume_traded = inner_dict.get("volume_traded", 0) - open_order_volume = inner_dict.get("open_order_volume", 0) - imbalance = inner_dict.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 len(controller_info) > 0: - is_running = True - 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 + # 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:.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("📊 NET PNL (%)", variant="h6") - mui.Typography(f"{total_global_pnl_pct:.2%}", 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"}) + status = "Running" + color = "green" + else: + status = "Stopped" + color = "red" - 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_controllers(bot_name), - variant="outlined", - color="warning", - sx={"width": "100%", "height": "100%"}): - mui.icon.AddCircleOutline() - mui.Typography("Stop") - 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") + 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, @@ -229,4 +282,4 @@ class BotPerformanceCardV2(Dashboard.Item): 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"}) + mui.Typography(str(e), sx={"padding": "10px 15px 10px 15px"}) \ No newline at end of file diff --git a/frontend/pages/orchestration/instances/app.py b/frontend/pages/orchestration/instances/app.py index a163d91..d27c555 100644 --- a/frontend/pages/orchestration/instances/app.py +++ b/frontend/pages/orchestration/instances/app.py @@ -21,7 +21,7 @@ def get_grid_positions(n_cards: int, cols: int = NUM_CARD_COLS, card_width: int return sorted(x_y, key=lambda x: (x[1], x[0])) -def update_active_bots(api_client, active_instances_board): +def update_active_bots(api_client): active_bots_response = api_client.get_active_bots_status() if active_bots_response.get("status") == "success": current_active_bots = active_bots_response.get("data") @@ -29,16 +29,12 @@ def update_active_bots(api_client, active_instances_board): new_bots = set(current_active_bots.keys()) - set(stored_bots.keys()) removed_bots = set(stored_bots.keys()) - set(current_active_bots.keys()) - - for bot in new_bots: - x, y = get_grid_positions(1)[0] # Get a new position - card = BotPerformanceCardV2(active_instances_board, x, y, CARD_WIDTH, CARD_HEIGHT) - st.session_state.active_instances_board.bot_cards.append((card, bot)) - for bot in removed_bots: st.session_state.active_instances_board.bot_cards = [card for card in st.session_state.active_instances_board.bot_cards if card[1] != bot] - - st.session_state.active_instances_board.bot_cards.sort(key=lambda x: x[1]) # Sort by bot name + positions = get_grid_positions(len(current_active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for bot, (x, y) in zip(new_bots, positions[:len(new_bots)]): + card = BotPerformanceCardV2(st.session_state.active_instances_board.dashboard, x, y, CARD_WIDTH, CARD_HEIGHT) + st.session_state.active_instances_board.bot_cards.append((card, bot)) initialize_st_page(title="Instances", icon="🦅") @@ -49,26 +45,23 @@ if not api_client.is_docker_running(): st.stop() if "active_instances_board" not in st.session_state: - try: - active_bots_response = api_client.get_active_bots_status() - active_bots = active_bots_response.get("data") - bot_cards = [] - board = Dashboard() - if active_bots: - positions = get_grid_positions(len(active_bots), NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) - for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): - card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT) - bot_cards.append((card, bot)) - st.session_state.active_instances_board = SimpleNamespace( - dashboard=board, - bot_cards=bot_cards, - ) - except Exception as e: - st.error(f"Error fetching active bots, reload the page if persists: {e}") - st.rerun() + active_bots_response = api_client.get_active_bots_status() + bot_cards = [] + board = Dashboard() + st.session_state.active_instances_board = SimpleNamespace( + dashboard=board, + bot_cards=bot_cards, + ) + active_bots = active_bots_response.get("data") + number_of_bots = len(active_bots) + if number_of_bots > 0: + positions = get_grid_positions(number_of_bots, NUM_CARD_COLS, CARD_WIDTH, CARD_HEIGHT) + for (bot, bot_info), (x, y) in zip(active_bots.items(), positions): + bot_status = api_client.get_bot_status(bot) + card = BotPerformanceCardV2(board, x, y, CARD_WIDTH, CARD_HEIGHT) + st.session_state.active_instances_board.bot_cards.append((card, bot)) else: - active_instances_board = st.session_state.active_instances_board - update_active_bots(api_client, active_instances_board) + update_active_bots(api_client) with elements("active_instances_board"): with mui.Paper(sx={"padding": "2rem"}, variant="outlined"): @@ -76,7 +69,3 @@ with elements("active_instances_board"): for card, bot in st.session_state.active_instances_board.bot_cards: with st.session_state.active_instances_board.dashboard(): card(bot) - -while True: - time.sleep(2) - st.rerun() \ No newline at end of file From 84a10812d23ac9b96ecf713e55eafb2406b7d0eb Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 27 May 2024 17:55:29 -0500 Subject: [PATCH 126/139] (feat) update reamdes --- frontend/pages/config/bollinger_v1/README.md | 76 +++++++++++++--- frontend/pages/config/dman_maker_v2/README.md | 70 ++++++++++++--- frontend/pages/config/macd_bb_v1/README.md | 89 ++++++++++++++++--- frontend/pages/config/pmm_dynamic/README.md | 71 ++++++++++++--- frontend/pages/config/pmm_simple/README.md | 58 +++++++++--- frontend/pages/config/supertrend_v1/README.md | 81 ++++++++++++++--- .../pages/config/xemm_controller/README.md | 69 +++++++++++--- 7 files changed, 416 insertions(+), 98 deletions(-) diff --git a/frontend/pages/config/bollinger_v1/README.md b/frontend/pages/config/bollinger_v1/README.md index 2fa8d53..0f0918a 100644 --- a/frontend/pages/config/bollinger_v1/README.md +++ b/frontend/pages/config/bollinger_v1/README.md @@ -1,19 +1,67 @@ -# D-Man Maker V2 +# Bollinger V1 Configuration Tool + +Welcome to the Bollinger V1 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the Bollinger V1 directional trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the Bollinger V1 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Bollinger Bands Length**: Define the length of the Bollinger Bands. +- **Standard Deviation Multiplier**: Set the standard deviation multiplier for the Bollinger Bands. +- **Long Threshold**: Configure the threshold for long positions. +- **Short Threshold**: Configure the threshold for short positions. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Bollinger Bands + +Visualize the Bollinger Bands on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the Bollinger Bands: + +- **Bollinger Bands Length**: A larger length will make the Bollinger Bands wider and smoother, while a smaller length will make them narrower and more volatile. +- **Long Threshold**: This is a reference to the Bollinger Band. A value of 0 means the lower band, and a value of 1 means the upper band. For example, if the long threshold is 0, long positions will only be taken if the price is below the lower band. +- **Short Threshold**: Similarly, a value of 1.1 means the price must be above the upper band by 0.1 of the band’s range to take a short position. +- **Thresholds**: The closer you set the thresholds to 0.5, the more trades will be executed. The farther away they are, the fewer trades will be executed. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/frontend/pages/config/dman_maker_v2/README.md b/frontend/pages/config/dman_maker_v2/README.md index 2fa8d53..da98f61 100644 --- a/frontend/pages/config/dman_maker_v2/README.md +++ b/frontend/pages/config/dman_maker_v2/README.md @@ -1,19 +1,61 @@ -# D-Man Maker V2 +# D-Man Maker V2 Configuration Tool + +Welcome to the D-Man Maker V2 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the D-Man Maker V2 trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the D-Man Maker V2 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Buy/Sell Spread Distributions**: Configure the distribution of buy and sell spreads. +- **Order Amounts**: Specify the percentages for buy and sell order amounts. +- **Custom D-Man Maker V2 Settings**: Set specific parameters like top executor refresh time and activation bounds. + +### 3. Executor Distribution Visualization + +Visualize the distribution of your trading executors. This helps you understand how your buy and sell orders are spread across different price levels and amounts. + +### 4. DCA Distribution + +After setting the executor distribution, you will need to configure the internal distribution of the DCA (Dollar Cost Averaging). This involves multiple open orders and one close order per executor level. Visualize the DCA distribution to see how the entry prices are spread and ensure the initial DCA order amounts are above the minimum order size of the exchange. + +### 5. Risk Management + +Configure risk management settings, including take profit, stop loss, time limit, and trailing stop settings for each DCA. This step is crucial for managing your trades and minimizing risk. + +### 6. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 7. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/frontend/pages/config/macd_bb_v1/README.md b/frontend/pages/config/macd_bb_v1/README.md index 2fa8d53..b7e7adb 100644 --- a/frontend/pages/config/macd_bb_v1/README.md +++ b/frontend/pages/config/macd_bb_v1/README.md @@ -1,19 +1,80 @@ -# D-Man Maker V2 +# MACD BB V1 Configuration Tool + +Welcome to the MACD BB V1 Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the MACD BB V1 directional trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the MACD BB V1 strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Bollinger Bands Length**: Define the length of the Bollinger Bands. +- **Standard Deviation Multiplier**: Set the standard deviation multiplier for the Bollinger Bands. +- **Long Threshold**: Configure the threshold for long positions. +- **Short Threshold**: Configure the threshold for short positions. +- **MACD Fast**: Set the fast period for the MACD indicator. +- **MACD Slow**: Set the slow period for the MACD indicator. +- **MACD Signal**: Set the signal period for the MACD indicator. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Indicators + +Visualize the Bollinger Bands and MACD on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the indicators: + +- **Bollinger Bands Length**: A larger length will make the Bollinger Bands wider and smoother, while a smaller length will make them narrower and more volatile. +- **Long Threshold**: This is a reference to the Bollinger Band. A value of 0 means the lower band, and a value of 1 means the upper band. For example, if the long threshold is 0, long positions will only be taken if the price is below the lower band. +- **Short Threshold**: Similarly, a value of 1.1 means the price must be above the upper band by 0.1 of the band’s range to take a short position. +- **Thresholds**: The closer you set the thresholds to 0.5, the more trades will be executed. The farther away they are, the fewer trades will be executed. +- **MACD**: The MACD is used to determine trend changes. If the MACD value is negative and the histogram becomes positive, it signals a market trend up, suggesting a long position. Conversely, if the MACD value is positive and the histogram becomes negative, it signals a market trend down, suggesting a short position. + +### Combining MACD and Bollinger Bands for Trade Signals + +The MACD BB V1 strategy uses the MACD to identify potential trend changes and the Bollinger Bands to filter these signals: + +- **Long Signal**: The MACD value must be negative, and the histogram must become positive, indicating a potential uptrend. The price must also be below the long threshold of the Bollinger Bands (e.g., below the lower band if the threshold is 0). +- **Short Signal**: The MACD value must be positive, and the histogram must become negative, indicating a potential downtrend. The price must also be above the short threshold of the Bollinger Bands (e.g., above the upper band if the threshold is 1.1). + +This combination ensures that you only take trend-following trades when the market is already deviated from the mean, enhancing the effectiveness of your trading strategy. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/frontend/pages/config/pmm_dynamic/README.md b/frontend/pages/config/pmm_dynamic/README.md index 2fa8d53..c16ce62 100644 --- a/frontend/pages/config/pmm_dynamic/README.md +++ b/frontend/pages/config/pmm_dynamic/README.md @@ -1,19 +1,62 @@ -# D-Man Maker V2 +# PMM Dynamic Configuration Tool + +Welcome to the PMM Dynamic Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the PMM Dynamic trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the PMM Dynamic strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **MACD Fast Period**: Set the fast period for the MACD indicator. +- **MACD Slow Period**: Set the slow period for the MACD indicator. +- **MACD Signal Period**: Set the signal period for the MACD indicator. +- **NATR Length**: Define the length for the NATR indicator. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Indicator Visualization + +Visualize the candlestick data along with the MACD and NATR indicators. This helps you understand how the MACD will shift the mid-price and how the NATR will be used as a base multiplier for spreads. + +### 4. Executor Distribution + +The distribution of orders is now a multiplier of the base spread, which is determined by the NATR indicator. This allows the algorithm to adapt to changing market conditions by adjusting the spread based on the average size of the candles. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/frontend/pages/config/pmm_simple/README.md b/frontend/pages/config/pmm_simple/README.md index 2fa8d53..4b3640d 100644 --- a/frontend/pages/config/pmm_simple/README.md +++ b/frontend/pages/config/pmm_simple/README.md @@ -1,19 +1,49 @@ -# D-Man Maker V2 +# PMM Simple Configuration Tool + +Welcome to the PMM Simple Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the PMM Simple trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the PMM Simple strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Position Mode**: Choose between different position modes. +- **Cooldown Time**: Set the cooldown period between trades. +- **Executor Refresh Time**: Define how often the executors refresh. +- **Buy/Sell Spread Distributions**: Configure the distribution of buy and sell spreads. +- **Order Amounts**: Specify the percentages for buy and sell order amounts. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Executor Distribution Visualization + +Visualize the distribution of your trading executors. This helps you understand how your buy and sell orders are spread across different price levels and amounts. + +### 4. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 5. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. \ No newline at end of file diff --git a/frontend/pages/config/supertrend_v1/README.md b/frontend/pages/config/supertrend_v1/README.md index 2fa8d53..f93bf3c 100644 --- a/frontend/pages/config/supertrend_v1/README.md +++ b/frontend/pages/config/supertrend_v1/README.md @@ -1,19 +1,72 @@ -# D-Man Maker V2 +# Super Trend Configuration Tool + +Welcome to the Super Trend Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the Super Trend directional trading strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the Super Trend strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Connector Name**: Select the trading platform or exchange. +- **Trading Pair**: Choose the cryptocurrency trading pair. +- **Leverage**: Set the leverage ratio. (Note: if you are using spot trading, set the leverage to 1) +- **Total Amount (Quote Currency)**: Define the total amount you want to allocate for trading. +- **Max Executors per Side**: Specify the maximum number of executors per side. +- **Cooldown Time**: Set the cooldown period between trades. +- **Position Mode**: Choose between different position modes. +- **Candles Connector**: Select the data source for candlestick data. +- **Candles Trading Pair**: Choose the trading pair for candlestick data. +- **Interval**: Set the interval for candlestick data. +- **Super Trend Length**: Define the length of the Super Trend indicator. +- **Super Trend Multiplier**: Set the multiplier for the Super Trend indicator. +- **Percentage Threshold**: Set the percentage threshold for signal generation. +- **Risk Management**: Set parameters for stop loss, take profit, time limit, and trailing stop settings. + +### 3. Visualize Indicators + +Visualize the Super Trend indicator on the OHLC (Open, High, Low, Close) chart to see the impact of your configuration. Here are some hints to help you fine-tune the indicators: + +- **Super Trend Length**: A larger length will make the Super Trend indicator smoother and less sensitive to short-term price fluctuations, while a smaller length will make it more responsive to recent price changes. +- **Super Trend Multiplier**: Adjusting the multiplier affects the sensitivity of the Super Trend indicator. A higher multiplier makes the trend detection more conservative, while a lower multiplier makes it more aggressive. +- **Percentage Threshold**: This defines how close the price needs to be to the Super Trend band to generate a signal. For example, a 0.5% threshold means the price needs to be within 0.5% of the Super Trend band to consider a trade. + +### Combining Super Trend and Percentage Threshold for Trade Signals + +The Super Trend V1 strategy uses the Super Trend indicator combined with a percentage threshold to generate trade signals: + +- **Long Signal**: The Super Trend indicator must signal a long trend, and the price must be within the percentage threshold of the Super Trend long band. For example, if the threshold is 0.5%, the price must be within 0.5% of the Super Trend long band to trigger a long trade. +- **Short Signal**: The Super Trend indicator must signal a short trend, and the price must be within the percentage threshold of the Super Trend short band. Similarly, if the threshold is 0.5%, the price must be within 0.5% of the Super Trend short band to trigger a short trade. + +### 4. Executor Distribution + +The total amount in the quote currency will be distributed among the maximum number of executors per side. For example, if the total amount quote is 1000 and the max executors per side is 5, each executor will have 200 to trade. If the signal is on, the first executor will place an order and wait for the cooldown time before the next one executes, continuing this pattern for the subsequent orders. + +### 5. Backtesting + +Run backtests to evaluate the performance of your configured strategy. The backtesting section allows you to: + +- **Process Data**: Analyze historical trading data. +- **Visualize Results**: See performance metrics and charts. +- **Evaluate Accuracy**: Assess the accuracy of your strategy’s predictions and trades. +- **Understand Close Types**: Review different types of trade closures and their frequencies. + +### 6. Save Configuration + +Once you are satisfied with your configuration and backtest results, save the configuration for future use in the Deploy tab. This allows you to deploy the same strategy later without having to reconfigure it from scratch. + +--- + +Feel free to experiment with different configurations to find the optimal setup for your trading strategy. Happy trading! \ No newline at end of file diff --git a/frontend/pages/config/xemm_controller/README.md b/frontend/pages/config/xemm_controller/README.md index 2fa8d53..a5ae110 100644 --- a/frontend/pages/config/xemm_controller/README.md +++ b/frontend/pages/config/xemm_controller/README.md @@ -1,19 +1,60 @@ -# D-Man Maker V2 +# XEMM Configuration Tool + +Welcome to the XEMM Configuration Tool! This tool allows you to create, modify, visualize, backtest, and save configurations for the XEMM (Cross-Exchange Market Making) strategy. Here’s how you can make the most out of it. ## Features -- **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. +- **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. -## 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. +## How to Use -For more detailed documentation on the backend API and additional configurations, please refer to the project's documentation or contact the development team. \ No newline at end of file +### 1. Load Default Configuration + +Start by loading the default configuration for the XEMM strategy. This provides a baseline setup that you can customize to fit your needs. + +### 2. User Inputs + +Input various parameters for the strategy configuration. These parameters include: + +- **Maker Connector**: Select the maker trading platform or exchange where limit orders will be placed. +- **Maker Trading Pair**: Choose the trading pair on the maker exchange. +- **Taker Connector**: Select the taker trading platform or exchange where market orders will be executed to hedge the imbalance. +- **Taker Trading Pair**: Choose the trading pair on the taker exchange. +- **Min Profitability**: Set the minimum profitability percentage at which orders will be refreshed to avoid risking liquidity. +- **Max Profitability**: Set the maximum profitability percentage at which orders will be refreshed to avoid being too far from the mid-price. +- **Buy Maker Levels**: Specify the number of buy maker levels. +- **Buy Targets and Amounts**: Define the target profitability and amounts for each buy maker level. +- **Sell Maker Levels**: Specify the number of sell maker levels. +- **Sell Targets and Amounts**: Define the target profitability and amounts for each sell maker level. + +### 3. Visualize Order Distribution + +Visualize the order distribution with profitability targets using Plotly charts. This helps you understand how your buy and sell orders are distributed across different profitability levels. + +### Min and Max Profitability + +The XEMM strategy uses min and max profitability bounds to manage the placement of limit orders: + +- **Min Profitability**: If the expected profitability of a limit order drops below this value, the order will be refreshed to avoid risking liquidity. +- **Max Profitability**: If the expected profitability of a limit order exceeds this value, the order will be refreshed to avoid being too far from the mid-price. + +### Combining Profitability Targets and Order Amounts + +- **Buy Orders**: Configure the target profitability and amounts for each buy maker level. The orders will be refreshed if they fall outside the min and max profitability bounds. +- **Sell Orders**: Similarly, configure the target profitability and amounts for each sell maker level, with orders being refreshed based on the profitability bounds. + +### 4. Save and Download Configuration + +Once you have configured your strategy, you can save and download the configuration as a YAML file. This allows you to deploy the strategy later without having to reconfigure it from scratch. + +### 5. Upload Configuration to Backend API + +You can also upload the configuration directly to the Backend API for immediate deployment. This ensures that your strategy is ready to be executed in real-time. + +## Conclusion + +By following these steps, you can efficiently configure your XEMM strategy, visualize its potential performance, and deploy it for trading. Feel free to experiment with different configurations to find the optimal setup for your trading needs. Happy trading! \ No newline at end of file From 09186624618f87b38a1b388f38eba7bf62f3d5fa Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 28 May 2024 09:14:19 -0500 Subject: [PATCH 127/139] (feat) handle no executors or data available error --- frontend/components/backtesting.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/components/backtesting.py b/frontend/components/backtesting.py index 66f7a32..d333256 100644 --- a/frontend/components/backtesting.py +++ b/frontend/components/backtesting.py @@ -29,5 +29,10 @@ def backtesting_section(inputs, backend_api_client): 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 From 85795ab54fc43cf149a85ea84dcb4a913bf886ed Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 28 May 2024 09:14:30 -0500 Subject: [PATCH 128/139] (feat) add realtime func --- frontend/pages/orchestration/instances/app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/pages/orchestration/instances/app.py b/frontend/pages/orchestration/instances/app.py index d27c555..512c65c 100644 --- a/frontend/pages/orchestration/instances/app.py +++ b/frontend/pages/orchestration/instances/app.py @@ -69,3 +69,7 @@ with elements("active_instances_board"): for card, bot in st.session_state.active_instances_board.bot_cards: with st.session_state.active_instances_board.dashboard(): card(bot) + +while True: + time.sleep(5) + st.rerun() \ No newline at end of file From db06a463d769abeb500161a678a7ca5fd4c3bb3e Mon Sep 17 00:00:00 2001 From: cardosofede Date: Tue, 28 May 2024 09:14:42 -0500 Subject: [PATCH 129/139] (feat) remove position builder --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 5770dac..1190d7a 100644 --- a/main.py +++ b/main.py @@ -23,7 +23,7 @@ def main_page(): Page("frontend/pages/config/macd_bb_v1/app.py", "MACD_BB V1", "📊"), Page("frontend/pages/config/supertrend_v1/app.py", "SuperTrend V1", "👨‍🔬"), Page("frontend/pages/config/xemm_controller/app.py", "XEMM Controller", "⚡️"), - Page("frontend/pages/config/position_builder/app.py", "Position Builder", "🔭"), + # Page("frontend/pages/config/position_builder/app.py", "Position Builder", "🔭"), Section("Data", "💾"), Page("frontend/pages/data/download_candles/app.py", "Download Candles", "💹"), # Page("pages/create/create.py", "Create", "⚔️"), From fa6ca0e123d4ef249e8cadfa88971228c69357bb Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 29 May 2024 01:47:54 -0500 Subject: [PATCH 130/139] (feat) adapt components to backend api methods --- frontend/components/deploy_v2_with_controllers.py | 2 +- frontend/components/launch_strategy_v2.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/components/deploy_v2_with_controllers.py b/frontend/components/deploy_v2_with_controllers.py index 49d3e43..9faae93 100644 --- a/frontend/components/deploy_v2_with_controllers.py +++ b/frontend/components/deploy_v2_with_controllers.py @@ -102,7 +102,7 @@ class LaunchV2WithControllers: 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_available_credentials() + 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") diff --git a/frontend/components/launch_strategy_v2.py b/frontend/components/launch_strategy_v2.py index b372f42..810957a 100644 --- a/frontend/components/launch_strategy_v2.py +++ b/frontend/components/launch_strategy_v2.py @@ -92,10 +92,10 @@ class LaunchStrategyV2(Dashboard.Item): "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_available_credentials() + 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=available_credentials[0], + 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) @@ -106,7 +106,7 @@ class LaunchStrategyV2(Dashboard.Item): 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="hummingbot/hummingbot:latest", + 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) From 701293379f79f39d3ab39346a51672336b552ce4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 29 May 2024 01:48:19 -0500 Subject: [PATCH 131/139] (feat) add methods in backend api client to manage credentials --- backend/services/backend_api_client.py | 60 +++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py index 024a259..c24687d 100644 --- a/backend/services/backend_api_client.py +++ b/backend/services/backend_api_client.py @@ -137,12 +137,6 @@ class BackendAPIClient: response = requests.get(url) return response.json() - def get_available_credentials(self): - """Get available credentials.""" - url = f"{self.base_url}/list-credentials" - 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}" @@ -227,3 +221,57 @@ class BackendAPIClient: 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() From 491d54a8df28d5e5c4daec2e91da92c90eb1837a Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 29 May 2024 01:48:27 -0500 Subject: [PATCH 132/139] (feat) add credentials page --- .../pages/orchestration/credentials/README.md | 19 +++++ .../orchestration/credentials/__init__.py | 0 .../pages/orchestration/credentials/app.py | 83 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 frontend/pages/orchestration/credentials/README.md create mode 100644 frontend/pages/orchestration/credentials/__init__.py create mode 100644 frontend/pages/orchestration/credentials/app.py diff --git a/frontend/pages/orchestration/credentials/README.md b/frontend/pages/orchestration/credentials/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/frontend/pages/orchestration/credentials/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/frontend/pages/orchestration/credentials/__init__.py b/frontend/pages/orchestration/credentials/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/orchestration/credentials/app.py b/frontend/pages/orchestration/credentials/app.py new file mode 100644 index 0000000..88ae1cb --- /dev/null +++ b/frontend/pages/orchestration/credentials/app.py @@ -0,0 +1,83 @@ +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 +import streamlit as st +import pandas as pd +import plotly.express as px + +initialize_st_page(title="Account Balances", icon="💰") + +# Page content +client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) + +@st.cache_data(ttl=60) +def get_all_balances(): + return client.get_all_balances() + +# Fetch all balances +balances = get_all_balances() + +# Convert balances to a DataFrame for easier manipulation +def balances_to_df(balances): + data = [] + for account, exchanges in balances.items(): + for exchange, tokens in exchanges.items(): + for token, amount in tokens.items(): + data.append({"Account": account, "Exchange": exchange, "Token": token, "Amount": amount}) + return pd.DataFrame(data) + +df_balances = balances_to_df(balances) + +# Aggregation at different levels +account_agg = df_balances.groupby(["Account", "Token"])["Amount"].sum().reset_index() +exchange_agg = df_balances.groupby(["Exchange", "Token"])["Amount"].sum().reset_index() +overall_agg = df_balances.groupby("Token")["Amount"].sum().reset_index() + +# Display balances +st.header("Current Balances") +st.write(df_balances) + +# Aggregated Balances +st.header("Aggregated Balances") +st.write(account_agg) + +# Displaying account level balances +st.subheader("Account Level") +for account in account_agg["Account"].unique(): + st.write(f"**{account}**") + df = account_agg[account_agg["Account"] == account] + st.write(df) + +# Displaying exchange level balances +st.subheader("Exchange Level") +for exchange in exchange_agg["Exchange"].unique(): + st.write(f"**{exchange}**") + df = exchange_agg[exchange_agg["Exchange"] == exchange] + st.write(df) + +# Overall holdings +st.subheader("Overall Holdings") +st.write(overall_agg) + +# Visualizations +st.header("Visualizations") + +# Account level pie chart +st.subheader("Account Level Balances") +for account in account_agg["Account"].unique(): + df = account_agg[account_agg["Account"] == account] + fig = px.pie(df, names='Token', values='Amount', title=f"Account: {account}") + st.plotly_chart(fig) + +# Exchange level bar chart +st.subheader("Exchange Level Balances") +for exchange in exchange_agg["Exchange"].unique(): + df = exchange_agg[exchange_agg["Exchange"] == exchange] + fig = px.bar(df, x='Token', y='Amount', title=f"Exchange: {exchange}") + st.plotly_chart(fig) + +# Overall holdings pie chart +st.subheader("Overall Holdings by Token") +fig = px.pie(overall_agg, names='Token', values='Amount', title="Overall Holdings by Token") +st.plotly_chart(fig) + From 60578d176d7ec4956f04c8ca4ae106ca80719da7 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 29 May 2024 01:48:33 -0500 Subject: [PATCH 133/139] (feat) add portfolio page --- .../pages/orchestration/portfolio/README.md | 19 ++++++ .../pages/orchestration/portfolio/__init__.py | 0 frontend/pages/orchestration/portfolio/app.py | 58 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 frontend/pages/orchestration/portfolio/README.md create mode 100644 frontend/pages/orchestration/portfolio/__init__.py create mode 100644 frontend/pages/orchestration/portfolio/app.py diff --git a/frontend/pages/orchestration/portfolio/README.md b/frontend/pages/orchestration/portfolio/README.md new file mode 100644 index 0000000..18f4d94 --- /dev/null +++ b/frontend/pages/orchestration/portfolio/README.md @@ -0,0 +1,19 @@ +### Description + +This page helps you deploy and manage Hummingbot instances: + +- Starting and stopping Hummingbot Broker +- Creating, starting and stopping bot instances +- Managing strategy and script files that instances run +- Fetching status of running instances + +### Maintainers + +This page is maintained by Hummingbot Foundation as a template other pages: + +* [cardosfede](https://github.com/cardosfede) +* [fengtality](https://github.com/fengtality) + +### Wiki + +See the [wiki](https://github.com/hummingbot/dashboard/wiki/%F0%9F%90%99-Bot-Orchestration) for more information. \ No newline at end of file diff --git a/frontend/pages/orchestration/portfolio/__init__.py b/frontend/pages/orchestration/portfolio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frontend/pages/orchestration/portfolio/app.py b/frontend/pages/orchestration/portfolio/app.py new file mode 100644 index 0000000..286bc1b --- /dev/null +++ b/frontend/pages/orchestration/portfolio/app.py @@ -0,0 +1,58 @@ +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 +import streamlit as st +import pandas as pd + +initialize_st_page(title="Portfolio", icon="💰") + +# Page content +client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) +NUM_COLUMNS = 4 + +@st.cache_data +def get_all_balances(): + return client.get_all_balances() + +# Fetch all balances +balances = get_all_balances() + +# Convert balances to a DataFrame for easier manipulation +def balances_to_df(balances): + data = [] + for account, exchanges in balances.items(): + for exchange, tokens in exchanges.items(): + for token, amount in tokens.items(): + data.append({"Account": account, "Exchange": exchange, "Token": token, "Amount": amount}) + return pd.DataFrame(data) + +df_balances = balances_to_df(balances) +c1, c2 = st.columns([1, 1]) +with c1: + st.header("Current Balances") +with c2: + st.header("Aggregated Balances") + +c1, c2, c3, c4 = st.columns([2.5, 1.5, 1.5, 1.1]) +with c1: + # Display balances + st.subheader("All Balances") + st.dataframe(df_balances) + +with c2: + # Aggregation at the account level + account_agg = df_balances.groupby(["Account", "Token"])["Amount"].sum().reset_index() + st.subheader("Account Level") + st.dataframe(account_agg) + +with c3: + # Aggregation at the exchange level + exchange_agg = df_balances.groupby(["Exchange", "Token"])["Amount"].sum().reset_index() + st.subheader("Exchange Level") + st.dataframe(exchange_agg) + +with c4: + # Overall holdings + overall_agg = df_balances.groupby("Token")["Amount"].sum().reset_index() + st.subheader("Token Level") + st.write(overall_agg) From e35244fa4776ed35a2c198f004a39c6d3ae3b54f Mon Sep 17 00:00:00 2001 From: cardosofede Date: Wed, 29 May 2024 01:48:46 -0500 Subject: [PATCH 134/139] (feat) include new pages in main --- main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.py b/main.py index 1190d7a..218827f 100644 --- a/main.py +++ b/main.py @@ -13,6 +13,8 @@ def main_page(): Section("Bot Orchestration", "🐙"), Page("frontend/pages/orchestration/instances/app.py", "Instances", "🦅"), Page("frontend/pages/orchestration/launch_bot_v2/app.py", "Deploy", "🚀"), + Page("frontend/pages/orchestration/credentials/app.py", "Credentials", "🔑"), + Page("frontend/pages/orchestration/portfolio/app.py", "Portfolio", "💰"), # Page("frontend/pages/orchestration/launch_bot_v2_st/app.py", "Deploy ST", "🙌"), # Page("pages/file_manager/app.py", "File Explorer", "🗂"), Section("Config Generator", "🎛️"), From 4e895714982ae18fb30e94105c373e64d221deec Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 30 May 2024 13:27:25 -0500 Subject: [PATCH 135/139] (feat) handle empty processed data --- backend/services/backend_api_client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/services/backend_api_client.py b/backend/services/backend_api_client.py index c24687d..786eacf 100644 --- a/backend/services/backend_api_client.py +++ b/backend/services/backend_api_client.py @@ -196,8 +196,12 @@ class BackendAPIClient: } 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": pd.DataFrame(backtesting_results["processed_data"]), + "processed_data": data, "executors": [ExecutorInfo(**executor) for executor in backtesting_results["executors"]], "results": backtesting_results["results"] } From 6fb923cd8aad1c9343228bfc0a690be545bdbaec Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 30 May 2024 13:27:41 -0500 Subject: [PATCH 136/139] (feat) adapt pages to time --- frontend/pages/config/dman_v5/app.py | 2 +- frontend/pages/config/kalman_filter_v1/app.py | 2 +- frontend/pages/config/utils.py | 2 +- frontend/pages/data/download_candles/app.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/pages/config/dman_v5/app.py b/frontend/pages/config/dman_v5/app.py index c706077..ac39d9f 100644 --- a/frontend/pages/config/dman_v5/app.py +++ b/frontend/pages/config/dman_v5/app.py @@ -51,7 +51,7 @@ with c3: # 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='ms') +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 diff --git a/frontend/pages/config/kalman_filter_v1/app.py b/frontend/pages/config/kalman_filter_v1/app.py index 47d2b64..5f7e358 100644 --- a/frontend/pages/config/kalman_filter_v1/app.py +++ b/frontend/pages/config/kalman_filter_v1/app.py @@ -91,7 +91,7 @@ with c2: # 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='ms') +df.index = pd.to_datetime(df['timestamp'], unit='s') candles_processed = add_indicators(df, observation_covariance, transition_covariance) diff --git a/frontend/pages/config/utils.py b/frontend/pages/config/utils.py index df1ec82..cfebbd2 100644 --- a/frontend/pages/config/utils.py +++ b/frontend/pages/config/utils.py @@ -23,6 +23,6 @@ def get_candles(connector_name="binance", trading_pair="BTC-USDT", interval="1m" 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='ms') + df.index = pd.to_datetime(df.timestamp, unit='s') return df diff --git a/frontend/pages/data/download_candles/app.py b/frontend/pages/data/download_candles/app.py index d2a8d19..ee1a7cc 100644 --- a/frontend/pages/data/download_candles/app.py +++ b/frontend/pages/data/download_candles/app.py @@ -35,7 +35,7 @@ if get_data_button: ) candles_df = pd.DataFrame(candles) - candles_df.index = pd.to_datetime(candles_df["timestamp"], unit='ms') + candles_df.index = pd.to_datetime(candles_df["timestamp"], unit='s') # Plotting the candlestick chart fig = go.Figure(data=[go.Candlestick( From 373a497227fac7bad64b160c927b4464b9cf3a11 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 30 May 2024 13:27:50 -0500 Subject: [PATCH 137/139] (feat) add credentials page --- .../pages/orchestration/credentials/app.py | 133 +++++++++--------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/frontend/pages/orchestration/credentials/app.py b/frontend/pages/orchestration/credentials/app.py index 88ae1cb..c719fe3 100644 --- a/frontend/pages/orchestration/credentials/app.py +++ b/frontend/pages/orchestration/credentials/app.py @@ -2,82 +2,83 @@ 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 import streamlit as st -import pandas as pd -import plotly.express as px -initialize_st_page(title="Account Balances", icon="💰") + +initialize_st_page(title="Credentials", icon="🔑") # Page content client = BackendAPIClient.get_instance(host=BACKEND_API_HOST, port=BACKEND_API_PORT) +NUM_COLUMNS = 4 -@st.cache_data(ttl=60) -def get_all_balances(): - return client.get_all_balances() -# Fetch all balances -balances = get_all_balances() +@st.cache_data +def get_all_connectors_config_map(): + return client.get_all_connectors_config_map() -# Convert balances to a DataFrame for easier manipulation -def balances_to_df(balances): - data = [] - for account, exchanges in balances.items(): - for exchange, tokens in exchanges.items(): - for token, amount in tokens.items(): - data.append({"Account": account, "Exchange": exchange, "Token": token, "Amount": amount}) - return pd.DataFrame(data) +# Section to display available accounts and credentials +accounts = client.get_accounts() +all_connector_config_map = get_all_connectors_config_map() +st.header("Available Accounts and Credentials") -df_balances = balances_to_df(balances) +if accounts: + n_accounts = len(accounts) + accounts.remove("master_account") + accounts.insert(0, "master_account") + for i in range(0, n_accounts, NUM_COLUMNS): + cols = st.columns(NUM_COLUMNS) + for j, account in enumerate(accounts[i:i + NUM_COLUMNS]): + with cols[j]: + st.subheader(f"🏦 {account}") + credentials = client.get_credentials(account) + st.json(credentials) +else: + st.write("No accounts available.") -# Aggregation at different levels -account_agg = df_balances.groupby(["Account", "Token"])["Amount"].sum().reset_index() -exchange_agg = df_balances.groupby(["Exchange", "Token"])["Amount"].sum().reset_index() -overall_agg = df_balances.groupby("Token")["Amount"].sum().reset_index() +st.markdown("---") -# Display balances -st.header("Current Balances") -st.write(df_balances) +c1, c2 = st.columns([1, 1]) +with c1: + # Section to create a new account + st.header("Create a New Account") + new_account_name = st.text_input("New Account Name") + if st.button("Create Account"): + if new_account_name: + response = client.add_account(new_account_name) + st.write(response) + else: + st.write("Please enter an account name.") -# Aggregated Balances -st.header("Aggregated Balances") -st.write(account_agg) +with c2: + # Section to delete an existing account + st.header("Delete an Account") + delete_account_name = st.selectbox("Select Account to Delete", options=accounts if accounts else ["No accounts available"], ) + if st.button("Delete Account"): + if delete_account_name and delete_account_name != "No accounts available": + response = client.delete_account(delete_account_name) + st.write(response) + else: + st.write("Please select a valid account.") -# Displaying account level balances -st.subheader("Account Level") -for account in account_agg["Account"].unique(): - st.write(f"**{account}**") - df = account_agg[account_agg["Account"] == account] - st.write(df) - -# Displaying exchange level balances -st.subheader("Exchange Level") -for exchange in exchange_agg["Exchange"].unique(): - st.write(f"**{exchange}**") - df = exchange_agg[exchange_agg["Exchange"] == exchange] - st.write(df) - -# Overall holdings -st.subheader("Overall Holdings") -st.write(overall_agg) - -# Visualizations -st.header("Visualizations") - -# Account level pie chart -st.subheader("Account Level Balances") -for account in account_agg["Account"].unique(): - df = account_agg[account_agg["Account"] == account] - fig = px.pie(df, names='Token', values='Amount', title=f"Account: {account}") - st.plotly_chart(fig) - -# Exchange level bar chart -st.subheader("Exchange Level Balances") -for exchange in exchange_agg["Exchange"].unique(): - df = exchange_agg[exchange_agg["Exchange"] == exchange] - fig = px.bar(df, x='Token', y='Amount', title=f"Exchange: {exchange}") - st.plotly_chart(fig) - -# Overall holdings pie chart -st.subheader("Overall Holdings by Token") -fig = px.pie(overall_agg, names='Token', values='Amount', title="Overall Holdings by Token") -st.plotly_chart(fig) +st.markdown("---") +# Section to add credentials +st.header("Add Credentials") +c1, c2 = st.columns([1, 1]) +with c1: + account_name = st.selectbox("Select Account", options=accounts if accounts else ["No accounts available"]) +with c2: + all_connectors = list(all_connector_config_map.keys()) + binance_perpetual_index = all_connectors.index("binance_perpetual") if "binance_perpetual" in all_connectors else None + connector_name = st.selectbox("Select Connector", options=all_connectors, index=binance_perpetual_index) +if account_name and account_name != "No accounts available" and connector_name: + st.write(f"Configuration Map for {connector_name}:") + config_map = all_connector_config_map[connector_name] + config_inputs = {} + cols = st.columns(NUM_COLUMNS) + for i, config in enumerate(config_map): + with cols[i % (NUM_COLUMNS - 1)]: + config_inputs[config] = st.text_input(config) + with cols[NUM_COLUMNS - 1]: + if st.button("Submit Credentials"): + response = client.add_connector_keys(account_name, connector_name, config_inputs) + st.write(response) From 38a21142ffa9a9d0dad5f47eef075dc66dbd74d2 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 30 May 2024 13:28:04 -0500 Subject: [PATCH 138/139] (feat) adapt visualizations to seconds --- frontend/visualization/candles.py | 2 +- frontend/visualization/executors.py | 4 ++-- frontend/visualization/indicators.py | 2 +- frontend/visualization/pnl.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/visualization/candles.py b/frontend/visualization/candles.py index 3300249..6bbbdda 100644 --- a/frontend/visualization/candles.py +++ b/frontend/visualization/candles.py @@ -15,7 +15,7 @@ def get_candlestick_trace(df): def get_bt_candlestick_trace(df): - df.index = pd.to_datetime(df.timestamp, unit='ms') + df.index = pd.to_datetime(df.timestamp, unit='s') return go.Scatter(x=df.index, y=df['close'], mode='lines', diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py index 83fff4a..b94cff7 100644 --- a/frontend/visualization/executors.py +++ b/frontend/visualization/executors.py @@ -7,9 +7,9 @@ from hummingbot.connector.connector_base import TradeType def add_executors_trace(fig, executors, row, col): for executor in executors: - entry_time = pd.to_datetime(executor.timestamp, unit='ms') + entry_time = pd.to_datetime(executor.timestamp, unit='s') entry_price = executor.custom_info["current_position_average_price"] - exit_time = pd.to_datetime(executor.close_timestamp, unit='ms') + exit_time = pd.to_datetime(executor.close_timestamp, unit='s') exit_price = executor.custom_info["close_price"] name = "Buy Executor" if executor.config.side == TradeType.BUY else "Sell Executor" diff --git a/frontend/visualization/indicators.py b/frontend/visualization/indicators.py index 93f86bf..e5965a4 100644 --- a/frontend/visualization/indicators.py +++ b/frontend/visualization/indicators.py @@ -23,7 +23,7 @@ def get_bbands_traces(df, bb_length, bb_std): def get_volume_trace(df): - df.index = pd.to_datetime(df.timestamp, unit='ms') + df.index = pd.to_datetime(df.timestamp, unit='s') return go.Bar(x=df.index, y=df['volume'], name="Volume", marker_color=theme.get_color_scheme()["volume"], opacity=0.7) def get_macd_traces(df, macd_fast, macd_slow, macd_signal): diff --git a/frontend/visualization/pnl.py b/frontend/visualization/pnl.py index 11f17f6..488f75e 100644 --- a/frontend/visualization/pnl.py +++ b/frontend/visualization/pnl.py @@ -7,7 +7,7 @@ def get_pnl_trace(executors): pnl = [e.net_pnl_quote for e in executors] cum_pnl = np.cumsum(pnl) return go.Scatter( - x=pd.to_datetime([e.close_timestamp for e in executors], unit="ms"), + x=pd.to_datetime([e.close_timestamp for e in executors], unit="s"), y=cum_pnl, mode='lines', line=dict(color='gold', width=2, dash="dash"), From 925acc0d719448a3c3370dea5b81ea3db16c62d4 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Thu, 30 May 2024 13:28:08 -0500 Subject: [PATCH 139/139] (feat) adapt labeling to seconds --- quants_lab/utils/labeling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quants_lab/utils/labeling.py b/quants_lab/utils/labeling.py index 0650d56..eac567d 100644 --- a/quants_lab/utils/labeling.py +++ b/quants_lab/utils/labeling.py @@ -5,7 +5,7 @@ import pandas as pd def triple_barrier_method(df, tp=1.0, sl=1.0, tl=5, std_span: Optional[int] = 100, trade_cost=0.0006, max_executors: int = 1): - df.index = pd.to_datetime(df.timestamp, unit="ms") + df.index = pd.to_datetime(df.timestamp, unit="s") if std_span: df["target"] = df["close"].rolling(std_span).std() / df["close"] else: