{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### Run this in Python kernel" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%html\n", " " ] }, { "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", " [clojure-backtesting.data :refer :all]\n", " [clojure-backtesting.data-management :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.portfolio :refer :all]\n", " [clojure-backtesting.parameters :refer :all]\n", " [clojure-backtesting.large-data :refer :all]\n", " [clojure.string :as str]\n", " [clojure.pprint :as pprint]\n", " [java-time :as t]\n", " [clojure.java.io :as io]\n", " [clojure.data.csv :as csv]\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": "markdown", "metadata": {}, "source": [ "### Initialise portfolio (Go back here everytime you want to restart.)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"1963-02-05\"" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "; path to dataset = \"../resources/CRSP-extract.csv\"\n", "; change it to the relative to your own dataset\n", ";\n", "(load-large-dataset \"../../data-sorted-cleaned/data-CRSP-sorted-cleaned.csv\" \"main\" add-aprc-by-date)\n", ";; load compustat\n", "(load-large-dataset \"../../data-sorted-cleaned/data-Compustat-sorted-cleaned.csv\" \"compustat\")\n", ";; (def y (swap! dataset-col assoc \"compustat\" (read-csv-lazy \"../../data-sorted-cleaned/data-Compustat-sorted-cleaned.csv\")))\n", "(set-main \"main\")\n", "(init-portfolio \"1963-02-05\" 10)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:ewretd \"-0.002846\", :HSICCD \"3679.0\", :CUM-RET 0.1454830196005322, :CFACSHR \"1.05\", :date \"1963-02-11\", :INIT-PRICE 5.875, :OPENPRC \"\", :SECSTAT \"R\", :SHROUT \"0.468\", :TICKER \"ACC\", :APRC 6.7950138076418956, :COMNAM \"ALLIED CENTRAL INC\", :PRIMEXCH \"A\", :TRDSTAT \"A\", :FACSHR \"\", :HEXCD \"2\", :RET \"0.030769\", :EXCHCD \"2\", :CFACPR \"1.05\", :DLRET \"\", :PRC \"8.375\", :vwretd \"-0.004841\", :FACPR \"\", :CUSIP \"1910510\", :NCUSIP \"\", :PERMCO \"23583\", :DIVAMT \"\", :PERMNO \"28871\", :SHRCD \"10\", :sprtrn \"-0.006196\", :VOL \"500.0\", :SICCD \"3679\"}" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; (next-date)\n", ";; (keys (deref available-tics) )\n", ";; (get-date)\n", "(get-line)\n", ";; (first (get (deref dataset-col) \"compustat\"))" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:ewretd \"0.001045\", :HSICCD \"7379.0\", :CUM-RET 0.11367103002080017, :CFACSHR \"76.875\", :date \"1963-02-08\", :INIT-PRICE 350.0, :OPENPRC \"\", :SECSTAT \"R\", :SHROUT \"27.678\", :TICKER \"IBM\", :APRC 392.13422210161104, :COMNAM \"INTERNATIONAL BUSINESS MACHS COR\", :PRIMEXCH \"N\", :TRDSTAT \"A\", :FACSHR \"\", :HEXCD \"1\", :RET \"0.002387\", :EXCHCD \"1\", :CFACPR \"77.200386\", :DLRET \"\", :PRC \"420.0\", :vwretd \"0.000528\", :FACPR \"\", :CUSIP \"45920010\", :NCUSIP \"\", :PERMCO \"20990\", :DIVAMT \"\", :PERMNO \"12490\", :SHRCD \"11\", :sprtrn \"0.0\", :VOL \"8000.0\", :SICCD \"3573\"}" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; (next-date)\n", ";; (keys (deref available-tics) )\n", ";; (get-date)\n", ";; (get-line)\n", ";; (first (get (deref dataset-col) \"compustat\"))\n", "(get (get (deref available-tics) \"IBM\") :reference)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1962-02-08\n", "1962-11-08\n" ] }, { "data": { "text/plain": [ "{:fincfq \"\", :dpcy \"\", :fic \"USA\", :itccy \"\", :cshoq \"\", :dltisq \"\", :wcapchq \"\", :niq \"59.213\", :dpcq \"\", :sstkq \"\", :cik \"51143.0\", :oancfq \"\", :loq \"\", :pstkrq \"\", :oibdpq \"\", :ipodate \"\", :datafqtr \"1962.5\", :prstkcq \"\", :ivncfy \"\", :pstkq \"\", :saleq \"467.7\", :ltq \"\", :lctq \"\", :txdbq \"\", :dlcq \"\", :ppentq \"\", :dpq \"\", :atq \"\", :dvpq \"\", :wcapchy \"\", :capxy \"\", :itccq \"\", :xintq \"\", :datacqtr \"1962.5\", :dltrq \"\", :ibq \"59.213\", :actq \"\", :mibtq \"\", :xrdq \"\", :sstky \"\", :invtq \"\", :dvy \"\", :oiadpq \"\", :mibq \"\", :icaptq \"\", :txditcq \"\", :prccq \"353.1442\", :ceqq \"\", :sppey \"\", :dvq \"\", :seqq \"\", :fincfy \"\", :capxq \"\", :revtq \"467.7\", :oancfy \"\", :ivncfq \"\", :cusip \"459200101\", :dltisy \"\", :gvkey \"6066\", :addzip \"10504\", :dlttq \"\", :prstkcy \"\", :rdq \"\", :sic \"7370.0\", :xsgaq \"\", :exchg \"11.0\", :dltry \"\", :rectq \"\", :sppeq \"\", :cogsq \"\", :tic \"IBM\", :cheq \"\", :datadate \"1962-09-30\", :conm \"INTL BUSINESS MACHINES CORP\"}" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; (count (deref available-tics))\n", ";; (end-order)\n", ";; (next-date)\n", "(get-compustat-line (get (get (deref available-tics) \"IBM\") :reference) \"compustat\")\n", ";; (get-line)\n", ";; (first(get (deref dataset-col) \"compustat\"))\n", ";; (first (rest (get (deref dataset-col) \"compustat\")))\n", ";; (compare \"1963-01-26\" (get-date))\n", ";; (get-date)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{\"PPL\" {:date \"1963-02-05\", :expiration 3, :quantity 10, :remaining false, :leverage true, :print true, :direct true}}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(order-lazy \"PPL\" 10 :print true)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "362.5\n", "Order: 1963-02-06 | PPL | 10.000000.\n", "\"Elapsed time: 23677.080628 msecs\"\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(time (while \n", " (< (compare (get-date) \"1965-02\") 0)\n", " ;(order-lazy \"PPL\" 10 :direct false)\n", " (update-eval-report (get-date))\n", " (next-date)))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\"Elapsed time: 916551.674745 msecs\"\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(time (while \n", " (< (compare (curr-date) \"2016-02\") 0)(next-day)))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"1965-02-01\"" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; (take 20 (deref portfolio-value)) \n", ";; (view-portfolio-record 20)\n", "(get-date)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Write a strategy\n", "\n", "The following code implements a trading strategy called Golden Rule:\n", "\n", "MA 50 cross above the MA 200 (golden cross)\n", "\n", "MA 200 cross below the MA 50 (death cross)\n", "\n", "So in the codes, MA50 and MA200 are compared on a daily basis, if golden cross occurs, then you set a buy order; if death cross occurs, then you set a sell order first \n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "(time (do (def MA50-vec-aapl [])\n", " (def MA200-vec-aapl [])\n", " (def MA50-vec-f [])\n", " (def MA200-vec-f [])\n", " (while (not= (get-date) \"2016-12-29\")\n", " (do\n", " ;; write your trading strategy here\n", " (def tics (deref available-tics)) ;20 ms\n", " (def MA50-vec-aapl (get-prev-n-days :PRC 50 \"AAPL\" MA50-vec-aapl (get (get tics \"AAPL\"):reference)))\n", " (def MA200-vec-aapl (get-prev-n-days :PRC 200 \"AAPL\" MA200-vec-aapl (get (get tics \"AAPL\") :reference)))\n", " (def MA50-vec-f (get-prev-n-days :PRC 50 \"F\" MA50-vec-f (get (get tics \"F\"):reference)))\n", " (def MA200-vec-f (get-prev-n-days :PRC 200 \"F\" MA200-vec-f (get (get tics \"F\") :reference)))\n", " (let [[MA50 MA200] [(moving-average :PRC MA50-vec-aapl) (moving-average :PRC MA200-vec-aapl)]]\n", " (if (> MA50 MA200)\n", " (order \"AAPL\" 1 :reference (get (get tics \"AAPL\") :reference) :print false) \n", " (order \"AAPL\" 0 :remaining true :reference (get (get tics \"AAPL\") :reference))))\n", " (let [[MA50 MA200] [(moving-average :PRC MA50-vec-f) (moving-average :PRC MA200-vec-f)]]\n", " (if (> MA50 MA200)\n", " (order \"F\" 1 :reference (get (get tics \"F\") :reference) :print false) \n", " (order \"F\" 0 :remaining true :reference (get (get tics \"F\") :reference))))\n", " ;(update-eval-report (get-date))\n", " (next-date)))))\n", "(.close wrtr)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(count (deref order-record))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Check portfolio record" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ ";; view final portfolio\n", "(view-portfolio)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :tot-value | :daily-ret | :tot-ret | :loan | :leverage |\n", "|------------+------------+------------+----------+---------+-----------|\n", "| 1963-02-05 | $10 | 0.00% | 0.00% | $0.00 | 0.00% |\n", "| 1963-02-06 | $11 | -5.83% | -5.83% | $362.50 | -103.24% |\n", "| 1963-02-07 | $7 | 7.46% | 12.58% | $362.50 | -102.12% |\n", "| 1963-02-08 | $6 | -5.07% | 20.09% | $362.50 | -101.78% |\n", "| 1963-02-11 | $8 | 3.38% | 5.69% | $362.50 | -102.48% |\n", "| 1963-02-12 | $6 | -0.00% | 20.40% | $362.50 | -101.76% |\n", "| 1963-02-13 | $7 | -8.78% | 11.62% | $362.50 | -102.16% |\n", "| 1963-02-14 | $9 | 3.05% | 0.74% | $362.50 | -102.77% |\n", "| 1963-02-15 | $12 | -5.47% | -8.79% | $362.50 | -103.45% |\n", "| 1963-02-18 | $9 | -0.00% | 1.49% | $362.50 | -102.72% |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; view portfolio value and return\n", "(view-portfolio-record 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generate evaluation report" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "| :date | :tot-value | :vol | :sharpe | :pnl-pt |\n", "|------------+------------+---------+-----------+---------|\n", "| 1963-02-06 | $11 | 4.1193% | -1.4142% | $1 |\n", "| 1963-02-07 | $7 | 6.6608% | 1.8884% | $-2 |\n", "| 1963-02-08 | $6 | 6.1197% | 3.2828% | $-3 |\n", "| 1963-02-11 | $8 | 5.6290% | 1.0103% | $-1 |\n", "| 1963-02-12 | $6 | 5.0347% | 4.0522% | $-3 |\n", "| 1963-02-13 | $7 | 5.6675% | 2.0504% | $-2 |\n", "| 1963-02-14 | $9 | 5.4642% | 0.1348% | $0 |\n", "| 1963-02-15 | $12 | 5.3506% | -1.6425% | $2 |\n", "| 1963-02-18 | $9 | 5.0600% | 0.2939% | $0 |\n", "| 1963-02-19 | $9 | 4.8539% | 0.8618% | $0 |\n", "| 1963-02-20 | $8 | 4.8346% | 1.5895% | $-1 |\n", "| 1963-02-21 | $11 | 4.6563% | -1.4574% | $1 |\n", "| 1963-02-25 | $11 | 4.7606% | -1.5677% | $1 |\n", "| 1963-02-26 | $11 | 4.6208% | -1.4181% | $1 |\n", "| 1963-02-27 | $9 | 4.4684% | 0.3075% | $0 |\n", "| 1963-02-28 | $8 | 4.3302% | 1.7501% | $-1 |\n", "| 1963-03-01 | $6 | 4.3743% | 4.4066% | $-3 |\n", "| 1963-03-04 | $8 | 4.3166% | 1.3664% | $-1 |\n", "| 1963-03-05 | $8 | 4.2156% | 1.2531% | $-1 |\n", "| 1963-03-06 | $7 | 4.2215% | 2.9620% | $-2 |\n", "| 1963-03-07 | $12 | 4.4807% | -1.9882% | $2 |\n", "| 1963-03-08 | $13 | 4.3927% | -3.0921% | $3 |\n", "| 1963-03-11 | $11 | 4.5966% | -1.8214% | $1 |\n", "| 1963-03-12 | $13 | 4.7277% | -2.8832% | $3 |\n", "| 1963-03-13 | $15 | 4.6342% | -4.2193% | $5 |\n", "| 1963-03-14 | $13 | 4.6527% | -2.8265% | $3 |\n", "| 1963-03-15 | $14 | 4.5777% | -3.8109% | $4 |\n", "| 1963-03-18 | $13 | 4.5245% | -3.3641% | $3 |\n", "| 1963-03-19 | $16 | 4.4940% | -5.1476% | $6 |\n", "| 1963-03-20 | $27 | 4.4197% | -10.6472% | $17 |\n" ] }, { "data": { "text/plain": [ "nil" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ ";; (update-eval-report (get-date))\n", "(eval-report 30)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plot variables" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(def data (deref portfolio-value))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#'clojure-backtesting.demo/data-to-plot" ] }, "execution_count": 8, "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": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{:date \"1963-02-05\", :tot-value 10, :daily-ret 0.0, :tot-ret 0.0, :loan 0.0, :leverage 0.0, :plot \"portfolio\"}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first data-to-plot)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Error printing return value at clojure.data.json/write-double (json.clj:368).\n", "JSON error: cannot write Double NaN\n" ] }, { "ename": "class clojure.lang.ExceptionInfo", "evalue": "", "output_type": "error", "traceback": [ " json.clj: 368 clojure.data.json$write_double/invokeStatic", " json.clj: 365 clojure.data.json$write_double/invoke", " json.clj: 286 clojure.data.json$fn__2762$G__2757__2769/invoke", " json.clj: 335 clojure.data.json$write_object/invokeStatic", " json.clj: 319 clojure.data.json$write_object/invoke", " json.clj: 286 clojure.data.json$fn__2762$G__2757__2769/invoke", " json.clj: 348 clojure.data.json$write_array/invokeStatic", " json.clj: 342 clojure.data.json$write_array/invoke", " json.clj: 286 clojure.data.json$fn__2762$G__2757__2769/invoke", " json.clj: 335 clojure.data.json$write_object/invokeStatic", " json.clj: 319 clojure.data.json$write_object/invoke", " json.clj: 286 clojure.data.json$fn__2762$G__2757__2769/invoke", " json.clj: 335 clojure.data.json$write_object/invokeStatic", " json.clj: 319 clojure.data.json$write_object/invoke", " json.clj: 286 clojure.data.json$fn__2762$G__2757__2769/invoke", " json.clj: 475 clojure.data.json$write/invokeStatic", " json.clj: 424 clojure.data.json$write/doInvoke", " RestFn.java: 425 clojure.lang.RestFn/invoke", " AFn.java: 156 clojure.lang.AFn/applyToHelper", " RestFn.java: 132 clojure.lang.RestFn/applyTo", " core.clj: 669 clojure.core$apply/invokeStatic", " core.clj: 660 clojure.core$apply/invoke", " json.clj: 482 clojure.data.json$write_str/invokeStatic", " json.clj: 477 clojure.data.json$write_str/doInvoke", " RestFn.java: 410 clojure.lang.RestFn/invoke", " clojupyter.clj: 50 oz.notebook.clojupyter$fn__24566$live_embed__24569/invoke", " clojupyter.clj: 90 oz.notebook.clojupyter$fn__24566$embed__24579/invoke", " clojupyter.clj: 110 oz.notebook.clojupyter$fn__24566$view_BANG_$reify__24586/to_mime", "nrepl_middleware.clj: 20 clojupyter.kernel.nrepl_middleware$mime_values$fn$reify__23032/send", " main.clj: 442 clojure.main$repl$read_eval_print__9086/invoke", " main.clj: 458 clojure.main$repl$fn__9095/invoke", " main.clj: 368 clojure.main$repl/doInvoke", " RestFn.java: 1523 clojure.lang.RestFn/invoke", " AFn.java: 22 clojure.lang.AFn/run", " AFn.java: 22 clojure.lang.AFn/run", " Thread.java: 832 java.lang.Thread/run" ] } ], "source": [ "(plot data-to-plot :plot :date :total-ret false)" ] }, { "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 }