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

508 lines
22 KiB
C#

using Qdp.Pricing.Base.Implementations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.BLL.MarginCalculation.DongWu;
using YLErp.DBModels;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Models;
using YLErp.Modules;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.MarginModule;
using YLErp.QdpModule;
namespace YLErp.BLL.MarginCalculation
{
/// <summary>
/// 东吴预付金计算
/// TODO :东吴预付金计算待解决问题:
/// 1.接入收盘测试并测试
/// 2.接入定价预付金计算并测试
/// 3.接入实时预付金计算并测试
/// </summary>
public class DongWuMarginCalculation : MarginCalculationBase
{
private const string ExtensionNodeName = "DongWu_Margin";
private const string Margin1PreName = "Margin1";
private const string Margin3PreName = "Margin3";
private const int PriceCount = 21;
private const double CoverMarginRate = 1.4;
// 定义一个静态变量来保存类的实例
public static readonly DongWuMarginCalculation Instance;
static DongWuMarginCalculation()
{
Instance = new DongWuMarginCalculation();
}
protected DongWuMarginCalculation()
{
}
#region Override Base Method
/// <summary>
/// 计算交易预付金
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
// 交易预付金计算结果
var resultMap = new Dictionary<int, trade_span>();
var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider);
// 为了算客户角度的一个预付金数值
helper.ReverseTradeSide();
// 设置期权详情
helper.SetFieldsByTradeType();
//标的集合
helper.GetMarginParamProvider(MarginParamTypeEnum.MarginRate | MarginParamTypeEnum.UpDownLimit);
var umCodeSet = helper.GetUnderlyingCodes();
// 预付金参数管理
var marginParamProviderManager = new MarginParamProviderManager(umCodeSet);
marginParamProviderManager.Initial(req);
// 创建价格
var priceProviders = new Dictionary<string, List<PriceProviderWrap>>();
foreach (var underlyingCode in umCodeSet)
{
// 生成Margin1 价格
double? settlePrice = null;
if (req.CalcMarginType != CalcMarginTypeEnum.EodMargin)
{
// 如果是实时预付金计算,用前一天的结算价
var settlementDayEodPrice = EodPriceQueryService.GetEodPrice(req.settleDate.AddDays(-1), underlyingCode);
settlePrice = settlementDayEodPrice?.SettlePrice ?? 0;
}
IPriceFactory priceProviderFactory1 = new PriceFactory(req.settleDate, settlePrice, underlyingCode, new UpDownLimitMargin1Provider(req.settleDate, underlyingCode, marginParamProviderManager));
var unPriceProviders1 = priceProviderFactory1.Generate(priceCount: PriceCount, preName: Margin1PreName);
// 生成Margin3 价格
IPriceFactory priceProviderFactory3 = new PriceFactory(req.settleDate, settlePrice, underlyingCode, new UpDownLimitMargin3Provider(req.settleDate, underlyingCode));
var unPriceProviders3 = priceProviderFactory3.Generate(priceCount: PriceCount, preName: Margin3PreName);
priceProviders.Add(underlyingCode, new List<PriceProviderWrap>() { unPriceProviders1, unPriceProviders3 });
}
foreach (var trade in req.tradeList)
{
// 获取标的价格
if (!priceProviders.TryGetValue(trade.UnderlyingCode, out List<PriceProviderWrap> priceProviderWraps))
{
continue;
}
// 计算交易PV结果
var pVJsons = new List<PVJson>();
//计算pv方法
Action<Dictionary<string, IPriceProvider>> calcPv = (unPriceProviders) =>
{
foreach (var priceProvider in unPriceProviders)
{
var cloneTrade = trade.Clone();
var tradeRiskResult = CalculateRisksForTrades(req, priceProvider.Value, cloneTrade);
var pVJson = new PVJson();
pVJson.TradeId = trade.id;
pVJson.UnderlyingId = trade.UnderlyingId;
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
pVJson.VarietyId = un.UnderlyingTypeId;
pVJson.Name = priceProvider.Key;
pVJson.ClientId = trade.ClientId;
pVJson.Value = tradeRiskResult.Results.FirstOrDefault().ValueResult.Pv;
pVJsons.Add(pVJson);
}
};
// 计算 Margin1 trade pv
var priceProviderWrap1 = priceProviderWraps.FirstOrDefault(g => g.Name == Margin1PreName);
calcPv(priceProviderWrap1.priceProviders);
// 计算 Margin3 trade pv
var priceProviderWrap3 = priceProviderWraps.FirstOrDefault(g => g.Name == Margin3PreName);
calcPv(priceProviderWrap3.priceProviders);
// Create TradeSpan
var contains = resultMap.TryGetValue(trade.id, out var tempTradeSpan);
if (!contains)
{
resultMap[trade.id] = tempTradeSpan = helper.CreateTradeSpan(trade);
}
tempTradeSpan.WorstCastClientPayable = pVJsons.Max(l => l.Value);
PriceJson priceJson = new PriceJson()
{
Pvs = pVJsons,
UpLimitRate = priceProviderWrap1.UpLimitRate,
DownLimitRate = priceProviderWrap1.DownLimitRate,
UpLimitRate3 = priceProviderWrap3.UpLimitRate,
DownLimitRate3 = priceProviderWrap3.DownLimitRate,
IsCover = priceProviderWrap1.IsCover,
IsTouch = priceProviderWrap1.IsTouch
};
tempTradeSpan.SetExtensions(ExtensionNodeName, priceJson);
}
return resultMap.Values.ToList();
}
/// <summary>
/// 计算客户预付金
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public override List<trade_span> CalcClientMargin(CalcClientMarginReq req)
{
// 客户预付金计算结果
var clientSpanNews = new List<ClientSpan>();
var updateTradeSpans = new List<trade_span>();
if (req.tradeSpans != null && req.tradeSpans.Count > 0)
{
var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList();
var tradeList = GetTrades(tradeIds);
var tradeSpanInfo = (from tradeSpan in req.tradeSpans
join trade in tradeList on tradeSpan.TradeId equals trade.id
//2023-02-24:排除掉现金流交易,现金流交易应该不需要预付金
where tradeSpan.ValueDate == req.settleDate && trade.TradeType != "现金流交易"
select new
{
PriceJson = tradeSpan.GetFromExtensions<PriceJson>(ExtensionNodeName),
trade,
tradeSpan
}).ToList();
var varietyCoverStatus = tradeSpanInfo.GroupBy(g => g.tradeSpan.VarietyId ?? 0).ToDictionary(g => g.Key, g => g.Any(b => b.PriceJson != null && b.PriceJson.IsCover));
var varietyTouchStatus = tradeSpanInfo.GroupBy(g => g.tradeSpan.VarietyId ?? 0).ToDictionary(g => g.Key, g => g.Any(b => b.PriceJson != null && b.PriceJson.IsTouch));
var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId);
foreach (var clientGroup in clientGroups)
{
var pVJsons1 = new List<DongwuPVJson>();
var pVJsons3 = new List<DongwuPVJson>();
foreach (var tradeInfo in clientGroup)
{
if (tradeInfo.PriceJson != null)
{
// margin1
pVJsons1.AddRange(tradeInfo.PriceJson.Pvs.Where(b => b.Name.StartsWith(Margin1PreName)).Select(
g => new DongwuPVJson
{
TradeSpanId = tradeInfo.tradeSpan.id,
ClientId = g.ClientId,
Describe = g.Describe,
Name = g.Name,
TradeId = g.TradeId,
UnderlyingId = g.UnderlyingId,
Value = g.Value,
VarietyId = g.VarietyId
}).ToList());
// margin3
pVJsons3.AddRange(tradeInfo.PriceJson.Pvs.Where(b => b.Name.StartsWith(Margin3PreName)).Select(
g => new DongwuPVJson
{
TradeSpanId = tradeInfo.tradeSpan.id,
ClientId = g.ClientId,
Describe = g.Describe,
Name = g.Name,
TradeId = g.TradeId,
UnderlyingId = g.UnderlyingId,
Value = g.Value,
VarietyId = g.VarietyId
}).ToList());
}
}
// 计算单个客户预付金
var clientPv = CalcClientMargin(clientGroup.Key, pVJsons1, pVJsons3, (int varietyId) =>
{
varietyCoverStatus.TryGetValue(varietyId, out bool isCover);
return isCover;
}, (int varietyId) =>
{
varietyTouchStatus.TryGetValue(varietyId, out bool isTouch);
return isTouch;
}, (List<DongwuPVJson> UpdatePVJsons) =>
{
updateTradeSpans.AddRange(UpdatePVJsons.Select(g => new trade_span() { id = g.TradeSpanId, WorstCastClientPayable = g.Value }));
});
// 计算客户维度预付金
var clientSpan = new ClientSpan
{
ClientId = clientGroup.Key,
ValueDate = req.settleDate,
DeltaMargin = tradeSpanInfo.Where(g => g.trade.ClientId == clientGroup.Key).Sum(g => g.tradeSpan.DeltaMargin * -1),
// 负数代表客户应缴预付金,正数代表客户应收预付金
WorstCastClientPayable = -clientPv,
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
};
clientSpan.PVJsonList = new List<ClientPVJson>();
clientSpan.PVJsons = JsonHelper.ToJson(clientSpan.PVJsonList);
clientSpanNews.Add(clientSpan);
}
}
SaveClientSpan(req, clientSpanNews, updateTradeSpans);
return req.tradeSpans;
}
/// <summary>
/// 获取交易预付金计算结果
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public override double GetTradeMargin(GetTradeMarginReq req)
{
var trade = req.trade;
using (var db = new YLContext())
{
if (trade.TradeType == "结构化交易")
{
trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList();
}
}
var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq());
if (null != tradeMargin && tradeMargin.FirstOrDefault() != null)
{
var margin = tradeMargin.FirstOrDefault().WorstCastClientPayable ?? 0.0;
return Math.Max(margin, 0);
}
return 0.0;
}
#endregion
#region
/// <summary>
/// 东吴-计算客户预付金逻辑
/// </summary>
/// <param name="pVJsons1"></param>
/// <param name="pVJsons3"></param>
/// <returns></returns>
private double CalcClientMargin(int clientId, List<DongwuPVJson> pVJsons1, List<DongwuPVJson> pVJsons3, Func<int, bool> isCoverFunc, Func<int, bool> isTouchFunc, Action<List<DongwuPVJson>> updateTradespansAction)
{
/*
计算客户预付金:
1.计算标的预付金
2.计算品种预付金
3.计算客户预付金
*/
// 计算品种维度预付金方法
var calcVarietyMargin = (List<DongwuPVJson> pVJsons) =>
{
var varietyMarginDic = pVJsons
.GroupBy(g => new { g.VarietyId, g.UnderlyingId, g.Name }).Select(g => new
{
// 同品种、同标的、同价格合计pv
UnderlyingId = g.Key.UnderlyingId,
VarietyId = g.Key.VarietyId,
Name = g.Key.Name, // 前缀+价格,例如:Margin1_3.00
UnPriceSumPv = g.Sum(b => b.Value),
Tradespans = g.ToList()
})
.GroupBy(g => new { g.VarietyId, g.UnderlyingId }).Select(g => new
{
// 同品种、同标的取最大pv
UnderlyingId = g.Key.UnderlyingId,
VarietyId = g.Key.VarietyId,
UnPv = g.Max(g => g.UnPriceSumPv),
Tradespans = g.Where(a => a.UnPriceSumPv == g.Max(g => g.UnPriceSumPv)).First().Tradespans
})
.GroupBy(g => g.VarietyId).Select(g => new
{
// 同品种轧差pv
VarietyId = g.Key,
VarietyPv = g.Sum(b => b.UnPv),
Tradespans = g.SelectMany(a => a.Tradespans).ToList()
}).ToDictionary(g => g.VarietyId, g => g);
return varietyMarginDic;
};
// 计算品种维度margin1预付金
var varietyMargin1Dic = calcVarietyMargin(pVJsons1);
// 计算品种维度margin3预付金
var varietyMargin3Dic = calcVarietyMargin(pVJsons3);
// 品种Pv=Max(Margin1,Margin3),如果有触板的品种需要乘1.4
Dictionary<int, double> varietyPv = new Dictionary<int, double>();
foreach (var varietyId in varietyMargin1Dic.Keys)
{
var clientMargin1 = varietyMargin1Dic[varietyId];
var clientMargin3 = varietyMargin3Dic[varietyId];
var maxPv = 0.00;
List<DongwuPVJson> updatePvJsons;
if (clientMargin1.VarietyPv > clientMargin3.VarietyPv)
{
maxPv = clientMargin1.VarietyPv;
updatePvJsons = clientMargin1.Tradespans;
}
else
{
maxPv = clientMargin3.VarietyPv;
updatePvJsons = clientMargin3.Tradespans;
}
bool isCover = isCoverFunc(varietyId);
bool isTouch = isTouchFunc(varietyId);
if (!isCover && isTouch)
{
maxPv = CoverMarginRate * maxPv;
}
varietyPv.Add(varietyId, maxPv);
// 回写trade_span预付金值
updateTradespansAction(updatePvJsons);
}
// 计算客户维度pv=合计所有品种的pv
var clientPv = varietyPv.Values.Sum();
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
if (client.MarginOptionType == (int)MarginOptionEnum.单向追保)
{
// 单项预付金如果小于0,则显示0
clientPv = Math.Max(clientPv, 0);
}
return clientPv;
}
/// <summary>
/// 计算交易风险
/// </summary>
/// <param name="req"></param>
/// <param name="priceProvider"></param>
/// <param name="trade"></param>
/// <returns></returns>
private TradeRiskResult CalculateRisksForTrades(RunMarginCalculationReq req, IPriceProvider priceProvider, trade trade)
{
var reqConv = new CalculateRisksForTradesReq
{
valueDate = req.settleDate,
tradeList = new List<trade>() { trade },
priceProvider = priceProvider,
pricingRequest = QdpPricingRequest.PV_ONLY,
volType = req.volType,
isUseTradeVol = PS.Config.IsTradeVol,
PreciseTimeMode = req.CalcMarginType != Enums.CalcMarginTypeEnum.EodMargin,
isAddVolPercent = true,
isMarginCalc = true
};
if (req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin)
{
reqConv.calcScenario = Enums.CalcScenarioEnum.EodSettlement;
}
if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin)
{
reqConv.calcScenario = Enums.CalcScenarioEnum.InitialMargin;
}
return CalculatorHelper.CalculateRisksForTrades(reqConv);
}
/// <summary>
/// 查询交易
/// </summary>
/// <param name="tradeIds"></param>
/// <returns></returns>
private List<trade> GetTrades(List<int> tradeIds)
{
using (var db = new YLContext())
{
var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList();
return tradeList;
}
}
/// <summary>
/// 批量保存客户预付金
/// </summary>
/// <param name="req"></param>
/// <param name="clientSpanNews"></param>
private void SaveClientSpan(CalcClientMarginReq req, List<ClientSpan> clientSpanNews, List<trade_span> updateTradeSpans)
{
using (var db = new YLContext())
{
// 回写更新交易预付金
var tradespanids = updateTradeSpans.Select(t => t.id).ToList();
var trade_spans = db.trade_span.Where(g => tradespanids.Contains(g.id)).ToList();
var updatetradeSpanData = (from updatetradeSpan in updateTradeSpans
join tradeSpan in trade_spans on updatetradeSpan.id equals tradeSpan.id
select new
{
updatetradeSpan,
tradeSpan
});
foreach (var item in updatetradeSpanData)
{
item.tradeSpan.WorstCastClientPayable = item.updatetradeSpan.WorstCastClientPayable;
}
//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();
}
}
#endregion
}
public class DongwuPVJson : PVJson
{
public int TradeSpanId { get; set; }
}
}