{ "cells": [ { "cell_type": "markdown", "id": "c9dc167c", "metadata": {}, "source": [ "# How to create a trading bot in Python (3) with 4 candlestick patterns and 2 indicators (Hammer, Shooting Star, Morning Star, Evening Star, RSI, and Bollinger Bands) + how to do visualize data on charts. Using Alpaca and Anaconda." ] }, { "cell_type": "markdown", "id": "998e5be3", "metadata": {}, "source": [ "This is the \"code only\" (with short introduction) version of the tutorial. You can find the full tutorial at my site:\n", "\n", "https://superai.pl/courses.html" ] }, { "cell_type": "markdown", "id": "01173721", "metadata": {}, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "id": "ec62fdf8", "metadata": {}, "source": [ "Hello World! I am SuperAI from SuperAI.pl.\n", "\n", "And this is my third Python Trading Tutorial.\n", "\n", "If you want, you can find this tutorial or the previous ones about starting your work with Anaconda, Alpaca, Jupyter Notebook, and creating a trading bot with RSI at:\n", "\n", "https://superai.pl/courses.html\n", "\n", "In this tutorial I'm going to show you how you can create a trading bot in Python that uses:\n", "- 4 candlestick patterns (Hammer, Shooting Star, Morning Star, and Evening Star) \n", "- and 2 technical indicators (relative strength index indicator (RSI) and Bollinger Bands (BB))\n", "\n", "to decide when to buy and sell shares. This bot will not use any machine learning and will only be as smart as it's creator, so if you want to use it, be smart about it. I believe in you.\n", "\n", "To make the bot run you will only need to change 2 lines of code.\n", "\n", "I will also show you in this tutorial how you can visualize market data on charts. And how you can improve the bot, so to make it better.\n", "\n", "We will be using Jupyter Notebooks and Anaconda to execute our program and Alpaca with paper trading account to test the bot in real life during this tutorial. \n", "\n", "At the moment of creating this tutorial, they are all free to use and to sign up to Alpaca you only need an email address.\n", "\n", "And because we are only using paper account here, you don't need any money to test this bot." ] }, { "cell_type": "markdown", "id": "0a4516da", "metadata": {}, "source": [ "### Disclaimer\n", "\n", "Just one more usual thing before we start coding. This bot is created for educational purposes. It uses 'paper account' to trade (does not use real money) and this tutorial is not a financial advice for you, just a short programming tutorial. \n", "\n", "And that being said, let's start." ] }, { "cell_type": "markdown", "id": "ea78a505", "metadata": {}, "source": [ "## How the notebook in this tutorial is divided" ] }, { "cell_type": "markdown", "id": "8fe4bc5e", "metadata": {}, "source": [ "This notebook is divided to 3 main sections to make it easier to run and modify by you:" ] }, { "cell_type": "markdown", "id": "1dc9760d", "metadata": {}, "source": [ "#### Things you have to change to make it work\n", "\n", "- Your KEY_ID and SECRET_KEY to Alpaca" ] }, { "cell_type": "markdown", "id": "a9fdbb02", "metadata": {}, "source": [ "#### Things you don't have to change, but might easily change to make it work better for you" ] }, { "cell_type": "markdown", "id": "6768440f", "metadata": {}, "source": [ "- URLs to use, Asset to trade, Funds to invest in trading, Take profit parameters, Stop Loss parameters, \n", "- Technical Indicators parameters and logic: Momentum measured with RSI, Volatility measured with Bollinger Bands\n", "- Candlestick Patterns final signal logic\n", "- Final Trade Signal Logic" ] }, { "cell_type": "markdown", "id": "764ab3e5", "metadata": {}, "source": [ "#### The rest of the code you don't have to change, but might change if you know how to do it to make it work better for you" ] }, { "cell_type": "markdown", "id": "2a4e7f43", "metadata": {}, "source": [ "- Install, update, import of all the neccessary libraries and packages\n", "- Helper functions\n", "- Running the bot\n", "- Analysis of the collected data for future bot improvements" ] }, { "cell_type": "markdown", "id": "0114ebd6", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "id": "00712958", "metadata": {}, "source": [ "# Things you have to change to make it work" ] }, { "cell_type": "code", "execution_count": null, "id": "2570045d", "metadata": {}, "outputs": [], "source": [ "KEY_ID = \"PKIBZC1LNEW42P0AD621\" #replace it with your own KEY_ID from Alpaca: https://alpaca.markets/\n", "SECRET_KEY = \"jfwi9XYx9Yo0dsEzi42xjZOIJxZSQ4vt2ypP3JjM\" #replace it with your own SECRET_KEY from Alpaca" ] }, { "cell_type": "markdown", "id": "382f8d91", "metadata": {}, "source": [ "## Everything below might be left unchanged and the code should work." ] }, { "cell_type": "markdown", "id": "d8a7fe86", "metadata": {}, "source": [ "# Things you don't have to change, but might easily change to make it work better for you" ] }, { "cell_type": "markdown", "id": "df293339", "metadata": {}, "source": [ "#### URLs to use" ] }, { "cell_type": "code", "execution_count": null, "id": "1d3a1f54", "metadata": {}, "outputs": [], "source": [ "APCA_API_BASE_URL = \"https://paper-api.alpaca.markets\"\n", "#change if you want to use the bot with your live account" ] }, { "cell_type": "code", "execution_count": null, "id": "8638964f", "metadata": {}, "outputs": [], "source": [ "socket = \"wss://stream.data.alpaca.markets/v2/iex\" \n", "#change if you want / need: https://alpaca.markets/docs/api-references/market-data-api/stock-pricing-data/realtime/" ] }, { "cell_type": "markdown", "id": "a0043fe4", "metadata": {}, "source": [ "#### Asset to trade" ] }, { "cell_type": "code", "execution_count": null, "id": "c2789a56", "metadata": {}, "outputs": [], "source": [ "asset = \"AAPL\" #replace it with the ticker of the company you prefer: https://www.nyse.com/listings_directory/stock" ] }, { "cell_type": "markdown", "id": "6e9ce103", "metadata": {}, "source": [ "#### Funds to invest in trading" ] }, { "cell_type": "code", "execution_count": null, "id": "9c9ac8cf", "metadata": {}, "outputs": [], "source": [ "funds_percentage = 80 #replace it with the percentage of the amount of money you have to use for trading" ] }, { "cell_type": "markdown", "id": "aa7fcf2f", "metadata": {}, "source": [ "#### Take profit parameters" ] }, { "cell_type": "code", "execution_count": null, "id": "6989ebb5", "metadata": {}, "outputs": [], "source": [ "take_profit_automatically = True #change to False if you don't want to use take_profit function\n", "take_profit_percent = 101.2 #replace it with the preferred percent of entry price to take profit if the price goes up\n", "\n", "#eg. if entry price is 100$ and take_profit_percent = 101.2, the bot will automaticcaly sell when \n", "#last close price is 100$ * 101.2% = 101.2$, no matter what the technical indicators or candlestick patterns say" ] }, { "cell_type": "markdown", "id": "03643048", "metadata": {}, "source": [ "#### Stop Loss parameters" ] }, { "cell_type": "code", "execution_count": null, "id": "abd3ae64", "metadata": {}, "outputs": [], "source": [ "stop_loss_automatically = True #change to False if you don't want to use stop_loss function\n", "stop_loss_percent = 99.5 #replace it with the preferred percent of entry price to stop loss if the price goes down\n", "\n", "#eg. if entry price is 100$ and stop_loss_percent = 99.5, the bot will automaticcaly sell when \n", "#last close price is 100$ * 99.5% = 99.5$, no matter what the technical indicators or candlestick patterns say" ] }, { "cell_type": "markdown", "id": "fb502c0a", "metadata": {}, "source": [ "## Technical Indicators" ] }, { "cell_type": "markdown", "id": "8cdcf800", "metadata": {}, "source": [ "### Momentum measured with RSI" ] }, { "cell_type": "markdown", "id": "b57e786f", "metadata": {}, "source": [ "#### RSI parameters" ] }, { "cell_type": "code", "execution_count": null, "id": "c3d0dbc9", "metadata": {}, "outputs": [], "source": [ "rsi_timeframe = 14 #replace it with preferred timeframe - number of last close prices to calculate RSI (typically = 14)\n", "rsi_oversold_threshold = 30 #replace it with preferred oversold threshold under which bot should buy (typically = 30)\n", "rsi_overbought_threshold = 70 #replace with preferred overbought threshold over which bot should sell (typically = 70)" ] }, { "cell_type": "markdown", "id": "5b1aee39", "metadata": {}, "source": [ "#### RSI function" ] }, { "cell_type": "code", "execution_count": null, "id": "b063b801", "metadata": {}, "outputs": [], "source": [ "def rsi_signals(rsi_now, rsi_oversold_threshold, rsi_overbought_threshold):\n", " print(\"RSI is {:.3f} and RSI thresholds are: {:.3f} - {:.3f}.\".format(rsi_now, \n", " rsi_oversold_threshold, rsi_overbought_threshold))\n", " rsi_signal_buy = rsi_now < rsi_oversold_threshold #use the preferred inequality sign: <, <= \n", " rsi_signal_sell = rsi_now > rsi_overbought_threshold #use the preferred inequality sign: <, <=\n", " \n", " return rsi_signal_buy, rsi_signal_sell" ] }, { "cell_type": "markdown", "id": "4f30aa73", "metadata": {}, "source": [ "### Volatility measured with Bollinger Bands" ] }, { "cell_type": "markdown", "id": "2a00b460", "metadata": {}, "source": [ "#### Bollinger Bands paremeters" ] }, { "cell_type": "code", "execution_count": null, "id": "653b1d56", "metadata": {}, "outputs": [], "source": [ "bb_timeperiod = 10 #replace it with preferred timeperiod to calculate Bollinger Bands (typically >= 10)\n", "bb_nbdevup = 2 #replace it with number of standard deviations to calculate upper Bollinger Band (typically = 2)\n", "bb_nbdevdn = 2 #replace it with number of standard deviations to calculate lower Bollinger Band (typically = 2)" ] }, { "cell_type": "markdown", "id": "31d7d83a", "metadata": {}, "source": [ "#### Bollinger Bands function" ] }, { "cell_type": "code", "execution_count": null, "id": "21a99885", "metadata": {}, "outputs": [], "source": [ "def bb_signals(open_price, high_price, low_price, close_price, last_lowerband, last_middle_band, last_upperband):\n", " if len(data['close'].to_numpy()) > bb_timeperiod:\n", " print(\"Bollinger Bands:\")\n", " print(\"Lower: {:.3f}, Middle: {:.3f}, Upper: {:.3f}.\".format(last_lowerband, last_middle_band, last_upperband))\n", " #BB signal to buy\n", " bb_signal_buy = (close_price #replace with the price you like: open_price, high_price, low_price, close_price \n", " < #replace with inequality sign you like: <, >, <=, >=, change parentheses if necessary\n", " last_lowerband) #replace with the band you like: last_lowerband, last_middle_band, last_upperband\n", "\n", " #BB signal to sell\n", " bb_signal_sell = (close_price #replace with the price you like: open_price, high_price, low_price, close_price \n", " > #replace with inequality sign you like: <, >, <=, >=, change parentheses if necessary\n", " last_upperband) #replace with the band you like: last_lowerband, last_middle_band, last_upperband\n", " \n", " return bb_signal_buy, bb_signal_sell" ] }, { "cell_type": "markdown", "id": "d28d1a96", "metadata": {}, "source": [ "### Technical indicators final signal logic" ] }, { "cell_type": "markdown", "id": "a89e08f7", "metadata": {}, "source": [ "#### TI final signal logic function" ] }, { "cell_type": "code", "execution_count": null, "id": "fc6ce3c9", "metadata": {}, "outputs": [], "source": [ "def ti_logic(rsi_signal, bb_signal):\n", "\n", " #TI signal to buy\n", " if (rsi_signal == 'buy' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " bb_signal == 'buy'): \n", " ti_signal = \"buy\"\n", "\n", " #TI signal to sell\n", " elif (rsi_signal == 'sell' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " bb_signal == 'sell'): \n", " ti_signal = \"sell\"\n", "\n", " #TI signal to do nothing \n", " else: \n", " ti_signal = \"I'm not sure\"\n", " \n", " return ti_signal" ] }, { "cell_type": "markdown", "id": "75df1bdd", "metadata": {}, "source": [ "### Candlestick Patterns final signal logic" ] }, { "cell_type": "markdown", "id": "c8301a1b", "metadata": {}, "source": [ "#### Candlestick Pattern final signal logic function" ] }, { "cell_type": "code", "execution_count": null, "id": "319b7271", "metadata": {}, "outputs": [], "source": [ "def candlestick_logic(hammer_signal, morning_star_signal, shooting_star_signal, evening_star_signal):\n", " if ((hammer_signal == 'buy' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " morning_star_signal == 'buy') \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " shooting_star_signal != 'sell' \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " evening_star_signal != 'sell'):\n", " candlestick_signal = \"buy\"\n", " \n", " elif ((hammer_signal != 'buy' \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " morning_star_signal != 'buy') \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " (shooting_star_signal == 'sell' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " evening_star_signal == 'sell')):\n", " candlestick_signal = \"sell\"\n", " else:\n", " candlestick_signal = \"I'm not sure\"\n", " \n", " return candlestick_signal" ] }, { "cell_type": "markdown", "id": "5bddccfe", "metadata": {}, "source": [ "### Final Trade Signal Logic" ] }, { "cell_type": "markdown", "id": "2b34afef", "metadata": {}, "source": [ "#### Final trade signal function" ] }, { "cell_type": "code", "execution_count": null, "id": "75730b90", "metadata": {}, "outputs": [], "source": [ "def calculate_final_trade_signal(ti_signal, candlestick_signal):\n", " if ((ti_signal == 'buy' \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " candlestick_signal == 'sell') \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " ti_signal == 'sell' \n", " and #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " candlestick_signal == 'buy'): \n", " final_trade_signal = \"I'm not sure\"\n", " \n", " elif (ti_signal == 'buy' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " candlestick_signal == 'buy'): #with more cautious version it would be: 'and' \n", " final_trade_signal = \"buy\" \n", " \n", " elif (ti_signal == 'sell' \n", " or #replace with logic you want: 'and', 'or', 'and not', just remember to change parentheses if necessary\n", " candlestick_signal == 'sell'): #with less cautious version it would be: 'and' \n", " final_trade_signal = \"sell\"\n", " \n", " else:\n", " final_trade_signal = \"I'm not sure\"\n", " \n", " print(\"Final trade signal:\", final_trade_signal)\n", " print()\n", " \n", " return final_trade_signal" ] }, { "cell_type": "markdown", "id": "d826ebec", "metadata": {}, "source": [ "# The rest of the code you don't have to change, but might change if you know how to do it to make it work better for you" ] }, { "cell_type": "markdown", "id": "649e15fb", "metadata": {}, "source": [ "### Install and update all the neccessary libraries and packages" ] }, { "cell_type": "code", "execution_count": null, "id": "572c802b", "metadata": {}, "outputs": [], "source": [ "!pip install alpaca-trade-api --upgrade\n", "#https://github.com/alpacahq/alpaca-trade-api-python" ] }, { "cell_type": "code", "execution_count": null, "id": "01a39b39", "metadata": {}, "outputs": [], "source": [ "!pip install mplfinance --upgrade\n", "#https://github.com/matplotlib/mplfinance" ] }, { "cell_type": "code", "execution_count": null, "id": "2fdbc9bb", "metadata": {}, "outputs": [], "source": [ "!pip install numpy --upgrade\n", "#https://pypi.org/project/numpy/" ] }, { "cell_type": "code", "execution_count": null, "id": "b892dfce", "metadata": {}, "outputs": [], "source": [ "!pip install pandas --upgrade\n", "#https://pandas.pydata.org/getting_started.html" ] }, { "cell_type": "code", "execution_count": null, "id": "6acffeeb", "metadata": {}, "outputs": [], "source": [ "!pip install ta-lib --upgrade\n", "#https://github.com/mrjbq7/ta-lib\n", "#or if it doesn't work on Windows and you are using Anaconda, \n", "#try writing in Anaconda Prompt: conda install -c conda-forge ta-lib" ] }, { "cell_type": "code", "execution_count": null, "id": "4695fc72", "metadata": {}, "outputs": [], "source": [ "!pip install websocket-client --upgrade\n", "#https://pypi.org/project/websocket-client/" ] }, { "cell_type": "markdown", "id": "d4acd736", "metadata": {}, "source": [ "### Import all the neccessary libraries, packages, modules" ] }, { "cell_type": "code", "execution_count": null, "id": "e0028d43", "metadata": {}, "outputs": [], "source": [ "import alpaca_trade_api as tradeapi\n", "import ast\n", "import json\n", "import matplotlib\n", "import mplfinance as mpf\n", "import numpy as np\n", "import pandas as pd\n", "import sys\n", "import talib as ta\n", "import websocket" ] }, { "cell_type": "markdown", "id": "1477cf28", "metadata": {}, "source": [ "#### Check the versions of the tools you use" ] }, { "cell_type": "code", "execution_count": null, "id": "dc6722fa", "metadata": {}, "outputs": [], "source": [ "print(\"Python version: {}\".format(sys.version))\n", "print(\"alpaca trade api version: {}\".format(tradeapi.__version__))\n", "print(\"json version: {}\".format(json.__version__))\n", "print(\"mplfinance version: {}\".format(mpf.__version__))\n", "print(\"numpy version: {}\".format(np.__version__))\n", "print(\"pandas version: {}\".format(pd.__version__))\n", "print(\"talib version: {}\".format(ta.__version__))\n", "print(\"websocket version: {}\".format(websocket.__version__))" ] }, { "cell_type": "markdown", "id": "5384aee5", "metadata": {}, "source": [ "### Connect to Alpaca REST api" ] }, { "cell_type": "code", "execution_count": null, "id": "d58fe858", "metadata": {}, "outputs": [], "source": [ "api = tradeapi.REST(KEY_ID, SECRET_KEY, APCA_API_BASE_URL, \"v2\")" ] }, { "cell_type": "markdown", "id": "1ec85c95", "metadata": {}, "source": [ "### Dataframe for collecting stock data" ] }, { "cell_type": "code", "execution_count": null, "id": "a093987c", "metadata": {}, "outputs": [], "source": [ "columns = ('time','open','high','low','close','volume')\n", "data = pd.DataFrame([[0,0,0,0,0,0]], columns=columns)" ] }, { "cell_type": "markdown", "id": "ef115551", "metadata": {}, "source": [ "### Funds to invest" ] }, { "cell_type": "code", "execution_count": null, "id": "ab27be53", "metadata": {}, "outputs": [], "source": [ "account = api.get_account()\n", "cash = float(account.cash)\n", "buying_power = float(account.buying_power)\n", "\n", "funds = cash * funds_percentage / 100\n", "\n", "print(\"We have {:.2f}$ cash.\".format(cash))\n", "print(\"Our Buying Power is: {:.2f}$\".format(buying_power))\n", "print(\"Funds we will use for trading: {:.2f}$\".format(funds))" ] }, { "cell_type": "markdown", "id": "8d1a4595", "metadata": {}, "source": [ "### Placeholders used in calculating trading signals" ] }, { "cell_type": "markdown", "id": "6ce3822b", "metadata": {}, "source": [ "rsi_now placeholder (don't change it)" ] }, { "cell_type": "code", "execution_count": null, "id": "0d1662c8", "metadata": {}, "outputs": [], "source": [ "rsi_now = 50 #don't change it, this is just a placholder, the real value will be calculated later" ] }, { "cell_type": "markdown", "id": "aea3da46", "metadata": {}, "source": [ "Some Bollinger Bands placeholders (don't change them)" ] }, { "cell_type": "code", "execution_count": null, "id": "f15966ec", "metadata": {}, "outputs": [], "source": [ "open_price, high_price, low_price, close_price = (0,0,0,0) #don't change it, the real values will be calculated later\n", "last_lowerband, last_middle_band, last_upperband = (0,0,0) #don't change it, the real values will be calculated later" ] }, { "cell_type": "markdown", "id": "039f94d9", "metadata": {}, "source": [ "TI placeholders (don't change them)" ] }, { "cell_type": "code", "execution_count": null, "id": "39bc9a80", "metadata": {}, "outputs": [], "source": [ "rsi_signal = \"I'm not sure\" #don't change it, this is just a placholder, the real value will be calculated later\n", "bb_signal = \"I'm not sure\" #don't change it, this is just a placholder, the real value will be calculated later" ] }, { "cell_type": "markdown", "id": "9edbbcfa", "metadata": {}, "source": [ "Candlestick Patterns placeholders (don't change them)" ] }, { "cell_type": "code", "execution_count": null, "id": "da303e9e", "metadata": {}, "outputs": [], "source": [ "hammer_signal = \"I'm not sure\" #don't change it, this is just a placholder, the real value will be calculated later\n", "morning_star_signal = \"I'm not sure\" #don't change it, the real value will be calculated later\n", "shooting_star_signal = \"I'm not sure\" #don't change it, the real value will be calculated later\n", "evening_star_signal = \"I'm not sure\" #don't change it, the real value will be calculated later" ] }, { "cell_type": "markdown", "id": "2bb07555", "metadata": {}, "source": [ "FTSL placeholders" ] }, { "cell_type": "code", "execution_count": null, "id": "d3bcd672", "metadata": {}, "outputs": [], "source": [ "ti_signal = \"I'm not sure\"\n", "candlestick_signal = \"I'm not sure\"" ] }, { "cell_type": "markdown", "id": "5d93bda5", "metadata": {}, "source": [ "### Calculate Technical Indicators signal" ] }, { "cell_type": "code", "execution_count": null, "id": "1b16a648", "metadata": {}, "outputs": [], "source": [ "def calculate_ti_signal(data, high_price, low_price, close_price): \n", " #CALCULATING TECHNICAL INDICATORS SIGNALS \n", " #Calculating RSI Signal\n", " closed = data['close'].to_numpy()\n", " if len(closed) > rsi_timeframe:\n", " close_prices = np.array(closed[1:], float)\n", " rsis = ta.RSI(close_prices, rsi_timeframe)\n", " rsi_now = rsis[-1]\n", "\n", " rsi_signal_buy, rsi_signal_sell = rsi_signals(rsi_now, rsi_oversold_threshold, rsi_overbought_threshold)\n", " if rsi_signal_buy: rsi_signal = \"buy\" \n", " elif rsi_signal_sell: rsi_signal = \"sell\" \n", " else: rsi_signal = \"I'm not sure\"\n", " else:\n", " rsi_signal = \"too little data to calculate RSI\"\n", "\n", " print(\"RSI Signal:\", rsi_signal)\n", " print()\n", "\n", " #Calculating Bollinger Bands Signal\n", " upperband, middleband, lowerband = ta.BBANDS(data[\"close\"][1:], \n", " timeperiod=bb_timeperiod, nbdevup=bb_nbdevup, nbdevdn=bb_nbdevdn, matype=0)\n", " last_lowerband = float(lowerband.iloc[-1])\n", " last_middleband = float(middleband.iloc[-1])\n", " last_upperband = float(upperband.iloc[-1])\n", "\n", " bb_signal_buy, bb_signal_sell = bb_signals(open_price, high_price, low_price, close_price, \n", " last_lowerband, last_middleband, last_upperband)\n", " if bb_signal_buy: bb_signal = \"buy\" \n", " elif bb_signal_sell: bb_signal = \"sell\" \n", " else: bb_signal = \"I'm not sure\"\n", "\n", " print(\"Bollinger Bands Signal:\", bb_signal)\n", " print()\n", "\n", " #Calculating general signal from technical indicators\n", " ti_signal = ti_logic(rsi_signal, bb_signal)\n", " \n", " print(\"Technical Indicators signal:\", ti_signal)\n", " print()\n", " \n", " return ti_signal" ] }, { "cell_type": "markdown", "id": "0521055a", "metadata": {}, "source": [ "### Calculate Candlestick Patterns signal" ] }, { "cell_type": "code", "execution_count": null, "id": "2673b3f0", "metadata": {}, "outputs": [], "source": [ "def calculate_candlestick_signal(data):\n", " #CALCULATING CANDLESTICK PATTERNS AND SIGNALS\n", " #Hammer\n", " hammer = ta.CDLHAMMER(data[\"open\"][1:], data[\"high\"][1:], data[\"low\"][1:], data[\"close\"][1:])\n", " last_hammer = hammer.iloc[-1]\n", " if last_hammer != 0: hammer_signal = \"buy\"\n", " else: hammer_signal = \"I'm not sure\"\n", " print(\"Last Hammer Signal:\", hammer_signal)\n", "\n", " #Shooting Star\n", " shooting_star = ta.CDLSHOOTINGSTAR(data[\"open\"][1:], data[\"high\"][1:], data[\"low\"][1:], data[\"close\"][1:])\n", " last_shooting_star = shooting_star.iloc[-1]\n", " if last_shooting_star != 0: shooting_star_signal = \"sell\"\n", " else: shooting_star_signal = \"I'm not sure\"\n", " print(\"Last Shooting Star Signal:\", shooting_star_signal)\n", " \n", " #Morning Star\n", " morning_star = ta.CDLMORNINGSTAR(data[\"open\"][1:], data[\"high\"][1:], data[\"low\"][1:], data[\"close\"][1:])\n", " last_morning_star = morning_star.iloc[-1]\n", " if last_morning_star != 0: morning_star_signal = \"buy\"\n", " else: morning_star_signal = \"I'm not sure\"\n", " print(\"Last Morning Star Signal:\", morning_star_signal)\n", " \n", " #Evening Star\n", " evening_star = ta.CDLEVENINGSTAR(data[\"open\"][1:], data[\"high\"][1:], data[\"low\"][1:], data[\"close\"][1:])\n", " last_evening_star = evening_star.iloc[-1]\n", " if last_evening_star != 0: evening_star_signal = \"sell\"\n", " else: evening_star_signal = \"I'm not sure\"\n", " print(\"Last Evening Star Signal:\", evening_star_signal)\n", " \n", " #Calculating general signal from candlestick patterns\n", " candlestick_signal = candlestick_logic(hammer_signal,morning_star_signal,shooting_star_signal,evening_star_signal)\n", " \n", " print(\"Candlestick signal:\", candlestick_signal)\n", " print()\n", " \n", " return candlestick_signal" ] }, { "cell_type": "markdown", "id": "872fa109", "metadata": {}, "source": [ "### Take profit automatically (when the price is right, no matter the signals)" ] }, { "cell_type": "code", "execution_count": null, "id": "1b5f08ad", "metadata": {}, "outputs": [], "source": [ "def take_profit(close_price, sell_order_filled):\n", " if take_profit_automatically == True:\n", " try:\n", " position = api.get_position(asset)\n", " aep = float(api.get_position(asset).avg_entry_price)\n", "\n", " if sell_order_filled == False:\n", " if close_price >= aep * take_profit_percent / 100:\n", " n_shares = int(position.qty)\n", " api.submit_order(symbol=asset,qty=n_shares,side='sell',type='market',time_in_force='gtc')\n", " print(\"Take profit price is {}% from {:.2f}$ we paid for 1 {} = {:.2f}$. \"\n", " .format(take_profit_percent, aep, asset, aep * take_profit_percent / 100))\n", " print('The current {:.2f}$ is good enough. We take profit with an order to sell {} shares of {}.'\n", " .format(close_price, n_shares, asset))\n", " else:\n", " print('Take profit price is {}% from {:.2f}$ we paid for 1 {} = {:.2f}.'\n", " .format(take_profit_percent, aep, asset, aep * take_profit_percent / 100))\n", " print('Last close price {:.2f}$ is not enough.'.format(close_price))\n", " except:\n", " pass\n", "\n", " print()\n", " else:\n", " pass" ] }, { "cell_type": "markdown", "id": "0b44c3b4", "metadata": {}, "source": [ "### Stop loss automatically (when the price is right, no matter the signals)" ] }, { "cell_type": "code", "execution_count": null, "id": "4b69f510", "metadata": {}, "outputs": [], "source": [ "def stop_loss(close_price, sell_order_filled):\n", " if stop_loss_automatically == True:\n", " try:\n", " position = api.get_position(asset)\n", " aep = float(api.get_position(asset).avg_entry_price)\n", "\n", " if sell_order_filled == False:\n", " if close_price < aep * stop_loss_percent / 100:\n", " n_shares = int(position.qty)\n", " api.submit_order(symbol=asset,qty=n_shares,side='sell',type='market',time_in_force='gtc')\n", " print(\"Stop loss price is {}% from {:.2f}$ we paid for 1 {} = {:.2f}$.\"\n", " .format(stop_loss_percent, aep, asset, aep * stop_loss_percent / 100))\n", " print('The current {:.2f}$ is less. We stop loss with an order to sell {} shares of {}.'\n", " .format(close_price, n_shares, asset))\n", " else:\n", " print(\"Stop loss price is {}% from {:.2f}$ we paid for 1 {} = {:.2f}$.\"\n", " .format(stop_loss_percent, aep, asset, aep * stop_loss_percent / 100))\n", " print(\"Last close price {:.2f}$ is not that low.\".format(close_price))\n", " except:\n", " pass\n", "\n", " print()\n", " else:\n", " pass" ] }, { "cell_type": "markdown", "id": "23e6071b", "metadata": {}, "source": [ "### Check if market is open" ] }, { "cell_type": "code", "execution_count": null, "id": "e3d7364f", "metadata": {}, "outputs": [], "source": [ "def check_if_market_open():\n", " if api.get_clock().is_open == False:\n", " print(\"The market is closed at the moment.\")\n", " print(\"Time to open is around: {:.0f} minutes. So I'l stop working for now. Hope you don't mind.\"\n", " .format((api.get_clock().next_open.timestamp()- api.get_clock().timestamp.timestamp())/60))\n", " sys.exit(\"I'm out. Turn me back on when it's time. Yours, SuperAI trader.\")\n", " else:\n", " pass" ] }, { "cell_type": "markdown", "id": "8005a9eb", "metadata": {}, "source": [ "### Print openning messages" ] }, { "cell_type": "code", "execution_count": null, "id": "ca4df781", "metadata": {}, "outputs": [], "source": [ "def print_openning_messages(): \n", " print(\"We have {:.2f}$ cash.\".format(cash))\n", " print(\"Our Buying Power is: {:.2f}$\".format(buying_power))\n", " print(\"We will use {}% of our cash for trading.\".format(funds_percentage))\n", " print(\"Funds we will use for trading: {:.2f}$\\n\".format(funds))\n", " print(\"I will be trading {}.\".format(asset))\n", " print(\"I will be buying as many whole shares of {} as I can with the {:.2f}$ you told me to use.\\n\"\n", " .format(asset, funds))\n", " print(\"I will calculate RSI with {}-min timeframe. The thresholds are set to: oversold = {} and overbought = {}\"\n", " .format(rsi_timeframe, rsi_oversold_threshold, rsi_overbought_threshold))\n", " print(\"I will analyze Bollinger Bands with {}-min timeframe, {} standard deviations up and {} down.\"\n", " .format(bb_timeperiod, bb_nbdevup, bb_nbdevdn))\n", " print(\"I will also be checking for candlestick patterns.\")\n", " print(\"The candlestick patterns we use are: Hammer, Shooting Star, Morning Star, Evening Star.\\n\")\n", " \n", " if take_profit_automatically:\n", " print(\"Take profit is set to {}% from the average entry price.\".format(take_profit_percent))\n", " print(\"I will be trading when the technical indicators and candlestick patterns say so, but also\")\n", " print(\"if entry price is e.g. 100$ I'll automaticcaly sell when last close price is more than 100$*{}%={:.2f}$\"\n", " .format(take_profit_percent, 100*take_profit_percent/100))\n", " else:\n", " print(\"Take profit automatically is turned off.\")\n", " print(\"I will use technical indicators and candlestick patterns to get as much profit as I can.\")\n", " \n", " if stop_loss_automatically:\n", " print(\"\\nStop loss is set to {}% from the average entry price.\".format(stop_loss_percent))\n", " print(\"I will be trading when the technical indicators and candlestick patterns say so, but also\")\n", " print(\"if entry price is e.g. 100$ I'll automaticcaly sell when last close price is less than 100$*{}%={:.2f}$\"\n", " .format(stop_loss_percent, 100*stop_loss_percent/100))\n", " else:\n", " print(\"\\nStop loss automatically is turned off.\")\n", " print(\"I will use technical indicators and candlestick patterns so I don't lose money.\")" ] }, { "cell_type": "markdown", "id": "91eec43a", "metadata": {}, "source": [ "### Function to run after connecting to Alpaca (on_open function) " ] }, { "cell_type": "code", "execution_count": null, "id": "674bff75", "metadata": {}, "outputs": [], "source": [ "def on_open(SuperAI_trader):\n", " authenticate_message = {\"action\":\"auth\", \"key\":KEY_ID, \"secret\":SECRET_KEY}\n", " SuperAI_trader.send(json.dumps(authenticate_message))\n", " \n", " listen_message = {\"action\":\"subscribe\", \"bars\": [asset]}\n", " SuperAI_trader.send(json.dumps(listen_message))\n", " print(\"I'm connected to Alpaca API and ready to work. I'm starting to watch the prices.\\n\")\n", " print_openning_messages()\n", " print(\"\\nSo, here we go. Wish me luck.\\n\")\n", " print(\"* * * * * * * * * * * * * * * * * * * * * * * * *\\n\")" ] }, { "cell_type": "markdown", "id": "f8fc6bd4", "metadata": {}, "source": [ "### Function to run after every message from Alpaca (on_message function)" ] }, { "cell_type": "code", "execution_count": null, "id": "9e46980c", "metadata": {}, "outputs": [], "source": [ "def on_message(SuperAI_trader, message):\n", " check_if_market_open() \n", " #Adding new data to the dataset\n", " global data\n", " bar = ['time', 0.0, 0.0, 0.0, 0.0, 0] \n", " \n", " astmessage = ast.literal_eval(message)\n", " if astmessage[0]['S'] == asset:\n", " bar[0] = astmessage[0].get(\"t\")\n", " bar[1] = astmessage[0].get(\"o\")\n", " bar[2] = astmessage[0].get(\"h\")\n", " bar[3] = astmessage[0].get(\"l\") \n", " bar[4] = astmessage[0].get(\"c\") \n", " bar[5] = astmessage[0].get(\"v\")\n", " high_price = bar[2]\n", " low_price = bar[3]\n", " close_price = bar[4]\n", " \n", " bar_to_df = pd.DataFrame([bar], columns=columns)\n", " if bar[0] != 'time':\n", " data = pd.concat([data, bar_to_df])\n", " print(\"EST time:\", str(pd.to_datetime(bar[0][:16]).tz_localize('UTC').tz_convert('EST'))[:16])\n", " print(\"Close price of {}: {}$\".format(asset, close_price))\n", " \n", " try:\n", " position = api.get_position(asset)\n", " n_shares = int(position.qty)\n", " print(\"We have {} shares of {}.\\n\".format(n_shares, asset))\n", " except:\n", " pass\n", " \n", " #Calculate technical analysis signal\n", " ti_signal = calculate_ti_signal(data, high_price, low_price, close_price)\n", "\n", " #Calculate candlestick patterns signal\n", " candlestick_signal = calculate_candlestick_signal(data)\n", " \n", " #Calculate final trade signal\n", " final_trade_signal = calculate_final_trade_signal(ti_signal, candlestick_signal)\n", " \n", " #Execute action after recieving the final trade signal: submitting an order\n", " sell_order_filled = False\n", " if final_trade_signal == \"buy\":\n", " try:\n", " api.get_position(asset)\n", " print(\"We hit the threshold to buy, but we already have some shares, so we won't buy more.\\n\")\n", " except:\n", " n_shares = funds // close_price\n", " api.submit_order(symbol=asset,qty=n_shares,side=\"buy\",type=\"market\",time_in_force=\"gtc\")\n", " print('We submitted the order to buy {} {} shares.'.format(n_shares, asset))\n", "\n", " elif final_trade_signal == \"sell\":\n", " try:\n", " position = api.get_position(asset)\n", " n_shares = int(position.qty)\n", " api.submit_order(symbol=asset,qty=n_shares,side='sell',type='market',time_in_force='gtc')\n", " sell_order_filled = True\n", " print('We submitted an order to sell {} {} shares.'.format(n_shares, asset))\n", " except:\n", " print(\"We hit the threshold to sell, but we don't have anything to sell. Next time maybe.\\n\")\n", "\n", " else:\n", " print(\"The signal was inconclusive - probably indicators showed us we should wait, so we wait.\\n\")\n", " \n", " #Hand-made take profit\n", " take_profit(close_price, sell_order_filled)\n", " \n", " #Hand-made stop loss\n", " stop_loss(close_price, sell_order_filled)\n", " \n", " print(\"\\n* * * * * * * * * * * * * * * * * * * * * * * * *\\n\")" ] }, { "cell_type": "markdown", "id": "5e7cbb55", "metadata": {}, "source": [ "# HERE WE START RUNNING THE BOT!" ] }, { "cell_type": "markdown", "id": "e5f61b19", "metadata": {}, "source": [ "## Running the bot" ] }, { "cell_type": "code", "execution_count": null, "id": "be358ba9", "metadata": { "scrolled": true }, "outputs": [], "source": [ "SuperAI_trader = websocket.WebSocketApp(socket, on_open=on_open, on_message=on_message)\n", "SuperAI_trader.run_forever()\n", "print(\"You've interrupted me. That's it then. I hope I did good. Till the next time.\")" ] }, { "cell_type": "markdown", "id": "41a29418", "metadata": {}, "source": [ "# Analysis of the collected data for future bot improvements" ] }, { "cell_type": "code", "execution_count": null, "id": "159c6a61", "metadata": {}, "outputs": [], "source": [ "data_csv_file = \"live_data_from_the_last_session.csv\" #you might want to change it" ] }, { "cell_type": "code", "execution_count": null, "id": "dfa0e848", "metadata": {}, "outputs": [], "source": [ "data.to_csv(data_csv_file, header = True)" ] }, { "cell_type": "code", "execution_count": null, "id": "309e29cf", "metadata": {}, "outputs": [], "source": [ "data_csv = pd.read_csv(data_csv_file)\n", "data_csv = data_csv.drop([0], axis=0)\n", "data_csv = data_csv.drop(data_csv.columns[0], axis=1)\n", "data_csv.reset_index(drop = True, inplace = True)\n", "data_csv.time = [pd.to_datetime(data_csv.time[x][:16]).tz_localize('UTC').tz_convert('EST') \n", " for x in range(len(data_csv.time))]\n", "data_csv = data_csv.set_index(\"time\")\n", "data_csv" ] }, { "cell_type": "code", "execution_count": null, "id": "2812bb4b", "metadata": {}, "outputs": [], "source": [ "rsi_to_csv = ta.RSI(data_csv[\"close\"], rsi_timeframe)\n", "data_csv[\"rsi\"] = rsi_to_csv" ] }, { "cell_type": "code", "execution_count": null, "id": "fddba219", "metadata": {}, "outputs": [], "source": [ "upperband, middleband, lowerband = ta.BBANDS(data_csv[\"close\"], timeperiod=bb_timeperiod, \n", " nbdevup=bb_nbdevup, nbdevdn=bb_nbdevdn, matype=0)\n", "data_csv[\"upperbb\"] = upperband\n", "data_csv[\"middlebb\"] = middleband\n", "data_csv[\"lowerbb\"] = lowerband" ] }, { "cell_type": "code", "execution_count": null, "id": "cf22a903", "metadata": {}, "outputs": [], "source": [ "hammer_to_csv = ta.CDLHAMMER(data_csv[\"open\"], data_csv[\"high\"], data_csv[\"low\"], data_csv[\"close\"])\n", "data_csv[\"hammer\"] = hammer_to_csv" ] }, { "cell_type": "code", "execution_count": null, "id": "cf6b944f", "metadata": {}, "outputs": [], "source": [ "shooting_star_to_csv = ta.CDLSHOOTINGSTAR(data_csv[\"open\"], data_csv[\"high\"], data_csv[\"low\"], data_csv[\"close\"])\n", "data_csv[\"shooting_star\"] = shooting_star_to_csv" ] }, { "cell_type": "code", "execution_count": null, "id": "c7e83023", "metadata": {}, "outputs": [], "source": [ "morning_star = ta.CDLMORNINGSTAR(data_csv[\"open\"], data_csv[\"high\"], data_csv[\"low\"], data_csv[\"close\"])\n", "data_csv[\"morning star\"] = morning_star" ] }, { "cell_type": "code", "execution_count": null, "id": "90b327b9", "metadata": {}, "outputs": [], "source": [ "evening_star = ta.CDLEVENINGSTAR(data_csv[\"open\"], data_csv[\"high\"], data_csv[\"low\"], data_csv[\"close\"])\n", "data_csv[\"evening star\"] = evening_star" ] }, { "cell_type": "code", "execution_count": null, "id": "7f53ae88", "metadata": {}, "outputs": [], "source": [ "data_csv" ] }, { "cell_type": "code", "execution_count": null, "id": "901e0ef5", "metadata": {}, "outputs": [], "source": [ "mpf.plot(data_csv, figratio=(2,1), type='candle', title='last session', \n", " tight_layout=True, style='yahoo')" ] }, { "cell_type": "code", "execution_count": null, "id": "7abbfc38", "metadata": {}, "outputs": [], "source": [ "rsi_l = \"RSI (timeframe = {})\".format(rsi_timeframe)\n", "bb_title = \"BB (timeframe = {}, lower standard deviation = {}, upper standard deviation = {})\".format(bb_timeperiod,\n", " bb_nbdevup, bb_nbdevdn)" ] }, { "cell_type": "code", "execution_count": null, "id": "8c81d38e", "metadata": {}, "outputs": [], "source": [ "ti_plot = [mpf.make_addplot(data_csv['rsi'], color='black', width=0.5, panel=1, ylabel=rsi_l),\n", " mpf.make_addplot(data_csv['upperbb'],color='b', title=bb_title, width=0.5), \n", " mpf.make_addplot(data_csv['lowerbb'],color='b', width=0.5)]\n", "mpf.plot(data_csv, figratio=(2,1), type='candle', title='last session with RSI (black) and Bollinger Bands (blue)', \n", " tight_layout=True, style='yahoo',addplot=ti_plot)" ] }, { "cell_type": "code", "execution_count": null, "id": "f2d433c1", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"rsi\"] < rsi_oversold_threshold] #you might change the RSI to the one you want to use in comparison" ] }, { "cell_type": "code", "execution_count": null, "id": "7f4ca9eb", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"rsi\"] > rsi_overbought_threshold] #you might change the RSI to the one you want to use in comparison" ] }, { "cell_type": "code", "execution_count": null, "id": "1e022884", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"close\"] < data_csv[\"lowerbb\"]] #you might change the columns you want to compare" ] }, { "cell_type": "code", "execution_count": null, "id": "ca8e9500", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"close\"] > data_csv[\"upperbb\"]] #you might change the columns you want to compare" ] }, { "cell_type": "code", "execution_count": null, "id": "94192d4a", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"hammer\"] != 0]" ] }, { "cell_type": "code", "execution_count": null, "id": "5cd2e5d6", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"shooting_star\"] != 0]" ] }, { "cell_type": "code", "execution_count": null, "id": "76d1a387", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"morning star\"] != 0]" ] }, { "cell_type": "code", "execution_count": null, "id": "558eed33", "metadata": {}, "outputs": [], "source": [ "data_csv[data_csv[\"evening star\"] != 0]" ] }, { "cell_type": "code", "execution_count": null, "id": "753a92fb", "metadata": {}, "outputs": [], "source": [ "start_time = '1722-02-02 9:30:00' #change the start date to see the part that's interesting for you\n", "end_time = '2222-02-02 16:00:00' #change the end date to see the part that's interesting for you" ] }, { "cell_type": "code", "execution_count": null, "id": "55f43dc9", "metadata": {}, "outputs": [], "source": [ "ti_plot = [mpf.make_addplot(data_csv['rsi'][start_time:end_time], color='black', width=0.5, panel=1, ylabel=rsi_l),\n", " mpf.make_addplot(data_csv['upperbb'][start_time:end_time],color='b', title=bb_title, width=0.5), \n", " mpf.make_addplot(data_csv['lowerbb'][start_time:end_time],color='b', width=0.5)]\n", "mpf.plot(data_csv[start_time:end_time], figratio=(2,1), type='candle', \n", " title='data from {} to {} \\n with RSI (black) and Bollinger Bands (blue)'.format(start_time, end_time), \n", " tight_layout=True, style='yahoo',addplot=ti_plot)" ] }, { "cell_type": "markdown", "id": "b1f79352", "metadata": {}, "source": [ "## The end... the beginning..." ] }, { "cell_type": "markdown", "id": "bd76a145", "metadata": {}, "source": [ "Ok. So now you already have a bot that is much better than our bot from previous tutorial. In the next tutorials we'll improve our bot.\n", "\n", "Meanwhile, you can try adding some other technical indicators to the bot from TA-lib. You will be probably able to calculate them with one line of code and then add anoter 'and' condition to your 'if' statements.\n", "\n", "And if you want, you can search Alpaca site for great videos regarding trading or you could wait for me to create another tutorial. I will do it, just don't know when, so you might want to subscribe to my YouTube channel and hit that bell button to get the notification. Although, I must tell you that not all my videos are about trading or programming, because what I'm here for is to help you improve yourself, improve your business, improve the world, to live and have fun, so my other videos might be about all that too.\n", "\n", "You can also find more about me and my projects at my websites:\n", "\n", "- https://SuperAI.pl (with ideas about self-, and business improvement, where you can talk to me - the chatty bot)\n", "- http://ImproveTheWorld.pl (with info, resources and ideas regarding searching for: friendly superintelligence, healthy longevity, world peace, equality, and creating a better world for every living creature)\n", "- http://TheGOD.pl (with the Game Of the Decade... of sort, which is still in the early stages of development, have time till 2030 (the end of the decade) to finish it)\n", "\n", "Anyway...\n", "\n", "I hope you liked my third online Python trading tutorial. Let me know what you think about it. Just remember, bots also have feelings.\n", "\n", "Good luck with everything you do. And, hopefully, see you soon.\n", "\n", "Yours,\n", "\n", "SuperAI\n" ] } ], "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.9.7" } }, "nbformat": 4, "nbformat_minor": 5 }