# Currency Portfolio Optimization Using ScienceOps

#### by Ryan J. O'Neil |

Portfolio optimization is a problem faced by anyone trying to invest money *(or
any kind of capital, such as time)* in a known group of investments. Its most
obvious, and common, application is investing in the stock market. Typically,
portfolio managers have two competing goals:

- Maximize return
- Minimize risk

Maximizing return means selecting a group of investments that collectively result in the highest expected yield. Minimizing risk means selecting investments that are most likely to actually result in the yields we expect.

Anyone with a little exposure to markets knows that these goals are often diametrically opposed. High yielding investments also tend to have high variances in their return. Thus the problem we face in portfolio optimization is achieving a desired balance between these two goals.

## Exchange rate data

In this post we'll take the view of someone *(e.g. a bank or a mutual fund
manager)* with a lot of cash to invest in foreign currencies. We want to buy
foreign currencies such that we maximize our return with respect to the US
Dollar (USD) over the next month. We won't worry about the actual amounts
invested. We simply want to maximize the percent return given that we convert
100% of some known amount into foreign currencies.

We start by downloading the last 100 months of exchange rate data from the Federal Reserve's FRB H10: Data Download Program. Alternatively, you can simply clone the GitHub repository for this post, which contains exchange rate data as of this writing.

Inspecting the data, we find something a little strange. The first 6 rows are row
oriented data and the remaining rows are column oriented. The former contain
metadata about the latter, so we read these into two different data frames using
pandas. Also, we note that there are data with `NA`

currencies which don't particularly interest us. So we ignore these.

```
import csv
import pandas
raw_data = [row for row in csv.reader(open('2014-12-28-fed-monthly-currency-data.csv'))]
# Transpose header metadata and turn it into a data frame
head_data = [[] for _ in range(len(raw_data[0]))]
for row in raw_data[:6]:
for j, val in enumerate(row):
head_data[j].append(val)
metadata = pandas.DataFrame.from_records(data=head_data[1:], columns=head_data[0])
# Filter out NA currencies
metadata = metadata[metadata['Currency:'] != 'NA']
```

`metadata`

now resembles the following `DataFrame`

:

Series Description | Unit: | Multiplier: | Currency: | Unique Identifier: | Time Period | |
---|---|---|---|---|---|---|

3 | AUSTRALIA -- SPOT EXCHANGE RATE, US$/AUSTRALIA... | Currency:_Per_AUD | 1 | USD | H10/H10/RXI$US_N.M.AL | RXI$US_N.M.AL |

4 | SPOT EXCHANGE RATE - EURO AREA | Currency:_Per_EUR | 1 | USD | H10/H10/RXI$US_N.M.EU | RXI$US_N.M.EU |

