ParameterBase.Clone() 保留运行时类型深拷贝; ValueCalculator 两个薄接入方法; GreeksBumpCalculator/GreeksRiskFactor 引擎。加法性重定价桥,不动现有定价输出。
128 lines
5.5 KiB
C#
128 lines
5.5 KiB
C#
using System.Collections.Generic;
|
||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using YLErp.BLL.Calculation.V2;
|
||
using YLErp.BLL.Calculation.V2.Parameter;
|
||
|
||
namespace UnitTestProject.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// RiskFactor(② 风险因子抽象)+ ParameterBase.Clone 的纯单测(Layer A,无 DB / QDP)。
|
||
/// 用假 reprice 委托验证:克隆类型保持、字典深拷、三类因子落点正确、波动率因子施于非期权参数抛异常,
|
||
/// 以及经 BuildPvFunction 喂入 GreeksBumpCalculator 后 DeltaR / Delta / Vega / BumpPv1Bp 数值正确(线性函数精确)。
|
||
/// </summary>
|
||
[TestClass]
|
||
public class GreeksRiskFactorTests
|
||
{
|
||
// —— Clone 行为与类型保持 ——
|
||
|
||
[TestMethod]
|
||
public void Clone_PreservesRuntimeType_And_CopiesOptionFields()
|
||
{
|
||
var src = new VanillaOptionParameter
|
||
{
|
||
Volatility = 0.2,
|
||
RiskFreeRate = 0.03,
|
||
SpotPrices = new Dictionary<string, double> { { "X", 100 } }
|
||
};
|
||
ParameterBase clone = src.Clone();
|
||
// MemberwiseClone 必须保留运行时类型,否则 ValueCalculator 内 parameter as VanillaOptionParameter 会 cast 成 null
|
||
Assert.IsInstanceOfType(clone, typeof(VanillaOptionParameter));
|
||
Assert.AreEqual(0.2, ((BaseOptionParameter)clone).Volatility);
|
||
Assert.AreEqual(100, clone.SpotPrices["X"]);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void Clone_DeepCopiesSpotPrices_So_Bump_Does_Not_Pollute_Original()
|
||
{
|
||
var src = new ParameterBase
|
||
{
|
||
SpotPrices = new Dictionary<string, double> { { "X", 100 } }
|
||
};
|
||
ParameterBase clone = src.Clone();
|
||
clone.SpotPrices["X"] = 999; // 改克隆体
|
||
Assert.AreEqual(100, src.SpotPrices["X"], "原参数的 SpotPrices 不应被克隆体的 bump 污染");
|
||
}
|
||
|
||
// —— 三类因子 ApplyTo 落点正确 ——
|
||
|
||
[TestMethod]
|
||
public void RateFactor_ApplyTo_Sets_RiskFreeRate()
|
||
{
|
||
var p = new ParameterBase();
|
||
RiskFactor.Rate("CNY-OIS-2Y").ApplyTo(p, 0.025m);
|
||
Assert.AreEqual(0.025, p.RiskFreeRate);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void PriceFactor_ApplyTo_Sets_SpotPrices_By_TargetKey()
|
||
{
|
||
var p = new ParameterBase();
|
||
RiskFactor.Price("000300.SH").ApplyTo(p, 3500m);
|
||
Assert.AreEqual(3500, p.SpotPrices["000300.SH"]);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void VolFactor_ApplyTo_On_OptionParameter_Sets_Volatility()
|
||
{
|
||
var p = new BaseOptionParameter();
|
||
RiskFactor.Volatility("X").ApplyTo(p, 0.18m);
|
||
Assert.AreEqual(0.18, p.Volatility);
|
||
}
|
||
|
||
[TestMethod]
|
||
[ExpectedException(typeof(System.InvalidOperationException))]
|
||
public void VolFactor_ApplyTo_On_PlainParameter_Throws()
|
||
{
|
||
// 波动率不在 ParameterBase 基类上,只能施于期权参数
|
||
RiskFactor.Volatility("X").ApplyTo(new ParameterBase(), 0.1m);
|
||
}
|
||
|
||
// —— 端到端:假 reprice 验证 中心差分 / 1bp 数值正确(线性函数精确) ——
|
||
|
||
[TestMethod]
|
||
public void RateFactor_Through_Engine_DeltaR_Equals_Slope()
|
||
{
|
||
var baseParam = new ParameterBase { RiskFreeRate = 0.03 };
|
||
Func<ParameterBase, decimal> reprice = p => (decimal)((p.RiskFreeRate ?? 0) * 1000); // PV = 1000 * r
|
||
var factor = RiskFactor.Rate("r"); // 标准步长:绝对 1bp
|
||
Func<decimal, decimal> pv = factor.BuildPvFunction(reprice, baseParam);
|
||
|
||
var calc = new GreeksBumpCalculator();
|
||
decimal deltaR = calc.DeltaR(pv, 0.03m, factor.Shift); // 中心差分对线性函数精确
|
||
Assert.AreEqual(1000m, deltaR, 1e-4m);
|
||
|
||
decimal bump1bp = calc.BumpPv1Bp(pv, 0.03m); // 前向 1bp PV 差
|
||
Assert.AreEqual(1000m * 0.0001m, bump1bp, 1e-9m);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void PriceFactor_Through_Engine_Delta_Equals_One()
|
||
{
|
||
var baseParam = new ParameterBase
|
||
{
|
||
SpotPrices = new Dictionary<string, double> { { "X", 100 } }
|
||
};
|
||
Func<ParameterBase, decimal> reprice = p => (decimal)p.SpotPrices["X"]; // PV = S
|
||
var factor = RiskFactor.Price("X"); // 标准步长:相对 1% → ε = 1
|
||
Func<decimal, decimal> pv = factor.BuildPvFunction(reprice, baseParam);
|
||
|
||
var calc = new GreeksBumpCalculator();
|
||
decimal delta = calc.Delta(pv, 100m, factor.Shift); // (101 - 99) / 2 = 1 精确
|
||
Assert.AreEqual(1m, delta, 1e-6m);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void VolFactor_Through_Engine_Vega_Equals_Slope()
|
||
{
|
||
var baseParam = new BaseOptionParameter { Volatility = 0.2 };
|
||
Func<ParameterBase, decimal> reprice = p => (decimal)(((BaseOptionParameter)p).Volatility ?? 0) * 50; // PV = 50 * σ
|
||
var factor = RiskFactor.Volatility("X"); // 标准步长:绝对 1bp vol
|
||
Func<decimal, decimal> pv = factor.BuildPvFunction(reprice, baseParam);
|
||
|
||
var calc = new GreeksBumpCalculator();
|
||
decimal vega = calc.Vega(pv, 0.2m, factor.Shift); // 中心差分对线性函数精确
|
||
Assert.AreEqual(50m, vega, 1e-4m);
|
||
}
|
||
}
|
||
}
|