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

437 lines
16 KiB
C#

using Qdp.Pricing.Base.Implementations;
using YLErp.BLL;
using YLErp.Models;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
/// <summary>
/// 获取fixing的服务
/// </summary>
public static class FixingService
{
/// <summary>
/// 获取fixing数据
/// </summary>
public static string GetFixingString(FixingRequest request)
{
if (request is null)
{
return null;
}
if (request.ValueDate == default)
{
request.ValueDate = valuedateBLL.ValueDate;
}
if (request.StartDate == default)
{
request.StartDate = request.ValueDate;
}
else if (request.ValueDate < request.StartDate)
{
return string.Empty;
}
//估值日期大于到期日期则估值日期变为到期日期
if (request.ValueDate > request.ExerciseDate)
{
request.ValueDate = request.ExerciseDate;
}
Dictionary<DateTime, double> priceDic = null;
HashSet<DateTime> observationDates = null;
//仅选出观察日列表中的价格作为fixing
if (!string.IsNullOrWhiteSpace(request.ObservationDates))
{
observationDates = QdpHelper.ParseObservationDate(request.ObservationDates).Select(x => x.DateTime).ToHashSet();
}
else
{
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(request.StartDate, request.ValueDate).Select(x => x.DateTime).ToHashSet();
observationDates.Add(request.StartDate);
observationDates.Add(request.ValueDate);
}
var useReferencePrice = request.SettlementType == SettlementTypeEnum.ReferencePrice;
using (var db = DbContextFactory.GetYLDbContext())
{
//日终股票价格
if (ConsGlobal.InstrumentType.IsStock(request.InstrumentType))
{
var query = from e in db.eod_stock_price
where e.UnderlyingCode == request.UnderlyingCode
&& e.ValueDate >= request.StartDate && e.ValueDate <= request.ValueDate
orderby e.ValueDate
select new
{
e.ValueDate,
Price = useReferencePrice ? (e.ReferencePrice ?? e.ClosePrice) : e.ClosePrice
};
priceDic = query.ToDictionary(n => n.ValueDate, n => n.Price);
ExdividenProcess(request, priceDic);
}
else
{
var query = from e in db.eod_commodity_future_price
where e.UnderlyingCode == request.UnderlyingCode
&& e.ValueDate >= request.StartDate && e.ValueDate <= request.ValueDate
orderby e.ValueDate
select new EodPrice
{
ValueDate = e.ValueDate,
ClosePrice = e.ClosePrice,
ReferencePrice = e.ReferencePrice,
SettlePrice = e.SettlePrice
};
priceDic = query.ToDictionary(e => e.ValueDate, e => e.GetPrice(request.SettlementType));
}
//厦门象屿使用参考价的交易在上午10点15分进入下一交易日,所以会出现导入参考价而没有导入收盘价和结算价的情况
//如果取到的收盘价或结算价为0则可能是异常价格,受制于数据库结构不好修改,所以只能简单处理下
if (priceDic != null && !useReferencePrice && PS.Config.Company == Configuration.CompanyEnum.厦门象屿
&& request.ValueDate == valuedateBLL.ValueDate && DateTime.Now.TimeOfDay < GlobalConfig.EodStartTime
&& priceDic.TryGetValue(request.ValueDate, out var price) && Math.Abs(price) < 1e-6)
{
priceDic.Remove(request.ValueDate);
}
if (request.ValueDate <= request.ExerciseDate && request.ValueDate >= request.StartDate)
{
if (priceDic == null)
{
priceDic = new Dictionary<DateTime, double>();
}
var valueDatePrice = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode).Price ?? 0;
if (priceDic.Count < 1 && !QdpCalendarHelper.IsHoliday(request.ValueDate))
{
foreach (var observationDate in observationDates.OrderBy(x => x))
{
if(request.StartDate <= observationDate && observationDate <= request.ValueDate)
{
priceDic.Add(observationDate, valueDatePrice);
}
}
}
if (priceDic.Any() && priceDic.Last().Key != request.ValueDate && !QdpCalendarHelper.IsHoliday(request.ValueDate))
{
priceDic.Add(request.ValueDate, valueDatePrice);
}
}
}
if (priceDic != null && priceDic.Count > 0)
{
var em = priceDic.Where(n => observationDates.Contains(n.Key)).Select(p => $"{p.Key:yyyy-MM-dd},{p.Value}");
return string.Join(";", em);
}
return string.Empty;
}
/// <summary>
/// 获取fixing数据
/// </summary>
/// <param name="valueDate">估值日期</param>
/// <param name="otcTrade">场外交易</param>
/// <param name="startDate">开始日期</param>
/// <param name="observationDates">观察日</param>
public static string GetFixingString(DateTime valueDate, OtcTradeBase otcTrade, DateTime startDate, string observationDates)
{
if (otcTrade is null)
{
return null;
}
return GetFixingString(new FixingRequest(
tradeId: otcTrade.id,
valueDate: valueDate,
startDate: startDate,
exerciseDate: otcTrade.ExerciseDate ?? valueDate,
observationDates: observationDates,
instrumentType: otcTrade.UnderlyingInstrumentType,
underlyingCode: otcTrade.UnderlyingCode,
settlementType: otcTrade.SettlementType));
}
/// <summary>
/// 日终价格进行除权处理
/// </summary>
private static void ExdividenProcess(FixingRequest request, Dictionary<DateTime, double> priceDic)
{
if (priceDic == null || priceDic.Count < 1)
{
return;
}
var service = new DividendService(OptUserInfo.SystemUser);
//获取按照除权除息日期正序排列的数组,估值日不需要除权处理(O.ExDividendDate < request.ValueDate)
var exDividendInfos = service.GetExDividendInfos(request.UnderlyingCode)
.Where(O => O.ExDividendDate >= request.StartDate && O.ExDividendDate < request.ValueDate)
.OrderBy(n => n.ExDividendDate).ToArray();
if (!exDividendInfos.Any())
{
return;
}
var ratioDict = new DbRecordChangesService<TradeChanges>(OptUserInfo.SystemUser).GetValue(
changeType: DBModels.Consts.ConsInfoChangeType.Dividend,
recordId: request.TradeId,
fieldName: nameof(trade.DividendRatio),
optDateStart: request.StartDate,
optDateEnd: request.ValueDate)
.ToDictionary(K => K.OptDate, V => { return double.TryParse(V.NewValue, out var tempValue) ? (double?)tempValue : null; });
//日终价格和除权除息信息都按照正序排列
//获取除权价格则使用大于日终价格日期的除权信息除权
//循环日终价格,如果除权日期小于价格日期则被排除掉
var startIndex = 0;
foreach (var kv in priceDic)
{
(var date, var price) = (kv.Key, kv.Value);
for (var i = startIndex; i < exDividendInfos.Length; i++)
{
var dividenInfo = exDividendInfos[i];
//除权日当天的收盘价也需要处理
if (date <= dividenInfo.ExDividendDate.Value)
{
ratioDict.TryGetValue(date, out var ratio);
price = service.GetPrice(price, dividenInfo, ratio);
}
else
{
startIndex = i;
}
}
priceDic[date] = price;
}
}
/// <summary>
/// 获取fixing请求参数
/// </summary>
/// <param name="valueDate"></param>
/// <param name="otcTrade"></param>
/// <returns></returns>
public static FixingRequestBase GetRequestBase(DateTime valueDate, OtcTradeBase otcTrade)
{
if (otcTrade is null)
{
return null;
}
return new FixingRequestBase(
tradeId: otcTrade.id,
valueDate: valueDate,
exerciseDate: otcTrade.ExerciseDate ?? valueDate,
instrumentType: otcTrade.UnderlyingInstrumentType,
underlyingCode: otcTrade.UnderlyingCode,
settlementType: otcTrade.SettlementType
);
}
/// <summary>
/// 添加或替换最后一天的fixing价格,仅fixing有值的情况下进行处理
/// </summary>
public static string AddOrReplaceLastDateSpotPrice(string fixing, DateTime lastDate, double spotPrice)
{
if (!string.IsNullOrEmpty(fixing))
{
var span = fixing.AsSpan().Trim(';');
var lastIndex = span.LastIndexOf(';');
if (span.Slice(lastIndex + 1).StartsWith(lastDate.ToString("yyyy-MM-dd").AsSpan()))
{
fixing = span.Slice(0, lastIndex + 1).ToString();
}
else
{
fixing += ";";
}
fixing += $"{lastDate:yyyy-MM-dd},{spotPrice}";
}
return fixing;
}
}
/// <summary>
/// 亚式期权fixing数据服务
/// </summary>
public static class AsianOptionFixingService
{
private static trade_asian_option GetAsianOption(int tradeId)
{
if (tradeId > 0)
{
using (var db = DbContextFactory.GetYLDbContext())
{
return db.trade_asian_option.AsNoTracking().FirstOrDefault(n => n.TradeId == tradeId);
}
}
return null;
}
/// <summary>
/// 为亚式期权交易获取fixing数据
/// </summary>
public static string GetFixingString(DateTime valueDate, trade trade, bool setFixingBeforeAPdStartDate = false)
{
if (trade.trade_asian_option == null)
{
trade.trade_asian_option = GetAsianOption(trade.id);
if (trade.trade_asian_option == null)
{
return string.Empty;
}
}
var fixingReq = GetRequest(valueDate, trade, setFixingBeforeAPdStartDate);
return GetFixingString(fixingReq, trade.trade_asian_option);
}
/// <summary>
/// 为亚式期权交易获取fixing数据
/// </summary>
public static string GetFixingString(DateTime valueDate, OtcTradeBase trade, trade_asian_option asianOption)
{
if (trade is null)
{
return null;
}
if (asianOption == null || asianOption.TradeId != trade.id)
{
asianOption = GetAsianOption(trade.id);
if (asianOption == null)
{
return string.Empty;
}
}
return FixingService.GetFixingString(valueDate, trade
, asianOption.AveragingPeriodStartDate ?? trade.TradeDate ?? valueDate, asianOption.ObservationDates);
}
/// <summary>
/// 为亚式期权交易获取fixing数据
/// </summary>
public static string GetFixingString(AsianFixingRequest request, trade_asian_option asianOption)
{
if (request is null)
{
return null;
}
if (asianOption == null)
{
asianOption = GetAsianOption(request.TradeId);
if (asianOption == null)
{
return string.Empty;
}
}
var AveragingPeriodStartDate = asianOption.AveragingPeriodStartDate ?? request.ValueDate;
//这段逻辑从方顿Logic中抽取,但应该是通用逻辑
if (request.ValueDate < AveragingPeriodStartDate)
{
if (request.SetFixingBeforeAPdStartDate)
{
var umPrice = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode).Price;
asianOption.Fixings = $"{request.ValueDate:yyyy-MM-dd},{umPrice}";
}
else
{
asianOption.Fixings = string.Empty;
}
return asianOption.Fixings;
}
return FixingService.GetFixingString(new FixingRequest(request, AveragingPeriodStartDate, asianOption.ObservationDates));
}
/// <summary>
/// 获取亚式期权fixing请求参数
/// </summary>
/// <param name="valueDate">估值日期</param>
/// <param name="otcTrade">场外交易对象</param>
/// <param name="onlyFixed">是否只在固定行权价时获取</param>
/// <param name="setFixingBeforeAPdStartDate"></param>
public static AsianFixingRequest GetRequest(DateTime valueDate, OtcTradeBase otcTrade, bool setFixingBeforeAPdStartDate = false)
{
if (otcTrade is null)
{
return null;
}
return new AsianFixingRequest(
tradeId: otcTrade.id,
valueDate: valueDate,
exerciseDate: otcTrade.ExerciseDate ?? valueDate,
instrumentType: otcTrade.UnderlyingInstrumentType,
underlyingCode: otcTrade.UnderlyingCode,
settlementType: otcTrade.SettlementType)
{
SetFixingBeforeAPdStartDate = setFixingBeforeAPdStartDate
};
}
/// <summary>
/// 检查fixings是否需要填充
/// </summary>
public static string CheckAsiaFixings(OtcTradeBase tr, trade_asian_option asianOption, string fixings, double spotPrice)
{
if (string.IsNullOrWhiteSpace(fixings) && asianOption.PayoffType == "EnhancedArithmeticAverage" && asianOption.StrikeType != "Segmented")
{
return $"{tr.StartDate.Value:yyyy-MM-dd},{spotPrice}";
}
return fixings;
}
}
/// <summary>
/// 亚式期权fixing请求model
/// </summary>
public class AsianFixingRequest : FixingRequestBase
{
public AsianFixingRequest(FixingRequestBase baseReq) : base(baseReq)
{
}
public AsianFixingRequest(DateTime valueDate, int tradeId, string instrumentType, string underlyingCode, DateTime exerciseDate, SettlementTypeEnum settlementType)
: base(valueDate, tradeId, instrumentType, underlyingCode, exerciseDate, settlementType)
{
}
/// <summary>
/// 来源于以前的方顿逻辑暂时保留
/// </summary>
public bool SetFixingBeforeAPdStartDate { get; set; }
}
}