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

698 lines
45 KiB
C#

using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.DBModels.Helpers;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.BLL.MarginCalculation
{
/// <summary>
/// 东证润和预付金计算
/// </summary>
public class DongZhengRunHeMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例
public static readonly DongZhengRunHeMarginCalculation Instance;
static DongZhengRunHeMarginCalculation()
{
Instance = new DongZhengRunHeMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
protected DongZhengRunHeMarginCalculation()
{
}
private static Dictionary<int, double> GetOverrideVols(RunMarginCalculationReq req)
{
Dictionary<int, double> overrideVols = null;
//东证润和使用日终价的波动率
if (PS.Config.Company == Configuration.CompanyEnum.润和)
{
overrideVols = new Dictionary<int, double>();
var voltype = req.volType == "对冲" || req.volType == "持仓" ? "交易" : req.volType;
var excludeTypes = new[] { ConsGlobal.TradeType.PayoffSwap, ConsGlobal.TradeType.CashFlow, ConsGlobal.TradeType.Forward };
foreach (var td in req.tradeList)
{
var underlyingVols = VolatilityHelper.GetVol(req.settleDate, voltype, td.UnderlyingCode, userGroup: null)
?? throw new Exception($"未找到'{voltype}'波动率:{td.UnderlyingCode}");
var vol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: VolConstructionType.Normal,
volSurface: underlyingVols,
valueDate: req.settleDate,
underlyingCode: td.UnderlyingCode,
exerciseDate: td.ExerciseDate.Value,
strike: td.Strike ?? 0,
isBuy: td.BuySell == "买入",
isCall: td.CallPut == "Call",
spotPrice: req.PriceProvider.GetPrice(td.UnderlyingCode),
isMoneynessOption: td.IsMoneynessOption == "是",
isEodCalc: req.CalcMarginType == CalcMarginTypeEnum.EodMargin);
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
{
overrideVols[td.id] = td.Vol ?? vol;
}
else
{
if (!excludeTypes.Contains(td.TradeType))
{
overrideVols[td.id] = vol;
}
}
}
}
return overrideVols;
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
if (req.tradeList == null || !req.tradeList.Any())
{
return new List<trade_span>();
}
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
var resultMap = new Dictionary<int, trade_span>();
//为了算客户角度的一个预付金数值
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), ("normal", req.PriceProvider) };
var overrideVols = GetOverrideVols(helper.req);
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: overrideVols, isEodCalc: req.CalcMarginType == CalcMarginTypeEnum.EodMargin);
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;
if (!helper.GetSpecialMargin(item.Trade, pv, out var value))
{
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 "down_0":
tempTradeSpan.Spv3 = value; break;
case "down_1":
tempTradeSpan.Spv4 = value; break;
case "normal_0":
tempTradeSpan.Spv5 = value; break;
}
if (contains)
{
tempTradeSpan.SetWorstCastClientPayable();
}
}
}
return resultMap.Values.ToList();
}
/// <summary>
/// 计算客户预付金
/// <para>从国君预付金那里赋值过来的</para>
/// <para>逻辑和国君保持一致,唯一区别是商品类对手方预付金,正负值都需要</para>
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
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 underlyingIdList = tradeList.Select(t => t.UnderlyingId).ToList();
var underlyingList = db.underlying_manager.AsNoTracking().Where(t => underlyingIdList.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 um in underlyingList on trade.UnderlyingId equals um.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, um.UnderlyingTypeId }).ToList();
//商品类预付金计算
if (!PS.Config.ErpElement.IsStockMargin)
{
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
join um in underlyingList on trade.UnderlyingId equals um.id
where tradeSpan.ValueDate == req.settleDate
select new { trade, tradeSpan, um.UnderlyingTypeId }).ToList();
var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId);
foreach (var clientGroup in clientGroups)
{
var varietyGroups = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).GroupBy(t => t.UnderlyingTypeId).Select(t => new ClientSpan
{
VarietyId = 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),
Spv5 = t.Sum(g => g.tradeSpan.Spv5) * (-1),
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType
}).ToList();
foreach (var item in varietyGroups)
{
item.WorstCastClientPayable = new[] { item.Spv1 ?? 0, item.Spv2 ?? 0, item.Spv3 ?? 0, item.Spv4 ?? 0, item.Spv5 ?? 0 }.Min();
item.TwoSideMargin = new[] { item.Spv1 ?? 0, item.Spv2 ?? 0, item.Spv3 ?? 0, item.Spv4 ?? 0, item.Spv5 ?? 0 }.Min();
#region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致
var tradeIdList = clientGroup.Where(x => x.UnderlyingTypeId == item.VarietyId).Select(x => x.tradeSpan.TradeId);
var tradeSpansUpdate = db.trade_span.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.ValueDate == req.settleDate).ToList();
var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && 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);
}
#endregion
}
var clientSpan = new ClientSpan
{
ClientId = clientGroup.Key,
ValueDate = req.settleDate,
Spv1 = varietyGroups.Sum(g => g.Spv1),
Spv2 = varietyGroups.Sum(g => g.Spv2),
Spv3 = varietyGroups.Sum(g => g.Spv3),
Spv4 = varietyGroups.Sum(g => g.Spv4),
Spv5 = varietyGroups.Sum(g => g.Spv5),
//负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = varietyGroups.Sum(g => g.WorstCastClientPayable),
MySideMargin = varietyGroups.Sum(g => g.WorstCastClientPayable),
TwoSideMargin = varietyGroups.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.Spv5 += singleMarginTrade.Sum(O => O.Spv5 * (-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);
}
//处理从客户角度的预付金计算(将交易买卖方向反向处理)
var clientGroupsOtherSide = tradeSpanInfoOtherSide.GroupBy(t => t.trade.ClientId);
foreach (var clientGroup in clientGroupsOtherSide)
{
var varietyGroups = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).GroupBy(t => t.UnderlyingTypeId).Select(t => new ClientSpan
{
VarietyId = 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),
Spv5 = t.Sum(g => g.tradeSpan.Spv5) * (-1),
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType
}).ToList();
foreach (var item in varietyGroups)
{
item.WorstCastClientPayable = new[] { item.Spv1 ?? 0, item.Spv2 ?? 0, item.Spv3 ?? 0, item.Spv4 ?? 0, item.Spv5 ?? 0 }.Min();
}
var clientSpan = clientSpanNews.FirstOrDefault(x => x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate);
clientSpan.OtherSideMargin = varietyGroups.Sum(g => g.WorstCastClientPayable);
var singleMarginTrade = clientGroup.Where(O => O.trade.MarginType != DBModels.Enums.MarginTypeEnum.DEFAULT).Select(O => O.tradeSpan);
clientSpan.OtherSideMargin += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1));
}
}
}
//权益类预付金计算
else
{
var defaultMarginTemplates = db.margin_template_v2.Where(x => x.IsDefault && !x.IsForClient).ToList();
var groupMarginTemplates = db.margin_template_v2.Where(x => !x.IsDefault && x.MarginType == (int)MarginTypeV2Enum.多腿).ToList();
var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId);
foreach (var clientGroup in clientGroups)
{
var worstCastClientPayable = 0.0;
var groupTradeIds = new List<int>();
#region 默认组合的交易类型的交易处理
defaultMarginTemplates.ForEach(x =>
{
var marginDetail = db.margin_template_detail.Where(y => y.MarginTemplateId == x.id && y.ValueDate <= req.settleDate).OrderByDescending(y => y.ValueDate).FirstOrDefault();
if (marginDetail == null && (x.RuleType != (int)MarginRuleTypeEnum.无预付金 && x.RuleType != (int)MarginRuleTypeEnum.自动赎回买入预付金规则 && x.RuleType != (int)MarginRuleTypeEnum.含赔付预付金计算规则 && x.RuleType != (int)MarginRuleTypeEnum.保底预付金规则))
{
throw new Exception(string.Format("[{0}]不在{1}有效期内", req.settleDate.ToString("YYYY-MM-DD"), x.Name));
}
if (x.RuleType == (int)MarginRuleTypeEnum.类香草预付金规则)
{
var groupRatio = marginDetail.GroupRatio ?? 0;
var stockEqvNotional = marginDetail.StockEqvNotional ?? 0;
var singleStockEqvNotional = marginDetail.SingleStockEqvNotional ?? 0;
var amount = marginDetail.PositionUnderlyingAmount ?? 0;
//香草期权只考虑交易员买入的交易,卖出的交易不需要考虑预付金
var tradeVanillaSpanInfo = clientGroup.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && y.trade.BuySell == "买入" && y.tradetemplate == null && y.tradeSpan.IsSingleMargin != true);
//自定义预付金
var tradeVanillaSpanInfoSingle = clientGroup.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && y.trade.BuySell == "买入" && y.tradetemplate == null && y.tradeSpan.IsSingleMargin == true);
var umdStockEqvNotional = tradeVanillaSpanInfo.GroupBy(y => y.trade.UnderlyingId).Select(t => new
{
UnderlyingId = t.Key,
StockEqvNotional = t.Sum(g => TradeHelper.GetStockEqvNotionalReal(g.trade.StockEqvNotional, g.trade.ParticipationRate, g.trade.AnnualizeFactor)),
PositionStockEqvNotional = t.Sum(g => TradeHelper.GetStockEqvNotionalReal(g.trade.StockEqvNotional, g.trade.ParticipationRate, g.trade.AnnualizeFactor) * g.tradeSpan.UnderlyingPrice / g.trade.SpotPrice)
}).ToList();
var underlyingIds = umdStockEqvNotional.Select(y => y.UnderlyingId);
var totalStockEqvNotional = umdStockEqvNotional.Sum(y => y.PositionStockEqvNotional);
if (totalStockEqvNotional >= stockEqvNotional && umdStockEqvNotional.Any(y => y.PositionStockEqvNotional >= singleStockEqvNotional) && underlyingIds.Count() >= amount)
{
//获取组合持仓名义本金和初始名义本金的较高者
var maxStockEqvNotional = Math.Max(umdStockEqvNotional.Sum(y => y.StockEqvNotional >= singleStockEqvNotional ? singleStockEqvNotional : y.StockEqvNotional), umdStockEqvNotional.Sum(y => y.PositionStockEqvNotional >= singleStockEqvNotional ? singleStockEqvNotional : y.PositionStockEqvNotional ?? 0));
worstCastClientPayable += maxStockEqvNotional * groupRatio + tradeVanillaSpanInfo.Sum(y => y.tradeSpan.PositionWin ?? 0);
}
else
{
worstCastClientPayable += tradeVanillaSpanInfo.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
//自定义预付金处理的交易
worstCastClientPayable += tradeVanillaSpanInfoSingle.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
else if (x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则1 && x.DefaultParameterType == (int)DefaultMarginParameterTypeEnum.个股)
{
//排除指数类交易
var clientGroupWithoutIndex = clientGroup.Where(y => !(y.trade.UnderlyingCode.StartsWith("000") && y.trade.UnderlyingCode.EndsWith(".SH") || y.trade.UnderlyingCode.StartsWith("39") || y.trade.UnderlyingCode.StartsWith("IF") || y.trade.UnderlyingCode.StartsWith("IH") || y.trade.UnderlyingCode.StartsWith("IC")) && y.tradetemplate == null);
//初始预付金率
var redeemRatio = marginDetail.MarginRatio1 ?? 0;
//持仓预付金率
var redeemRatio1 = marginDetail.MarginRatio2 ?? 0;
var redeemPriceLimit = marginDetail.RedeemPriceLimit ?? 0;
var redeemAmount = marginDetail.PositionUnderlyingAmount ?? 0;
var tradeSpanInfoGroupSingle = clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && (y.trade.BuySell == "卖出" && y.tradeSpan.IsSingleMargin == true || y.trade.BuySell == "买入"));
var tradeSpanInfoGroup = clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && y.trade.BuySell == "卖出" && y.tradeSpan.IsSingleMargin != true);
if (tradeSpanInfoGroup.Select(y => y.trade.UnderlyingId).Distinct().Count() >= redeemAmount)
{
var groupSpotPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.trade.SpotPrice * y.trade.Notional) ?? 0);
var groupPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.tradeSpan.UnderlyingPrice * y.trade.Notional) ?? 0);
var groupStockEqvNotional = tradeSpanInfoGroup.Sum(y => TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor));
if (groupPriceWithNotional / groupSpotPriceWithNotional <= (1 - redeemPriceLimit))
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio1;
worstCastClientPayable += groupStockEqvNotional * (1 - groupPriceWithNotional / groupSpotPriceWithNotional);
}
else
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
//卖出的保本雪球,指数类的凤凰雪球需要单独计算预付金,买入的期权,进行累加
//以及自定义预付金处理的交易
worstCastClientPayable += tradeSpanInfoGroupSingle.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
else
{
worstCastClientPayable += clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType)).Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
}
else if (x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则2 && x.DefaultParameterType == (int)DefaultMarginParameterTypeEnum.个股)
{
//排除指数类交易
var clientGroupWithoutIndex = clientGroup.Where(y => !(y.trade.UnderlyingCode.StartsWith("000") && y.trade.UnderlyingCode.EndsWith(".SH") || y.trade.UnderlyingCode.StartsWith("39") || y.trade.UnderlyingCode.StartsWith("IF") || y.trade.UnderlyingCode.StartsWith("IH") || y.trade.UnderlyingCode.StartsWith("IC")) && y.tradetemplate == null);
//初始预付金率
var redeemRatio = marginDetail.MarginRatio1 ?? 0;
//持仓预付金率
var redeemRatio1 = marginDetail.MarginRatio2 ?? 0;
var redeemPriceLimit = marginDetail.RedeemPriceLimit ?? 0;
var redeemAmount = marginDetail.PositionUnderlyingAmount ?? 0;
var tradeSpanInfoGroupSingle = clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && (y.trade.BuySell == "卖出" && y.tradeSpan.IsSingleMargin == true || y.trade.BuySell == "买入"));
var tradeSpanInfoGroup = clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && y.trade.BuySell == "卖出" && y.tradeSpan.IsSingleMargin != true);
if (tradeSpanInfoGroup.Select(y => y.trade.UnderlyingId).Distinct().Count() >= redeemAmount)
{
var groupSpotPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.trade.SpotPrice * y.trade.Notional) ?? 0);
var groupPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.tradeSpan.UnderlyingPrice * y.trade.Notional) ?? 0);
var groupStockEqvNotional = tradeSpanInfoGroup.Sum(y => TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor));
var groupStockEqvNotionalNow = tradeSpanInfoGroup.Sum(y => (TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor) * y.tradeSpan.UnderlyingPrice / y.trade.SpotPrice) ?? 0);
if (groupPriceWithNotional / groupSpotPriceWithNotional <= (1 - redeemPriceLimit))
{
worstCastClientPayable += groupStockEqvNotionalNow * redeemRatio1;
worstCastClientPayable += groupStockEqvNotional * (1 - groupPriceWithNotional / groupSpotPriceWithNotional);
}
else
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
//卖出的保本雪球,指数类的凤凰雪球需要单独计算预付金,买入的期权,进行累加
//以及自定义预付金处理的交易
worstCastClientPayable += tradeSpanInfoGroupSingle.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
else
{
worstCastClientPayable += clientGroupWithoutIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType)).Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
}
else if ((x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则1 || x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则2) && x.DefaultParameterType == (int)DefaultMarginParameterTypeEnum.指数)
{
//指数类交易
var clientGroupIndex = clientGroup.Where(y => (y.trade.UnderlyingCode.StartsWith("000") && y.trade.UnderlyingCode.EndsWith(".SH") || y.trade.UnderlyingCode.StartsWith("39") || y.trade.UnderlyingCode.StartsWith("IF") || y.trade.UnderlyingCode.StartsWith("IH") || y.trade.UnderlyingCode.StartsWith("IC")) && y.tradetemplate == null);
worstCastClientPayable += clientGroupIndex.Where(y => x.TradeTypes.Contains(y.trade.TradeType)).Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
else
{
worstCastClientPayable += clientGroup.Where(y => x.TradeTypes.Contains(y.trade.TradeType) && y.tradetemplate == null).Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
});
#endregion
#region 自定义组合预付金规则
groupMarginTemplates.ForEach(x =>
{
var marginDetail = db.margin_template_detail.Where(y => y.MarginTemplateId == x.id && y.ValueDate <= req.settleDate).OrderByDescending(y => y.ValueDate).FirstOrDefault();
if (marginDetail == null)
{
throw new Exception(string.Format("[{0}]不在{1}有效期内", req.settleDate.ToString("YYYY-MM-DD"), x.Name));
}
//非自定义预付金的交易
var tradeSpanInfoGroup = clientGroup.Where(y => y.tradetemplate != null && y.tradetemplate.MarginTemplateId == x.id && y.tradeSpan.IsSingleMargin != true);
//自定义预付金的交易
var tradeSpanInfoGroupSingle = clientGroup.Where(y => y.tradetemplate != null && y.tradetemplate.MarginTemplateId == x.id && y.tradeSpan.IsSingleMargin == true);
groupTradeIds.AddRange(tradeSpanInfoGroup.Select(y => y.trade.id));
groupTradeIds.AddRange(tradeSpanInfoGroupSingle.Select(y => y.trade.id));
if (x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则1)
{
//初始预付金率
var redeemRatio = marginDetail.MarginRatio1 ?? 0;
//持仓预付金率
var redeemRatio1 = marginDetail.MarginRatio2 ?? 0;
var redeemPriceLimit = marginDetail.RedeemPriceLimit ?? 0;
var groupSpotPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.trade.SpotPrice * y.trade.Notional) ?? 0);
var groupPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.tradeSpan.UnderlyingPrice * y.trade.Notional) ?? 0);
var groupStockEqvNotional = tradeSpanInfoGroup.Sum(y => TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor));
if (groupPriceWithNotional / groupSpotPriceWithNotional <= (1 - redeemPriceLimit))
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio1;
worstCastClientPayable += groupStockEqvNotional * (1 - groupPriceWithNotional / groupSpotPriceWithNotional);
}
else
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
}
else if (x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则2)
{
//初始预付金率
var redeemRatio = marginDetail.MarginRatio1 ?? 0;
//持仓预付金率
var redeemRatio1 = marginDetail.MarginRatio2 ?? 0;
var redeemPriceLimit = marginDetail.RedeemPriceLimit ?? 0;
var groupSpotPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.trade.SpotPrice * y.trade.Notional) ?? 0);
var groupPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.tradeSpan.UnderlyingPrice * y.trade.Notional) ?? 0);
var groupStockEqvNotional = tradeSpanInfoGroup.Sum(y => TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor));
var groupStockEqvNotionalNow = tradeSpanInfoGroup.Sum(y => (TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor) * y.tradeSpan.UnderlyingPrice / y.trade.SpotPrice) ?? 0);
if (groupPriceWithNotional / groupSpotPriceWithNotional <= (1 - redeemPriceLimit))
{
worstCastClientPayable += groupStockEqvNotionalNow * redeemRatio1;
worstCastClientPayable += groupStockEqvNotional * (1 - groupPriceWithNotional / groupSpotPriceWithNotional);
}
else
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
}
else if (x.RuleType == (int)MarginRuleTypeEnum.触发条件预付金规则3)
{
//预付金比例
var redeemRatio = marginDetail.MarginRatio1 ?? 0;
//追加比例
var redeemRatio1 = marginDetail.MarginRatio2 ?? 0;
var redeemPriceLimit = marginDetail.RedeemPriceLimit ?? 0;
var groupSpotPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.trade.SpotPrice * y.trade.Notional) ?? 0);
var groupPriceWithNotional = tradeSpanInfoGroup.Sum(y => (y.tradeSpan.UnderlyingPrice * y.trade.Notional) ?? 0);
var groupStockEqvNotional = tradeSpanInfoGroup.Sum(y => TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor));
var groupStockEqvNotionalNow = tradeSpanInfoGroup.Sum(y => (TradeHelper.GetStockEqvNotionalReal(y.trade.StockEqvNotional, y.trade.ParticipationRate, y.trade.AnnualizeFactor) * y.tradeSpan.UnderlyingPrice / y.trade.SpotPrice) ?? 0);
var times = 0.0;
if (marginDetail.MarginRatio2 > 0)
{
if (marginDetail.PriceLimitType == 0)
{
times = Math.Floor(((1 - groupPriceWithNotional / groupSpotPriceWithNotional) - redeemPriceLimit) / marginDetail.MarginRatio2.Value);
}
else
{
times = Math.Floor(((groupPriceWithNotional / groupSpotPriceWithNotional - 1) - redeemPriceLimit) / marginDetail.MarginRatio2.Value);
}
}
if (marginDetail.UsePositionStockEqvNotional)
{
if (times >= 1)
{
//满足追加比例时,持仓名本分段式处理
var rateTemp = 0.0;
if (marginDetail.PriceLimitType == 0)
{
rateTemp = 1 - (redeemPriceLimit + marginDetail.MarginRatio2.Value * times);
}
else
{
rateTemp = 1 + (redeemPriceLimit + marginDetail.MarginRatio2.Value * times);
}
worstCastClientPayable += groupStockEqvNotional * rateTemp * redeemRatio;
}
else
{
//不满足追加比例时,用初始预付金
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
}
else
{
worstCastClientPayable += groupStockEqvNotional * redeemRatio;
}
if (groupSpotPriceWithNotional > 0 && marginDetail.MarginRatio2 > 0 && groupPriceWithNotional / groupSpotPriceWithNotional <= (1 - redeemPriceLimit) && marginDetail.PriceLimitType == 0 || groupPriceWithNotional / groupSpotPriceWithNotional >= (1 + redeemPriceLimit) && marginDetail.PriceLimitType == 1)
{
worstCastClientPayable += groupStockEqvNotional * marginDetail.MarginRatio2.Value * times;
}
}
else
{
worstCastClientPayable += tradeSpanInfoGroup.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
}
//自定义预付金处理的交易
worstCastClientPayable += tradeSpanInfoGroupSingle.Sum(y => y.tradeSpan.WorstCastClientPayable ?? 0);
});
#endregion
//自定义单腿规则的预付金加总
worstCastClientPayable += clientGroup.Where(x => x.tradetemplate != null && !groupTradeIds.Contains(x.trade.id)).Sum(x => x.tradeSpan.WorstCastClientPayable ?? 0);
var clientSpan = new ClientSpan
{
ClientId = clientGroup.Key,
ValueDate = req.settleDate,
//负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = -worstCastClientPayable,
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType
};
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;
}
}
}
}