Quantum Portfolio Optimizer: A Qiskit Function by Global Data Quantum
API 참조를 확인하세요
Qiskit Functions는 IBM Quantum® Premium Plan, Flex Plan, On-Prem(IBM Quantum Platform API 경유) Plan 사용자에게만 제공되는 실험적 기능입니다. 현재 미리 보기 릴리스 상태이며 변경될 수 있습니다.
개요
Quantum Portfolio Optimizer는 동적 포트폴리오 최적화 문제를 해결하는 Qiskit Function입니다. 이 문제는 수익을 극대화하고 위험을 최소화하기 위해 여러 자산에 걸친 정기적 투자를 재조정하는 금융 분야의 표준 문제입니다. 최첨단 양자 최적화 기법을 적용하여, 양자 컴퓨팅에 대한 전문 지식이 없는 사용자도 최적의 투자 경로를 찾는 데 있어 양자 컴퓨팅의 장점을 활용할 수 있도록 프로세스를 간소화합니다. 포트폴리오 매니저, 계량금융 연구자, 개인 투자자 모두에게 적합하며, 포트폴리오 최적화의 트레이딩 전략 백테스팅을 지원합니다.
함수 설명
Quantum Portfolio Optimizer 함수는 Variational Quantum Eigensolver(VQE) 알고리즘을 사용하여 QUBO(Quadratic Unconstrained Binary Optimization) 문제를 풀고, 동적 포트폴리오 최적화 문제를 해결합니다. 사용자는 자산 가격 데이터를 제공하고 투자 제약 조건을 정의하기만 하면, 함수가 양자 최적화 프로세스를 실행하여 최적화된 투자 경로 집합을 반환합니다.
프로세스는 네 가지 주요 단계로 구성됩니다. 먼저 입력 데이터를 양자 호환 문제로 매핑하여 동적 포트폴리오 최적화 문제의 QUBO를 구성하고 양자 연산자(Ising Hamiltonian)로 변환합니다. 다음으로 입력 문제와 VQE 알고리즘이 양자 하드웨어에서 실행될 수 있도록 조정됩니다. 이후 VQE 알고리즘이 양자 하드웨어에서 실행되고, 마지막으로 결과를 후처리하여 최적의 투자 경로를 제공합니다. 또한 출력 품질을 극대화하기 위해 노이즈 인식(SQD 기반) 후처리가 포함되어 있습니다.
이 Qiskit Function은 Global Data Quantum의 출판된 논문을 기반으로 합니다.
시작하기
API 키를 사용하여 인증하고 다음과 같이 Qiskit Function을 선택하세요. (이 코드 조각은 이미 계정을 로컬 환경에 저장했다고 가정합니다.)
# Added by doQumentation — required packages for this notebook
!pip install -q pandas qiskit-ibm-catalog
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
예제: 7개 자산을 이용한 동적 포트폴리오 최적화
이 예제에서는 동적 포트폴리오 최적화(DPO) 함수를 실행하고 최적의 성능을 위해 설정을 조정하는 방법을 보여줍니다. 원하는 결과를 달성하기 위해 파라미터를 미세 조정하는 자세한 단계를 포함합니다.
이 경우 7개의 자산, 4개의 시간 단계, 4개의 해상도 Qubit를 포함하며, 총 112개의 Qubit가 필요합니다.
1. 포트폴리오에 포함된 자산 읽기
포트폴리오의 모든 자산이 특정 경로의 폴더에 저장되어 있다면, 다음 함수를 사용하여 pandas.DataFrame으로 로드하고 dict 형식 객체로 변환할 수 있습니다.
import os
import glob
import pandas as pd
def read_and_join_csv(file_pattern):
"""
Reads multiple CSV files matching the file pattern and combines them
into a single DataFrame.
Parameters:
file_pattern (str): The pattern to match CSV files.
Returns:
pd.DataFrame: Combined DataFrame with data from all CSV files.
"""
# Find all files matching the pattern
csv_files = glob.glob(file_pattern)
# Get the base file names without the .csv extension
file_names = [os.path.basename(f).replace(".csv", "") for f in csv_files]
# Read each CSV file into a DataFrame and set the first column as the index
df_list = [pd.read_csv(f).set_index("Unnamed: 0") for f in csv_files]
# Rename columns in each DataFrame to the base file names
for df, name in zip(df_list, file_names):
df.columns = [name]
# Combine all DataFrames into one by merging them side by side
combined_df = pd.concat(df_list, axis=1)
return combined_df
file_pattern = "route/to/folder/with/assets/data/*.csv"
assets = read_and_join_csv(file_pattern).to_dict()
이 예제에서는 8801.T, CLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y, XS2239553048 자산을 사용했습니다. 다음 그림은 이 예제에서 사용된 데이터를 보여주며, 2023년 1월 1일부터 9월 1일까지 자산의 일일 종가 변동을 나타냅니다.
이 예제에서는 날짜 간 균일성을 보장하기 위해 비거래일에는 이전 거래일의 종가를 채웠습니다. 선택된 자산이 거래일이 다른 여러 시장에서 왔기 때문에 데이터셋의 일관성을 위해 표준화하는 이 단계가 필수적입니다.
2. 문제 정의
qubo_settings 딕셔너리에서 파라미터를 구성하여 문제의 사양을 정의합니다.
qubo_settings = {
"nt": 4,
"nq": 4,
"dt": 30,
"max_investment": 25,
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}
3. 옵티마이저 및 ansatz 설정 정의 (선택 사항)
선택적으로 옵티마이저와 그 파라미터의 선택, 그리고 primitive와 그 구성의 지정을 포함하여 최적화 프로세스에 대한 특정 요구 사항을 정의할 수 있습니다.
Tailored Ansatz의 경우, 선택된 population size는 이 값이 안정적이고 효율적인 최적화를 제공한다는 이전 실험에 기반합니다.
Real Amplitudes Ansatz의 경우, population_size와 Circuit의 qubit 수 사이의 선형 관계를 따를 수 있습니다. 대략적인 경험 법칙으로, real_amplitudes ansatz에 대해 최소 population_size ~ 0.8 * n_qubits를 사용하는 것이 권장됩니다.
Optimized Real Amplitudes가 Real Amplitudes ansatz보다 더 나은 최적화 성능을 보일 것으로 예상됩니다. 그러나 이 ansatz에서 최적화할 변수의 수가 Real Amplitudes의 경우보다 훨씬 빠르게 증가합니다(논문 참조). 따라서 큰 문제의 경우 Optimized Real Amplitudes는 더 많은 Circuit 실행이 필요합니다. Optimized Real Amplitudes는 최대 100 Qubit가 필요한 문제에 유용할 가능성이 높지만, population_size 파라미터를 설정할 때 주의하는 것이 좋습니다. population_size 확장의 예로, 이전 표에서 84-Qubit 문제의 경우 Optimized Real Amplitudes는 120 population_size가 필요하고, 56-Qubit 문제의 경우 population_size 40이면 충분합니다.
optimizer_settings = {
"de_optimizer_settings": {
"num_generations": 20,
"population_size": 90,
"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,
},
}
특정 ansatz를 선택하는 것도 가능합니다. 다음은 'Tailored' ansatz를 사용합니다.
ansatz_settings = {
"ansatz": "tailored",
"multiple_passmanager": False,
}
4. 문제 실행
dpo_job = dpo_solver.run(
assets=assets,
qubo_settings=qubo_settings,
optimizer_settings=optimizer_settings,
ansatz_settings=ansatz_settings,
backend_name="<backend name>",
previous_session_id=[],
apply_postprocess=True,
)
5. 결과 조회
함수는 목적 함수 값에 따라 낮은 것부터 높은 것 순으로 정렬된 투자 궤적이 포함된 딕셔너리를 반환합니다(API 참조의 출력 섹션 참고). 이 결과 세트를 통해 가장 낮은 비용의 궤적과 해당 투자 평가를 식별할 수 있습니다. 또한 다양한 궤적을 분석하여 특정 요구 사항이나 목표에 가장 잘 맞는 궤적을 선택할 수 있습니다. 이러한 유연성은 다양한 선호도나 시나리오에 맞게 선택을 조정할 수 있게 합니다. 프로세스 중 발견된 가장 낮은 목적 비용을 달성한 결과 전략을 먼저 확인합니다.
# Get the results of the job
dpo_result = dpo_job.result()
# Show the solution strategy
dpo_result["result"]
{'time_step_0': {'8801.T': 0.11764705882352941,
'ITX.MC': 0.20588235294117646,
'META': 0.38235294117647056,
'GBPJPY=X': 0.058823529411764705,
'TMBMKDE-10Y': 0.0,
'CLF': 0.058823529411764705,
'XS2239553048': 0.17647058823529413},
'time_step_1': {'8801.T': 0.11428571428571428,
'ITX.MC': 0.14285714285714285,
'META': 0.2,
'GBPJPY=X': 0.02857142857142857,
'TMBMKDE-10Y': 0.42857142857142855,
'CLF': 0.0,
'XS2239553048': 0.08571428571428572},
'time_step_2': {'8801.T': 0.0,
'ITX.MC': 0.09375,
'META': 0.3125,
'GBPJPY=X': 0.34375,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.25},
'time_step_3': {'8801.T': 0.3939393939393939,
'ITX.MC': 0.09090909090909091,
'META': 0.12121212121212122,
'GBPJPY=X': 0.18181818181818182,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.21212121212121213}}
그 후 메타데이터를 사용하여 모든 샘플링된 전략의 결과에 접근할 수 있습니다. 이를 통해 옵티마이저가 반환한 대안 궤적을 추가로 분석할 수 있습니다. 이를 위해 dpo_result['metadata']['all_samples_metrics']에 저장된 딕셔너리를 읽으면 됩니다. 이 딕셔너리에는 최적 전략에 대한 추가 정보뿐만 아니라 최적화 중 평가된 다른 후보 전략의 세부 정보도 포함되어 있습니다.
다음 예제는 pandas를 사용하여 최적 전략과 관련된 주요 메트릭을 추출하는 방법을 보여줍니다. 여기에는 Restriction Deviation, Sharpe Ratio, 그리고 해당 투자 수익률이 포함됩니다.
# Convert metadata to a DataFrame
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']}")
Minimum Objective Cost Found: -3.78
Best Solution:
- Restriction Deviation: 40.0
- Sharpe Ratio: 24.82
- Return: 0.46
6. 성능 분석
마지막으로, 최적화 애플리케이션의 성능을 분석합니다. 구체적으로, 이전 예제에서 얻은 결과를 랜덤 기준선과 비교하여 접근 방식의 효과를 평가합니다. 양자 알고리즘이 입증 가능하고 일관되게 더 낮은 비용 값의 결과를 생성한다면, 이는 효과적인 최적화 프로세스를 나타냅니다.
그림은 목적 비용의 확률 분포를 보여줍니다. 이 분포를 생성하려면 함수 결과에서 목적 비용 목록을 가져오고 각 비용 값의 발생 횟수를 세면 됩니다(값은 소수점 둘째 자리로 반올림). 그런 다음 동일한 반올림 값의 횟수를 합산하여 카운트 열을 업데이트합니다. 더 나은 시각적 비교를 위해 발생 횟수가 정규화되어 각 분포가 0과 1 사이에 표시됩니다.
그림에서 보듯이(파란색 실선), Variational Quantum Eigensolver(SQD로 후처리) 접근 방식의 비용 분포는 낮은 목적 비용 값에 집중되어 있어 좋은 최적화 성능을 나타냅니다. 반면 노이즈가 있는 기준선은 더 높은 비용 값을 중심으로 더 넓은 분포를 보입니다. 회색 점선 수직선은 랜덤 분포의 평균값을 나타내며, 최적화된 투자 전략을 반환하는 함수의 일관성을 더욱 강조합니다. 추가 비교를 위해 그림의 검은색 점선은 Gurobi 옵티마이저(무료 버전)로 얻은 솔루션에 해당합니다. 이 모든 결과는 아래의 벤치마크에서 "Tailored" ansatz로 평가된 "Mixed Assets" 예제에 대해 더 자세히 살펴봅니다.
벤치마크
이 함수는 해상도 qubit, ansatz Circuit, 다양한 부문의 자산 그룹화에 대한 여러 구성에서 테스트되었습니다: 다양한 자산 혼합(Set 1), 석유 파생상품(Set 2), IBEX35(Set 3). 자세한 내용은 다음 표를 참조하세요.
| Set | Date | Assets |
|---|---|---|
| Set 1 | 01/01/2023 | 8801.T, CL=F, GBPJPY=X, ITX.MC, META, TMBMKDE-10Y, XS2239553048 |
| Set 2 | 01/06/2023 | CL=F, BZ=F, HO=F, NG=F, XOM, RB=F, 2222.SR |
| Set 3 | 01/11/2022 | ACS.MC, ITX.MC, FER.MC, ELE.MC, SCYR.MC, AENA.MC, AMS.MC |
솔루션 품질을 평가하기 위해 두 가지 주요 메트릭이 사용되었습니다.
- 목적 비용: 각 실험의 비용 함수 값을 Gurobi(무료 버전) 결과와 비교하여 최적화 효율성을 측정합니다.
- Sharpe 비율: 각 포트폴리오의 위험 조정 수익률을 포착하여 솔루션의 재무 성과에 대한 통찰력을 제공합니다.
이 메트릭들은 양자 생성 포트폴리오의 계산적 및 재무적 측면을 모두 벤치마크합니다.
| Example | qubits | Ansatz | Depth | Runtime Usage (s) | Total usage (s) | Objective cost | Sharpe | Gurobi objective cost | Gurobi Sharpe |
|---|---|---|---|---|---|---|---|---|---|
| Mixed Assets (Set 1, 4 time steps, 4-bit) | 112 | Tailored | 83 | 12735 | 13095 | -3.78 | 24.82 | -4.25 | 24.71 |
| Mixed Assets (Set 1,4 time steps, 4 time steps, 4-bit) | 112 | Real Amplitudes | 359 | 11739 | 11903 | -3.39 | 23.64 | -4.25 | 24.71 |
| Oil Derivatives (Set 2, 4 time steps, 3-bit) | 84 | Optimized Real Amplitudes | 78 | 6180 | 6350 | -3.73 | 19.13 | -4.19 | 21.71 |
| IBEX35 (Set 3, 4 time steps, 2-bit) | 56 | Optimized Real Amplitudes | 96 | 3314 | 3523 | -3.67 | 14.48 | -4.11 | 16.44 |
결과에 따르면 문제별 ansatz를 사용한 양자 옵티마이저는 다양한 포트폴리오 유형에서 효율적인 투자 전략을 효과적으로 식별합니다.
아래에서 optimizer_options 딕셔너리에 지정된 population size와 세대 수를 자세히 설명합니다. 다른 모든 파라미터는 기본값으로 설정되었습니다.
| Example | population_size | num_generations |
|---|---|---|
| Mixed Assets Portfolio | 90 | 20 |
| Mixed Assets Portfolio | 92 | 20 |
| Oil Derivatives Portfolio | 120 | 20 |
| IBEX35 Portfolio | 40 | 20 |
세대 수는 20으로 설정되었는데, 이 값이 수렴에 도달하기에 충분한 것으로 확인되었기 때문입니다. 또한 옵티마이저의 내부 파라미터 기본값은 변경하지 않았는데, 일관되게 좋은 성능을 제공하며 문헌과 구현 가이드라인에서 일반적으로 권장되기 때문입니다.
지원 받기
도움이 필요하면 qpo.support@globaldataquantum.com으로 이메일을 보낼 수 있습니다. 메시지에 함수 작업 ID를 포함하세요.
다음 단계
- 관련 연구 논문을 읽어보세요.
- 이 Qiskit Function의 API 참조를 방문하세요.
- 이 양식을 작성하여 함수 접근을 요청하세요.
- 동적 포트폴리오 최적화 튜토리얼을 시도해 보세요.