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

514 lines
21 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 Qdp.Pricing.Base.Implementations;
using YLErp.BLL.Calculation;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model.Enum;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.MarginModule;
namespace YLErp.BLL.MarginCalculation
{
public class GDGZMarginCalculation : MarginCalculationBase
{
// 定义一个静态变量来保存类的实例(单例模式)
public static readonly GDGZMarginCalculation Instance;
static GDGZMarginCalculation()
{
Instance = new GDGZMarginCalculation();
}
// 定义私有构造函数,使外界不能创建该类实例
private GDGZMarginCalculation()
{
}
public override List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
{
if (req.tradeList == null || !req.tradeList.Any())
{
return new List<trade_span>(0);
}
return new InnerRunMarginCalculation(req, _underlyingDataProvider).CalcMargin();
}
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,
//负数代表客户应缴预付金,正数代表客户应收预付金
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();
clientSpanNews = clientSpanNews.Where(c => !clientSpanOldsWithFlag.Any(t => t.ValueDate == c.ValueDate && t.ClientId == c.ClientId)).ToList();
}
if (clientSpanNews.Count > 0)
{
db.client_span.AddRange(clientSpanNews);
}
db.SaveChanges();
return req.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;
}
/// <summary>
/// 内部计算交易预付金类
/// </summary>
class InnerRunMarginCalculation
{
readonly RunMarginCalculationReq _req;
readonly List<trade_span> _tradeSpans;
readonly bool _isInitialMargin;
readonly RunMarginCalculationHelper _helper;
MarginParamProvider _mpProvider;
public InnerRunMarginCalculation(RunMarginCalculationReq req, UnderlyingDataProvider underlyingDataProvider)
{
_req = req;
_tradeSpans = new List<trade_span>();
_isInitialMargin = req.CalcMarginType == CalcMarginTypeEnum.InitialMargin;
_helper = new RunMarginCalculationHelper(_req, underlyingDataProvider);
}
public List<trade_span> CalcMargin()
{
_tradeSpans.Clear();
if (_req.tradeList == null || !_req.tradeList.Any())
{
return _tradeSpans;
}
//设置详细信息
_helper.SetFieldsByTradeType();
//计算一组数据的交易风险指标
var calcReq = _helper.GetCalculateRisksForTradesReq(
priceProvider: _req.PriceProvider,
addVolRateDic: null, overrideVols: null,
pricingRequest: PricingRequest.Pv | PricingRequest.Delta | PricingRequest.Gamma);
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq);
if (tradeRiskResult.Results.Count < 1)
{
return _tradeSpans;
}
//跨式组合
var tradeStraddleList = new List<TradeRiskResultRecord>();
//结构化交易组合
var tradeStructureList = new List<TradeRiskResultRecord>();
//这个循环只处理最简单的情况
foreach (var item in tradeRiskResult.Results)
{
if (item.Trade.StructureType == "跨式组合" || item.Trade.StructureType == "宽跨式组合")
{
tradeStraddleList.Add(item);
}
else if (item.Trade.StructureType == "结构化交易")
{
tradeStructureList.Add(item);
}
else
{
ProcessSingleTrade(item);
}
}
//处理跨式交易
var groups = tradeStraddleList.GroupBy(n => n.Trade.ParentTradeId);
foreach (var grp in groups)
{
ProcessStraddleTrades(grp.ToArray());
}
//处理结构化组合交易
groups = tradeStructureList.GroupBy(n => n.Trade.ParentTradeId);
foreach (var grp in groups)
{
ProcessStructureTrades(grp.ToArray());
}
return _tradeSpans;
}
/// <summary>
/// 处理单个交易
/// </summary>
private void ProcessSingleTrade(TradeRiskResultRecord item)
{
var margin = GetUpDownEffectValue(item.ValueResult, out var updownLimit);
if (!_isInitialMargin)
{
margin += item.ValueResult.Pv;
margin = GetRealMargin(margin, item.Trade.BuySell, item.Trade.ClientId, out _);
}
else if (item.Trade.BuySell == "卖出")
{
margin = -margin;
}
var tradeSpan = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(margin);
tradeSpan.Comment = GetComment(item.ValueResult, updownLimit);
_tradeSpans.Add(tradeSpan);
}
/// <summary>
/// 处理跨式交易
/// </summary>
private void ProcessStraddleTrades(TradeRiskResultRecord[] items)
{
var firstItem = items[0];
//组内只有一条交易
if (items.Length == 1)
{
ProcessSingleTrade(firstItem);
return;
}
//初始预付金:两腿初始预付金平分
//跨式组合的两腿交易方向是一致的
if (_isInitialMargin)
{
var spans1 = items.Select(n =>
{
var tradeSpan = _helper.CreateTradeSpan(n.Trade);
tradeSpan.WorstCastClientPayable = n.ValueResult.Pv + GetUpDownEffectValue(n.ValueResult, out var updownLimit);
tradeSpan.Comment = GetComment(n.ValueResult, updownLimit);
return tradeSpan;
}).ToArray();
var margin = spans1.Average(n => n.WorstCastClientPayable ?? 0);
if (firstItem.Trade.BuySell == "卖出")
{
margin = -margin;
}
foreach (var span in spans1)
{
span.SetAllSpvAndWorst(margin);
}
_tradeSpans.AddRange(spans1);
return;
}
//持仓预付金
//一腿预付金为期末结算收支
//另一腿预付金为期末结算收支+两腿标的涨跌停造成期权价值变化孰大者
var maxEffectVal = 0d;
var lastItem = items.Last();
InnerClient client = null;
var spans2 = items.Select(n =>
{
var effectVal = GetUpDownEffectValue(n.ValueResult, out var updownLimit);
maxEffectVal = Math.Max(maxEffectVal, effectVal);
var tradeSpan = _helper.CreateTradeSpan(n.Trade);
tradeSpan.Comment = GetComment(n.ValueResult, updownLimit);
var margin = n == lastItem ? n.ValueResult.Pv + maxEffectVal : n.ValueResult.Pv;
if (client != null && client.ClientId == n.Trade.ClientId)
{
margin = GetRealMargin(margin, n.Trade.BuySell, (MarginOptionEnum)client.MarginOptionType);
}
else
{
margin = GetRealMargin(margin, n.Trade.BuySell, n.Trade.ClientId, out client);
}
//设置预付金
tradeSpan.SetAllSpvAndWorst(margin);
return tradeSpan;
});
_tradeSpans.AddRange(spans2);
}
/// <summary>
/// 处理结构化组合交易
/// </summary>
private void ProcessStructureTrades(TradeRiskResultRecord[] items)
{
var firstItem = items[0];
InnerClient client = null;
client = _helper.GetClient(firstItem.Trade.ClientId);
double margin = 0d, updownLimit = 0;
//远期:光子买的腿,初始预付金为期初权利金*2,持仓预付金计算公式与香草一致
if (firstItem.Trade.TradeNumber.Contains("FWD"))
{
foreach (var item in items)
{
if (_isInitialMargin)
{
if (item.Trade.BuySell == "买入")
{
updownLimit = 0;
margin = item.ValueResult.Pv * 2;
}
else
{
margin = -GetUpDownEffectValue(item.ValueResult, out updownLimit);
}
}
else
{
margin = item.ValueResult.Pv + GetUpDownEffectValue(item.ValueResult, out updownLimit);
if (client != null && client.ClientId == item.Trade.ClientId)
{
margin = GetRealMargin(margin, item.Trade.BuySell, (MarginOptionEnum)client.MarginOptionType);
}
else
{
margin = GetRealMargin(margin, item.Trade.BuySell, item.Trade.ClientId, out client);
}
}
var tradeSpan = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(margin);
tradeSpan.Comment = GetComment(item.ValueResult, updownLimit);
_tradeSpans.Add(tradeSpan);
}
return;
}
//价差
if (firstItem.Trade.TradeNumber.Contains("SPD"))
{
//行权价 K1=4000K2=4500Notional=10
//单向追保:
//1.交易员买入,预付金5000
//2.交易员卖出,预付金0
//双向追保:
//1.交易员买入,预付金5000
//2.交易员卖出,预付金 - 5000
//对手方单向追保:
//1.交易员买入,预付金0
//2.交易员卖出,预付金 - 5000
//双向追保 交易员买入 买入 + 卖出 0
// 卖出 买入 0 卖出 -
var sumBuy = items.Sum(n => n.Trade.BuySell == "买入" ? (n.Trade.TradePrice ?? 0) : 0);
var sumSell = items.Sum(n => n.Trade.BuySell == "买入" ? 0 : (n.Trade.TradePrice ?? 0));
//行权价之差的绝对值,因为取得时总和的绝对值所以在任何方向取负值都没问题
var strikeDiff = Math.Abs(items.Sum(n => (n.Trade.BuySell == "买入" ? -1 : 1) * (n.Trade.Strike ?? 0) * n.Trade.TradeAmount));
//交易员买入价差
if (sumBuy > sumSell)
{
if ((MarginOptionEnum)client.MarginOptionType == MarginOptionEnum.对手方单向追保)
{
margin = 0;
}
else
{
margin = strikeDiff;
}
//最后一笔交易员买入
var lastItem = items.LastOrDefault(n => n.Trade.BuySell == "买入");
var spans = items.Select(n =>
{
var tradeSpan = _helper.CreateTradeSpan(n.Trade);
if (n == lastItem)
{
tradeSpan.SetAllSpvAndWorst(margin);
tradeSpan.Comment = "客户卖出价差LAST";
}
else
{
tradeSpan.Comment = "客户卖出价差";
}
return tradeSpan;
}).ToArray();
_tradeSpans.AddRange(spans);
}
else
{
//最后一笔客户卖出腿/交易员买入
var lastItem = items.LastOrDefault(n => n.Trade.BuySell == "买入");
if ((MarginOptionEnum)client.MarginOptionType == MarginOptionEnum.单向追保)
{
margin = 0;
}
else
{
margin = -strikeDiff;
}
var spans = items.Select(n =>
{
var tradeSpan = _helper.CreateTradeSpan(n.Trade);
if ((n == lastItem && (MarginOptionEnum)client.MarginOptionType != MarginOptionEnum.双向追保) || (n != lastItem && (MarginOptionEnum)client.MarginOptionType == MarginOptionEnum.双向追保))
{
tradeSpan.SetAllSpvAndWorst(margin);
tradeSpan.Comment = "客户卖出价差LAST";
}
else
{
tradeSpan.Comment = "客户卖出价差";
}
return tradeSpan;
}).ToArray();
_tradeSpans.AddRange(spans);
}
}
else
{
foreach (var item in items)
{
ProcessSingleTrade(item);
}
}
}
/// <summary>
/// 获取涨跌停造成期权价值变化
/// </summary>
private double GetUpDownEffectValue(TradeValueResult ValueResult, out double updownLimit)
{
if (_mpProvider == null)
{
_mpProvider = new MarginParamProvider(_req.UserInfo, _req.settleDate);
}
if (!_mpProvider.TryGetUpdownLimit(ValueResult.UnderlyingCode, out updownLimit, out _))
{
return 0;
}
var spotPrice = ValueResult.SpotPrice ?? 0;
var spotPriceLimit = spotPrice * updownLimit;
return Math.Abs(ValueResult.Delta * spotPriceLimit) + Math.Abs(ValueResult.Gamma * spotPriceLimit * spotPriceLimit) / 2;
}
/// <summary>
/// 根据买卖方向和客户追保方向获取预付金实际取值(只用于持仓预付金)
/// </summary>
private double GetRealMargin(double margin, string buysell, int clientId, out InnerClient client)
{
client = _helper.GetClient(clientId);
if (client != null)
{
return GetRealMargin(margin, buysell, (MarginOptionEnum)client.MarginOptionType);
}
return margin;
}
/// <summary>
/// 根据买卖方向和客户追保方向获取预付金实际取值(只用于持仓预付金)
/// </summary>
private static double GetRealMargin(double margin, string buysell, MarginOptionEnum marginOptionType)
{
if (marginOptionType == MarginOptionEnum.单向追保)
{
return buysell == "卖出" ? 0 : Math.Max(margin, 0);
}
if (marginOptionType == MarginOptionEnum.对手方单向追保)
{
return buysell == "卖出" ? Math.Min(margin, 0) : 0;
}
return margin;
}
/// <summary>
/// 获取交易预付金备注
/// </summary>
private static string GetComment(TradeValueResult ValueResult, double updownLimit)
{
return $"Delta:{ValueResult.Delta},closePrice:{ValueResult.SpotPrice},Gamma:{ValueResult.Gamma},updownLimit:{updownLimit:0.00%}";
}
}
}
}