Files
zszq-trs/YLErpDAL/BLL/MarginCalculation/HuaAnMarginCalculation.cs
T
2024-05-09 14:06:26 +08:00

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;
}
}
}
}