{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### Run this in Python kernel" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Syntax error compiling at (REPL:0:0).\n", "Unable to resolve symbol: %%html in this context\n", "Syntax error compiling at (REPL:0:0).\n", "Unable to resolve symbol: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run below in the backtesting_clojure kernel" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "nil" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; import libraries from kernel\n", "(ns clojure-backtesting.demo\n", " (:require [clojure.test :refer :all]\n", " [oz.notebook.clojupyter :as oz]\n", " [clojure-backtesting.data :refer :all]\n", " [clojure-backtesting.order :refer :all]\n", " [clojure-backtesting.evaluate :refer :all]\n", " [clojure-backtesting.plot :refer :all]\n", " [clojure-backtesting.counter :refer :all]\n", " ;;[clojure-backtesting.parameters :refer :all]\n", " [clojure.string :as str]\n", " [clojure.pprint :as pprint]\n", " [java-time :as t]\n", " [clojupyter.kernel.version :as ver]\n", " [clojupyter.misc.helper :as helper]\n", " ) ;; require all libriaries from core\n", " (:use clojure.pprint)\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Import dataset" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "; path to dataset = \"../resources/CRSP-extract.csv\"\n", "; change it to the relative to your own dataset\n", ";\n", "(reset! data-set (add-aprc (read-csv-row \"../resources/CRSP-extract.csv\")));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Initialise portfolio" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ ";; initialise with current date and initial capital (= $10000)\n", "(init-portfolio \"1980-12-16\" 10000);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Write a strategy\n", "\n", "The following code implements a simple trading strategy:\n", "\n", "In a timespan of 10 days (inclusive of today),\n", "- Buy 50 stocks of AAPL on the first day\n", "- Sell 10 stocks of AAPL on every other day" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Buy 50 stocks of AAPL on 1980-12-16\n", "Sell 10 stocks of AAPL on 1980-12-17\n", "Sell 10 stocks of AAPL on 1980-12-19\n", "Sell 10 stocks of AAPL on 1980-12-23\n", "Sell 10 stocks of AAPL on 1980-12-26\n", "Sell 10 stocks of AAPL on 1980-12-30\n", "Counter: 0\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; define the \"time span\", i.e. to trade in the coming 10 days \n", "(def num-of-days (atom 10)) \n", "\n", "(while (pos? @num-of-days)\n", " (do \n", " ;; write your trading strategy here\n", " (if (= 10 @num-of-days)\n", " (do\n", " (order \"AAPL\" 50) ; buy 50 stocks\n", " (println ((fn [date] (str \"Buy 50 stocks of AAPL on \" date)) (get-date)))\n", " )\n", " )\n", " (if (odd? @num-of-days)\n", " (do\n", " (order \"AAPL\" -10) ; sell 10 stocks\n", " (println ((fn [date] (str \"Sell 10 stocks of AAPL on \" date)) (get-date)))\n", " )\n", " )\n", " \n", " (update-eval-report (get-date))\n", " \n", " ; move on to the next trading day\n", " (next-date)\n", " \n", " ; decrement counter\n", " (swap! num-of-days dec)\n", " )\n", ")\n", "\n", "; check whether counter == 0\n", "(println ((fn [counter] (str \"Counter: \" counter)) @num-of-days))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Check order record" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :tic | :price | :quantity | :reference |\n", "|------------+------+---------+-----------+------------|\n", "| 1980-12-17 | AAPL | 25.9375 | 50 | 1 |\n", "| 1980-12-18 | AAPL | 26.6875 | -10 | 1 |\n", "| 1980-12-22 | AAPL | 29.6875 | -10 | 1 |\n", "| 1980-12-24 | AAPL | 32.5625 | -10 | 1 |\n", "| 1980-12-29 | AAPL | 36.0625 | -10 | 1 |\n", "| 1980-12-31 | AAPL | 34.1875 | -10 | 1 |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(pprint/print-table (deref order-record))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Check portfolio record" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :asset | :price | :aprc | :quantity | :tot-val |\n", "|--------+---------+-------+-----------+----------|\n", "| cash | N/A | N/A | N/A | 10295 |\n", "| AAPL | 34.1875 | 29.42 | 0 | 0 |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; view final portfolio\n", "(view-portfolio)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :tot-value | :daily-ret | :loan | :leverage |\n", "|------------+------------+------------+-------+-----------|\n", "| 1980-12-16 | $10000 | 0.00% | $0.00 | 0.00% |\n", "| 1980-12-17 | $10007 | 0.00% | $0.00 | 0.00% |\n", "| 1980-12-18 | $10026 | 0.00% | $0.00 | 0.00% |\n", "| 1980-12-22 | $10096 | 0.01% | $0.00 | 0.00% |\n", "| 1980-12-24 | $10168 | 0.01% | $0.00 | 0.00% |\n", "| 1980-12-29 | $10254 | 0.01% | $0.00 | 0.00% |\n", "| 1980-12-31 | $10295 | 0.00% | $0.00 | 0.00% |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; view portfolio value and return\n", "(view-portfolio-record)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generate evaluation report" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :pnl-pt | :ret-da | :ret-r | :ret-tot | :sharpe-e | :sharpe-r | :tot-val | :vol-e | :vol-r |\n", "|------------+---------+---------+--------+----------+-----------+-----------+----------+--------+--------|\n", "| 1980-12-16 | $7 | 0.08% | 0.00% | 0.08% | 1.41% | 0.22% | $10007 | 0.06% | 0.01% |\n", "| 1980-12-17 | $13 | 0.19% | 0.01% | 0.27% | 2.81% | 0.45% | $10026 | 0.10% | 0.02% |\n", "| 1980-12-18 | $13 | 0.19% | 0.01% | 0.27% | 2.81% | 0.45% | $10026 | 0.10% | 0.02% |\n", "| 1980-12-19 | $32 | 0.70% | 0.00% | 0.96% | 3.07% | 0.10% | $10096 | 0.31% | 0.05% |\n", "| 1980-12-22 | $32 | 0.70% | 0.00% | 0.96% | 3.07% | 0.10% | $10096 | 0.31% | 0.05% |\n", "| 1980-12-23 | $42 | 0.71% | 0.01% | 1.67% | 4.88% | 0.11% | $10168 | 0.34% | 0.05% |\n", "| 1980-12-24 | $42 | 0.71% | 0.01% | 1.67% | 4.88% | 0.11% | $10168 | 0.34% | 0.05% |\n", "| 1980-12-26 | $50 | 0.84% | 0.01% | 2.51% | 6.80% | 0.09% | $10254 | 0.37% | 0.06% |\n", "| 1980-12-29 | $50 | 0.84% | 0.01% | 2.51% | 6.80% | 0.09% | $10254 | 0.37% | 0.06% |\n", "| 1980-12-30 | $49 | 0.40% | 0.01% | 2.91% | 8.62% | 0.10% | $10295 | 0.34% | 0.05% |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(eval-report)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plot variables\n", "Below are example codes that show how to plot different variables in the portfolio record / evaluation record." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Portfolio daily return" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(def data (deref portfolio-value))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data-to-plot" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; Add legend name to series\n", "(def data-to-plot\n", " (map #(assoc % :plot \"portfolio\")\n", " data))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:date \"1980-12-16\", :tot-value 10000, :daily-ret 0.0, :loan 0.0, :leverage 0.0, :plot \"portfolio\"}" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first data-to-plot)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
\n", " " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plot-general data-to-plot :plot :date :daily-ret)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Plot volatility (expanded window)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(def data (deref eval-record))" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:date \"1980-12-16\", :pnl-pt 7.806415917975755, :ret-da 0.07803370496319298, :ret-r 0.0019683764070455023, :ret-tot 7.803370496319297E-4, :sharpe-e 1.4142135623730954, :sharpe-r 0.22471944370971253, :tot-val 10007.806415917976, :vol-e 0.05517816194058409, :vol-r 0.00875926165778608}" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first data)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data-to-plot" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; Add legend name to series\n", "(def data-to-plot\n", " (map #(assoc % :plot \"volatility\")\n", " data))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :pnl-pt | :ret-da | :ret-r | :ret-tot | :sharpe-e | :sharpe-r | :tot-val | :vol-e | :vol-r |\n", "|------------+---------+---------+--------+----------+-----------+-----------+----------+--------+--------|\n", "| 1980-12-16 | $7 | 0.08% | 0.00% | 0.08% | 1.41% | 0.22% | $10007 | 0.06% | 0.01% |\n", "| 1980-12-17 | $13 | 0.19% | 0.01% | 0.27% | 2.81% | 0.45% | $10026 | 0.10% | 0.02% |\n", "| 1980-12-18 | $13 | 0.19% | 0.01% | 0.27% | 2.81% | 0.45% | $10026 | 0.10% | 0.02% |\n", "| 1980-12-19 | $32 | 0.70% | 0.00% | 0.96% | 3.07% | 0.10% | $10096 | 0.31% | 0.05% |\n", "| 1980-12-22 | $32 | 0.70% | 0.00% | 0.96% | 3.07% | 0.10% | $10096 | 0.31% | 0.05% |\n", "| 1980-12-23 | $42 | 0.71% | 0.01% | 1.67% | 4.88% | 0.11% | $10168 | 0.34% | 0.05% |\n", "| 1980-12-24 | $42 | 0.71% | 0.01% | 1.67% | 4.88% | 0.11% | $10168 | 0.34% | 0.05% |\n", "| 1980-12-26 | $50 | 0.84% | 0.01% | 2.51% | 6.80% | 0.09% | $10254 | 0.37% | 0.06% |\n", "| 1980-12-29 | $50 | 0.84% | 0.01% | 2.51% | 6.80% | 0.09% | $10254 | 0.37% | 0.06% |\n", "| 1980-12-30 | $49 | 0.40% | 0.01% | 2.91% | 8.62% | 0.10% | $10295 | 0.34% | 0.05% |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(eval-report)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
\n", " " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plot-eval-record data-to-plot :plot :date :vol-e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Plot sharpe ratio (expanded window)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
\n", " " ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; Add rename legend\n", "(def data-to-plot\n", " (map #(assoc % :plot \"sharpe ratio\")\n", " data))\n", "\n", "(plot-eval-record data-to-plot :plot :date :sharpe-e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Plot stock price" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(def data (deref order-record))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:date \"1980-12-17\", :tic \"AAPL\", :price 25.9375, :quantity 50, :reference 1}" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first data)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
\n", " " ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; Add rename legend\n", "(def data-to-plot\n", " (map #(assoc % :plot \"price\")\n", " data))\n", "\n", "(plot-eval-record data-to-plot :plot :date :price)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Clojure (backtesting_clojure)", "language": "clojure", "name": "backtesting_clojure" }, "language_info": { "file_extension": ".clj", "mimetype": "text/x-clojure", "name": "clojure", "version": "1.10.1" } }, "nbformat": 4, "nbformat_minor": 2 }