using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Calculation.V2.Parameter;
namespace UnitTestProject.Modules.SwapModule
{
///
/// RiskFactor(② 风险因子抽象)+ ParameterBase.Clone 的纯单测(Layer A,无 DB / QDP)。
/// 用假 reprice 委托验证:克隆类型保持、字典深拷、三类因子落点正确、波动率因子施于非期权参数抛异常,
/// 以及经 BuildPvFunction 喂入 GreeksBumpCalculator 后 DeltaR / Delta / Vega / BumpPv1Bp 数值正确(线性函数精确)。
///
[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 { { "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 { { "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 reprice = p => (decimal)((p.RiskFreeRate ?? 0) * 1000); // PV = 1000 * r
var factor = RiskFactor.Rate("r"); // 标准步长:绝对 1bp
Func 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 { { "X", 100 } }
};
Func reprice = p => (decimal)p.SpotPrices["X"]; // PV = S
var factor = RiskFactor.Price("X"); // 标准步长:相对 1% → ε = 1
Func 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 reprice = p => (decimal)(((BaseOptionParameter)p).Volatility ?? 0) * 50; // PV = 50 * σ
var factor = RiskFactor.Volatility("X"); // 标准步长:绝对 1bp vol
Func pv = factor.BuildPvFunction(reprice, baseParam);
var calc = new GreeksBumpCalculator();
decimal vega = calc.Vega(pv, 0.2m, factor.Shift); // 中心差分对线性函数精确
Assert.AreEqual(50m, vega, 1e-4m);
}
}
}