diff --git "a/fin_rl_qlearning_v2.ipynb" "b/fin_rl_qlearning_v2.ipynb" new file mode 100644--- /dev/null +++ "b/fin_rl_qlearning_v2.ipynb" @@ -0,0 +1,1352 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "nwaAZRu1NTiI" + }, + "source": [ + "# Q-learning \n", + "\n", + "#### This version implements q-learning using a custom enviroment on a 4h tick ETHUSDT\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "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", + "!pip install binance_historical_data\n", + "!mkdir ./data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "id": "LNXxxKojNTiL" + }, + "outputs": [], + "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", + "from binance_historical_data import BinanceDataDumper\n", + "import glob\n" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "id": "dmAuEhZZNTiL" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Choose tickers to dump:\n", + "---> Found overall tickers: 2137\n", + "---> Filter to asked tickers: 1\n", + "------> Tickers left: 1\n", + "---> Exclude asked tickers: 1\n", + "------> Tickers left: 1\n", + "Download full data for 1 tickers: \n", + "---> Data Frequency: 4h\n", + "---> Start Date: 20170101\n", + "---> End Date: 20221217\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9a01a9e1a2ec4ad6acc88d66f23de10b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Tickers: 0%| | 0/1 [00:00 For ETHUSDT new data saved for: 0 months 0 days\n" + ] + } + ], + "source": [ + "# Get data\n", + "data_dumper = BinanceDataDumper(\n", + " path_dir_where_to_dump=\"./data\",\n", + " data_type=\"klines\", # aggTrades, klines, trades\n", + " data_frequency=\"4h\", # argument for data_type=\"klines\"\n", + ")\n", + "data_dumper.dump_data(\n", + " tickers=['ETHUSDT'],\n", + " date_start=None,\n", + " date_end=None,\n", + " is_to_update_existing=False,\n", + " tickers_to_exclude=[\"UST\"],\n", + ")\n", + "\n", + "def get_df():\n", + " df_arr = []\n", + " for f in glob.glob('./data/spot/monthly/klines/ETHUSDT/4h/*.csv'):\n", + " df= pd.read_csv(f)\n", + " df.columns = ['datetime', 'Open', 'High', 'Low', 'Close', 'Volume','close_time', 'qav', 'num_trades','taker_base_vol', 'taker_quote_vol', 'ignore'] \n", + " df['datetime'] = df['datetime'].div(1000)\n", + " df['datetime'] = pd.to_datetime(df['datetime'],unit='s')\n", + " df.set_index('datetime', inplace=True)\n", + " df = df.drop(['qav','taker_base_vol','taker_quote_vol','ignore','close_time'],axis=1)\n", + " df_arr.append(df)\n", + "\n", + " df_f = pd.concat(df_arr,axis=0)\n", + " df_f = df_f.replace(np.inf, np.nan)\n", + " df_f = df_f.dropna()\n", + " return pd.concat(df_arr,axis=0)\n", + "df = get_df()" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": {}, + "outputs": [], + "source": [ + "eth_train = df[0:-500]\n", + "eth_test = df[-500:]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "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": 47, + "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 return DO_NOTHING \n", + " if abs(np.max(Qtable[state])) > 0:\n", + " action = np.argmax(Qtable[state])\n", + " else:\n", + " action = 2\n", + " \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": 48, + "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", + " \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": 99, + "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", + " metadata = {'render.modes': ['human']}\n", + "\n", + " def __init__(self, df, max_steps=0):\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", + "\n", + " def reset(self):\n", + " self._done = False\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 = []\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.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._position_history))\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", + "\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", + "\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], 'yo')\n", + "\n", + " plt.suptitle(\n", + " \"Total Reward: %.6f\" % self._total_reward + ' ~ ' +\n", + " \"Total Profit: %.6f\" % self._total_profit\n", + " )\n", + "\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.append(1)\n", + "\n", + " elif action == Actions.Buy.value and self._position > 0:\n", + " step_reward += penalty\n", + " self._position_history.append(-1)\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.append(4)\n", + " self._trade_history.append(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.append(3)\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.append(2)\n", + " self._trade_history.append(step_reward)\n", + " elif action == Actions.Sell.value and self._position < 0:\n", + " step_reward += penalty\n", + " self._position_history.append(-1)\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.append(0)\n", + " elif action == Actions.Do_nothing.value and self._position < 0:\n", + " step_reward += -1 * price_diff\n", + " self._position_history.append(0)\n", + " elif action == Actions.Do_nothing.value and self._position == 0:\n", + " step_reward += -1 * abs(price_diff)\n", + " self._position_history.append(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['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['adx_r'] = ta.ADX(self.df['High'], self.df['Low'], self.df['Close'], timeperiod=timeperiod)\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", + "\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", + " self.df['state'] = self.df['di']*1000+ self.df['mfi']*100 + self.df['stock_d']*10 + self.df['adx']\n", + " self.df = self.df.dropna()\n", + " self.df['state'] = self.df['state'].astype(\"int\")\n", + " prices = self.df.loc[:, 'Close'].to_numpy()\n", + " # print(self.df.head(30))\n", + "\n", + " signal_features = self.df.loc[:, 'state'].to_numpy()\n", + "\n", + " return prices, signal_features" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [], + "source": [ + "# Training parameters\n", + "n_training_episodes = 10000 # 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": 118, + "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)" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "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": 120, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1801" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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": 121, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 417 + }, + "id": "FIQ0OqtsO3jo", + "outputId": "f98374ad-c7de-4dc4-80b1-25f018ad96eb" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(15,6))\n", + "plt.cla()\n", + "env.render()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.329999999999984,\n", + " 12.79000000000002,\n", + " -2.5500000000000114,\n", + " -3.6399999999999864,\n", + " -1.8199999999999932]" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "env._trade_history" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "884" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(np.unique(state_history, return_counts=True)[1])\n", + "# count = 0\n", + "# for i in range(len(state_history)):\n", + "# if state_history[i] == 1987:\n", + "# count +=1\n", + "# count" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "884" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Qtable_trading[1987]\n", + "len(np.unique(env.signal_features))" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate_agent(env, max_steps, n_eval_episodes, Q):\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", + " :param seed: The evaluation seed array (for taxi-v3)\n", + " \"\"\"\n", + " episode_rewards = []\n", + " episode_profits = []\n", + " for episode in tqdm(range(n_eval_episodes)):\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", + " action = greedy_policy(Q, state)\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", + " 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", + "\n", + " return mean_reward, std_reward, mean_profit, std_profit" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "135863eba0b4449d91486c62b9e76b04", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/200 [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": 128, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "200 0.9852216748768473\n" + ] + } + ], + "source": [ + "def count_equal(env1, env2):\n", + " count=0\n", + " for i in np.unique(env1.signal_features):\n", + " if i in env2.signal_features:\n", + " count+=1\n", + " \n", + " print(count, count / len(np.unique(env2.signal_features)))\n", + "\n", + "count_equal(env, env_test)" + ] + }, + { + "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 +}