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
{
///
/// 渤海荣盛
///
public class BHRSMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例
public static readonly BHRSMarginCalculation Instance;
static BHRSMarginCalculation()
{
Instance = new BHRSMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
private BHRSMarginCalculation()
{
}
public override List RunMarginCalculation(RunMarginCalculationReq req)
{
var tradeSpans = new List();
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;
}
///
/// 股票类期权计算预付金
///
private List StockMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var tradeSpans = new List();
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;
}
///
/// 商品期权计算预付金
///
private List FutureMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var futureTradeList = req.tradeList;
var tradeSpans = new List();
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 ForwardMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper)
{
var futureTradeList = req.tradeList;
var tradeSpans = new List();
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();
//平仓比例
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 CalcClientMargin(CalcClientMarginReq req)
{
var clientSpanNews = new List();
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 clientSpans = new List();
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();
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($"{nameof(ClientSpan.ClientId)} in @ids", new { ids = req.RefreshClientIds });
}
else
{
db.BulkDelete($"{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(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(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 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;
}
}
}