Lewati ke konten utama

Lakukan optimasi portofolio dinamis dengan Portfolio Optimizer dari Global Data Quantum

Catatan

Qiskit Functions adalah fitur eksperimental yang hanya tersedia untuk pengguna IBM Quantum® Premium Plan, Flex Plan, dan On-Prem (melalui IBM Quantum Platform API) Plan. Fitur ini masih dalam status rilis pratinjau dan dapat berubah sewaktu-waktu.

Estimasi penggunaan: Sekitar 55 menit pada prosesor Heron r2. (CATATAN: Ini hanya estimasi. Waktu aktual bisa berbeda.)

Latar Belakang

Masalah optimasi portofolio dinamis bertujuan menemukan strategi investasi optimal di beberapa periode waktu untuk memaksimalkan imbal hasil portofolio yang diharapkan dan meminimalkan risiko, seringkali dengan batasan tertentu seperti anggaran, biaya transaksi, atau penghindaran risiko. Berbeda dengan optimasi portofolio standar yang mempertimbangkan satu waktu untuk menyeimbangkan ulang portofolio, versi dinamis memperhitungkan sifat aset yang terus berkembang dan menyesuaikan investasi berdasarkan perubahan kinerja aset dari waktu ke waktu.

Tutorial ini mendemonstrasikan cara melakukan optimasi portofolio dinamis menggunakan Qiskit Function Quantum Portfolio Optimizer. Secara khusus, kami mengilustrasikan cara menggunakan fungsi aplikasi ini untuk memecahkan masalah alokasi investasi di beberapa langkah waktu.

Pendekatannya melibatkan perumusan optimasi portofolio sebagai masalah Quadratic Unconstrained Binary Optimization (QUBO) multi-objektif. Secara spesifik, kami merumuskan fungsi QUBO OO untuk mengoptimalkan empat tujuan berbeda secara bersamaan:

  • Memaksimalkan fungsi imbal hasil FF
  • Meminimalkan risiko investasi RR
  • Meminimalkan biaya transaksi CC
  • Mematuhi pembatasan investasi, yang dirumuskan dalam suku tambahan untuk diminimalkan PP.

Singkatnya, untuk menangani tujuan-tujuan ini kami merumuskan fungsi QUBO sebagai O=F+γ2R+C+ρP,O = -F + \frac{\gamma}{2} R + C + \rho P, di mana γ\gamma adalah koefisien penghindaran risiko dan ρ\rho adalah koefisien penguatan pembatasan (pengganda Lagrange). Perumusan eksplisit dapat ditemukan di Persamaan (15) dari manuskrip kami [1].

Kami menyelesaikannya menggunakan metode hibrida kuantum-klasik berdasarkan Variational Quantum Eigensolver (VQE). Dalam pengaturan ini, Circuit kuantum memperkirakan fungsi biaya, sementara optimasi klasik dilakukan menggunakan algoritma Differential Evolution, memungkinkan navigasi yang efisien di lanskap solusi. Jumlah Qubit yang dibutuhkan bergantung pada tiga faktor utama: jumlah aset na, jumlah periode waktu nt, dan resolusi bit yang digunakan untuk merepresentasikan investasi nq. Secara khusus, jumlah minimum Qubit dalam masalah kami adalah na*nt*nq.

Untuk tutorial ini, kami fokus pada pengoptimalan portofolio regional berdasarkan indeks IBEX 35 Spanyol. Secara spesifik, kami menggunakan portofolio tujuh aset seperti yang ditunjukkan pada tabel di bawah:

Portofolio IBEX 35ACS.MCITX.MCFER.MCELE.MCSCYR.MCAENA.MCAMS.MC

Kami menyeimbangkan ulang portofolio dalam empat langkah waktu, masing-masing dipisahkan oleh interval 30 hari mulai 1 November 2022. Setiap variabel investasi dikodekan menggunakan dua bit. Ini menghasilkan masalah yang membutuhkan 56 Qubit untuk diselesaikan.

Kami menggunakan ansatz Optimized Real Amplitudes, sebuah adaptasi yang disesuaikan dan efisien secara perangkat keras dari ansatz Real Amplitudes standar, yang secara khusus dirancang untuk meningkatkan kinerja pada jenis masalah optimasi keuangan ini.

Eksekusi kuantum dilakukan pada Backend ibm_torino. Untuk penjelasan rinci tentang perumusan masalah, metodologi, dan evaluasi kinerja, lihat manuskrip yang telah diterbitkan [1].

Persyaratan

# Added by doQumentation — required packages for this notebook
!pip install -q numpy
!pip install qiskit-ibm-catalog
!pip install pandas
!pip install matplotlib
!pip install yfinance

Pengaturan

