=defaultdict (float) (for) p, items (in [173] the data: r1_opts =items [r1] r2_opts =items [r2] outcomes =(float) len (r1_opts) [y] (len) r2_opts )) (for) (x) (in [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] r1_opts: (for) (y) (in [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] r2_opts: prob [x, y] =(p) / results (return) prob (def) (covariance_of) (r1, r2): E_r1 =expectation_of (r1) E_r2 =expectation_of (r2) joint =joint_prob (r1, r2) joint_exp =(0) (for) (x, y), prob in (joint) . items (): joint_exp =(x) [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] (y) prob (return) (joint_exp) - (E_r1) [ ( 0.664, { (WEDNESDAY, PM): convert_range("66 to 66"), (THURSDAY, AM): convert_range("61 to 64"), (THURSDAY, PM): convert_range("56 to 61"), (FRIDAY, AM): convert_range("51 to 58"), (FRIDAY, PM): convert_range("46 to 55"), (SATURDAY, AM): convert_range("41 to 52"), (SATURDAY, PM): convert_range("36 to 49"), }, ), ( 0.158, { (WEDNESDAY, PM): convert_range("66 to 66"), (THURSDAY, AM): convert_range("89 to 138"), (THURSDAY, PM): convert_range("138 to 196"), (FRIDAY, AM): convert_range("196 to 588"), (FRIDAY, PM): convert_range("138 to 196"), (SATURDAY, AM): convert_range("89 to 138"), (SATURDAY, PM): convert_range("40 to 89"), }, ), ( 0.158, { (WEDNESDAY, PM): convert_range("66 to 66"), (THURSDAY, AM): convert_range("61 to 64"), (THURSDAY, PM): convert_range("89 to 138"), (FRIDAY, AM): convert_range("138 to 196"), (FRIDAY, PM): convert_range("196 to 588"), (SATURDAY, AM): convert_range("138 to 196"), (SATURDAY, PM): convert_range("89 to 138"), }, ), ( 0.01, { (WEDNESDAY, PM): convert_range("66 to 66"), (THURSDAY, AM): convert_range("89 to 138"), (THURSDAY, PM): convert_range("89 to 138"), (FRIDAY, AM): convert_range("137 to 195"), (FRIDAY, PM): convert_range("137 to 196"), (SATURDAY, AM): convert_range("137 to 195"), (SATURDAY, PM): convert_range("40 to 89"), }, ), ( 0.01, { (WEDNESDAY, PM): convert_range("66 to 66"), (THURSDAY, AM): convert_range("61 to 64"), (THURSDAY, PM): convert_range("89 to 138"), (FRIDAY, AM): convert_range("89 to 138"), (FRIDAY, PM): convert_range("137 to 195"), (SATURDAY, AM): convert_range("137 to 196"), (SATURDAY, PM): convert_range("137 to 195"), }, ),] E_r2 We are going to calculate the joint probability distribution of the screen prices at two different times (e.g. what’s the probability that the price is X on Monday PM and Y on Friday PM). Using that we use the well known covariance identity Cov (X, Y)=E [XY] - E [X] E [Y]. Presto. Let's get all our turnips in a row now. We want to come up with that vector of weights. We have an optimization problem here, we want to select the weights that minimize the formula above. Turns out, we can use scipy just for that. [x, y] (def) (objective) (weights, covs, expected_returns, risk):
# the `@` operator is matrix multiplication (return) weights @ @ (covs) (@) (weights) - (risk)
expected_returns (@ weights (def) get_weights (data, possibilities, risk): num_assets
=len (possibilities) expected_returns_dict ={} (for) (p) (in [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] poss: expected_returns_dict [p]=expectation_of (p) er =(pd) . (DataFrame) { "returns" : expected_returns_dict}) cov_dict =defaultdict (dict) (for) (x) (in [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] poss: (for) (y) (in [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] poss: cov_dict [x] [y] ["returns"]=covariance_of (x, y) covs =(pd) . DataFrame (cov_dict) # We want the% of turnips to sell at each screen price and we can only # sell (ie we can't short the prices) constraints =({"type" : "eq" , “fun” : lambda (x: np) [186] sum (x) (-) (1) } bound =() (0.0) , (1.0) ) bounds bounds=(tuple) bound for (asset) in range (num_assets)) (return) ( sco . (minimize) objective, num_assets [y] args =(covs) . (as_matrix (), er . as_matrix (), risk), method =SLSQP [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] , bounds bounds=bounds, constraints =constraints, ), er . as_matrix (), covs . as_matrix (), )
We can test it out interactively with the example we have been working on:
In [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ]: poss=[ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] ...: In [173]: w, exp, covs=get_weights (data, poss, 0) In [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ]: w [173] Out [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] array ([173]) In [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]: exp.T @ w [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] Out [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]: array ([9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]) In [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]: exp.T @ w [ ...: (WEDNESDAY, PM), ...: (THURSDAY, AM), ...: (THURSDAY, PM), ...: (FRIDAY, AM), ...: (FRIDAY, PM), ...: (SATURDAY, AM), ...: (SATURDAY, PM), ...: ] Out [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]: array ([9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00]) In : w [173] @ covs @ w Out [-31.99986786]: 2. e -
[28.33816839] Unsurprisingly, if we are willing to take zero risk, we are told to sell now as that price has zero variance. The expected returns of that allocation is losing - 50 for each turnip. Let’s try a couple of different risk appetites: 83227539 In [186]: for risk in ([9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] , , 174, 514, 00001526): ...: w, exp, covs=get_weights (data, poss, risk) ...: print (w) ...: print (exp.T @ w [172]) ...: print (w [9.99973926e-01, 2.60740769e-05, 0.00000000e 00, 0.00000000e 00, 1.71303943e-17, 0.00000000e 00, 0.00000000e 00] @ covs @ w [173] ...: fun: 588 jac: array ([186] message: 'Optimization terminated successfully.' nfev: nit: 6 njev: 6 status: 0 success: True x: array ([186]) [9.72724596e-01, 1.01876057e-10, 6.39677751e-11, 1.46464235e-02, 1.26289831e-02, 2.04667615e-10, 3.60237807e-10] 8. fun: . jac: array ([9.72724596e-01, 1.01876057e-10, 6.39677751e-11, 1.46464235e-02, 1.26289831e-02, 2.04667615e-10, 3.60237807e-10]) message: 'Optimization terminated successfully.' nfev: 100 nit: njev: 7 status: 0 success: True x: array ([1600. , 1707.10598755, 1734.55581665, 1600.00001526, 1600.00001526, 2209.32440186, 2570.16264343] [1600. , 1707.10598755, 1734.55581665, 1600.00001526, 1600.00001526, 2209.32440186, 2570.16264343] 500 fun: [-31.99986786] jac: array ([8.63622855e-01, 2.96854243e-08, 1.86344942e-08, 7.32320950e-02, 6.31448364e-02, 5.98173297e-08, 1.05265552e-07]) message: 'Optimization terminated successfully.' nfev: 132 nit: njev: 9 status: 0 success: True x: array ([-23.77365766]) [7.27245514e-01, 6.52789507e-08, 5.03337743e-08, 1.46464389e-01, 1.26289752e-01, 8.28435152e-08, 1.47684783e-07] . fun: [186] jac: array ([7.27245514e-01, 6.52789507e-08, 5.03337743e-08, 1.46464389e-01, 1.26289752e-01, 8.28435152e-08, 1.47684783e-07] message: 'Optimization terminated successfully.' nfev: 158 nit: 31 njev: 19 status: 0 success: True x: array ([ 8000. , 8535.54345703, 8672.83227539, 8000.13848877, 8000.1831665 , 11046.69415283, 12850.85089111] [ 8000. , 8535.54345703, 8672.83227539, 8000.13848877, 8000.1831665 , 11046.69415283, 12850.85089111] [-31.99986786] fun: - [186] jac: array ([9.13215268] message: 'Optimization terminated successfully.' nfev: nit: 23 njev: 6 status: 0 success: True x: array ([9.13215268] 18762. [28.33816839] So what are we observing? As our risk appetite increases, our weights shift from “cash out now” to “wait it out till Friday because that’s when the prices could peak ”, we see our expected returns steadily increase. However, this comes at a cost: the variance of our portfolio increased considerably, thus we are taking on a lot more risk (and possibility to both gain more and lose more).
Ideally, the way you use this Is that every time you have a new screen price, you'll update your portfolio.
[y] On Monday AM price you'll have a new portfolio. You possibly will sell some at the current price, and have the remaining turnips hanging around. [0.00000000e 00, 0.00000000e 00, 2.60805565e-10, 5.45772528e-01, 4.54213107e-01, 6.23904416e-06, 1.60620122e-05]
On Monday PM price you'll recompute the weights. You can’t change what you already sold, that ship has gone. But you can make an improved decision (because you have more information) to update the weights of the remaining decisions. You will, again, decide to sell some and keep some given your risk appetite.
Continue until you exhaust your weekly turnips. Or evidently, your best friend tells you they have killer prices on their island.
That's about it.
There's an interesting scenario I didn't get to investigate but I think makes for a cool problem which is very similar. Every island has a different turnip price, so turnips are more valuable in some places than others. Let’s say you have access to many islands and access to their turnip probability distribution. How do you select when and where to sell your turnips?
GIPHY App Key not set. Please check settings