Files
zszq-trs/YLErpDAL/Modules/TradeModule/DealModule/OtcTradeCloseService.cs
T
2024-05-09 14:06:26 +08:00

909 lines
39 KiB
C#

using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.Foundation.Utilities;
using Qdp.Pricing.Library.Common.Interfaces;
using Qdp.Pricing.Library.Options.Products.Asian;
using System.Data;
using System.Globalization;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.Commons;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Model.Enum;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.ClientModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 交易了结处理服务
/// </summary>
public class OtcTradeCloseService : TradeCashService
{
public OtcTradeCloseService(OptUserInfo userInfo) : base(userInfo)
{
}
public OtcTradeCloseService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 导入批量了结
/// </summary>
/// <param name="streamIn"></param>
/// <param name="totalNum">当前文件中的目标期权总条数</param>
/// <param name="successNum">成功入库的数量</param>
public void BatchCloseByImport(Stream streamIn, out int totalNum, out int successNum)
{
totalNum = 0;
successNum = 0;
var rowIndex = 0;
try
{
var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0);
if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2)
{
throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" };
}
var table = ds.Tables[0];
var reader = new DataRowReader(table);
totalNum = table.Rows.Count;
rowIndex = 1;
foreach (var row in table.Rows.Cast<DataRow>().Skip(1))
{
rowIndex++;
if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString())))
{
totalNum--;
continue;
}
reader.SetDataRow(row);
var model = new TradeCloseRequestModel
{
ImportFlag = "批量了结导入",
SkipWorkflow = true,
TradeNumber = reader.GetString("交易编号", true),
CloseType = reader.GetString("了结方式", true),
CloseDate = reader.GetDate("了结日期", true).Value,
CloseTradeAmount = reader.GetDouble("了结数量"),
CloseTradeAmountRate = reader.GetPercent("了结数量比例"),
UnderlyingPrice = reader.GetDouble("了结标的价格", true).Value
};
if (model.CloseType == "平仓")
{
model.UnwindTotalFee = reader.GetDouble("平仓总额", "了结总额");
model.UnwindPrice = reader.GetDouble("平仓单价", "了结单价");
model.UnwindPriceRate = reader.GetPercent("平仓单价比例", "了结单价比例");
model.UnwindVolatility = reader.GetPercent("平仓波动率");
}
if (model.CloseType == "到期")
{
model.UnwindTotalFee = reader.GetDouble("平仓总额", "了结总额");
}
ExecuteClose(model);
successNum++;
}
}
catch (ServiceException se)
{
if (se.Tag != null)
{
throw;
}
throw new ServiceException($"已成功了结{successNum}条,第{rowIndex}行,{se.Message}");
}
catch (Exception ex)
{
LogFactory.GetLogger("批量了结导入").Error(ex);
throw new ServiceException($"已成功了结{successNum}条,第{rowIndex}行,发生错误:{ex.Message}", ex);
}
}
/// <summary>
/// 执行了结(适用于交易导入、批量了结、api了结,如果用作其它用途需要注意IsApproval的赋值)
/// </summary>
public TradeCloseResult ExecuteClose(TradeCloseRequestModel req, bool isAveragePriceOverride = false)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
var dbTrade = DbContext.trade.FirstOrDefault(t => t.TradeNumber == req.TradeNumber && t.ValidState != "InValid");
if (dbTrade == null)
{
throw new ServiceException("交易数据 不存在,交易编号:" + req.TradeNumber);
}
CheckRequest(dbTrade, req);
if (dbTrade.Warning)
{
new TradeDalModule.TradeDalService(OptUser).RollbackToBeforeSettle(dbTrade, req.CloseDate);
}
CalcForClose(dbTrade, req, isAveragePriceOverride, out var countRatio, out var closeNotional);
var isPartialClose = Math.Abs(closeNotional - dbTrade.Notional) > 1e-4;
if (isPartialClose && (req.CloseType == "行权" || req.CloseType == "到期"))
{
throw new ServiceException("了结方式为'行权'或'到期'时,了结数量必须等于持仓数量,交易编号:" + dbTrade.TradeNumber);
}
#region---添加trade_cash---
var tradeCash = new trade_cash
{
ValidState = ConsGlobal.Valid,
OptId = OptUser.UserId,
OptName = OptUser.UserName,
OptDate = OptDate,
ExceciseType = "现金",
TradeType = dbTrade.BuySell,
CallPut = dbTrade.CallPut,
Strike = dbTrade.IsMoneynessOptionData ? dbTrade.Strike * (dbTrade.SpotPrice ?? 0.0) : dbTrade.Strike,
Notional = dbTrade.Notional,
Amount = req.UnwindTotalFee ?? 0,
ExtraAmount = req.ExtraAmount,
TradeId = dbTrade.id,
FinalPrice = req.UnderlyingAvgPrice ?? req.UnderlyingPrice,
VolType = "交易",
TradeAmount = dbTrade.TradeAmount,
Status = TradeCashStatusEnum.已执行,
ValueDate = req.CloseDate,
Comments = "导入了结数据",
SpotPrice = req.UnderlyingPrice,
ExerciseWay = "",
UnwindType = "",//部分平仓/行权、全部平仓/行权/到期
Action = (req.CloseType == "行权" || req.CloseType == "到期") ? ClientCashInCashOut.系统操作_行权费 : ClientCashInCashOut.系统操作_平仓费
};
DbContext.trade_cash.Add(tradeCash);
DbContext.SaveChanges();
tradeCash.UnwindNotional = closeNotional;
tradeCash.UnwindTradeAmount = req.CloseTradeAmount;
tradeCash.UnwindPercentRate = req.CloseTradeAmountRate;
tradeCash.NotionalPercentRate = req.CloseTradeAmountRate;
tradeCash.UnwindPrice = req.UnwindPrice;
tradeCash.UnwindPricePercentRate = req.UnwindPriceRate;
if (req.CloseType == "平仓")
{
tradeCash.UnwindVol = req.UnwindVolatility;
tradeCash.UnwindType = isPartialClose ? "部分平仓" : "全部平仓";
tradeCash.Action = ClientCashInCashOut.系统操作_平仓费;
tradeCash.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权;
tradeCash.IsLastAction = !isPartialClose;
}
else if (req.CloseType == "提前行权")
{
if (!PS.Config.TradeElement.IsSingleExecutionTemplate && dbTrade.ExerciseMode != "European")
{
tradeCash.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权;
}
else
{
tradeCash.ExerciseWay = TradeCashExerciseWayEnum.到期行权;
}
if (isPartialClose)
{
tradeCash.UnwindType = "部分行权";
tradeCash.Action = ClientCashInCashOut.系统操作_平仓费;
}
else
{
tradeCash.UnwindType = "全部行权";
tradeCash.Action = ClientCashInCashOut.系统操作_行权费;
}
tradeCash.IsLastAction = !isPartialClose;
}
else
{
tradeCash.Action = ClientCashInCashOut.系统操作_行权费;
tradeCash.ExerciseWay = TradeCashExerciseWayEnum.到期行权;
tradeCash.UnwindType = req.CloseType == "到期" ? "到期" : "全部行权";
tradeCash.IsLastAction = true;
}
#endregion
//审批流程
if (!req.SkipWorkflow && HasTradeProcess())
{
var isUnwind = tradeCash.Action.Contains("平仓");
tradeCash.ValidState = ConsGlobal.InValid;
dbTrade.TradeStatus = isUnwind ? ConsTrade.平仓待复核 : ConsTrade.行权待复核;
dbTrade.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfOld);
//检查是否有交易审批流程
if (valuedateBLL.SystemDate.CloseReApprove == 1)
{
// 如果有审批组
InitTradeProcessOrder(dbTrade, UserId);
}
AddTradeOperationHistoryAndSetParentTradeInfo(false, dbTrade, optType: isUnwind ? "平仓审核提交" : "行权审核提交", comments: req.ImportFlag);
}
else
{
//SaveTradeCashDetail(2021-11-23:如果进入审批流程则在审批后才增加tradecashDetail)
SaveTradeCashDetail(tradeCash, false);
#region---存入ClientCashInCashOut---
var cl = ClientDataQueryService.GetClient(dbTrade.ClientId, true);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
Money = tradeCash.Amount * -1,
HappenDate = req.CloseDate,
State = ClientCashInCashOut.已确认,
OptId = tradeCash.OptId,
OptName = tradeCash.OptName,
OptDate = tradeCash.OptDate,
CreatorId = tradeCash.OptId,
CreatorName = tradeCash.OptName,
CreateDate = tradeCash.OptDate,
TradeId = tradeCash.TradeId,
TradeCashId = tradeCash.id,
Action = tradeCash.Action,
TradeNumber = dbTrade.TradeNumber,
IsGroup = dbTrade.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
#endregion
#region---更新trade---
dbTrade.UnWindNotional = tradeCash.UnwindNotional;
if (req.CloseType == "平仓" || req.CloseType == "提前行权")
{
dbTrade.Notional -= tradeCash.UnwindNotional ?? 0;
if (isPartialClose)
{
dbTrade.HasPartialUnWind = 1;
}
else
{
dbTrade.Notional = 0;
if (req.CloseType == "平仓")
{
dbTrade.TradeStatus = ConsTrade.已平仓;
}
else
{
dbTrade.TradeStatus = ConsTrade.已执行;
}
}
}
else
{
dbTrade.Notional = 0;
dbTrade.TradeStatus = req.CloseType == "到期" ? ConsTrade.已到期 : ConsTrade.已执行;
}
dbTrade.UnWindDate = req.CloseDate;
dbTrade.TradeAmount = dbTrade.Notional / countRatio;
if (dbTrade.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(dbTrade.BasisUnderlyingCode))
{
dbTrade.StockEqvNotional -= TradeHelper.GetStockEqvNotional(tradeCash.UnwindNotional / dbTrade.OriginalNotional * dbTrade.OriginalStockEqvNotional, 1, 1);
}
else
{
dbTrade.StockEqvNotional = TradeHelper.GetStockEqvNotional(dbTrade.Notional * (dbTrade.SpotPrice ?? 0.0), dbTrade.ParticipationRate, dbTrade.AnnualizeFactor);
}
dbTrade.FinalPrice = tradeCash.FinalPrice;
#endregion
//删除E/Bod数据
RemoveEodTradeAndFutureInfo(false, dbTrade.id, tradeCash.ValueDate);
AddTradeOperationHistoryAndSetParentTradeInfo(false, dbTrade, optType: "批量了结-" + req.CloseType, comments: req.ImportFlag);
//当前这个方法只会被导入了结交易和api了结调用,所以直接记录提成就行;
if (PS.Config.SalesCommissionCalculation == "公式1")
{
//销售提成
new SalesModule.SalesCommissionDetailDataService(this).CalcuSalesCommissionDetail(tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.招证)
{
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(dbTrade, tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.物产中大)
{
new BizLogicWCZD().GenerateWCZDNumber(DbContext, dbTrade, tradeCash.ValueDate, tradeCash.id);
}
}
dbTrade.OptId = OptUser.UserId;
dbTrade.OptName = OptUser.UserName;
dbTrade.OptDate = OptDate;
dbTrade.IsApproval = dbTrade.ParentTradeId > 0 && dbTrade.IsGroup == 2;
//保存更改
DbContext.SaveChanges();
return new TradeCloseResult
{
Trade = dbTrade,
TradeCash = tradeCash
};
}
//检查请求数据
private void CheckRequest(trade td, TradeCloseRequestModel req)
{
if (td.TradeType == "结构化交易")
{
throw new ServiceException("结构化交易主交易不允许了结操作,交易编号:" + req.TradeNumber);
}
if (td.TradeStatus != ConsTrade.确认成交 && td.TradeStatus != ConsTrade.提前终止拒绝)
{
throw new ServiceException("只有确认成交或者提前终止拒绝的交易可以导入,交易编号:" + req.TradeNumber);
}
//----------------------------------------------
// 了结方式
//----------------------------------------------
switch (req.CloseType)
{
case "平仓":
break;
case "行权":
case "提前行权":
case "到期":
if (td.TradeType == "自定义交易")
{
throw new ServiceException("了结方式 填写错误,结构类型为'自定义交易'时仅支持'平仓',交易编号:" + td.TradeNumber);
}
break;
default:
if (string.IsNullOrWhiteSpace(req.CloseType))
{
throw new ServiceException("了结方式 必须填写,交易编号:" + td.TradeNumber);
}
else
{
throw new ServiceException("了结方式 填写错误,不支持:" + req.CloseType + ",交易编号:" + td.TradeNumber);
}
}
//----------------------------------------------
// 了结日期
//----------------------------------------------
if (req.CloseDate > SystemValueDate)
{
throw new ServiceException("了结日期 不能大于 系统日期,交易编号:" + td.TradeNumber);
}
if (req.CloseDate < td.TradeDate)
{
throw new ServiceException("了结日期不能早于交易日期,交易编号:" + td.TradeNumber);
}
if (req.CloseDate > td.ExerciseDate)
{
throw new ServiceException("了结日期不能大于到期日期,交易编号:" + td.TradeNumber);
}
if (td.ExerciseMode != "American" && req.CloseType == "提前行权")
{
throw new ServiceException("只有美式期权的了结方式可以填写'提前行权',交易编号:" + td.TradeNumber);
}
if ((req.CloseType == "到期" || req.CloseType == "行权") && req.CloseDate != td.ExerciseDate)
{
throw new ServiceException("了结日期应该为交易的到期日期,交易编号:" + td.TradeNumber);
}
var maxDate = DbContext.trade_cash.Where(n => n.TradeId == td.id && n.ValidState != ConsGlobal.InValid && !n.IsDeleted)
.Max(n => (DateTime?)n.ValueDate);
if (maxDate.HasValue && maxDate.Value > req.CloseDate)
{
throw new ServiceException($"了结日期 填写错误,因为{maxDate:yyyy-MM-dd}已存在了结操作,必须大于等于此日期,交易编号:{td.TradeNumber}");
}
//----------------------------------------------
// 了结数量
//----------------------------------------------
if ((req.CloseTradeAmount ?? 0) <= 0 && td.IsUsePremiumRate == false)
{
throw new ServiceException("请填写了结数量,交易编号:" + td.TradeNumber);
}
if ((req.CloseTradeAmountRate ?? 0) <= 0 && td.IsUsePremiumRate == true)
{
throw new ServiceException("请填写了结数量比例,交易编号:" + td.TradeNumber);
}
//----------------------------------------------
// 了结金额
//----------------------------------------------
if (req.CloseType == "平仓")
{
if (td.IsUsePremiumRate == true)
{
if (!req.UnwindPriceRate.HasValue && !req.UnwindTotalFee.HasValue)
{
throw new ServiceException("名义本金成交方式时平仓总额和平仓单价比例至少有一个不能为空,交易编号:" + td.TradeNumber);
}
}
else if (!req.UnwindPrice.HasValue && !req.UnwindTotalFee.HasValue)
{
throw new ServiceException("数量成交方式的交易了结单价和了结总额至少有一个不能为空,交易编号:" + td.TradeNumber);
}
//用户页面操作模式下平仓波动率不能为空
if (!req.UnwindVolatility.HasValue && string.IsNullOrEmpty(req.ImportFlag) && td.TradeType != "自定义交易")
{
throw new ServiceException("平仓波动率不能为空,交易编号:" + td.TradeNumber);
}
}
}
//互算加计算了结收益
private void CalcForClose(trade td, TradeCloseRequestModel req, bool isAveragePriceOverride, out int countRatio, out double closeNotional)
{
//-------------------------------------
// 了结数量和了结数量比例互算
//-------------------------------------
closeNotional = 0;
countRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode);
if (td.IsUsePremiumRate == true || req.CloseTradeAmount == null)
{
closeNotional = (td.OriginalNotional * req.CloseTradeAmountRate) ?? 0;
req.CloseTradeAmount = closeNotional / countRatio;
}
var diff = req.CloseTradeAmount.Value - td.TradeAmount;
//比较了结数量和持仓数量
if (diff > 1e-4)
{
throw new ServiceException("了结数量超过了持仓数量,交易编号:" + td.TradeNumber);
}
else if (Math.Abs(diff) < 1e-4)
{
req.CloseTradeAmount = td.TradeAmount;
}
if (!td.IsUsePremiumRate.GetValueOrDefault())
{
closeNotional = (req.CloseTradeAmount ?? 0) * countRatio;
req.CloseTradeAmountRate = closeNotional / td.OriginalNotional;
}
//-------------------------------------
// 了结单价和了结总额互算
//-------------------------------------
if (req.CloseType == "平仓")
{
var StockEqvNotional = td.OriginalStockEqvNotional * req.CloseTradeAmountRate;
if (req.UnwindTotalFee.HasValue)
{
req.UnwindTotalFee = (req.UnwindTotalFee ?? 0) * (valuedateBLL.SystemDate.UnwindAmountAngle == 1 ? 1 : EodOperationBase.GetSign(td.BuySell));
req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradePrice(req.UnwindTotalFee, StockEqvNotional, td.ParticipationRate, td.PrincipalSum(), td.AnnualizeFactor, td.BuySell, td.TradeType, false);
req.UnwindPrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.UnwindTotalFee, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false);
req.UnwindPriceRate = (req.UnwindPriceRate ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
req.UnwindPrice = (req.UnwindPrice ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
}
else
{
bool ActualHasMinusValueOptions = valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1 ? ConsTrade.HasMinusValueOptions.Contains(td.TradeType) : !ConsTrade.HasMinusValueOptions.Contains(td.TradeType);
int UnwindpriceAndMoneyIsAgreement = td.BuySell == "买入" || valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1 || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) || td.TradeType == "远期" ? 1 : -1;
if (td.IsUsePremiumRate == true)
{
req.UnwindPriceRate = (req.UnwindPriceRate ?? 0) * (td.TradeType == "远期" || ActualHasMinusValueOptions ? 1 : EodOperationBase.GetSign(td.BuySell));
req.UnwindPrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.UnwindPriceRate, td.SpotPrice);
req.UnwindTotalFee = TradeHelper.GetTradePriceByPremiumRate(req.UnwindPriceRate * UnwindpriceAndMoneyIsAgreement, StockEqvNotional, td.ParticipationRate, td.PrincipalSum(), td.AnnualizeFactor, td.BuySell, td.TradeType, false) * UnwindpriceAndMoneyIsAgreement;
}
else
{
req.UnwindPrice = (req.UnwindPrice ?? 0) * (td.TradeType == "远期" || ActualHasMinusValueOptions ? 1 : EodOperationBase.GetSign(td.BuySell));
req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice * UnwindpriceAndMoneyIsAgreement, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false) * UnwindpriceAndMoneyIsAgreement;
}
req.UnwindTotalFee = (req.UnwindTotalFee ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
}
}
else
{
tradeBLL.SetFieldsByTradeType(td);
if (td.TradeType == "亚式期权" && req.UnderlyingAvgPrice.HasValue)
{
var strike = td.Strike ?? 0;
var finalPrice = req.UnderlyingPrice;
if (td.trade_asian_option.StrikeType == "Floating")
{
strike = req.UnderlyingAvgPrice.Value;
}
else
{
finalPrice = req.UnderlyingAvgPrice.Value;
}
req.UnwindPrice = 0;
if (td.OptionType == "看涨")
{
if (finalPrice > strike)
{
req.UnwindPrice = finalPrice - strike;
}
}
else if (td.OptionType == "看跌")
{
if (finalPrice < strike)
{
req.UnwindPrice = strike - finalPrice;
}
}
if (td.BuySell != "买入" && req.UnwindPrice > 0)
{
req.UnwindPrice = -req.UnwindPrice;
}
if (!req.UnwindTotalFee.HasValue)
{
if (td.IsUsePremiumRate == true)
{
req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false);
}
else
{
req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false);
}
}
req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
}
else if (td.TradeType == "Risky期权")
{
var finalPrice = req.UnderlyingPrice;
var UnwindPrice = 0d;
var UnwindTotalFee = 0d;
var QdpTrades = GetToQdpOptionRisk(td, countRatio);
foreach (var item in QdpTrades)
{
if (item != null && item.Instrument != null && item.Instrument is IOption optitem)
{
var cashFlows = optitem.GetPayoff(new double[] { finalPrice });
if (!(cashFlows != null && (cashFlows[0].PaymentAmount == 0 || double.IsNaN(cashFlows[0].PaymentAmount))))
{
var perAmount = cashFlows[0].PaymentAmount / td.Notional;
UnwindPrice += perAmount;
UnwindTotalFee += OtcFormatHelper.FormatValue(cashFlows[0].PaymentAmount, 2);
}
}
}
req.UnwindPrice = UnwindPrice;
//risky保底是一个总的值
var amount = TradeHelper.GetAmountByPaymentAmount(0, td.PrincipalSum(), td.BuySell);
UnwindTotalFee += OtcFormatHelper.FormatValue(amount, 2);
if (req.UnwindTotalFee != null)
{
req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee;
}
else
{
req.UnwindTotalFee = UnwindTotalFee;
}
req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
}
else
{
var finalPrice = req.UnderlyingPrice;
var QdpTrade = TradeCalcHelper.GetQdpTrade(td);
if (QdpTrade != null && QdpTrade.Instrument != null && QdpTrade.Instrument is IOption opt)
{
if (td.TradeType == "亚式期权" && td.trade_asian_option != null)
{
if (isAveragePriceOverride && finalPrice > 0)
{
req.UnderlyingAvgPrice = finalPrice;
}
else
{
var asianOpt = opt as AsianOption;
if (td.trade_asian_option.StrikeType != "Floating")
{
//分段式亚式期权,行权时的默认标的价格需要通过QDP计算得出,而不是标的现价,再根据该价格算出行权收益
req.UnderlyingAvgPrice = finalPrice = asianOpt.FinalPrice();
}
else
{
req.UnderlyingAvgPrice = asianOpt.Strike;
}
}
}
var cashFlows = opt.GetPayoff(new double[] { finalPrice });
var perAmount = cashFlows[0].PaymentAmount / td.Notional;
req.UnwindPrice = perAmount;
}
if (req.UnwindTotalFee != null)
{
if (td.IsUsePremiumRate == true)
{
var UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false);
req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee;
}
else
{
var UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false);
req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee;
}
}
else
{
if (td.IsUsePremiumRate == true)
{
req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false);
}
else
{
req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false);
}
}
req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
}
}
if (td.TradeType != "亚式期权")
{
req.UnderlyingAvgPrice = null;
}
}
public static List<TradeBase> GetToQdpOptionRisk(trade trade, int countRatio)
{
var options = new List<TradeBase>();
var tradeclone = trade.Clone();
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1);
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, countRatio);
var td1 = tradeclone.Clone();
td1.Strike = trade.trade_risky_option.Strike1;
td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1;
td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1);
td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, countRatio);
td1.OptionType = "看跌";
td1.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
var option1 = TradeCalcHelper.GetQdpTrade(td1);
if (option1 != null)
{
options.Add(option1);
}
var td2 = tradeclone.Clone();
td2.Strike = trade.trade_risky_option.Strike2;
td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2;
td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1);
td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, countRatio);
var option2 = TradeCalcHelper.GetQdpTrade(td2);
if (option2 != null)
{
options.Add(option2);
}
var td3 = tradeclone.Clone();
td3.Strike = trade.trade_risky_option.Strike3;
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate2 - (decimal)trade.trade_risky_option.ParticipationRate3;
td3.ParticipationRate = (double?)Math.Abs(participationRate3);
td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1);
td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, countRatio);
if (participationRate3 < 0)
{
td3.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
}
var option3 = TradeCalcHelper.GetQdpTrade(td3);
if (option3 != null)
{
options.Add(option3);
}
return options;
}
#region----DataRowReader----
class DataRowReader
{
DataRow _row;
readonly Dictionary<string, int> _colMap;
public DataRowReader(DataTable table)
{
var colCount = table.Columns.Count;
_colMap = new Dictionary<string, int>(colCount, StringComparer.OrdinalIgnoreCase);
var row = table.Rows[0];
for (var index = 0; index < colCount; index++)
{
var col = row[index]?.ToString()?.Trim();
if (string.IsNullOrEmpty(col))
{
continue;
}
_colMap[col.Replace("%", "")] = index;
}
}
/// <summary>
/// 设置datarow
/// </summary>
public void SetDataRow(DataRow row)
{
_row = row;
}
public string GetString(string fieldName, bool required = false)
{
var str = _colMap.TryGetValue(fieldName, out var colIndex) ? _row[colIndex]?.ToString()?.Trim() : null;
if (required && string.IsNullOrEmpty(str))
{
throw new ServiceException($"{fieldName} 必须填写");
}
return str;
}
public double? GetDouble(string fieldName, bool required = false)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
return double.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
//为了兼容模板修改导致的字段名称改变问题
public double? GetDouble(string fieldName, string fieldName2, bool required = false)
{
var str = GetString(fieldName, false) ?? GetString(fieldName2, false);
if (string.IsNullOrEmpty(str))
{
return required ? throw new ServiceException($"{fieldName} 必须填写") : (double?)null;
}
return double.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
public double? GetPercent(string fieldName, bool required = false)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
var percent = str.EndsWith("%");
if (percent)
{
str = str.TrimEnd('%');
}
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
//为了兼容模板修改导致的字段名称改变问题
public double? GetPercent(string fieldName, string fieldName2, bool required = false)
{
var str = GetString(fieldName, false) ?? GetString(fieldName2, false);
if (string.IsNullOrEmpty(str))
{
return required ? throw new ServiceException($"{fieldName} 必须填写") : (double?)null;
}
var percent = str.EndsWith("%");
if (percent)
{
str = str.TrimEnd('%');
}
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
/// <summary>
/// 获取日期(不包括时间)
/// </summary>
public DateTime? GetDate(string fieldName, bool required = false)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
if (str.Length == 8)
{
return DateTime.TryParseExact(str, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt2) ? dt2 : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
return DateTime.TryParse(str, out var dt) ? dt.Date : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
public int? GetInt32(string fieldName, bool required = false)
{
var str = GetString(fieldName, required);
if (!required && string.IsNullOrEmpty(str))
{
return null;
}
return int.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}");
}
}
#endregion
}
/// <summary>
/// 交易了结结果
/// </summary>
public class TradeCloseResult
{
public trade Trade { get; set; }
public trade_cash TradeCash { get; set; }
}
}