109 lines
3.9 KiB
Markdown
109 lines
3.9 KiB
Markdown
Quick attempt at an optimal portfolio balancer.
|
|
|
|
## Notes:
|
|
1. The balancing algorithm is not currently optimal (Work in progress). [See Closeness section](#closeness)
|
|
1. No external stock API is used. The given API gives only daily time-series output. This application is built as a stream processing system which would allow automatic rebalancing for specific events such as a price change of more than X%. I may add a randomized price generator as an input.
|
|
|
|
## Install
|
|
```
|
|
yarn
|
|
```
|
|
|
|
## To run
|
|
Using the pre-seed file with pricing from Jul 12th:
|
|
```
|
|
< test node .
|
|
```
|
|
|
|
Using the pre-seed file and allowing interactive commands:
|
|
```
|
|
cat test - | node .
|
|
```
|
|
|
|
|
|
## Interactive Commands:
|
|
`
|
|
<TICKER>:<price>
|
|
`
|
|
Sets the stock price for the given ticker
|
|
|
|
example:
|
|
```
|
|
AAPL:152.3
|
|
```
|
|
|
|
`
|
|
rebalance:<account_id>
|
|
`
|
|
Triggers a rebalance for the given account ID
|
|
|
|
example:
|
|
```
|
|
rebalance:1
|
|
```
|
|
note: Account 1 is there by default.
|
|
|
|
|
|
## Closeness
|
|
|
|
The problem of portfolio balancing can be described as such:
|
|
```
|
|
Minimize:
|
|
w_f - w_t
|
|
e.g.
|
|
SUM( |c_i*p_i - t_i| ) for stocks i
|
|
or
|
|
SUM( (c_i*p_i - t_i)^2 ) for stocks i
|
|
|
|
Subject to:
|
|
SUM( c_i*p_i ) <= T
|
|
c_i are Integers >= 0 (Except USD)
|
|
|
|
Where
|
|
w_f is the final portfolio
|
|
w_t is the target portfolio
|
|
c_i is the number of shares of stock i
|
|
p_i is the price of stock i
|
|
t_i is the total optimal fractional amount invested in stock i
|
|
e.g. % allocation * total portfolio value
|
|
```
|
|
|
|
This problem statement falls in the Mixed Integers Quadratic Programming category. This problem can be solved through enumeration in exponential time `2^n` where n is the total
|
|
number of possible stocks purchase (not just ticker), which is very much impractical. Optimisations can be applied to reduce it to `2^n` where n is the total number of tickers,
|
|
although still exponential. From there (or possibly through a simplified problem statement), this can possibly be solved in pseudo-polynomial time using algorithms such as
|
|
branch-and-bound or Quadknap.
|
|
|
|
Currently, the algorithm used is non-optimal in some edge-cases but produces very close results in most cases. That algorithm runs in linear time (quasilinear if tickers are not
|
|
previously sorted) and can be used to evaluate the bounds in a branch-and-bound algorithm for potentially optimal results.
|
|
|
|
Unfortunately, time constraints prevented me from finishing the implementation as of Sunday July 12th. I will update this README if that changes.
|
|
|
|
## Architecture
|
|
|
|
The application is built to process a stream of events, such as pricing updates and rebalance triggers. For this reason, no external API was used. As a possibility, I could
|
|
implement a readable stream which gets initialized from the provided API and then generates random variations of stock prices and a processor that triggers rebalancing on specific
|
|
events (i.e. APPL went down 5% today)
|
|
The following message would update the AAPL price to 123.
|
|
```
|
|
{type:"pricing", ticker: "AAPL", price: 123}
|
|
```
|
|
The following message would trigger an account rebalance on account id 1
|
|
```
|
|
{type:"rebalance", accountId: 1}
|
|
```
|
|
|
|
Although much of the code is simplified for presentation only, storage backends using different databases could easily be added and different stream inputs (such as Kafka, NATS,
|
|
rabbitMQ) could be implemented. Testing of the codebase would remain easy as dependencies are contained in specific modules with simple interfaces.
|
|
|
|
## Dependencies
|
|
|
|
This example application currently only depends (ignoreing devDependencies) on the following packages:
|
|
1. uuid
|
|
1. strom
|
|
|
|
UUID is used only to generate an account ID when none is provided. By default an account with id `1` is present for easier user interaction since the goal of this application is
|
|
presentation.
|
|
|
|
Strom is a dependency-free nodejs stream processing utility library, originally forked from https://github.com/Wenzil/Mhysa. Strom was written by Wenzil, Jerry Kurian and Lewis
|
|
Diamond (myself).
|