Skip to content

Explain system prerequisites #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
190 changes: 190 additions & 0 deletions classical_portfolio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Classical Portfolio Optimization - Alternative to D-Wave Implementation
# This version uses scipy and cvxpy instead of quantum solvers

import numpy as np
import pandas as pd
from scipy.optimize import minimize
import cvxpy as cp
import yfinance as yf
import matplotlib.pyplot as plt

class ClassicalPortfolioOptimizer:
"""Classical portfolio optimization without D-Wave quantum computing."""

def __init__(self, stocks, budget=1000, alpha=0.005):
self.stocks = stocks
self.budget = budget
self.alpha = alpha # Risk aversion coefficient
self.data = None
self.returns = None
self.cov_matrix = None

def load_data(self, file_path=None, start_date='2020-01-01', end_date='2023-01-01'):
"""Load stock data from CSV or Yahoo Finance."""
if file_path:
# Load from CSV
self.data = pd.read_csv(file_path, index_col=0)
else:
# Download from Yahoo Finance
self.data = yf.download(self.stocks, start=start_date, end=end_date)['Adj Close']

# Calculate returns and covariance
daily_returns = self.data.pct_change().dropna()
self.returns = daily_returns.mean() * 252 # Annualized returns
self.cov_matrix = daily_returns.cov() * 252 # Annualized covariance

def optimize_scipy(self):
"""Optimize portfolio using scipy (continuous weights)."""
n_stocks = len(self.stocks)

# Objective function: minimize risk - alpha * return
def objective(weights):
portfolio_return = np.dot(weights, self.returns)
portfolio_risk = np.sqrt(np.dot(weights.T, np.dot(self.cov_matrix, weights)))
return portfolio_risk - self.alpha * portfolio_return

# Constraints
constraints = [
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}, # Weights sum to 1
]

# Bounds: 0 <= weight <= 1 for each stock
bounds = tuple((0, 1) for _ in range(n_stocks))

# Initial guess: equal weights
x0 = np.array([1/n_stocks] * n_stocks)

# Optimize
result = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=constraints)

if result.success:
weights = result.x
# Convert weights to number of shares
latest_prices = self.data.iloc[-1]
investment_per_stock = weights * self.budget
shares = (investment_per_stock / latest_prices).astype(int)

return {
'shares': dict(zip(self.stocks, shares)),
'weights': dict(zip(self.stocks, weights)),
'expected_return': np.dot(weights, self.returns),
'risk': np.sqrt(np.dot(weights.T, np.dot(self.cov_matrix, weights))),
'total_cost': sum(shares * latest_prices)
}
else:
print("Optimization failed:", result.message)
return None

def optimize_cvxpy(self, max_risk=None, min_return=None):
"""Optimize portfolio using cvxpy (supports integer constraints)."""
n_stocks = len(self.stocks)
latest_prices = self.data.iloc[-1].values

# Decision variables: number of shares (integer)
shares = cp.Variable(n_stocks, integer=True)

# Portfolio value for each stock
portfolio_values = cp.multiply(shares, latest_prices)

# Total investment
total_investment = cp.sum(portfolio_values)

# Portfolio weights
weights = portfolio_values / total_investment

# Expected return
portfolio_return = weights @ self.returns.values

# Risk (variance)
portfolio_risk = cp.quad_form(weights, self.cov_matrix.values)

# Constraints
constraints = [
shares >= 0, # No short selling
total_investment <= self.budget, # Budget constraint
total_investment >= 0.9 * self.budget # Use at least 90% of budget
]

# Objective based on formulation
if max_risk is not None:
# Maximize return subject to risk constraint
objective = cp.Maximize(portfolio_return)
constraints.append(portfolio_risk <= max_risk)
elif min_return is not None:
# Minimize risk subject to return constraint
objective = cp.Minimize(portfolio_risk)
constraints.append(portfolio_return >= min_return)
else:
# Default: minimize risk - alpha * return
objective = cp.Minimize(portfolio_risk - self.alpha * portfolio_return)

# Create and solve problem
problem = cp.Problem(objective, constraints)
problem.solve(solver=cp.GLPK_MI) # Mixed integer solver

if problem.status == 'optimal':
shares_result = shares.value.astype(int)
total_cost = sum(shares_result * latest_prices)

# Calculate actual weights and metrics
actual_weights = (shares_result * latest_prices) / total_cost
actual_return = np.dot(actual_weights, self.returns)
actual_risk = np.sqrt(np.dot(actual_weights.T, np.dot(self.cov_matrix, actual_weights)))

return {
'shares': dict(zip(self.stocks, shares_result)),
'weights': dict(zip(self.stocks, actual_weights)),
'expected_return': actual_return,
'risk': actual_risk,
'total_cost': total_cost
}
else:
print("Optimization failed:", problem.status)
return None

# Example usage
if __name__ == "__main__":
# Example stocks
stocks = ['AAPL', 'MSFT', 'GOOGL', 'AMZN']
budget = 10000

# Create optimizer
optimizer = ClassicalPortfolioOptimizer(stocks, budget, alpha=0.005)

# Load data
print("Loading stock data...")
optimizer.load_data(start_date='2022-01-01', end_date='2023-12-31')

# Optimize using scipy (continuous)
print("\n--- Scipy Optimization (Continuous Weights) ---")
result_scipy = optimizer.optimize_scipy()
if result_scipy:
print("Optimal shares:", result_scipy['shares'])
print(f"Expected return: {result_scipy['expected_return']:.2%}")
print(f"Risk (std dev): {result_scipy['risk']:.2%}")
print(f"Total cost: ${result_scipy['total_cost']:.2f}")

# Optimize using cvxpy (integer shares)
print("\n--- CVXPY Optimization (Integer Shares) ---")
result_cvxpy = optimizer.optimize_cvxpy()
if result_cvxpy:
print("Optimal shares:", result_cvxpy['shares'])
print(f"Expected return: {result_cvxpy['expected_return']:.2%}")
print(f"Risk (std dev): {result_cvxpy['risk']:.2%}")
print(f"Total cost: ${result_cvxpy['total_cost']:.2f}")

# Risk-bounded optimization
print("\n--- Risk-Bounded Optimization ---")
result_risk_bounded = optimizer.optimize_cvxpy(max_risk=0.20)
if result_risk_bounded:
print("Optimal shares:", result_risk_bounded['shares'])
print(f"Expected return: {result_risk_bounded['expected_return']:.2%}")
print(f"Risk (std dev): {result_risk_bounded['risk']:.2%}")

# Return-bounded optimization
print("\n--- Return-Bounded Optimization ---")
result_return_bounded = optimizer.optimize_cvxpy(min_return=0.15)
if result_return_bounded:
print("Optimal shares:", result_return_bounded['shares'])
print(f"Expected return: {result_return_bounded['expected_return']:.2%}")
print(f"Risk (std dev): {result_return_bounded['risk']:.2%}")
9 changes: 9 additions & 0 deletions requirements_classical.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Requirements for classical portfolio optimization
# No D-Wave dependencies needed

numpy>=1.21.0
pandas>=1.3.0
scipy>=1.7.0
cvxpy>=1.2.0
matplotlib>=3.3.4
yfinance>=0.2.0