Untuk menggunakan Quantum Portfolio Optimizer, pilih fungsi melalui Qiskit Functions Catalog. Kamu memerlukan akun IBM Quantum Premium Plan atau Flex Plan beserta lisensi dari Global Data Quantum untuk menjalankan fungsi ini.

Pertama, autentikasi dengan kunci API-mu. Kemudian, muat fungsi yang diinginkan dari Qiskit Functions Catalog. Di sini, kamu mengakses fungsi quantum_portfolio_optimizer dari katalog menggunakan kelas QiskitFunctionsCatalog. Fungsi ini memungkinkan kita menggunakan solver Quantum Portfolio Optimization yang telah ditentukan sebelumnya.

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(
channel="ibm_quantum_platform",
instance="INSTANCE_CRN",
token="YOUR_API_KEY", # Use the 44-character API_KEY you created and saved from the IBM Quantum Platform Home dashboard
)

# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")

Langkah 1: Baca portofolio input

Pada langkah ini, kami memuat data historis untuk tujuh aset yang dipilih dari indeks IBEX 35, khususnya dari 1 November 2022 hingga 1 April 2023.

Kami mengambil data menggunakan Yahoo Finance API, dengan fokus pada harga penutupan. Data kemudian diproses untuk memastikan semua aset memiliki jumlah hari data yang sama. Data yang hilang (hari non-perdagangan) ditangani secara tepat, memastikan semua aset sejajar pada tanggal yang sama.

Data disusun dalam DataFrame dengan format yang konsisten di semua aset.

import yfinance as yf
import pandas as pd

# List of IBEX 35 symbols
symbols = [
"ACS.MC",
"ITX.MC",
"FER.MC",
"ELE.MC",
"SCYR.MC",
"AENA.MC",
"AMS.MC",
]

start_date = "2022-11-01"
end_date = "2023-4-01"

series_list = []
symbol_names = [symbol.replace(".", "_") for symbol in symbols]

# Create a full date index including weekends
full_index = pd.date_range(start=start_date, end=end_date, freq="D")

for symbol, name in zip(symbols, symbol_names):
print(f"Downloading data for {symbol}...")
data = yf.download(symbol, start=start_date, end=end_date)["Close"]
data.name = name

# Reindex to include weekends
data = data.reindex(full_index)

# Fill missing values (for example, weekends or holidays) by forward/backward fill
data.ffill(inplace=True)
data.bfill(inplace=True)

series_list.append(data)

# Combine all series into a single DataFrame
df = pd.concat(series_list, axis=1)

# Convert index to string for consistency
df.index = df.index.astype(str)

# Convert DataFrame to dictionary
assets = df.to_dict()
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************]  1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
Downloading data for ACS.MC...
Downloading data for ITX.MC...
Downloading data for FER.MC...
Downloading data for ELE.MC...
Downloading data for SCYR.MC...
Downloading data for AENA.MC...
Downloading data for AMS.MC...

Langkah 2: Tentukan input masalah

Parameter yang dibutuhkan untuk mendefinisikan masalah QUBO dikonfigurasi dalam dictionary qubo_settings. Kita menentukan jumlah langkah waktu (nt), jumlah bit untuk spesifikasi investasi (nq), dan jendela waktu untuk setiap langkah waktu (dt). Selain itu, kita mengatur investasi maksimum per aset, koefisien risk aversion, biaya transaksi, dan koefisien pembatasan (lihat paper kami untuk detail formulasi masalahnya). Pengaturan ini memungkinkan kita menyesuaikan masalah QUBO dengan skenario investasi yang spesifik.

qubo_settings = {
"nt": 4,
"nq": 2,
"dt": 30,
"max_investment": 5, # maximum investment per asset is 2**nq/max_investment = 80%
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}

Dictionary optimizer_settings mengonfigurasi proses optimasi, termasuk parameter seperti num_generations untuk jumlah iterasi dan population_size untuk jumlah kandidat solusi per generasi. Pengaturan lainnya mengontrol aspek seperti tingkat rekombinasi, pekerjaan paralel, ukuran batch, dan rentang mutasi. Selain itu, pengaturan primitive seperti estimator_shots, estimator_precision, dan sampler_shots mendefinisikan konfigurasi Estimator dan Sampler kuantum untuk proses optimasi.

optimizer_settings = {
"de_optimizer_settings": {
"num_generations": 20,
"population_size": 40,
"recombination": 0.4,
"max_parallel_jobs": 5,
"max_batchsize": 4,
"mutation_range": [0.0, 0.25],
},
"optimizer": "differential_evolution",
"primitive_settings": {
"estimator_shots": 25_000,
"estimator_precision": None,
"sampler_shots": 100_000,
},
}
catatan

Jumlah total sirkuit bergantung pada parameter optimizer_settings dan dihitung sebagai (num_generations + 1) * population_size.

