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

272 lines
12 KiB
C#

using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using YLErp.Abstract.DataProviders;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule
{
/// <summary>
///
/// </summary>
public class TradeRangeAccrualService : ExoticOptionModule.TradeCashServiceEx
{
public TradeRangeAccrualService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeRangeAccrualService(YLBaseService baseService) : base(baseService)
{
}
public void CheckStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_rangeaccrual> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from trade in DbContext.trade
join rangeaccrual in DbContext.trade_rangeaccrual on trade.id equals rangeaccrual.TradeId
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate && trade.ExerciseDate >= valueDate
&& (trade.TradeType == "区间累积期权")
&& ConsTrade.确认成交 == trade.TradeStatus
&& trade.ValidState != ConsGlobal.InValid
&& trade.DividendDate < valueDate
select new
{
trade,
tradeRange = rangeaccrual
};
#region 增加客户筛选 tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
var tradeIds = trades.Select(x => x.trade.id).ToArray();
var manuallyTradeObservationPrices = DbContext.manually_trade_observation_price
.Where(x => tradeIds.Contains(x.TradeId) && x.ValueDate == valueDate).ToDictionary(n => n.TradeId);
foreach (var tr in trades)
{
if (tr.trade.ExerciseDate < valueDate)
{
continue;//已到期交易不再观察;
}
var tradeStatus = tr.trade.TradeStatus;
double closePrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price ?? 0;
}
else if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodprice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{tr.trade.UnderlyingCode} 未找到结算价");
}
else
{
closePrice = eodprice.ClosePrice;
}
CheckRangeAccrualBonus(tr.trade, tr.tradeRange, valueDate, closePrice);
if (tradeStatus != tr.trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeRange).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeRange);
}
}
DbContext.SaveChanges();
}
public void CheckRangeAccrualBonus(OtcTradeBase trade, trade_rangeaccrual tradeRange, DateTime valueDate, double closePrice)
{
var tcQuery = from x in DbContext.trade_cash
where x.TradeId == trade.id && x.ValidState != ConsGlobal.InValid
&& !x.IsDeleted && x.Action == "系统操作-平仓费"
&& x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)
&& x.UnwindNotional < x.Notional
select x;
var notional = (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus) && trade.UnWindDate <= valueDate
? 0 : trade.Notional) + (tcQuery.Sum(x => x.UnwindNotional) ?? 0);
if (CheckRangeAccrualBonus(trade, tradeRange, valueDate, closePrice, notional, out var couponCash))
{
SaveObservation(trade, tradeRange, valueDate, couponCash, notional);
//这里要保存,否则下面查询的时候,查不到最后一天的票息记录.
DbContext.SaveChanges();
}
//到期日生成票息资金记录
if (valueDate.Date == trade.ExerciseDate.Value.Date)
{
trade.UnWindDate = valueDate.Date;
trade.TradeStatus = ConsTrade.已到期;
var happenedObservations = DbContext.autocall_observation.Where(o => o.TradeId == trade.id).ToList();
var totalPaymentAmount = happenedObservations?.Sum(x => x.PaymentAmount) ?? 0;
totalPaymentAmount += trade.PrincipalSum() * (trade.BuySell == "卖出" ? -1 : 1);
var parentTradeId = 0;
var parentTradeCashId = 0;
if (trade.IsGroup == 2 && trade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == trade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = trade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(trade, valueDate, totalPaymentAmount, closePrice, out bool continueTradeCashHandle).id;
}
}
SaveCash(trade, ClientCashInCashOut.系统操作_票息, null, totalPaymentAmount, valueDate, closePrice, valueDate, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
//生成确认书
if (PS.Config.IsAutoGenerateContracts && ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus))
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(trade.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { trade.id });
}
}
}
public static bool CheckRangeAccrualBonus(OtcTradeBase trade, trade_rangeaccrual tradeRange, DateTime valueDate, double closePrice, double notional, out double couponCash)
{
couponCash = 0;
if (trade is null || tradeRange is null)
{
return false;
}
var observDates = QdpHelper.ParseObservationDate(tradeRange.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(trade.TradeDate.Value, trade.ExerciseDate.Value).ToArray();
}
if (observDates != null && observDates.Contains(new Date(valueDate)))
{
var upperRange = trade.IsMoneynessOptionData ? tradeRange.UpperRange * trade.SpotPrice.Value : tradeRange.UpperRange;
var lowerRange = trade.IsMoneynessOptionData ? tradeRange.LowerRange * trade.SpotPrice.Value : tradeRange.LowerRange;
//有区间收益
if (closePrice < upperRange && closePrice > lowerRange)
{
couponCash = tradeRange.BonusRate * notional * (trade.SpotPrice ?? 0) / observDates.Length * (trade.BuySell == "卖出" ? -1 : 1);
return true;
}
}
return false;
}
public autocall_observation SaveObservation(OtcTradeBase trade, trade_rangeaccrual rangeaccrual, DateTime valueDate, double couponCash, double notional, bool saveChanges = true)
{
var observationRecord = DbContext.autocall_observation.FirstOrDefault(o => o.TradeId == trade.id && o.EndDate == valueDate.Date);
if (observationRecord == null)
{
observationRecord = new autocall_observation()
{
TradeId = trade.id,
StartDate = valueDate.Date,
EndDate = valueDate.Date,
CouponRate = rangeaccrual.BonusRate,
StockEqvNotional = notional * (trade.SpotPrice ?? 0),
PaymentAmount = couponCash,
PaymentDate = trade.ExerciseDate.Value.Date
};
if (saveChanges)
{
DbContext.autocall_observation.Add(observationRecord);
}
}
else
{
observationRecord.StartDate = valueDate.Date;
observationRecord.EndDate = valueDate.Date;
observationRecord.CouponRate = rangeaccrual.BonusRate;
observationRecord.StockEqvNotional = notional * (trade.SpotPrice ?? 0);
observationRecord.PaymentAmount = couponCash;
observationRecord.PaymentDate = trade.ExerciseDate.Value.Date;
}
return observationRecord;
}
public double GetRangeCoupon(int tradeId, DateTime valueDate, double price)
{
var trade = DbContext.trade.AsNoTracking().FirstOrDefault(t => t.id == tradeId);
var tradeRangeAccrual = DbContext.trade_rangeaccrual.AsNoTracking().FirstOrDefault(t => t.TradeId == tradeId);
return GetRangeCoupon(trade, tradeRangeAccrual, valueDate, price);
}
public double GetRangeCoupon(OtcTradeBase trade, trade_rangeaccrual tradeRangeAccrual, DateTime valueDate, double price)
{
if (trade == null || tradeRangeAccrual == null)
{
return 0.0;
}
var tradeCashs = DbContext.trade_cash.AsNoTracking().Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == trade.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
var notional = (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus) && trade.UnWindDate <= valueDate ? 0 : trade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
var observDates = QdpHelper.ParseObservationDate(tradeRangeAccrual.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(trade.TradeDate.Value), new Date(trade.ExerciseDate.Value)).ToArray();
}
var upperRange = trade.IsMoneynessOptionData ? tradeRangeAccrual.UpperRange * (trade.SpotPrice ?? 0) : tradeRangeAccrual.UpperRange;
var lowerRange = trade.IsMoneynessOptionData ? tradeRangeAccrual.LowerRange * (trade.SpotPrice ?? 0) : tradeRangeAccrual.LowerRange;
if (price < upperRange && price > lowerRange)
{
return (observDates != null && observDates.Length > 0) ?
tradeRangeAccrual.BonusRate * notional * (trade.SpotPrice ?? 0) / observDates.Length :
tradeRangeAccrual.BonusRate * notional * (trade.SpotPrice ?? 0);
}
else
{
return 0.0;
}
}
}
}