@@ -14,6 +14,7 @@ namespace plt = matplotlibcpp;
14
14
15
15
using namespace boost ::property_tree;
16
16
17
+ // Fetches the stock price data from Financial Modeling Prep
17
18
std::string fmp_address (std::string ticker){
18
19
std::string url = " https://financialmodelingprep.com" ;
19
20
std::string key = " " ;
@@ -56,6 +57,7 @@ std::string Request(const std::string& url) {
56
57
return readBuffer;
57
58
}
58
59
60
+ // Matrix Multiplication Function
59
61
std::vector<std::vector<double >> MMULT (std::vector<std::vector<double >> x,
60
62
std::vector<std::vector<double >> y)
61
63
{
@@ -77,6 +79,7 @@ std::vector<std::vector<double>> MMULT(std::vector<std::vector<double>> x,
77
79
return result;
78
80
}
79
81
82
+ // Matrix Transpose Function
80
83
std::vector<std::vector<double >> TRANSPOSE (std::vector<std::vector<double >> z)
81
84
{
82
85
std::vector<std::vector<double >> X;
@@ -91,6 +94,7 @@ std::vector<std::vector<double>> TRANSPOSE(std::vector<std::vector<double>> z)
91
94
return X;
92
95
}
93
96
97
+ // Inverse Matrix Function using Gaussian Elimination
94
98
std::vector<std::vector<double >> INVERSE (std::vector<std::vector<double >> x)
95
99
{
96
100
std::vector<std::vector<double >> I;
@@ -142,6 +146,7 @@ std::vector<std::vector<double>> INVERSE(std::vector<std::vector<double>> x)
142
146
return I;
143
147
}
144
148
149
+ // Multiplies a matrix by a coeffecient
145
150
std::vector<std::vector<double >> FACTOR (double a, std::vector<std::vector<double >> x)
146
151
{
147
152
for (int i = 0 ; i < x.size (); ++i){
@@ -152,6 +157,7 @@ std::vector<std::vector<double>> FACTOR(double a, std::vector<std::vector<double
152
157
return x;
153
158
}
154
159
160
+ // Adds or Subtracts a matrix from a matrix with sign = -1 or 1
155
161
std::vector<std::vector<double >> ADDSUB (std::vector<std::vector<double >> a, std::vector<std::vector<double >> b, double sign)
156
162
{
157
163
for (int i = 0 ; i < a.size (); ++i){
@@ -162,6 +168,7 @@ std::vector<std::vector<double>> ADDSUB(std::vector<std::vector<double>> a, std:
162
168
return a;
163
169
}
164
170
171
+ // Calculates the rate of return matrix of a given matrix of stock prices
165
172
std::vector<std::vector<double >> RateOfReturn (std::vector<std::vector<double >> x)
166
173
{
167
174
std::vector<std::vector<double >> y;
@@ -176,82 +183,104 @@ std::vector<std::vector<double>> RateOfReturn(std::vector<std::vector<double>> x
176
183
return y;
177
184
}
178
185
186
+ // Fetches the stock price data and stores them into a map with the key being the stock ticker and the value being the vector of close prices
179
187
std::map<std::string, std::vector<double >> Cyclone (std::vector<std::string> tickA, std::vector<std::string> tickB){
180
188
std::map<std::string, std::vector<double >> prices;
181
189
190
+ // Fetches stocks
182
191
for (auto & ticker : tickA){
192
+ // Fetch the stock data
183
193
std::string resp = Request (fmp_address (ticker));
184
194
std::stringstream ss (resp);
185
195
ptree df;
196
+ // Parse string to JSON using Boost
186
197
read_json (ss, df);
187
198
for (ptree::const_iterator it = df.begin (); it != df.end (); ++it){
188
199
if (it->first == " historical" ){
189
200
for (ptree::const_iterator jt = it->second .begin (); jt != it->second .end (); ++jt){
190
201
for (ptree::const_iterator kt = jt->second .begin (); kt != jt->second .end (); ++kt){
191
202
if (kt->first == " adjClose" ){
203
+ // Store adjusted close prices in map vector based on stock ticker
192
204
prices[ticker].push_back (atof (kt->second .get_value <std::string>().c_str ()));
193
205
}
194
206
}
195
207
}
196
208
}
197
209
}
210
+ // Reverse prices so oldest price is first and newest price is last
198
211
std::reverse (prices[ticker].begin (), prices[ticker].end ());
199
212
}
213
+
214
+ // Fetches hedging instruments
200
215
for (auto & ticker : tickB){
216
+ // Fetches data from Financial Modeling Prep
201
217
std::string resp = Request (fmp_address (ticker));
202
218
std::stringstream ss (resp);
203
219
ptree df;
220
+ // Parses response as JSON with Boost
204
221
read_json (ss, df);
205
222
for (ptree::const_iterator it = df.begin (); it != df.end (); ++it){
206
223
if (it->first == " historical" ){
207
224
for (ptree::const_iterator jt = it->second .begin (); jt != it->second .end (); ++jt){
208
225
for (ptree::const_iterator kt = jt->second .begin (); kt != jt->second .end (); ++kt){
209
226
if (kt->first == " adjClose" ){
227
+ // Stores adjusted close prices in map
210
228
prices[ticker].push_back (atof (kt->second .get_value <std::string>().c_str ()));
211
229
}
212
230
}
213
231
}
214
232
}
215
233
}
234
+ // Oldest prices first, newest prices last
216
235
std::reverse (prices[ticker].begin (), prices[ticker].end ());
217
236
}
218
237
return prices;
219
238
}
220
239
240
+ // Calculates the Minimum Variance Portfolio
221
241
std::vector<double > MinVariancePortfolio (std::vector<std::vector<double >> ror, int lookback)
222
242
{
243
+ // Generates a set of weights for each inputted portfolio returns snapshot
223
244
auto solver = [](std::vector<std::vector<double >> x){
245
+
246
+ // Sets bounds
224
247
int m = x.size ();
225
248
int n = x[0 ].size ();
226
249
std::vector<std::vector<double >> mu, cov, temporary;
250
+
251
+ // Calculates the mean vector
227
252
for (int i = 0 ; i < m; ++i){
228
253
mu.push_back ({1.0 });
229
254
}
230
255
mu = FACTOR (1.0 /((double ) m), MMULT (TRANSPOSE (mu), x));
256
+
257
+ // Subtracts mean from returns matrix
231
258
for (int i = 0 ; i < m; ++i){
232
259
for (int j = 0 ; j < n; ++j){
233
260
x[i][j] -= mu[0 ][j];
234
261
}
235
262
}
263
+
264
+ // Calculates the covariance matrix
236
265
cov = FACTOR (1.0 /((double ) m - 1 ), MMULT (TRANSPOSE (x), x));
237
- /*
238
- ((2E, 1),
239
- (1, 0)) (0, 1)
240
-
241
- */
242
266
267
+ // Multiplies covariance matrix by 2.0 for the MinVariance matrix
243
268
cov = FACTOR (2.0 , cov);
244
269
std::vector<double > ones, weights;
245
270
mu.clear ();
271
+
272
+ // Adds 1.0 to each column in the matrix
246
273
for (int i = 0 ; i < n; ++i){
247
274
cov[i].push_back (1.0 );
248
275
ones.push_back (1.0 );
249
- mu.push_back ({0.0 });
276
+ mu.push_back ({0.0 }); // Necessary for matrix multiplication
250
277
}
278
+ // Adds 1.0 to each row in the matrix
251
279
ones.push_back (0.0 );
252
280
cov.push_back (ones);
253
281
mu.push_back ({1.0 });
254
282
283
+ // Computes the weights of the portfolio by taking the inverse matrix and multiplying it by zeros
255
284
temporary = MMULT (INVERSE (cov), mu);
256
285
for (int i = 0 ; i < n; ++i){
257
286
weights.push_back (temporary[i][0 ]);
@@ -261,9 +290,15 @@ std::vector<double> MinVariancePortfolio(std::vector<std::vector<double>> ror, i
261
290
262
291
std::vector<double > result;
263
292
293
+ // Takes a rolling snapshot of the rate of returns matrix and inputs it into the solver to generate min-variance weights
264
294
for (int i = lookback; i < ror.size (); ++i){
295
+ // Returns snapshot
265
296
std::vector<std::vector<double >> hold_items = {ror.begin () + (i - lookback), ror.begin () + i};
297
+
298
+ // Allocated weights
266
299
std::vector<double > weights = solver (hold_items);
300
+
301
+ // Summation to generate weighted portfolio from inputted stocks
267
302
double total = 0 ;
268
303
for (int j = 0 ; j < ror[0 ].size (); ++j){
269
304
total += weights[j]*ror[i][j];
@@ -277,13 +312,17 @@ std::vector<double> MinVariancePortfolio(std::vector<std::vector<double>> ror, i
277
312
return result;
278
313
}
279
314
315
+ // Uses the Kalman Filter to calculate a hedging ratio along with its test statistic to test significance
280
316
std::vector<double > HedgingRatio (std::vector<double > x, std::vector<double > y){
281
317
std::vector<double > result;
318
+
319
+ // Used for building a quick vector off just x[i]
282
320
auto bx = [](double ux){
283
321
std::vector<std::vector<double >> result = {{1.0 }, {ux}};
284
322
return result;
285
323
};
286
324
325
+ // Calculates the mean of a given vector
287
326
auto mean = [](std::vector<double > q){
288
327
double total = 0 ;
289
328
for (auto & i : q){
@@ -292,39 +331,58 @@ std::vector<double> HedgingRatio(std::vector<double> x, std::vector<double> y){
292
331
total /= ((double ) q.size ());
293
332
return total;
294
333
};
295
-
334
+
296
335
std::vector<std::vector<double >> B, Bp, Pp, Q, P, Yp, K, DK, deltaB;
297
336
double R = 0 ;
298
337
338
+ // Set initial parameters to Kalman Filter inputs
299
339
B = {{0.1 }, {0.1 }};
300
340
Bp = {{0.1 }, {0.1 }};
301
341
Pp = {{1.0 , 0.0 }, {0.0 , 1.0 }};
302
342
Q = {{1.0 , 0.0 }, {0.0 , 1.0 }};
303
343
P = {{1.0 , 0.0 }, {0.0 , 1.0 }};
304
344
345
+ // Kalman Filter Processing
305
346
for (int i = 0 ; i < x.size (); ++i){
347
+ // Iterate newest beta
306
348
Bp = B;
349
+
350
+ // Add Q and P
307
351
Pp = ADDSUB (Q, P, 1.0 );
352
+
353
+ // Generate Yhat for current point
308
354
Yp = MMULT (TRANSPOSE (Bp), bx (x[i]));
355
+
356
+ // Compute the R parameter which is the average residual
309
357
if (i > 2 ){
310
358
R = 0 ;
311
359
for (int t = 0 ; t < i; ++t){
312
360
R += pow (y[t] - (Bp[0 ][0 ] + Bp[1 ][0 ]*x[t]), 2 );
313
361
}
314
362
R = R / ((double ) i - 1 );
315
363
}
364
+
365
+ // Compute Kalman Gain
316
366
K = MMULT (Pp, bx (x[i]));
317
367
DK = MMULT (TRANSPOSE (K), bx (x[i]));
368
+
369
+ // Add R to and divide Kalman Gain
318
370
DK[0 ][0 ] += R;
319
371
for (int t = 0 ; t < K.size (); ++t){
320
372
K[t][0 ] /= DK[0 ][0 ];
321
373
}
374
+ // Add beta to error multiplied by Kalman gain
322
375
B = ADDSUB (Bp, FACTOR (y[i] - Yp[0 ][0 ], K), 1.0 );
376
+
377
+ // Update P by subtracting Pp by the multiplication of Kalman gain by X and Pp
323
378
P = ADDSUB (Pp, MMULT (K, MMULT (TRANSPOSE (bx (x[i])), Pp)), -1.0 );
379
+
380
+ // Update Q by multiplying the error between beta and predicted beta
324
381
deltaB = ADDSUB (B, Bp, -1.0 );
325
382
Q = MMULT (deltaB, TRANSPOSE (deltaB));
326
383
}
327
384
385
+ // Compute test statistic off the residual sum of squares and the sum squared of x minus its mean
328
386
double rss = 0 ;
329
387
double bottom = 0 ;
330
388
double mux = mean (x);
@@ -334,6 +392,8 @@ std::vector<double> HedgingRatio(std::vector<double> x, std::vector<double> y){
334
392
}
335
393
336
394
rss /= ((double ) x.size () - 2 );
395
+
396
+ // Generate test statistic and return hedging ratio with it
337
397
double t_stat = B[1 ][0 ] / sqrt (rss/bottom);
338
398
339
399
result.push_back (B[0 ][0 ]);
@@ -346,6 +406,7 @@ std::vector<double> HedgingRatio(std::vector<double> x, std::vector<double> y){
346
406
347
407
int main ()
348
408
{
409
+ // Initialize stock and hedging instrument tickers
349
410
std::vector<std::string> port_ticks = {" AAPL" ," MSFT" ," NVDA" ," GOOGL" };
350
411
std::map<std::string, std::string> hedge_items = {
351
412
{" VDC" ," Vangaurd Consumer Staples ETF" },
@@ -355,10 +416,12 @@ int main()
355
416
};
356
417
std::vector<std::string> hedge_ticks = {" VDC" ," IXJ" ," SHY" ," GLDM" };
357
418
419
+ // Fetch all price data
358
420
std::map<std::string, std::vector<double >> prices = Cyclone (port_ticks, hedge_ticks);
359
421
360
422
std::vector<std::vector<double >> closePort, closeHedge, rorPort, rorHedge;
361
423
424
+ // Split up stocks and hedging instrument prices
362
425
for (auto & tick : port_ticks){
363
426
closePort.push_back (prices[tick]);
364
427
}
@@ -367,18 +430,22 @@ int main()
367
430
closeHedge.push_back (prices[tick]);
368
431
}
369
432
433
+ // Transpose matrices in order to easily compute the rate of returns
370
434
closePort = TRANSPOSE (closePort);
371
435
closeHedge = TRANSPOSE (closeHedge);
372
436
373
437
rorPort = RateOfReturn (closePort);
374
438
rorHedge = RateOfReturn (closeHedge);
375
439
440
+ // Transpose hedging instruments back so they are easier to loop through for the kalman filter
376
441
rorHedge = TRANSPOSE (rorHedge);
377
442
378
443
int lookback = 100 ;
379
444
445
+ // Generate the Minimum Variance Portfolio from the stock data returns
380
446
std::vector<double > PortfolioReturns = MinVariancePortfolio (rorPort, lookback);
381
447
448
+ // Calculate regression line x-axis off the portfolio returns min and max
382
449
auto min_p = std::min_element (PortfolioReturns.begin (), PortfolioReturns.end ());
383
450
auto max_p = std::max_element (PortfolioReturns.begin (), PortfolioReturns.end ());
384
451
@@ -393,13 +460,17 @@ int main()
393
460
for (int i = 0 ; i < line_length; ++i){
394
461
XP.push_back (x0 + i*dX);
395
462
}
396
-
463
+
464
+ // Generate plots
397
465
std::vector<PyObject*> plots;
398
466
for (auto & place : {221 , 222 , 223 , 224 }){
399
467
plots.push_back (plt::chart2D (place));
400
468
}
401
469
470
+ // Generate hedging parameters and construct regression to be plotted
402
471
for (int i = 0 ; i < hedge_ticks.size (); ++i){
472
+
473
+ // Adjust hedging data to match portfolio returns dimensions
403
474
rorHedge[i] = {rorHedge[i].begin () + lookback, rorHedge[i].end ()};
404
475
std::vector<double > hp = HedgingRatio (PortfolioReturns, rorHedge[i]);
405
476
@@ -410,9 +481,11 @@ int main()
410
481
YP.push_back (hp[0 ] + hp[1 ]*XP[t]);
411
482
}
412
483
484
+ // Plot the points and regression line
413
485
plt::scatter2D (plots[i], PortfolioReturns, rorHedge[i], " red" );
414
486
plt::plot2D (plots[i], XP, YP, " blue" );
415
487
488
+ // Prints out whether each hedging instrument has a significant ratio or not
416
489
std::cout << hedge_ticks[i] << " has a test-statistic of " << hp[2 ] << std::endl;
417
490
}
418
491
@@ -421,4 +494,4 @@ int main()
421
494
422
495
423
496
return 0 ;
424
- }
497
+ }
0 commit comments