750 lines
42 KiB
C#
750 lines
42 KiB
C#
using YLErp.Abstract.DataProviders;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.BLL.Eod;
|
|
using YLErp.Helpers;
|
|
using YLErp.Modules;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.BLL.MarginCalculation
|
|
{
|
|
public class HuaAnMarginCalculation : MarginCalculationBase
|
|
{
|
|
// 定义一个静态变量来保存类的实例
|
|
public static readonly HuaAnMarginCalculation Instance;
|
|
|
|
static HuaAnMarginCalculation()
|
|
{
|
|
Instance = new HuaAnMarginCalculation();
|
|
}
|
|
|
|
// 定义私有构造函数,使外界不能创建该类实例
|
|
private HuaAnMarginCalculation()
|
|
{
|
|
|
|
}
|
|
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
|
|
{
|
|
var resultList = new List<trade_span>();
|
|
|
|
if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin)
|
|
{
|
|
resultList = calcInitialMargin(req);
|
|
}
|
|
else
|
|
{
|
|
resultList = calcPositionMargin(req);
|
|
}
|
|
|
|
return resultList;
|
|
}
|
|
|
|
private List<trade_span> calcPositionMargin(RunMarginCalculationReq req)
|
|
{
|
|
var resultMap = new Dictionary<int, trade_span>();
|
|
req.PriceProvider = new EodPriceProvider(req.settleDate).GetPriceProvider(SettlementTypeEnum.SettlePrice);
|
|
var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
|
|
_helper.GetUpDownLimitPrices(out var upPrices, out var downPrices);
|
|
_helper.GetUpDownVolRateDic(out var upVolRateDic, out _);
|
|
|
|
var ForwordOrSwapList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期");
|
|
var ForwordOrSwapcalcReq =
|
|
_helper.GetCalculateRisksForTradesReq(
|
|
req.PriceProvider,
|
|
null,
|
|
null,
|
|
Qdp.Pricing.Base.Implementations.PricingRequest.Pv);
|
|
ForwordOrSwapcalcReq.tradeList = req.tradeList.Where(O => O.TradeType == "远期" || O.TradeType == "收益互换");
|
|
var ForwordOrSwapRusult = CalculatorHelper.CalculateRisksForTrades(ForwordOrSwapcalcReq);
|
|
foreach (var f in ForwordOrSwapRusult.Results.Where(l => l.Trade.TradeType == "远期" || l.Trade.TradeType == "收益互换"))
|
|
{
|
|
double? marginRate2 = 0;
|
|
if (!string.IsNullOrEmpty(f.Trade.BasisUnderlyingCode) && f.Trade.TradeType == "远期")
|
|
{
|
|
marginRate2 = GetUnderlyingCodeMarginRate(f.Trade.BasisUnderlyingCode);
|
|
}
|
|
double? marginRate1 = GetUnderlyingCodeMarginRate(f.Trade.UnderlyingCode);
|
|
var positionPnl = EodOperationBase.GetPositionPnl(f.ValueResult.Pv, f.Trade.TradePrice ?? 0.0, f.Trade.Notional, f.Trade.OriginalNotional ?? 0, f.Trade.BuySell);
|
|
var marginRate = ((marginRate1 > marginRate2 ? marginRate1 : marginRate2) ?? 0);
|
|
var margin = (f.Trade.TradeType == "远期" ? f.Trade.SpotPrice : req.PriceProvider.GetPrice(f.Trade.UnderlyingCode)) * f.Trade.Notional * marginRate + positionPnl;
|
|
var sp = _helper.CreateTradeSpan(f.Trade).SetAllSpvAndWorst(Math.Max((margin ?? 0), 0));
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
var allList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期");
|
|
var calcReq =
|
|
_helper.GetCalculateRisksForTradesReq(
|
|
req.PriceProvider,
|
|
null,
|
|
null,
|
|
Qdp.Pricing.Base.Implementations.PricingRequest.Delta);
|
|
calcReq.tradeList = allList;
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
|
|
|
|
var sellList = tradeRiskResult.Results.Where(O => O.Trade.ParentTradeId == 0 && O.Trade.IsGroup == 0 && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "卖出");
|
|
foreach (var item in sellList)
|
|
{
|
|
var sp = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
//1、交易员角度买入香草和亚式期权
|
|
//先按标的分类,在标的i下
|
|
//Span算法:先算出品种i下每笔交易的span算法预付金,然后合计
|
|
//Delta算法:先算出每一笔交易的delta算法预付金 = max(期权的总的delta* 收盘价*交易所预付金比例,0),然后合计算出该品种的delta算法预付金
|
|
//品种i的预付金 = max(span算法预付金,Delta算法预付金)
|
|
//香草和亚式的总的预付金
|
|
var VanillaOrAsianList = tradeRiskResult.Results.Where(O => !resultMap.Keys.Contains(O.Trade.id) && (O.Trade.ParentTradeId == 0 || O.Trade.IsGroup == 2) && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "买入");
|
|
|
|
foreach (var i in VanillaOrAsianList)
|
|
{
|
|
var deltaMargin = i.ValueResult.Delta * i.Trade.SpotPrice * GetUnderlyingCodeMarginRate(i.Trade.UnderlyingCode);
|
|
var sp = marginCalculation(req, new List<trade> { i.Trade }).First();
|
|
var spanMargin = sp.WorstCastClientPayable;
|
|
deltaMargin = deltaMargin > 0 ? deltaMargin : 0;
|
|
var margin = Math.Max((deltaMargin ?? 0), (spanMargin ?? 0));
|
|
sp.DeltaMargin = deltaMargin;
|
|
sp.WorstCastClientPayable = margin;
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
|
|
|
|
//买入宽跨式(跨式)期权组合
|
|
var StraddleTradeIds = req.tradeList.Where(O => (O.StructureType ?? O.TradeType).Contains("跨式")).Select(O => O.id);
|
|
var StraddleResultDict = tradeRiskResult.Results.Where(O => StraddleTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
|
|
foreach (var straddle in StraddleResultDict)
|
|
{
|
|
//获取多个腿的 deltaInitialMargin
|
|
var deltaInitialMargin = straddle.Value.Where(l => l.Trade.BuySell == "买入").ToDictionary(k => k.Trade.id, O => O.ValueResult.Delta * req.PriceProvider.GetPrice(O.Trade.UnderlyingCode) * GetUnderlyingCodeMarginRate(O.ValueResult.UnderlyingCode));
|
|
//获取多个腿的 spanInitialMargin
|
|
var spanInitialMargin = marginCalculation(req, straddle.Value.Where(l => l.Trade.BuySell == "买入").Select(l => l.Trade).ToList()).ToDictionary(k => k.TradeId, v => v.WorstCastClientPayable);
|
|
double margin = 0;
|
|
int selfLegTradeId = 0;
|
|
deltaInitialMargin.ToList().Union(spanInitialMargin.ToList()).ToList().ForEach(x =>
|
|
{
|
|
if (margin < x.Value)
|
|
{
|
|
margin = x.Value ?? 0;
|
|
selfLegTradeId = x.Key;
|
|
}
|
|
});
|
|
//另外一条腿的权利金总额
|
|
var straddleCalcReq = _helper.GetCalculateRisksForTradesReq(
|
|
req.PriceProvider,
|
|
null,
|
|
null,
|
|
Qdp.Pricing.Base.Implementations.PricingRequest.Pv);
|
|
straddleCalcReq.tradeList = new List<trade> { straddle.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() };
|
|
var straddleRusult = CalculatorHelper.CalculateRisksForTrades(straddleCalcReq);
|
|
var t = straddleRusult.Results.FirstOrDefault().Trade;
|
|
double? otherLegAmount = t.BuySell == "买入" ? Math.Abs(t.TradePrice ?? 0) : 0;
|
|
var partialNotionalRate = t.Notional / t.OriginalNotional;
|
|
for (int i = 0; i < straddle.Value.Count; i++)
|
|
{
|
|
if (straddle.Value[i].Trade.id == selfLegTradeId)
|
|
{
|
|
var sp = _helper.CreateTradeSpan(straddle.Value[i].Trade).SetAllSpvAndWorst(margin);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[straddle.Value[i].Trade.id] = sp;
|
|
}
|
|
else
|
|
{
|
|
var sp = _helper.CreateTradeSpan(straddle.Value[i].Trade).SetAllSpvAndWorst((otherLegAmount ?? 0) * (partialNotionalRate ?? 0));
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[straddle.Value[i].Trade.id] = sp;
|
|
}
|
|
}
|
|
}
|
|
//其它奇异期权 持仓预付金:用span算
|
|
var ExoticOptionsList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && (O.ParentTradeId == 0 || O.IsGroup == 2) && !O.TradeType.Contains("香草") && !O.TradeType.Contains("亚式"));
|
|
|
|
//牛市价差、熊市价差期权
|
|
var NiuxiongTradeIds = req.tradeList.Where(O => ((O.StructureType ?? O.TradeType).Contains("牛市") || (O.StructureType ?? O.TradeType).Contains("熊市"))).Select(O => O.id);
|
|
var NiuxiongResultDict = tradeRiskResult.Results.Where(O => NiuxiongTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
|
|
foreach (var buyNiuxiong in NiuxiongResultDict)
|
|
{
|
|
var margin = buyNiuxiong.Value.Max(O => Math.Max(Math.Abs(O.ValueResult.Delta) / O.Trade.Notional, 0.5) * req.PriceProvider.GetPrice(O.Trade.UnderlyingCode) * O.Trade.Notional * GetUnderlyingCodeMarginRate(O.Underlyings.Select(l => l.UnderlyingCode).FirstOrDefault())) * 0.5;
|
|
for (int i = 0; i < buyNiuxiong.Value.Count; i++)
|
|
{
|
|
if (buyNiuxiong.Value[i].Trade.BuySell == "买入")
|
|
{
|
|
var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(margin ?? 0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[buyNiuxiong.Value[i].Trade.id] = sp;
|
|
}
|
|
else
|
|
{
|
|
var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[buyNiuxiong.Value[i].Trade.id] = sp;
|
|
}
|
|
}
|
|
}
|
|
//除了宽跨式(跨式)期权、牛熊市价差期权以外的其他组合期权
|
|
var OtherTradeList = req.tradeList.Where(O => (O.ParentTradeId > 0 && O.IsGroup == 0) && !(O.StructureType ?? O.TradeType).Contains("牛市") && !(O.StructureType ?? O.TradeType).Contains("熊市") && !(O.StructureType ?? O.TradeType).Contains("跨式")).ToList();
|
|
|
|
var tradeList = ExoticOptionsList.Union(OtherTradeList).ToList();
|
|
if (tradeList.Count > 0)
|
|
{
|
|
var resultspan = marginCalculation(req, tradeList);
|
|
foreach (var i in resultspan)
|
|
{
|
|
if (resultMap.ContainsKey(i.TradeId))
|
|
{
|
|
resultMap[i.TradeId] = i;
|
|
}
|
|
else
|
|
{
|
|
resultMap.Add(i.TradeId, i);
|
|
}
|
|
}
|
|
}
|
|
return resultMap.Values.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取标的现价
|
|
/// </summary>
|
|
/// <param name="underlyingCode"></param>
|
|
/// <param name="CurUser"></param>
|
|
/// <param name="tradeDate"></param>
|
|
/// <returns></returns>
|
|
public static double GetUnderlyingPrice(RunMarginCalculationReq req, string underlyingCode, DateTime tradeDate, SettlementTypeEnum typeEnum = SettlementTypeEnum.SettlePrice)
|
|
{
|
|
double price = 0;
|
|
|
|
var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (udm != null)
|
|
{
|
|
var priceProvider = new EodPriceProvider(tradeDate).GetPriceProvider(typeEnum);
|
|
price = priceProvider.GetPrice(underlyingCode);
|
|
}
|
|
|
|
return price;
|
|
}
|
|
public List<trade_span> calcInitialMargin(RunMarginCalculationReq req)
|
|
{
|
|
var resultMap = new Dictionary<int, trade_span>();
|
|
if (req.tradeList == null || !req.tradeList.Any())
|
|
{
|
|
return new List<trade_span>();
|
|
}
|
|
|
|
var tradeSpanList = new List<trade_span>();
|
|
var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
|
|
_helper.GetUpDownLimitPrices(out var upPrices, out var downPrices);
|
|
_helper.GetUpDownVolRateDic(out var upVolRateDic, out _);
|
|
|
|
//收益互换交易单独计算trade_span
|
|
var swap = req.tradeList.Where(O => O.TradeType == "收益互换");
|
|
tradeBLL.SetFieldsByTradeType(swap);
|
|
foreach (var item in swap)
|
|
{
|
|
var margin = 0d;
|
|
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
|
|
if (un != null)
|
|
{
|
|
margin = (double)(item.StockEqvNotional * (item.trade_swap?.GetMarginRate ?? 0));
|
|
}
|
|
var sp = _helper.CreateTradeSpan(item).SetAllSpvAndWorst(margin);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
//远期计算span
|
|
if (req.tradeList.Any(t => t.TradeType == "远期"))
|
|
{
|
|
var forwardTradeList = req.tradeList.Where(t => t.TradeType == "远期").ToList();
|
|
foreach (var t in forwardTradeList)
|
|
{
|
|
double? marginRate2 = 0;
|
|
if (!string.IsNullOrEmpty(t.BasisUnderlyingCode))
|
|
{
|
|
marginRate2 = GetUnderlyingCodeMarginRate(t.BasisUnderlyingCode);
|
|
}
|
|
double? marginRate1 = GetUnderlyingCodeMarginRate(t.UnderlyingCode);
|
|
var margin = t.OriginalNotional * (t.SpotPrice ?? 0) * (marginRate1 > marginRate2 ? marginRate1 : marginRate2);
|
|
var sp = _helper.CreateTradeSpan(t).SetAllSpvAndWorst(margin ?? 0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
}
|
|
|
|
|
|
var allList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期");
|
|
var calcReq =
|
|
_helper.GetCalculateRisksForTradesReq(
|
|
req.PriceProvider,
|
|
null,
|
|
null,
|
|
Qdp.Pricing.Base.Implementations.PricingRequest.Delta);
|
|
calcReq.tradeList = allList;
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
|
|
|
|
var sellList = tradeRiskResult.Results.Where(O => (O.Trade.CalcId == null || (O.Trade.CalcId != null && !O.Trade.CalcId.Contains("-"))) && O.Trade.IsGroup == 0 && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "卖出");
|
|
foreach (var item in sellList)
|
|
{
|
|
var sp = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
|
|
//1、买入香草和亚式期权 初始预付金:基于delta算法,max(期权的总Delta *期初价格*交易所预付金比例,0)
|
|
var VanillaOrAsianList = tradeRiskResult.Results.Where(O => (O.Trade.CalcId == null || (O.Trade.CalcId != null && !O.Trade.CalcId.Contains("-"))) && !resultMap.Keys.Contains(O.Trade.id) && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "买入");
|
|
foreach (var item in VanillaOrAsianList)
|
|
{
|
|
var deltaMargin = item.ValueResult.Delta * item.Trade.SpotPrice * GetUnderlyingCodeMarginRate(item.Trade.UnderlyingCode);
|
|
var sp = marginCalculation(req, new List<trade> { item.Trade }).First();
|
|
var spanMargin = sp.WorstCastClientPayable;
|
|
deltaMargin = deltaMargin > 0 ? deltaMargin : 0;
|
|
var margin = Math.Max((deltaMargin ?? 0), (spanMargin ?? 0));
|
|
sp.DeltaMargin = deltaMargin;
|
|
sp.WorstCastClientPayable = margin;
|
|
resultMap[sp.TradeId] = sp;
|
|
}
|
|
|
|
|
|
//买入宽跨式(跨式)期权组合
|
|
var StraddleTradeIds = req.tradeList.Where(O => (O.StructureType ?? O.TradeType).Contains("跨式")).Select(O => O.id);
|
|
var StraddleResultDict = tradeRiskResult.Results.Where(O => StraddleTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
|
|
foreach (var item in StraddleResultDict)
|
|
{
|
|
//获取多个腿的 deltaInitialMargin
|
|
var deltaInitialMargin = item.Value.Where(l => l.Trade.BuySell == "买入").ToDictionary(k => k.Trade.id, O => O.ValueResult.Delta * O.ValueResult.SpotPrice * GetUnderlyingCodeMarginRate(O.ValueResult.UnderlyingCode));
|
|
//获取多个腿的 spanInitialMargin
|
|
var spanInitialMargin = marginCalculation(req, item.Value.Where(l => l.Trade.BuySell == "买入").Select(l => l.Trade).ToList()).ToDictionary(k => k.TradeId, v => v.WorstCastClientPayable);
|
|
double margin = 0;
|
|
int selfLegTradeId = 0;
|
|
deltaInitialMargin.ToList().Union(spanInitialMargin.ToList()).ToList().ForEach(x =>
|
|
{
|
|
if (margin < x.Value)
|
|
{
|
|
margin = x.Value ?? 0;
|
|
selfLegTradeId = x.Key;
|
|
|
|
}
|
|
});
|
|
if (item.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() != null)
|
|
{
|
|
var straddleCalcReq = _helper.GetCalculateRisksForTradesReq(
|
|
req.PriceProvider,
|
|
null,
|
|
null,
|
|
Qdp.Pricing.Base.Implementations.PricingRequest.Pv);
|
|
straddleCalcReq.tradeList = new List<trade> { item.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() };
|
|
var straddleRusult = CalculatorHelper.CalculateRisksForTrades(straddleCalcReq);
|
|
var t = straddleRusult.Results.FirstOrDefault().Trade;
|
|
double? otherLegAmount = t.BuySell == "买入" ? Math.Abs(t.TradePrice ?? 0) : 0;
|
|
for (int i = 0; i < item.Value.Count; i++)
|
|
{
|
|
if (item.Value[i].Trade.id == selfLegTradeId)
|
|
{
|
|
var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst(margin);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[item.Value[i].Trade.id] = sp;
|
|
}
|
|
else
|
|
{
|
|
var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst((otherLegAmount ?? 0));
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[item.Value[i].Trade.id] = sp;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < item.Value.Count; i++)
|
|
{
|
|
if (item.Value[i].Trade.id == selfLegTradeId)
|
|
{
|
|
var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst(margin);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[item.Value[i].Trade.id] = sp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//其它奇异期权 初始预付金:用span算
|
|
var ExoticOptionsList = req.tradeList.Where(O => !(O.CalcId ?? "").Contains("-") && !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期" && !O.TradeType.Contains("香草") && !O.TradeType.Contains("亚式") && O.BuySell == "买入");
|
|
|
|
//牛市价差、熊市价差期权
|
|
var NiuxiongTradeIds = req.tradeList.Where(O => ((O.StructureType ?? O.TradeType).Contains("牛市") || (O.StructureType ?? O.TradeType).Contains("熊市"))).Select(l => l.id);
|
|
var NiuxiongResultDict = tradeRiskResult.Results.Where(O => NiuxiongTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
|
|
foreach (var buyNiuxiong in NiuxiongResultDict)
|
|
{
|
|
var margin = buyNiuxiong.Value.Max(O => Math.Max((Math.Abs(O.ValueResult.Delta) / O.Trade.OriginalNotional ?? 0), 0.5) * O.Trade.SpotPrice * O.Trade.OriginalNotional * GetUnderlyingCodeMarginRate(O.Trade.UnderlyingCode)) * 0.5;
|
|
for (int i = 0; i < buyNiuxiong.Value.Count; i++)
|
|
{
|
|
if (buyNiuxiong.Value[i].Trade.BuySell == "买入")
|
|
{
|
|
var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(margin ?? 0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[buyNiuxiong.Value[i].Trade.id] = sp;
|
|
}
|
|
else
|
|
{
|
|
var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(0);
|
|
sp.SetWorstCastClientPayable();
|
|
resultMap[buyNiuxiong.Value[i].Trade.id] = sp;
|
|
}
|
|
}
|
|
}
|
|
//除了宽跨式(跨式)期权、牛熊市价差期权以外的其他组合期权
|
|
var OtherTradeList = req.tradeList.Where(O => (O.CalcId ?? "").Contains("-") && !(O.StructureType ?? O.TradeType).Contains("牛市") && !(O.StructureType ?? O.TradeType).Contains("熊市") && !(O.StructureType ?? O.TradeType).Contains("跨式"));
|
|
|
|
var tradeList = ExoticOptionsList.Union(OtherTradeList).ToList();
|
|
if (tradeList.Count > 0)
|
|
{
|
|
var resultspan = marginCalculation(req, tradeList);
|
|
foreach (var i in resultspan)
|
|
{
|
|
if (resultMap.ContainsKey(i.TradeId))
|
|
{
|
|
resultMap[i.TradeId] = i;
|
|
}
|
|
else
|
|
{
|
|
resultMap.Add(i.TradeId, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return resultMap.Values.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取预付金比率
|
|
/// </summary>
|
|
/// <param name="UnderlyingCode"></param>
|
|
/// <returns></returns>
|
|
public double? GetUnderlyingCodeMarginRate(string UnderlyingCode)
|
|
{
|
|
double? MarginRate = 0;
|
|
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode);
|
|
if (un != null)
|
|
{
|
|
MarginRate = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode).MarginRate ?? DataCacheProvider.GetVarietyDataSource().GetData(UnderlyingCode).Margin;
|
|
}
|
|
|
|
return MarginRate;
|
|
}
|
|
public List<trade_span> marginCalculation(RunMarginCalculationReq req, List<trade> trades)
|
|
{
|
|
var thisReq = req.Clone();
|
|
var helper = new RunMarginCalculationHelper(thisReq, _underlyingDataProvider);
|
|
var resultMap = new Dictionary<int, trade_span>();
|
|
if (trades != null && trades.Any())
|
|
{
|
|
//处理香草,亚式,跨式
|
|
thisReq.tradeList = trades;
|
|
}
|
|
helper.SetFieldsByTradeType();
|
|
helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices);
|
|
helper.GetTradVolRateDic(out var tradeVolRateDic);
|
|
|
|
var vols = new[] { null, tradeVolRateDic };
|
|
var prices = new (string key, IPriceProvider priceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices), ("normal", thisReq.PriceProvider) };
|
|
var loops = prices.SelectMany(n => vols.Select(m => new
|
|
{
|
|
pricekey = n.key,
|
|
priceProvider = n.priceProvider,
|
|
addVolRateDic = m
|
|
})).ToArray();
|
|
var uProvider = new UnderlyingDataProvider();
|
|
|
|
foreach (var loop in loops)
|
|
{
|
|
var calcReq = helper.GetCalculateRisksForTradesReq(priceProvider: loop.priceProvider, addVolRateDic: loop.addVolRateDic, overrideVols: null, pricingRequest: QdpPricingRequest.BASIC_PRICING);
|
|
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
|
|
if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1)
|
|
{
|
|
continue;
|
|
}
|
|
var key = $"{loop.pricekey}_{(loop.addVolRateDic == null ? 0 : 1)}";
|
|
if (key == "normal_1") continue;
|
|
|
|
foreach (var item in tradeRiskResult.Results)
|
|
{
|
|
if ((item.Trade.TradeType == "远期" || item.Trade.TradeType == "收益互换"))
|
|
{
|
|
continue;
|
|
}
|
|
var pv = item.ValueResult.Pv;
|
|
if (!helper.GetSpecialMargin(item.Trade, pv, out var value, helper.req.forOtherSide))
|
|
{
|
|
var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0;
|
|
value = double.IsNaN(pv) ? 0 : pv * clientRatio;
|
|
}
|
|
|
|
var contains = resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan);
|
|
|
|
if (!contains)
|
|
{
|
|
resultMap[item.Trade.id] = tempTradeSpan = helper.CreateTradeSpan(item.Trade);
|
|
}
|
|
|
|
switch (key)
|
|
{
|
|
case "up_0":
|
|
tempTradeSpan.Spv1 = value; break;
|
|
case "up_1":
|
|
tempTradeSpan.Spv2 = value; break;
|
|
case "normal_0":
|
|
if (!(item.Trade.StructureType ?? item.Trade.TradeType).Contains("跨式"))
|
|
{
|
|
|
|
var um = uProvider.GetUnderlying(item.Trade.UnderlyingCode);
|
|
var variety = uProvider.GetVariety(item.Trade.UnderlyingCode);
|
|
var marginRate = ((um?.MarginRate ?? 0) > 0 ? um.MarginRate : variety?.Margin) ?? 0;
|
|
double deltaCash = 0;
|
|
if ((item.Trade.BuySell == "买入" && item.Trade.OptionType == "看涨") || (item.Trade.BuySell == "卖出" && item.Trade.OptionType == "看跌"))
|
|
{
|
|
deltaCash = Math.Abs(item.ValueResult.DeltaCash);
|
|
}
|
|
else
|
|
{
|
|
deltaCash = -Math.Abs(item.ValueResult.DeltaCash);
|
|
}
|
|
value = deltaCash * marginRate;
|
|
tempTradeSpan.DeltaMargin = value;
|
|
tempTradeSpan.PositionWinLoss = EodOperationBase.GetPositionPnl(item.ValueResult.Pv, item.Trade.TradePrice ?? 0.0, item.Trade.Notional, item.Trade.OriginalNotional ?? 0, item.Trade.BuySell);
|
|
}
|
|
break;
|
|
case "down_0":
|
|
tempTradeSpan.Spv3 = value; break;
|
|
case "down_1":
|
|
tempTradeSpan.Spv4 = value; break;
|
|
}
|
|
|
|
if (contains)
|
|
{
|
|
tempTradeSpan.SetWorstCastClientPayable();
|
|
|
|
if (tempTradeSpan.WorstCastClientPayable > 0)
|
|
{
|
|
tempTradeSpan.WorstCastClientPayable = Math.Max(tempTradeSpan.WorstCastClientPayable.Value, Math.Abs(tempTradeSpan.DeltaMargin ?? 0) + (tempTradeSpan.PositionWinLoss ?? 0));
|
|
}
|
|
else
|
|
{
|
|
tempTradeSpan.WorstCastClientPayable = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return resultMap.Values.ToList();
|
|
}
|
|
|
|
public override List<trade_span> CalcClientMargin(CalcClientMarginReq req)
|
|
{
|
|
var clientSpanNews = new List<ClientSpan>();
|
|
using (var db = new YLContext())
|
|
{
|
|
var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList();
|
|
var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList();
|
|
|
|
//获取收盘日那天对应的预付金模板
|
|
var tradeTemplates = db.trade_margin_template.AsNoTracking().Where(t => tradeIds.Contains(t.TradeId) && t.ValueDate <= req.settleDate);
|
|
var groupQuery = from tt in tradeTemplates
|
|
group tt by tt.TradeId into tts
|
|
select new
|
|
{
|
|
TradeId = tts.Key,
|
|
ValueDate = tts.Max(n => n.ValueDate)
|
|
};
|
|
var groupTemplates = from gq in groupQuery
|
|
join tt in tradeTemplates
|
|
on new { gq.TradeId, gq.ValueDate } equals new { tt.TradeId, tt.ValueDate }
|
|
select tt;
|
|
|
|
var tradeSpanInfo = (from tradeSpan in req.tradeSpans
|
|
join trade in tradeList on tradeSpan.TradeId equals trade.id
|
|
join tradetemplate in groupTemplates on tradeSpan.TradeId equals tradetemplate.TradeId into templates
|
|
from tradetemplate in templates.DefaultIfEmpty()
|
|
where tradeSpan.ValueDate == req.settleDate
|
|
select new { trade, tradeSpan, tradetemplate }).ToList();
|
|
|
|
if (req.tradeSpans != null && req.tradeSpans.Count > 0)
|
|
{
|
|
var spans = req.tradeSpansOtherSide != null
|
|
? req.tradeSpansOtherSide.Where(n => n != null).ToArray() : Enumerable.Empty<trade_span>();
|
|
var tradeSpanInfoOtherSide = (from tradeSpan in spans
|
|
join trade in tradeList on tradeSpan.TradeId equals trade.id
|
|
where tradeSpan.ValueDate == req.settleDate
|
|
select new { trade, tradeSpan }).ToList();
|
|
|
|
var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId);
|
|
foreach (var clientGroup in clientGroups)
|
|
{
|
|
var underlyingGroup = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).GroupBy(t => t.trade.UnderlyingId).Select(t => new ClientSpan
|
|
{
|
|
UnderlyingId = t.Key,
|
|
ClientId = clientGroup.Key,
|
|
ValueDate = req.settleDate,
|
|
Spv1 = t.Sum(g => g.tradeSpan.Spv1) * (-1),
|
|
Spv2 = t.Sum(g => g.tradeSpan.Spv2) * (-1),
|
|
Spv3 = t.Sum(g => g.tradeSpan.Spv3) * (-1),
|
|
Spv4 = t.Sum(g => g.tradeSpan.Spv4) * (-1),
|
|
DeltaMargin = t.Sum(g => g.tradeSpan.DeltaMargin) * (-1),
|
|
PositionWinLoss = t.Sum(g => g.tradeSpan.PositionWinLoss) * (-1),
|
|
OptId = req.userId,
|
|
OptName = req.userName,
|
|
OptDate = DateTime.Now,
|
|
SpanType = req.SpanType
|
|
}).ToList();
|
|
|
|
foreach (var item in underlyingGroup)
|
|
{
|
|
var tradeSpanIds = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).Select(x => x.tradeSpan.id);
|
|
var tradeSpansUpdate = db.trade_span.Where(x => tradeSpanIds.Contains(x.id) && x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
|
|
var tradeSpansReq = req.tradeSpans.Where(x => tradeSpanIds.Contains(x.id) && x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
|
|
|
|
var minSpv = Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0);
|
|
if (minSpv < 0)
|
|
{
|
|
item.WorstCastClientPayable = Math.Max(Math.Abs(minSpv), Math.Abs(item.DeltaMargin ?? 0) - (item.PositionWinLoss ?? 0)) * (-1);
|
|
item.TwoSideMargin = item.WorstCastClientPayable;
|
|
|
|
if (Math.Abs(minSpv) > Math.Abs(item.DeltaMargin ?? 0) - (item.PositionWinLoss ?? 0))
|
|
{
|
|
//更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致
|
|
if (item.WorstCastClientPayable == item.Spv1)
|
|
{
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv1);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv1);
|
|
}
|
|
else if (item.WorstCastClientPayable == item.Spv2)
|
|
{
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv2);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv2);
|
|
}
|
|
else if (item.WorstCastClientPayable == item.Spv3)
|
|
{
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv3);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv3);
|
|
}
|
|
else if (item.WorstCastClientPayable == item.Spv4)
|
|
{
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv4);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
item.WorstCastClientPayable = 0;
|
|
item.TwoSideMargin = 0;
|
|
|
|
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = 0);
|
|
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = 0);
|
|
}
|
|
}
|
|
|
|
var clientSpan = new ClientSpan
|
|
{
|
|
ClientId = clientGroup.Key,
|
|
ValueDate = req.settleDate,
|
|
Spv1 = underlyingGroup.Sum(g => g.Spv1),
|
|
Spv2 = underlyingGroup.Sum(g => g.Spv2),
|
|
Spv3 = underlyingGroup.Sum(g => g.Spv3),
|
|
Spv4 = underlyingGroup.Sum(g => g.Spv4),
|
|
DeltaMargin = underlyingGroup.Sum(g => g.DeltaMargin),
|
|
//负数代表客户应缴预付金,正数代表客户应收预付金
|
|
WorstCastClientPayable = underlyingGroup.Sum(g => g.WorstCastClientPayable),
|
|
MySideMargin = underlyingGroup.Sum(g => g.WorstCastClientPayable),
|
|
TwoSideMargin = underlyingGroup.Sum(g => g.TwoSideMargin),
|
|
OptId = req.userId,
|
|
OptName = req.userName,
|
|
OptDate = DateTime.Now,
|
|
SpanType = req.SpanType,
|
|
AdditionalWorstCastClientPayable = req.clientAdditionalMarginDic != null && req.clientAdditionalMarginDic.TryGetValue(clientGroup.Key, out var dd) ? dd : 0
|
|
};
|
|
//单笔预付金算法的交易不参与品种轧差;
|
|
var singleMarginTrade = clientGroup.Where(O => O.trade.MarginType != DBModels.Enums.MarginTypeEnum.DEFAULT).Select(O => O.tradeSpan);
|
|
clientSpan.Spv1 += singleMarginTrade.Sum(O => O.Spv1 * (-1));
|
|
clientSpan.Spv2 += singleMarginTrade.Sum(O => O.Spv2 * (-1));
|
|
clientSpan.Spv3 += singleMarginTrade.Sum(O => O.Spv3 * (-1));
|
|
clientSpan.Spv4 += singleMarginTrade.Sum(O => O.Spv4 * (-1));
|
|
clientSpan.DeltaMargin += singleMarginTrade.Sum(O => O.DeltaMargin * (-1));
|
|
clientSpan.WorstCastClientPayable += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1));
|
|
clientSpan.MySideMargin += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1));
|
|
clientSpan.TwoSideMargin += singleMarginTrade.Sum(O => O.TwoSideMargin);
|
|
clientSpan.MySideMargin = Math.Min(clientSpan.MySideMargin ?? 0, 0);
|
|
|
|
//交易员不支付预付金
|
|
clientSpan.WorstCastClientPayable = Math.Min(clientSpan.WorstCastClientPayable ?? 0, 0);
|
|
|
|
clientSpanNews.Add(clientSpan);
|
|
}
|
|
}
|
|
|
|
//span类型为实时删除所有实时计算的交易的预付金信息
|
|
if (req.SpanType == ClientSpan.SpanType_RealTime)
|
|
{
|
|
if (req.RefreshClientIds != null)
|
|
{
|
|
db.BulkDelete<ClientSpan>($"{nameof(ClientSpan.ClientId)} in @ids", new { ids = req.RefreshClientIds });
|
|
}
|
|
else
|
|
{
|
|
db.BulkDelete<ClientSpan>($"{nameof(ClientSpan.SpanType)}=@SpanType", new { req.SpanType });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (req.ClientIds != null)
|
|
{
|
|
var sql = $"{nameof(ClientSpan.ClientId)} in @ids and {nameof(ClientSpan.ValueDate)}='{req.settleDate.ToSqlDate()}' and {nameof(ClientSpan.SpanType)}={req.SpanType} and {nameof(ClientSpan.ModifiedFlag)}=0";
|
|
db.BulkDelete<ClientSpan>(sql, new { ids = req.ClientIds });
|
|
}
|
|
else
|
|
{
|
|
var sql = $"{nameof(ClientSpan.ValueDate)}='{req.settleDate.ToSqlDate()}' and {nameof(ClientSpan.SpanType)}={req.SpanType} and {nameof(ClientSpan.ModifiedFlag)}=0";
|
|
db.BulkDelete<ClientSpan>(sql);
|
|
}
|
|
|
|
var clientSpanOldsWithFlag = db.client_span.Where(t => t.ValueDate == req.settleDate && t.SpanType == req.SpanType && t.ModifiedFlag)
|
|
.Select(n => new { n.ValueDate, n.ClientId }).ToList();
|
|
//筛选出可以修改的clientSpan
|
|
clientSpanNews = clientSpanNews.Where(c => !clientSpanOldsWithFlag.Any(t => t.ValueDate == c.ValueDate && t.ClientId == c.ClientId)).ToList();
|
|
}
|
|
if (clientSpanNews.Count > 0)
|
|
{
|
|
MySqlBulkExtensions.BulkInsert(db, clientSpanNews);
|
|
}
|
|
db.SaveChanges();
|
|
return req.tradeSpans;
|
|
}
|
|
}
|
|
|
|
public override double GetTradeMargin(GetTradeMarginReq req)
|
|
{
|
|
using (var db = new YLContext())
|
|
{
|
|
if (req.trade.TradeType == "结构化交易")
|
|
{
|
|
req.trade.SubTrades = db.trade.Where(x => x.ParentTradeId == req.realTradeId).ToList();
|
|
}
|
|
|
|
var marginReq = req.GetRunMarginCalculationReq();
|
|
if (req.trade.IsGroup == 1)
|
|
{
|
|
marginReq.tradeList = db.trade.Where(x => x.ParentTradeId == req.realTradeId).ToList();
|
|
}
|
|
var tradeMargin = RunMarginCalculation(marginReq);
|
|
if (null != tradeMargin)
|
|
{
|
|
var margin = req.trade.IsGroup == 1 ? tradeMargin.Sum(x => x.WorstCastClientPayable ?? 0) : (tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0);
|
|
return margin;
|
|
}
|
|
return 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|