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

551 lines
27 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.BLL.Calculation;
using YLErp.BLL.Eod;
using YLErp.DBModels.Helpers;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.QdpModule;
namespace YLErp.BLL.MarginCalculation
{
/// <summary>
/// 渤海荣盛
/// </summary>
public class BHRSMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例
public static readonly BHRSMarginCalculation Instance;
static BHRSMarginCalculation()
{
Instance = new BHRSMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
private BHRSMarginCalculation()
{
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
var tradeSpans = new List<trade_span>();
if (req?.tradeList == null || !req.tradeList.Any())
{
return tradeSpans;
}
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
var tempStockTradeList = req.tradeList.Where(t => t.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock).ToList();
if (tempStockTradeList.Any())
{
var stockTradeSpanlist = StockMarginCalculation(req.Clone(tempStockTradeList), helper);
if (stockTradeSpanlist.Count > 0)
{
tradeSpans.AddRange(stockTradeSpanlist);
}
}
var tempFutureTradeList = req.tradeList.Where(t => t.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommodityFutures).ToList();
if (tempFutureTradeList.Any())
{
var tradeList = tempFutureTradeList.Where(a => a.TradeType != "远期").ToList();
if (tradeList.Any())
{
req = req.Clone(tradeList);
var futureTradeSpanlist = FutureMarginCalculation(req, helper);
if (futureTradeSpanlist.Count > 0)
{
tradeSpans.AddRange(futureTradeSpanlist);
}
}
var forwardTradeList = tempFutureTradeList.Where(a => a.TradeType == "远期").ToList();
if (forwardTradeList.Any())
{
req = req.Clone(forwardTradeList);
var futureTradeSpanlist = ForwardMarginCalculation(req, helper);
if (futureTradeSpanlist.Count > 0)
{
tradeSpans.AddRange(futureTradeSpanlist);
}
}
}
return tradeSpans;
}
/// <summary>
/// 股票类期权计算预付金
/// </summary>
private List<trade_span> StockMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var tradeSpans = new List<trade_span>();
if (req?.tradeList == null || !req.tradeList.Any())
{
return tradeSpans;
}
var marginRation = valuedateBLL.SystemDate.MarginRatio ?? 0.15;
foreach (var t in req.tradeList)
{
var client = helper.GetClient(t.ClientId);
if (client == null)
{
continue;
}
double twoSideMargin;
if (helper.GetSpecialMargin(t, 0, out var value))
{
twoSideMargin = value;
}
else
{
var clientRatio = client?.Ratio ?? 1.0;
//如果是股票去名义本金,如果是期货取:份额 * 即期价格
var baseValue = (t.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(t.Notional * t.SpotPrice, t.ParticipationRate, t.AnnualizeFactor) : t.StockEqvNotional) * marginRation;
value = baseValue * (t.BuySell == "买入" ? 1 : (PS.Config.ErpElement.TwoSideMargin && client.MarginOptionType == 1 ? -1 : 0)) * clientRatio;
twoSideMargin = baseValue * (t.BuySell == "买入" ? 1 : -1) * clientRatio;
}
tradeSpans.Add(new trade_span
{
TradeId = t.id,
OptDate = DateTime.Now,
OptId = req.userId,
OptName = req.userName,
ClientId = client.ClientId,
UnderlyingId = t.UnderlyingId,
UnderlyingCode = t.UnderlyingCode,
ValueDate = req.settleDate,
Spv1 = value,
Spv2 = value,
Spv3 = value,
Spv4 = value,
WorstCastClientPayable = value,
TwoSideMargin = twoSideMargin
});
}
return tradeSpans;
}
/// <summary>
/// 商品期权计算预付金
/// </summary>
private List<trade_span> FutureMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var futureTradeList = req.tradeList;
var tradeSpans = new List<trade_span>();
using (var db = new YLContext())
{
if (!req.hasOptionInfo)
{
tradeBLL.SetFieldsByTradeType(futureTradeList);
}
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
valueDate: req.settleDate,
tradeList: futureTradeList,
calcScenario: req.GetCalcScenario(),
priceProvider: req.PriceProvider,
pricingRequest: QdpPricingRequest.BASIC_GREEKS, addVolRateDic: null,
volType: req.volType,
settlementType: req.settlementType,
isUseTradeVol: PS.Config.IsTradeVol,
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin);
logger.Info($"商品期货预付金计算,交易入:[{futureTradeList.Count}条],返回[{tradeRiskResult.Results.Count}],错误:{tradeRiskResult.ErrorMessage}");
//计算预付金包含错误信息时弹出错误信息
if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage))
{
throw new Exception(tradeRiskResult.ErrorMessage);
}
if (tradeRiskResult.Results.Count < 1)
{
return tradeSpans;
}
var vegaCoefficient = valuedateBLL.SystemDate.FutureMarginVegaCoefficient ?? 3;
var omegaCoefficient = valuedateBLL.SystemDate.FutureMarginOmegaCoefficient ?? 2.5;
var alphaRate = valuedateBLL.SystemDate.FutureMarginAlphaRate ?? 1;
foreach (var item in tradeRiskResult.Results)
{
var tempVariety = _underlyingDataProvider.GetVariety(item.Trade.UnderlyingCode);
double closePrice = 0.0;
switch (req.CalcMarginType)
{
case CalcMarginTypeEnum.None:
case CalcMarginTypeEnum.EodMargin:
closePrice = req.PriceProvider.GetPrice(item.Trade.UnderlyingCode);
break;
case CalcMarginTypeEnum.InitialMargin:
closePrice = item.Trade.SpotPrice ?? 0;
break;
default:
break;
}
var client = helper.GetClient(item.Trade.ClientId);
if (item.ValueResult == null)
{
throw new Exception("PV结算结果为null");
}
var deltaMerge = item.ValueResult.Delta;
var vegaMerge = item.ValueResult.Vega;
var gammaMerge = item.ValueResult.Gamma;
var pvMerge = item.ValueResult.Pv;
var PositionPnl = req.CalcMarginType == CalcMarginTypeEnum.InitialMargin ? 0 :
pvMerge - (EodOperationBase.GetPositionCost(item.Trade.TradePrice ?? 0, item.Trade.Notional, item.Trade.OriginalNotional ?? 0, item.Trade.BuySell));
PositionPnl = PositionPnl > 0 ? PositionPnl : 0;
bool isTwoSide = HasTwoSideMargin(item.Trade.ClientId);
//逐笔预付金算法
var margin = MarginAlgorithm(deltaMerge, pvMerge, closePrice, isTwoSide, tempVariety.Margin ?? 0, item.Trade, PositionPnl);
if (item.Trade.TradeType == "自定义交易")
{
if (helper.GetSpecialMargin(item.Trade, 0, out var value))
{
margin = value;
}
}
tradeSpans.Add(new trade_span
{
TradeId = item.Trade.id,//默认记录为第一条交易记录中
OptDate = DateTime.Now,
OptId = req.userId,
OptName = req.userName,
ClientId = item.Trade.ClientId,
ValueDate = req.settleDate,
UnderlyingId = item.Trade.UnderlyingId,
UnderlyingCode = item.Trade.UnderlyingCode,
DeltaMargin = margin,
WorstCastClientPayable = margin,
Comment = $"delta:{deltaMerge},closePrice:{closePrice},pv:{pvMerge}" +
$",gamma{gammaMerge},交易员方向:{item.Trade.BuySell},持仓盈亏:{PositionPnl},预付金率:{tempVariety.Margin},单双向:{client.MarginOptionType}"
});
}
return tradeSpans;
}
}
private List<trade_span> ForwardMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var futureTradeList = req.tradeList;
var tradeSpans = new List<trade_span>();
using (var db = new YLContext())
{
if (!req.hasOptionInfo)
{
tradeBLL.SetFieldsByTradeType(futureTradeList);
}
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
valueDate: req.settleDate,
tradeList: futureTradeList,
calcScenario: req.GetCalcScenario(),
priceProvider: req.PriceProvider,
pricingRequest: QdpPricingRequest.BASIC_GREEKS, addVolRateDic: null,
volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol,
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin);
logger.Info($"远期预付金计算,交易入:[{futureTradeList.Count}条],返回[{tradeRiskResult.Results.Count}],错误:{tradeRiskResult.ErrorMessage}");
//计算预付金包含错误信息时弹出错误信息
if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage))
{
throw new Exception(tradeRiskResult.ErrorMessage);
}
if (tradeRiskResult.Results.Count < 1)
{
return tradeSpans;
}
var vegaCoefficient = valuedateBLL.SystemDate.FutureMarginVegaCoefficient ?? 3;
var omegaCoefficient = valuedateBLL.SystemDate.FutureMarginOmegaCoefficient ?? 2.5;
var alphaRate = valuedateBLL.SystemDate.FutureMarginAlphaRate ?? 1;
//如果持仓中的所有远期delta方向相同,那么无视delta方向,每笔远期交易都按照delta绝对值计算margin
bool IsDeltaMerge = tradeRiskResult.Results.All(a => a.ValueResult.Delta >= 0) || tradeRiskResult.Results.All(a => a.ValueResult.Delta <= 0);
var tradeIds = futureTradeList.Where(O => O.id > 0).Select(O => O.id);
var tradeCashDict =
db.trade_cash.Where(O => tradeIds.Contains(O.TradeId) && O.ValidState != "InValid" && !O.IsDeleted && O.Action != ClientCashInCashOut.系统操作_期权费)
.AsEnumerable()
.GroupBy(O => O.TradeId)
.ToDictionary(K => K.Key, V => V.ToList());
var marginCostDict = db.eod_forward_margin.Where(x => x.ValueDate == req.settleDate && tradeIds.Contains(x.TradeId))
.AsEnumerable()
.GroupBy(O => O.TradeId)
.ToDictionary(K => K.Key, V => V.FirstOrDefault()?.MarginCost ?? 0);
foreach (var item in tradeRiskResult.Results)
{
var tempVariety = _underlyingDataProvider.GetVariety(item.Trade.UnderlyingCode);
double closePrice = 0.0;
switch (req.CalcMarginType)
{
case CalcMarginTypeEnum.None:
case CalcMarginTypeEnum.EodMargin:
closePrice = req.PriceProvider.GetPrice(item.Trade.UnderlyingCode);
break;
case CalcMarginTypeEnum.InitialMargin:
closePrice = item.Trade.SpotPrice ?? 0;
break;
default:
break;
}
if (item.ValueResult == null)
{
throw new Exception("PV结算结果为null");
}
var deltaMerge = item.ValueResult.Delta;
var vegaMerge = item.ValueResult.Vega;
var gammaMerge = item.ValueResult.Gamma;
var pvMerge = item.ValueResult.Pv;
var marginCost = marginCostDict.TryGetValue(item.Trade.id, out var margincost) ? margincost : 0;
var unwindTradeCashList = tradeCashDict.TryGetValue(item.Trade.id, out var cashList) ? cashList : new List<trade_cash>();
//平仓比例
double unwindRatio = unwindTradeCashList.Any() ? unwindTradeCashList.Sum(a => a.UnwindNotional.Value) / item.Trade.OriginalNotional.Value : 0;
//持仓比例
double positionRatio = 1 - unwindRatio;
//持仓盈亏 = 持仓市值+开仓总费用 * 持仓比例
var pnl = pvMerge + item.Trade.TradePrice.Value * positionRatio + marginCost;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}持仓盈亏 = 持仓市值 + 开仓总费用 * 持仓比例");
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}PnL = {pvMerge} + {item.Trade.TradePrice.Value} * {positionRatio} + {marginCost}");
pnl = req.CalcMarginType == CalcMarginTypeEnum.InitialMargin ? 0 : Math.Max(0, pnl);
deltaMerge = IsDeltaMerge ? Math.Abs(deltaMerge) : deltaMerge;
//预付金计算公式: delta * 标的收盘价 * 交易所预付金率 + 客户亏损部分
double value = deltaMerge * closePrice * (tempVariety.Margin ?? 0) + pnl;
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金计算公式: delta * 标的收盘价 * 交易所预付金率 + 客户亏损部分");
logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {deltaMerge} * {closePrice} * {(tempVariety.Margin ?? 0)} + {pnl}");
tradeSpans.Add(new trade_span
{
TradeId = item.Trade.id,//默认记录为第一条交易记录中
OptDate = DateTime.Now,
OptId = req.userId,
OptName = req.userName,
ClientId = item.Trade.ClientId,
ValueDate = req.settleDate,
UnderlyingId = item.Trade.UnderlyingId,
UnderlyingCode = item.Trade.UnderlyingCode,
DeltaMargin = value,
WorstCastClientPayable = value,
Comment = $"远期交易,delta:{deltaMerge},closePrice:{closePrice},pv:{pvMerge}" +
$",gamma{gammaMerge},交易员方向:{item.Trade.BuySell},预付金率:{tempVariety.Margin}"
});
}
}
return 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.trade.id).ToList();
}
}
var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq());
if (null != tradeMargin)
{
return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0;
}
return 0.0;
}
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 clientGroups = req.tradeSpans.AsEnumerable().GroupBy(t => t.ClientId);
foreach (var clientGroup in clientGroups)
{
List<ClientSpan> clientSpans = new List<ClientSpan>();
var tradeSpanGroup = clientGroup.ToList();
var tradeIds = tradeSpanGroup.Select(a => a.TradeId).ToList();
var notForwardTrades = db.trade.Where(a => tradeIds.Contains(a.id) && a.TradeType != "远期").ToList();
#region 非远期
if (notForwardTrades.Any())
{
var notForwardTradeIds = notForwardTrades.Select(a => a.id);
//双向追保,客户只有买入交易 则预付金为0
if (notForwardTrades.All(a => a.BuySell == "卖出") && HasTwoSideMargin(clientGroup.Key ?? 0))
{
var tradeSpansUpdate = db.trade_span.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList();
var tradeSpansReq = req.tradeSpans.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList();
tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = 0);
tradeSpansReq.ForEach(x => x.WorstCastClientPayable = 0);
var clientSpan = new ClientSpan
{
ClientId = clientGroup.Key ?? 0,
ValueDate = req.settleDate,
DeltaMargin = 0,
WorstCastClientPayable = 0
};
clientSpans.Add(clientSpan);
}
else
{
var umTradeSpanGroup = tradeSpanGroup.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).AsEnumerable().GroupBy(a => a.UnderlyingId);
var umClientSpans = new List<ClientSpan>();
foreach (var umTrade in umTradeSpanGroup)
{
var umClientSpan = new ClientSpan
{
UnderlyingId = umTrade.Key,
ClientId = clientGroup.Key ?? 0,
ValueDate = req.settleDate,
DeltaMargin = umTrade.Sum(u => u.DeltaMargin) * (-1)
};
umClientSpans.Add(umClientSpan);
}
var notForwardClientSpan = new ClientSpan
{
ClientId = clientGroup.Key ?? 0,
ValueDate = req.settleDate,
//负数代表客户应缴预付金,正数代表客户应收预付金
DeltaMargin = umClientSpans.Sum(g => g.DeltaMargin),
WorstCastClientPayable = umClientSpans.Sum(g => g.DeltaMargin)
};
clientSpans.Add(notForwardClientSpan);
}
}
#endregion
#region 远期
var forwardTrades = db.trade.Where(a => tradeIds.Contains(a.id) && a.TradeType == "远期").ToList();
if (forwardTrades.Any())
{
var forwardTradeIds = forwardTrades.Select(a => a.id);
var forwardTradeSpans = tradeSpanGroup.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate);
var margin = Math.Abs((forwardTradeSpans.Sum(g => g.DeltaMargin) ?? 0)) * -1;
var forwardClientSpan = new ClientSpan
{
ClientId = clientGroup.Key ?? 0,
ValueDate = req.settleDate,
//负数代表客户应缴预付金,正数代表客户应收预付金
DeltaMargin = margin,
WorstCastClientPayable = margin
};
clientSpans.Add(forwardClientSpan);
var tradeSpansUpdate = db.trade_span.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList();
var tradeSpansReq = req.tradeSpans.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList();
if (margin > 0)
{
tradeSpansUpdate.ForEach(a => a.WorstCastClientPayable = 0);
tradeSpansReq.ForEach(a => a.WorstCastClientPayable = 0);
}
}
#endregion
var item = new ClientSpan
{
ClientId = clientGroup.Key ?? 0,
ValueDate = req.settleDate,
//负数代表客户应缴预付金,正数代表客户应收预付金
DeltaMargin = clientSpans.Sum(g => g.DeltaMargin),
WorstCastClientPayable = clientSpans.Sum(g => g.DeltaMargin),
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now,
SpanType = req.SpanType,
AdditionalWorstCastClientPayable = req.clientAdditionalMarginDic != null
&& req.clientAdditionalMarginDic.TryGetValue(clientGroup.Key ?? 0, out var dd) ? dd : 0
};
clientSpanNews.Add(item);
}
}
//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).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;
}
}
/// <summary>
/// 预付金算法
/// </summary>
/// <returns></returns>
public double? MarginAlgorithm(double deltaMerge, double pvMerge, double closePrice, bool isTwoSide, double Margin, trade tradeModel, double PositionPnl)
{
//交易员买入 delta为正,卖出为负
deltaMerge = tradeModel.BuySell == "买入" ? Math.Abs(deltaMerge) : -Math.Abs(deltaMerge);
var value = 0.0;
// 预付金 = delta(交易员方向delta) * 标的收盘价 * 交易所预付金率 + 持亏(交易员方向)
value = deltaMerge * closePrice * Margin + PositionPnl;
if (tradeModel.BuySell == "卖出" && !isTwoSide)//单向 客户买入
{
value = 0;
}
return value;
}
}
}