Skip to content

Instantly share code, notes, and snippets.

@pschatzmann
Created November 7, 2018 22:09
Show Gist options
  • Save pschatzmann/35366ee70b9e3e0e6364977280d98b08 to your computer and use it in GitHub Desktop.
Save pschatzmann/35366ee70b9e3e0e6364977280d98b08 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{"metadata":{"kernelspec":{"display_name":"Scala","language":"scala","name":"scala"},"language_info":{"codemirror_mode":"text/x-scala","file_extension":".scala","mimetype":"","name":"Scala","nbconverter_exporter":"","version":"2.11.12"}},"nbformat_minor":2,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Portfolio Optimization\n\nPortfolio optimization is the process of selecting the best portfolio (asset distribution), out of the set of all portfolios being considered, according to some objective. The objective typically maximizes factors such as expected return, and minimizes costs like financial risk.\n\nIn this Blog we demonstrate how the [Invstor API](https://www.pschatzmann.ch/home/category/quantitative-trading/) can be used to determine an optimized 'Strategy Portfolios'. We use the RandomDistributor to generate randomly distributed porfolios and the KPIValues in order to determine the best combination. In our example we will optimize the 'Sharpe Ratio'!\n\nWe are using Jupyter with the [BeakerX](http://beakerx.com/) Scala kernel.\n\n## Setup\nWe load the relevant libraries and import the investor packages","metadata":{}},{"cell_type":"code","source":"%classpath config resolver maven http://software.pschatzmann.ch/repository/maven-public/\n%classpath add mvn ch.pschatzmann investor LATEST\n%classpath add mvn ch.pschatzmann jupyter-jdk-extensions LATEST\n\n// our investor framwork\nimport ch.pschatzmann.dates._\nimport ch.pschatzmann.stocks._\nimport ch.pschatzmann.stocks.data.universe._\nimport ch.pschatzmann.stocks.data.index._\nimport ch.pschatzmann.stocks.input._\nimport ch.pschatzmann.stocks.accounting._\nimport ch.pschatzmann.stocks.accounting.kpi._\nimport ch.pschatzmann.stocks.execution._\nimport ch.pschatzmann.stocks.execution.fees._\nimport ch.pschatzmann.stocks.execution.price._\nimport ch.pschatzmann.stocks.parameters._\nimport ch.pschatzmann.stocks.strategy._\nimport ch.pschatzmann.stocks.strategy.optimization._\nimport ch.pschatzmann.stocks.strategy.allocation._\nimport ch.pschatzmann.stocks.strategy.selection._\nimport ch.pschatzmann.stocks.integration._\nimport ch.pschatzmann.stocks.strategy.OptimizedStrategy.Schedule._\nimport ch.pschatzmann.stocks.strategy.allocation._\n\n// java\nimport java.util.stream.Collectors\nimport java.util._\nimport java.lang._\nimport java.util.function.Consumer\nimport scala.collection.JavaConverters\n\n/// jupyter custom displayer\nimport ch.pschatzmann.display._\n","metadata":{"trusted":true},"execution_count":1,"outputs":[{"name":"stdout","output_type":"stream","text":"Added new repo: maven\n"},{"output_type":"display_data","data":{"application/vnd.jupyter.widget-view+json":{"model_id":"","version_major":2,"version_minor":0},"method":"display_data"},"metadata":{}},{"output_type":"display_data","data":{"application/vnd.jupyter.widget-view+json":{"model_id":"f719df4f-2701-42c2-8ae4-16bb65c98a43","version_major":2,"version_minor":0},"method":"display_data"},"metadata":{}},{"output_type":"display_data","data":{"application/vnd.jupyter.widget-view+json":{"model_id":"","version_major":2,"version_minor":0},"method":"display_data"},"metadata":{}},{"output_type":"display_data","data":{"application/vnd.jupyter.widget-view+json":{"model_id":"0d30d2cb-7bea-4f14-9429-fb2f2decb92e","version_major":2,"version_minor":0},"method":"display_data"},"metadata":{}},{"execution_count":1,"output_type":"execute_result","data":{"text/plain":"import ch.pschatzmann.dates._\nimport ch.pschatzmann.stocks._\nimport ch.pschatzmann.stocks.data.universe._\nimport ch.pschatzmann.stocks.data.index._\nimport ch.pschatzmann.stocks.input._\nimport ch.pschatzmann.stocks.accounting._\nimport ch.pschatzmann.stocks.accounting.kpi._\nimport ch.pschatzmann.stocks.execution._\nimport ch.pschatzmann.stocks.execution.fees._\nimport ch.pschatzmann.stocks.execution.price._\nimport ch.pschatzmann.stocks.parameters._\nimport ch.pschatzmann.stocks.strategy._\nimport ch.pschatzmann.stocks.strategy.optimization._\nimport ch.pschatzmann.stocks.strategy.allocation._\nimport ch.pschatzmann.stocks.strategy.selection._\nimport ch.pschatzmann.stocks.integration._\nimport ch.pschatzmann.stocks.strategy.OptimizedStrategy.Schedule._\nimport ch.pschatzmann.stocks.strategy.alloca..."},"metadata":{}}]},{"cell_type":"markdown","source":"## Baseline\nHere is the basic shared data that we use for all examples:","metadata":{}},{"cell_type":"code","source":"var periods = Context.getDateRanges(\"2016-01-01\",\"2017-01-01\")\nvar stock1 = new StockData(new StockID(\"AMZN\", \"NASDAQ\"), new YahooReader());\nvar stock2 = new StockData(new StockID(\"GOOG\", \"NASDAQ\"), new YahooReader());\nvar stock3 = new StockData(new StockID(\"INTL\", \"NASDAQ\"), new YahooReader());\n\nOutputCell.HIDDEN","metadata":{"trusted":true},"execution_count":26,"outputs":[]},{"cell_type":"markdown","source":"We use the baseline portfolio where we distribute the stocks evenly:","metadata":{}},{"cell_type":"code","source":"var account = new Account(\"Simulation\",\"USD\", 100000.00, Context.date(\"2016-01-01\"), new PerTradeFees(10.0));\nvar trader = new PaperTrader(account);\nvar distributor = new EvenDistributor()\nvar allocationStrategy = new DistributedAllocationStrategy(trader, distributor);\nvar executor = new StrategyExecutor(trader, allocationStrategy);\nexecutor.addStrategy(new BuyAndHoldStrategy(stock1));\nexecutor.addStrategy(new BuyAndHoldStrategy(stock2));\nexecutor.addStrategy(new BuyAndHoldStrategy(stock3));\nexecutor.run(periods.get(0));\n\nprintln(s\"SharpeRatio: ${account.getKPIValue(KPI.SharpeRatio)}\")\nDisplayers.display(account.getKPIValues())","metadata":{"trusted":true},"execution_count":27,"outputs":[{"name":"stdout","text":"SharpeRatio: 1.1905846521156693\n","output_type":"stream"},{"execution_count":27,"output_type":"execute_result","data":{"text/html":"<table><tr><th>name</th><th>value</th><th>kpi</th><th>string</th><th>doubleValue</th></tr><tr><td>Absolute Return</td><td>81740.5401</td><td>AbsoluteReturn</td><td>81740.5401</td><td>81740.5401</td></tr><tr><td>Absolute Return Average per day</td><td>113.8448</td><td>AbsoluteReturnAveragePerDay</td><td>113.8448</td><td>113.8448</td></tr><tr><td>Absolute Return StdDev</td><td>1780.1773</td><td>AbsoluteReturnStdDev</td><td>1780.1773</td><td>1780.1773</td></tr><tr><td>Return %</td><td>81.7405</td><td>ReturnPercent</td><td>81.7405</td><td>81.7405</td></tr><tr><td>Return % per year</td><td>28.649</td><td>ReturnPercentAnualized</td><td>28.649</td><td>28.649</td></tr><tr><td>Return % StdDev</td><td>0.0121</td><td>ReturnPercentStdDev</td><td>0.0121</td><td>0.0121</td></tr><tr><td>Sharp Ratio</td><td>1.1906</td><td>SharpeRatio</td><td>1.1906</td><td>1.1906</td></tr><tr><td>Max Draw Down %</td><td>20.6969</td><td>MaxDrawDownPercent</td><td>20.6969</td><td>20.6969</td></tr><tr><td>Max Draw Down Absolute</td><td>44919.3699</td><td>MaxDrawDownPercent</td><td>44919.3699</td><td>44919.3699</td></tr><tr><td>Max Draw Down - Number of days</td><td>48</td><td>MaxDrawDownNumberOfDays</td><td>48</td><td>48</td></tr><tr><td>Max Draw Down - High</td><td>217034.0223</td><td>MaxDrawDownHighValue</td><td>217034.0223</td><td>217034.0223</td></tr><tr><td>Max Draw Down - Low</td><td>172114.6524</td><td>MaxDrawDownLowValue</td><td>172114.6524</td><td>172114.6524</td></tr><tr><td>Max Draw Down - Period</td><td><table ><tr><th>Key</th><th>Value</th></tr><tr><td>start</td><td>2018-08-29</td></tr><tr><td>end</td><td>2018-10-29</td></tr><tr><td>name</td><td></td></tr></table></td><td>MaxDrawDownPeriod</td><td>20180829-20181029</td><td></td></tr><tr><td>Number of Trades</td><td>3</td><td>NumberOfTrades</td><td>3</td><td>3</td></tr><tr><td>Number of Buys</td><td>3</td><td>NumberOfBuys</td><td>3</td><td>3</td></tr><tr><td>Number of Sells</td><td>0</td><td>NumberOfSells</td><td>0</td><td>0</td></tr><tr><td>Number of Cash Transfers</td><td>1</td><td>NumberOfCashTransfers</td><td>1</td><td>1</td></tr><tr><td>Number of Traded Stocks</td><td>3</td><td>NumberOfTradedStocks</td><td>3</td><td>3</td></tr><tr><td>Total Fees</td><td>30</td><td>TotalFees</td><td>30</td><td>30</td></tr><tr><td>Cash</td><td>1048.6724</td><td>Cash</td><td>1048.6724</td><td>1048.6724</td></tr><tr><td>Total Value (at actual rates) including cash</td><td>181740.5401</td><td>ActualValue</td><td>181740.5401</td><td>181740.5401</td></tr><tr><td>Total Value (at purchased rates)</td><td>99970</td><td>PurchasedValue</td><td>99970</td><td>99970</td></tr><tr><td>Realized Gains</td><td>0</td><td>RealizedGains</td><td>0</td><td>0</td></tr><tr><td>Unrealized Gains</td><td>81770.5401</td><td>UnrealizedGains</td><td>81770.5401</td><td>81770.5401</td></tr></table>"},"metadata":{}}]},{"cell_type":"markdown","source":"## Optimization\nNow we use the RandomDistributor class to generate random allocation distributions for our strategies: we just need to get the best generated random distribution out of n examples. \n\nThe bigger n is, the closer we get to the optimum - but at the cost of the runtime. Below is an example of iteratios vs Sharpe Ratio: \n\n Number Sharpe Time\n Ratio min\n 10 -> 1.32 0.01\n 100 -> 1.36 0.13\n 1000 -> 1.37 1.20\n 10000 -> 1.37 12.14 \n\nWe recommend to set the number of iterations between 100 and 1000","metadata":{}},{"cell_type":"code","source":"var maxSharpe = 0.0\nvar maxDistributor:RandomDistributor = null\nvar count = 0\nvar start = System.currentTimeMillis()\n\nfor (j <- 1 to 500) {\n var account = new Account(\"Simulation\",\"USD\", 100000.00, Context.date(\"2016-01-01\"), new PerTradeFees(10.0));\n var trader = new PaperTrader(account);\n var distributor = new RandomDistributor()\n var allocationStrategy = new DistributedAllocationStrategy(trader, distributor);\n var executor = new StrategyExecutor(trader, allocationStrategy);\n executor.addStrategy(new BuyAndHoldStrategy(stock1));\n executor.addStrategy(new BuyAndHoldStrategy(stock2));\n executor.addStrategy(new BuyAndHoldStrategy(stock3));\n executor.run(periods.get(0));\n\n var sharpe = account.getKPIValue(KPI.SharpeRatio)\n if (sharpe > maxSharpe) {\n maxSharpe = sharpe\n maxDistributor = distributor\n count += 1\n } \n}\n\nprintln(s\"Runtime (min): ${(System.currentTimeMillis()-start)/(1000.0*60.0)}\")\nprintln(s\"Number of updates: $count\")\nprintln(s\"Max SharpeRatio: $maxSharpe\")\n\nvar optimizedDistribution = maxDistributor.getDistributionFactors()","metadata":{"trusted":true},"execution_count":34,"outputs":[{"name":"stdout","text":"Runtime (min): 0.5775666666666667\nNumber of updates: 6\nMax SharpeRatio: 1.3681402122630453\n","output_type":"stream"},{"output_type":"display_data","data":{"method":"display_data","application/vnd.jupyter.widget-view+json":{"version_minor":0,"model_id":"506ceaba-077c-44cc-8803-94fe7aec1844","version_major":2}},"metadata":{}}]},{"cell_type":"markdown","source":"## Validation\nFinally we double check the result by re-running the scenario with the determined optimized distribution to confirm the outcome:","metadata":{}},{"cell_type":"code","source":"var account = new Account(\"Simulation\",\"USD\", 100000.00, Context.date(\"2016-01-01\"), new PerTradeFees(10.0));\nvar trader = new PaperTrader(account);\nvar distributor = new RandomDistributor(optimizedDistribution)\nvar allocationStrategy = new DistributedAllocationStrategy(trader, distributor);\nvar executor = new StrategyExecutor(trader, allocationStrategy);\nexecutor.addStrategy(distributor.getAllStrategies());\nexecutor.run(periods.get(0));\n\nprintln(s\"SharpeRatio: ${account.getKPIValue(KPI.SharpeRatio)}\")\nDisplayers.display(account.getKPIValues())","metadata":{"trusted":true},"execution_count":35,"outputs":[{"name":"stdout","text":"SharpeRatio: 1.3681402122630453\n","output_type":"stream"},{"execution_count":35,"output_type":"execute_result","data":{"text/html":"<table><tr><th>name</th><th>value</th><th>kpi</th><th>string</th><th>doubleValue</th></tr><tr><td>Absolute Return</td><td>135335.5225</td><td>AbsoluteReturn</td><td>135335.5225</td><td>135335.5225</td></tr><tr><td>Absolute Return Average per day</td><td>188.4896</td><td>AbsoluteReturnAveragePerDay</td><td>188.4896</td><td>188.4896</td></tr><tr><td>Absolute Return StdDev</td><td>2685.2528</td><td>AbsoluteReturnStdDev</td><td>2685.2528</td><td>2685.2528</td></tr><tr><td>Return %</td><td>135.3355</td><td>ReturnPercent</td><td>135.3355</td><td>135.3355</td></tr><tr><td>Return % per year</td><td>47.4333</td><td>ReturnPercentAnualized</td><td>47.4333</td><td>47.4333</td></tr><tr><td>Return % StdDev</td><td>0.0152</td><td>ReturnPercentStdDev</td><td>0.0152</td><td>0.0152</td></tr><tr><td>Sharp Ratio</td><td>1.3681</td><td>SharpeRatio</td><td>1.3681</td><td>1.3681</td></tr><tr><td>Max Draw Down %</td><td>23.9139</td><td>MaxDrawDownPercent</td><td>23.9139</td><td>23.9139</td></tr><tr><td>Max Draw Down Absolute</td><td>69299.5752</td><td>MaxDrawDownPercent</td><td>69299.5752</td><td>69299.5752</td></tr><tr><td>Max Draw Down - Number of days</td><td>45</td><td>MaxDrawDownNumberOfDays</td><td>45</td><td>45</td></tr><tr><td>Max Draw Down - High</td><td>289787.3646</td><td>MaxDrawDownHighValue</td><td>289787.3646</td><td>289787.3646</td></tr><tr><td>Max Draw Down - Low</td><td>220487.7894</td><td>MaxDrawDownLowValue</td><td>220487.7894</td><td>220487.7894</td></tr><tr><td>Max Draw Down - Period</td><td><table ><tr><th>Key</th><th>Value</th></tr><tr><td>start</td><td>2018-09-04</td></tr><tr><td>end</td><td>2018-10-30</td></tr><tr><td>name</td><td></td></tr></table></td><td>MaxDrawDownPeriod</td><td>20180904-20181030</td><td></td></tr><tr><td>Number of Trades</td><td>3</td><td>NumberOfTrades</td><td>3</td><td>3</td></tr><tr><td>Number of Buys</td><td>3</td><td>NumberOfBuys</td><td>3</td><td>3</td></tr><tr><td>Number of Sells</td><td>0</td><td>NumberOfSells</td><td>0</td><td>0</td></tr><tr><td>Number of Cash Transfers</td><td>1</td><td>NumberOfCashTransfers</td><td>1</td><td>1</td></tr><tr><td>Number of Traded Stocks</td><td>3</td><td>NumberOfTradedStocks</td><td>3</td><td>3</td></tr><tr><td>Total Fees</td><td>30</td><td>TotalFees</td><td>30</td><td>30</td></tr><tr><td>Cash</td><td>859.0539</td><td>Cash</td><td>859.0539</td><td>859.0539</td></tr><tr><td>Total Value (at actual rates) including cash</td><td>235335.5225</td><td>ActualValue</td><td>235335.5225</td><td>235335.5225</td></tr><tr><td>Total Value (at purchased rates)</td><td>99970</td><td>PurchasedValue</td><td>99970</td><td>99970</td></tr><tr><td>Realized Gains</td><td>0</td><td>RealizedGains</td><td>0</td><td>0</td></tr><tr><td>Unrealized Gains</td><td>135365.5225</td><td>UnrealizedGains</td><td>135365.5225</td><td>135365.5225</td></tr></table>"},"metadata":{}}]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment