主頁 > 後端開發 > QuantLib 金融計算——C++ 代碼改寫成 Python 程式的一些經驗

QuantLib 金融計算——C++ 代碼改寫成 Python 程式的一些經驗

2020-09-20 15:50:15 後端開發

目錄
  • QuantLib 金融計算——C++ 代碼改寫成 Python 程式的一些經驗
    • 概述
    • 將 C++ 代碼改寫成 Python 程式
      • 對比結果
    • 總結

QuantLib 金融計算——C++ 代碼改寫成 Python 程式的一些經驗

概述

Python 在科學計算、資料分析和可視化等方面已經形成了非常強大的生態,而且,作為一門時尚的腳本語言,易學易用,因此,對于量化分析和風險管理的從業者來說,將某些 QuantLib 的歷史代碼轉換成 Python 程式是一件值得嘗試的作業,

Python 本身的面向物件機制非常完善,借助 SWIG 的包裝,由 C++ 代碼轉換而成的 Python 程式基本上可以完整地保留原本的類架構,對于用戶來說,應用層面的歷史代碼幾乎可以平行的進行移植,只需稍加修改即可,

本文將以 QuantLib 官方網站上的 EquityOption.cpp 為例,展示如何將應用層面的 C++ 代碼轉換成 Python 程式,并總結出一般的轉換方法和注意事項,

將 C++ 代碼改寫成 Python 程式

下面,我將逐句把 C++ 代碼改寫成 Python 程式,

C++ 代碼:

#include <ql/qldefines.hpp>
#ifdef BOOST_MSVC
#  include <ql/auto_link.hpp>
#endif
#include <ql/instruments/vanillaoption.hpp>
#include <ql/pricingengines/vanilla/binomialengine.hpp>
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
#include <ql/pricingengines/vanilla/baroneadesiwhaleyengine.hpp>
#include <ql/pricingengines/vanilla/bjerksundstenslandengine.hpp>
#include <ql/pricingengines/vanilla/batesengine.hpp>
#include <ql/pricingengines/vanilla/integralengine.hpp>
#include <ql/pricingengines/vanilla/fdblackscholesvanillaengine.hpp>
#include <ql/pricingengines/vanilla/mceuropeanengine.hpp>
#include <ql/pricingengines/vanilla/mcamericanengine.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/utilities/dataformatters.hpp>

#include <iostream>
#include <iomanip>
 
using namespace QuantLib;
 
#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {
    Integer sessionId() { return 0; }
}
#endif

Python 代碼:

import QuantLib as ql
import prettytable as pt

首先,引入必要的模塊,對 C++ 來說是一組頭檔案,Python 的優勢顯而易見,


C++ 代碼:

// set up dates
Calendar calendar = TARGET();
Date todaysDate(15, May, 1998);
Date settlementDate(17, May, 1998);
Settings::instance().evaluationDate() = todaysDate;

Python 代碼:

# set up dates
calendar = ql.TARGET()
todaysDate = ql.Date(15, ql.May, 1998)
settlementDate = ql.Date(17, ql.May, 1998)
ql.Settings.instance().evaluationDate = todaysDate

C++ 中物件的宣告有兩種常見的方式:

  1. BaseClass object = Class(...),其中 Class 可以是 BaseClass 本身,或者其派生類,示例中的 TARGET 正是 Calendar 的派生類;
  2. Class object(...)

Python 中無需宣告物件型別,而是以賦值的形式創建一個物件,所以對于上述兩類格式的代碼,統一改寫成 object = Class(...)

經驗 1:物件宣告陳述句 BaseClass object = Class(...)Class object(...) 統一改寫成 object = Class(...)

Settings 是 QuantLib 中的一個“單體模式”的實作,通常用來為整個程式設定統一的估值日期,幾乎每個應用程式中都會出現,通過呼叫 Settings 的靜態方法 instance(),用戶可以修改單體實體的某些屬性,其中 evaluationDate() 方法可以把存盤估值日期的成員變數地址暴露出來,讓用戶進行設定,

不過,Python 中的類沒有 :: 運算子,類的方法也不能暴露成員變數的地址,所以,原本的靜態方法一律通過 . 運算子呼叫,同時 evaluationDate() 方法被重定義為類的 property,這就是為什么 Python 陳述句中 evaluationDate 后面沒有 (),注意,instance() 后面的 () 不能丟,

經驗 2:用來對 Settings::instance() 進行配置的成員函式,例如 evaluationDate(),在 Python 中以類的 property 形式出現,不過名稱不變,


C++ 代碼:

// our options
Option::Type type(Option::Put);
Real underlying = 36;
Real strike = 40;
Spread dividendYield = 0.00;
Rate riskFreeRate = 0.06;
Volatility volatility = 0.20;
Date maturity(17, May, 1999);
DayCounter dayCounter = Actual365Fixed();

Python 代碼:

# our options
optType = ql.Option.Put
underlying = 36.0
strike = 40.0
dividendYield = 0.00
riskFreeRate = 0.06
volatility = 0.20
maturity = ql.Date(17, ql.May, 1999)
dayCounter = ql.Actual365Fixed()

C++ 中類內部列舉型別的物件宣告和類物件宣告相似,采用 Class::Enum object(Class::element) 的形式,列舉元素本質上是一些整數常量,

SWIG 在包裝 QuantLib 的 Python 介面時會把 C++ 類內部的列舉型別轉換成 Python 類中的公有屬性,其值依然是一些整數值,所以,列舉型別物件的宣告就直接改寫成賦值陳述句,因此,Class::Enum object(Class::element) 陳述句統一改寫成 object = Class.element

示例中的 TypeOption 類內部的一個列舉型,而 PutType 中的一個元素,另一個是 Call,因為 type 是 Python 的關鍵字,改寫時一定要重命名,

經驗 3:對于類中的列舉型別,Class::Enum object(Class::element) 陳述句統一改寫成 object = Class.element

對于基本型別(整數、浮點數、字符、字串)來說,改寫非常容易,由于 Python 無需宣告型別,Type object = value 陳述句統一改寫成賦值陳述句——object = value

經驗 4:對于基本型別,Type object = value 陳述句統一改寫成 object = value


C++ 代碼:

std::cout << "Option type = " << type << std::endl;
std::cout << "Maturity = " << maturity << std::endl;
std::cout << "Underlying price = " << underlying << std::endl;
std::cout << "Strike = " << strike << std::endl;
std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl;
std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl;
std::cout << "Volatility = " << io::volatility(volatility) << std::endl;
std::cout << std::endl;
std::string method;
std::cout << std::endl ;

// write column headings
Size widths[] = { 35, 14, 14, 14 };
std::cout << std::setw(widths[0]) << std::left << "Method"
          << std::setw(widths[1]) << std::left << "European"
          << std::setw(widths[2]) << std::left << "Bermudan"
          << std::setw(widths[3]) << std::left << "American"
          << std::endl;

Python 代碼:

print('Option type =', optType)
print('Maturity =', maturity)
print('Underlying price =', underlying)
print('Strike =', strike)
print('Risk-free interest rate =', '{0:%}'.format(riskFreeRate))
print('Dividend yield =', '{0:%}'.format(dividendYield))
print('Volatility =', '{0:%}'.format(volatility))
print()

# show table

tab = pt.PrettyTable(['Method', 'European', 'Bermudan', 'American'])

字串輸出部分沒什么好說的,我使用了 prettytable 包來美化輸出結果,


C++ 代碼:

std::vector<Date> exerciseDates;
for (Integer i = 1; i <= 4; i++)
    exerciseDates.push_back(settlementDate + 3 * i * Months);

Python 代碼:

exerciseDates = ql.DateVector()
for i in range(1, 5):
    exerciseDates.push_back(settlementDate + ql.Period(3 * i, ql.Months))

Python 本身沒有“模板”的概念,因此 SWIG 只能對模板的實體化進行包裝(模板的實體化就是一個具體的類),進而得到一些 Python 類,對于某些常用型別,例如 Date,QuantLib 的 Python 介面包裝了對應的 std::vector 模板的實體化,包裝后得到的 Python 類有一致的命名格式——ClassVector,對于 std::vector<Date> 而言就是 DateVector

因為模板的實體化實際上就是一個具體的類,因此,這部分代碼的改寫方法遵循經驗 1

和 C++ 完全不同,Python 不是一個“強型別”的語言,在改寫涉及隱式轉換的代碼時要格外注意,Months 是 QuantLib 中的列舉型別 TimeUnit 的元素,SWIG 在包裝列舉型別時會將元素轉換成 Python 中的整數,丟失了 TimeUnit 的型別資訊,由于 Python 不是強型別的,被包裝的列舉型別會丟失型別資訊,因此,3 * i * Months 在 C++ 中可以順利地隱式轉換成一個 Period 物件——Period(3 * i, Months),但是,在 Python 中 3 * i * Months 只會被當做三個整數相乘,此時,3 * i * Months 必須改寫成顯式宣告的格式——ql.Period(3 * i, ql.Months)

經驗 5:隱式轉換成 Period 物件的代碼在改寫時要改成顯式宣告的格式,這類代碼通常與列舉型別 TimeUnit 有關,


C++ 代碼:

ext::shared_ptr<Exercise> europeanExercise(
    new EuropeanExercise(maturity));

ext::shared_ptr<Exercise> bermudanExercise(
    new BermudanExercise(exerciseDates));

ext::shared_ptr<Exercise> americanExercise(
    new AmericanExercise(settlementDate, maturity));

Python 代碼:

europeanExercise = ql.EuropeanExercise(maturity)
bermudanExercise = ql.BermudanExercise(exerciseDates)
americanExercise = ql.AmericanExercise(settlementDate, maturity)

C++ 中宣告智能指標的最常見方式是:shared_ptr<BaseClass> object(new Class(...))shared_ptr 也是最常用的智能指標類模板),其中 Class 可以是 BaseClass 本身,或者其派生類,示例中的 EuropeanExercise 正是 Exercise 的派生類,這類代碼在 Python 中統一改寫成宣告物件的形式——object = Class(...),因為智能指標通常被視為一個物件,

經驗 6:對于智能指標,shared_ptr<BaseClass> object(new Class(...)) 統一改寫成 object = Class(...)


C++ 代碼:

Handle<Quote> underlyingH(
    ext::shared_ptr<Quote>(new SimpleQuote(underlying)));

Python 代碼:

underlyingH = ql.QuoteHandle(ql.SimpleQuote(underlying))

Quote 類和 Handle 模板是 QuantLib 中最常用到的兩個類(模板),它們通常充當“觀察者模式”中被觀察的一方,一般被當做引數來配置更復雜類的實體,Quote 類接受一個浮點數做引數,而 Handle 模板接受一個智能指標,當用戶修改 Quote 實體的值,或 Handle 實體指向的指標之后,那些接受過這些實體的復雜類物件會接到通知,并自動觸發相關計算,這個機制非常贊!

關于 Quote 的具體使用案例,詳情可以參考《Quote 帶來的便利》,

QuantLib 的 Python 介面已經包裝了 Handle 模板的一些實體化,例如 QuoteHandle 和下面將要看到的 YieldTermStructureHandle,這些類有一致的命名格式——ClassHandle

還是那句話,C++ 模板的實體化實際上就是一個具體的類,因此,這部分代碼的改寫方法遵循經驗 1經驗 6


C++ 代碼:

// bootstrap the yield/dividend/vol curves
Handle<YieldTermStructure> flatTermStructure(
    ext::shared_ptr<YieldTermStructure>(
        new FlatForward(settlementDate, riskFreeRate, dayCounter)));
Handle<YieldTermStructure> flatDividendTS(
    ext::shared_ptr<YieldTermStructure>(
        new FlatForward(settlementDate, dividendYield, dayCounter)));
Handle<BlackVolTermStructure> flatVolTS(
    ext::shared_ptr<BlackVolTermStructure>(
        new BlackConstantVol(
            settlementDate, calendar, volatility, dayCounter)));
ext::shared_ptr<StrikedTypePayoff> payoff(
    new PlainVanillaPayoff(type, strike));
ext::shared_ptr<BlackScholesMertonProcess> bsmProcess(
    new BlackScholesMertonProcess(
        underlyingH, flatDividendTS, flatTermStructure, flatVolTS));

// options
VanillaOption europeanOption(payoff, europeanExercise);
VanillaOption bermudanOption(payoff, bermudanExercise);
VanillaOption americanOption(payoff, americanExercise);

// Analytic formulas:

// Black-Scholes for European
method = "Black-Scholes";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new AnalyticEuropeanEngine(bsmProcess)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;

// semi-analytic Heston for European
method = "Heston semi-analytic";
ext::shared_ptr<HestonProcess> hestonProcess(
    new HestonProcess(
        flatTermStructure, flatDividendTS, underlyingH,
        volatility * volatility, 1.0, volatility * volatility, 0.001, 0.0));
ext::shared_ptr<HestonModel> hestonModel(
    new HestonModel(hestonProcess));
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new AnalyticHestonEngine(hestonModel)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;

// semi-analytic Bates for European
method = "Bates semi-analytic";
ext::shared_ptr<BatesProcess> batesProcess(
    new BatesProcess(
        flatTermStructure, flatDividendTS, underlyingH,
        volatility * volatility, 1.0, volatility * volatility,
        0.001, 0.0, 1e-14, 1e-14, 1e-14));
ext::shared_ptr<BatesModel> batesModel(
    new BatesModel(batesProcess));
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(new BatesEngine(batesModel)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;
          
// Barone-Adesi and Whaley approximation for American
method = "Barone-Adesi/Whaley";
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BaroneAdesiWhaleyApproximationEngine(bsmProcess)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << "N/A"
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Bjerksund and Stensland approximation for American
method = "Bjerksund/Stensland";
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BjerksundStenslandApproximationEngine(bsmProcess)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << "N/A"
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Integral
method = "Integral";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new IntegralEngine(bsmProcess)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;

// Finite differences
Size timeSteps = 801;
method = "Finite differences";
ext::shared_ptr<PricingEngine> fdengine =
    ext::make_shared<FdBlackScholesVanillaEngine>(
        bsmProcess, timeSteps, timeSteps - 1);
europeanOption.setPricingEngine(fdengine);
bermudanOption.setPricingEngine(fdengine);
americanOption.setPricingEngine(fdengine);
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

Python 代碼:

# bootstrap the yield/dividend/vol curves
flatTermStructure = ql.YieldTermStructureHandle(
    ql.FlatForward(settlementDate, riskFreeRate, dayCounter))
flatDividendTS = ql.YieldTermStructureHandle(
    ql.FlatForward(settlementDate, dividendYield, dayCounter))
flatVolTS = ql.BlackVolTermStructureHandle(
    ql.BlackConstantVol(
        settlementDate, calendar, volatility, dayCounter))
payoff = ql.PlainVanillaPayoff(optType, strike)
bsmProcess = ql.BlackScholesMertonProcess(
    underlyingH, flatDividendTS, flatTermStructure, flatVolTS)

# options
europeanOption = ql.VanillaOption(payoff, europeanExercise)
bermudanOption = ql.VanillaOption(payoff, bermudanExercise)
americanOption = ql.VanillaOption(payoff, americanExercise)

# Analytic formulas:

# Black-Scholes for European
method = 'Black-Scholes'
europeanOption.setPricingEngine(
    ql.AnalyticEuropeanEngine(bsmProcess))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# semi-analytic Heston for European
method = 'Heston semi-analytic'
hestonProcess = ql.HestonProcess(
    flatTermStructure, flatDividendTS, underlyingH,
    volatility * volatility, 1.0, volatility * volatility, 0.001, 0.0)
hestonModel = ql.HestonModel(hestonProcess)
europeanOption.setPricingEngine(
    ql.AnalyticHestonEngine(hestonModel))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# semi-analytic Bates for European
method = 'Bates semi-analytic'
batesProcess = ql.BatesProcess(
    flatTermStructure, flatDividendTS, underlyingH,
    volatility * volatility, 1.0, volatility * volatility,
    0.001, 0.0, 1e-14, 1e-14, 1e-14)
batesModel = ql.BatesModel(batesProcess)
europeanOption.setPricingEngine(
    ql.BatesEngine(batesModel))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Barone-Adesi and Whaley approximation for American
method = 'Barone-Adesi/Whaley'
americanOption.setPricingEngine(
    ql.BaroneAdesiWhaleyEngine(bsmProcess))
tab.add_row([method, 'N/A', 'N/A', americanOption.NPV()])

# Bjerksund and Stensland approximation for American
method = 'Bjerksund/Stensland'
americanOption.setPricingEngine(
    ql.BjerksundStenslandEngine(bsmProcess))
tab.add_row([method, 'N/A', 'N/A', americanOption.NPV()])

# Integral
method = 'Integral'
europeanOption.setPricingEngine(
    ql.IntegralEngine(bsmProcess))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Finite differences
timeSteps = 801
method = 'Finite differences'
fdengine = ql.FdBlackScholesVanillaEngine(bsmProcess, timeSteps, timeSteps - 1)
europeanOption.setPricingEngine(fdengine)
bermudanOption.setPricingEngine(fdengine)
americanOption.setPricingEngine(fdengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

這部分代碼的改寫沒什么新意,需要注意的是,某些非模板類在被包裝時會被重命名,例如 BaroneAdesiWhaleyApproximationEngine 被重命名為 BaroneAdesiWhaleyEngine,如果用戶根據前面的 6 條經驗找不到 Python 介面中的對應物,那么,要改寫的 C++ 代碼可能遇到了重命名的情況,這時,用戶需要到 QuantLib-SWIG 的介面檔案中查找 C++ 類(結構體)或函式,看看有沒有被重命名,繼續前面的例子,SWIG 代碼 %rename(BaroneAdesiWhaleyEngine) BaroneAdesiWhaleyApproximationEngine; 表明 BaroneAdesiWhaleyApproximationEngine 被重命名為 BaroneAdesiWhaleyEngine

經驗 7:疑似遇到重命名的情況(常見于名字特別長的類),到 QuantLib-SWIG 的介面檔案中查找重命名命令,


C++ 代碼:

// Binomial method: Jarrow-Rudd
method = "Binomial Jarrow-Rudd";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Cox-Ross-Rubinstein
method = "Binomial Cox-Ross-Rubinstein";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Additive equiprobabilities
method = "Additive equiprobabilities";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<AdditiveEQPBinomialTree>(
            bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<AdditiveEQPBinomialTree>(
            bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<AdditiveEQPBinomialTree>(
            bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Binomial Trigeorgis
method = "Binomial Trigeorgis";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Binomial Tian
method = "Binomial Tian";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Binomial Leisen-Reimer
method = "Binomial Leisen-Reimer";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

// Binomial method: Binomial Joshi
method = "Binomial Joshi";
europeanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps)));
bermudanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps)));
americanOption.setPricingEngine(
    ext::shared_ptr<PricingEngine>(
        new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps)));
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << bermudanOption.NPV()
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

Python 代碼:

