Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,31 @@ A default CQM run with a transaction cost factor of 1% can be done with the foll

`python portfolio.py -m 'CQM' -t 0.01`

##### Stride Runs

Similar to the CQM runs, the user can run the same problem using the Stride™ hybrid solver.

```python portfolio.py -m 'Stride'```

The same parameters as the CQM formulation can be used. The output of the default Stride solver run is printed on the console as follows.

```bash
Stride run...
Minimize mean-variance expression

Best feasible solution:
AAPL 65
MSFT 9
AAL 1
WMT 1

Estimated Returns: 15.31
Sales Revenue: 0.00
Purchase Cost: 999.60
Transaction Cost: 0.00
Variance: 2464.38
```

##### DQM Runs

The user can select to build a disctrete quadratic model (DQM) using the following command:
Expand Down Expand Up @@ -272,25 +297,25 @@ expression with an associated penalty coefficient γ. The DQM code contains
to do a grid search in order to obtain the best values of the penalty coefficients γ
and the risk-aversion coefficient α that result in the best objective value.

### CQM
### CQM and Stride

### Variables
Each x<sub>i</sub> denotes the number of shares of stock i to be purchased.

### Constraints

All CQM formulations include the budget constraint described earlier.
All CQM and Stride solver formulations include the budget constraint described earlier.

##### Risk-Bounding Formulation

This includes an upper bound on the risk as a quadratic constraint.
Such a constraint is supported natively by CQM.
Such a constraint is supported natively by the CQM and Stride solvers.

##### Return-Bounding Formulation
This includes a lower bound on the returns as a linear inequality constraint.

### Objective
There are 3 CQM formulations, each with a different objective:
There are 3 formulations, each with a different objective:
- The bi-objective formulation corresponds to the original problem formulation where
a combination of variance and mean is minimized.
- The risk-bounding formulation where only the returns are maximized.
Expand Down
19 changes: 13 additions & 6 deletions portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@
"--model-type",
default="CQM",
multiple=False,
type=click.Choice(["CQM", "DQM"], case_sensitive=False),
type=click.Choice(["CQM", "DQM", "Stride"], case_sensitive=False),
show_default=True,
help="Model type, CQM or DQM",
help="Model type, CQM, Stride or DQM",
)
@click.option(
"-r",
Expand Down Expand Up @@ -142,19 +142,26 @@ def main(
t_cost: float,
):

solver_type = SolverType.CQM if model_type == "CQM" else SolverType.DQM
if model_type == "CQM":
solver_type = SolverType.CQM
elif model_type == "Stride":
solver_type = SolverType.Stride
else:
solver_type = SolverType.DQM

if (max_risk or min_return) and solver_type is SolverType.DQM:
raise Exception("The bound options require a CQM.")
raise Exception("The bound options require a CQM or Stride.")

if (gamma or bin_size) and solver_type is SolverType.CQM:
if (gamma or bin_size) and solver_type in (SolverType.CQM, SolverType.Stride):
raise Exception("The option gamma or bin-size requires a DQM.")

if num and not dates:
raise Exception("User must provide dates with option 'num'.")

if t_cost and solver_type is SolverType.DQM:
raise Exception("The transaction cost option requires a CQM. Set t_cost=0 for DQM.")
raise Exception(
"The transaction cost option requires a CQM or Stride. Set t_cost=0 for DQM."
)

if rebalance:
print(f"\nRebalancing portfolio optimization run...")
Expand Down
6 changes: 4 additions & 2 deletions src/demo_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
class SolverType(Enum):
CQM = 0
DQM = 1
Stride = 2

@property
def label(self):
return {
SolverType.CQM: "Quantum Hybrid (CQM)",
SolverType.DQM: "Quantum Hybrid (DQM)",
SolverType.CQM: "CQM Hybrid Solver",
SolverType.DQM: "DQM Hybrid Solver",
SolverType.Stride: "Stride Hybrid Solver",
}[self]


Expand Down
14 changes: 13 additions & 1 deletion src/multi_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import matplotlib.pyplot as plt

from dwave.system import LeapHybridCQMSampler, LeapHybridDQMSampler
from dwave.system import LeapHybridCQMSampler, LeapHybridDQMSampler, LeapHybridNLSampler

from src.single_period import SinglePeriod

Expand Down Expand Up @@ -180,6 +180,7 @@ def initiate_run_update(
self.sampler = {
"CQM": LeapHybridCQMSampler(**self.sampler_args),
"DQM": LeapHybridDQMSampler(**self.sampler_args),
"Stride": LeapHybridNLSampler(**self.sampler_args),
}

baseline_result, months, all_solutions, init_holdings, initial_budget = self.run_update(
Expand All @@ -197,6 +198,7 @@ def initiate_run_update(
self.sampler = {}
self.sample_set["CQM"] = {}
self.sample_set["DQM"] = {}
self.sample_set["Stride"] = {}

return baseline_result, months, all_solutions, init_holdings

Expand Down Expand Up @@ -301,6 +303,16 @@ def run_update(
if self.model_type is SolverType.DQM:
self.build_dqm()
solution = self.solve_dqm()
elif self.model_type is SolverType.Stride:
# Set budget to 0 to enforce that portfolio is self-financing
if self.t_cost and not first_purchase:
self.budget = 0

solution = self.solve_stride(
max_risk=max_risk, min_return=min_return, init_holdings=init_holdings
)

init_holdings = solution["stocks"]
else:
# Set budget to 0 to enforce that portfolio is self-financing
if self.t_cost and not first_purchase:
Expand Down
Loading