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

501 lines
25 KiB
C#

using Org.BouncyCastle.Ocsp;
using System.Collections.Generic;
using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.DBModels;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Modules;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.MarginModule;
using YLErp.QdpModule;
using static iTextSharp.text.pdf.AcroFields;
namespace YLErp.BLL.MarginCalculation
{
public class HongYuanMarginCalculation : MarginCalculationBase
{
//定义一个静态变量来保存类的实例(单例模式)
public static readonly HongYuanMarginCalculation Instance;
static HongYuanMarginCalculation()
{
Instance = new HongYuanMarginCalculation();
}
//定义私有构造函数,使外界不能创建该类实例
private HongYuanMarginCalculation()
{
}
/// <summary>
/// 宏源定制算法 -- 远期和敲入的气囊
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private List<trade_span> CalculationSingleTrade(RunMarginCalculationReq req)
{
if (req.tradeList == null || !req.tradeList.Any())
{
return new List<trade_span>(0);
}
var resultMap = new Dictionary<int, trade_span>();
var vols = new Dictionary<int, double>();
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
helper.SetFieldsByTradeType();
var normalProvider = new (string key, IPriceProvider priceProvider)[] { ("normal", helper.req.PriceProvider) };
foreach (var t in req.tradeList)
{
var clone = t.Clone();
var vol = clone.TradeOpenVolatility ?? 0;
var client = helper.GetClient(clone);
var ProperClientClass = client?.ProperClientClass;
if (string.IsNullOrWhiteSpace(ProperClientClass))
{
throw new Exception($"交易编号为{clone.TradeNumber}的客户适当性类型不存在,无法计算预付金!");
}
if (ProperClientClass.Contains("普通投资者"))
{
vol *= 1.2;
}
vols[clone.id] = vol;
}
var calcReq = helper.GetCalculateRisksForTradesReq(helper.req.PriceProvider, null, vols, QdpPricingRequest.PV_ONLY);
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
var mpProvider = new MarginParamProvider(req.UserInfo, req.settleDate).Initialize(tradeRiskResult.Results.GroupBy(l => l.Trade.UnderlyingCode).Select(l => l.Key).ToHashSet(), MarginParamTypeEnum.MarginRate);
foreach (var r in tradeRiskResult.Results)
{
if (!req.PriceProvider.TryGetPrice(r.Trade.UnderlyingCode, out var settlementPrice))
{
settlementPrice = r.Trade.SpotPrice ?? 0;
}
mpProvider.TryGetMarginRate(r.Trade.UnderlyingCode, out var marginRate);
var positionNumber = r.Trade.Notional * marginRate;
var positionWinLoss = (double.IsNaN(r.ValueResult.Pv) ? 0.0 : r.ValueResult.Pv) + (r.Trade.TradePrice ?? 0.0 * r.Trade.Notional / r.Trade.OriginalNotional ?? 0) * (r.Trade.BuySell == "买入" ? -1 : 1);
var yqvalue = Math.Abs(settlementPrice * positionNumber + (double)positionWinLoss);
var yqtempTradeSpan = new trade_span
{
TradeId = r.Trade.id,
ClientId = r.Trade.ClientId,
ValueDate = req.settleDate,
UnderlyingId = r.Trade.UnderlyingId,
UnderlyingCode = r.Trade.UnderlyingCode,
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
Spv1 = yqvalue,
Spv2 = yqvalue,
Spv3 = yqvalue,
Spv4 = yqvalue,
IsSingleMargin = true,
WorstCastClientPayable = yqvalue
};
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
{
var client = helper.GetClient(r.Trade);
if (client != null && client.Ratio1 != null)
{
yqtempTradeSpan.WorstCastClientPayable = yqtempTradeSpan.WorstCastClientPayable * client.Ratio1;
}
}
resultMap[r.Trade.id] = yqtempTradeSpan;
}
return resultMap.Values.ToList();
}
/// <summary>
/// span算法
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
private List<trade_span> SpanRunMarginCalculation(RunMarginCalculationReq req)
{
using (var db = new YLContext())
{
var tradeIds = req.tradeList.Select(x => x.id);
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
{
tradeIds = new List<int> { req.realTradeId };
}
var tradeMarginTemplates = db.trade_margin_template.Where(x => tradeIds.Contains(x.TradeId)).ToList();
var tradeMarginTemplateIds = tradeMarginTemplates.Select(x => x.MarginTemplateId).ToArray();
var marginTemplates = db.margin_template_v2.Where(x => tradeMarginTemplateIds.Contains(x.id)).ToList();
var resultMap = new Dictionary<int, trade_span>();
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
//为了算客户角度的一个预付金数值
helper.ReverseTradeSide();
helper.SetFieldsByTradeType();
helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices);
helper.GetTradVolRateDic(out var tradeVolRateDic);
var vols = new[] { null, tradeVolRateDic };
var prices = new (string, IPriceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices) };
var loops = prices.SelectMany(n => vols.Select(m => new
{
pricekey = n.Item1,
priceProvider = n.Item2,
addVolRateDic = m
})).ToArray();
foreach (var loop in loops)
{
var calcReq = helper.GetCalculateRisksForTradesReq(priceProvider: loop.priceProvider, addVolRateDic: loop.addVolRateDic, overrideVols: null);
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1)
{
continue;
}
var key = $"{loop.pricekey}_{(loop.addVolRateDic == null ? 0 : 1)}";
foreach (var item in tradeRiskResult.Results)
{
var pv = item.ValueResult.Pv;
var isSpecial = true;
var client = helper.GetClient(item.Trade);
if (!helper.GetSpecialMargin(item.Trade, pv, out var value))
{
isSpecial = false;
value = double.IsNaN(pv) ? 0 : pv;
}
if (!isSpecial)
{
if (req.CalcMarginType != CalcMarginTypeEnum.InitialMargin || ("雪球期权".Equals(item.Trade.TradeType) && "买入".Equals(item.Trade.BuySell)))
{
if (!("雪球期权".Equals(item.Trade.TradeType) && "卖出".Equals(item.Trade.BuySell)))
{
var clientRatio = client?.Ratio ?? 1.0;
value = value * clientRatio;
}
}
}
if (req.CalcMarginType != CalcMarginTypeEnum.InitialMargin && "雪球期权".Equals(item.Trade.TradeType) && "卖出".Equals(item.Trade.BuySell))
{
if (item.Trade.InitialMargin != null && value < item.Trade.InitialMargin)
{
value = item.Trade.InitialMargin.Value;
}
}
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin && !"雪球期权".Equals(item.Trade.TradeType) && !"收益互换".Equals(item.Trade.TradeType))
{
value = value * (client?.Ratio1 ?? 1.0);
}
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 "down_0":
tempTradeSpan.Spv3 = value; break;
case "down_1":
tempTradeSpan.Spv4 = value; break;
}
if (contains)
{
tempTradeSpan.SetWorstCastClientPayable();
//设置了特殊的规则
var tradeMarginTemplate = tradeMarginTemplates.Where(x => x.TradeId == item.Trade.id && x.ValueDate <= req.settleDate).OrderByDescending(x => x.ValueDate).FirstOrDefault();
if (tradeMarginTemplate != null)
{
var marginTemplate = marginTemplates.FirstOrDefault(x => x.id == tradeMarginTemplate.MarginTemplateId);
if (marginTemplate == null)
{
throw new Exception(string.Format("{0}未找到配置的预付金模板", item.Trade.TradeNumber));
}
if (marginTemplate.RuleType == (int)MarginRuleTypeEnum.单独计算规则)
{
tempTradeSpan.IsSingleMargin = true;
}
}
}
}
}
return resultMap.Values.ToList();
}
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
if (req.tradeList == null || !req.tradeList.Any())
{
return new List<trade_span>(0);
}
//结果集
var resultMap = new List<trade_span>();
if ((req.CalcMarginType != CalcMarginTypeEnum.EodMargin && req.CalcMarginType != CalcMarginTypeEnum.InitialMargin))
{
//价格列表不是空的说明是计算实时预付金或期初预付金
//宏源在计算实时预付金的时候使用前一天的结算价这个逻辑是一开始确认的,后续对数据的时候要再次确认 --时嬴政
var lastDate = QdpCalendarHelper.GetNonHolidayDefore(req.settleDate.AddDays(-1));
req.PriceProvider = new EodPriceProvider(lastDate).GetPriceProvider(SettlementTypeEnum.SettlePrice);
req.CalcMarginType = CalcMarginTypeEnum.EodMargin;
}
var list1 = req.tradeList.Where(x => x.TradeType == "远期" || (x.TradeType == "气囊结构" && x.trade_airbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)).ToList();
resultMap.AddRange(CalculationSingleTrade(req.Clone(list1)));
var list2 = req.tradeList.Where(x => x.TradeType != "远期" && !(x.TradeType == "气囊结构" && x.trade_airbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)).ToList();
resultMap.AddRange(SpanRunMarginCalculation(req.Clone(list2)));
return resultMap;
}
public override List<trade_span> CalcClientMargin(CalcClientMarginReq req)
{
var clientSpanNews = new List<ClientSpan>();
using (var db = new YLContext())
{
if (req.tradeSpans != null && req.tradeSpans.Count > 0)
{
var teamA = db.assetunit.Where(x => x.UserGroup == "A").Select(x => x.id).ToArray();
var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList();
var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList();
var clientids = tradeList.Select(x => x.ClientId).Distinct().ToArray();
foreach (var clientid in clientids)
{
var client = DataCacheProvider.GetClientDataSource().GetData(clientid);
if (client == null)
{
throw new Exception(string.Format("该交易{0},客户不存在", tradeList.Where(x => x.ClientId == clientid).FirstOrDefault().TradeNumber));
}
//A团队
var tradeListA = tradeList.Where(x => teamA.Contains(x.AssetId) && x.ClientId == clientid).ToList();
var clientSpanA = GetUnderlyingGroup(client, req, tradeListA);
//B团队
var tradeListB = tradeList.Where(x => !teamA.Contains(x.AssetId) && x.ClientId == clientid).ToList();
var clientSpanB = GetUnderlyingGroup(client, req, tradeListB);
UserGroupPVJson userGroupPVJson = new UserGroupPVJson();
userGroupPVJson.ValueA = clientSpanA.WorstCastClientPayable ?? 0;
userGroupPVJson.ValueB = clientSpanB.WorstCastClientPayable ?? 0;
var clientSpan = new ClientSpan
{
ClientId = clientid,
ValueDate = req.settleDate,
Spv1 = clientSpanA.Spv1 + clientSpanB.Spv1,
Spv2 = clientSpanA.Spv2 + clientSpanB.Spv2,
Spv3 = clientSpanA.Spv3 + clientSpanB.Spv3,
Spv4 = clientSpanA.Spv4 + clientSpanB.Spv4,
//负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = clientSpanA.WorstCastClientPayable + clientSpanB.WorstCastClientPayable,
SwapWorstCastClientPayable = clientSpanA.SwapWorstCastClientPayable + clientSpanB.SwapWorstCastClientPayable,
MySideMargin = clientSpanA.MySideMargin + clientSpanB.MySideMargin,
TwoSideMargin = clientSpanA.TwoSideMargin + clientSpanB.TwoSideMargin,
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType,
PVJsons = JsonHelper.ToJson(userGroupPVJson),
AdditionalWorstCastClientPayable = req.clientAdditionalMarginDic != null
&& req.clientAdditionalMarginDic.TryGetValue(clientid, out var dd) ? dd : 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)}={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.client_span.AddRange(clientSpanNews);
}
db.SaveChanges();
return req.tradeSpans;
}
}
public ClientSpan GetUnderlyingGroup(Client client, CalcClientMarginReq req, List<trade> tradeList)
{
using (var db = new YLContext())
{
var tradeSpanInfo = (from tradeSpan in req.tradeSpans
join trade in tradeList on tradeSpan.TradeId equals trade.id
where tradeSpan.ValueDate == req.settleDate
select new { trade, tradeSpan }).ToList();
var tradeIdList = tradeSpanInfo.Where(x => x.tradeSpan.IsSingleMargin != true).Select(x => x.tradeSpan.TradeId);
var underlyingGroup = tradeSpanInfo.Where(x => x.tradeSpan.IsSingleMargin != true).GroupBy(t => t.trade.UnderlyingId).Select(t => new ClientSpan
{
UnderlyingId = t.Key,
ClientId = client.id,
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),
SwapWorstCastClientPayable = t.Where(g => g.trade.TradeType == "收益互换").Sum(g => g.tradeSpan.WorstCastClientPayable) * (-1),
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType,
}).ToList();
foreach (var item in underlyingGroup)
{
item.WorstCastClientPayable = Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0);
item.TwoSideMargin = Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0);
#region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致
var tradeSpansUpdate = db.trade_span.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == client.id && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == client.id && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
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);
}
#endregion
}
var clientSpan = new ClientSpan
{
ClientId = client.id,
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),
//负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = underlyingGroup.Sum(g => g.WorstCastClientPayable),
SwapWorstCastClientPayable = underlyingGroup.Sum(g => g.SwapWorstCastClientPayable),
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(client.id, out var dd) ? dd : 0
};
var tradeSpanSingle = tradeSpanInfo.Where(x => x.tradeSpan.IsSingleMargin == true);
clientSpan.Spv1 += tradeSpanSingle.Sum(x => -(x.tradeSpan.Spv1 ?? 0));
clientSpan.Spv2 += tradeSpanSingle.Sum(x => -(x.tradeSpan.Spv2 ?? 0));
clientSpan.Spv3 += tradeSpanSingle.Sum(x => -(x.tradeSpan.Spv3 ?? 0));
clientSpan.Spv4 += tradeSpanSingle.Sum(x => -(x.tradeSpan.Spv4 ?? 0));
clientSpan.WorstCastClientPayable += tradeSpanSingle.Sum(x => -(x.tradeSpan.WorstCastClientPayable ?? 0));
clientSpan.SwapWorstCastClientPayable += tradeSpanSingle.Where(g => g.trade.TradeType == "收益互换").Sum(x => -(x.tradeSpan.WorstCastClientPayable ?? 0));
clientSpan.MySideMargin += tradeSpanSingle.Sum(x => -(x.tradeSpan.WorstCastClientPayable ?? 0));
clientSpan.TwoSideMargin += tradeSpanSingle.Sum(x => -(x.tradeSpan.WorstCastClientPayable ?? 0));
if (client.MarginOptionType == (int)MarginOptionEnum.单向追保)
{
clientSpan.WorstCastClientPayable = Math.Min(clientSpan.WorstCastClientPayable.Value, 0);
}
else if (client.MarginOptionType == (int)MarginOptionEnum.对手方单向追保)
{
clientSpan.WorstCastClientPayable = Math.Max(clientSpan.WorstCastClientPayable.Value, 0);
}
db.SaveChanges();
return clientSpan;
}
}
public override double GetTradeMargin(GetTradeMarginReq req)
{
var trade = req.trade;
if (trade.TradeType == "结构化交易" && (trade.SubTrades == null || trade.SubTrades.Count() == 0))
{
using (var db = new YLContext())
{ trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList(); }
}
var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq());
if (null != tradeMargin)
{
return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0;
}
return 0.0;
}
}
}