392 lines
19 KiB
C#
392 lines
19 KiB
C#
using YLErp.BLL.Calculation;
|
|
using YLErp.Configuration;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Enums;
|
|
using YLErp.Model;
|
|
using YLErp.Modules;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.TradeModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.BLL.MarginCalculation
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static class MarginDefault
|
|
{
|
|
/// <summary>
|
|
/// 计算日终预付金
|
|
/// </summary>
|
|
public static List<trade_span> RunMarginCalculation(RunMarginCalculationReq req)
|
|
{
|
|
var result = new List<trade_span>();
|
|
var eodTradeids = new YLContext().eod_trade_risk_manual.Where(x => x.ValueDate == req.settleDate && x.Margin != null && string.IsNullOrEmpty(x.VolType)).Select(x => x.TradeId);
|
|
var tempTradeList = req.tradeList.Where(O => eodTradeids.Contains(O.id)).ToList();
|
|
if (tempTradeList.Any())
|
|
{
|
|
result = EodtraderiskmanualCalculation(req.Clone(tempTradeList));
|
|
}
|
|
tempTradeList = req.tradeList.Where(O => O.TradeType == "现金流交易" && !eodTradeids.Contains(O.id)).ToList();
|
|
if (tempTradeList.Any())
|
|
{
|
|
result.AddRange(cashflowMarginCalculation(req.Clone(tempTradeList)));
|
|
}
|
|
tempTradeList = req.tradeList.Where(O => O.TradeType != "现金流交易" && O.MarginType != MarginTypeEnum.DEFAULT && !eodTradeids.Contains(O.id)).ToList();
|
|
if (tempTradeList.Any())
|
|
{
|
|
result.AddRange(SingleMarginCalculation(req.Clone(tempTradeList)));
|
|
}
|
|
tempTradeList = req.tradeList.Where(O => O.TradeType != "现金流交易" && O.MarginType == MarginTypeEnum.DEFAULT && !eodTradeids.Contains(O.id)).ToList();
|
|
if (tempTradeList.Any())
|
|
{
|
|
result.AddRange(DefaultMarginCalculation.Instance.RunMarginCalculation(req.Clone(tempTradeList)));
|
|
}
|
|
foreach (var ret in result)
|
|
{
|
|
var trade = tempTradeList.FirstOrDefault(o => o.id == ret.TradeId);
|
|
if (trade != null && trade.TradeType == "现金流交易")
|
|
{
|
|
ret.WorstCastClientPayable = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<trade_span> cashflowMarginCalculation(RunMarginCalculationReq runMarginCalculationReq)
|
|
{
|
|
List<trade_span> result = new List<trade_span>();
|
|
runMarginCalculationReq.tradeList.ForEach(O =>
|
|
{
|
|
result.Add(new trade_span()
|
|
{
|
|
TradeId = O.id,
|
|
OptDate = DateTime.Now,
|
|
OptId = runMarginCalculationReq.userId,
|
|
OptName = runMarginCalculationReq.userName,
|
|
ClientId = O.ClientId,
|
|
UnderlyingId = O.UnderlyingId,
|
|
UnderlyingCode = O.UnderlyingCode,
|
|
ValueDate = runMarginCalculationReq.settleDate,
|
|
Spv1 = 0,
|
|
Spv2 = 0,
|
|
Spv3 = 0,
|
|
Spv4 = 0,
|
|
Spv5 = 0,
|
|
Spv6 = 0,
|
|
Spv7 = 0,
|
|
Spv8 = 0,
|
|
Spv = 0,
|
|
WorstCastClientPayable = 0,
|
|
TwoSideMargin = 0
|
|
});
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private static List<trade_span> SingleMarginCalculation(RunMarginCalculationReq req)
|
|
{
|
|
var result = new List<trade_span>();
|
|
double value = 0;
|
|
foreach (var trade in req.tradeList)
|
|
{
|
|
bool isSingleMargin = false;
|
|
switch (trade.MarginType)
|
|
{
|
|
case MarginTypeEnum.FIXED:
|
|
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
|
|
{
|
|
value = (trade.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional) * trade.MarginRate;
|
|
}
|
|
else
|
|
{
|
|
if (PS.Config.ErpElement.EodFixedMarginUseSpotPrice)
|
|
{
|
|
value = (trade.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional) * trade.PositionMarginRate;
|
|
}
|
|
else
|
|
{
|
|
value = (trade.StockEqvNotional == 0 || (trade.SpotPrice ?? 0) == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * req.PriceProvider.GetPrice(trade.UnderlyingCode), trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional * req.PriceProvider.GetPrice(trade.UnderlyingCode) / (trade.SpotPrice ?? 0)) * trade.PositionMarginRate;
|
|
}
|
|
}
|
|
break;
|
|
case MarginTypeEnum.FLOAT:
|
|
value = (trade.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional) * trade.MarginRate;
|
|
if (req.CalcMarginType != CalcMarginTypeEnum.InitialMargin)
|
|
{
|
|
if (trade.TradeType == "远期")
|
|
{
|
|
var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider(req.settleDate);
|
|
var spotPrice = eodpriceProvider.GetPrice(trade.UnderlyingCode, SettlementTypeEnum.ClosePrice);//收盘价
|
|
TradeValueResult curValue = ForwardradeCalcService.CalcValue(trade, spotPrice);
|
|
var tradePrice = (trade.TradePrice * trade.Notional / trade.OriginalNotional) ?? 0;
|
|
double pnl = 0;
|
|
pnl = curValue.Pv + tradePrice * (trade.BuySell == "买入" ? -1 : 1);
|
|
value += pnl;
|
|
if (value < 0)
|
|
{ value = 0; }
|
|
|
|
}
|
|
else
|
|
{
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
|
|
valueDate: req.settleDate,
|
|
tradeList: new[] { trade },
|
|
calcScenario: req.GetCalcScenario(),
|
|
priceProvider: req.PriceProvider,
|
|
pricingRequest: QdpPricingRequest.PV_ONLY,
|
|
addVolRateDic: null,
|
|
volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol,
|
|
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin);
|
|
if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage))
|
|
{
|
|
LogFactory.GetLogger<MarginCalculationBase>().Error("Pv计算失败:" + tradeRiskResult.ErrorMessage);
|
|
}
|
|
var tradePrice = (trade.TradePrice * trade.Notional / trade.OriginalNotional) ?? 0;
|
|
double pnl = 0;
|
|
if (trade.BuySell == "买入")
|
|
{
|
|
pnl = tradePrice - Math.Abs(tradeRiskResult.Results.First().ValueResult.Pv);
|
|
}
|
|
else
|
|
{
|
|
pnl = Math.Abs(tradeRiskResult.Results.First().ValueResult.Pv) - tradePrice;
|
|
}
|
|
value += pnl;
|
|
if (value < 0)
|
|
{ value = 0; }
|
|
}
|
|
}
|
|
|
|
break;
|
|
case MarginTypeEnum.FLOATP:
|
|
if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin)
|
|
{
|
|
value = (trade.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional) * trade.MarginRate;
|
|
}
|
|
else if (req.CalcMarginType != CalcMarginTypeEnum.InitialMargin)
|
|
{
|
|
value = (trade.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(trade.Notional * req.PriceProvider.GetPrice(trade.UnderlyingCode), trade.ParticipationRate, trade.AnnualizeFactor) : trade.StockEqvNotional) * trade.PositionMarginRate;
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
|
|
valueDate: req.settleDate,
|
|
tradeList: new[] { trade },
|
|
calcScenario: req.GetCalcScenario(),
|
|
priceProvider: req.PriceProvider,
|
|
pricingRequest: QdpPricingRequest.PV_ONLY,
|
|
addVolRateDic: null,
|
|
volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol,
|
|
preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin);
|
|
if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage))
|
|
{
|
|
LogFactory.GetLogger<MarginCalculationBase>().Error("Pv计算失败:" + tradeRiskResult.ErrorMessage);
|
|
}
|
|
var tradePrice = (trade.TradePrice * trade.Notional / trade.OriginalNotional) ?? 0;
|
|
double pnl = 0;
|
|
if (trade.BuySell == "买入")
|
|
{
|
|
pnl = tradePrice - Math.Abs(tradeRiskResult.Results.First().ValueResult.Pv);
|
|
}
|
|
else
|
|
{
|
|
pnl = Math.Abs(tradeRiskResult.Results.First().ValueResult.Pv) - tradePrice;
|
|
}
|
|
value += pnl;
|
|
if (value < 0)
|
|
{ value = 0; }
|
|
}
|
|
break;
|
|
default:
|
|
case MarginTypeEnum.NONE:
|
|
value = 0;
|
|
break;
|
|
}
|
|
|
|
result.Add(new trade_span()
|
|
{
|
|
TradeId = trade.id,
|
|
OptDate = DateTime.Now,
|
|
OptId = req.userId,
|
|
OptName = req.userName,
|
|
ClientId = trade.ClientId,
|
|
UnderlyingId = trade.UnderlyingId,
|
|
UnderlyingCode = trade.UnderlyingCode,
|
|
ValueDate = req.settleDate,
|
|
Spv1 = value,
|
|
Spv2 = value,
|
|
Spv3 = value,
|
|
Spv4 = value,
|
|
Spv5 = value,
|
|
Spv6 = value,
|
|
Spv7 = value,
|
|
Spv8 = value,
|
|
Spv = value,
|
|
WorstCastClientPayable = value,
|
|
TwoSideMargin = value,
|
|
IsSingleMargin = isSingleMargin
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static List<trade_span> EodtraderiskmanualCalculation(RunMarginCalculationReq req)
|
|
{
|
|
var result = new List<trade_span>();
|
|
List<int> reqids = req.tradeList.Select(x => x.id).ToList();
|
|
var eodTradeRiskManual = new YLContext().eod_trade_risk_manual.Where(x => reqids.Contains(x.TradeId) && x.ValueDate == req.settleDate && x.Margin != null && string.IsNullOrEmpty(x.VolType));
|
|
if (eodTradeRiskManual.Any())
|
|
{
|
|
var tradeRiskManuals = (from trade in req.tradeList
|
|
join riskManuals in eodTradeRiskManual on trade.id equals riskManuals.TradeId
|
|
select new { trade, riskManuals }).ToList();
|
|
foreach (var item in tradeRiskManuals)
|
|
{
|
|
double value = item.riskManuals.Margin ?? 0;
|
|
result.Add(new trade_span()
|
|
{
|
|
TradeId = item.trade.id,
|
|
OptDate = DateTime.Now,
|
|
OptId = req.userId,
|
|
OptName = req.userName,
|
|
ClientId = item.trade.ClientId,
|
|
UnderlyingId = item.trade.UnderlyingId,
|
|
UnderlyingCode = item.trade.UnderlyingCode,
|
|
ValueDate = req.settleDate,
|
|
Spv1 = value,
|
|
Spv2 = value,
|
|
Spv3 = value,
|
|
Spv4 = value,
|
|
Spv5 = value,
|
|
Spv6 = value,
|
|
Spv7 = value,
|
|
Spv8 = value,
|
|
Spv = value,
|
|
WorstCastClientPayable = value,
|
|
TwoSideMargin = value,
|
|
IsSingleMargin = true
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static List<trade_span> CalcClientMargin(CalcClientMarginReq req)
|
|
{
|
|
return DefaultMarginCalculation.Instance.CalcClientMargin(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 这个方法不明白什么意思
|
|
/// 按照历史代码,国君应该一直返回的是false,但事实是根据收盘数据看2021年9月24日及以前返回的是true
|
|
/// </summary>
|
|
public static bool IsMarginCalcNeedSpecial(DateTime valueDate)
|
|
{
|
|
//国君商品类预付金需要算反向预付金,权益类不需要
|
|
if (PS.Config.IsGuoJun && (!PS.Config.ErpElement.IsStockMargin || PS.Config.ClientElement.TwoSideMargin) && valuedateBLL.TwoSideMarginValueDate.HasValue && valueDate >= valuedateBLL.TwoSideMarginValueDate)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static double? GetInitialMarginRatio(trade trade)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
public static double? GetPositionMarginRatio(trade trade)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
public static double GetInitialMarginWithPreHandle(trade trade)
|
|
{
|
|
var tradeClone = trade.Clone();
|
|
tradeBLL.SetFieldsByTradeType(tradeClone);
|
|
var realTradeId = tradeClone.id;
|
|
tradeClone.id = 0;
|
|
tradeClone.VolType = "报价Bid";
|
|
tradeClone.TradeCloseVolatility = null;//不设置为null会影响计算结果
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeClone.UnderlyingCode);
|
|
tradeClone.StockEqvNotional = tradeClone.OriginalStockEqvNotional ?? 0;
|
|
tradeClone.Notional = tradeClone.OriginalNotional ?? 0;
|
|
tradeClone.TradeAmount = underlying != null && underlying.CountRatio != 0 ? tradeClone.Notional / underlying.CountRatio : tradeClone.Notional;
|
|
if (!tradeClone.TTMDays.HasValue)
|
|
{
|
|
tradeClone.TTMDays = TradeCalcHelper.CalculateTTMDays(tradeClone.TradeDate.Value, tradeClone.ExerciseDate.Value,
|
|
underlying?.UnderlyingTypeId ?? 0, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
|
|
}
|
|
return GetInitialMargin(tradeClone, realTradeId, true);
|
|
}
|
|
|
|
public static double GetInitialMargin(trade trade, int realTradeId, bool hasOptionInfo = false)
|
|
{
|
|
if (trade is null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
|
|
trade.MaturityDate = um?.MaturityDate;
|
|
|
|
///关于交易id的多义性: 1.组合交易需要真实的交易id;2.考虑到波动率不能获取持仓波动率,将id赋值为0;3.定价页面结构化交易子交易id会赋值-1和-2
|
|
var req = new GetTradeMarginReq
|
|
{
|
|
realTradeId = realTradeId,
|
|
trade = trade,
|
|
price = trade.SpotPrice ?? 0,
|
|
hasOptionInfo = hasOptionInfo,
|
|
calcMarginType = CalcMarginTypeEnum.InitialMargin
|
|
};
|
|
return GetTradeMargin(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// isInitialMargin为true: 为初始预付金
|
|
/// isInitialMargin为false: 为实时预付金
|
|
/// </summary>
|
|
public static double GetTradeMargin(GetTradeMarginReq req)
|
|
{
|
|
if (req.trade.TradeType == "结构化交易")
|
|
{
|
|
var option = StructureOption_Code.StructureOptions.FirstOrDefault(o => o.Name == req.trade.StructureType || o.CnName == req.trade.StructureType);
|
|
//自由组合时不在变更StructureType字段值,保持原有结构化交易的内容;
|
|
if (option != null)
|
|
{
|
|
req.trade.StructureType = option.CnName;
|
|
}
|
|
}
|
|
double? tempInitialMargin = null;
|
|
if (req.calcMarginType != CalcMarginTypeEnum.InitialMargin && req.trade.CalcFlag == 1)
|
|
{
|
|
tempInitialMargin = req.trade.InitialMargin;
|
|
req.trade.InitialMargin = null;
|
|
}
|
|
try
|
|
{
|
|
if (req.trade.TradeType == "现金流交易")
|
|
{
|
|
return 0;
|
|
}
|
|
else if (req.trade.MarginType != MarginTypeEnum.DEFAULT)
|
|
{
|
|
var result = SingleMarginCalculation(req.GetRunMarginCalculationReq());
|
|
return (result.FirstOrDefault()?.WorstCastClientPayable) ?? 0;
|
|
}
|
|
else
|
|
{
|
|
return DefaultMarginCalculation.Instance.GetTradeMargin(req);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (req.calcMarginType == CalcMarginTypeEnum.InitialMargin && req.trade.CalcFlag == 1 && req.trade.InitialMargin == null)
|
|
{
|
|
req.trade.InitialMargin = tempInitialMargin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|