508 lines
22 KiB
C#
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; }
|
|
}
|
|
}
|