Files
zszq-trs/YLErpDAL/Modules/CalculationModule/CCRService.cs
T
2024-05-09 14:06:26 +08:00

160 lines
7.6 KiB
C#

using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
public class CCRService : YLBaseService
{
public CCRService(OptUserInfo userInfo) : base(userInfo)
{
}
public CCRService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 是否实时计算
/// <para>实时计算时传参不一样</para>
/// </summary>
public bool IsRealtime { get; set; }
private DateTime? _settlementDate = null;
/// <summary>
/// 结算日期
/// </summary>
public DateTime SettlementDate
{
get
{
return _settlementDate ?? SystemValueDate;
}
set
{
_settlementDate = value;
}
}
/// <summary>
/// 要忽略计算的交易类型
/// </summary>
private readonly List<string> ignoreTradeTypes = new List<string>()
{
"结构化交易",
"自定义交易"
};
/// <summary>
/// 执行ccr计算
/// <para>原公式:CCR=MTM+k*(ADD-ON);最新的更改为:CCR=MTM+(ADD-ON)</para>
/// <para>MTM:持仓浮动盈亏</para>
/// <para>ADD-ON:假定未来盈亏</para>
/// </summary>
/// <param name="trades">要计算的交易;一般为某个客户的所有持仓交易</param>
/// <param name="j">CCR算法必要参数</param>
/// <param name="N">CCR算法必要参数</param>
/// <returns>Key:交易Id:Value:CCR值</returns>
public Dictionary<int, double> CalculationCCR(List<trade> trades, double j, double N)
{
var result = new Dictionary<int, double>();
var add_onVal = CalculationADD_ON(trades, (int)(j * 244), (int)N, out var mtmVal);
if (add_onVal.Count != mtmVal.Count)
{
throw new Exception($"计算出错,结果数量不匹配:\r\nadd_on:{add_onVal.ToJson()}\r\nmtm:{mtmVal.ToJson()}");
}
foreach (var item in add_onVal)
{
result[item.Key] = mtmVal[item.Key] + item.Value;
}
return result;
}
/// <summary>
/// 计算ADD_ON
/// <para>算法由国海提供</para>
/// </summary>
/// <param name="trades">要计算的交易;一般为某个客户的所有持仓交易</param>
/// <param name="dayCount">计算要覆盖的交易日数量;一般为244的倍数</param>
/// <param name="interval">计算时滚动价格的间隔;为国海算法中的N</param>
/// <param name="mtmVal">当前持仓交易持仓部分累计浮动盈亏;</param>
/// <returns>Key:交易Id:Value:CCR值</returns>
private Dictionary<int, double> CalculationADD_ON(List<trade> trades, int dayCount, int interval, out Dictionary<int, double> mtmVal)
{
if (trades is null)
{
throw new ArgumentNullException(nameof(trades));
}
var calcTrades = trades.Where(O => !ignoreTradeTypes.Contains(O.TradeType)).ToList();
if (calcTrades.Count == 0)
{
throw new ServiceException("不存在可计算的交易");
}
var underlyingCodes = calcTrades.Select(O => O.UnderlyingCode);
var dateList = QdpCalendarHelper.AllBizDays(QdpCalendarHelper.BizDayShift(SettlementDate, -dayCount), SettlementDate);
dateList.Add(SettlementDate);
var settlementType = ConsGlobal.SettlePriceMode.GetSettlementType(valuedateBLL.SystemDate.EodSettlePriceMode);
var latestPriceProvider = IsRealtime ? (IPriceProvider)DataCacheProvider.GetUnderlyingDataSource() : new EodPriceProvider(SettlementDate).GetPriceProvider(settlementType);
var latestRiskResult = CalculatorHelper.CalculateRisksForTrades(SettlementDate, calcTrades, Enums.CalcScenarioEnum.EodSettlement, latestPriceProvider, QdpPricingRequest.PV_ONLY, settlementType: settlementType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: false, canUseManual: true);
var priceProviderDict = new Dictionary<DateTime, EodPriceProvider>();
var latestPvMap = new Dictionary<int, double>();
// 日期维度 合计值 交易编号 浮动盈亏
var pvList = new List<KeyValuePair<double, Dictionary<int, double>>>();
//var pricessss = new List<string>();
for (int i = 0; i < dateList.Count; i++)
{
TradeRiskResult riskResult = null;
if (dateList[i].Date.Equals(SettlementDate.Date))
{
riskResult = latestRiskResult;
}
else
{
var priceProvider = new EodPriceProvider(dateList[i]);
priceProviderDict[dateList[i]] = priceProvider;
if (i < interval) { continue; }
var calcPriceProvider = new ManualPriceProvider();
//pricessss.Add("");
foreach (var code in underlyingCodes)
{
var currentPrice = priceProviderDict[dateList[i]].GetPrice(code, SettlementTypeEnum.ClosePrice);
var lastPrice = priceProviderDict[dateList[i - interval]].GetPrice(code, SettlementTypeEnum.ClosePrice);
var price = latestPriceProvider.GetPrice(code) * (currentPrice / lastPrice).Normalize();
//pricessss[i - 1] += $"{dateList[i].ToString("yyyy-MM-dd")}\t{code}\t{currentPrice}\t{lastPrice}\t{price}\t";
calcPriceProvider.SetPrice(code, price);
}
riskResult = CalculatorHelper.CalculateRisksForTrades(SystemValueDate, calcTrades, IsRealtime ? Enums.CalcScenarioEnum.Pricing : Enums.CalcScenarioEnum.EodSettlement, calcPriceProvider, QdpPricingRequest.PV_ONLY, settlementType: settlementType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: false);
}
//ADD-ON合计规则
var pv = riskResult.Results.ToDictionary(
K => K.Trade.id,
V =>
{
if (!(V.Trade.MetaDic.TryGetValue(YLErp.DBModels.Consts.ConsTradeMetaKey.CCR_K, out var strK) && double.TryParse(strK, out var k)))
{
k = 1;
}
if (!latestPvMap.ContainsKey(V.Trade.id))
{
latestPvMap[V.Trade.id] = latestRiskResult.Results.Where(B => B.Trade.id == V.Trade.id).FirstOrDefault()?.ValueResult.Pv ?? 0;
}
return k * (V.ValueResult.Pv - latestPvMap[V.Trade.id]);
});
//System.Diagnostics.Debug.WriteLine(((double)i / dateList.Count).ToString("0.##%"));
pvList.Add(new KeyValuePair<double, Dictionary<int, double>>(pv.Values.Sum(), pv));
}
//LogFactory.GetLogger<CCRService>().Info($"计算次数:{pvList.Count}\r\n{pvList.Select(O => O.Value).ToJson()}");
mtmVal = latestRiskResult.Results
.ToDictionary(
K => K.Trade.id,
V => V.ValueResult.Pv);
return pvList.OrderByDescending(O => O.Key).FirstOrDefault().Value;
}
}
}