Quantum Portfolio Optimizer: A Qiskit Function by Global Data Quantum
Qiskit Functions เป็นฟีเจอร์ทดลองที่ให้บริการเฉพาะสำหรับผู้ใช้ IBM Quantum® Premium Plan, Flex Plan และ On-Prem (via IBM Quantum Platform API) Plan เท่านั้น ขณะนี้อยู่ในสถานะ preview release และอาจมีการเปลี่ยนแปลง
ภาพรวม
Quantum Portfolio Optimizer คือ Qiskit Function ที่ออกแบบมาเพื่อแก้ปัญหา dynamic portfolio optimization ซึ่งเป็นปัญหาพื้นฐานในด้านการเงินที่มุ่งปรับสัดส่วนการลงทุนเป็นระยะ ๆ ในสินทรัพย์หลายประเภท เพื่อเพิ่มผลตอบแทนและลดความเสี่ยง ด้วยการใช้เทคนิค quantum optimization ล้ำสมัย ฟังก์ชันนี้ทำให้กระบวนการง่ายขึ้น ผู้ใช้ที่ไม่มีความเชี่ยวชาญด้าน quantum computing ก็สามารถได้รับประโยชน์จากมันในการหา investment trajectory ที่ดีที่สุด เหมาะสำหรับผู้จัดการพอร์ตโฟลิโอ นักวิจัยด้าน quantitative finance และนักลงทุนทั่วไป เครื่องมือนี้รองรับการ back-testing กลยุทธ์การซื้อขายในการ optimize พอร์ตโฟลิโอ
คำอธิบายฟังก์ชัน
Quantum Portfolio Optimizer ใช้อัลกอริทึม Variational Quantum Eigensolver (VQE) เพื่อแก้ปัญหา Quadratic Unconstrained Binary Optimization (QUBO) สำหรับ dynamic portfolio optimization ผู้ใช้เพียงแค่ระบุข้อมูลราคาสินทรัพย์และกำหนดเงื่อนไขการลงทุน จากนั้นฟังก์ชันจะรันกระบวนการ quantum optimization และคืนค่าชุด investment trajectory ที่ optimize แล้ว
กระบวนการประกอบด้วยสี่ขั้นตอนหลัก ขั้นแรก ข้อมูล input จะถูก map ไปยังปัญหาที่ compatible กับ quantum โดยสร้าง QUBO ของ dynamic portfolio optimization และแปลงเป็น quantum operator (Ising Hamiltonian) ต่อมา ปัญหา input และอัลกอริทึม VQE จะถูกปรับแต่งเพื่อรันบน quantum hardware จากนั้นรัน VQE บน quantum hardware และสุดท้ายผลลัพธ์จะถูก post-process เพื่อให้ได้ investment trajectory ที่ดีที่สุด ระบบยังมี noise-aware (SQD-based) post-processing เพื่อเพิ่มคุณภาพของ output ด้วย
Qiskit Function นี้อ้างอิงจาก published manuscript ของ Global Data Quantum
Input
อาร์กิวเมนต์ input ของฟังก์ชันมีรายละเอียดในตารางต่อไปนี้ ต้องระบุข้อมูลสินทรัพย์และรายละเอียดของปัญหา นอกจากนี้ยังสามารถกำหนด VQE settings เพื่อปรับแต่งกระบวนการ optimize ได้
| ชื่อ | ประเภท | คำอธิบาย | จำเป็น | ค่าเริ่มต้น | ตัวอย่าง |
|---|---|---|---|---|---|
| assets | json | Dictionary ที่เก็บราคาสินทรัพย์ | ใช่ | - | - |
| qubo_settings | json | การตั้งค่าของ QUBO | ใช่ | - | ดูตัวอย่างในตารางด้านล่าง |
| ansatz_settings | json | การตั้งค่าของ ansatz | ไม่ | None | ดูตัวอย่างในตารางด้านล่าง |
| optimizer_settings | json | การตั้งค่าของ optimizer | ไม่ | None | ดูตัวอย่างในตารางด้านล่าง |
| backend | str | ชื่อ QPU Backend | ไม่ | - | "ibm_torino" |
| previous_session_id | list of str | รายการ Session ID สำหรับดึงข้อมูลจากการรันก่อนหน้า(*) | ไม่ | Empty list | ["session_id_1", "session_id_2"] |
| apply_postprocess | bool | ใช้ noise-aware SQD post-processing | ไม่ | True | True |
| tags | list of strings | รายการ tag สำหรับระบุ experiment | ไม่ | Empty list | ["optimization", "quantum_computing"] |
หากต้องการรันต่อจากเดิมหรือดึง job ที่ประมวลผลใน Session ก่อนหน้า ให้ส่งรายการ Session ID ผ่านพารามิเตอร์ previous_session_id ซึ่งมีประโยชน์มากในกรณีที่ optimization task ล้มเหลวกลางคัน และต้องรันต่อจนเสร็จ ในการทำเช่นนั้น ต้องระบุอาร์กิวเมนต์เดิมที่ใช้ในการรันครั้งแรก พร้อมกับรายการ previous_session_id ตามที่อธิบาย
การโหลดข้อมูลจาก Session ก่อนหน้า (เพื่อรันต่อ) อาจใช้เวลาสูงสุดหนึ่งชั่วโมง
assets
ข้อมูลต้องมีโครงสร้างเป็น JSON object ที่เก็บข้อมูลราคาปิดของสินทรัพย์ทางการเงินในวันที่ระบุ รูปแบบมีดังนี้
- Primary key (string): ชื่อหรือสัญลักษณ์ ticker ของสินทรัพย์ทางการเงิน (เช่น "8801.T")
- Secondary key (string): วันที่ในรูปแบบ YYYY-MM-DD
- Value (number): ราคาปิดของสินทรัพย์ในวันที่ระบุ สามารถป้อนราคาแบบ normalized หรือไม่ normalized ก็ได้
โปรดทราบว่า dictionary ทั้งหมดต้องมี secondary key (วันที่) เหมือนกัน หากสินทรัพย์บางตัวไม่มีวันที่ที่สินทรัพย์อื่นมี ต้องเติมข้อมูลให้ครบเพื่อความ สม่ำเสมอ เช่น สามารถใช้ราคาปิดล่าสุดที่บันทึกไว้ของสินทรัพย์นั้น
ตัวอย่าง
{
"8801.T": {
"2023-01-01": 2374.0,
"2023-01-02": 2374.0,
"2023-01-03": 2374.0,
"2023-01-04": 2356.5,
...
},
"AAPL": {
"2023-01-01": 145.2,
"2023-01-02": 146.5,
"2023-01-03": 147.3,
"2023-01-04": 148.1,
...
},
...
}
# Added by doQumentation — required packages for this notebook
!pip install -q pandas qiskit-ibm-catalog
{
"asset_name": {
"date": closing_value,
...
},
...
}
ข้อมูลสินทรัพย์ต้องมีราคาปิดอย่างน้อย (nt+1) * dt timestamps (เช่น วัน) (ดูส่วน qubo_settings)
qubo_settings
ตารางต่อไปนี้อธิบาย key ของ dictionary qubo_settings สร้าง dictionary โดยระบุจำนวน time steps nt, จำนวน resolution Qubit nq, และ max_investment หรือจะเปลี่ยนค่า default อื่น ๆ ก็ได้
| ชื่อ | ประเภท | คำอธิบา ย | จำเป็น | ค่าเริ่มต้น | ตัวอย่าง |
|---|---|---|---|---|---|
| nt | int | จำนวน time steps | ใช่ | - | 4 |
| nq | int | จำนวน resolution Qubit | ใช่ | - | 4 |
| max_investment | float | จำนวนสูงสุดของหน่วยเงินที่ลงทุนรวมทุกสินทรัพย์ | ใช่ | - | 10 |
| dt* | int | ช่วงเวลาที่พิจารณาในแต่ละ time step หน่วยตรงกับช่วงเวลาระหว่าง key ในข้อมูลสินทรัพย์ | ไม่ | 30 | - |
| risk_aversion | float | ค่าสัมประสิทธิ์ความเกลียดความเสี่ยง | ไม่ | 1000 | - |
| transaction_fee | float | ค่าสัมประสิทธิ์ค่าธรรมเนียมการซื้อขาย | ไม่ | 0.01 | - |
| restriction_coeff | float | Lagrange multiplier ที่ใช้บังคับเงื่อนไขของปัญหาใน QUBO formulation | ไม่ | 1 | - |
ansatz_settings
หากต้องการแก้ไข default options ให้สร้าง dictionary สำหรับพารามิเตอร์ ansatz_settings โดยมี key ดังต่อไปนี้ ค่าเริ่มต้นของ ansatz คือ "real_amplitudes" และทั้ง extra options (ดูตารางต่อไปนี้) ถูกตั้งเป็น False
| ชื่อ | ประเภท | คำอธิบาย | จำเป็น | ค่าเริ่มต้น |
|---|---|---|---|---|
| ansatz* | str | Ansatz ที่จะใช้ | ไม่ | "real_amplitudes" |
| multiple_passmanager** | bool | เปิดใช้งาน multiple passmanager subroutine (ไม่รองรับสำหรับ Tailored ansatz) | ไม่ | False |
| dd_enable | bool | เพิ่ม dynamical decoupling | ไม่ | False |
* Ansatz ที่รองรับ
real_amplitudescyclicoptimized_real_amplitudestailored(เฉพาะibm_torinoBackend, 7 สินทรัพย์, 4 time steps และ 4 resolution Qubit)
** ถ้า multiple_passmanager ถูกตั้งเป็น False ฟังก์ชันจะใช้ default Qiskit pass manager ที่มี optimization_level=3 ถ้าตั้งเป็น True subroutine multiple_passmanager จะเปรียบเทียบ pass manager สามตัว ได้แก่ default Qiskit pass manager เดิม, pass manager ที่ map Qubit ไปยัง QPU first neighbors chain และ AI transpiler services จากนั้นจะเลือก pass manager ที่มีค่า cumulative error ต่ำที่สุดโดยประมาณ
optimizer_settings
พารามิเตอร์นี้คือ dictionary ที่มี options ที่ปรับแต่งได้สำหรับกระบวนการ optimize
| ชื่อ | ประเภท | คำอธิบาย | จำเป็น | ค่าเริ่มต้น |
|---|---|---|---|---|
| primitive_options | json | การตั้งค่าของ primitive | ไม่ | - |
| optimizer | str | classical optimizer ที่เลือก | ไม่ | "differential_evolution" |
| optimizer_options | json | การกำหนดค่าของ optimizer | ไม่ | - |
ปัจจุบัน optimizer option ที่รองรับมีเพียง "differential_evolution" เท่านั้น
ภายใต้ key primitive_options และ optimizer_options เราตั้งค่า dictionary ที่มีพารามิเตอร์ดังต่อไปนี้
primitive_options
| ชื่อ | ประเภท | คำอธิบาย | จำเป็น | ค่าเริ่มต้น | ตัวอย่าง |
|---|---|---|---|---|---|
| sampler_shots | int | จำนวน shots ของ Sampler | ไม่ | 100000 | - |
| estimator_shots | int | จำนวน shots ของ Estimator | ไม่ | 25000 | - |
| estimator_precision | float | ความแม่นยำที่ต้องการของค่าคาดหวัง ถ้าระบุ จะใช้ precision แทน estimator_shots | ไม่ | None | 0.015625 · (1 / sqrt(4096)) |
| max_time | int or str | เวลาสูงสุดที่ runtime session จะเปิดอยู่ได้ก่อนถูกปิดโดยบังคับ สามารถกำหนดเป็นวินาที (int) หรือ string เช่น "2h 30m 40s" ต้องน้อยกว่าค่าสูงสุด ที่ระบบกำหนด | ไม่ | None | "1h 15m" |
optimizer_options
| ชื่อ | ประเภท | คำอธิบาย | จำเป็น | ค่าเริ่มต้น |
|---|---|---|---|---|
| num_generations | int | จำนวน generation | ไม่ | 20 |
| population_size | int | ขนาดของ population | ไม่ | 20 |
| mutation_range | list | ค่า mutation factor สูงสุดและต่ำสุด | ไม่ | [0, 0.25] |
| recombination | float | ค่า recombination factor | ไม่ | 0.4 |
| max_parallel_jobs | int | จำนวนสูงสุดของ QPU job ที่รันแบบ parallel | ไม่ | 3 |
| max_batchsize | int | ขนาด batch สูงสุด | ไม่ | 200 |
-
จำนวน generation ที่ประเมินโดย differential evolution คือ
num_generations+ 1 เนื่องจาก population เริ่มต้นถูกรวมด้วย -
จำนวน Circuit ทั้งหมดคำนวณเป็น
(num_generations + 1) * population_size -
การใช้ population size และจำนวน generation ที่มากขึ้นโดยทั่วไปจะปรับปรุงคุณภาพของผล optimization แต่ไม่แนะนำให้เกิน population size 120 และจำนวน generation มากกว่า 20 (เช่น
120 * 21 = 2520Circuit ทั้งหมด) เพราะจะสร้าง Circuit จำนวนมากเกินไป ซึ่งอาจใช้ทรัพยากรการคำนวณและเวลามาก -
ฟังก์ชันรองรับการรัน optimization ต่อจากครั้งก่อน และสามารถเพิ่มจำนวน generation ได้เสมอ (โดยใช้ input เดิมยกเว้น
previous_session_idและเพิ่มค่าnum_generations)
ตรวจสอบให้แน่ใจว่าเป็นไปตามข้อจำกัด Qiskit Runtime job
- Sampler:
sampler_shots <= 10_000_000 - Estimator:
max_batchsize * estimator_shots * observable_size <= 10_000_000(สำหรับฟังก์ชันนี้ ทุก term ของ observable commute กัน ดังนั้นobservable_size=1)
ดูคำแนะนำ Job limits สำหรับข้อมูลเพิ่มเติม
Output
ฟังก์ชันคืนค่า dictionary สองตัว ได้แก่ dictionary "result" ที่มีผลลัพธ์ optimization ที่ดีที่สุด รวมถึง optimal solution และ minimum objective cost ที่เกี่ยวข้อง และ "metadata" ที่มีข้อมูลจากผลลัพธ์ทั้งหมดที่ได้ในระหว่างกระบวนการ optimize พร้อมกับ metric ที่เกี่ยวข้อง
Dictionary แรกมุ่งเน้นที่ solution ที่ทำงานได้ดีที่สุด ขณะที่ dictionary ที่สองให้ข้อมูลรายละเอียดเกี่ยวกับ solution ทั้งหมด รวมถึง objective cost และ metric อื่น ๆ ที่เกี่ยวข้อง
Output dictionaries
| ชื่อ | ประเภท | คำอธิบาย | ตัวอย่าง |
|---|---|---|---|
| result | dict[str, dict[str, float]] | มีกลยุทธ์การลงทุนตามช่วงเวลา โดยแต่ละ timestamp จะ map ไปยังน้ำหนักการลงทุนเฉพาะสินทรัพย์ (น้ำหนักแต่ละตัวคือจำนวนเงินลงทุนที่ normalize ด้วยจำนวนเงินลงทุนรวม) | {'time_1': {'asset_1': 0.2, 'asset_2': 0.3, ...\}, ...\} |
| metadata | dict[str, Any] | ข้อมูลที่สร้างระหว่างการวิเคราะห์ รวมถึงผลลัพธ์ ต้นทุน และ metrics | ดูตัวอย่างด้านล่าง |
คำอธิบาย dictionary metadata
| ชื่อ | ประเภท | คำอธิบาย | ตัวอย่าง |
|---|---|---|---|
| session_id | str | ตัวระบุเฉพาะสำหรับ IBM Quantum Session | "d0h30qjvpqf00084fgw0" |
| all_samples_metrics | dict | Dictionary ที่มี metrics ต่าง ๆ สำหรับแต่ละ sample ที่ผ่าน postprocess เช่น ต้นทุนหรือ constraints | ดูคำอธิบายด้านล่าง |
| sampler_counts | dict[str, int] | Dictionary ที่ key เป็น bitstring ของผลลัพธ์ที่สุ่มได้และ value เป็นจำนวนครั้งที่พบ | {"101010": 3, "111000": 1\} |
| asset_order | list[str] | List ที่แสดงลำดับการลงทุนของสินทรัพย์ในแต่ละ time step ภายในกลยุทธ์การลงทุน | ["Asset_0", "Asset_1", "Asset_3"] |
| QUBO | list[list[float]] | QUBO matrix ของปัญหา | [[-6.96e-01, 5.81e-01, -1.26e-02, 0.00e+00], ...] |
| resource_summary | dict[str, dict[str, float]] | สรุปเวลาการใช้งาน CPU และ QPU (หน่วยวินาที) ในแต่ละขั้นตอนของกระบวนการ | {'RUNNING: EXECUTING_QPU': {'CPU_TIME': 412.84, 'QPU_TIME': 87.22\}, ...\} |
คำอธิบาย dictionary all_samples_metrics
| ชื่อ | ประเภท | คำอธิบาย | ตัวอย่าง |
|---|---|---|---|
| investment_trajectories | list[list] | กลยุทธ์การลงทุนที่ได้จากการ decode quantum states | [[1, 2, 2], [1, 2, 1]] |
| counts | list[int] | จำนวนครั้งที่แต่ละ investment trajectory ถูกสุ่ม index ตรงกับ investment_trajectories | [5, 3] |
| objective_costs | list[float] | ค่าของ objective function สำหรับแต่ละ investment trajectory เรียงจากต่ำสุดไปสูงสุด | [0.98, 1.25] |
| sharpe_ratios | list[float] | ประสิทธิภาพที่ปรับตามความเสี่ยง (Sharpe ratio) สำหรับแต่ละ investment trajectory จัดเรียงตาม index | [1.1, 0.7] |
| returns | list[float] | ผลตอบแทนที่คาดหวังสำหรับแต่ละ investment trajectory จัดเรียงตาม index | [0.15, 0.10] |
| rest_breaches | list[float] | การเบี่ยงเบนสูงสุดของ constraint ภายในแต่ละ investment trajectory จัดเรียงตาม index | [0.0, 0.25] |
| transaction_costs | list[float] | ต้นทุนธุรกรรมโดยประมาณของแต่ละ investment trajectory จัดเรียงตาม index | [0.01, 0.02] |
เริ่มต้นใช้งาน
ยืนยันตัวตนด้วย API key และเลือก Qiskit Function ดังนี้ (โค้ดนี้สมมติว่าคุณบันทึก account ไว้ในเครื่องแล้ว)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")