- EodPriceQueryService 新增 TryGetSettlementEodPrice:债券走中债估值、期货/股票走原路径 - 单笔 tradeExpireInner + 批量 MultipleTradeExpireConfirm 改用统一方法,修 GLMS-20260715-0002 债券期权到期报'结算价未找到' - 移除批量路径未初始化的 EodPriceProvider(对债券无效且有误导性的 footgun) - Layer2:标注 EodPriceProvider.Initialize 与 GetBondPrice 债券 ClosePrice/SettlePrice 净全价定义相反,待统一(不改逻辑) - 新增白盒单测覆盖债券标的到期取价(3用例 DB驱动,均通过)
771 lines
38 KiB
C#
771 lines
38 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|