{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "nwaAZRu1NTiI" }, "source": [ "# Q-learning \n", "\n", "#### This version implements q-learning using a custom enviroment 1 day, with synthetic data, this version fix render method\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "DDf1gLC2NTiK" }, "outputs": [], "source": [ "# !pip install -r ./requirements.txt\n", "# !pip install stable_baselines3[extra]\n", "# !pip install yfinance\n", "# !pip install talib-binary\n", "# !pip install huggingface_sb3\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "LNXxxKojNTiL" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import gym\n", "from gym import spaces\n", "from gym.utils import seeding\n", "\n", "import talib as ta\n", "from tqdm.notebook import tqdm\n", "\n", "import yfinance as yf\n", "import pandas as pd\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def get_syntetic_data(tf, start_date, end_date, plot=True, add_noise=None):\n", " df = pd.date_range(start=start_date, end=end_date, freq=tf)\n", " df = df.to_frame()\n", "\n", " df['v1'] = np.arange(len(df.index))\n", " df[['Open','High','Low','Close','Volume']] = 0.0\n", " df = df.drop([0], axis=1)\n", "\n", " # adjust here the function to generate the data\n", " # df[\"Close\"]=df[\"v1\"].map(lambda x: np.sin(x)+10 ) # Simple sine wave all positive values \n", " df[\"Close\"]=df[\"v1\"].map(lambda x: np.sin(x)+10 + np.sin(x/2) ) # a composition of 2 sine waves \n", " if add_noise is not None: # could be 0.5\n", " noise = np.random.normal(0, add_noise, len(df))\n", " df[\"Close\"] += noise\n", "\n", " if plot:\n", " plt.figure(figsize=(15,6))\n", " df['Close'].tail(30).plot()\n", "\n", " df[\"Open\"]=df[\"Close\"].shift(1)\n", " df = df.dropna()\n", " x = 1.5\n", " df[\"High\"] = np.where( df[\"Close\"] > df['Open'], df[\"Close\"]+x, df[\"Open\"]+x )\n", " df[\"Low\"] = np.where( df[\"Close\"] < df['Open'], df[\"Close\"]-x, df[\"Open\"]-x )\n", " df[\"Volume\"] = 10\n", " return df" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "dmAuEhZZNTiL" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3080\n", "1931\n" ] } ], "source": [ "# Get data\n", "eth_usd = yf.Ticker(\"ETH-USD\")\n", "eth = eth_usd.history(period=\"max\")\n", "\n", "btc_usd = yf.Ticker(\"BTC-USD\")\n", "btc = btc_usd.history(period=\"max\")\n", "print(len(btc))\n", "print(len(eth))\n", "\n", "btc_train = btc[-3015:-200]\n", "btc_test = btc[-200:]\n", "eth_train = eth[-1864:-200]\n", "eth_test = eth[-200:]\n", "# len(eth_train)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/png": "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+ixdfScWi39/3nHS17dfi4tK5vYLJo3eDyEiQUlFn4iwaGIaX75yGj96bi+XTM/k5kX57/s99ceOc/9fq7lxfi5z8pIDkFJERM7E7TIkx0aSHHv2HRsiMj7oXr6IAPDZy6aydHIa//aXSg42d73v+T9+bi8W+OdrZox9OBEREREZNhV9IgIMXSH+2eoFREW4+PyDW+nznn6luLfqO3hsax13XDBpRK1IIiIiIjL2VPSJyDuyk2P44U3z2FXfwX+s33Pa875XVkVSTCT/dNnUAKYTERERkeFQ0Sci73L17Gw+dv5EfvPXal7a0/Se11/d28xr+1r4/BVTSY7TvBERERGRYKeiT0Te45vXzmJmdiL/vGY7TZ297xwf9Fm+V7abgrRYPnr+RAcTioiIiMjZUtEnIu8RE+nm3tsW0N3v5atrtuPzWQAe33qEqoYOvnbNTKIj3O/zLiIiIiISDFT0icgpTZuQyL+tmM1r+1r4zV8P0jswyI+f20NxfjIr5uY4HU9EREREzpL26ROR07ptcQGv7Wvmh+v38FZ9Bw3tvfx09XxcLm3ELiIiIhIqdKdPRE7LGMP3PzSPrMRonthWz5Wzslg6Od3pWCIiIiJyDlT0icgZJcdFcu+HF1BckMLXl89yOo6IiIiInCO1d4rI+1o0MY2/3HWh0zFEREREZBh0p09ERERERCSMqegTEREREREJYyr6REREREREwpiKPhERERERkTCmok9ERERERCSMqegTEREREREJYyr6REREREREwpiKPhERERERkTCmok9ERERERCSMqegTEREREREJYyr6REREREREwpiKPhERERERkTCmok9ERERERCSMGWut0xnewxjTCewJ4JDJQHsAx9OYGjMUxxwPP6PG1JihOOZ4+Bk1psYMtfE0psYcDTOstYmj8k7W2qB7ABUBHu9XDvyMGlNjhtSY4+Fn1JgaMxTHHA8/o8bUmKE2nsbUmKM05qjVRGrvHLJOY2pMjRl042lMjakxg3M8jakxQ3HM8fAzaszwG3PUBGt7Z4W1tsTpHCIiIiIiIk4YzZooWO/0/crpACIiIiIiIg4atZooKO/0iYiIiIiIyOgI1jt9YccYs8wYs8cYs98Y83X/sf9tjDlijNnmf1zrdE4Rpxhj7jfGNBljKk849h/GmN3GmB3GmMeNMSlOZhRx2ml+T4qNMW8YY3YaY9YZY5KczCjiNGNMgTHmJWNMlTFmlzHmiye89nn/57FdxpgfOplTJJB0py8AjDFuYC9wFVAHbAJuA24Buqy1P3IwnkhQMMZcAnQBf7DWzvEfuxrYYK31GmN+AGCtvdvBmCKOOs3vySbgn621rxhjPg4UWWv/l5M5RZxkjMkBcqy1W4wxicBm4EZgAvCvwHXW2j5jTJa1tsnJrCKBojt9gbEY2G+tPWit7QceAm5wOJNIULHWvgp4Tjr2nLXW6//PN4H8gAcTCSKn+j0BZgCv+p8/D9wU0FAiQcZa22Ct3eJ/3glUAXnAZ4HvW2v7/K+p4JNxQ0VfYOQBh0/47zr/MYDP+VvX7jfGpAY+mkjI+DhQ5nQIkSBUCVzvf74KKHAwi0hQMcZMAhYAG4HpwMXGmI3GmFeMMaVOZhMJJBV9gWFOccwCvwSmAPOBBuDHgQwlEiqMMf8KeIE/OZ1FJAh9HLjLGLMZSAT6Hc4jEhSMMQnAWuBL1toOIAJIBZYCXwPWGGNO9RlNJOxEOB1gnKjj3Vde84F6a+3Rtw8YY34NPBXoYCLBzhhzO7AC+IDVJGSR97DW7gauBjDGTAeuczaRiPOMMZEMFXx/stY+5j9cBzzm/7ek3BjjAzKAZodiigSM7vQFxiZgmjGmyBgTBdwKPOmfaPy2DzLUoiMifsaYZcDdwPXW2h6n84gEI2NMlv+rC/gWcJ+ziUSc5b9791ugylr7kxNeegK4wn/OdCAKaAl8QpHA052+APCvPPg54FnADdxvrd1ljPmjMWY+Q62eNcBnHIwp4ihjzIPAZUCGMaYO+HfgG0A08Ly/A+dNa+2djoUUcdhpfk8SjDF3+U95DPidQ/FEgsWFwEeBncaYbf5j3wTuB+73b3nSD9yuDhIZL7Rlg4iIiIiISBhTe6eIiIiIiEgYU9EnIiIiIiISxlT0iYiIiIiIhDEVfSIiIiIiImFMRZ+IiIiIiEgYU9EnIiIiIiISxlT0iYiIiIiIhDEVfSIiIiIiImFMRZ+IiIiIiEgYU9EnIiIiIiISxlT0iYiIiIiIhDEVfSIiIiIiImFMRZ+IiIiIiEgYU9EnIiIiIiISxlT0iYiIiIiIhDEVfSIiIiIiImFMRZ+IiIiIiIgDjDFdgRhHRZ+IiIiIiEgYU9EnIiIiIiLiEGNMgjHmRWPMFmPMTmPMDf7jk4wxVcaYXxtjdhljnjPGxA5rDGvt6KYWERERERGR9+Vv70wB4qy1HcaYDOBNYBowEdgPlFhrtxlj1gBPWmv/51zHiRjN0CIiIiIiInJODPBdY8wlgA/IAyb4X6u21m7zP98MTBrOACr6REREREREnPMRIBNYZK0dMMbUADH+1/pOOG8QGFZ7p+b0iYiIiIiIOCcZaPIXfJcz1NY5qnSnT0REREREJMCMMREM3cn7E7DOGFMBbAN2j/pYWshFREREREQksIwxxcCvrbWLx3ostXeKiIiIiIgEkDHmTuBB4FsBGU93+kRERERERMKX7vSJiIiIiIiMMWNMgTHmJf+G67uMMV/0H08zxjxvjNnn/5rqP36VMWazf8P2zcaYK054r/XGmO3+97nPGOM+49i60yciIiIiIjK2jDE5QI61dosxJpGhffduBP4R8Fhrv2+M+TqQaq292xizADhqra03xswBnrXW5vnfK8m/mbsBHgUesdY+dLqxdadPRERERERkjFlrG6y1W/zPO4EqhjZivwH4vf+03zNUCGKt3Wqtrfcf3wXEGGOi/a91+I9HAFHAGe/kqegTEREREREJIGPMJGABsBGYYK1tgKHCEMg6xbfcBGy11vad8B7PAk1AJ0N3+05LRZ+IiIiIiEiAGGMSgLXAl064Y3em82cDPwA+c+Jxa+01QA4QDVxxim99h4o+ERERERGRADDGRDJU8P3JWvuY//BR/3y/t+f9NZ1wfj7wOPAxa+2Bk9/PWtsLPMlQi+hpqegTEREREREZY/5FV34LVFlrf3LCS08Ct/uf3w78xX9+CvA08A1r7d9OeJ+EE4rECOBaYPcZx9bqnSIiIiIiImPLGHMR8BqwE/D5D3+ToXl9a4BC4BCwylrrMcZ8C/gGsO+Et7kaMMBTDLV1uoENwJettd7Tjq2iT0REREREJHypvVNERERERCSMqegTEREREREJYyr6REREREREwpiKPhERERERkTCmok9ERERERCSMqegTEREREREJYyr6REREREREwpiKPhERERERkTD2/wGBtDpoybn0LwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# use synthetic data\n", "# synthetic_data = get_syntetic_data(tf=\"D\", start_date=\"2015-01-01\", end_date=\"2015-02-05\", add_noise=None)\n", "synthetic_data = get_syntetic_data(tf=\"D\", start_date=\"2015-01-01\", end_date=\"2023-01-01\", add_noise=None)\n", "eth_train = synthetic_data[-1864:-200]\n", "eth_test = synthetic_data[-200:]\n", "\n", "# eth_train = btc_train\n", "# eth_test = btc_test\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def initialize_q_table(state_space, action_space):\n", " Qtable = np.zeros((state_space, action_space))\n", " return Qtable" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Policy\n", "\n", "def greedy_policy(Qtable, state):\n", " # Exploitation: take the action with the highest state, action value\n", " # if we dont have a state with values return DO_NOTHING \n", " if abs(np.max(Qtable[state])) > 0:\n", " action = np.argmax(Qtable[state])\n", " else:\n", " action = 2\n", " # action = np.argmax(Qtable[state])\n", " return action\n", "\n", "\n", "def epsilon_greedy_policy(Qtable, state, epsilon, env):\n", " # Randomly generate a number between 0 and 1\n", " random_num = np.random.uniform(size=1)\n", " # if random_num > greater than epsilon --> exploitation\n", " if random_num > epsilon:\n", " # Take the action with the highest value given a state\n", " # np.argmax can be useful here\n", " action = greedy_policy(Qtable, state)\n", " # else --> exploration\n", " else:\n", " # action = np.random.random_integers(4,size=1)[0]\n", " action = env.action_space.sample()\n", " \n", " return action" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "wlC-EdLENTiN" }, "outputs": [], "source": [ "def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable, learning_rate, gamma):\n", " state_history = []\n", "# np.random.seed(42)\n", " for episode in range(n_training_episodes):\n", " # Reduce epsilon (because we need less and less exploration)\n", " epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)\n", " # Reset the environment\n", " state = env.reset()\n", " step = 0\n", " done = False\n", "\n", " # repeat\n", " for step in range(max_steps):\n", " # Choose the action At using epsilon greedy policy\n", " action = epsilon_greedy_policy(Qtable, state, epsilon, env)\n", "\n", " # Take action At and observe Rt+1 and St+1\n", " # Take the action (a) and observe the outcome state(s') and reward (r)\n", " new_state, reward, done, info = env.step(action)\n", "\n", " # Update Q(s,a):= Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]\n", " Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * ( np.max(Qtable[new_state]) ) - Qtable[state][action] )\n", "\n", " # If done, finish the episode\n", " if done:\n", " break\n", " \n", " # Our next state is the new state\n", " state = new_state\n", "\n", " state_history.append(state) \n", "\n", " return Qtable, state_history" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from enum import Enum\n", "class Actions(Enum):\n", " Sell = 0\n", " Buy = 1\n", " Do_nothing = 2\n", "\n", "class CustTradingEnv(gym.Env):\n", "\n", " def __init__(self, df, max_steps=0, random_start=True):\n", " self.seed()\n", " self.df = df\n", " self.prices, self.signal_features = self._process_data()\n", "\n", " # spaces\n", " self.action_space = spaces.Discrete(3)\n", " self.observation_space = spaces.Box(low=0, high=1999, shape=(1,) , dtype=np.float64)\n", "\n", " # episode\n", " self._start_tick = 0\n", " self._end_tick = 0\n", " self._done = None\n", " self._current_tick = None\n", " self._last_trade_tick = None\n", " self._position = None\n", " self._position_history = None\n", " self._total_reward = None\n", " self._total_profit = None\n", " self._first_rendering = None\n", " self.history = None\n", " self._max_steps = max_steps\n", " self._start_episode_tick = None\n", " self._trade_history = None\n", " self._trade_tick_history = None\n", " self._random_start = random_start\n", "\n", " def reset(self):\n", " self._done = False\n", " if self._random_start:\n", " self._start_episode_tick = np.random.randint(1,high=len(self.df)- self._max_steps )\n", " self._end_tick = self._start_episode_tick + self._max_steps\n", " else:\n", " self._start_episode_tick = 1\n", " self._end_tick = len(self.df)-1\n", " # self._start_episode_tick = np.random.randint(1,len(self.df)- self._max_steps )\n", " # self._end_tick = self._start_episode_tick + self._max_steps\n", " self._current_tick = self._start_episode_tick\n", " self._last_trade_tick = self._current_tick - 1\n", " self._position = 0\n", " self._position_history = [-1] * (len(self.prices)) \n", " # self._position_history = (self.window_size * [None]) + [self._position]\n", " self._total_reward = 0.\n", " self._total_profit = 0.\n", " self._trade_history = []\n", " self._trade_tick_history = []\n", " self.history = {}\n", " return self._get_observation()\n", "\n", "\n", " def step(self, action):\n", " self._done = False\n", " self._current_tick += 1\n", "\n", " if self._current_tick == self._end_tick:\n", " self._done = True\n", "\n", " step_reward = self._calculate_reward(action)\n", " self._total_reward += step_reward\n", "\n", " observation = self._get_observation()\n", " info = dict(\n", " total_reward = self._total_reward,\n", " total_profit = self._total_profit,\n", " position = self._position,\n", " action = action\n", " )\n", " self._update_history(info)\n", "\n", " return observation, step_reward, self._done, info\n", "\n", " def seed(self, seed=None):\n", " self.np_random, seed = seeding.np_random(seed)\n", " return [seed]\n", " \n", " def _get_observation(self):\n", " return self.signal_features[self._current_tick]\n", "\n", " def _update_history(self, info):\n", " if not self.history:\n", " self.history = {key: [] for key in info.keys()}\n", "\n", " for key, value in info.items():\n", " self.history[key].append(value)\n", "\n", "\n", " def render(self, mode='human'):\n", " window_ticks = np.arange(len(self.prices))\n", " prices = self.prices\n", " # prices = self.prices[self._start_episode_tick:self._end_tick+1]\n", " plt.plot(prices)\n", "\n", " open_buy = []\n", " close_buy = []\n", " open_sell = []\n", " close_sell = []\n", " do_nothing = []\n", " penalty = []\n", "\n", " for i, tick in enumerate(window_ticks):\n", " if self._position_history[i] == 1:\n", " open_buy.append(tick)\n", " elif self._position_history[i] == 2 :\n", " close_buy.append(tick)\n", " elif self._position_history[i] == 3 :\n", " open_sell.append(tick)\n", " elif self._position_history[i] == 4 :\n", " close_sell.append(tick)\n", " elif self._position_history[i] == 0 :\n", " do_nothing.append(tick)\n", " elif self._position_history[i] == 5 :\n", " penalty.append(tick)\n", "\n", " plt.plot(open_buy, prices[open_buy], 'go', marker=\"^\")\n", " plt.plot(close_buy, prices[close_buy], 'go', marker=\"v\")\n", " plt.plot(open_sell, prices[open_sell], 'ro', marker=\"v\")\n", " plt.plot(close_sell, prices[close_sell], 'ro', marker=\"^\")\n", " \n", " plt.plot(do_nothing, prices[do_nothing], 'oc')\n", " plt.plot(penalty, prices[penalty], 'yo')\n", "\n", " plt.suptitle(\n", " \"Total Reward: %.6f\" % self._total_reward + ' ~ ' +\n", " \"Total Profit: %.6f\" % self._total_profit\n", " )\n", "\n", " # the action is taken when the market is off after the session ends and we receive the close price of the day\n", " # this will be the current price \n", " def _calculate_reward(self, action):\n", " step_reward = 0\n", "\n", " current_price = self.prices[self._current_tick]\n", " last_price = self.prices[self._current_tick - 1]\n", " price_diff = current_price - last_price\n", "\n", " penalty = -1 * last_price * 0.01\n", " # OPEN BUY - 1\n", " if action == Actions.Buy.value and self._position == 0:\n", " self._position = 1\n", " step_reward += price_diff\n", " self._last_trade_tick = self._current_tick - 1\n", " self._position_history[self._current_tick-1]=1\n", "\n", " # CLOSE BUY - 2\n", " elif action == Actions.Sell.value and self._position > 0:\n", " self._position = 0\n", " step_reward += self.prices[self._current_tick-1] - self.prices[self._last_trade_tick] \n", " self._total_profit += step_reward\n", " self._position_history[self._current_tick-1]=2\n", " self._trade_history.append(step_reward)\n", " self._trade_tick_history.append((self._last_trade_tick, self._current_tick-1, self.prices[self._last_trade_tick], self.prices[self._current_tick-1], step_reward))\n", "\n", " elif action == Actions.Buy.value and self._position > 0:\n", " step_reward += penalty\n", " self._position_history[self._current_tick-1]=5\n", " # CLOSE SELL - 4\n", " elif action == Actions.Buy.value and self._position < 0:\n", " self._position = 0\n", " step_reward += -1 * (self.prices[self._current_tick-1] - self.prices[self._last_trade_tick]) \n", " self._total_profit += step_reward\n", " self._position_history[self._current_tick-1]=4\n", " self._trade_history.append(step_reward)\n", " self._trade_tick_history.append((self._last_trade_tick, self._current_tick-1, self.prices[self._last_trade_tick], self.prices[self._current_tick-1], step_reward))\n", "\n", " # OPEN SELL - 3\n", " elif action == Actions.Sell.value and self._position == 0:\n", " self._position = -1\n", " step_reward += -1 * price_diff\n", " self._last_trade_tick = self._current_tick - 1\n", " self._position_history[self._current_tick-1]=3\n", "\n", " elif action == Actions.Sell.value and self._position < 0:\n", " step_reward += penalty\n", " self._position_history[self._current_tick-1]=5\n", "\n", " # DO NOTHING - 0\n", " elif action == Actions.Do_nothing.value and self._position > 0:\n", " step_reward += price_diff\n", " self._position_history[self._current_tick-1]=0\n", " elif action == Actions.Do_nothing.value and self._position < 0:\n", " step_reward += -1 * price_diff\n", " self._position_history[self._current_tick-1]=0\n", " elif action == Actions.Do_nothing.value and self._position == 0:\n", " step_reward += -1 * abs(price_diff)\n", " self._position_history[self._current_tick-1]=0\n", "\n", " return step_reward\n", "\n", " def _do_bin(self,df):\n", " df = pd.cut(df,bins=[0,10,20,30,40,50,60,70,80,90,100],labels=False, include_lowest=True)\n", " return df\n", " # Our state will be encode with 4 features MFI and Stochastic(only D line), ADX and DI+DI-\n", " # the values of each feature will be binned in 10 bins, ex:\n", " # MFI goes from 0-100, if we get 25 will put on the second bin \n", " # DI+DI- if DI+ is over DI- set (1 otherwise 0) \n", " # \n", " # that will give a state space of 10(MFI) * 10(STOCH) * 10(ADX) * 2(DI) = 2000 states\n", " # encoded as bins of DI MFI STOCH ADX = 1 45.2 25.4 90.1 , binned = 1 4 2 9 state = 1429 \n", " def _process_data(self):\n", " timeperiod = 14\n", " self.df = self.df.copy()\n", " \n", " self.df['adx_r'] = ta.ADX(self.df['High'], self.df['Low'], self.df['Close'], timeperiod=timeperiod)\n", " self.df['mfi_r'] = ta.MFI(self.df['High'], self.df['Low'], self.df['Close'],self.df['Volume'], timeperiod=timeperiod)\n", " _, self.df['stock_d_r'] = ta.STOCH(self.df['High'], self.df['Low'], self.df['Close'], fastk_period=5, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0)\n", " self.df['p_di'] = ta.PLUS_DI(self.df['High'], self.df['Low'], self.df['Close'], timeperiod=timeperiod)\n", " self.df['m_di'] = ta.MINUS_DI(self.df['High'], self.df['Low'], self.df['Close'], timeperiod=timeperiod)\n", " self.df['di'] = np.where( self.df['p_di'] > self.df['m_di'], 1, 0)\n", " self.df = self.df.dropna()\n", " self.df['mfi'] = self._do_bin(self.df['mfi_r'])\n", " self.df['stock_d'] = self._do_bin(self.df['stock_d_r'])\n", " self.df['adx'] = self._do_bin(self.df['adx_r'])\n", "\n", " self.df['state'] = self.df['di']*1000+ self.df['mfi']*100 + self.df['stock_d']*10 + self.df['adx']\n", "\n", " prices = self.df.loc[:, 'Close'].to_numpy()\n", " signal_features = self.df.loc[:, 'state'].to_numpy()\n", " # signal_features = self.df.loc[:, ['di', 'mfi', 'stock_d','adx']].to_numpy()\n", "\n", " return prices, signal_features" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# Training parameters\n", "n_training_episodes = 20000 # Total training episodes\n", "learning_rate = 0.2 # Learning rate\n", "\n", "# Environment parameters\n", "max_steps = 20 # Max steps per episode\n", "gamma = 0.95 # Discounting rate\n", "\n", "# Exploration parameters\n", "max_epsilon = 1.0 # Exploration probability at start\n", "# max_epsilon = 1.0 # Exploration probability at start\n", "min_epsilon = 0.05 # Minimum exploration probability \n", "# min_epsilon = 0.05 # Minimum exploration probability \n", "decay_rate = 0.0005 # Exponential decay rate for exploration prob" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "REhmfLkYNTiN", "outputId": "cf676f6d-83df-43f5-89fe-3258e0041d9d" }, "outputs": [], "source": [ "# create env\n", "env = CustTradingEnv(df=eth_train, max_steps=max_steps, random_start=True)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# create q-table\n", "\n", "action_space = env.action_space.n # buy sell do_nothing\n", "state_space = 2000\n", "\n", "Qtable_trading = initialize_q_table(state_space, action_space)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "97" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# train with ETH\n", "Qtable_trading, state_history = train(n_training_episodes, min_epsilon, max_epsilon, \n", " decay_rate, env, max_steps, Qtable_trading, learning_rate, gamma )\n", "len(np.where( Qtable_trading > 0 )[0])\n", "\n", "# #train with BTC\n", "# env = CustTradingEnv(df=btc_train, max_steps=max_steps)\n", "# Qtable_trading, state_history = train(n_training_episodes, min_epsilon, max_epsilon, \n", "# decay_rate, env, max_steps, Qtable_trading, learning_rate, gamma )\n", "# len(np.where( Qtable_trading > 0 )[0])" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def evaluate_agent(env, max_steps, n_eval_episodes, Q, random=False):\n", " \"\"\"\n", " Evaluate the agent for ``n_eval_episodes`` episodes and returns average reward and std of reward.\n", " :param env: The evaluation environment\n", " :param n_eval_episodes: Number of episode to evaluate the agent\n", " :param Q: The Q-table\n", " \"\"\"\n", " episode_positive_perc_trades = []\n", " episode_rewards = []\n", " episode_profits = []\n", " for episode in tqdm(range(n_eval_episodes), disable=random):\n", " state = env.reset()\n", " step = 0\n", " done = False\n", " total_rewards_ep = 0\n", " total_profit_ep = 0\n", " \n", " for step in range(max_steps):\n", " # Take the action (index) that have the maximum expected future reward given that state\n", " if random:\n", " action = env.action_space.sample()\n", " else:\n", " action = greedy_policy(Q, state)\n", "\n", " new_state, reward, done, info = env.step(action)\n", " total_rewards_ep += reward\n", " \n", " if done:\n", " break\n", " state = new_state\n", "\n", " if len(env._trade_history) > 0:\n", " episode_positive_perc_trades.append(np.count_nonzero(np.array(env._trade_history) > 0)/len(env._trade_history))\n", " episode_rewards.append(total_rewards_ep)\n", " episode_profits.append(env.history['total_profit'][-1])\n", " # print(env.history)\n", " # env.render()\n", " # assert 0\n", "\n", " mean_reward = np.mean(episode_rewards)\n", " std_reward = np.std(episode_rewards)\n", " mean_profit = np.mean(episode_profits)\n", " std_profit = np.std(episode_profits)\n", " positive_perc_trades = np.mean(episode_positive_perc_trades)\n", "\n", " return mean_reward, std_reward, mean_profit, std_profit, positive_perc_trades" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b6c2093f5c824b4faeff7bad9a401fb8", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/1000 [00:00" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(15,6))\n", "plt.cla()\n", "env_test.render()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "602403a96bba4a0c91db842c27e5e1e1", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/1 [00:00" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(15,6))\n", "plt.cla()\n", "env_test.render()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "env_test._trade_tick_history" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3.8.13 ('rl2')", "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.8.13" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "cd60ab8388a66026f336166410d6a8a46ddf65ece2e85ad2d46c8b98d87580d1" } }, "widgets": { "application/vnd.jupyter.widget-state+json": { "01a2dbcb714e40148b41c761fcf43147": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "20b0f38ec3234ff28a62a286cd57b933": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "PasswordModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "PasswordModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "PasswordView", "continuous_update": true, "description": "Token:", "description_tooltip": null, "disabled": false, "layout": "IPY_MODEL_01a2dbcb714e40148b41c761fcf43147", "placeholder": "​", "style": "IPY_MODEL_90c874e91b304ee1a7ef147767ac00ce", "value": "" } }, "270cbb5d6e9c4b1e9e2f39c8b3b0c15f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "VBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "VBoxView", "box_style": "", "children": [ "IPY_MODEL_a02224a43d8d4af3bd31d326540d25da", "IPY_MODEL_20b0f38ec3234ff28a62a286cd57b933", "IPY_MODEL_f6c845330d6743c0b35c2c7ad834de77", "IPY_MODEL_f1675c09d16a4251b403f9c56255f168", "IPY_MODEL_c1a82965ae26479a98e4fdbde1e64ec2" ], "layout": "IPY_MODEL_3fa248114ac24656ba74923936a94d2d" } }, "2dc5fa9aa3334dfcbdee9c238f2ef60b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "3e753b0212644990b558c68853ff2041": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "3fa248114ac24656ba74923936a94d2d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": "center", "align_self": null, "border": null, "bottom": null, "display": "flex", "flex": null, "flex_flow": "column", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "50%" } }, "42d140b838b844819bc127afc1b7bc84": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "90c874e91b304ee1a7ef147767ac00ce": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "9d847f9a7d47458d8cd57d9b599e47c6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "a02224a43d8d4af3bd31d326540d25da": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_caef095934ec47bbb8b64eab22049284", "placeholder": "​", "style": "IPY_MODEL_2dc5fa9aa3334dfcbdee9c238f2ef60b", "value": "

Copy a token from your Hugging Face\ntokens page and paste it below.
Immediately click login after copying\nyour token or it might be stored in plain text in this notebook file.
" } }, "a2cfb91cf66447d7899292854bd64a07": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "c1a82965ae26479a98e4fdbde1e64ec2": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9d847f9a7d47458d8cd57d9b599e47c6", "placeholder": "​", "style": "IPY_MODEL_42d140b838b844819bc127afc1b7bc84", "value": "\nPro Tip: If you don't already have one, you can create a dedicated\n'notebooks' token with 'write' access, that you can then easily reuse for all\nnotebooks. " } }, "caef095934ec47bbb8b64eab22049284": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "eaba3f1de4444aabadfea2a3dadb1d80": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": "" } }, "ee4a21bedc504171ad09d205d634b528": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ButtonStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ButtonStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "button_color": null, "font_weight": "" } }, "f1675c09d16a4251b403f9c56255f168": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ButtonModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ButtonModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ButtonView", "button_style": "", "description": "Login", "disabled": false, "icon": "", "layout": "IPY_MODEL_a2cfb91cf66447d7899292854bd64a07", "style": "IPY_MODEL_ee4a21bedc504171ad09d205d634b528", "tooltip": "" } }, "f6c845330d6743c0b35c2c7ad834de77": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "CheckboxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "CheckboxView", "description": "Add token as git credential?", "description_tooltip": null, "disabled": false, "indent": true, "layout": "IPY_MODEL_3e753b0212644990b558c68853ff2041", "style": "IPY_MODEL_eaba3f1de4444aabadfea2a3dadb1d80", "value": true } } } } }, "nbformat": 4, "nbformat_minor": 0 }