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

412 lines
20 KiB
C#

using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.DBModels.Consts;
using YLErp.Model;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 障碍期权敲入敲出操作
/// 迁移自:trade_barrier_optionBLL
/// </summary>
public class BarrierOptionKnockioService : TradeCashServiceEx
{
public BarrierOptionKnockioService(YLBaseService baseService) : base(baseService)
{
}
public BarrierOptionKnockioService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 设置障碍期权敲入敲出 ,返回error
/// </summary>
public void SetKnockInOut(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null,
Action<OtcTrade, trade_barrier_option> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from td in DbContext.trade
join tb in DbContext.trade_barrier_option on td.id equals tb.TradeId
where td.TradeDate > startDate.Value && td.TradeDate <= valueDate
&& string.IsNullOrEmpty(tb.KnockInOutStatus)
&& td.TradeType == "障碍期权"
&& ConsTrade.确认成交 == td.TradeStatus
&& td.ValidState != ConsGlobal.InValid
&& td.DividendDate < valueDate
select new
{
trade = td,
tradeBarrier = tb
};
#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).ToList();
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;//已到期交易不再观察;
}
double? closePrice, highPrice, lowPrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price;
highPrice = manuallyTradeObservationPrice.Price;
lowPrice = manuallyTradeObservationPrice.Price;
}
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;
highPrice = eodprice.HighPrice;
lowPrice = eodprice.LowPrice;
}
var tradeStatus = tr.trade.TradeStatus;
var oldKnockInOutStatus = tr.tradeBarrier.KnockInOutStatus;
CheckBarrierKnockInOutStatus(tr.trade, tr.tradeBarrier, valueDate, closePrice, highPrice, lowPrice);
if (oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus)
{
var KnockInOutStatus = tr.tradeBarrier.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus);
}
//到期仍未敲入的情况
if (tr.tradeBarrier.BarrierType.Contains("敲入")
&& (string.IsNullOrWhiteSpace(tr.tradeBarrier.KnockInOutStatus) || ConsTrade.KnockState.IsMonitoring(tr.tradeBarrier.KnockInOutStatus))
&& tr.trade.ExerciseDate <= valueDate)
{
tr.trade.TradeStatus = ConsTrade.已到期;
tr.trade.UnWindDate = valueDate;
SaveBarrierRebateCash(tr.trade, tr.tradeBarrier, valueDate, closePrice, knockOutBarrierPrice: null);
}
if (tradeStatus != tr.trade.TradeStatus || oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeBarrier).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeBarrier);
}
//更新,不能放到循环外,黑箱交易的子交易相互有依赖关系
DbContext.SaveChanges();
}
}
/// <summary>
///
/// </summary>
public void CheckBarrierKnockInOutStatus(OtcTradeBase td, trade_barrier_option tradeBarrier
, DateTime valuedate, double? closePrice, double? highPrice, double? lowPrice)
{
var BarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.BarrierPrice * td.SpotPrice : tradeBarrier.BarrierPrice;
var UpperBarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.UpperBarrierPrice * td.SpotPrice : tradeBarrier.UpperBarrierPrice;
switch (tradeBarrier.Discrete)
{
case "离散":
var observationDates = QdpHelper.GetObservationDatesFromString(tradeBarrier.ObservationDates);
//每日观察或者当前结算日是观察日的时候,才检查是否会敲入敲出
if (observationDates == null || observationDates.Contains(valuedate))
{
switch (tradeBarrier.BarrierType)
{
case "上升敲入":
if (closePrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "上升敲出":
if (closePrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice);
}
break;
case "下降敲入":
if (closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "下降敲出":
if (closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice);
}
break;
case "双障碍敲出":
if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice)
{
var barrierPrice = closePrice >= UpperBarrierPrice ?
tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice;
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
var upDown = closePrice >= UpperBarrierPrice ? true : false;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, barrierPrice, upDown);
}
break;
case "双障碍敲入":
if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
}
}
break;
case "连续":
switch (tradeBarrier.BarrierType)
{
case "上升敲入":
if (highPrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;//UpdateDate
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "上升敲出":
if (highPrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, highPrice, tradeBarrier.BarrierPrice);
}
break;
case "下降敲入":
if (lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "下降敲出":
if (lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, lowPrice, tradeBarrier.BarrierPrice);
}
break;
case "双障碍敲出":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
var price = highPrice >= UpperBarrierPrice ? highPrice : lowPrice;
var barrierPrice = highPrice >= UpperBarrierPrice ?
tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice;
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = valuedate;
var upDown = highPrice >= UpperBarrierPrice ? true : false;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, price, barrierPrice, upDown);
}
break;
case "双障碍敲入":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
}
break;
}
}
/// <summary>
/// 障碍期权到期时仍未敲入,或者已经敲出,应获得rebate,保存相应的资金信息
/// </summary>
public void SaveBarrierRebateCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false)
{
var tc = SetTradeCash(td, tradeBarrier, settleDate, closePrice, knockOutBarrierPrice, upDown);
SaveTradeCashDetail(tc);
//增加出入金记录
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
}
/// <summary>
/// 生成TradeCash
/// </summary>
/// <param name="td">交易</param>
/// <param name="tradeBarrier">子对象</param>
/// <param name="settleDate">敲出/了结日期</param>
/// <param name="closePrice">收盘价</param>
/// <param name="knockOutBarrierPrice">障碍价</param>
/// <param name="saveChanges">是否保存</param>
/// <returns></returns>
public trade_cash SetTradeCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false, bool saveChanges = true)
{
var spotPrice = td.SpotPrice ?? 0;
double rebate, rebateRate;
if (upDown)
{
if (td.IsUsePremiumRate == true)
{
rebateRate = tradeBarrier.RebateHighRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = tradeBarrier.RebateHigh ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
else
{
if (td.IsUsePremiumRate == true)
{
rebateRate = tradeBarrier.RebateRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = tradeBarrier.Rebate ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
if (tradeBarrier.RebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBarrier.RebateDayCount) ? new Act365() : tradeBarrier.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(td.StartDate.Value), new Date(settleDate));
rebate *= fraction;
rebate += Math.Abs((td.TradeSinglePrice - td.TradeSinglePrice * fraction) ?? 0);
}
var req = new TradeCashReq
{
UnwindNotional = td.Notional,
UnwindPrice = rebate,
UnwindPricePercentRate = rebateRate,
FinalPrice = closePrice,
UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional.Value : 0,
Notional = td.Notional,
TradeAmount = td.TradeAmount,
ValueDate = settleDate,
HappenedDate = settleDate,//记录流水记录的结算日
BarrierPrice = knockOutBarrierPrice
};
req.UnwindFee = req.UnwindNotional * (req.UnwindPrice ?? 0) + ((req.UnwindNotional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0);
if (valuedateBLL.SystemDate.UnwindAmountAngle == 1)
{
req.UnwindFee = req.UnwindFee * (td.BuySell == "卖出" ? -1 : 1);
}
if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1)
{
req.UnwindPrice = req.UnwindPrice * (td.BuySell == "卖出" ? -1 : 1);
}
var tc = CloseTrade_TradeCashSave(td, req, isFromRecheckOrKO: true, isLastAction: true, saveChanges: saveChanges);
if (tradeBarrier.RebateType == "AtEnd")
{
tc.ValueDate = td.ExerciseDate.Value;
tc.HappenedDate = settleDate;
}
if (td.IsGroup == 2 && td.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
tc.ParentTradeCashId = groupAction.ParentTradeCashId;
tc.ParentTradeId = groupAction.ParentTradeId;
}
else
{
tc.ParentTradeId = td.ParentTradeId;
tc.ParentTradeCashId = SaveGroupUnwindCash(td, tc.ValueDate, tc.Amount, closePrice ?? 0, out var continueTradeCashHandle).id;
}
}
tc.ValidState = "Valid";
//敲出价格为null,代表该交易为敲入类型的交易,但未敲入,到期后需要返还补偿金额的一条tradecash记录,归为到期行为
if (knockOutBarrierPrice == null)
{
tc.ExerciseWay = TradeCashExerciseWayEnum.到期行权;
}
else
{
tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权;
}
return tc;
}
}
}