Files
zszq-trs/YLErpDAL/Modules/TradeModule/DealModule/TradeExpireConfirmService.cs
T
hjhan a17cdc2b74 fix(期权到期): 债券标的到期取价补查中债估值表,并标注债券净/全价映射不一致(Layer2)
- EodPriceQueryService 新增 TryGetSettlementEodPrice:债券走中债估值、期货/股票走原路径
- 单笔 tradeExpireInner + 批量 MultipleTradeExpireConfirm 改用统一方法,修 GLMS-20260715-0002 债券期权到期报'结算价未找到'
- 移除批量路径未初始化的 EodPriceProvider(对债券无效且有误导性的 footgun)
- Layer2:标注 EodPriceProvider.Initialize 与 GetBondPrice 债券 ClosePrice/SettlePrice 净全价定义相反,待统一(不改逻辑)
- 新增白盒单测覆盖债券标的到期取价(3用例 DB驱动,均通过)
2026-07-16 14:41:44 +08:00

771 lines
38 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.EntityFrameworkCore;
using System.Text;
using YLErp.BLL;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Eod;
using YLErp.BLL.EodSettlement;
using YLErp.Commons;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Modules.ClientModule;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 将单个交易标记为到期(并增加一条收益为零的trade_cash记录,直接到期的交易记录期末价格为0)
/// </summary>
public class TradeExpireConfirmService : TradeServiceBase
{
public TradeExpireConfirmService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeExpireConfirmService(YLBaseService baseService) : base(baseService)
{
}
public TradeExpireConfirmResult TradeExpireConfirm(int tradeId, double spotPrice = 0)
{
var td = DbContext.trade.Find(tradeId);
var error = checkCanTradeExpire(td);
//如果有配对交易,配对交易也要到期
if (!string.IsNullOrEmpty(td.PairTrade))
{
var pairid = Convert.ToInt32(td.PairTrade);
var pairtd = DbContext.trade.Find(pairid);
if (pairtd.TradeStatus == ConsTrade.已到期)
{
//执行单笔已到期
}
else
{
var error2 = checkCanTradeExpire(pairtd);
if (string.IsNullOrEmpty(error) && string.IsNullOrEmpty(error2))
{
//2个交易都可以到到期才到期
tradeExpireInner(td, spotPrice);
tradeExpireInner(pairtd, spotPrice);
//生成文档
if (PS.Config.IsAutoGenerateContracts)
{
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { td.id, pairid });
}
AddTradeOperationHistoryAndSetParentTradeInfo(false, td, "过期交易到期");
AddTradeOperationHistoryAndSetParentTradeInfo(false, pairtd, "过期交易到期");
DbContext.SaveChanges();
return new TradeExpireConfirmResult(td)
{
Success = true,
Message = $"确认到期成功,且配对交易{pairtd.TradeNumber}也到期成功"
};
}
return new TradeExpireConfirmResult(td)
{
Success = false,
Message = error + error2
};
}
}
//无匹配交易
if (string.IsNullOrEmpty(error))
{
tradeExpireInner(td, spotPrice);
//生成文档
if (PS.Config.IsAutoGenerateContracts)
{
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { td.id });
}
SaveTradeOperationHistory(td, "过期交易到期");
return new TradeExpireConfirmResult(td) { Success = true, Message = "确认到期成功" };
}
return new TradeExpireConfirmResult(td) { Success = false, Message = error };
}
/// <summary>
/// 检查交易是否可以到期,返回错误信息,空为成功
/// </summary>
private string checkCanTradeExpire(trade td)
{
if (PS.Config.TradeElement.ExecuteAfterGeneratedConfirmDoc && !td.HasGeneratedConfirmBook() && td.TradeType != "自定义交易")
{
return "请先去生成交易确认书";
}
var valueDate = valuedateBLL.ValueDate;
if (td.TradeStatus == ConsTrade.确认成交 && ((td.ExerciseMode == "European" && td.ExerciseDate <= valueDate) || (td.ExerciseMode == "American" && valueDate >= td.StartDate) || string.IsNullOrEmpty(td.ExerciseMode)))
{
return string.Empty;
}
return $"确认到期失败,交易必须为[确认成交]状态并且[欧式期权]必须到期日{td.ExerciseDate}之后,美式期权必须在开始日{td.StartDate}之后";
}
/// <summary>
/// 执行到期
/// </summary>
private void tradeExpireInner(trade td, double spotPrice = 0)
{
//到期日期如果是节假日,取节假日后的第一个工作日
var exerciseDate = QdpCalendarHelper.GetNonHoliday(td.ExerciseDate ?? valuedateBLL.ValueDate);
var valueDate = valuedateBLL.ValueDate;
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = exerciseDate;
td.OptDate = DateTime.Now;
// 直接到期的交易设置
var tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == td.id && t.Action == ClientCashInCashOut.系统操作_行权费 && !t.IsDeleted);
if (tc == null)
{
tc = new trade_cash();
DbContext.trade_cash.Add(tc);
}
#region 设置期末价格和执行价格
// 债券标的需走中债估值表取价,原 TryGetEodPrice 只查期货/股票两表会漏掉债券,导致"结算价未找到"。
// 统一改用债券感知的 TryGetSettlementEodPrice(见 EodPriceQueryService)。
var finalPrice = EodPriceQueryService.TryGetSettlementEodPrice(exerciseDate, td.UnderlyingCode, out var eodPrice)
? eodPrice.GetPrice(td.SettlementType) : 0;
if (finalPrice <= 0)
{
throw new Exception("标的收盘价不合法,请检查后再进行到期操作");
}
SettlementCalcCommons.SetPossibleExec(tc, td, finalPrice, true);
if (td.TradeType != "亚式期权")
{
tc.FinalPrice = finalPrice;
tc.Strike = td.Strike;
}
else
{
tc.SpotPrice = spotPrice;
}
//修改只收买方行权费
tc.ExtraAmount = Math.Abs(td.StockEqvNotional * (valuedateBLL.SystemDate.ExerciseCostRatioReal ?? 0)) * ("卖出".Equals(td.BuySell) ? 1 : 0);
tc.Amount += tc.ExtraAmount ?? 0;
#endregion
tc.ValidState = "Valid";
tc.OptId = UserId;
tc.OptName = UserName;
tc.OptDate = DateTime.Now;
tc.Action = ClientCashInCashOut.系统操作_行权费;
tc.IsLastAction = true;
tc.ValueDate = exerciseDate;
tc.Notional = td.Notional;
var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode);
tc.TradeAmount = tc.Notional / CountRatio;
tc.Status = TradeCashStatusEnum.已执行;
tc.TradeType = td.BuySell;
tc.TradeId = td.id;
tc.ParentTradeId = td.ParentTradeId;
tc.ExerciseWay = TradeCashExerciseWayEnum.到期行权;
tc.UnwindVol = 0;//到期交易不需要存平仓波动率
DbContext.SaveChanges();
if (tc.IsPossibleExec)
{
new TradeCashService(this).SaveTradeCashDetail(tc);
//增加出入金记录
var cl = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
//客户和交易员反方向
Money = tc.Amount * -1,
HappenDate = tc.ValueDate,
State = ClientCashInCashOut.已确认,
//ee.OpenBankId = bc.id + "";
//ee.OpenBankCard = bc.Card;
TradeId = tc.TradeId,
TradeCashId = tc.id,
OptId = tc.OptId,
OptName = tc.OptName,
OptDate = tc.OptDate,
CreatorId = tc.OptId,
CreatorName = tc.OptName,
CreateDate = tc.OptDate,
Action = ClientCashInCashOut.系统操作_行权费,
TradeNumber = td.TradeNumber,
IsGroup = td.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
td.TradeStatus = ConsTrade.已执行;
}
//结构化子交易到期行权时,检查另外一个兄弟子交易是否已了结,如果已了结,则主交易状态也跟着当前子交易状态变化
if (td.ParentTradeId > 0)
{
var otherTrade = DbContext.trade.Where(x => x.ParentTradeId == td.ParentTradeId && x.id != td.id);
if (otherTrade.All(n => ConsTrade.TradeCompleteStatus.Contains(n.TradeStatus)))
{
var parentTrade = DbContext.trade.Find(td.ParentTradeId);
parentTrade.TradeStatus = td.TradeStatus;
parentTrade.UnWindDate = td.UnWindDate;
}
}
//删除E/Bod数据
RemoveEodTradeAndFutureInfo(false, td.id, tc.ValueDate);
DbContext.SaveChanges();
}
/// <summary>
/// 将多个交易标记为到期
/// </summary>
public void MultipleTradeExpireConfirm(IEnumerable<int> tradeIds, out string successMsg)
{
var valueDate = valuedateBLL.ValueDate;
successMsg = "执行到期成功:";
//批量行权到期 获取所有交易到期日小于当前系统日期非通道交易
var buySellList = new List<string> { "卖出", "买入" };
//所有应行权到期的通道自营交易(如果前一天是假日,要显示包含假日的交易)
var preday = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(-1));
var dateEnd = valueDate.AddDays(1);
IQueryable<trade> tradeQuery = DbContext.trade;
if (tradeIds != null && tradeIds.Any(n => n > 0))
{
var selectParentTradeIds = DbContext.trade.Where(t => tradeIds.Contains(t.id) && t.ParentTradeId != 0).Select(x => x.ParentTradeId).ToHashSet().ToArray();
tradeQuery = DbContext.trade.Where(t => tradeIds.Contains(t.id) || tradeIds.Contains(t.ParentTradeId) || selectParentTradeIds.Contains(t.ParentTradeId));
}
tradeQuery = tradeQuery.Where(t => t.ClientId > 0
&& t.ExerciseDate > preday && t.ExerciseDate < dateEnd && t.TradeStatus == ConsTrade.确认成交
&& t.ValidState != "InValid" && t.ClientId > 0 && t.TradeNumber != null
&& !ConsTrade.NonOptionTradeTypes.Contains(t.TradeType) && t.TradeType != "自定义交易");
var needKnockInTradeIds = tradeQuery.Where(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权").Select(x => x.id).ToList();
var unKnockInTradeIds = DbContext.trade_autocall.Where(x => needKnockInTradeIds.Contains(x.TradeId) && x.KnockInOutStatus != ConsTrade.KnockState.KnockedIn).Select(x => x.TradeId).Union(DbContext.trade_snowball.Where(x => needKnockInTradeIds.Contains(x.TradeId) && x.KnockInOutStatus != "KnockedIn").Select(x => x.TradeId)).ToList();
tradeQuery = tradeQuery.Where(x => !unKnockInTradeIds.Contains(x.id));
var tradeUnwindTrades = tradeQuery.ToList();
if (!tradeUnwindTrades.Any())
{
throw new ServiceException($"不存在可批量执行到期的交易,或者勾选的交易不可被批量执行到期");
}
var tradeCashList = new List<trade_cash>();
var clientCashList = new List<ClientCashInCashOut>();
trade_cash tradeCash = null;
//日终价格
//批量结算的全是现金流交易就不用结算价
if (!EodPriceQueryService.CheckDbExists(valueDate) && tradeQuery.Any(t => t.TradeType != "现金流交易"))
{
throw new ServiceException($"当日交易的结算价或收盘价未找到!");
}
var sbMsg = new StringBuilder().AppendLine(successMsg);
var parentTradeIds = new List<int>();
foreach (var t in tradeUnwindTrades)
{
if (t.ParentTradeId > 0 && t.IsGroup == 0)
{
parentTradeIds.Add(t.ParentTradeId);
}
if (PS.Config.TradeElement.ExecuteAfterGeneratedConfirmDoc && !t.HasGeneratedConfirmBook() && t.TradeType != "远期" && t.TradeType != "现金流交易")
{
throw new ServiceException($"交易{t.TradeNumber}的交易确认书没有生成。");
}
double settlePrice = 0;
var CountRatio = 1;
if (t.TradeType != "现金流交易")
{
//结算价(债券感知统一取价:债券走中债估值,期货/股票走原路径,见 EodPriceQueryService.TryGetSettlementEodPrice
if (EodPriceQueryService.TryGetSettlementEodPrice(valueDate, t.UnderlyingCode, out var eodPrice))
{
settlePrice = eodPrice.GetPrice(t.SettlementType);
}
else
{
throw new ServiceException($"交易{t.TradeNumber},标的[{t.UnderlyingCode}]{valueDate}结算价或收盘价或参考价不存在!");
}
CountRatio = UnderlyingDataProvider.GetCountRatio(t.UnderlyingCode);
}
var client = ClientDataQueryService.GetClient(t.ClientId);
if (client == null)
{
throw new ServiceException($"交易{t.TradeNumber}的客户{t.ClientName}在系统中不存在!请检查数据后再操作。");
}
if (t.TradeType == "收益互换")
{
//t.trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == t.id);
//if(t.trade_swap == null)
//{
// throw new ServiceException($"交易{t.TradeNumber}对应的trade_swap数据不存在!");
//}
//var unwindPercentRate = t.OriginalNotional > 0 ? t.Notional / t.OriginalNotional : 0;
//var initialAmount = t.SpotPrice > 0 ? (settlePrice / (t.FinalPrice ?? t.SpotPrice) - 1) * (t.OriginalStockEqvNotional * (t.FinalPrice ?? t.SpotPrice) / t.SpotPrice) * unwindPercentRate * (t.BuySell == "买入" ? 1 : -1) * (t.OptionType == "看涨" ? 1 : -1) : 0;
//int days = (valueDate - (t.UnWindDate ?? t.StartDate.Value)).Days;
//var extraAmount = t.OriginalStockEqvNotional * unwindPercentRate * t.trade_swap.GetSwapRate * ((double)days / t.trade_swap.AnnualDays) * (t.BuySell == "买入" ? -1 : 1);
//var amount = (initialAmount ?? 0) + (extraAmount ?? 0);
//tradeCash = new trade_cash
//{
// ValueDate = valueDate,
// TradeId = t.id,
// ExceciseType = "到期行权",
// CallPut = t.CallPut,
// Notional = t.Notional,
// UnwindNotional = t.Notional,
// TradeAmount = t.Notional / CountRatio,
// UnwindPercentRate = unwindPercentRate,
// NotionalPercentRate = unwindPercentRate,
// FinalPrice = settlePrice.Value,
// Amount = amount,
// ExtraAmount = extraAmount,
// Action = ClientCashInCashOut.系统操作_互换,
// Status = TradeCashStatusEnum.已执行,
// OptId = UserId,
// OptName = UserName,
// OptDate = DateTime.Now,
// ExerciseWay = TradeCashExerciseWayEnum.到期行权
//};
//tradeCashList.Add(tradeCash);
//clientCashList.Add(new ClientCashInCashOut
//{
// Direction = "应收",
// Number = UniqueTimeId.GetStr(),
// ClientId = client.id,
// ClientNumber = client.Number,
// ClientName = client.Name,
// ClientIdentityNumber = client.IdentificationNumber,
// //客户和交易员反方向
// Money = amount * -1,
// HappenDate = valueDate == DateTime.Now.Date ? DateTime.Now : valueDate,
// State = ClientCashInCashOut.已确认,
// TradeId = tradeCash.TradeId,
// OptId = tradeCash.OptId,
// OptName = tradeCash.OptName,
// OptDate = tradeCash.OptDate,
// Action = ClientCashInCashOut.系统操作_互换,
// TradeNumber = t.TradeNumber
//});
//t.TradeStatus = ConsTrade.已到期;
//t.UnWindDate = valueDate;
//t.FinalPrice = settlePrice;
}
else
{
var parentTradeCash = DbContext.trade_cash.OrderByDescending(x => x.id).FirstOrDefault(x => x.TradeId == t.ParentTradeId && x.Action != "系统操作-期权费" && x.Status == null && x.ValidState != "InValid");
var parentTrade = DbContext.trade.Find(t.ParentTradeId);
if (t.IsGroup == 2)
{
//主交易和子交易都是当天到期
if (parentTrade.ExerciseDate > preday && parentTrade.ExerciseDate < dateEnd)
{
if (parentTradeCash == null)
{
parentTradeCash = new trade_cash()
{
Action = ClientCashInCashOut.系统操作_行权费,
ExerciseWay = "到期行权",
IsLastAction = true,
TradeType = parentTrade.BuySell,
Notional = parentTrade.Notional,
TradeAmount = parentTrade.TradeAmount,
UnwindNotional = parentTrade.Notional,
UnwindTradeAmount = parentTrade.TradeAmount,
UnwindPercentRate = parentTrade.OriginalNotional > 0 ? parentTrade.Notional / parentTrade.OriginalNotional : 0,
TradeId = t.ParentTradeId,
HappenedDate = valueDate,
ValueDate = valueDate,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now
};
parentTrade.TradeStatus = "已到期";
parentTrade.UnWindDate = valueDate;
parentTrade.FinalPrice = settlePrice;
parentTrade.UnWindNotional = parentTrade.Notional;
parentTrade.Notional = 0;
parentTrade.TradeAmount = 0;
parentTrade.StockEqvNotional = 0;
DbContext.trade_cash.Add(parentTradeCash);
DbContext.SaveChanges();
#region---存入ClientCashInCashOut---
var cl = ClientDataQueryService.GetClient(parentTrade.ClientId, true);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
HappenDate = parentTradeCash.ValueDate,
State = ClientCashInCashOut.已确认,
OptId = parentTradeCash.OptId,
OptName = parentTradeCash.OptName,
OptDate = parentTradeCash.OptDate,
CreatorId = parentTradeCash.OptId,
CreatorName = parentTradeCash.OptName,
CreateDate = parentTradeCash.OptDate,
TradeId = parentTradeCash.TradeId,
TradeCashId = parentTradeCash.id,
Action = parentTradeCash.Action,
TradeNumber = parentTrade.TradeNumber,
IsGroup = parentTrade.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
#endregion
}
}
else
{
if (parentTradeCash == null)
{
//主交易非当日到期
parentTrade.UnWindDate = valueDate;
parentTrade.FinalPrice = settlePrice;
var childrenExerciseDateCount = DbContext.trade.Where(x => x.ParentTradeId == parentTrade.id && x.ValidState != "InValid").Select(x => x.ExerciseDate).Distinct().Count();
parentTrade.UnWindNotional = 1.0 / childrenExerciseDateCount * parentTrade.OriginalNotional;
parentTradeCash = new trade_cash()
{
Action = ClientCashInCashOut.系统操作_平仓费,
ExerciseWay = "提前终止行权",
IsLastAction = false,
TradeType = parentTrade.BuySell,
Notional = parentTrade.Notional,
TradeAmount = parentTrade.TradeAmount,
UnwindNotional = parentTrade.UnWindNotional,
UnwindTradeAmount = parentTrade.UnWindNotional / (parentTrade.CountRatio ?? 1),
UnwindPercentRate = parentTrade.OriginalNotional > 0 ? parentTrade.UnWindNotional / parentTrade.OriginalNotional : 0,
TradeId = t.ParentTradeId,
HappenedDate = valueDate,
ValueDate = valueDate,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now
};
parentTrade.Notional -= (parentTradeCash.UnwindNotional ?? 0);
parentTrade.TradeAmount -= (parentTradeCash.UnwindTradeAmount ?? 0);
parentTrade.StockEqvNotional -= ((parentTrade.OriginalStockEqvNotional ?? 0) * (parentTradeCash.UnwindPercentRate ?? 0));
DbContext.trade_cash.Add(parentTradeCash);
DbContext.SaveChanges();
#region---存入ClientCashInCashOut---
var cl = ClientDataQueryService.GetClient(parentTrade.ClientId, true);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
HappenDate = parentTradeCash.ValueDate,
State = ClientCashInCashOut.已确认,
OptId = parentTradeCash.OptId,
OptName = parentTradeCash.OptName,
OptDate = parentTradeCash.OptDate,
CreatorId = parentTradeCash.OptId,
CreatorName = parentTradeCash.OptName,
CreateDate = parentTradeCash.OptDate,
TradeId = parentTradeCash.TradeId,
TradeCashId = parentTradeCash.id,
Action = parentTradeCash.Action,
TradeNumber = parentTrade.TradeNumber,
IsGroup = parentTrade.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
#endregion
}
}
var tradeCashGroupAction = new trade_cash_group_action()
{
TradeId = t.id,
ParentTradeId = t.ParentTradeId,
ParentTradeCashId = parentTradeCash.id,
Status = "已完成",
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
IsFinishedUnwindPercent = true
};
DbContext.trade_cash_group_action.Add(tradeCashGroupAction);
}
tradeCash = new trade_cash
{
ValueDate = valueDate,
TradeId = t.id,
ParentTradeId = t.ParentTradeId,
ExceciseType = "现金",
CallPut = t.CallPut,
Strike = t.IsMoneynessOptionData ? (t.Strike * t.SpotPrice) : t.Strike,
Notional = t.Notional,
UnwindNotional = t.Notional,
UnwindPercentRate = t.OriginalNotional > 0 ? t.Notional / t.OriginalNotional : 0,
FinalPrice = settlePrice,
Action = ClientCashInCashOut.系统操作_行权费,
IsLastAction = true,
Status = TradeCashStatusEnum.已执行,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
ExerciseWay = TradeCashExerciseWayEnum.到期行权,
TradeType = t.BuySell
};
tradeCash.SpotPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(t.UnderlyingCode);
tradeCash.TradeAmount = tradeCash.Notional / CountRatio;
//计算行权收益
SettlementCalcCommons.SetPossibleExec(tradeCash, t, tradeCash.FinalPrice ?? 0.0, true);
if (t.TradeType == "现金流交易")
{
var udm = new underlying_manager
{
QuotationDate = valueDate
};
var OptionValue = ValueCalculator.GetOptionValueResultV2(
userId: OptUser.UserId.ToString(),
underlying: udm,
trade: t,
vols: new double[] { t.StockEqvNotional },
spotPrices: new double[] { udm.Price ?? 0.0 },
fixing: null,
timeToMaturityDays: 0,
preciseTimeMode: true,
request: QdpPricingRequest.BASIC_PRICING);
var amount = OptionValue.Pv;
tradeCash.IsPossibleExec = true;
tradeCash.Amount = OtcFormatHelper.FormatValue(amount, 2);
tradeCash.InitialAmount = OtcFormatHelper.FormatValue(amount, 2);
}
//额外的行权费
//根据买卖方向 针对买方才会收行权费
var ExtraAmount = Math.Abs(t.StockEqvNotional * (valuedateBLL.SystemDate.ExerciseCostRatioReal ?? 0)) * ("卖出".Equals(t.BuySell) ? 1 : 0);
//行权净收益
var Amount = tradeCash.Amount + ExtraAmount;
//判断客户行权如果收益小于0 则行权收益为0 则不可行权
var flag = true;
if (EodOperationBase.GetSign(t.BuySell) * Amount <= 0 && (t.TradeType != "雪球期权" && t.TradeType != "凤凰期权"))
{
Amount = 0;
flag = false;
}
//查看是否可以行权 行权收益为正数时可行权
if (tradeCash.IsPossibleExec && flag)
{
sbMsg.Append("交易编号[").Append(t.TradeNumber).Append("],行权收益:").Append(Amount).AppendLine(",行权。");
tradeCash.Amount = Amount;
tradeCash.ExtraAmount = ExtraAmount;
tradeCash.UnwindType = "全部行权";
t.TradeStatus = ConsTrade.已执行;
clientCashList.Add(new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = client.id,
ClientNumber = client.Number,
ClientName = client.Name,
//客户和交易员反方向
Money = Amount * -1,
HappenDate = valueDate == DateTime.Now.Date ? DateTime.Now : valueDate,
State = ClientCashInCashOut.已确认,
//ee.OpenBankId = bc.id + "";
//ee.OpenBankCard = bc.Card;
TradeId = tradeCash.TradeId,
OptId = tradeCash.OptId,
OptName = tradeCash.OptName,
OptDate = tradeCash.OptDate,
CreatorId = tradeCash.OptId,
CreatorName = tradeCash.OptName,
CreateDate = tradeCash.OptDate,
Action = ClientCashInCashOut.系统操作_行权费,
TradeNumber = t.TradeNumber,
IsGroup = t.IsGroup
});
}
else
{
sbMsg.Append("交易编号[").Append(t.TradeNumber).AppendLine("],到期。");
tradeCash.Amount = 0.0;
tradeCash.ExtraAmount = 0.0;
tradeCash.UnwindType = "到期";
tradeCash.ValueDate = t.ExerciseDate.Value;
t.TradeStatus = "已到期";
}
if (t.TradeType != "现金流交易")
{
tradeCash.UnwindPrice = TradeHelper.GetTradeSinglePriceByTradePrice(tradeCash.Amount, t.Notional, t.PrincipalSum(), t.BuySell, t.TradeType, false);
tradeCash.UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradePrice(tradeCash.Amount, t.StockEqvNotional, t.ParticipationRate, t.PrincipalSum(), t.AnnualizeFactor, t.BuySell, t.TradeType, false);
t.FinalPrice = tradeCash.FinalPrice;
}
t.UnWindDate = valueDate;
//行权/到期都不需要平仓波动率
tradeCash.UnwindVol = 0;
tradeCashList.Add(tradeCash);
if (t.IsGroup == 2)
{
tradeCash.ParentTradeCashId = parentTradeCash.id;
parentTradeCash.FinalPrice = settlePrice;
parentTradeCash.Amount += tradeCash.Amount;
parentTradeCash.UnwindPrice = TradeHelper.GetTradeSinglePriceByTradePrice(parentTradeCash.Amount, parentTradeCash.UnwindNotional, parentTrade.PrincipalSum(), parentTrade.BuySell, parentTrade.TradeType, false);
parentTradeCash.UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradePrice(parentTradeCash.Amount, parentTrade.OriginalStockEqvNotional * parentTradeCash.UnwindPercentRate, parentTrade.ParticipationRate, parentTrade.PrincipalSum(), parentTrade.AnnualizeFactor, parentTrade.BuySell, parentTrade.TradeType, false);
//当天到期的子交易均已经到期处理了
if (!DbContext.trade.Any(x => x.ParentTradeId == parentTrade.id && x.ValidState != "InValid" && x.ExerciseDate > preday && x.ExerciseDate < dateEnd && x.id != t.id && !ConsTrade.TradeCompleteStatus.Contains(x.TradeStatus)))
{
parentTradeCash.Status = TradeCashStatusEnum.已执行;
AddTradeOperationHistoryAndSetParentTradeInfo(false, parentTrade, "批量执行到期");
}
}
DbContext.SaveChanges();
}
AddTradeOperationHistoryAndSetParentTradeInfo(false, t, "批量执行到期");
};
if (tradeCashList.Count > 0)
{
DbContext.trade_cash.AddRange(tradeCashList);
DbContext.SaveChanges();
}
if (clientCashList.Count > 0)
{
clientCashList.ForEach(x => x.TradeCashId = tradeCashList.First(y => y.TradeId == x.TradeId).id);
DbContext.ClientCashInCashOut.AddRange(clientCashList);
}
DbContext.SaveChanges();
var tradeCashDetails = new List<trade_cash_detail>();
tradeCashList.ForEach(x =>
{
var tradeCashDetail = new trade_cash_detail()
{
TradeId = x.TradeId,
TradeCashId = x.id,
Amount = x.Amount,
ExtraAmount = x.ExtraAmount,
SinglePrice = x.UnwindPrice,
SinglePricePercentRate = x.UnwindPricePercentRate,
Action = x.Action,
ValueDate = x.ValueDate,
OptDate = DateTime.Now,
OptId = x.OptId,
OptName = x.OptName
};
tradeCashDetails.Add(tradeCashDetail);
});
DbContext.trade_cash_detail.AddRange(tradeCashDetails);
DbContext.SaveChanges();
//结构化子交易到期时,检查子交易是否都已了结,如果已了结,则主交易状态也跟着当前子交易状态变化
if (parentTradeIds.Count > 0)
{
foreach (var id in parentTradeIds)
{
var tradeStatus = ConsTrade.确认成交;
var UnwindDate = DateTime.MinValue;
var tradeChildren = DbContext.trade.Where(x => x.ParentTradeId == id);
foreach (var child in tradeChildren)
{
if (ConsTrade.TradeCompleteStatus.Contains(child.TradeStatus))
{
if (child.UnWindDate > UnwindDate)
{
UnwindDate = child.UnWindDate ?? DateTime.MinValue;
tradeStatus = child.TradeStatus;
}
}
else
{
tradeStatus = ConsTrade.确认成交;
}
}
if (tradeStatus != ConsTrade.确认成交)
{
var tradeParent = DbContext.trade.Find(id);
if (tradeParent != null)
{
tradeParent.TradeStatus = tradeStatus;
}
DbContext.SaveChanges();
}
}
}
//生成文档
if (PS.Config.IsAutoGenerateContracts)
{
new TradeContractGenerateService(this).GenerateContractsAsync(tradeCashList.Select(tc => tc.TradeId).ToList());
}
successMsg = sbMsg.ToString();
}
}
/// <summary>
/// 到期确认操作结果
/// </summary>
public class TradeExpireConfirmResult
{
public trade Trade;
public bool Success;
public string Message;
public TradeExpireConfirmResult(trade trade)
{
Trade = trade;
}
}
}