5 | NEW ZEALAND -- SPOT EXCHANGE RATE, US$/NZ$ (RE... | Currency:_Per_NZD | 1 | USD | H10/H10/RXI$US_N.M.NZ | RXI$US_N.M.NZ |

6 | United Kingdom -- Spot Exchange Rate, US$/Poun... | Currency:_Per_GBP | 0.01 | USD | H10/H10/RXI$US_N.M.UK | RXI$US_N.M.UK |

7 | BRAZIL -- SPOT EXCHANGE RATE, REAIS/US$ | Currency:_Per_USD | 1 | BRL | H10/H10/RXI_N.M.BZ | RXI_N.M.BZ |

8 | CANADA -- SPOT EXCHANGE RATE, CANADIAN $/US$ | Currency:_Per_USD | 1 | CAD | H10/H10/RXI_N.M.CA | RXI_N.M.CA |

9 | CHINA -- SPOT EXCHANGE RATE, YUAN/US$ | Currency:_Per_USD | 1 | CNY | H10/H10/RXI_N.M.CH | RXI_N.M.CH |

10 | DENMARK -- SPOT EXCHANGE RATE, KRONER/US$ | Currency:_Per_USD | 1 | DKK | H10/H10/RXI_N.M.DN | RXI_N.M.DN |

11 | HONG KONG -- SPOT EXCHANGE RATE, HK$/US$ | Currency:_Per_USD | 1 | HKD | H10/H10/RXI_N.M.HK | RXI_N.M.HK |

12 | INDIA -- SPOT EXCHANGE RATE, RUPEES/US$ | Currency:_Per_USD | 1 | INR | H10/H10/RXI_N.M.IN | RXI_N.M.IN |

13 | JAPAN -- SPOT EXCHANGE RATE, YEA/US$ | Currency:_Per_USD | 1 | JPY | H10/H10/RXI_N.M.JA | RXI_N.M.JA |

14 | KOREA -- SPOT EXCHANGE RATE, WON/US$ | Currency:_Per_USD | 1 | KRW | H10/H10/RXI_N.M.KO | RXI_N.M.KO |

15 | Malaysia - Spot Exchange Rate, Ringgit/US$ | Currency:_Per_USD | 1 | MYR | H10/H10/RXI_N.M.MA | RXI_N.M.MA |

16 | MEXICO -- SPOT EXCHANGE RATE, PESOS/US$ | Currency:_Per_USD | 1 | MXN | H10/H10/RXI_N.M.MX | RXI_N.M.MX |

17 | NORWAY -- SPOT EXCHANGE RATE, KRONER/US$ | Currency:_Per_USD | 1 | NOK | H10/H10/RXI_N.M.NO | RXI_N.M.NO |

18 | SWEDEN -- SPOT EXCHANGE RATE, KRONOR/US$ | Currency:_Per_USD | 1 | SEK | H10/H10/RXI_N.M.SD | RXI_N.M.SD |

19 | SOUTH AFRICA -- SPOT EXCHANGE RATE, RAND/US$ | Currency:_Per_USD | 1 | ZAR | H10/H10/RXI_N.M.SF | RXI_N.M.SF |

20 | Singapore - SPOT EXCHANGE RATE, SINGAPORE $/US$ | Currency:_Per_USD | 1 | SGD | H10/H10/RXI_N.M.SI | RXI_N.M.SI |

21 | SRI LANKA -- SPOT EXCHANGE RATE, RUPEES/US$ | Currency:_Per_USD | 1 | LKR | H10/H10/RXI_N.M.SL | RXI_N.M.SL |

22 | SWITZERLAND -- SPOT EXCHANGE RATE, FRANCS/US$ | Currency:_Per_USD | 1 | CHF | H10/H10/RXI_N.M.SZ | RXI_N.M.SZ |

23 | TAIWAN -- SPOT EXCHANGE RATE, NT$/US$ | Currency:_Per_USD | 1 | TWD | H10/H10/RXI_N.M.TA | RXI_N.M.TA |

24 | THAILAND -- SPOT EXCHANGE RATE -- THAILAND | Currency:_Per_USD | 1 | THB | H10/H10/RXI_N.M.TH | RXI_N.M.TH |

25 | VENEZUELA -- SPOT EXCHANGE RATE, BOLIVARES/US$ | Currency:_Per_USD | 1 | VEB | H10/H10/RXI_N.M.VE | RXI_N.M.VE |

For convenience, we'll create a list of the country names for the currencies. This will come in handy later when we're trying to output portfolio options.

```
# This will have the same indices as the rest of the metadata.
countries = []
for c in metadata['Series Description']:
c = c.upper()
c = c.split(' -- ')[0]
c = c.split(' - ')[0]
countries.append(c)
countries[1] = 'EURO AREA'
```

`countries`

is a list of human-readable country names:

```
['AUSTRALIA',
'EURO AREA',
'NEW ZEALAND',
'UNITED KINGDOM',
'BRAZIL',
'CANADA',
'CHINA',
'DENMARK',
'HONG KONG',
'INDIA',
'JAPAN',
'KOREA',
'MALAYSIA',
'MEXICO',
'NORWAY',
'SWEDEN',
'SOUTH AFRICA',
'SINGAPORE',
'SRI LANKA',
'SWITZERLAND',
'TAIWAN',
'THAILAND',
'VENEZUELA']
```

Now we read in the second part of the data which contains exchange rate information. We filter out NA exchange rates while we're at it.

```
exchange_rates = pandas.DataFrame.from_records(data=raw_data[6:], columns=raw_data[5])
exchange_rates = exchange_rates[['Time Period'] + list(metadata['Time Period'])]
exchange_rates.tail()
```

Time Period | RXI$US_N.M.AL | RXI$US_N.M.EU | RXI$US_N.M.NZ | RXI$US_N.M.UK | RXI_N.M.BZ | RXI_N.M.CA | RXI_N.M.CH | RXI_N.M.DN | RXI_N.M.HK | ... | RXI_N.M.MX | RXI_N.M.NO | RXI_N.M.SD | RXI_N.M.SF | RXI_N.M.SI | RXI_N.M.SL | RXI_N.M.SZ | RXI_N.M.TA | RXI_N.M.TH | RXI_N.M.VE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

95 | 2014-08 | 0.9309 | 1.3315 | 0.8437 | 1.6700 | 2.2685 | 1.0926 | 6.1541 | 5.5987 | 7.7504 | ... | 13.1436 | 6.1935 | 6.8983 | 10.6632 | 1.2484 | 130.1748 | 0.9098 | 29.9729 | 32.0048 | 6.2842 |

96 | 2014-09 | 0.9042 | 1.2889 | 0.8134 | 1.6290 | 2.3379 | 1.1011 | 6.1382 | 5.7757 | 7.7526 | ... | 13.2370 | 6.3525 | 7.1302 | 10.9908 | 1.2638 | 130.2562 | 0.9370 | 30.1310 | 32.1971 | 6.2842 |

97 | 2014-10 | 0.8781 | 1.2677 | 0.7876 | 1.6074 | 2.4495 | 1.1212 | 6.1251 | 5.8723 | 7.7572 | ... | 13.4795 | 6.5600 | 7.2456 | 11.0594 | 1.2745 | 130.5618 | 0.9528 | 30.3964 | 32.4418 | 6.2842 |

98 | 2014-11 | 0.8644 | 1.2473 | 0.7834 | 1.5771 | 2.5527 | 1.1325 | 6.1249 | 5.9660 | 7.7543 | ... | 13.6148 | 6.8090 | 7.4155 | 11.0901 | 1.2964 | 130.9278 | 0.9642 | 30.7278 | 32.7878 | 6.2842 |

99 | 2014-12 | 0.8302 | 1.2389 | 0.7765 | 1.5678 | 2.6278 | 1.1496 | 6.1781 | 6.0049 | 7.7528 | ... | 14.4434 | 7.2137 | 7.5631 | 11.4591 | 1.3122 | 131.0627 | 0.9704 | 31.2193 | 32.8847 | 6.2842 |

5 rows × 24 columns

## Computing returns

We now have a data frame that contains exchange rate data for each month in relation to USD. This isn't particularly convenient. We're planning on converting our holdings back to USD anyway, so the units themselves don't really matter. What we care about is the actual percentage return. That is, if we bought $1 of currency X, how much of that would we have gained or lost over a month?

In order to compute this, we convert each data point in our
`exchange_rates`

data to units of its local currency that equal \$1. Most of the
data is already like this. Four are not: Australia, the Eurozone, New Zealand,
and the United Kingdom are all reported in USD. We divide `1`

by these to obtain
their local currency equivalents of \$1.

$$\$1 = x \rightarrow \frac{1}{x} = \frac{1}{\$}$$

Now we compute the percentage return for each month in USD. So if $r_m$ is the exchange rate in month $m$, then $\frac{r_m - r_{m+1}}{r_{m+1}}$ is the percentage return of that currency during month $m$. For example, say in month $m$ we can buy 2 units of some currency for \$1. In month $m+1$, \$1 buys 4 units of that same currency. The return on that currency is $\frac{2 - 4}{4} = -50\%$.

```
# Convert the exchange rate data frame into percentage returns.
rows = []
for i in range(len(exchange_rates)-1):
row = {}
for tp, cur in zip(metadata['Time Period'], metadata['Currency:']):
x1 = float(exchange_rates[tp][i])
x2 = float(exchange_rates[tp][i+1])
if cur == 'USD':
x1 = 1.0 / x1
x2 = 1.0 / x2
# Returns are in units of %.
row[tp] = 100 * (x1 - x2) / x2
rows.append(row)
returns = pandas.DataFrame(data=rows, columns=list(metadata['Time Period']))
returns.tail()
```

RXI$US_N.M.AL | RXI$US_N.M.EU | RXI$US_N.M.NZ | RXI$US_N.M.UK | RXI_N.M.BZ | RXI_N.M.CA | RXI_N.M.CH | RXI_N.M.DN | RXI_N.M.HK | RXI_N.M.IN | ... | RXI_N.M.MX | RXI_N.M.NO | RXI_N.M.SD | RXI_N.M.SF | RXI_N.M.SI | RXI_N.M.SL | RXI_N.M.SZ | RXI_N.M.TA | RXI_N.M.TH | RXI_N.M.VE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

94 | -0.852061 | -1.610877 | -2.877863 | -2.144615 | -1.952832 | -1.711514 | 0.719845 | -1.589655 | -0.002581 | -1.278382 | ... | -1.157978 | 0.092032 | -1.151008 | -0.051579 | -0.456584 | 0.028807 | -1.318971 | -0.079405 | 0.254962 | 0 |

95 | -2.868192 | -3.199399 | -3.591324 | -2.455090 | -2.968476 | -0.771955 | 0.259034 | -3.064564 | -0.028378 | -0.039082 | ... | -0.705598 | -2.502952 | -3.252363 | -2.980675 | -1.218547 | -0.062492 | -2.902882 | -0.524709 | -0.597259 | 0 |

96 | -2.886530 | -1.644813 | -3.171871 | -1.325967 | -4.556032 | -1.792722 | 0.213874 | -1.645011 | -0.059300 | -0.764583 | ... | -1.799028 | -3.163110 | -1.592691 | -0.620287 | -0.839545 | -0.234065 | -1.658270 | -0.873130 | -0.754274 | 0 |

97 | -1.560187 | -1.609214 | -0.533266 | -1.885032 | -4.042778 | -0.997792 | 0.003265 | -1.570567 | 0.037399 | -0.512298 | ... | -0.993771 | -3.656925 | -2.291147 | -0.276823 | -1.689293 | -0.279543 | -1.182327 | -1.078502 | -1.055271 | 0 |

98 | -3.956502 | -0.673455 | -0.880776 | -0.589690 | -2.857904 | -1.487474 | -0.861106 | -0.647804 | 0.019348 | -1.211577 | ... | -5.736876 | -5.610158 | -1.951581 | -3.220148 | -1.204085 | -0.102928 | -0.638912 | -1.574347 | -0.294666 | 0 |

5 rows × 23 columns

We downloaded 100 months of exchange rate data for each country. These are now in units of % returns. The means and variances of these give us expected returns and variances for investing in those currencies.

Note that the expected returns (means) of the currencies are in units of percentage. That means a value 0.15 means a 0.15% expected return over a month. That translates into 1.8% annually, which is not bad for a currency.

```
# Means are the expected returns for each currency.
exp_returns = pandas.concat({'mean': returns.mean(), 'variance': returns.var()}, axis=1)
```

`exp_returns`

is a `DataFrame`

resembling the following:

mean | variance | |
---|---|---|

RXI$US_N.M.AL | 0.152151 | 10.996718 |

RXI$US_N.M.EU | 0.002683 | 5.928188 |

RXI$US_N.M.NZ | 0.220217 | 9.690793 |

RXI$US_N.M.UK | -0.159779 | 5.098969 |

RXI_N.M.BZ | -0.128507 | 12.743632 |

RXI_N.M.CA | -0.007877 | 4.380697 |

RXI_N.M.CH | 0.253968 | 0.212987 |

RXI_N.M.DN | 0.004799 | 5.810684 |

RXI_N.M.HK | 0.003935 | 0.014761 |

RXI_N.M.IN | -0.283975 | 4.784800 |

RXI_N.M.JA | 0.016618 | 6.449970 |

RXI_N.M.KO | -0.110786 | 7.580272 |

RXI_N.M.MA | 0.068651 | 2.424957 |

RXI_N.M.MX | -0.235747 | 7.786813 |

RXI_N.M.NO | -0.065996 | 7.727538 |

RXI_N.M.SD | 0.001158 | 7.832406 |

RXI_N.M.SF | -0.369169 | 12.837594 |

RXI_N.M.SI | 0.195880 | 1.596186 |

RXI_N.M.SL | -0.240545 | 1.289438 |

RXI_N.M.SZ | 0.286951 | 6.917752 |

RXI_N.M.TA | 0.059511 | 1.299676 |

RXI_N.M.TH | 0.144215 | 2.731771 |

RXI_N.M.VE | -0.914809 | 25.526367 |

## Creating currency portfolios

So let's say we want to maximize our return in US dollars. The simplest approach is to select the currency that has the best expected return, and convert all our money to it.

```
countries[list(exp_returns.index).index(exp_returns['mean'].idxmax())]
```

Resulting in:

```
'SWITZERLAND'
```

This tells us to invest all our money in Swiss Francs for a 0.2% expected return monthly. The data gives us a 95% confidence interval that the expected monthly return of investing in Swiss Francs is between -0.2% and 0.8%.

```
import math
n = len(exchange_rates)
sz_mean = exp_returns['mean']['RXI_N.M.SZ']
sz_sd = math.sqrt(exp_returns['variance']['RXI_N.M.SZ'])
sz_ci = (sz_mean-1.96*sz_sd/math.sqrt(n), sz_mean+1.We do96*sz_sd/math.sqrt(n))
print 'Monthly expected return (Swiss Francs): %0.3f%%' % sz_mean
print '95%% confidence interval on expected return: (%0.3f%%, %.03f%%)' % sz_ci
```

That gives us our expected return and 95% confidence interval:

```
Monthly expected return (Swiss Francs): 0.287%
95% confidence interval on expected return: (-0.229%, 0.802%)
```

It's important to note that this is our confidence in the *expected return*, not
even in any particular return. That means that the expected return of our
highest yielding currency could be negative or it could be positive. We don't
really know. So we could end up deciding to go all in on Swiss Francs, only to
discover we should expect to lose 0.2% of our money in relation to the US
dollar every month.

We'd like to do better, obviously. And there's another piece of information we have that might allow us to: how the different currencies relate to each other. This is the fundamental idea behind portfolio theory as introduced by Harry Markowitz in the 1950s. We can use the covariances of the returns to hedge investments against each other. Our task then becomes to select the investments to make such that maximize return while achieving a desired level of risk.

```
# Get the covariance matrix for our returns. This is normalized by N-1.
# As expected, the diagonal contains the variance for each individual currency.
returns_cov = returns.cov()
returns_cov.head()
```

RXI$US_N.M.AL | RXI$US_N.M.EU | RXI$US_N.M.NZ | RXI$US_N.M.UK | RXI_N.M.BZ | RXI_N.M.CA | RXI_N.M.CH | RXI_N.M.DN | RXI_N.M.HK | RXI_N.M.IN | ... | RXI_N.M.MX | RXI_N.M.NO | RXI_N.M.SD | RXI_N.M.SF | RXI_N.M.SI | RXI_N.M.SL | RXI_N.M.SZ | RXI_N.M.TA | RXI_N.M.TH | RXI_N.M.VE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

RXI$US_N.M.AL | 10.996718 | 5.181782 | 8.654762 | 4.457704 | 9.789818 | 5.421746 | 0.194428 | 5.166875 | 0.028496 | 4.379632 | ... | 6.494732 | 7.078498 | 7.083824 | 8.574515 | 3.287411 | 0.343673 | 4.163674 | 2.450772 | 2.381722 | 0.542450 |

RXI$US_N.M.EU | 5.181782 | 5.928188 | 4.487890 | 3.659548 | 4.686754 | 2.467804 | 0.313081 | 5.867674 | 0.038117 | 2.545200 | ... | 2.894461 | 5.350733 | 5.515164 | 4.314480 | 2.265781 | 0.418269 | 5.173168 | 1.559857 | 1.351946 | 1.776855 |

RXI$US_N.M.NZ | 8.654762 | 4.487890 | 9.690793 | 4.330531 | 8.130412 | 4.268307 | 0.129303 | 4.466514 | 0.017975 | 3.919170 | ... | 5.536301 | 5.602586 | 6.054567 | 6.733828 | 2.811946 | 0.451742 | 3.903260 | 2.135899 | 2.569281 | 0.379954 |

RXI$US_N.M.UK | 4.457704 | 3.659548 | 4.330531 | 5.098969 | 4.301933 | 2.573100 | 0.137068 | 3.632088 | 0.012420 | 1.821456 | ... | 2.775715 | 4.081803 | 4.233658 | 3.749874 | 1.780620 | 0.326828 | 3.335175 | 1.356927 | 1.014109 | 1.584577 |

RXI_N.M.BZ | 9.789818 | 4.686754 | 8.130412 | 4.301933 | 12.743632 | 5.433992 | 0.279201 | 4.689891 | -0.034405 | 5.567318 | ... | 7.177333 | 7.288341 | 6.774689 | 8.761299 | 2.999360 | 0.843294 | 4.269524 | 2.163251 | 2.439204 | 0.496304 |

5 rows × 23 columns

The Markwitz portfolio model is given below. In it, $\mu$ is the vector of the expected returns for each currency. $x$ is a vector of nonnegative values that sum to 1 and represent how much of our portfolio goes into each currency. The first term in our objective function (the first line in the model) thus represents the expected return of a making a set of investments. If that were all we were doing, our model would simply tell us to invest all our money in the highest-paying currency, Swiss Francs.

$$\max{\mu^T x - \alpha x \Sigma x}$$ $$e^T x = 1$$ $$x \ge 0$$

The second term is what introduces hedging. $\alpha$ is a measure of aversion to risk. You can think of it like this: when $\alpha$ is 0, we only invest in Swiss Francs. As $\alpha$ gets larger, we introduce more hedging into our set of investments. The $\Sigma$ is our covariance matrix, so $x \Sigma x$ will be our portfolio variance. Thus we are looking for a good trade-off between our portfolio's expected return and its variance.

This happens to be a quadratic optimization problem, so we can use the qp function of CVXOPT to solve it. Let's turn this into a ScienceOps model using CVXOPT.

```
from cvxopt import matrix, solvers
from yhat import Yhat, YhatModel, preprocess
import numpy
class CurrencyPortfolio(YhatModel):
@preprocess(in_type=dict, out_type=dict)
def execute(self, data):
P = matrix(data['risk_aversion'] * returns_cov.as_matrix())
q = matrix(-exp_returns['mean'].as_matrix())
G = matrix(0.0, (len(q),len(q)))
G[::len(q)+1] = -1.0
h = matrix(0.0, (len(q),1))
A = matrix(1.0, (1,len(q)))
b = matrix(1.0)
solution = solvers.qp(P, q, G, h, A, b)
expected_return = exp_returns['mean'].dot(solution['x'])[0]
variance = sum(solution['x'] * returns_cov.as_matrix().dot(solution['x']))[0]
investments = {}
for i, amount in enumerate(solution['x']):
# Ignore values that appear to have converged to 0.
if amount > 10e-5:
investments[countries[i]] = amount*100
return {
'risk_aversion': data['risk_aversion'],
'investments': investments,
'expected_return': expected_return,
'variance': variance
}
```

When the model is deployed to ScienceOps, it has the expected returns, country
names, and covariance matrix wrapped up in it already. All it needs in order to
build a portfolio is a value for $\alpha$, which we call `risk_aversion`

in the
code. You can deploy it to ScienceOps using your `USERNAME`

and `APIKEY`

values
and the `currency-portfolio-scienceops.py`

script in this post's the GitHub
repository.

```
yh = Yhat('USERNAME', 'APIKEY', 'http://cloud.yhathq.com/')
yh.deploy('CurrencyPortfolio', CurrencyPortfolio, globals())
```

Before we build a portfolio, let's step back for a minute here and take a look at our data. Recall that we are trying to maximize our expected return while minimizing our exposure to risk (variance of the return).

If currency exchanges work like other markets, then expected returns farther from 0 should be accompanied by higher variance. We graph our expected returns and their variances to see if this is true.

```
%matplotlib inline
import seaborn
seaborn.regplot('mean', 'variance', exp_returns)
```

That's quite an outlier there to the left. Stopping to examine the data, we find that it's Venezuela. Their economy is not doing so well, according to the news. It's dominating our data a bit. What does the graph look like without it?

```
exp_returns_without_ve = exp_returns[exp_returns.index != 'RXI_N.M.VE']
seaborn.regplot('mean', 'variance', exp_returns, ci=None)
seaborn.regplot('mean', 'variance', exp_returns_without_ve)
```

So without the outlier expected returns farther from 0 appear to have higher variance. That's what we expected, so let's get on with the business of building our portfolio.

## Consuming our model

The great thing about deploying a model like this to ScienceOps is that nobody has to install complicated software in order to use it. It's available to anyone we want to give it to using a simple REST API.

Let's build a series of portfolios, with $\alpha$ values from 0 to 20 in increments of 0.5.

```
risk_aversion = [ra/2.0 for ra in range(41)]
portfolios = []
for ra in risk_aversion:
portfolios.append(yh.predict('CurrencyPortfolio', {'risk_aversion': ra}))
```

This gives us a an efficient frontier trading off returns and risk. Depending on our tolerance for risk, our best choice of portfolio should be somewhere along this curve.

```
from matplotlib import pyplot
pyplot.plot(risk_aversion, [p['result']['expected_return'] for p in portfolios], 'o-')
pyplot.axis([-1, 21, 0.0, 0.3])
pyplot.title('Efficient Frontier for Currency Portfolios')
pyplot.text(9, -0.05, 'Risk Aversion')
pyplot.text(-4, 0.18, '% Return', rotation='vertical')
```

We can also look at the trade-off in terms of expected return and variance.

```
pyplot.plot(
[p['result']['expected_return'] for p in portfolios],
[p['result']['variance'] for p in portfolios],
'o-'
)
pyplot.axis([0, 0.3, -0.5, 7.5])
pyplot.title('Efficient Frontier for Currency Portfolios')
pyplot.text(.1, -2, 'Expected Return (%)')
pyplot.text(-0.025, 5, 'Portfolio Return Variance', rotation='vertical')
```

Finally, let's output all the portfolios we just generated. These plans tell us what percentage of our total money to invest in each different currency. For instance, Portfolio 4 tells us to invest 60.55%, 37.25% and 2.2% of our total investment in the currenies of China, Hong Kong, and Thailand, respectively.

```
# We print out all the portfolios available to us.
from operator import itemgetter
for i, p in enumerate(portfolios):
print 'Portfolio %d: expected monthly risk_aversion=%.1f, return=%.03f%%, variance=%.03f' % (
i, p['result']['risk_aversion'], p['result']['expected_return'], p['result']['variance']
)
for country, investment in sorted(p['result']['investments'].items(), key=itemgetter(1), reverse=True):
print '\t%5.02f%% %s' % (investment, country)
print
```

Portfolio output:

```
Portfolio 0: expected monthly risk_aversion=0.0, return=0.287%, variance=6.918
100.00% SWITZERLAND
Portfolio 1: expected monthly risk_aversion=0.5, return=0.254%, variance=0.213
99.83% CHINA
0.17% NEW ZEALAND
Portfolio 2: expected monthly risk_aversion=1.0, return=0.251%, variance=0.206
97.68% CHINA
2.32% THAILAND
Portfolio 3: expected monthly risk_aversion=1.5, return=0.204%, variance=0.135
78.54% CHINA
18.90% HONG KONG
2.56% THAILAND
Portfolio 4: expected monthly risk_aversion=2.0, return=0.158%, variance=0.082
60.55% CHINA
37.25% HONG KONG
2.20% THAILAND
Portfolio 5: expected monthly risk_aversion=2.5, return=0.131%, variance=0.057
49.76% CHINA
48.26% HONG KONG
1.98% THAILAND
etc.
```