315 lines
15 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|