# Binomial method: Jarrow-Rudd
method = 'Binomial Jarrow-Rudd'
jrengine = ql.BinomialJRVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(jrengine)
bermudanOption.setPricingEngine(jrengine)
americanOption.setPricingEngine(jrengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Cox-Ross-Rubinstein
method = 'Binomial Cox-Ross-Rubinstein'
crrengine = ql.BinomialCRRVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(crrengine)
bermudanOption.setPricingEngine(crrengine)
americanOption.setPricingEngine(crrengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Additive equiprobabilities
method = 'Additive equiprobabilities'
eqpengine = ql.BinomialEQPVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(eqpengine)
bermudanOption.setPricingEngine(eqpengine)
americanOption.setPricingEngine(eqpengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Binomial Trigeorgis
method = 'Binomial Trigeorgis'
trengine = ql.BinomialTrigeorgisVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(trengine)
bermudanOption.setPricingEngine(trengine)
americanOption.setPricingEngine(trengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Binomial Tian
method = 'Binomial Tian'
tiengine = ql.BinomialTianVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(tiengine)
bermudanOption.setPricingEngine(tiengine)
americanOption.setPricingEngine(tiengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Binomial Leisen-Reimer
method = 'Binomial Leisen-Reimer'
lrengine = ql.BinomialLRVanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(lrengine)
bermudanOption.setPricingEngine(lrengine)
americanOption.setPricingEngine(lrengine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

# Binomial method: Binomial Joshi
method = 'Binomial Joshi'
j4engine = ql.BinomialJ4VanillaEngine(bsmProcess, timeSteps)
europeanOption.setPricingEngine(j4engine)
bermudanOption.setPricingEngine(j4engine)
americanOption.setPricingEngine(j4engine)
tab.add_row([method, europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()])

對于 C++ 中的模板,SWIG 在包裝 Python 介面時只包裝模板的實體化,并且會為模板的實體化取一個新名字,這時,用戶需要到 QuantLib-SWIG 的介面檔案中查找模板的實體化,看看取了什么新名字,繼續前面的例子,SWIG 代碼 %template(BinomialJRVanillaEngine) BinomialVanillaEngine<JarrowRudd>; 表示 BinomialVanillaEngine<JarrowRudd> 在 Python 中對應的類叫做 BinomialJRVanillaEngine

經驗 8:遇到模板實體化的情況,到 QuantLib-SWIG 的介面檔案中查找實體化后新的類名,


C++ 代碼:

// Monte Carlo Method: MC (crude)
timeSteps = 1;
method = "MC (crude)";
Size mcSeed = 42;
ext::shared_ptr<PricingEngine> mcengine1;
mcengine1 = MakeMCEuropeanEngine<PseudoRandom>(
                bsmProcess)
                .withSteps(timeSteps)
                .withAbsoluteTolerance(0.02)
                .withSeed(mcSeed);
europeanOption.setPricingEngine(mcengine1);
// Real errorEstimate = europeanOption.errorEstimate();
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;

// Monte Carlo Method: QMC (Sobol)
method = "QMC (Sobol)";
Size nSamples = 32768;    // 2^15
ext::shared_ptr<PricingEngine> mcengine2;
mcengine2 = MakeMCEuropeanEngine<LowDiscrepancy>(
                bsmProcess)
                .withSteps(timeSteps)
                .withSamples(nSamples);
europeanOption.setPricingEngine(mcengine2);
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << europeanOption.NPV()
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << "N/A"
          << std::endl;

// Monte Carlo Method: MC (Longstaff Schwartz)
method = "MC (Longstaff Schwartz)";
ext::shared_ptr<PricingEngine> mcengine3;
mcengine3 = MakeMCAmericanEngine<PseudoRandom>(
                bsmProcess)
                .withSteps(100)
                .withAntitheticVariate()
                .withCalibrationSamples(4096)
                .withAbsoluteTolerance(0.02)
                .withSeed(mcSeed);
americanOption.setPricingEngine(mcengine3);
std::cout << std::setw(widths[0]) << std::left << method
          << std::fixed
          << std::setw(widths[1]) << std::left << "N/A"
          << std::setw(widths[2]) << std::left << "N/A"
          << std::setw(widths[3]) << std::left << americanOption.NPV()
          << std::endl;

Python 代碼:

timeSteps = 1
# Monte Carlo Method: MC (crude)
method = 'MC (crude)'
mcSeed = 42
mcengine1 = ql.MCPREuropeanEngine(
    bsmProcess,
    timeSteps=timeSteps,
    requiredTolerance=0.02,
    seed=mcSeed)

europeanOption.setPricingEngine(mcengine1)
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Monte Carlo Method: QMC (Sobol)
method = 'QMC (Sobol)'
nSamples = 32768  # 2^15
mcengine2 = ql.MCLDEuropeanEngine(
    bsmProcess,
    timeSteps=timeSteps,
    requiredSamples=nSamples)
europeanOption.setPricingEngine(mcengine2)
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Monte Carlo Method: MC (Longstaff Schwartz)
method = 'MC (Longstaff Schwartz)'
mcengine3 = ql.MCPRAmericanEngine(
    bsmProcess,
    timeSteps=100,
    antitheticVariate=True,
    nCalibrationSamples=4096,
    requiredTolerance=0.02,
    seed=mcSeed)
americanOption.setPricingEngine(mcengine3)
tab.add_row([method, 'N/A', 'N/A', americanOption.NPV()])

tab.float_format = '.6'
tab.align = 'l'
print(tab)

MakeMCEuropeanEngine<PseudoRandom> 是 QuantLib 中工廠模式的一個實作,對于擁有較多默認引數的類,QuantLib 會提供一個對應的工廠類,用戶借助工廠類“制造”一個半成品物件,并通過一組成員函式以流水線的方式配置這個半成品的引數,以實作對默認引數的靈活配置,這些流水線函式有一致的命名格式——withArgumentArgument 通常是某個默認引數的名字,這套機制也被稱為“命名引數慣用法”,這些工廠類有一致的命名規范——MakeClass,其中 Class 是一個類的名字或實體化的模板,MakeClass 將制造出一個 Class 物件,

Python 中存在“關鍵字引數”的機制,因此,上述“流水線函式”顯得非常笨拙,對于這類代碼的改寫,用戶只要知道“MakeClass 將制造出一個 Class 物件”這一點,并理解流水線函式所配置的引數,然后應用前面總結的 8 條經驗就可以成功改寫,

經驗 9:名為 MakeClass 的工廠類將制造出一個 Class 物件,后續的成員函式表示配置的引數,

對比結果

C++ 代碼運行結果:

Option type = Put
Maturity = May 17th, 1999
Underlying price = 36
Strike = 40
Risk-free interest rate = 6.000000 %
Dividend yield = 0.000000 %
Volatility = 20.000000 %


Method                             European      Bermudan      American      
Black-Scholes                      3.844308      N/A           N/A           
Heston semi-analytic               3.844306      N/A           N/A           
Bates semi-analytic                3.844306      N/A           N/A           
Barone-Adesi/Whaley                N/A           N/A           4.459628      
Bjerksund/Stensland                N/A           N/A           4.453064      
Integral                           3.844309      N/A           N/A           
Finite differences                 3.844330      4.360765      4.486113      
Binomial Jarrow-Rudd               3.844132      4.361174      4.486552      
Binomial Cox-Ross-Rubinstein       3.843504      4.360861      4.486415      
Additive equiprobabilities         3.836911      4.354455      4.480097      
Binomial Trigeorgis                3.843557      4.360909      4.486461      
Binomial Tian                      3.844171      4.361176      4.486413      
Binomial Leisen-Reimer             3.844308      4.360713      4.486076      
Binomial Joshi                     3.844308      4.360713      4.486076      
MC (crude)                         3.834522      N/A           N/A           
QMC (Sobol)                        3.844613      N/A           N/A           
MC (Longstaff Schwartz)            N/A           N/A           4.456935      

Python 程式運行結果:

Option type = -1
Maturity = May 17th, 1999
Underlying price = 36.0
Strike = 40.0
Risk-free interest rate = 6.000000%
Dividend yield = 0.000000%
Volatility = 20.000000%

+------------------------------+----------+----------+----------+
| Method                       | European | Bermudan | American |
+------------------------------+----------+----------+----------+
| Black-Scholes                | 3.844308 | N/A      | N/A      |
| Heston semi-analytic         | 3.844306 | N/A      | N/A      |
| Bates semi-analytic          | 3.844306 | N/A      | N/A      |
| Barone-Adesi/Whaley          | N/A      | N/A      | 4.459628 |
| Bjerksund/Stensland          | N/A      | N/A      | 4.453064 |
| Integral                     | 3.844309 | N/A      | N/A      |
| Finite differences           | 3.844330 | 4.360765 | 4.486113 |
| Binomial Jarrow-Rudd         | 3.844132 | 4.361174 | 4.486552 |
| Binomial Cox-Ross-Rubinstein | 3.843504 | 4.360861 | 4.486415 |
| Additive equiprobabilities   | 3.836911 | 4.354455 | 4.480097 |
| Binomial Trigeorgis          | 3.843557 | 4.360909 | 4.486461 |
| Binomial Tian                | 3.844171 | 4.361176 | 4.486413 |
| Binomial Leisen-Reimer       | 3.844308 | 4.360713 | 4.486076 |
| Binomial Joshi               | 3.844308 | 4.360713 | 4.486076 |
| MC (crude)                   | 3.834522 | N/A      | N/A      |
| QMC (Sobol)                  | 3.844613 | N/A      | N/A      |
| MC (Longstaff Schwartz)      | N/A      | N/A      | 4.456935 |
+------------------------------+----------+----------+----------+

完全一樣!

總結

  • 經驗 1:物件宣告陳述句 BaseClass object = Class(...)Class object(...) 統一改寫成 object = Class(...)
  • 經驗 2:用來對 Settings::instance() 進行配置的成員函式,例如 evaluationDate(),在 Python 中以類的 property 形式出現,不過名稱不變,
  • 經驗 3:對于類中的列舉型別,Class::Enum object(Class::element) 陳述句統一改寫成 object = Class.element
  • 經驗 4:對于基本型別,Type object = value 陳述句統一改寫成 object = value
  • 經驗 5:隱式轉換成 Period 物件的代碼在改寫時要改成顯式宣告的格式,這類代碼通常與列舉型別 TimeUnit 有關,
  • 經驗 6:對于智能指標,shared_ptr<BaseClass> object(new Class(...)) 統一改寫成 object = Class(...)
  • 經驗 7:疑似遇到重命名的情況(常見于名字特別長的類),到 QuantLib-SWIG 的介面檔案中查找重命名命令,
  • 經驗 8:遇到模板實體化的情況,到 QuantLib-SWIG 的介面檔案中查找實體化后新的類名,
  • 經驗 9:名為 MakeClass 的工廠類將制造出一個 Class 物件,后續的成員函式表示配置的引數,

需要注意的是,QuantLib 中并非所有的功能都有對應的 Python 介面,如果用戶需要的功能未被包裝,用戶只好修改 SWIG 代碼,自行生成 Python 介面,可以參考一下文章:

  • 《自己動手封裝 Python 介面(1)》
  • 《自己動手封裝 Python 介面(2)》
  • 《自己動手封裝 Python 介面(3)》

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/89933.html

標籤:Python

上一篇:可變不可變型別,數字型別及其常用操作,字串型別及其常用操作

下一篇:Django學習筆記:第一天

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more