Dictionary ansatz_settings mengonfigurasi ansatz Circuit kuantum. Parameter ansatz menentukan penggunaan pendekatan "optimized_real_amplitudes", yaitu ansatz yang hemat perangkat keras dan dirancang untuk masalah optimasi keuangan. Selain itu, pengaturan multiple_passmanager diaktifkan untuk memungkinkan beberapa pass manager (termasuk pass manager lokal Qiskit default dan layanan Transpiler bertenaga AI Qiskit) selama proses optimasi, meningkatkan performa dan efisiensi eksekusi sirkuit secara keseluruhan.

ansatz_settings = {
"ansatz": "optimized_real_amplitudes",
"multiple_passmanager": False,
}

Terakhir, kita menjalankan optimasi dengan memanggil fungsi dpo_solver.run(), dengan meneruskan input yang sudah disiapkan. Ini mencakup dictionary data aset (assets), konfigurasi QUBO (qubo_settings), parameter optimasi (optimizer_settings), dan pengaturan ansatz Circuit kuantum (ansatz_settings). Selain itu, kita menentukan detail eksekusi seperti backend, dan apakah akan menerapkan pasca-pemrosesan pada hasil. Ini memulai proses optimasi portofolio dinamis pada backend kuantum yang dipilih.

dpo_job = dpo_solver.run(
assets=assets,
qubo_settings=qubo_settings,
optimizer_settings=optimizer_settings,
ansatz_settings=ansatz_settings,
backend_name="ibm_torino",
previous_session_id=[],
apply_postprocess=True,
)

Langkah 3: Analisis hasil optimasi

Di bagian ini, kita mengekstrak dan menampilkan solusi dengan biaya objektif terendah dari hasil optimasi. Bersama dengan biaya objektif minimum, kita juga menyajikan metrik utama yang terkait dengan solusi tersebut, termasuk deviasi pembatasan, rasio Sharpe, dan imbal hasil investasi.

# Get the results of the job
dpo_result = dpo_job.result()

# Show the solution strategy
dpo_result["result"]
{'time_step_0': {'ACS.MC': 0.11764705882352941,
'ITX.MC': 0.20588235294117646,
'FER.MC': 0.38235294117647056,
'ELE.MC': 0.058823529411764705,
'SCYR.MC': 0.0,
'AENA.MC': 0.058823529411764705,
'AMS.MC': 0.17647058823529413},
'time_step_1': {'ACS.MC': 0.11428571428571428,
'ITX.MC': 0.14285714285714285,
'FER.MC': 0.2,
'ELE.MC': 0.02857142857142857,
'SCYR.MC': 0.42857142857142855,
'AENA.MC': 0.0,
'AMS.MC': 0.08571428571428572},
'time_step_2': {'ACS.MC': 0.0,
'ITX.MC': 0.09375,
'FER.MC': 0.3125,
'ELE.MC': 0.34375,
'SCYR.MC': 0.0,
'AENA.MC': 0.0,
'AMS.MC': 0.25},
'time_step_3': {'ACS.MC': 0.3939393939393939,
'ITX.MC': 0.09090909090909091,
'FER.MC': 0.12121212121212122,
'ELE.MC': 0.18181818181818182,
'SCYR.MC': 0.0,
'AENA.MC': 0.0,
'AMS.MC': 0.21212121212121213}}
import pandas as pd

# Get results from the job
dpo_result = dpo_job.result()

# Convert metadata to a DataFrame, excluding 'session_id'
df = pd.DataFrame(dpo_result["metadata"]["all_samples_metrics"])

# Find the minimum objective cost
min_cost = df["objective_costs"].min()
print(f"Minimum Objective Cost Found: {min_cost:.2f}")

# Extract the row with the lowest cost
best_row = df[df["objective_costs"] == min_cost].iloc[0]

# Display the results associated with the best solution
print("Best Solution:")
print(f" - Restriction Deviation: {best_row['rest_breaches']}%")
print(f" - Sharpe Ratio: {best_row['sharpe_ratios']:.2f}")
print(f" - Return: {best_row['returns']:.2f}")
Minimum Objective Cost Found: -3.67
Best Solution:
- Restriction Deviation: 40.0%
- Sharpe Ratio: 14.54
- Return: 0.28

Kode berikut menunjukkan cara memvisualisasikan dan membandingkan distribusi biaya algoritma optimasi dengan distribusi sampling acak. Demikian pula, kita menjelajahi lanskap fungsi objektif QUBO (yang dapat dimuat dari output fungsi) dengan mengevaluasinya menggunakan investasi acak. Kita memplot kedua distribusi yang dinormalisasi dalam amplitudo untuk perbandingan lebih mudah tentang bagaimana proses optimasi berbeda dari sampling acak dalam hal biaya. Selain itu, hasil yang diperoleh menggunakan DOCPlex disertakan sebagai garis referensi vertikal putus-putus sebagai tolok ukur klasik. Kita menggunakan versi gratis DOCPlex — pustaka open-source IBM® untuk optimasi matematika di Python — untuk memecahkan masalah yang sama secara klasik.

