160 lines
7.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|