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

315 lines
15 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.BLL.Eod;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 二元期权操作
/// 迁移自:trade_binary_optionBLL
/// </summary>
public class BinaryOptionDealService : TradeCashServiceEx
{
public BinaryOptionDealService(YLBaseService baseService) : base(baseService)
{
}
public BinaryOptionDealService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
///
/// </summary>
public void CheckTouchStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null,
Action<OtcTrade, trade_binary_option> afterKnowInOut = null, System.Collections.Generic.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_binary_option on td.id equals tb.TradeId
where td.TradeDate > startDate.Value && td.TradeDate <= valueDate
&& td.ExerciseDate >= valueDate
&& ConsTrade.确认成交 == td.TradeStatus
&& td.ValidState != ConsGlobal.InValid
&& td.TradeType == "二元期权" && td.ExerciseMode == "American"
&& td.DividendDate < valueDate
select new
{
trade = td,
tradeBinary = 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;
}
// 美式二元检查
foreach (var tr in trades)
{
if (tr.trade.ExerciseDate < valueDate)
{
continue;//已到期交易不再观察;
}
var tradeStatus = tr.trade.TradeStatus;
if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodprice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{tr.trade.UnderlyingCode} 未找到结算价");
}
double? upPrice = 0, lowPrice = 0;
var isObservationDate = false;
//根据是否为离散观察来确定用来比较的价格
//如果是离散观察,只用收盘价比较
//如果是连续观察,使用最高价和最低价
if (tr.tradeBinary.IsDiscreteMonitored)
{
var observationDates = QdpHelper.GetObservationDatesFromString(tr.tradeBinary.ObservationDates);
if (observationDates == null || observationDates.Contains(valueDate))
{
isObservationDate = true;
upPrice = lowPrice = eodprice.ClosePrice;
}
}
else
{
isObservationDate = true;
upPrice = eodprice.HighPrice;
lowPrice = eodprice.LowPrice;
}
var strike = tr.trade.IsMoneynessOptionData ? tr.trade.Strike * tr.trade.SpotPrice : tr.trade.Strike;
var UpperBarrier = new Lazy<double?>(() =>
tr.trade.IsMoneynessOptionData ? tr.tradeBinary.UpperBarrier * tr.trade.SpotPrice : tr.tradeBinary.UpperBarrier);
if (isObservationDate)
{
switch (tr.tradeBinary.PayoffType)
{
case "UpOneTouch":
if (upPrice > strike)
{
//触碰,买方获得盈利,交易结束
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, upPrice);
tr.trade.TradeStatus = ConsTrade.已执行;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice);
}
break;
case "DownOneTouch":
if (lowPrice < strike)
{
//触碰,买方获得盈利,交易结束
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, lowPrice);
tr.trade.TradeStatus = ConsTrade.已执行;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice);
}
break;
case "UpNoTouch":
if (upPrice > strike)
{
//触碰,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice);
tr.trade.TradeStatus = ConsTrade.已到期;
tr.trade.UnWindDate = valueDate;
}
break;
case "DownNoTouch":
if (lowPrice < strike)
{
//触碰,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice);
tr.trade.TradeStatus = ConsTrade.已到期;
tr.trade.UnWindDate = valueDate;
}
break;
case "DoubleOneTouch":
if (upPrice >= UpperBarrier.Value || lowPrice <= strike)
{
var breachHighBarrier = upPrice >= UpperBarrier.Value;
var price = breachHighBarrier ? upPrice : lowPrice;
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: breachHighBarrier);
//触碰上限或下限,买方获得盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, price);
tr.trade.TradeStatus = ConsTrade.已执行;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice);
}
break;
case "DoubleNoTouch":
if (upPrice > UpperBarrier.Value || lowPrice < strike)
{
var price = upPrice >= tr.tradeBinary.UpperBarrier ? upPrice : lowPrice;
//触碰上限或下限,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, price);
tr.trade.TradeStatus = ConsTrade.已到期;
tr.trade.UnWindDate = valueDate;
}
break;
}
}
//OneTouch类型,到期仍未触碰,则交易结束
if (tr.tradeBinary.PayoffType.Contains("OneTouch") && tr.trade.ExerciseDate <= valueDate)
{
//SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice);
tr.trade.TradeStatus = ConsTrade.已到期;
tr.trade.UnWindDate = valueDate;
}
//NoTouch类型,到期仍未触碰,买方获得盈利,交易结束
if (tr.tradeBinary.PayoffType.Contains("NoTouch") && tr.trade.ExerciseDate <= valueDate && !ConsTrade.TradeCompleteStatus.Contains(tr.trade.TradeStatus))
{
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, false, eodprice.ClosePrice);
tr.trade.TradeStatus = ConsTrade.已执行;
tr.trade.UnWindDate = valueDate;
}
if (tradeStatus != tr.trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeBinary).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeBinary);
}
//更新,不能放到循环外,黑箱交易的子交易相互有依赖关系
DbContext.SaveChanges();
}
}
/// <summary>
///
/// </summary>
public static double GetCashOrNothingAmount(OtcTradeBase trade, trade_binary_option tradeBinary, DateTime settleDate, bool useHighAmount = false)
{
var result = (trade.Notional / trade.OriginalNotional * (trade.OriginalPrincipalSum ?? 0)) ?? 0;
double refund;
if (trade.IsUsePremiumRate == true)
{
var rate = useHighAmount ? tradeBinary.CashOrNothingAmountHighRate : tradeBinary.CashOrNothingAmountRate;
refund = Math.Abs((rate ?? 0) * (trade.SpotPrice ?? 0) * trade.Notional);
}
else
{
var amount = useHighAmount ? tradeBinary.CashOrNothingAmountHigh : tradeBinary.CashOrNothingAmount;
refund = Math.Abs((amount ?? 0) * trade.Notional);
}
if (tradeBinary.RebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBinary.RebateDayCount) ? new Act365() : tradeBinary.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(trade.StartDate.Value), new Date(settleDate));
refund *= fraction;
refund += Math.Abs((trade.TradePrice - trade.TradePrice * fraction) ?? 0);
}
return result + refund;
}
/// <summary>
/// 美式二元期权触碰后,买方获得盈利,记录资金信息
/// </summary>
public trade_cash SaveBinarySettleCash(OtcTradeBase td, trade_binary_option tradeBinary, DateTime valuedate, double cash, bool earlyExecute, double? price, bool saveChanges = true)
{
var tradeCash = new trade_cash
{
ValidState = "Valid",
OptDate = DateTime.Now,
OptId=UserId,
OptName=UserName,
Action = ClientCashInCashOut.系统操作_行权费,
IsLastAction = true,
ValueDate = valuedate,
Strike = td.Strike,
CallPut = td.CallPut,
Amount = cash * EodOperationBase.GetSign(td.BuySell),
UnwindPrice = cash / td.Notional,
UnwindPricePercentRate = cash / td.OriginalStockEqvNotional,
Status = TradeCashStatusEnum.已执行,
TradeType = td.BuySell,
TradeId = td.id,
Notional = td.Notional,
TradeAmount = td.TradeAmount,
UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional : 0,
FinalPrice = price,
ExerciseWay = earlyExecute ? TradeCashExerciseWayEnum.提前终止行权 : TradeCashExerciseWayEnum.到期行权
};
if (tradeBinary.RebateType == "AtEnd")
{
tradeCash.ValueDate = td.ExerciseDate.Value;
tradeCash.HappenedDate = valuedate;
}
if (saveChanges)
{
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 = "已完成";
tradeCash.ParentTradeCashId = groupAction.ParentTradeCashId;
tradeCash.ParentTradeId = groupAction.ParentTradeId;
}
else
{
tradeCash.ParentTradeId = td.ParentTradeId;
tradeCash.ParentTradeCashId = SaveGroupUnwindCash(td, tradeCash.ValueDate, tradeCash.Amount, price ?? 0, out bool continueTradeCashHandle).id;
}
}
DbContext.trade_cash.Add(tradeCash);
DbContext.SaveChanges();
SaveTradeCashDetail(tradeCash);
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tradeCash, tradeCash.ValueDate);
}
return tradeCash;
}
}
}