import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import matplotlib.patheffects as patheffects

def plot_normalized(dpo_x, dpo_y_normalized, random_x, random_y_normalized):
"""
Plots normalized results for two sampling results.

Parameters:
dpo_x (array-like): X-values for the VQE Post-processed curve.
dpo_y_normalized (array-like): Y-values (normalized) for the VQE Post-processed curve.
random_x (array-like): X-values for the Noise (Random) curve.
random_y_normalized (array-like): Y-values (normalized) for the Noise (Random) curve.
"""
plt.figure(figsize=(6, 3))
plt.tick_params(axis="both", which="major", labelsize=12)

# Define custom colors
colors = ["#4823E8", "#9AA4AD"]

# Plot DPO results
(line1,) = plt.plot(
dpo_x, dpo_y_normalized, label="VQE Postprocessed", color=colors[0]
)
line1.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)

# Plot Random results
(line2,) = plt.plot(
random_x, random_y_normalized, label="Noise (Random)", color=colors[1]
)
line2.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)

# Set X-axis ticks to increment by 5 units
plt.gca().xaxis.set_major_locator(MultipleLocator(5))

# Axis labels and legend
plt.xlabel("Objective cost", fontsize=14)
plt.ylabel("Normalized Counts", fontsize=14)

# Add DOCPLEX reference line
plt.axvline(
x=-4.11, color="black", linestyle="--", linewidth=1, label="DOCPlex"
) # DOCPlex value
plt.ylim(bottom=0)

plt.legend()

# Adjust layout
plt.tight_layout()
plt.show()
import numpy as np
from collections import defaultdict

# ================================
# STEP 1: DPO COST DISTRIBUTION
# ================================

# Extract data from DPO results
counts_list = dpo_result["metadata"]["all_samples_metrics"][
"objective_costs"
] # List of how many times each solution occurred
cost_list = dpo_result["metadata"]["all_samples_metrics"][
"counts"
] # List of corresponding objective function values (costs)

# Round costs to one decimal and accumulate counts for each unique cost
dpo_counter = defaultdict(int)
for cost, count in zip(cost_list, counts_list):
rounded_cost = round(cost, 1)
dpo_counter[rounded_cost] += count

# Prepare data for plotting
dpo_x = sorted(dpo_counter.keys()) # Sorted list of cost values
dpo_y = [dpo_counter[c] for c in dpo_x] # Corresponding counts

# Normalize the counts to the range [0, 1] for better comparison
dpo_min = min(dpo_y)
dpo_max = max(dpo_y)
dpo_y_normalized = [
(count - dpo_min) / (dpo_max - dpo_min) for count in dpo_y
]

# ================================
# STEP 2: RANDOM COST DISTRIBUTION
# ================================

# Read the QUBO matrix
qubo = np.array(dpo_result["metadata"]["qubo"])

bitstring_length = qubo.shape[0]
num_random_samples = 100_000 # Number of random samples to generate
random_cost_counter = defaultdict(int)

# Generate random bitstrings and calculate their cost
for _ in range(num_random_samples):
x = np.random.randint(0, 2, size=bitstring_length)
cost = float(x @ qubo @ x.T)
rounded_cost = round(cost, 1)
random_cost_counter[rounded_cost] += 1

# Prepare random data for plotting
random_x = sorted(random_cost_counter.keys())
random_y = [random_cost_counter[c] for c in random_x]

# Normalize the random cost distribution
random_min = min(random_y)
random_max = max(random_y)
random_y_normalized = [
(count - random_min) / (random_max - random_min) for count in random_y
]

# ================================
# STEP 3: PLOTTING
# ================================

plot_normalized(dpo_x, dpo_y_normalized, random_x, random_y_normalized)

Output of the previous code cell

Grafik ini menunjukkan bagaimana optimizer portofolio kuantum secara konsisten menghasilkan strategi investasi yang teroptimasi.

Referensi

[1] Nodar, Álvaro, Irene De León, Danel Arias, Ernesto Mamedaliev, María Esperanza Molina, Manuel Martín-Cordero, Senaida Hernández-Santana et al. "Scaling the Variational Quantum Eigensolver for Dynamic Portfolio Optimization." arXiv preprint arXiv:2412.19150 (2024).

Survei tutorial

Luangkan waktu sebentar untuk memberikan masukan tentang tutorial ini. Wawasanmu akan membantu kami meningkatkan konten dan pengalaman pengguna. Tautan ke survei

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.

Source: IBM Quantum docs — updated 27 Apr 2026
English version on doQumentation — updated 7 Mei 2026
This translation based on the English version of 9 Apr 2026