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 设置期末价格和执行价格
|
|
|
|
var finalPrice = EodPriceQueryService.TryGetEodPrice(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;
|
|
|
|
//日终价格
|
|
var underlyingIds = tradeUnwindTrades.Select(t => t.UnderlyingId).ToList();
|
|
var EodPriceProvider = new EodPriceProvider(valueDate);
|
|
//批量结算的全是现金流交易就不用结算价
|
|
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 != "现金流交易")
|
|
{
|
|
//结算价
|
|
if (EodPriceProvider.TryGetEodPrice(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;
|
|
}
|
|
}
|
|
}
|