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

480 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.EntityFrameworkCore;
using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model.Enum;
using YLErp.Modules;
using YLErp.Modules.MarginModule;
using YLErp.QdpModule;
namespace YLErp.BLL.MarginCalculation
{
/// <summary>
/// 海通预付金计算
/// </summary>
public class HaiTongMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例
public static readonly HaiTongMarginCalculation Instance;
static HaiTongMarginCalculation()
{
Instance = new HaiTongMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
protected HaiTongMarginCalculation()
{
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
var resultMap = new List<trade_span>();
var list2 = new List<trade>(req.tradeList.Count);
using (var db = new YLContext())
{
var tradeIds = req.tradeList.Select(x => x.id);
var tradeMarginTemplates = db.trade_margin_template.Where(x => tradeIds.Contains(x.TradeId)).ToList();
foreach (var td in req.tradeList)
{
//交易员买客户卖的雪球,就用span算法;交易员卖客户买,就用文档里的公式
if (td.TradeType == "雪球期权" && td.BuySell == "卖出")
{
double value = 0;
var actualStrike = td.IsMoneynessOptionData ? td.Strike : td.Strike / td.SpotPrice;
var SpreadStrike = td.IsMoneynessOptionData ? td.trade_snowball.SpreadStrikeAtMaturity : td.trade_snowball.SpreadStrikeAtMaturity / td.SpotPrice;
//雪球期权 - 保底雪球(仅收初始预付金,后续不追保):逐笔交易持仓预付金=簿记预付金模板选为“雪球保底固定比例”;
//雪球期权 - 非保底雪球(不收初始预付金,后续线性追保,跌多少追多少):逐笔交易持仓预付金 = max(1 - 标的每日收盘价 / 期初价格,0)*名义本金;
//雪球期权 - 其他(系统定价类型):逐笔交易持仓预付金 = Span涨跌停行情下的雪球持仓市值 * 1.1。
if (td.trade_snowball.KIPayoffType == KIPayoffTypeEnum.ToCallSpreadOption)
{
value = ((td.StockEqvNotional) * (SpreadStrike - actualStrike)) ?? 0;
}
else if (td.trade_snowball.KIPayoffType == KIPayoffTypeEnum.ToPutSpreadOption)
{
value = ((td.StockEqvNotional) * (actualStrike - SpreadStrike)) ?? 0;
}
else
{
if (req.CalcMarginType != CalcMarginTypeEnum.InitialMargin)
{
var closePrice = req.PriceProvider.GetPrice(td.UnderlyingCode);
value = Math.Max(1 - closePrice / (double)td.SpotPrice, 0) * td.StockEqvNotional + td.InitialMargin ?? 0;
}
}
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
var client = helper.GetClient(td.ClientId);
var ts = new trade_span()
{
TradeId = td.id,
ClientId = td.ClientId,
ValueDate = req.settleDate,
UnderlyingId = td.UnderlyingId,
UnderlyingCode = td.UnderlyingCode,
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
IsSingleMargin = true,
Comment = "雪球特殊算法",
};
if (client != null)
{
if (client.MarginOptionType == (int)MarginOptionEnum.对手方单向追保 || client.MarginOptionType == (int)MarginOptionEnum.其他)
{
value = 0;
}
else
{
value = Math.Max((double)value, 0);
}
}
ts.SetAllSpvAndWorst((double)value);
resultMap.Add(ts);
}
else
{
list2.Add(td);
}
}
resultMap.AddRange(marginCalculation(req.Clone(list2)));
return resultMap;
}
}
private List<trade_span> marginCalculation(RunMarginCalculationReq req)
{
//结果集
var resultMap = new Dictionary<int, trade_span>();
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
var mpProvider = helper.GetMarginParamProvider(MarginParamTypeEnum.MarginRate | MarginParamTypeEnum.UpDownLimit);
//为了算客户角度的一个预付金数值
helper.ReverseTradeSide();
helper.SetFieldsByTradeType();
helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices);
helper.GetUpDownVolRateDic(out var tradeVolRateDicUp, out var tradeVolRateDicDown);
var prices = new (string, IPriceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices), ("normal", req.PriceProvider) };
var volRates = new (int, Dictionary<int, double>)[] {
(0, null),
(1, tradeVolRateDicUp),
(2, tradeVolRateDicDown)
};
foreach (var price in prices)
{
foreach (var itemDic in volRates)
{
var key = $"{price.Item1}_{itemDic.Item1}";
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
valueDate: req.settleDate,
tradeList: req.tradeList,
calcScenario: req.GetCalcScenario(),
priceProvider: price.Item2,
pricingRequest: QdpPricingRequest.PV_ONLY,
addVolRateDic: itemDic.Item2,
volType: req.volType,
isUseTradeVol: PS.Config.IsTradeVol,
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin,
isAddVolPercent: false);
if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1)
{
continue;
}
foreach (var item in tradeRiskResult.Results)
{
var client = helper.GetClient(item.Trade.ClientId);
var clientRatio = client?.Ratio ?? 1.0;
var clientRatio1 = client?.Ratio1 ?? 1.0;
var contains = resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan);
var pv = item.ValueResult.Pv;
if (!helper.GetSpecialMargin(item.Trade, pv, out var value))
{
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
{
value = double.IsNaN(pv) ? 0 : pv * clientRatio1;
}
else
{
value = double.IsNaN(pv) ? 0 : pv * clientRatio;
}
}
if (!contains)
{
resultMap[item.Trade.id] = tempTradeSpan = helper.CreateTradeSpan(item.Trade);
if (item.Trade.TradeType == "雪球期权" && item.Trade.BuySell == "买入")
{
resultMap[item.Trade.id].IsSingleMargin = true;
resultMap[item.Trade.id].Comment = "雪球买入";
}
}
switch (key)
{
case "up_1":
tempTradeSpan.Spv1 = value; break;
case "up_0":
tempTradeSpan.Spv2 = value; break;
case "up_2":
tempTradeSpan.Spv3 = value; break;
case "normal_0":
tempTradeSpan.Delta = item.ValueResult.Delta;
tempTradeSpan.UnderlyingPrice = req.PriceProvider.GetPrice(item.ValueResult.UnderlyingCode);
mpProvider.TryGetMarginRate(item.ValueResult.UnderlyingCode, out var marginRate);
tempTradeSpan.DeltaMargin = (tempTradeSpan.Delta * tempTradeSpan.UnderlyingPrice * marginRate) ?? 0;
break;
case "normal_1":
tempTradeSpan.Spv4 = value; break;
case "normal_2":
tempTradeSpan.Spv5 = value; break;
case "down_1":
tempTradeSpan.Spv6 = value; break;
case "down_0":
tempTradeSpan.Spv7 = value; break;
case "down_2":
tempTradeSpan.Spv8 = value; break;
}
if (contains)
{
tempTradeSpan.SetWorstCastClientPayable();
if (client != null)
{
var value2 = (double)tempTradeSpan.WorstCastClientPayable;
if (client.MarginOptionType == (int)MarginOptionEnum.单向追保)
{
value2 = item.Trade.BuySell == "卖出" ? 0 : Math.Max(value2, 0);
}
else if (client.MarginOptionType == (int)MarginOptionEnum.对手方单向追保 || client.MarginOptionType == (int)MarginOptionEnum.其他)
{
value2 = 0;
}
else if (client.MarginOptionType == (int)MarginOptionEnum.双向追保)
{
value2 = Math.Max(value2, 0);
}
tempTradeSpan.WorstCastClientPayable = value2;
}
}
}
}
}
return resultMap.Values.ToList();
}
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 tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList();
var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList();
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();
//海通预付金保底收益率-用来计算名义本金
double GuaranteedIncome = (db.valuedate.FirstOrDefault().GuaranteedIncome ?? 100) / 100;
var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId);
foreach (var clientGroup in clientGroups)
{
var client = DataCacheProvider.GetClientDataSource().GetData(clientGroup.Key);
var clientLevel = DataCacheProvider.GetClientLevelDataSource().GetData(client.LevelId ?? 0);
var clientRatio = clientLevel?.Ratio ?? 1.0;
var underlyingGroup = clientGroup.Where(x => x.tradeSpan.IsSingleMargin != true).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),
Spv2 = -t.Sum(g => g.tradeSpan.Spv2),
Spv3 = -t.Sum(g => g.tradeSpan.Spv3),
Spv4 = -t.Sum(g => g.tradeSpan.Spv4),
Spv5 = -t.Sum(g => g.tradeSpan.Spv5),
Spv6 = -t.Sum(g => g.tradeSpan.Spv6),
Spv7 = -t.Sum(g => g.tradeSpan.Spv7),
Spv8 = -t.Sum(g => g.tradeSpan.Spv8),
WorstCastClientPayable = -t.Sum(g => g.tradeSpan.WorstCastClientPayable),
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType
}).ToList();
foreach (var item in underlyingGroup)
{
if (client.MarginOptionType == (int)MarginOptionEnum.单向追保 || client.MarginOptionType == (int)MarginOptionEnum.双向追保)
{
if (clientGroup.Where(m => m.trade.UnderlyingId == item.UnderlyingId && m.trade.ClientId == item.ClientId).Count() > 1)
{
item.SetWorstCastClientPayableMin();
#region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致
var tradeIdList = clientGroup.Select(x => x.tradeSpan.TradeId);
var tradeSpansUpdate = db.trade_span.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && 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);
}
else if (item.WorstCastClientPayable == item.Spv5)
{
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv5);
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv5);
}
else if (item.WorstCastClientPayable == item.Spv6)
{
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv6);
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv6);
}
else if (item.WorstCastClientPayable == item.Spv7)
{
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv7);
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv7);
}
else if (item.WorstCastClientPayable == item.Spv8)
{
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv8);
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv8);
}
#endregion
}
else
{
var tradeSpansUpdate = db.trade_span.Where(x => x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList();
tradeSpansUpdate.ForEach(x => x.Comment = "单笔计算");
}
//客户信息中追保方向为“单向追保”或“双向追保”的客户: call 看涨
//(1)Call净名义本金 = 客户Call卖方持仓名义本金 - 客户Call买方持仓名义本金;
//(2)Put净名义本金 = 客户Put卖方持仓名义本金 - 客户Put买方持仓名义本金;
//(3)单品种预付金占用 = max(0, max(∑逐笔交易持仓预付金,(Call净名义本金 + Put净名义本金)*1 % *1.1)
//(4)预付金占用 =∑单品种预付金占用。
//客户信息追保方向“对手方单向追保”或“其他”的客户:预付金占用 = 0。
var callsale = clientGroup.Where(m => m.trade.UnderlyingId == item.UnderlyingId && m.trade.BuySell == "买入" && m.trade.OptionType == "看涨").Select(m => m.trade.StockEqvNotional).Sum();
var callbuy = clientGroup.Where(m => m.trade.UnderlyingId == item.UnderlyingId && m.trade.BuySell == "卖出" && m.trade.OptionType == "看涨").Select(m => m.trade.StockEqvNotional).Sum();
var putsale = clientGroup.Where(m => m.trade.UnderlyingId == item.UnderlyingId && m.trade.BuySell == "买入" && m.trade.OptionType == "看跌").Select(m => m.trade.StockEqvNotional).Sum();
var putbuy = clientGroup.Where(m => m.trade.UnderlyingId == item.UnderlyingId && m.trade.BuySell == "卖出" && m.trade.OptionType == "看跌").Select(m => m.trade.StockEqvNotional).Sum();
var StockEqvNotional = (double)((callsale - callbuy + putsale - putbuy) * GuaranteedIncome * clientRatio);
if (client.MarginOptionType != (int)MarginOptionEnum.双向追保)
{
item.WorstCastClientPayable = -Math.Max(0, Math.Max(-(double)item.WorstCastClientPayable, StockEqvNotional));
}
}
else
{
item.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),
Spv5 = underlyingGroup.Sum(g => g.Spv5),
Spv6 = underlyingGroup.Sum(g => g.Spv6),
Spv7 = underlyingGroup.Sum(g => g.Spv7),
Spv8 = underlyingGroup.Sum(g => g.Spv8),
//负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = underlyingGroup.Sum(g => g.WorstCastClientPayable),
DeltaMargin = tradeSpanInfo.Where(g => g.trade.ClientId == clientGroup.Key).Sum(g => g.tradeSpan.DeltaMargin * -1),
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
};
#region 雪球期权单独处理
var tradeSpanSingle = clientGroup.Where(x => x.tradeSpan.IsSingleMargin == true);
clientSpan.WorstCastClientPayable += tradeSpanSingle.Sum(x => -(x.tradeSpan.WorstCastClientPayable ?? 0));
#endregion
clientSpanNews.Add(clientSpan);
}
}
//span类型为实时删除所有实时计算的交易的预付金信息
if (req.SpanType == ClientSpan.SpanType_RealTime)
{
//var clientIds = clientSpanNews.Select(t => t.ClientId).Distinct().ToList();
//var clientSpanOlds = db.client_span.Where(t => t.SpanType == SpanType);
if (req.RefreshClientIds != null)
{
//clientSpanOlds = db.client_span.Where(t => RefreshClientIds.Contains(t.ClientId ?? 0));
db.BulkDelete<ClientSpan>($"{nameof(ClientSpan.ClientId)} in @ids", new { ids = req.RefreshClientIds });
}
else
{
db.BulkDelete<ClientSpan>($"{nameof(ClientSpan.SpanType)}=@SpanType", new { req.SpanType });
}
//MySqlBulkExtensions.BulkDelete(db, clientSpanOlds);
}
else
{
if(req.ClientIds !=null)
{
}
//var clientSpanOldsWithOutFlag = db.client_span.Where(t => t.ValueDate == settleDate && t.SpanType == SpanType && !t.ModifiedFlag).ToList();
//MySqlBulkExtensions.BulkDelete(db, clientSpanOldsWithOutFlag);
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).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)
{
var trade = req.trade;
using (var db = new YLContext())
{
if (trade.TradeType == "结构化交易")
{
trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList();
}
}
var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq());
if (null != tradeMargin && tradeMargin.FirstOrDefault() != null)
{
var margin = tradeMargin.FirstOrDefault().WorstCastClientPayable ?? 0.0;
return margin;
}
return 0.0;
}
}
}