903 lines
45 KiB
C#
903 lines
45 KiB
C#
using YLErp.BLL;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Modules.EodModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.TradeModule.DealModule
|
|
{
|
|
/// <summary>
|
|
/// 交易回退服务
|
|
/// </summary>
|
|
public class TradeBackService : TradeServiceBase
|
|
{
|
|
public TradeBackService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public TradeBackService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
public TradeBackService(OptUserInfo userInfo, YLContext dbContext) : base(userInfo, dbContext)
|
|
{
|
|
}
|
|
|
|
public trade Execute(int tradeId, bool backToBegin, TradeBackActionEnum actionEnum)
|
|
{
|
|
var trade = DbContext.trade.Find(tradeId);
|
|
|
|
if (trade == null)
|
|
{
|
|
throw new ServiceException("交易数据不存在");
|
|
}
|
|
|
|
new TradeExtendService(UserInfo, DbContext).SetTradeExtend(new[] { trade }, tracking: true);
|
|
|
|
if (trade.TradeType == "累计期权")
|
|
{
|
|
backToBegin = true;
|
|
|
|
if (trade.trade_accumulator_option.SettlementMode == "实物交割" || trade.trade_accumulator_option.SettlementMode == "现金实物合并结算")
|
|
{
|
|
var query = from ob in DbContext.autocall_observation
|
|
join tf in DbContext.trade on ob.ConvTradeNumber equals tf.TradeNumber
|
|
where ob.TradeId == trade.id && (ob.SettlementMode == "转远期" || ob.SettlementMode == "转掉期" || ob.SettlementMode == "现金当日&转远期") && tf.ValidState != ConsGlobal.InValid
|
|
select ob;
|
|
|
|
if (query.Any())
|
|
{
|
|
throw new ServiceException($"操作失败,当前累计期权交易({trade.TradeNumber})已结算为远期交易,请将所有结算后的远期交易删除后重新执行回退操作!");
|
|
}
|
|
}
|
|
}
|
|
|
|
var valueDate = trade.TradeDate.Value;
|
|
var tradeCashIds = new List<int>();
|
|
var actions = new List<string>()
|
|
{
|
|
"系统操作-行权费",
|
|
"系统操作-平仓费",
|
|
"系统操作-票息",
|
|
"系统操作-互换"
|
|
};
|
|
var tcQuery = DbContext.trade_cash.Where(x => x.TradeId == tradeId && (actions.Contains(x.Action) || x.Action == "系统操作-期权费" && x.ValueDate != trade.TradeDate && x.ValueDate != trade.PremiumPayDate) && x.ValidState != ConsGlobal.InValid && !x.IsDeleted);
|
|
var eodTrade = DbContext.eod_trade.Where(O => O.TradeId == tradeId && O.ValueDate == trade.TradeDate).FirstOrDefault();
|
|
var originTrade = eodTrade?.trade;
|
|
if (originTrade == null)
|
|
{
|
|
originTrade = trade;
|
|
new TradeExtendService(UserInfo, DbContext).SetTradeExtend(new[] { originTrade });
|
|
}
|
|
var stockEqvNotionalUnwind = (originTrade.OriginalStockEqvNotional ?? 0) - originTrade.StockEqvNotional;
|
|
var extensionTimeService = new ExtensionTimeService(UserInfo, DbContext);
|
|
if (backToBegin)
|
|
{
|
|
var tcTemp = DbContext.trade_cash.Where(x => x.TradeId == tradeId && x.Action == "系统操作-期权费" && x.ValidState != ConsGlobal.InValid && !x.IsDeleted).FirstOrDefault();
|
|
if (tcTemp == null)
|
|
{
|
|
throw new ServiceException("未查询到该交易的开仓记录,请删除该笔交易重新操作");
|
|
}
|
|
originTrade.TradeStatus = ConsTrade.确认成交;
|
|
originTrade.OriginalNotional = tcTemp.Notional;
|
|
originTrade.Notional = tcTemp.Notional;
|
|
originTrade.StockEqvNotional = originTrade.OriginalStockEqvNotional ?? 0;
|
|
DividendService.SetDividendTradeData(trade, originTrade);
|
|
valueDate = trade.TradeDate.Value;
|
|
tradeCashIds.AddRange(tcQuery.Select(O => O.id).ToList());
|
|
|
|
trade.UnWindDate = null;
|
|
trade.FinalPrice = null;
|
|
trade.UnWindNotional = null;
|
|
trade.UnWindPrice = null;
|
|
trade.HasPartialUnWind = null;
|
|
extensionTimeService.DelExtensionTime(tradeId, DateTime.MinValue, actionEnum != TradeBackActionEnum.EditMaturityDate);
|
|
}
|
|
else
|
|
{
|
|
if (!tcQuery.Any())
|
|
{
|
|
throw new ServiceException("未查询到该交易的了结记录,不可回退");
|
|
}
|
|
//有行权记录先回撤行权记录,没有行权记录,依次倒序撤回平仓记录
|
|
var tradeCash = tcQuery.OrderByDescending(x => x.id).FirstOrDefault();
|
|
var et = extensionTimeService.QueryExtensionTimeList(tradeId).Where(O => O.OptDate > tradeCash.ValueDate).OrderByDescending(O => O.OptDate).FirstOrDefault();
|
|
if (et != null)
|
|
{
|
|
extensionTimeService.DelExtensionTime(et.TradeId, et.id);
|
|
return trade;
|
|
}
|
|
trade.Notional = tradeCash.Notional;
|
|
if (trade.TradeType == "现金流交易")
|
|
{
|
|
if (tradeCash.UnwindType == "全部平仓")
|
|
{
|
|
trade.StockEqvNotional = tradeCash.Notional;
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = trade.StockEqvNotional + tradeCash.UnwindNotional.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (trade.IsUsePremiumRate == true)
|
|
{
|
|
//后期将会将所有已了结的交易均处理为0
|
|
if (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus))
|
|
{
|
|
trade.StockEqvNotional = 0;
|
|
}
|
|
trade.StockEqvNotional += trade.OriginalStockEqvNotional * tradeCash.UnwindPercentRate ?? 0;
|
|
}
|
|
else
|
|
{
|
|
if (trade.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(trade.BasisUnderlyingCode))
|
|
{
|
|
trade.StockEqvNotional += TradeHelper.GetStockEqvNotional(tradeCash.UnwindNotional / trade.OriginalNotional * trade.OriginalStockEqvNotional, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
}
|
|
}
|
|
valueDate = tradeCash.ValueDate;
|
|
tradeCashIds.Add(tradeCash.id);
|
|
|
|
//分步回退时将倒数第二条平仓记录赋值给trade
|
|
var lastSecondTradeCash = tcQuery.Where(x => x.id != tradeCash.id).OrderByDescending(x => x.id).FirstOrDefault();
|
|
if (lastSecondTradeCash != null && lastSecondTradeCash.Action == "系统操作-平仓费")
|
|
{
|
|
trade.UnWindDate = lastSecondTradeCash.ValueDate;
|
|
trade.FinalPrice = lastSecondTradeCash.FinalPrice;
|
|
trade.UnWindNotional = lastSecondTradeCash.UnwindNotional;
|
|
trade.UnWindPrice = lastSecondTradeCash.UnwindPrice;
|
|
trade.HasPartialUnWind = 1;
|
|
}
|
|
else
|
|
{
|
|
trade.UnWindDate = null;
|
|
trade.FinalPrice = null;
|
|
trade.UnWindNotional = null;
|
|
trade.UnWindPrice = null;
|
|
trade.HasPartialUnWind = null;
|
|
}
|
|
|
|
}
|
|
trade.TradeAmount = trade.Notional;
|
|
var varietyList = VarietyBLL.GetAllvarietyModel();
|
|
if (!string.IsNullOrWhiteSpace(trade.UnderlyingAssetClass))
|
|
{
|
|
var variety = varietyList.FirstOrDefault(O => O.VarietyName == trade.UnderlyingAssetClass);
|
|
if (variety != null)
|
|
{
|
|
trade.TradeUnit = variety.TradeUnit;
|
|
trade.TradeAmount /= variety.CountRatio;
|
|
}
|
|
}
|
|
|
|
trade.CheckStatus = null;
|
|
trade.OptId = UserId;
|
|
trade.OptName = UserName;
|
|
trade.OptDate = DateTime.Now;
|
|
trade.TradeStatus = ConsTrade.确认成交;
|
|
trade.IsApproval = false;
|
|
trade.ProcessOrderId = 0;
|
|
trade.ProcessOptDate = null;
|
|
trade.ProcessStatus = null;
|
|
trade.DividendDate = new DateTime(2000, 1, 1);
|
|
trade.SettlementFlag = 0;
|
|
|
|
//if (trade.TradeType == "结构化交易")
|
|
//{
|
|
// var tradeChildren = DbContext.trade.Where(x => x.ParentTradeId == trade.id).ToList();
|
|
// tradeChildren.ForEach(x =>
|
|
// {
|
|
// Execute(x.id, true, actionEnum);
|
|
// });
|
|
//}
|
|
//结构化交易回退时传入的是子交易的Id,此时只需要删掉主交易的Eod记录即可;
|
|
if (trade.ParentTradeId > 0)
|
|
{
|
|
RemoveEodTradeAndFutureInfo(false, trade.ParentTradeId, valueDate, backToBegin: backToBegin);
|
|
}
|
|
|
|
if (backToBegin)
|
|
{
|
|
if (trade.TradeType == "障碍期权")
|
|
{
|
|
trade.trade_barrier_option.KnockInOutStatus = null;
|
|
trade.trade_barrier_option.KnockInOutDate = null;
|
|
}
|
|
else if (trade.TradeType == "双鲨期权")
|
|
{
|
|
trade.trade_double_sharkfin_option.KnockInOutStatus = null;
|
|
trade.trade_double_sharkfin_option.KnockInOutDate = null;
|
|
}
|
|
else if (trade.TradeType == "凤凰期权")
|
|
{
|
|
trade.trade_autocall.KnockInOutStatus = null;
|
|
trade.trade_autocall.KnockInOutDate = null;
|
|
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
}
|
|
else if (trade.TradeType == "雪球期权")
|
|
{
|
|
if (trade.trade_snowball.IsInitialKnockedIn)
|
|
{
|
|
trade.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
|
|
trade.trade_snowball.KnockInOutDate = trade.TradeDate;
|
|
}
|
|
else
|
|
{
|
|
trade.trade_snowball.KnockInOutStatus = null;
|
|
trade.trade_snowball.KnockInOutDate = null;
|
|
}
|
|
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
}
|
|
else if (trade.TradeType == "区间累积期权")
|
|
{
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
}
|
|
else if (trade.TradeType == "累计期权")
|
|
{
|
|
trade.trade_accumulator_option.KnockOutDate = null;
|
|
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
|
|
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(trade, trade.TradeDate.Value);
|
|
|
|
var accumulator = trade.trade_accumulator_option;
|
|
if (accumulator != null)
|
|
{
|
|
var customObservDates = QdpHelper.ParseObservationDate(accumulator.KOObservationDates);
|
|
if (customObservDates == null)
|
|
{
|
|
customObservDates = Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn")
|
|
.BizDaysBetweenDatesExcluStartDay(trade.TradeDate.Value, trade.ExerciseDate.Value).ToArray();
|
|
}
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
|
|
accumulator.OriginalAccumuTradeAmount = (originTrade.OriginalNotional ?? 0) / underlying.CountRatio / customObservDates.Length;
|
|
accumulator.AccumuTradeAmount = accumulator.OriginalAccumuTradeAmount;
|
|
}
|
|
}
|
|
else if (trade.TradeType == "收益互换")
|
|
{
|
|
trade.trade_swap.GetFinalPrice = null;
|
|
trade.trade_swap.PayFinalPrice = null;
|
|
|
|
if (trade.IsGroup == 2)
|
|
{
|
|
var parentTrade = DbContext.trade.Find(trade.ParentTradeId);
|
|
parentTrade.StockEqvNotional += stockEqvNotionalUnwind;
|
|
parentTrade.TradeStatus = "确认成交";
|
|
|
|
var swapDetail = DbContext.trade_swap_detail.FirstOrDefault(x => x.TradeId == trade.ParentTradeId && x.ChildTradeId == trade.id);
|
|
swapDetail.Notional = trade.Notional;
|
|
}
|
|
}
|
|
else if (trade.TradeType == "气囊结构")
|
|
{
|
|
trade.trade_airbag.KnockInOutStatus = null;
|
|
trade.trade_airbag.KnockInOutDate = null;
|
|
trade.trade_airbag.KnockInOutNotional = null;
|
|
}
|
|
|
|
new ManuallyTradeObservationPriceService(OptUser).RemoveManuallyTradeObservationPrice(trade.id);
|
|
}
|
|
|
|
var actionService = new TradeActionDataService(this);
|
|
|
|
RemoveEodTradeAndFutureInfo(false, trade.id, valueDate, tradeCashIds, backToBegin: backToBegin);
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(false, trade,"交易回退");
|
|
|
|
var backAction = actionService.CreateTradeAction(tradeId, BLL.valuedateBLL.ValueDate, TradeActionType.BackTrade);
|
|
DbContext.TradeAction.Add(backAction);
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
actionService.SetInvalid(x => x.IsValid && x.TradeId == trade.id, backAction.id);
|
|
|
|
return trade;
|
|
}
|
|
|
|
public trade Execute(int tradeId, DateTime valueDate, bool ignoreBodTrade = false)
|
|
{
|
|
var trade = DbContext.trade.Find(tradeId);
|
|
|
|
if (trade == null)
|
|
{
|
|
throw new ServiceException("交易数据不存在");
|
|
}
|
|
|
|
new TradeExtendService(UserInfo, DbContext).SetTradeExtend(new[] { trade }, tracking: true);
|
|
|
|
if (valueDate == trade.TradeDate)
|
|
{
|
|
Execute(tradeId, true, TradeBackActionEnum.backTrade);
|
|
}
|
|
else
|
|
{
|
|
var bodTrade = DbContext.bod_Trade.FirstOrDefault(x => x.TradeId == trade.id && x.ValueDate == valueDate);
|
|
if (bodTrade != null)
|
|
{
|
|
if (trade.TradeType == "累计期权")
|
|
{
|
|
if (trade.trade_accumulator_option.SettlementMode == "实物交割" || trade.trade_accumulator_option.SettlementMode == "现金实物合并结算")
|
|
{
|
|
var query = from ob in DbContext.autocall_observation
|
|
join tf in DbContext.trade on ob.ConvTradeNumber equals tf.TradeNumber
|
|
where ob.TradeId == trade.id && (ob.SettlementMode == "转远期" || ob.SettlementMode == "转掉期" || ob.SettlementMode == "现金当日&转远期") && ob.PaymentDate >= valueDate && tf.ValidState != ConsGlobal.InValid
|
|
select ob;
|
|
|
|
if (query.Any())
|
|
{
|
|
throw new ServiceException($"操作失败,当前累计期权交易({trade.TradeNumber})已结算为远期交易,请将所有结算后的远期交易删除后重新执行回退操作!");
|
|
}
|
|
}
|
|
}
|
|
|
|
var ets = DbContext.ExtensionTime.Where(x => x.OptDate >= valueDate && x.IsValid).ToList();
|
|
if (ets.Any())
|
|
{
|
|
var extensionTimeService = new ExtensionTimeService(UserInfo, DbContext);
|
|
extensionTimeService.DelExtensionTime(trade.id, valueDate, false, true);
|
|
}
|
|
trade.ExerciseDate = bodTrade.trade.ExerciseDate;
|
|
trade.SettlementDate = bodTrade.trade.SettlementDate;
|
|
trade.SettlementFlag = bodTrade.trade.SettlementFlag;
|
|
//延期结算 >= 到期日才会有,为了回退取镜像数据
|
|
if (trade.ExerciseDate > valueDate && trade.SettlementFlag == 1)
|
|
{
|
|
trade.SettlementFlag = 0;
|
|
}
|
|
trade.SpotPrice = bodTrade.trade.SpotPrice;
|
|
trade.Strike = bodTrade.trade.Strike;
|
|
trade.OriginalNotional = bodTrade.trade.OriginalNotional;
|
|
trade.Notional = bodTrade.trade.Notional;
|
|
trade.TradeAmount = bodTrade.trade.TradeAmount;
|
|
trade.StockEqvNotional = bodTrade.trade.StockEqvNotional;
|
|
trade.UnWindDate = bodTrade.trade.UnWindDate;
|
|
trade.FinalPrice = bodTrade.trade.FinalPrice;
|
|
trade.UnWindNotional = bodTrade.trade.UnWindNotional;
|
|
trade.UnWindPrice = bodTrade.trade.UnWindPrice;
|
|
trade.HasPartialUnWind = bodTrade.trade.HasPartialUnWind;
|
|
trade.CheckStatus = bodTrade.trade.CheckStatus;
|
|
trade.TradeStatus = bodTrade.trade.TradeStatus;
|
|
trade.ProcessOrderId = bodTrade.trade.ProcessOrderId;
|
|
trade.ProcessOptDate = bodTrade.trade.ProcessOptDate;
|
|
trade.ProcessStatus = bodTrade.trade.ProcessStatus;
|
|
trade.DividendDate = bodTrade.trade.DividendDate;
|
|
trade.IsApproval = bodTrade.trade.IsApproval;
|
|
trade.OptId = UserId;
|
|
trade.OptName = UserName;
|
|
trade.OptDate = DateTime.Now;
|
|
|
|
if (trade.ParentTradeId > 0)
|
|
{
|
|
RemoveEodTradeAndFutureInfo(false, trade.ParentTradeId, valueDate, isDelTradeCashIncludeStartDay: true);
|
|
}
|
|
|
|
if (trade.TradeType == "障碍期权")
|
|
{
|
|
trade.trade_barrier_option.KnockInOutStatus = bodTrade.trade.trade_barrier_option.KnockInOutStatus;
|
|
trade.trade_barrier_option.KnockInOutDate = bodTrade.trade.trade_barrier_option.KnockInOutDate;
|
|
}
|
|
else if (trade.TradeType == "二元期权")
|
|
{
|
|
trade.trade_binary_option.UpperBarrier = bodTrade.trade.trade_binary_option.UpperBarrier;
|
|
}
|
|
else if (trade.TradeType == "双鲨期权")
|
|
{
|
|
trade.trade_double_sharkfin_option.BarrierHigh = bodTrade.trade.trade_double_sharkfin_option.BarrierHigh;
|
|
trade.trade_double_sharkfin_option.BarrierLow = bodTrade.trade.trade_double_sharkfin_option.BarrierLow;
|
|
trade.trade_double_sharkfin_option.StrikeHigh = bodTrade.trade.trade_double_sharkfin_option.StrikeHigh;
|
|
trade.trade_double_sharkfin_option.StrikeLow = bodTrade.trade.trade_double_sharkfin_option.StrikeLow;
|
|
trade.trade_double_sharkfin_option.KnockInOutStatus = bodTrade.trade.trade_double_sharkfin_option.KnockInOutStatus;
|
|
trade.trade_double_sharkfin_option.KnockInOutDate = bodTrade.trade.trade_double_sharkfin_option.KnockInOutDate;
|
|
}
|
|
else if (trade.TradeType == "凤凰期权")
|
|
{
|
|
trade.trade_autocall.CouponBarrier = bodTrade.trade.trade_autocall.CouponBarrier;
|
|
trade.trade_autocall.KIBarrier = bodTrade.trade.trade_autocall.KIBarrier;
|
|
trade.trade_autocall.KOBarrier = bodTrade.trade.trade_autocall.KOBarrier;
|
|
trade.trade_autocall.SpreadStrike = bodTrade.trade.trade_autocall.SpreadStrike;
|
|
trade.trade_autocall.SpreadStrike1 = bodTrade.trade.trade_autocall.SpreadStrike1;
|
|
trade.trade_autocall.KnockInOutStatus = bodTrade.trade.trade_autocall.KnockInOutStatus;
|
|
trade.trade_autocall.KnockInOutDate = bodTrade.trade.trade_autocall.KnockInOutDate;
|
|
}
|
|
else if (trade.TradeType == "雪球期权")
|
|
{
|
|
trade.trade_snowball.KIBarrier = bodTrade.trade.trade_snowball.KIBarrier;
|
|
trade.trade_snowball.KOBarrier = bodTrade.trade.trade_snowball.KOBarrier;
|
|
trade.trade_snowball.SpreadStrikeAtKO = bodTrade.trade.trade_snowball.SpreadStrikeAtKO;
|
|
trade.trade_snowball.SpreadStrikeAtKO1 = bodTrade.trade.trade_snowball.SpreadStrikeAtKO1;
|
|
trade.trade_snowball.SpreadStrikeAtMaturity = bodTrade.trade.trade_snowball.SpreadStrikeAtMaturity;
|
|
trade.trade_snowball.SpreadStrikeAtMaturity1 = bodTrade.trade.trade_snowball.SpreadStrikeAtMaturity1;
|
|
trade.trade_snowball.KnockInOutStatus = bodTrade.trade.trade_snowball.KnockInOutStatus;
|
|
trade.trade_snowball.KnockInOutDate = bodTrade.trade.trade_snowball.KnockInOutDate;
|
|
}
|
|
else if (trade.TradeType == "气囊结构")
|
|
{
|
|
trade.trade_airbag.HighStrike = bodTrade.trade.trade_airbag.HighStrike;
|
|
trade.trade_airbag.Barrier = bodTrade.trade.trade_airbag.Barrier;
|
|
}
|
|
else if (trade.TradeType == "区间累积期权")
|
|
{
|
|
trade.trade_rangeaccrual.UpperRange = bodTrade.trade.trade_rangeaccrual.UpperRange;
|
|
trade.trade_rangeaccrual.LowerRange = bodTrade.trade.trade_rangeaccrual.LowerRange;
|
|
}
|
|
else if (trade.TradeType == "累计期权")
|
|
{
|
|
trade.trade_accumulator_option.KOBarrier = bodTrade.trade.trade_accumulator_option.KOBarrier;
|
|
trade.trade_accumulator_option.KnockOutDate = bodTrade.trade.trade_accumulator_option.KnockOutDate;
|
|
trade.trade_accumulator_option.OriginalAccumuTradeAmount = bodTrade.trade.trade_accumulator_option.OriginalAccumuTradeAmount;
|
|
trade.trade_accumulator_option.AccumuTradeAmount = bodTrade.trade.trade_accumulator_option.AccumuTradeAmount;
|
|
}
|
|
else if (trade.TradeType == "收益互换")
|
|
{
|
|
trade.trade_swap.GetSpotPrice = bodTrade.trade.trade_swap.GetSpotPrice;
|
|
trade.trade_swap.PaySpotPrice = bodTrade.trade.trade_swap.PaySpotPrice;
|
|
trade.trade_swap.GetFinalPrice = bodTrade.trade.trade_swap.GetFinalPrice;
|
|
trade.trade_swap.PayFinalPrice = bodTrade.trade.trade_swap.PayFinalPrice;
|
|
}
|
|
|
|
var removeAutoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id && x.EndDate >= valueDate);
|
|
DbContext.autocall_observation.RemoveRange(removeAutoCallObservations);
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id && x.EndDate < valueDate && x.PaymentDate >= valueDate).ToList();
|
|
autoCallObservations.ForEach(x => x.PaymentDate = trade.ExerciseDate.Value);
|
|
|
|
if (trade.TradeType == "累计期权")
|
|
{
|
|
//由于回退操作是回退到valueDate的开盘状态,而换月操作是valueDate的日中操作,所以这里应该取前一天的;
|
|
var preValueDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(-1));
|
|
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(trade, preValueDate);
|
|
}
|
|
|
|
new ManuallyTradeObservationPriceService(OptUser).RemoveManuallyTradeObservationPrice(trade.id, valueDate);
|
|
}
|
|
else
|
|
{
|
|
//黑箱交易子交易有可能在回退日期之前就已经完全了结了,而其他子交易以及主交易,在回退日期之后还是存续状态,需要回退
|
|
if (ignoreBodTrade)
|
|
{
|
|
return trade;
|
|
}
|
|
|
|
var lastBodTrade = DbContext.bod_Trade.Where(x => x.TradeId == trade.id && x.ValueDate < valueDate).OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
|
if (lastBodTrade == null)
|
|
{
|
|
throw new ServiceException($"未查询到该交易在{valueDate.ToString("yyyy-MM-dd")}之前的收盘数据,只能回退到开仓日");
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException($"未查询到该交易在{valueDate.ToString("yyyy-MM-dd")}前一天的收盘数据,最近一次可以回退的日期为{lastBodTrade.ValueDate.ToString("yyyy-MM-dd")}");
|
|
}
|
|
}
|
|
}
|
|
|
|
var actionService = new TradeActionDataService(this);
|
|
|
|
using (var trans = DbContext.Database.BeginTransaction())
|
|
{
|
|
RemoveEodTradeAndFutureInfo(false, trade.id, valueDate, isDelTradeCashIncludeStartDay: true);
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(false, trade, "交易回退");
|
|
|
|
var backAction = actionService.CreateTradeAction(tradeId, BLL.valuedateBLL.ValueDate, TradeActionType.BackTrade);
|
|
DbContext.TradeAction.Add(backAction);
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
actionService.SetInvalid(x => x.IsValid && x.TradeId == trade.id, backAction.id);
|
|
|
|
trans.Commit();
|
|
}
|
|
|
|
return trade;
|
|
}
|
|
|
|
#region 分组回退
|
|
|
|
/// <summary>
|
|
/// 回退分组交易用
|
|
/// </summary>
|
|
/// <param name="tradeId"></param>
|
|
/// <param name="backToBegin"></param>
|
|
/// <returns></returns>
|
|
public trade BackGroup(int tradeId, bool backToBegin, TradeBackActionEnum actionEnum)
|
|
{
|
|
var tradeIds = new List<int>();
|
|
var tradeCashIds = new List<int>();
|
|
var extensionTimeService = new ExtensionTimeService(UserInfo, DbContext);
|
|
|
|
//主交易处理
|
|
var trade = DbContext.trade.Find(tradeId);
|
|
var valueDate = trade.TradeDate.Value;
|
|
tradeIds.Add(tradeId);
|
|
tradeCashIds.AddRange(BackGroupItem(tradeId, backToBegin, valueDate));
|
|
var tradeCashGrouActions = DbContext.trade_cash_group_action.Where(x => tradeCashIds.Contains(x.ParentTradeCashId));
|
|
DbContext.trade_cash_group_action.RemoveRange(tradeCashGrouActions);
|
|
//DbContext.SaveChanges();
|
|
|
|
//子交易处理
|
|
if (backToBegin)
|
|
{
|
|
var tradeChildren = DbContext.trade.Where(x => x.ParentTradeId == tradeId).ToList();
|
|
tradeIds.AddRange(tradeChildren.Select(x => x.id).ToList());
|
|
foreach (var tradeChild in tradeChildren)
|
|
{
|
|
tradeCashIds.AddRange(BackGroupItem(tradeChild.id, backToBegin, valueDate));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var tradeCashId = tradeCashIds.FirstOrDefault();
|
|
var tradeCashChildren = DbContext.trade_cash.Where(x => x.ParentTradeCashId == tradeCashId).ToList();
|
|
var tradeCash = tradeCashChildren.FirstOrDefault();
|
|
if (tradeCash != null)
|
|
{
|
|
var et = extensionTimeService.QueryExtensionTimeList(tradeId).Where(O => O.OptDate > tradeCash.ValueDate).OrderByDescending(O => O.OptDate).FirstOrDefault();
|
|
if (et != null)
|
|
{
|
|
extensionTimeService.DelExtensionTime(et.TradeId, et.id);
|
|
return trade;
|
|
}
|
|
}
|
|
foreach (var tradeCashChild in tradeCashChildren)
|
|
{
|
|
tradeCashIds.AddRange(BackGroupChildItemByStep(tradeCashChild.TradeId, tradeCashChild.id, valueDate));
|
|
tradeIds.AddRange(tradeCashChildren.Select(x => x.TradeId));
|
|
}
|
|
}
|
|
extensionTimeService.DelExtensionTime(tradeId, DateTime.MinValue, actionEnum != TradeBackActionEnum.EditMaturityDate);
|
|
RemoveGroupEodTradeAndFutureInfo(tradeIds, valueDate, tradeCashIds);
|
|
|
|
DbContext.SaveChanges();
|
|
return trade;
|
|
}
|
|
|
|
private List<int> BackGroupItem(int tradeId, bool backToBegin, DateTime valueDate)
|
|
{
|
|
var trade = DbContext.trade.Find(tradeId);
|
|
var tradeCashIds = new List<int>();
|
|
|
|
var actions = new List<string>()
|
|
{
|
|
"系统操作-行权费",
|
|
"系统操作-平仓费",
|
|
"系统操作-票息",
|
|
"系统操作-互换"
|
|
};
|
|
var tcQuery = DbContext.trade_cash.Where(x => x.TradeId == tradeId && actions.Contains(x.Action) && x.ValidState != "InValid" && !x.IsDeleted);
|
|
var eodTrade = DbContext.eod_trade.Where(O => O.TradeId == tradeId && O.ValueDate == trade.TradeDate).FirstOrDefault();
|
|
var originTrade = eodTrade?.trade;
|
|
if (originTrade == null)
|
|
{
|
|
originTrade = trade;
|
|
}
|
|
|
|
if (backToBegin)
|
|
{
|
|
var tcTemp = DbContext.trade_cash.Where(x => x.TradeId == tradeId && x.Action == "系统操作-期权费" && x.ValidState != ConsGlobal.InValid && !x.IsDeleted).FirstOrDefault();
|
|
if (tcTemp == null)
|
|
{
|
|
throw new ServiceException("未查询到该交易的开仓记录,请删除该笔交易重新操作");
|
|
}
|
|
originTrade.TradeStatus = ConsTrade.确认成交;
|
|
originTrade.OriginalNotional = tcTemp.Notional;
|
|
originTrade.Notional = tcTemp.Notional;
|
|
originTrade.StockEqvNotional = originTrade.OriginalStockEqvNotional ?? 0;
|
|
DividendService.SetDividendTradeData(trade, originTrade);
|
|
valueDate = trade.TradeDate.Value;
|
|
tradeCashIds.AddRange(tcQuery.Select(O => O.id).ToList());
|
|
|
|
trade.UnWindDate = null;
|
|
trade.FinalPrice = null;
|
|
trade.UnWindNotional = null;
|
|
trade.UnWindPrice = null;
|
|
trade.HasPartialUnWind = null;
|
|
}
|
|
else
|
|
{
|
|
if (!tcQuery.Any())
|
|
{
|
|
throw new ServiceException("未查询到该交易的了结记录,不可回退");
|
|
}
|
|
//有行权记录先回撤行权记录,没有行权记录,依次倒序撤回平仓记录
|
|
var tradeCash = tcQuery.OrderByDescending(x => x.id).FirstOrDefault();
|
|
trade.Notional = tradeCash.Notional;
|
|
if (trade.TradeType == "现金流交易")
|
|
{
|
|
if (tradeCash.UnwindType == "全部平仓")
|
|
{
|
|
trade.StockEqvNotional = tradeCash.Notional;
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = trade.StockEqvNotional + tradeCash.UnwindNotional.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (trade.IsUsePremiumRate == true)
|
|
{
|
|
//后期将会将所有已了结的交易均处理为0
|
|
if (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus))
|
|
{
|
|
trade.StockEqvNotional = 0;
|
|
}
|
|
trade.StockEqvNotional += trade.OriginalStockEqvNotional * tradeCash.UnwindPercentRate ?? 0;
|
|
}
|
|
else
|
|
{
|
|
if (trade.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(trade.BasisUnderlyingCode))
|
|
{
|
|
trade.StockEqvNotional += TradeHelper.GetStockEqvNotional(tradeCash.UnwindNotional / trade.OriginalNotional * trade.OriginalStockEqvNotional, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
}
|
|
}
|
|
valueDate = tradeCash.ValueDate;
|
|
tradeCashIds.Add(tradeCash.id);
|
|
|
|
//分步回退时将倒数第二条平仓记录赋值给trade
|
|
var lastSecondTradeCash = tcQuery.Where(x => x.id != tradeCash.id).OrderByDescending(x => x.id).FirstOrDefault();
|
|
if (lastSecondTradeCash != null && lastSecondTradeCash.Action == "系统操作-平仓费")
|
|
{
|
|
trade.UnWindDate = lastSecondTradeCash.ValueDate;
|
|
trade.FinalPrice = lastSecondTradeCash.FinalPrice;
|
|
trade.UnWindNotional = lastSecondTradeCash.UnwindNotional;
|
|
trade.UnWindPrice = lastSecondTradeCash.UnwindPrice;
|
|
trade.HasPartialUnWind = 1;
|
|
}
|
|
else
|
|
{
|
|
trade.UnWindDate = null;
|
|
trade.FinalPrice = null;
|
|
trade.UnWindNotional = null;
|
|
trade.UnWindPrice = null;
|
|
trade.HasPartialUnWind = null;
|
|
}
|
|
}
|
|
trade.TradeAmount = trade.Notional;
|
|
var varietyList = VarietyBLL.GetAllvarietyModel();
|
|
if (!string.IsNullOrWhiteSpace(trade.UnderlyingAssetClass))
|
|
{
|
|
var variety = varietyList.FirstOrDefault(O => O.VarietyName == trade.UnderlyingAssetClass);
|
|
if (variety != null)
|
|
{
|
|
trade.TradeUnit = variety.TradeUnit;
|
|
trade.TradeAmount /= variety.CountRatio;
|
|
}
|
|
}
|
|
|
|
trade.CheckStatus = null;
|
|
trade.OptId = UserId;
|
|
trade.OptName = UserName;
|
|
trade.OptDate = DateTime.Now;
|
|
trade.TradeStatus = ConsTrade.确认成交;
|
|
trade.ProcessOrderId = 0;
|
|
trade.ProcessOptDate = null;
|
|
trade.ProcessStatus = null;
|
|
trade.DividendDate = new DateTime(2000, 1, 1);
|
|
|
|
if (backToBegin && (trade.TradeType == "障碍期权" || trade.StructureType == "障碍期权"))
|
|
{
|
|
var trade_barrier_option = DbContext.trade_barrier_option.Where(x => x.TradeId == trade.id).FirstOrDefault();
|
|
trade_barrier_option.KnockInOutStatus = null;
|
|
trade_barrier_option.KnockInOutDate = null;
|
|
}
|
|
if (backToBegin && (trade.TradeType == "双鲨期权" || trade.StructureType == "双鲨期权"))
|
|
{
|
|
var trade_double_sharkfin_option = DbContext.trade_double_sharkfin_option.Where(x => x.TradeId == trade.id).FirstOrDefault();
|
|
trade_double_sharkfin_option.KnockInOutStatus = null;
|
|
trade_double_sharkfin_option.KnockInOutDate = null;
|
|
}
|
|
if (backToBegin && (trade.TradeType == "凤凰期权" || trade.StructureType == "凤凰期权"))
|
|
{
|
|
var trade_autocall = DbContext.trade_autocall.Where(x => x.TradeId == trade.id).FirstOrDefault();
|
|
trade_autocall.KnockInOutStatus = null;
|
|
trade_autocall.KnockInOutDate = null;
|
|
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
}
|
|
if (backToBegin && (trade.TradeType == "雪球期权" || trade.StructureType == "雪球期权"))
|
|
{
|
|
var trade_snowball = DbContext.trade_snowball.Where(x => x.TradeId == trade.id).FirstOrDefault();
|
|
trade_snowball.KnockInOutStatus = null;
|
|
trade_snowball.KnockInOutDate = null;
|
|
}
|
|
if (backToBegin && (trade.TradeType == "区间累积期权" || trade.StructureType == "区间累积期权"))
|
|
{
|
|
var autoCallObservations = DbContext.autocall_observation.Where(x => x.TradeId == trade.id);
|
|
DbContext.autocall_observation.RemoveRange(autoCallObservations);
|
|
}
|
|
if (backToBegin && (trade.TradeType == "收益互换" || trade.StructureType == "收益互换"))
|
|
{
|
|
var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == trade.id);
|
|
trade_swap.GetFinalPrice = null;
|
|
trade_swap.PayFinalPrice = null;
|
|
|
|
var trade_swap_details = DbContext.trade_swap_detail.Where(x => x.TradeId == trade.id).ToList();
|
|
trade_swap_details.ForEach(x =>
|
|
{
|
|
x.Notional = x.OriginalNotional;
|
|
x.SwapDate = null;
|
|
x.FinalPrice = null;
|
|
});
|
|
}
|
|
if (backToBegin)
|
|
{
|
|
new ManuallyTradeObservationPriceService(OptUser).RemoveManuallyTradeObservationPrice(trade.id);
|
|
}
|
|
|
|
DateTime mindate = trade.TradeDate ?? DateTime.MinValue;
|
|
var delTCArr = DbContext.trade_cash.Where(O => tradeCashIds.Contains(O.id));
|
|
if (delTCArr.Any())
|
|
{
|
|
delTCArr.ToList().ForEach(O => { O.ValidState = "InValid"; O.Status = TradeCashStatusEnum.已执行; O.OptDate = DateTime.Now; O.IsDeleted = true; });
|
|
mindate = delTCArr.Min(O => (DateTime?)O.ValueDate).GetValueOrDefault();
|
|
}
|
|
var trList = DbContext.trade_contract_r.Where(O => O.TradeId == tradeId && O.Type == "交易确认书").ToList();
|
|
foreach (var item in trList)
|
|
{
|
|
string infoTag = "_" + item.ContractCode.Replace("_", "-") + "_" + "了结_";
|
|
var notes = DbContext.sac_report_notes.Where(x => x.IsValid && x.InfoTag.Contains(infoTag) && x.ReportDate >= mindate).ToList();
|
|
foreach (var note in notes)
|
|
{
|
|
note.changeStatus = true;
|
|
}
|
|
}
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(false, trade, "交易回退");
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
return tradeCashIds;
|
|
}
|
|
|
|
private List<int> BackGroupChildItemByStep(int tradeId, int tradeCashId, DateTime valueDate)
|
|
{
|
|
var trade = DbContext.trade.Find(tradeId);
|
|
var tradeCashIds = new List<int>();
|
|
|
|
var actions = new List<string>()
|
|
{
|
|
"系统操作-行权费",
|
|
"系统操作-平仓费",
|
|
"系统操作-票息",
|
|
"系统操作-互换"
|
|
};
|
|
var tradeCash = DbContext.trade_cash.Find(tradeCashId);
|
|
var eodTrade = DbContext.eod_trade.Where(O => O.TradeId == tradeId && O.ValueDate == trade.TradeDate).FirstOrDefault();
|
|
var originTrade = eodTrade?.trade;
|
|
if (originTrade == null)
|
|
{
|
|
originTrade = trade;
|
|
}
|
|
|
|
if (tradeCash == null)
|
|
{
|
|
throw new ServiceException("未查询到该交易的了结记录,不可回退");
|
|
}
|
|
|
|
trade.Notional = tradeCash.Notional;
|
|
if (trade.TradeType == "现金流交易")
|
|
{
|
|
if (tradeCash.UnwindType == "全部平仓")
|
|
{
|
|
trade.StockEqvNotional = tradeCash.Notional;
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = trade.StockEqvNotional + tradeCash.UnwindNotional.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (trade.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(trade.BasisUnderlyingCode))
|
|
{
|
|
trade.StockEqvNotional += TradeHelper.GetStockEqvNotional(tradeCash.UnwindNotional / trade.OriginalNotional * trade.OriginalStockEqvNotional, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
else
|
|
{
|
|
trade.StockEqvNotional = TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor);
|
|
}
|
|
}
|
|
valueDate = tradeCash.ValueDate;
|
|
tradeCashIds.Add(tradeCash.id);
|
|
|
|
//分步回退时将倒数第二条平仓记录赋值给trade
|
|
var lastSecondTradeCash = DbContext.trade_cash.Where(x => x.TradeId == tradeId && x.id != tradeCash.id && x.ValidState != "InValid" && !x.IsDeleted).OrderByDescending(x => x.id).FirstOrDefault();
|
|
if (lastSecondTradeCash != null && lastSecondTradeCash.Action == "系统操作-平仓费")
|
|
{
|
|
trade.UnWindDate = lastSecondTradeCash.ValueDate;
|
|
trade.FinalPrice = lastSecondTradeCash.FinalPrice;
|
|
trade.UnWindNotional = lastSecondTradeCash.UnwindNotional;
|
|
trade.UnWindPrice = lastSecondTradeCash.UnwindPrice;
|
|
trade.HasPartialUnWind = 1;
|
|
}
|
|
else
|
|
{
|
|
trade.UnWindDate = null;
|
|
trade.FinalPrice = null;
|
|
trade.UnWindNotional = null;
|
|
trade.UnWindPrice = null;
|
|
trade.HasPartialUnWind = null;
|
|
}
|
|
|
|
trade.TradeAmount = trade.Notional;
|
|
var varietyList = VarietyBLL.GetAllvarietyModel();
|
|
if (!string.IsNullOrWhiteSpace(trade.UnderlyingAssetClass))
|
|
{
|
|
var variety = varietyList.FirstOrDefault(O => O.VarietyName == trade.UnderlyingAssetClass);
|
|
if (variety != null)
|
|
{
|
|
trade.TradeUnit = variety.TradeUnit;
|
|
trade.TradeAmount /= variety.CountRatio;
|
|
}
|
|
}
|
|
|
|
trade.CheckStatus = null;
|
|
trade.OptId = UserId;
|
|
trade.OptName = UserName;
|
|
trade.OptDate = DateTime.Now;
|
|
trade.TradeStatus = ConsTrade.确认成交;
|
|
trade.ProcessOrderId = 0;
|
|
trade.ProcessOptDate = null;
|
|
trade.ProcessStatus = null;
|
|
trade.DividendDate = new DateTime(2000, 1, 1);
|
|
|
|
DateTime mindate = trade.TradeDate ?? DateTime.MinValue;
|
|
var delTCArr = DbContext.trade_cash.Where(O => tradeCashIds.Contains(O.id));
|
|
if (delTCArr.Any())
|
|
{
|
|
delTCArr.ToList().ForEach(O => { O.ValidState = "InValid"; O.Status = TradeCashStatusEnum.已执行; O.OptDate = DateTime.Now; O.IsDeleted = true; });
|
|
mindate = delTCArr.Min(O => (DateTime?)O.ValueDate).GetValueOrDefault();
|
|
}
|
|
var trList = DbContext.trade_contract_r.Where(O => O.TradeId == tradeId && O.Type == "交易确认书").ToArray();
|
|
foreach (var item in trList)
|
|
{
|
|
string infoTag = "_" + item.ContractCode.Replace("_", "-") + "_" + "了结_";
|
|
var notes = (from note in DbContext.sac_report_notes
|
|
where
|
|
note.IsValid &&
|
|
note.InfoTag.Contains(infoTag) &&
|
|
note.ReportDate >= mindate
|
|
select note);
|
|
foreach (var note in notes)
|
|
{
|
|
note.changeStatus = true;
|
|
}
|
|
}
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(false, trade, "交易回退");
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
return tradeCashIds;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public enum TradeBackActionEnum
|
|
{
|
|
backTrade,
|
|
backTradeStepped,
|
|
EditMaturityDate
|
|
}
|
|
}
|