321 lines
14 KiB
C#
321 lines
14 KiB
C#
using YLErp.Abstract.DataProviders;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.Enums;
|
|
using YLErp.Helpers;
|
|
using YLErp.Model.Enum;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.BLL.MarginCalculation
|
|
{
|
|
/// <summary>
|
|
/// 伴兴预付金计算
|
|
/// </summary>
|
|
public class BXMarginCalculation : MarginCalculationBase
|
|
{
|
|
// 定义一个静态变量来保存类的实例
|
|
public static readonly BXMarginCalculation Instance;
|
|
|
|
static BXMarginCalculation()
|
|
{
|
|
Instance = new BXMarginCalculation();
|
|
}
|
|
|
|
// 定义私有构造函数,使外界不能创建该类实例
|
|
protected BXMarginCalculation()
|
|
{
|
|
}
|
|
|
|
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
|
|
{
|
|
var resultMap = new List<trade_span>();
|
|
|
|
var list2 = new List<trade>(req.tradeList.Count);
|
|
|
|
foreach (var td in req.tradeList)
|
|
{
|
|
if (td.InitialMargin != null && td.CalcFlag == (int)CalcFlagEnum.IgnoreMarginCalc)
|
|
{
|
|
var initMargin = td.InitialMargin * (td.Notional / td.OriginalNotional);
|
|
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,
|
|
Spv1 = initMargin,
|
|
Spv2 = initMargin,
|
|
Spv3 = initMargin,
|
|
Spv4 = initMargin,
|
|
};
|
|
ts.SetWorstCastClientPayable();
|
|
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);
|
|
|
|
helper.SetFieldsByTradeType();
|
|
|
|
using (var db = new YLContext())
|
|
{
|
|
helper.GetTradVolRateDic(out var tradeVolRateDic, t =>
|
|
{
|
|
var pclass = helper.GetClient(t.ClientId)?.ProperClientClass;
|
|
return pclass != null && pclass.Contains("普通投资者") ? 0.02 : 0;
|
|
});
|
|
|
|
helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices);
|
|
|
|
|
|
var prices = new (string, IPriceProvider)[] {
|
|
("up", upLimitPrices),
|
|
("down", downLimitPrices)
|
|
};
|
|
|
|
foreach (var price in prices)
|
|
{
|
|
foreach (var addVolRateDic in new[] { null, tradeVolRateDic })
|
|
{
|
|
var key = $"{price.Item1}_{(addVolRateDic == null ? 0 : 1)}";
|
|
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
|
|
valueDate: req.settleDate,
|
|
calcScenario: req.GetCalcScenario(),
|
|
tradeList: req.tradeList,
|
|
priceProvider: price.Item2,
|
|
pricingRequest: QdpPricingRequest.PV_ONLY,
|
|
addVolRateDic: addVolRateDic,
|
|
volType: req.volType,
|
|
isUseTradeVol: PS.Config.IsTradeVol,
|
|
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin,
|
|
isAddVolPercent: false);
|
|
|
|
if (tradeRiskResult.Results != null && tradeRiskResult.Results.Count > 0)
|
|
{
|
|
foreach (var item in tradeRiskResult.Results)
|
|
{
|
|
var client = helper.GetClient(item.Trade);
|
|
var clientRatio = client?.Ratio ?? 1.0;
|
|
|
|
var pv = item.ValueResult.Pv;
|
|
|
|
//if (item.Trade.TradeType == "收益互换")
|
|
//{
|
|
// var getclosePrice = req.PriceProvider.GetPrice(item.Trade.trade_swap.GetUnderlyingCode);
|
|
// UpdownLimit getprice = helper.GetUpDownLimit(item.Trade.trade_swap.GetUnderlyingCode, getclosePrice);
|
|
|
|
// var payclosePrice = req.PriceProvider.GetPrice(item.Trade.trade_swap.PayUnderlyingCode);
|
|
// UpdownLimit payprice = helper.GetUpDownLimit(item.Trade.trade_swap.PayUnderlyingCode, payclosePrice);
|
|
|
|
// TradeValueResult optionValue = null;
|
|
|
|
// if (key == "up_0")
|
|
// {
|
|
// var priceProvidr = new ManualPriceProvider();
|
|
// priceProvidr.SetPrice(item.Trade.trade_swap.GetUnderlyingCode, getprice.UpLimitPrice);
|
|
// priceProvidr.SetPrice(item.Trade.trade_swap.PayUnderlyingCode, payprice.UpLimitPrice);
|
|
|
|
// optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, null, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin);
|
|
|
|
// optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, priceProvidr, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin);
|
|
// }
|
|
// else if (key == "down_0")
|
|
// {
|
|
// var priceProvidr = new ManualPriceProvider();
|
|
// priceProvidr.SetPrice(item.Trade.trade_swap.GetUnderlyingCode, getprice.DownLimitPrice);
|
|
// priceProvidr.SetPrice(item.Trade.trade_swap.PayUnderlyingCode, payprice.DownLimitPrice);
|
|
// optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, priceProvidr, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin);
|
|
// }
|
|
// pv = optionValue == null ? 0 : optionValue.Pv;
|
|
//}
|
|
|
|
if (!helper.GetSpecialMargin(item.Trade, pv, out var value))
|
|
{
|
|
if (item.Trade.CalcFlag == (int)CalcFlagEnum.IgnoreMarginCalc)
|
|
{
|
|
value = item.Trade.InitialMargin ?? 0;
|
|
}
|
|
else
|
|
{
|
|
value = GetMargin(item.Trade, item.ValueResult, 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;
|
|
}
|
|
|
|
if (contains)
|
|
{
|
|
tempTradeSpan.SetWorstCastClientPayable();
|
|
|
|
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.对手方单向追保)
|
|
{
|
|
value2 = item.Trade.BuySell == "卖出" ? Math.Min(value2, 0) : 0;
|
|
}
|
|
tempTradeSpan.WorstCastClientPayable = value2;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return resultMap.Values.ToList();
|
|
}
|
|
}
|
|
|
|
private double GetMargin(trade trade, TradeValueResult valueResult, double clientRatio)
|
|
{
|
|
var value = 0.0;
|
|
|
|
//预付金不再传入结构化主交易数据
|
|
//if (trade.TradeType == "结构化交易")
|
|
//{
|
|
// if (trade.StructureType != null && trade.StructureType.Contains("跨式"))
|
|
// {
|
|
// value = valueResult.MaxAbsPv;
|
|
// }
|
|
// else
|
|
// {
|
|
// value = valueResult.SellPv;
|
|
// }
|
|
//}
|
|
|
|
if (trade.TradeType != "自定义交易")
|
|
{
|
|
value = valueResult.Pv;
|
|
}
|
|
|
|
return (double.IsNaN(value) ? 0 : value) * clientRatio;
|
|
}
|
|
|
|
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.GroupBy(t => t.ClientId);
|
|
foreach (var clientGroup in clientGroups)
|
|
{
|
|
var clientSpan = new ClientSpan
|
|
{
|
|
ClientId = clientGroup.Key ?? 0,
|
|
ValueDate = req.settleDate,
|
|
//负数代表客户应缴预付金,正数代表客户应收预付金
|
|
Spv1 = -clientGroup.Sum(g => g.Spv1),
|
|
Spv2 = -clientGroup.Sum(g => g.Spv2),
|
|
Spv3 = -clientGroup.Sum(g => g.Spv3),
|
|
Spv4 = -clientGroup.Sum(g => g.Spv4),
|
|
WorstCastClientPayable = -clientGroup.Sum(g => g.WorstCastClientPayable),
|
|
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(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).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;
|
|
|
|
if (trade.TradeType == "结构化交易" && trade.id > 0)
|
|
{
|
|
using (var db = new YLContext())
|
|
{
|
|
trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList();
|
|
}
|
|
}
|
|
|
|
var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq());
|
|
if (null != tradeMargin)
|
|
{
|
|
return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0;
|
|
}
|
|
return 0.0;
|
|
}
|
|
}
|
|
}
|