Files
zszq-trs/YLErpWeb/Controllers/trade_cashController.cs
T
hjhan 6424b38001 fix(期权到期): checkEodPrice 前置校验改用债券感知统一取价方法
- trade_cashController.checkEodPrice 是前端'执行到期'按钮的前置 gate,
  原用 EodPriceQueryService.TryGetEodPrice(仅查期货+股票两表),
  债券标的(如 GLMS-20260715-0002 / 180011.IB)必然取不到价而报
  '交易日{date}的结算价或收盘价未找到',挡在 tradeExpireConfirm 之前,
  导致层1 对 tradeExpireInner/MultipleTradeExpireConfirm 的修复被绕过。
- 改为与层1 一致的 EodPriceQueryService.TryGetSettlementEodPrice
  (债券走中债估值、期货/股票走原路径),三处到期取价口径统一。
2026-07-16 14:56:28 +08:00

950 lines
40 KiB
C#

using NPOI.SS.Formula.Functions;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.EodSettlement;
using YLErp.Commons;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Helpers;
using YLErp.Enums;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.TradeDalModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.AccumulatorOptionModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Modules.TradeModule.OrderModule;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Web.Controllers
{
public class trade_cashController : BaseController
{
readonly YLContext db = new YLContext();
public ActionResult executionTradeCash(string enid, bool isexpire = false, bool isUseApproval = false)
{
ViewBag.Title = "现金交割交易 | 执行交割";
var intid = DecryptInt(enid);
var td = db.trade.Find(intid);
if (td == null)
{
return Content("找不到现金交割交易");
}
if (td.Warning)
{
new TradeDalService(CurUser).RollbackToBeforeSettle(td, valuedateBLL.ValueDate);
}
ViewBag.Trade = td;
tradeBLL.SetFieldsByTradeType(td);
if (td.TradeType == "亚式期权" && td.trade_asian_option != null
&& !EodPriceQueryService.CheckDbExists(td.trade_asian_option.AveragingPeriodStartDate ?? td.TradeDate.Value,
valuedateBLL.ValueDate, td.UnderlyingInstrumentType, td.UnderlyingCode))
{
return Content("该亚式期权没有获取到均价起算日之后的收盘价列表");
}
var r = new TradeCashService(CurUser).ExecutionTradeCash(td);
//r.SpotPrice = EodPriceQueryService.TryGetEodPrice(r.ValueDate, td.UnderlyingCode, out var eodPrice)
// ? eodPrice.GetPrice(td.SettlementType) : DataCacheProvider.GetUnderlyingDataSource().GetPrice(r.UnderlyingId ?? 0);
//需要审批或者复核的交易都会显示行权审核提交按钮
ViewBag.IsShowReCheckClose = ((valuedateBLL.SystemDate.CloseReCheck == 1) ||
(valuedateBLL.SystemDate.CloseReApprove == 1 && HasTradeProcess())) && td.TradeStatus != ConsTrade.行权待复核;
ViewBag.IsReCheckConfirm = td.TradeStatus == ConsTrade.行权待复核;
ViewBag.isUseApproval = isUseApproval;
//复核时隐藏下面的字段,让用户手动输入验证
if (td.TradeStatus == ConsTrade.行权待复核)
{
r.ExtraAmount = null;
r.FinalPrice = null;
var tradeCash = db.trade_cash.Where(x => x.TradeId == td.id && (x.Action == "系统操作-行权费" || x.Action == "系统操作-平仓费") && x.ValidState == "InValid" && !x.IsDeleted).OrderByDescending(x => x.OptDate)?.FirstOrDefault();
r.IsExpire = tradeCash != null && tradeCash.ExerciseWay == TradeCashExerciseWayEnum.到期行权;
if (td.TradeType == "亚式期权")
{
r.Strike = tradeCash.Strike;
}
if (isUseApproval)
{
r.FinalPrice = tradeCash.FinalPrice;
r.Strike = tradeCash.Strike;
r.UnwindType = tradeCash.UnwindType;
r.UnwindNotional = tradeCash.UnwindNotional;
r.UnwindPercentRate = tradeCash.UnwindPercentRate * 100;
r.UnwindStockEqvNotional = tradeCash.UnwindStockEqvNotional;
r.UnwindTradeAmount = tradeCash.UnwindTradeAmount;
r.ExtraAmount = tradeCash.ExtraAmount;
r.UnwindPrice = tradeCash.UnwindPrice;
r.Amount = tradeCash.Amount;
r.InitialAmount = r.Amount - (r.ExtraAmount ?? 0);
}
if (r.IsExpire == true)
{
r.UnwindType = "全部行权";
}
ViewBag.IsExpire = r.IsExpire;
}
else
{
r.UnwindType = "全部行权";
r.IsExpire = isexpire;
ViewBag.IsExpire = isexpire;
}
if (!string.IsNullOrEmpty(td.PairTrade))
{
var pid = Convert.ToInt32(td.PairTrade);
ViewBag.PairTrade = db.trade.Find(pid);
}
if (r != null)
{
r.InitialSpotPrice = r.InitialSpotPrice.OtcFormatValue(OtcFormatFlag.umprice);
r.ValidStrike = r.ValidStrike.OtcFormatValue(OtcFormatFlag.umprice);
r.OriginalStockEqvNotional = td.StockEqvNotionalReal;
r.StockEqvNotional = TradeHelper.GetStockEqvNotionalReal(td.StockEqvNotional, td.ParticipationRate, td.AnnualizeFactor);
r.UnwindStockEqvNotional = r.StockEqvNotional;
r.IsUnwindStockEqvNotional = false;
}
return View(r);
}
public JsonResult checkEodPrice(string enid)
{
var intid = DecryptInt(enid);
var td = db.trade.Find(intid);
if (td == null)
{
return JsonError("找不到交易");
}
var valueDate = valuedateBLL.ValueDate;
if (PS.Config.Company == Configuration.CompanyEnum.厦门象屿 && td.SettlementType == SettlementTypeEnum.ReferencePrice)
{
valueDate = Modules.SpecialModule.XiaMenXiangYuHelper.GetRefernceValueDate(valueDate);
}
if (td.TradeDate > valueDate)
{
return JsonError("执行日期在交易起始日之前,操作失败");
}
if (td.ExerciseMode == "European" && td.ExerciseDate > valueDate)
{
return JsonError("执行日期在交易到期日之前,操作失败");
}
if (td.ExerciseDate != null && valueDate > td.ExerciseDate)
{
valueDate = td.ExerciseDate.Value;
}
if (QdpCalendarHelper.IsHoliday(valueDate))
{
valueDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate);
}
if (!EodPriceQueryService.TryGetSettlementEodPrice(valueDate, td.UnderlyingCode, out _))
{
return JsonError($"交易日{valueDate:yyyy-MM-dd}的结算价或收盘价未找到!");
}
return JsonSuccess();
}
/// <summary>
/// 计算到期收益
/// </summary>
[HttpPost]
public JsonResult CalcAmount(int enid, double finalPrice, double notional, bool isRefreshFinalPrice = false, double? strike = null)
{
var td = db.trade.Find(enid);
var r = new trade_cash();
if (td == null)
{
return JsonError("计算错误");
}
else
{
r.IsPossibleExec = true;
var um = db.underlying_manager.FirstOrDefault(t => t.id == td.UnderlyingId);
if (notional < td.Notional)
{
td.Notional = notional;
td.StockEqvNotionalReal = (notional * td.SpotPrice) ?? 0;
td.StockEqvNotional = TradeHelper.GetStockEqvNotional(td.StockEqvNotionalReal, td.ParticipationRate, td.AnnualizeFactor);
}
var valueDate = valuedateBLL.ValueDate;
if (td.ExerciseDate != null && valueDate > td.ExerciseDate.Value)
{
valueDate = td.ExerciseDate.Value;
}
r.ValueDate = valueDate;
SettlementCalcCommons.SetPossibleExec(r, td, finalPrice, isRefreshFinalPrice, strike: strike, finalPriceSettleDate: r.ValueDate);
return JsonSuccess("计算成功", new { r.IsPossibleExec, r.Amount, r.FinalPrice, r.UnwindPrice });
}
}
[HttpPost]
public JsonResult CalcMultyTradeAmount(int enid, double finalPrice)
{
var td = db.trade.Find(enid);
if (td == null)
{
return JsonError("计算错误");
}
else
{
double amount = 0;
var childTrades = db.trade.Where(x => x.ParentTradeId == td.id).ToList();
foreach (var trade in childTrades)
{
var r = new trade_cash();
tradeBLL.SetFieldsByTradeType(trade);
SettlementCalcCommons.SetPossibleExec(r, trade, finalPrice);
amount += r.Amount;
}
return JsonSuccess("计算成功", new { IsPossibleExec = true, Amount = amount });
}
}
public double? CalcAmountOnly(int enid, double tradePrice)
{
var td = db.trade.Find(enid);
var r = new trade_cash();
if (td != null)
{
tradeBLL.SetFieldsByTradeType(td);
r.IsPossibleExec = true;
var um = db.underlying_manager.FirstOrDefault(t => t.id == td.UnderlyingId);
SettlementCalcCommons.SetPossibleExec(r, td, tradePrice);
return r.Amount;
}
else
{
return 0;
}
}
/// <summary>
/// 执行合约/复核执行合约
/// </summary>
[HttpPost]
public JsonResult executionTradeCashJson(TradeCashReq req, string _Version)
{
if (req is null)
{
return JsonError("数据不能为空");
}
var tc = new TradeCashService(CurUser).GetLastSettleInfo(req.TradeId);
if (tc != null && tc.ValueDate > req.ValueDate)
{
return JsonError($"提交失败,了结日期不应早于{tc.ValueDate:yyyy-MM-dd},请重新操作");
}
var result = new TradeCashService(CurUser).ExecutionTradeCash(req);
setTradeExtensionInfo(result.Trade);
if (!result.Success)
{
return JsonError(result.Message, result.Trade);
}
if (result.TradeCash != null && _Version == "V2")
{
var trade = result.Trade;
new TradeExtendService(CurUser).SetTradeExtend(new[] { trade });
var text = TradeTerminationAbstractService.GetAbstractInfoText(result.TradeCash, trade, false);
return JsonSuccess(result.Message, text);
}
return JsonSuccess(result.Message, result.Trade);
}
/// <summary>
/// 日终执行批量行权到期
/// </summary>
[HttpPost]
public async Task<JsonResult> ExeExpireTradeCashJson(TradeCashReq req)
{
var tc = new trade_cash();
await TryUpdateModelAsync(tc);
new TradeExpireService(CurUser).ExeExpireTradeCash(req, tc);
return JsonSuccess("执行成功", null);
}
/// <summary>
/// 拒绝平仓审核
/// </summary>
[MyAuthorize("交易管理-交易平仓审核")]
[HttpPost]
public JsonResult DenyReCheck(TradeCashReq req)
{
new TradeProcessService(CurUser).DenyReCheck(req);
return JsonSuccess("提前终止拒绝成功");
}
/// <summary>
/// 平仓待复核提交
/// </summary>
[HttpPost]
public JsonResult CloseReCheck(TradeCashReq req, string _Version, bool isFromExercise = false)
{
var tc = new TradeCashService(CurUser).GetLastSettleInfo(req.TradeId);
if (tc != null && tc.ValueDate > req.ValueDate)
{
return JsonError($"提交失败,了结日期不应早于{tc.ValueDate:yyyy-MM-dd},请重新操作");
}
var result = new TradeCloseService(CurUser).CloseReCheck(req, isFromExercise);
setTradeExtensionInfo(result.Trade);
if (_Version == "V2")
{
var trade = result.Trade;
new TradeExtendService(CurUser).SetTradeExtend(new[] { trade });
var text = TradeTerminationAbstractService.GetAbstractInfoText(result.TradeCash, trade, true);
return JsonSuccess("提交审核成功", text);
}
return JsonSuccess("提交审核成功", result.Trade);
}
/// <summary>
/// 行权待复核提交
/// </summary>
[HttpPost]
public JsonResult applyExerciseRecheck(TradeCashReq req, string _Version)
{
var tc = new TradeCashService(CurUser).GetLastSettleInfo(req.TradeId);
if (tc != null && tc.ValueDate > req.ValueDate)
{
return JsonError($"提交失败,了结日期不应早于{tc.ValueDate:yyyy-MM-dd},请重新操作");
}
var result = new TradeExerciseService(CurUser).ApplyExerciseRecheck(req);
setTradeExtensionInfo(result.Trade);
if (_Version == "V2")
{
var trade = result.Trade;
new TradeExtendService(CurUser).SetTradeExtend(new[] { trade });
var text = TradeTerminationAbstractService.GetAbstractInfoText(result.TradeCash, trade, false);
return JsonSuccess("提交审核成功", text);
}
return JsonSuccess("提交审核成功", result.Trade);
}
//交易平仓复核/交易平仓
[MyAuthorize("交易管理-交易平仓,交易管理-交易平仓审核")]
[HttpPost]
public JsonResult UnwindTradeJson(TradeCashReq req, string _Version)
{
try
{
var tc = new TradeCashService(CurUser).GetLastSettleInfo(req.TradeId);
if (tc != null && tc.ValueDate > req.ValueDate)
{
return JsonError($"提交失败,了结日期不应早于{tc.ValueDate:yyyy-MM-dd},请重新操作");
}
var result = new TradeUnwindService(CurUser).UnwindTrade(req);
setTradeExtensionInfo(result.Trade);
tradeBLL.SetFieldsByTradeType(result.Trade);
if (result.ApprovalProcess)
{
return JsonSuccess("复核审批成功", result.Trade);
}
if (result.HasPairTrade)
{
return JsonSuccess("执行成功,配对交易平仓" + (result.PairTradeUnwindSuccess ? "执行成功" : "执行失败"), result.Trade);
}
if (_Version == "V2")
{
var trade = result.Trade;
new TradeExtendService(CurUser).SetTradeExtend(new[] { trade });
var text = TradeTerminationAbstractService.GetAbstractInfoText(result.TradeCash, trade, true);
return JsonSuccess("执行成功", text);
}
return JsonSuccess("执行成功", result.Trade);
}
catch (Exception ex)
{
LogFactory.GetLogger("平仓失败").Error("UnwindTradeJson", ex);
return JsonError("平仓失败" + ex.Message);
}
}
[HttpPost]
public JsonResult GetPositionVol(TradeCashReq req)
{
var td = db.trade.Find(req.TradeId);
if (td == null)
{
return JsonError("系统中没有此交易!");
}
if (!req.ValueDate.HasValue)
{
req.ValueDate = valuedateBLL.ValueDate;
}
var vol = new VolatilityService(CurUser).GetTradeVol(td, req.ValueDate.Value);
if (!PS.Config.IsTradeVol)
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
var variety = DataCacheProvider.GetVariety(td.UnderlyingCode);
var singleVol = new SingleVolReq()
{
VolType = "交易",
Strike = td.Strike ?? 0,
SpotPrice = req.SpotPrice ?? 0,
TradeDate = req.ValueDate.Value,
ExerciseDate = td.ExerciseDate ?? DateTime.Today,
IsMoneynessOption = td.IsMoneynessOption,
CallPut = td.CallPut == "看涨" ? "Call" : "Put",
UnderlyingId = um.id,
UnderlyingCode = um.UnderlyingCode,
UnderlyingName = um.UnderlyingName,
UnderlyingTypeId = variety.id,
BaseVol = null,
BidVar = null,
AskVar = null,
};
if (PS.Config.Is润和)
{
singleVol.TradeDate = req.ValueDate ?? DateTime.Today;
}
vol = SingleVolService.GetSingleVol(singleVol, UserId);
}
return JsonSuccess("", vol);
}
[HttpPost]
public JsonResult GetImpliedUnWindVol(TradeCashReq req)
{
if (req == null)
{
return JsonError("实体不能为空!");
}
if (!req.UnwindPrice.HasValue)
{
return JsonError("平仓单价不能为空");
}
if (!req.FinalPrice.HasValue)
{
return JsonError("标的价格不能为空");
}
var td = db.trade.Find(req.TradeId);
if (td == null)
{
return JsonError("系统中没有此交易!");
}
if (td.TradeType != "香草期权")
{
return JsonError("只支持香草期权获取波动率");
}
if (!req.ValueDate.HasValue)
{
req.ValueDate = valuedateBLL.ValueDate;
}
if (req.TTM.HasValue)
{
td.TTMDays = req.TTM;
}
var vol = Modules.PricingModule.ReverseCalcService.GetImpliedUnWindVol(td, req.ValueDate.Value, req.UnwindPrice.Value, req.FinalPrice.Value, valuedateBLL.ValueDate > req.ValueDate);
if (!vol.HasValue)
{
return JsonError("获取隐含波动率失败");
}
return JsonSuccess("", vol);
}
public JsonResult tradeUnwindCalc(TradeCashReq req)
{
Dictionary<string, double> resultDict = null;
var tradeBLL = new tradeBLL();
var userId = CurUser.UserId.ToString();
//修改交易主表信息交易状态为已执行
var td = db.trade.AsNoTracking().FirstOrDefault(t => t.id == req.TradeId);
if (td == null)
{
return JsonError("计算失败,找不到交易!", null);
}
if (!GlobalDicionary.SupportTradeTypes.Contains(td.TradeType))
{
return JsonError(string.Format("计算失败,当前服务不支持{0}的计算!", td.TradeType), null);
}
if (td.Warning)
{
new TradeDalService(CurUser).RollbackToBeforeSettle(td, req.ValueDate ?? valuedateBLL.ValueDate);
}
try
{
if (!req.ValueDate.HasValue)
{
req.ValueDate = valuedateBLL.ValueDate;
}
tradeBLL.SetFieldsByTradeType(td); //这个方法会慢
resultDict = GetTradeUnwindCalc(req, td.Clone());
if (PS.Config.Is润和)
{
req.UnwindNotional = req.ActualUnwindNotional ?? 1;
Dictionary<string, double> resultDictVolPV = GetTradeUnwindCalc(req, td.Clone());
req.UnwindVol = req.MidVol;
Dictionary<string, double> resultDictMidPV = GetTradeUnwindCalc(req, td.Clone());
resultDict.Add("UnwindVolPV", resultDictVolPV["PV"]);
resultDict.Add("MidPV", resultDictMidPV["PV"]);
}
return JsonSuccess("计算成功!", resultDict);
}
catch (Exception e)
{
LogFactory.GetLogger("tradeUnwindCalc出错").Error(e);
return JsonError("计算出错!");
}
}
public Dictionary<string, double> GetTradeUnwindCalc(TradeCashReq req, trade td)
{
Dictionary<string, double> resultDict = null;
var userId = CurUser.UserId.ToString();
if (req.UnwindNotional < td.Notional)
{
//平仓时都计算的平仓单价,将凤凰期权累积的票息金额处理成单份对应的票息金额
//20210728:涉及到部分平仓的话这个算法是有问题的
List<autocall_observation> HappenedObservations = null;
switch (td.TradeType)
{
case "凤凰期权":
HappenedObservations = td.trade_autocall.HappenedObservations;
break;
case "区间累积期权":
HappenedObservations = td.trade_rangeaccrual.HappenedObservations;
break;
}
if (HappenedObservations != null)
{
var spotPrice = Math.Abs(td.SpotPrice ?? 0);
foreach (var x in HappenedObservations)
{
var notional = td.Notional;
if (spotPrice > 1e-8)
{
notional = x.StockEqvNotional / spotPrice;
}
else if (td.StockEqvNotionalReal > 0)
{
notional = (td.OriginalNotional ?? 0) * x.StockEqvNotional / td.StockEqvNotionalReal;
}
if (notional > 0)
{
x.PaymentAmount /= notional;
}
}
}
td.Notional = req.UnwindNotional;
//现金流交易在平仓时的份额即名义本金,现金流没有份额的概念
if (td.TradeType == "现金流交易")
{
td.StockEqvNotional = req.UnwindNotional;
}
}
var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
if (td.TradeType == "现金流交易")
{
udm = new underlying_manager();
}
if (req.FinalPrice.HasValue)
{
udm.Price = req.FinalPrice;
}
udm.QuotationDate = req.ValueDate;
//应该不需要这段逻辑,先注释掉:OTC-13391
//td.TradeDate = req.ValueDate;
//td.StartDate = req.ValueDate;
string fixing = null;
//亚式期权
if (td.TradeType == "亚式期权" || td.StructureType == "亚式熊市价差")
{
fixing = AsianOptionFixingService.GetFixingString(req.ValueDate.Value, td);
fixing = FixingService.AddOrReplaceLastDateSpotPrice(fixing, req.ValueDate.Value, udm.Price ?? 0.0);
if (PS.Config.Is润和 && DateTime.Now.Hour < 15)
{
var index = fixing.IndexOf(req.ValueDate.Value.ToString("yyyy-MM-dd"));
if (index >= 0)
{
fixing = fixing.Remove(index).TrimEnd(';');
}
}
}
var ttm = req.TTM;
if (!ttm.HasValue)
{
if (PS.Config.Is厦门象屿 && td.SettlementType == SettlementTypeEnum.ReferencePrice)
{
ttm = TradeCalcHelper.CalculateTTMDaysForXiangYu(req.ValueDate.Value, td.ExerciseDate.Value, udm.UnderlyingTypeId, td.ExerciseDate.Value == valuedateBLL.ValueDate);
}
else
{
ttm = TradeCalcHelper.CalculateTTMDays(req.ValueDate.Value, td.ExerciseDate.Value, udm.UnderlyingTypeId, td.ExerciseDate.Value == valuedateBLL.ValueDate);
}
}
if (req.ValueDate < valuedateBLL.ValueDate)
{
new TradeHisDataService(CurUser).UpdateTradeWithHisData(td, req.ValueDate.Value);
}
try
{
if (td.TradeType == "累计期权")
{
var request = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
{
vols = new[] { req.UnwindVol ?? 0.0 },
spotPrices = new[] { udm.Price ?? 0.0 },
engineName = null,
preciseTimeMode = req.ValueDate == valuedateBLL.ValueDate.Date,//与定价计算统一
pricingRequest = QdpPricingRequest.BASIC_PRICING,
quadratureFastMode = true,
timeToMaturityDays = ttm.Value,
calcScenario = CalcScenarioEnum.Pricing
};
var OptionValue = OptionCalculatorV2.GetOptionValueResult(req.ValueDate.Value, td, request, out _);
//累计期权怕平仓时剩余的平仓观察日,需要根据已观察日进行区分,不依赖平仓日期来判断
var observations = db.autocall_observation.Where(x => x.TradeId == td.id);
var startDate = td.TradeDate.Value;
if (observations.Any())
{
startDate = observations.Max(x => x.EndDate).AddDays(1);
}
//累计期权计算的pv是总额,所以这里要转换为单价
var dayCount = new TradeAccumulatorChangeService(CurUser).GetAccTradeUnwindDayCount(td.trade_accumulator_option.KOObservationDates, startDate, td.TradeDate, td.ExerciseDate);
var count = td.trade_accumulator_option.AccumuTradeAmount * dayCount;
if (count != 0)
{
OptionValue.Pv = OptionValue.Pv / count;
}
OptionValue.RoundedPv = OtcFormatHelper.FormatValue(OptionValue.Pv, 2);
if (Math.Abs(OptionValue.DeltaInLots) < 1e-6 && count != 0)
{
OptionValue.DeltaInLots = OptionValue.Delta / count / udm.ContractSize;
}
resultDict = new Dictionary<string, double> { { "PV", OptionValue.Pv }, { "DeltaInLots", OptionValue.DeltaInLots } };
}
else if (td.TradeType == "自定义交易")
{
if (PS.Config.ErpElement.ExternalAPIForCustomCalcEnable)
{
td.StartDate = req.ValueDate.Value;
var positionVol = VolatilityHelper.GetTradeVol(td, req.ValueDate.Value);
var tempResult = TradeRiskCalcUtil.GetManualOptionValue(req.ValueDate.Value, td, udm.Price ?? 0, positionVol, true, CalcScenarioEnum.Pricing);
if (tempResult.manual != null)
{
var underlying = Modules.DataCacheModule.DataCacheManager.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
var deltaInLots = TradeLotsCalc.CalcDeltaInLots(tempResult.optionValue.Delta, null, underlying);
resultDict = new Dictionary<string, double> { { "PV", tempResult.optionValue.Pv }, { "DeltaInLots", deltaInLots } };
}
else
{
throw new Exception(PS.Config.ErpElement.ExternalAPIForCustomCalcEnable ? "失败:接口计算失败" : "失败:无法调用计算接口,请开启相应配置");
}
}
else
{
throw new ServiceException("计算失败,不支持自定义交易计算");
}
}
else
{
if (td.TradeType == "结构化产品")
{
using (var db = DbContextFactory.GetYLDbContext())
{
td.MetaDic = db.TradeMeta.Where(p => p.TradeId == td.id).ToDictionary(g => g.MetaKey, g => g.MetaValue);
}
}
var OptionValue = ValueCalculator.GetOptionValueResultV2(
userId: userId,
underlying: udm,
trade: td,
vols: new double[] { req.UnwindVol ?? 0.0 },
spotPrices: new double[] { udm.Price ?? 0.0 },
fixing: fixing,
timeToMaturityDays: ttm.Value,
preciseTimeMode: req.ValueDate == valuedateBLL.ValueDate.Date,//OTC-4198,与定价计算统一
request: QdpPricingRequest.BASIC_PRICING,
calcScenario: CalcScenarioEnum.Pricing);
if (td.TradeType == "雪球期权" && td.trade_snowball.PrepaymentUsed)
{
//OptionValue.Pv是根据td.notional算出来的
OptionValue.Pv -= td.Notional * (td.SpotPrice ?? 0) * (td.trade_snowball?.PrepaymentRatio ?? 0) * (td.BuySell == "卖出" ? -1 : 1);
}
resultDict = new Dictionary<string, double> { { "PV", OptionValue.Pv }, { "DeltaInLots", OptionValue.DeltaInLots } };
}
}
catch
{
resultDict = new Dictionary<string, double> { { "PV", 0 }, { "DeltaInLots", 0 } };
}
return resultDict;
}
/// <summary>
/// 获取累计期权平仓定价日的观察日期
/// </summary>
/// <param name="kOObservationDates">观察日期</param>
/// <param name="valueDate">定价日期或平仓日期</param>
/// <param name="tradeDate">交易日期</param>
/// <param name="exerciseDate">到期日期</param>
/// <returns></returns>
public JsonResult GetAccTradeUnwindDayCount(string kOObservationDates, DateTime? valueDate, DateTime tradeDate, DateTime exerciseDate)
{
var service = new TradeAccumulatorChangeService(CurUser);
var dayCount = service.GetAccTradeUnwindDayCount(kOObservationDates, valueDate, tradeDate, exerciseDate);
return JsonSuccess("", dayCount);
}
public JsonResult tradeSwapUnwindCalcGet(TradeCashReq req)
{
Dictionary<string, double> resultDict = null;
var tradeBLL = new tradeBLL();
//修改交易主表信息交易状态为已执行
var td = db.trade.AsNoTracking().FirstOrDefault(t => t.id == req.TradeId);
if (td == null)
{
return JsonError("计算失败,找不到交易!", null);
}
try
{
if (!req.ValueDate.HasValue)
{
req.ValueDate = valuedateBLL.ValueDate;
}
tradeBLL.SetFieldsByTradeType(td); //这个方法会慢
var tradeCashs = db.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= req.ValueDate);
var tradeCashIds = tradeCashs.Select(x => x.id);
var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault();
var cashSwaps = db.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray();
var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null;
//取最后一次手动收益;
var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto);
var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null;
var initialAmountGet = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.trade_swap.GetSpotPrice ?? 0
, req.FinalPrice ?? 0, (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate, req.ValueDate.Value, tradeCash?.ValueDate);
DateTime endDate;
var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, req.ValueDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDate);
var extraAmountPay = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate);
resultDict = new Dictionary<string, double> { { "InitialAmount", initialAmountGet }, { "ExtraAmount", extraAmountPay } };
return JsonSuccess("计算成功!", resultDict);
}
catch (Exception e)
{
LogFactory.GetLogger("tradeSwapUnwindCalc").Error(e);
return JsonError("计算出错!");
}
}
public JsonResult tradeSwapUnwindCalcPay(TradeCashReq req)
{
Dictionary<string, double> resultDict = null;
var tradeBLL = new tradeBLL();
//修改交易主表信息交易状态为已执行
var td = db.trade.AsNoTracking().FirstOrDefault(t => t.id == req.TradeId);
if (td == null)
{
return JsonError("计算失败,找不到交易!", null);
}
try
{
if (!req.ValueDate.HasValue)
{
req.ValueDate = valuedateBLL.ValueDate;
}
tradeBLL.SetFieldsByTradeType(td); //这个方法会慢
var tradeCashs = db.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= req.ValueDate);
var tradeCashIds = tradeCashs.Select(x => x.id);
var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault();
var cashSwaps = db.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray();
var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null;
//取最后一次手动收益;
var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto);
var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null;
var initialAmountPay = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.trade_swap.PaySpotPrice ?? 0
, req.FinalPrice ?? 0, (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate, req.ValueDate.Value, tradeCash?.ValueDate);
DateTime endDate;
var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, req.ValueDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDate);
var extraAmountGet = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate);
resultDict = new Dictionary<string, double> { { "InitialAmount", initialAmountPay }, { "ExtraAmount", extraAmountGet } };
return JsonSuccess("计算成功!", resultDict);
}
catch (Exception e)
{
LogFactory.GetLogger("tradeSwapUnwindCalc").Error(e);
return JsonError("计算出错!");
}
}
public JsonResult ApplyUnwindSwapTrade(TradeCashReq req)
{
_ = new TradeUnwindService(CurUser).ApplyUnwindSwapTrade(req);
return JsonSuccess("提交平仓审批成功!");
}
public JsonResult UnwindSwapTrade(TradeCashReq req)
{
_ = new TradeUnwindService(CurUser).UnwindSwapTrade(req);
return JsonSuccess("平仓成功!");
}
public JsonResult ApplySwapTrade(TradeCashReq req)
{
_ = new TradeUnwindService(CurUser).ApplySwapTradeSettlement(req);
return JsonSuccess("提交互换审批成功!");
}
public JsonResult SwapTrade(TradeCashReq req)
{
_ = new TradeUnwindService(CurUser).SwapTrade(req);
return JsonSuccess("互换成功!");
}
public JsonResult SaveGroupTradeCash(SaveGroupTradeCashReq req)
{
new TradeUnwindService(CurUser).SaveGroupTradeCash(req);
return JsonSuccess("保存成功!");
}
//交易平仓复核/交易平仓
[MyAuthorize("交易管理-交易平仓,交易管理-交易平仓审核")]
[HttpPost]
public JsonResult UnwindParentTrade(SaveGroupTradeCashReq req)
{
new TradeUnwindService(CurUser).UnwindParentTrade(req);
return JsonSuccess("复核成功!");
}
public JsonResult DenyReCheckByParent(int tradeId)
{
new TradeProcessService(CurUser).DenyReCheckByParent(tradeId);
return JsonSuccess("提前终止拒绝成功");
}
[HttpPost]
public JsonResult CheckFinishGroupTradeCash(int TcId)
{
var isFinished = !db.trade_cash_group_action.Any(x => x.ParentTradeCashId == TcId && x.Status == "待完成");
return Json(isFinished);
}
[HttpPost]
public JsonResult FinishGroupTradeCash(int TcId)
{
new TradeUnwindService(CurUser).FinishGroupTradeCash(TcId);
return Json("已完成");
}
/// <summary>
/// 导入了结(部分了结)交易
/// </summary>
[MyAuthorize("交易管理-批量了结")]
public ActionResult UploadFinishedTrades()
{
if (Request.Form.Files.Count < 1)
{
return JsonError("未获取上传文件");
}
var file = Request.Form.Files[0];
var fileExt = Path.GetExtension(file.FileName);
if (fileExt.ToLowerInvariant() != ".xlsx")
{
return JsonError("目前只支持xlsx格式的文件");
}
using var stream = file.OpenReadStream();
new OtcTradeCloseService(CurUser).BatchCloseByImport(stream, out _, out _);
return JsonSuccess();
}
private void setTradeExtensionInfo(trade td)
{
if (td is null)
{
return;
}
//生成简要时需要的信息补全
if (td.TradeType == "合成价差期权")
{
td.SyntheticUnderlyingTipsInfo = synthetic_underlyingBLL.GetUnderlyingTipsInfo(td.UnderlyingCode);
}
var varietyQuery = from um in db.underlying_manager
join va in db.variety on um.UnderlyingTypeId equals va.id
where um.id == td.UnderlyingId
select new VarietyDto
{
QuoteUnit = va.QuoteUnit
};
var variety = varietyQuery.FirstOrDefault();
if (variety != null)
{
td.QuoteUnit = variety.QuoteUnit;
td.QuoteUnitSingle = variety.QuoteUnitSingle;
}
}
private bool HasTradeProcess()
{
return db.approvalprocess.Any(t => t.processType == "TradeProcess");
}
}
}