437 lines
16 KiB
C#
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; }
|
|
}
|
|
}
|