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

410 lines
24 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 YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.BLL.Eod;
using YLErp.BLL.MarginCalculation;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.BLL
{
/// <summary>
/// 浙期
/// </summary>
public class ZheQiMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例
public static readonly ZheQiMarginCalculation Instance;
static ZheQiMarginCalculation()
{
Instance = new ZheQiMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
protected ZheQiMarginCalculation()
{
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
var resultMap = new Dictionary<int, trade_span>();
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", req.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)
{
// 远期掉期是否走现货算法
bool isStock = false;
if (item.Trade.TradeType == "远期")
{
isStock = uProvider.GetUnderlying(item.Trade.UnderlyingCode).UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommoditySpot;
if (!isStock && !string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode))
{
isStock = uProvider.GetUnderlying(item.Trade.BasisUnderlyingCode).UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommoditySpot;
}
}
if (isStock && key != "normal_0")
{
continue;
}
var pv = item.ValueResult.Pv;
var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0;
if (!helper.GetSpecialMargin(item.Trade, pv, out var value, helper.req.forOtherSide))
{
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 (isStock)
{
// 现货预付金:
// 普通掉期开仓固定 15%
// 基差掉期开仓预付金固定 10%
// 持仓预付金观察:
// 1. -5%*名义本金<单笔持仓盈亏<0,
// 一般掉期持仓预付金为15%
// 基差掉期持仓预付金为10%
// 2. 单笔持仓盈亏<-5%*名义本金
// 一般掉期持仓预付金为: -单笔持仓盈亏+15%
// 基差掉期持仓预付金为: -单笔持仓盈亏+10%
var config = valuedateBLL.SystemDate.ConfigInfoData;
if (helper.req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
{
double rate = string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode) ? config.NormalSwapInitMarginRatio : config.BasisSwapInitMarginRatio;
rate /= 100;
value = rate * item.Trade.StockEqvNotional;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = 普通掉期开仓固定 15%,基差掉期开仓预付金固定 10%");
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}");
}
else
{
double rate = string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode) ? config.NormalSwapMarginRatio : config.BasisSwapMarginRatio;
rate /= 100;
var tradePrice = (item.Trade.TradePrice * item.Trade.Notional / item.Trade.OriginalNotional) ?? 0;
double pnl = item.ValueResult.Pv + tradePrice;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} pnl = Pv + tradePrice");
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} pnl = {item.ValueResult.Pv} + {tradePrice}");
// 客户持仓盈亏
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = 1. -5%*名义本金<单笔持仓盈亏<0,\r\n一般掉期持仓预付金为15%,\r\n基差掉期持仓预付金为10%\r\n2. 单笔持仓盈亏<-5%*名义本金,一般掉期持仓预付金为:\r\n-单笔持仓盈亏+15%,\r\n基差掉期持仓预付金为:\r\n-单笔持仓盈亏+10%");
double clientpnl = -pnl;
double rateStockEqvNotional = config.StockEqvNotionalRatio / 100d * item.Trade.StockEqvNotional;
if (rateStockEqvNotional < clientpnl && clientpnl < 0)
{
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} if({config.StockEqvNotionalRatio / 100d} * {item.Trade.StockEqvNotional} < {clientpnl} && {clientpnl} > 0)");
value = rate * item.Trade.StockEqvNotional;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}");
}
else if (clientpnl < rateStockEqvNotional)
{
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} if({clientpnl} < {rateStockEqvNotional} > 0)");
value = -clientpnl + rate * item.Trade.StockEqvNotional;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = -{clientpnl} + {rate} * {item.Trade.StockEqvNotional}");
}
else
{
value = rate * item.Trade.StockEqvNotional;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}");
}
}
tempTradeSpan.SetAllSpvAndWorst(value);
}
else
{
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 * clientRatio;
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.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) * (-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.tradeSpan.IsSingleMargin != true).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 = (item.DeltaMargin > 0 ? -(x.DeltaMargin ?? 0) : (x.DeltaMargin ?? 0)) + (x.PositionWinLoss ?? 0));
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = (item.DeltaMargin > 0 ? -(x.DeltaMargin ?? 0) : (x.DeltaMargin ?? 0)) + (x.PositionWinLoss ?? 0));
}
}
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.tradeSpan.IsSingleMargin == true).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;
}
}
}
}