using BaseOUDAL;
using YLErp.BLL;
using YLErp.Configuration;
using YLErp.Configuration.Enums;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Modules.DataCacheModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.UnderlyingModule;
using YLErp.QdpModule;
using static YLErp.DBModels.ConsTrade;
namespace YLErp.Modules.TradeModule.OrderModule
{
///
/// 交易保存预处理
///
partial class TradeSavePreHandler : YLBaseService
{
AssetUnitDto _assetUnit;
underlying_manager _underlying;
readonly TradeRightChecker _tradeRightChecker;
public TradeSavePreHandler(YLBaseService baseService) : base(baseService)
{
_tradeRightChecker = new TradeRightChecker();
}
public bool isAddNew { get; private set; }
///
/// 交易编号已自动生成
///
public bool TradeNumberGenerated { get; private set; }
public trade Prepare(trade req, TradeSourceEnum dataSource)
{
if (string.IsNullOrWhiteSpace(req.TradeType))
{
throw new ServiceException("结构类型 必须填写");
}
if (ConsTrade.TradeTypesForHedge.Contains(req.TradeType))
{
throw new ServiceException("程序错误,不再支持对冲类型的交易写入");
}
if (req.UnWindDate != null && req.UnWindDate < req.StartDate)
{
throw new ServiceException("导入交易的平仓日期早于交易日期");
}
if (req.TradeType != "现金流交易")
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
// 标的代码
if (um == null)
{
if (req.TradeType == "收益互换" && req.trade_swap.SwapType == "多空组合")
{
um = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().FirstOrDefault();
if (um != null)
{
req.UnderlyingCode = um.UnderlyingCode;
req.UnderlyingId = um.id;
req.UnderlyingAssetClass = um.UnderlyingType;
req.UnderlyingInstrumentType = um.UnderlyingInstrumentType;
req.UnderlyingAssetName = um.UnderlyingName;
}
}
else
{
throw new ServiceException("标的代码 必须在系统中存在");
}
}
if (um.UnderlyingInstrumentType == "Stock" && !um.IsCombined())
{
var limit = req.TradeType == "收益互换" ? LimitRangeEnum.Swap : LimitRangeEnum.Option;
var limitStatus = new StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(limit, out var Codes);
if (Codes != null)
{
if (limitStatus == Codes.Contains(um.UnderlyingCode))
{
if (limitStatus)
{
throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + req.UnderlyingCode);
}
else
{
throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + req.UnderlyingCode);
}
}
}
}
}
if (req.TradeType == "收益互换")
{
req.InitialMargin = (req.trade_swap.GetMarginRate - req.trade_swap.PayMarginRate) * req.OriginalStockEqvNotional;
var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
//价差组合初始履约预付金=Max{腿一组合系数×腿一价格×腿一预付金率×数量×合约乘数,abs(腿二组合系数)×腿二价格×腿二预付金率×数量×合约乘数}
if (PS.Config.Company == CompanyEnum.华安 && udm.CommodityCode == "组合标的")
{
var synthetic = new SyntheticUnderlyingPriceService(this.OptUser).GetPriceModel(req.UnderlyingCode);
var InitMarginRate = synthetic.SuList.Max(l => DataCacheProvider.GetUnderlyingDataSource().GetData(l.UnderlyingCode).MarginRate);
var eodprice = new EodPriceProvider(req.TradeDate ?? DateTime.Now).GetPriceProvider(SettlementTypeEnum.ClosePrice);
if (req.trade_swap.GetMarginRate == InitMarginRate)
{
req.InitialMargin = synthetic.SuList.Max(l => Math.Abs(l.Coefficient) * l.ContractSize * eodprice.GetPrice(l.UnderlyingCode) * DataCacheProvider.GetUnderlyingDataSource().GetData(l.UnderlyingCode).MarginRate * req.trade_swap.PayNotional);
}
}
}
isAddNew = req.id == 0;
if (isAddNew)
{
SetDBModelCreator(req);
req.TradeStatus = ConsTrade.新增待确认;
req.TradeSource = dataSource.ToString();
if (string.IsNullOrWhiteSpace(req.TradeNumber) || (PS.Config.Company == CompanyEnum.光大光子 && (req.TradeNumber == "STG" || req.TradeNumber == "STD" || req.TradeNumber == "FWD" || req.TradeNumber == "SPD")))
{
TradeNumberGenerated = true;
if (req.UnderlyingCode != null && req.UnderlyingCode != "")
{
PrepareUnderlying(req, false);
}
req.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(req, DbContext);
}
else if (DbContext.trade.Any(n => n.TradeNumber == req.TradeNumber))
{
throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在");
}
else if (PS.Config.ErpElement.UpperTradeNumber)
{
req.TradeNumber = req.TradeNumber.ToUpperInvariant();
}
if (req.TradeType == "远期")
{
//添加拓展字段 是否补充远期价值,Type2:默认值 -1,其他默认值 0
if (!req.MetaDic.TryGetValue("IsSupplyForwardValue", out var metaDic))
{
if (PS.Config.ErpElement.ForwardValueIsSupplyOrPay == YLErp.Configuration.Enums.ForwardValueIsSupplyOrPay.Type2)
{
req.MetaDic.Add("IsSupplyForwardValue", "-1");
}
else
{
req.MetaDic.Add("IsSupplyForwardValue", "0");
}
}
}
}
else if (!string.IsNullOrWhiteSpace(req.TradeNumber))
{
if (DbContext.trade.Any(n => n.id != req.id && n.TradeNumber == req.TradeNumber && n.ValidState != "InValid"))
{
throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在");
}
if (PS.Config.ErpElement.UpperTradeNumber)
{
req.TradeNumber = req.TradeNumber.ToUpperInvariant();
}
}
if (req.TradeType == "结构化交易")
{
if (!isAddNew)
{
throw new ServiceException("程序错误,结构化交易仅支持新增录入");
}
PrepareStructureTrades(req, dataSource);
if (req.IsGroup == 1)
{
PrepareOtcTradeCalc(req);
}
}
else
{
if (req.ParentTradeId < 1 && req.TradeType != ConsGlobal.TradeType.Forward
&& req.TradeType != ConsGlobal.TradeType.Custom)
{
req.StructureType = null;
}
PrepareSingleTrade(req, dataSource);
}
req.PrincipalRateWrite = req.PrincipalRateWrite.IsNormalize() ? req.PrincipalRateWrite : null;
return req;
}
//准备结构化交易
private void PrepareStructureTrades(trade req, TradeSourceEnum dataSource)
{
var subTrades = req.SubTrades?.ToList();
if (subTrades == null || !subTrades.Any())
{
throw new ServiceException("结构化交易缺少子交易");
}
if (subTrades.Count < 2)
{
throw new ServiceException("结构化交易需要最少两笔子交易");
}
if (string.IsNullOrWhiteSpace(req.StructureType))
{
throw new ServiceException("缺少期权结构类型");
}
var option = StructureOption_Code.StructureOptions.FirstOrDefault(o => o.Name == req.StructureType || o.CnName == req.StructureType);
if (option != null)
{
req.StructureType = option.CnName;
}
var index = 1;
foreach (var subTrade in subTrades)
{
PrepareSingleTrade(subTrade, dataSource);
if (req.IsGroup == 0)
{
subTrade.StructureType = req.StructureType;
}
if (PS.Config.Is海通)
{
subTrade.TradeNumber = req.TradeNumber.Substring(0, req.TradeNumber.Length - 2) + index.ToString("D2");
}
else
{
subTrade.TradeNumber = BizLogicSingleton.Instance.GenerateSubTradeNumberBeforeConfirm(subTrade, req, index, DbContext);
}
index++;
}
if (subTrades.Select(n => n.ClientId).Distinct().Count() > 1)
{
throw new ServiceException("结构化交易组合必须有相同的交易对手方");
}
if (subTrades.Select(n => n.AssetId).Distinct().Count() > 1)
{
throw new ServiceException("结构化交易组合必须有相同的簿记账户");
}
}
//准备单个交易
private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource)
{
//准备交易基础信息
PrepareBaseInfo(req);
int? VarietyId2 = 0;
if (req.TradeType == "远期")
{
var underlying = DataCacheManager.GetUnderlyingDataSource().GetData(req.BasisUnderlyingCode ?? "");
VarietyId2 = underlying?.UnderlyingTypeId;
}
//检查交易权限,req.VarietyId来自PrepareBaseInfo
_tradeRightChecker.Check(_assetUnit, req.TraderId, req.ClientId, req.VarietyId ?? 0, VarietyId2 ?? 0, req.TradeType);
if (req.TradeType == "收益互换")
{
PrepareSwapTrade(req, dataSource);
}
else
{
PrepareOtcTrade(req, dataSource);
}
if (req.TradeType == "现金流交易")
{//现金流交易在平仓时的份额即名义本金,现金流没有份额的概念
req.Notional = req.StockEqvNotional;
req.OriginalNotional = req.StockEqvNotional;
req.TradeAmount = req.StockEqvNotional;
if (req.trade_cashflow.DepositType != CashflowDepositTypeEnum.资金收益)
{
req.trade_cashflow.PrepayRatio = 0;
}
}
req.OptId = UserId;
req.OptName = UserName;
req.OptDate = OptDate;
//设置HasGeneratedConfirmBook为0
//req.HasGeneratedConfirmBook = 0;
if (isAddNew)
{
req.ValidState = ConsGlobal.Valid;
req.CreateDate = DateTime.Now;
req.TradeSavedVol = PS.Config.Is厦门象屿 ? req.TradeCloseVolatility : req.TradeOpenVolatility;
if (PS.Config.ErpElement.VolMode == VolModeEnum.VolSurface)
{
req.TradeSavedVol = req.Vol;
}
}
req.UnderlyingCode = req.UnderlyingCode == null ? null : req.UnderlyingCode.ToUpperInvariant();
return req;
}
//准备交易标的信息
public underlying_manager PrepareUnderlying(trade req, bool prepareSynthetic)
{
var underlyingCode = req.UnderlyingCode ?? string.Empty;
_underlying ??= UnderlyingDataProvider.GetUnderlying(underlyingCode);
if (_underlying == null)
{
throw new ServiceException("标的信息不存在:" + req.UnderlyingCode);
}
if (_underlying.IsFutures() && _underlying.MaturityDate == null)
{
throw new ServiceException("标的到期日不存在:" + req.UnderlyingCode);
}
//组合标的缺少合成项的价格
if (prepareSynthetic)
{
var checkSynthetic = false;
if (req.TradeType == "收益互换")
{
if (!req.trade_swap.IsGetFloatingProfit)
{
var gun = UnderlyingDataProvider.GetUnderlying(req.trade_swap.GetUnderlyingCode);
checkSynthetic = gun != null && gun.IsSynthetic();
}
if (!req.trade_swap.IsPayFloatingProfit)
{
var pun = UnderlyingDataProvider.GetUnderlying(req.trade_swap.PayUnderlyingCode);
if (pun != null && pun.IsSynthetic() && (!req.MetaDic.TryGetValue("组合标的2", out var str) || string.IsNullOrWhiteSpace(str)))
{
throw new ServiceException("组合标的缺少组合价格:" + pun.UnderlyingCode);
}
}
}
else
{
checkSynthetic = _underlying.IsSynthetic();
}
if (checkSynthetic)
{
if (!req.MetaDic.TryGetValue("组合标的", out var str) || string.IsNullOrWhiteSpace(str))
{
throw new ServiceException("组合标的缺少组合价格:" + req.UnderlyingCode);
}
var saveExt = req.SaveExt;
if (saveExt == null || !saveExt.MaxSpotPrice.HasValue || saveExt.MaxSpotPrice.Value < 1e-6)
{
var sulist = JsonHelper.Deserialize(str)?.SuList;
var maxSpotPrice = sulist == null ? 0 : sulist.Max(n => Math.Abs(n.Coefficient * n.Price));
if (maxSpotPrice < 1e-6)
{
throw new ServiceException("[组合标的]标的价格 填写错误");
}
if (saveExt == null)
{
req.SaveExt = new TradeSaveExt();
}
req.SaveExt.MaxSpotPrice = maxSpotPrice;
}
}
}
req.UnderlyingId = _underlying.id;
req.UnderlyingCode = _underlying.UnderlyingCode;
req.VarietyId = _underlying.UnderlyingTypeId;
req.UnderlyingAssetClass = _underlying.UnderlyingType;
req.UnderlyingAssetName = _underlying.UnderlyingName;
req.MaturityDate = _underlying.IsFutures() ? _underlying.MaturityDate : null;
req.UnderlyingInstrumentType = _underlying.GetMainType();
req.CountRatio = _underlying.CountRatio;
//权益类标的关联的交易如果没有设置分红率则取标的上的分红率,如果标的分红率为空则取0
if (!req.DividendRate.HasValue && _underlying.CalcTypeIsStock())
{
req.DividendRate = _underlying.DividendRate ?? 0;
}
return _underlying;
}
//准备交易基础信息(标的、簿记、交易日期)
private void PrepareBaseInfo(trade req)
{
if (!req.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (req.TradeDate.Value.Year < 2000)
{
throw new ServiceException("交易日期 填写错误:" + req.TradeDate.Value.ToString("yyyy-MM-dd"));
}
//if (req.TradeDate > valuedateBLL.ValueDate)
//{
// throw new ServiceException("交易日期不能是未来日期:" + req.TradeDate.Value.ToString("yyyy-MM-dd"));
//}
if (QdpCalendarHelper.IsHoliday(req.TradeDate.Value) && req.TradeType != "现金流交易")
{
throw new ServiceException("交易日期不能是假日:" + req.TradeDate.Value.ToString("yyyy-MM-dd"));
}
//如果起始日没有值,默认赋值交易日
if (!req.StartDate.HasValue)
{
req.StartDate = req.TradeDate;
}
//--------------------------------------
// 标的信息
//--------------------------------------
if (req.TradeType != "现金流交易")
{
PrepareUnderlying(req, true);
}
//--------------------------------------
// 簿记帐户
//--------------------------------------
if (req.AssetId < 1)
{
throw new ServiceException("请选择簿记账户");
}
if (_assetUnit == null)
{
_assetUnit = DbContext.assetunit.Where(n => n.id == req.AssetId)
.Select(n => new AssetUnitDto { id = n.id, Name = n.Name, BookCategory = n.BookCategory, TraderIds = n.TraderIds, UserGroup = n.UserGroup }).FirstOrDefault();
if (_assetUnit == null)
{
throw new ServiceException("簿记账户不存在");
}
}
else if (_assetUnit.id != req.AssetId)
{
throw new ServiceException("程序错误,多个簿记账户并存");
}
req.AssetBookName = _assetUnit.Name;
//--------------------------------------
// 交易员信息(如果没有交易员,查找簿记账户中的交易员)
//--------------------------------------
if (string.IsNullOrWhiteSpace(req.TraderName))
{
//有交易员id,通过id查名字
if (req.TraderId > 0)
{
req.TraderName = UserBLL.GetNameById(req.TraderId);
}
else
{
//当前登录用户作为交易员
req.TraderId = UserId;
req.TraderName = UserName;
}
}
if (req.TradeType != "现金流交易")
{
if (req.TradeType != "远期")
{
req.Lots = req.Notional / _underlying.ContractSize;
}
else if (req.Lots == null || req.Lots < 1e-6)
{
throw new ServiceException("成交手数 必须填写并大于0");
}
}
}
//准备衍生品交易基础信息
private void PrepareOtcTrade(trade req, TradeSourceEnum dataSource)
{
var isForward = req.TradeType == "远期";
var isCustomTrad = req.TradeType == "自定义交易";
var flag = false;
var codes = new List
{
req.UnderlyingCode
};
if (isForward)
{
codes.Add(req.BasisUnderlyingCode);
}
//判断标的是否为现货
flag = UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) =>
{
return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false;
});
//--------------------------------------
// 日期信息
//--------------------------------------
if (!req.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
if (req.TradeDate.Value > req.ExerciseDate.Value)
{
throw new ServiceException("交易日应该在到期日之前");
}
if (req.MaturityDate != null && req.ExerciseDate.Value > req.MaturityDate.Value && req.TradeType != "累计期权")
{
throw new ServiceException("到期日期 必须小于等于 标的到期日");
}
if (flag && PS.Config.Is浙期 /*&& isForward*/)
{
if (!req.MetaDic.TryGetValue("ActualExerciseDate", out var ActualExerciseDateStr) && string.IsNullOrEmpty(ActualExerciseDateStr))
{
throw new ServiceException("实际到期日期 必须填写");
}
if (!DateTime.TryParse(ActualExerciseDateStr, out var ActualExerciseDate))
{
throw new ServiceException("实际到期日期 必须是日期格式");
}
if (req.TradeDate.Value > ActualExerciseDate)
{
throw new ServiceException("交易日应该在实际到期日之前");
}
if (req.MaturityDate != null && ActualExerciseDate > req.MaturityDate.Value)
{
throw new ServiceException("实际到期日期 必须小于等于 标的到期日");
}
}
if (!req.SettlementDate.HasValue)
{
req.SettlementDate = req.ExerciseDate;
}
else if (req.SettlementDate < req.TradeDate)
{
throw new ServiceException("结算日期 必须大于等于 成交日期");
}
if (!req.PremiumPayDate.HasValue || isForward)
{
req.PremiumPayDate = req.TradeDate;
}
//---------------------------------------------
// 交易方向
//---------------------------------------------
if (string.IsNullOrWhiteSpace(req.BuySell))
{
throw new ServiceException("交易方向 必须填写");
}
if (req.BuySell != "买入" && req.BuySell != "卖出")
{
throw new ServiceException("交易方向 填写错误:" + req.BuySell);
}
//---------------------------------------------
// 行权方式
//---------------------------------------------
CheckExerciseMode(req);
//---------------------------------------------
// 看涨看跌 + 远期多空方向
//---------------------------------------------
CheckOptionType(req);
//---------------------------------------------
// 标的价格 + 执行价格
//---------------------------------------------
if (!req.SpotPrice.HasValue && req.TradeType != "现金流交易")
{
throw new ServiceException("期初标的价格 必须填写");
}
if (string.IsNullOrWhiteSpace(req.IsMoneynessOption))
{
req.IsMoneynessOption = "否";
}
else if (req.IsMoneynessOption != "是" && req.IsMoneynessOption != "否")
{
throw new ServiceException("是否相对行权价 填写错误:" + req.IsMoneynessOption);
}
if (!req.Strike.HasValue && !(req.TradeType == "自定义交易" || req.TradeType == "现金流交易" || (req.TradeType == "亚式期权" && req.trade_asian_option?.StrikeType == "Floating")))
{
throw new ServiceException("执行价格 必须填写");
}
if (req.TradeType == "亚式期权" && req.trade_asian_option != null)
{
if (req.trade_asian_option.PayoffType == AsianAverageType.EnhancedArithmeticAverage && req.trade_asian_option.StrikeType != "Floating")
{
if (req.trade_asian_option.EnhancedPrice == 0)
{
throw new ServiceException("增强价格 必须填写");
}
}
else
{
req.trade_asian_option.EnhancedPrice = 0;
}
}
//--------------------------------------
// 客户信息
//--------------------------------------
PrepareOtcTradeClient(req, dataSource);
//--------------------------------------
// 交易波动率、平滑过渡天数
//--------------------------------------
PrepareOtcTradeVol(req, dataSource);
//--------------------------------------
// 奇异期权
//--------------------------------------
switch (req.TradeType)
{
case "凤凰期权":
var autocall = req.trade_autocall;
if (!autocall.IsAnnualized2 || !autocall.AnnualizeFactor2.HasValue)
{
autocall.AnnualizeFactor2 = 1;
req.MetaDic[nameof(autocall.AnnualizeFactor2)] = "/";
}
req.Strike = autocall.SpreadStrike1;
break;
case "雪球期权":
CheckSnowball(req);
break;
case "亚式期权":
if (req?.trade_asian_option?.StrikeType == "Floating")
{
req.Strike = null;
}
if (req?.trade_asian_option?.StrikeType == "Segmented" && req?.trade_asian_option?.PayoffType != "EnhancedArithmeticAverage")
{
throw new ServiceException("行权价类型为'分段式'时,均价计算类型必须为'增强算术平均'");
}
if (req?.trade_asian_option?.PayoffType == "EnhancedArithmeticAverage" && req?.trade_asian_option?.StrikeType == "Floating")
{
throw new ServiceException("均价计算类型为'增强算术平均'时,行权价类型不能为'浮动行权价'");
}
break;
case "自定义交易":
req.ExerciseMode = null;
break;
case "二元期权":
if (req.ExerciseMode == "American")
{
var payoffType = req.trade_binary_option.PayoffType;
if (payoffType.StartsWith("Down", StringComparison.OrdinalIgnoreCase))
{
req.OptionType = ConsGlobal.CallPut.Put_CN;
}
else if (payoffType.StartsWith("Double", StringComparison.OrdinalIgnoreCase))
{
req.OptionType = "";
}
else
{
req.OptionType = ConsGlobal.CallPut.Call_CN;
}
}
else
{
req.trade_binary_option.ObservationDates = null;
}
break;
case "障碍期权":
if (string.IsNullOrEmpty(req.trade_barrier_option.BarrierType))
{
throw new ServiceException("障碍类型 必须填写");
}
if (!req.trade_barrier_option.BarrierType.EndsWith("敲出"))
{
req.trade_barrier_option.RebateAnnualizedAtKO = false;
}
break;
case "累计期权":
if (req.trade_accumulator_option.AccumulatorStructureType != AccumulatorStructureTypeEnum.Segmented)
{
if (req.OptionType == "看涨")
{
if (req.Strike > req.trade_accumulator_option.KOBarrier)
{
throw new ServiceException("[累计期权]期权看涨时,执行价格必须小于等于障碍价格");
}
}
else if (req.Strike < req.trade_accumulator_option.KOBarrier)
{
throw new ServiceException("[累计期权]期权看跌时,执行价格必须大于等于障碍价格");
}
if (req.trade_accumulator_option.EarlyTerminate && req.trade_accumulator_option.SettlementMode == "现金期末")
{
throw new ServiceException("[累计期权]结算方式为'现金结算(期末)'时,需要将'敲出是否终止'设为'否'");
}
}
else
{
if (req.trade_accumulator_option.Strike2 == null)
{
throw new ServiceException("[累计期权 三段式]执行价格2 必须填写");
}
if (req.trade_accumulator_option.Strike3 == null)
{
throw new ServiceException("[累计期权 三段式]执行价格3 必须填写");
}
if (req.trade_accumulator_option.KOBarrier == null)
{
throw new ServiceException("[累计期权 三段式]障碍价格 必须填写");
}
if (req.trade_accumulator_option.Strike2 < req.Strike)
{
throw new ServiceException("[累计期权 三段式]执行价格2需要大于等于执行价格1");
}
if (req.trade_accumulator_option.Strike3 < req.trade_accumulator_option.Strike2)
{
throw new ServiceException("[累计期权 三段式]执行价格3需要大于等于执行价格2");
}
if (req.OptionType == "看涨")
{
if (req.trade_accumulator_option.Strike2 >= req.trade_accumulator_option.KOBarrier || req.trade_accumulator_option.Strike3 < req.trade_accumulator_option.KOBarrier)
{
throw new ServiceException("[累计期权 三段式]障碍价格需要大于执行价2小于等于执行价3");
}
}
else
{
if (req.Strike > req.trade_accumulator_option.KOBarrier || req.trade_accumulator_option.Strike2 <= req.trade_accumulator_option.KOBarrier)
{
throw new ServiceException("[累计期权 三段式]障碍价格需要大于等于执行价1小于执行价2");
}
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode2))
{
req.trade_accumulator_option.SettlementMode2 = "现金当日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType2))
{
req.trade_accumulator_option.ForwardDateType2 =
PS.Config.Company == CompanyEnum.中粮
? "标的到期日"
: PS.Config.Is厦门象屿
? "标的交割月前一交易日"
: "期权到期日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType2))
{
req.trade_accumulator_option.ForwardPriceType2 = "期权行权价格";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode3))
{
req.trade_accumulator_option.SettlementMode3 = "现金当日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType3))
{
req.trade_accumulator_option.ForwardDateType3 =
PS.Config.Company == CompanyEnum.中粮
? "标的到期日"
: PS.Config.Is厦门象屿
? "标的交割月前一交易日"
: "期权到期日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType3))
{
req.trade_accumulator_option.ForwardPriceType3 = "期权行权价格";
}
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.PayoffType))
{
req.trade_accumulator_option.PayoffType = "固定";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode))
{
req.trade_accumulator_option.SettlementMode = "现金当日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType))
{
req.trade_accumulator_option.ForwardDateType =
PS.Config.Company == CompanyEnum.中粮
? "标的到期日"
: PS.Config.Is厦门象屿
? "标的交割月前一交易日"
: "期权到期日";
}
if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType))
{
req.trade_accumulator_option.ForwardPriceType = "期权行权价格";
}
break;
case "Risky期权":
var risky = req.trade_risky_option;
req.Strike = risky.Strike1;
break;
}
//检查价差期权(暂时在外部处理)
//检查互算是否正确(名义本金、权利金、交易总额、交易数量、交易份额)
//--------------------------------------
// 其它处理
//--------------------------------------
if (isAddNew)
{
req.TradeStatus = ConsTrade.新增待确认;
}
if (!req.ParticipationRate.HasValue)
{
req.ParticipationRate = 1;
}
// 无风险利率
if (!req.NoRiskRate.HasValue)
{
req.NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate / 100;
}
else if (req.NoRiskRate < 0)
{
throw new ServiceException("无风险利率 填写错误,必须大于等于0,请求值:" + req.NoRiskRate);
}
//分红率是可以为负值的
//--------------------------------------
// 名义本金或总额成交互算 -- 保底收益率
//--------------------------------------
PrepareOtcTradeCalc(req);
if (req.SettlementType < 0 || req.SettlementType > SettlementTypeEnum.ReferencePrice)
{
throw new ServiceException("结算方式 填写错误,当前支持的类型为:0(收盘价)、1(结算价),、2(参考价),请求值为:" + (int)req.SettlementType);
}
//存续天数
if (!req.DurationDays.HasValue)
{
req.DurationDays = (short)(req.ExerciseDate.Value.Date.Subtract(req.TradeDate.Value.Date).TotalDays + 1);
}
}
//准备互换交易
private void PrepareSwapTrade(trade req, TradeSourceEnum dataSource)
{
PrepareSwapTradeClient(req);
if (!req.SettlementDate.HasValue)
{
req.SettlementDate = req.ExerciseDate;
}
req.OriginalNotional = req.Notional;
req.BuySell = (req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0) == 0 ? (req.trade_swap.IsPayFloatingProfit ? "卖出" : "买入") : ((req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0) > 0 ? "卖出" : "买入");
req.Strike = null;
req.OriginalStockEqvNotional = req.StockEqvNotional;
req.StockEqvNotionalReal = req.StockEqvNotional;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(req.QuoteCurrency, req.SettlementCurrency, req.TradeDate.Value
, seekPreday: dataSource == TradeSourceEnum.系统交易);
req.TradePrice = (req.trade_swap.IsTradePriceWhenOpen ? Math.Abs((req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0)) : 0) * currencyRate;
}
//场外期权交易波动率、平滑过渡天数
private static void PrepareOtcTradeVol(trade req, TradeSourceEnum dataSource)
{
if (!PS.Config.IsTradeVol)
{
//volsuface模式下也存在开仓波动率
req.TradeCloseVolatility = null;
if (req.Vol.HasValue)
{
req.TradeOpenVolatility = req.Vol;
}
else
{
req.Vol = req.TradeOpenVolatility; //从webapi请求,填写的是TradeOpenVolatility
}
}
else if (req.TradeType != "自定义交易" && req.TradeType != "远期" && req.TradeType != "收益互换" && req.TradeType != "现金流交易")
{
if (!req.TradeOpenVolatility.HasValue)
{
throw new ServiceException("成交波动率 必须填写");
}
if (req.TradeOpenVolatility.Value < 0)
{
throw new ServiceException("成交波动率 必须大于等于0");
}
if (!req.TradeCloseVolatility.HasValue)
{
throw new ServiceException("目标波动率 必须填写");
}
if (req.TradeCloseVolatility.Value < 0)
{
throw new ServiceException("目标波动率 必须大于等于0");
}
if (req.NumOfSmoothingDays.HasValue)
{
if (req.NumOfSmoothingDays.Value < 1)
{
throw new ServiceException("平滑过渡天数 必须大于0");
}
var remianDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1;
if (req.NumOfSmoothingDays > remianDays)
{
throw new ServiceException($"平滑过渡天数{req.NumOfSmoothingDays}不能大于存续天数{remianDays}天");
}
}
else if (dataSource == TradeSourceEnum.导入交易)
{
req.NumOfSmoothingDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1;
}
else
{
throw new ServiceException("平滑过渡天数 必须填写");
}
}
else if (req.TradeType == "自定义交易")
{
if (req.NumOfSmoothingDays.HasValue)
{
if (req.NumOfSmoothingDays.Value < 1)
{
throw new ServiceException("平滑过渡天数 必须大于0");
}
var remianDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1;
if (req.NumOfSmoothingDays > remianDays)
{
throw new ServiceException($"平滑过渡天数{req.NumOfSmoothingDays}不能大于存续天数{remianDays}天");
}
}
}
}
//衍生品交易客户信息
private void PrepareOtcTradeClient(trade req, TradeSourceEnum dataSource)
{
var client = PrepareBaseTradeClient(req);
var structureTypes = new List() { "牛市价差", "熊市价差" };//OTC-5843此两种组合特殊,去除客户可接受风险等级判断
if ((client.RiskServiceDegree == null || (client.RiskServiceDegree < 5 && !structureTypes.Contains(req.StructureType))) && req.BuySell == "买入")
{
throw new ServiceException($"客户'{client.Name}'为非高风险客户,没有卖出权限");
}
//if (_underlying != null && _underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity))
//{
// throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行权益类的期权交易");
//}
if (PS.Config.ErpElement.SecuritiesEnvironment)
{
if (client.AccessRule == AccessRuleEnum.MySideAccess.GetHashCode() && req.BuySell == "买入")
{
throw new ServiceException($"客户'{client.Name}'为我方准入,不能进行买入的期权交易");
}
if (client.AccessRule == AccessRuleEnum.OpSideAccess.GetHashCode() && req.BuySell == "卖出")
{
throw new ServiceException($"客户'{client.Name}'为对手方准入,不能进行卖出的期权交易");
}
}
req.ClientName = client.Name;
}
//互换交易
private void PrepareSwapTradeClient(trade req)
{
var client = PrepareBaseTradeClient(req);
if (_underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity))
{
throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行收益互换交易");
}
req.ClientName = client.Name;
req.SettlementCurrency = client.SettlementCurrency;
}
//客户信息
private Client PrepareBaseTradeClient(trade req)
{
if (req.ClientId < 1)
{
throw new ServiceException("客户信息不存在");
}
var client = DbContextFactory.GetClientDbContext(OptUser).client.FirstOrDefault(n => n.id == req.ClientId);
if (client == null)
{
throw new ServiceException("客户信息不存在");
}
if (client.ProcessStatus == "已休眠" || client.ProcessStatus == "已销户")
{
if (!client.ProcessOptDate.HasValue || client.ProcessOptDate.Value < req.TradeDate.Value)
{
var tradeDateStr = req.TradeDate.Value.ToString("yyyy-MM-dd");
var processDateStr = client.ProcessOptDate?.ToString("yyyy-MM-dd HH:mm:ss");
throw new ServiceException($"客户'{client.Name}'{client.ProcessStatus}并且处理日期{processDateStr}早于交易日期'{tradeDateStr}',无法保存交易!");
}
}
else if (client.ProcessStatus != "已开户")
{
throw new ServiceException($"客户'{client.Name}'尚未完成开户!");
}
return client;
}
//衍生品交易名义本金或总额成交互算
private void PrepareOtcTradeCalc(trade req)
{
if (!req.IsAnnualized || !req.AnnualizeFactor.HasValue)
{
req.AnnualizeFactor = 1;
req.MetaDic[nameof(req.AnnualizeFactor)] = "/";
}
var spotPrice = Math.Abs(req.SpotPrice == null ? 0 : req.SpotPrice.Value);
var CountRatio = _underlying == null ? 0 : _underlying.CountRatio;
var participationRate = req.ParticipationRate ?? 0;
var saveExt = req.SaveExt;
var annualizeFactor = req.AnnualizeFactor;
req.TradePrice = Commons.OtcFormatHelper.GetTradePriceDouble(req.TradePrice ?? 0);
//保存初始保底收益总额 实际保底收益率
//req.PrincipalSum = req.OriginalPrincipalSum;
req.PrincipalRate = TradeHelper.GetPrincipalRateReal(req.StockEqvNotionalReal, req.OriginalPrincipalSum, annualizeFactor);
if (req.TradeType == "现金流交易")
{
req.StockEqvNotionalReal = req.StockEqvNotional;
req.Notional = 0;
req.TradeSinglePrice = 0;
}
else if (req.TradeType == "远期")
{
req.Notional = req.TradeAmount * CountRatio;
req.TradeSinglePrice = req.Notional > 0 ? req.TradePrice / req.Notional : 0;
if (PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(req.BasisUnderlyingCode))
{
var spotInfo = req.MetaDic.ContainsKey("期初信息") ? JsonHelper.Deserialize(req.MetaDic["期初信息"]) : null;
if (spotInfo?.Length < 2)
{
throw new SystemException($"MetaDic标的价格有误");
}
double.TryParse(spotInfo[0], out var price1);
double.TryParse(spotInfo[1], out var price2);
req.StockEqvNotional = price1 > price2 ? TradeHelper.GetStockEqvNotional(req.Notional * price1, 1, 1) : TradeHelper.GetStockEqvNotional(req.Notional * price2, 1, 1);
}
else
{
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.Notional * spotPrice, 1, 1);
}
req.StockEqvNotionalReal = req.StockEqvNotional;
}
else
{
var isAutoCall = false;
switch (req.TradeType)
{
case "凤凰期权":
isAutoCall = true;
annualizeFactor = req.trade_autocall.AnnualizeFactor2;
break;
case "雪球期权":
isAutoCall = true;
annualizeFactor = req.trade_snowball.AnnualizeFactor2;
break;
}
if (saveExt != null && saveExt.NotionalFlag != TradingNotionalFlag.None)
{
if (req.TradeType == "累计期权")
{
var customObservDates = QdpHelper.ParseObservationDate(req.trade_accumulator_option.KOObservationDates);
customObservDates ??= Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn")
.BizDaysBetweenDatesExcluStartDay(req.TradeDate.Value, req.ExerciseDate.Value).ToArray();
req.TradeAmount = req.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length;
req.Notional = req.TradeAmount * CountRatio;
req.StockEqvNotionalReal = req.Notional * spotPrice;
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
req.IsTradePricePayType = saveExt.PremiumFlag == TradingPremiumFlag.TradePrice;
if (req.IsTradePricePayType)
{
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
}
else
{
req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice);
}
}
else
{
switch (saveExt.NotionalFlag)
{
case TradingNotionalFlag.StockEqvNotionalReal:
{
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
var notionalEqv = isAutoCall ? req.StockEqvNotional * participationRate : req.StockEqvNotionalReal;
req.Notional = Math.Abs(spotPrice) > 0 ? notionalEqv / spotPrice : 0;
req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio;
}
break;
case TradingNotionalFlag.StockEqvNotional:
{
req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor);
var notionalEqv = isAutoCall ? req.StockEqvNotional * participationRate : req.StockEqvNotionalReal;
req.Notional = Math.Abs(spotPrice) > 0 ? notionalEqv / spotPrice : 0;
req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio;
}
break;
case TradingNotionalFlag.Notional:
req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio;
if (isAutoCall)
{
req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0;
req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor);
}
else
{
req.StockEqvNotionalReal = req.Notional * spotPrice;
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
}
break;
case TradingNotionalFlag.TradeAmount:
req.Notional = CountRatio == 0 ? 0 : req.TradeAmount * CountRatio;
if (isAutoCall)
{
req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0;
req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor);
}
else
{
req.StockEqvNotionalReal = req.Notional * spotPrice;
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
}
break;
default:
throw new SystemException($"[{nameof(TradeSavePreHandler)}.{nameof(PrepareOtcTradeCalc)}][NotionalFlag]系统程序错误");
}
req.IsUsePremiumRate = false;
req.IsTradePricePayType = false;
switch (saveExt.PremiumFlag)
{
case TradingPremiumFlag.TradePrice:
req.IsTradePricePayType = true;
req.IsUsePremiumRate = saveExt.NotionalFlag == TradingNotionalFlag.StockEqvNotionalReal || saveExt.NotionalFlag == TradingNotionalFlag.StockEqvNotional;
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
break;
case TradingPremiumFlag.TradeSinglePrice:
req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice);
break;
case TradingPremiumFlag.PremiumRate:
req.IsUsePremiumRate = true;
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.PremiumRate, spotPrice);
req.TradePrice = TradeHelper.GetTradePriceByPremiumRate(req.PremiumRate, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
break;
case TradingPremiumFlag.AllZero:
break;
default:
throw new SystemException($"[{nameof(TradeSavePreHandler)}.{nameof(PrepareOtcTradeCalc)}][PremiumFlag]系统程序错误");
}
}
}
else
{
if (req.TradeType == "累计期权")
{
var customObservDates = QdpHelper.ParseObservationDate(req.trade_accumulator_option.KOObservationDates);
customObservDates ??= Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn")
.BizDaysBetweenDatesExcluStartDay(req.TradeDate.Value, req.ExerciseDate.Value).ToArray();
req.TradeAmount = req.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length;
req.Notional = req.TradeAmount * CountRatio;
req.StockEqvNotionalReal = req.Notional * spotPrice;
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
if (req.IsTradePricePayType)
{
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
}
else
{
req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice);
}
}
else
{
if (req.IsUsePremiumRate == true)
{
if (req.IsTradePricePayType == true)
{
req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
}
else
{
req.TradePrice = TradeHelper.GetTradePriceByPremiumRate(req.PremiumRate, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
}
req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor);
if (isAutoCall)
{
req.Notional = Math.Abs(spotPrice) > 0 ? req.StockEqvNotional * participationRate / spotPrice : 0;
}
else
{
req.Notional = Math.Abs(spotPrice) > 0 ? req.StockEqvNotionalReal / spotPrice : 0;
}
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.PremiumRate, spotPrice);
}
else
{
if (isAutoCall)
{
req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0;
req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor);
}
else
{
req.StockEqvNotionalReal = req.Notional * spotPrice;
req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor);
}
if (req.IsTradePricePayType == true)
{
req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true);
}
else
{
req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true);
req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice);
}
}
req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio;
}
}
}
req.TradePrice = Commons.OtcFormatHelper.GetTradePriceDouble(req.TradePrice ?? 0);
//最大名义本金
if (saveExt != null && saveExt.MaxSpotPrice > 0)
{
req.StockEqvNotionalMax = TradeHelper.GetStockEqvNotional(saveExt.MaxSpotPrice.Value * req.Notional, participationRate, annualizeFactor);
}
else //if (!req.StockEqvNotionalMax.HasValue || req.StockEqvNotionalMax.Value < 1e-6)
{
req.StockEqvNotionalMax = req.StockEqvNotional;
}
req.OriginalNotional = req.Notional;
req.OriginalStockEqvNotional = req.StockEqvNotional;
}
///
/// 检查交易权限
///
class TradeRightChecker
{
IEnumerable _allVarietyIds;
readonly Dictionary> _checkDic;
public TradeRightChecker()
{
_checkDic = new Dictionary>();
}
///
/// 检查是否交易员
///
public void Check(AssetUnit assetUnit, int traderId, int? clientId, int varietyId, int varietyId2 = 0, string tradeType = null)
{
if (traderId < 1)
{
throw new ServiceException("请选择交易员");
}
//交易用户组必须和簿记账户一致
if (ConsUserGroup.HasGroup)
{
var userGroup = UserBLL.GetUserGroup(traderId) ?? string.Empty;
if (userGroup != (assetUnit.UserGroup ?? string.Empty))
{
throw new ServiceException($"交易员所在用户组({userGroup})必须和簿记账户所在用户组({assetUnit.UserGroup})一致");
}
}
if (!_checkDic.TryGetValue("trader:" + traderId, out var userVarietyIds))
{
if (!UserBLL.IsTradeOfCurrentLogin(traderId))
{
throw new ServiceException("非交易员,不允许录入交易");
}
if (!("," + assetUnit.TraderIds + ",").Contains("," + traderId + ","))
{
throw new ServiceException("交易员不在所选簿记中,请重新选择簿记");
}
var varietyIds = TradeRightProvider.GetUserVarietyIds(traderId);
if (varietyIds == null)
{
_checkDic["trader:" + traderId] = userVarietyIds = null;
}
else
{
_checkDic["trader:" + traderId] = userVarietyIds = varietyIds.ToHashSet();
}
}
if (varietyId < 1 && userVarietyIds == null)
{
return;
}
if (userVarietyIds != null && !userVarietyIds.Contains(varietyId) && tradeType != "现金流交易" && varietyId != 0)
{
throw new ServiceException("该交易员没有该品种的交易权限,不能添加该品种交易");
}
if (varietyId2 != 0)
{
if (userVarietyIds != null && !userVarietyIds.Contains(varietyId2))
{
throw new ServiceException("该交易员没有该品种的交易权限,不能添加该品种交易");
}
}
if (!clientId.HasValue || clientId.Value < 1)
{
return;
}
HashSet clientVarietyidSet = null;
if (PS.Config.ClientElement.CanSelectCreditVariety && !_checkDic.TryGetValue("client:" + clientId.Value, out clientVarietyidSet))
{
var clientCredits = DbContextFactory.GetYLDbContext()
.credit.Where(c => c.ClientId == clientId.Value && c.ProcessStatus == "已审批" && (c.Type == CreditTable.RoleType || (c.Type == CreditTable.ClientType && c.IsVariety == true)))
.Select(n => new
{
n.VarietyId,
IsFeelingWhiteList = n.Type == CreditTable.RoleType || n.IsWhitelist == 1,
}).ToArray();
if (clientCredits.Any())
{
clientVarietyidSet = new HashSet();
foreach (var c in clientCredits)
{
var VarietyIds = DataConvert.ConvertCommaValuesToInt32Array(c.VarietyId);
//白名单、黑名单
if (c.IsFeelingWhiteList)
{
foreach (var v in VarietyIds)
{
clientVarietyidSet.Add(v);
}
}
else
{
_allVarietyIds ??= VarietyBLL.GetAllvarietyModel().Select(v => v.id).ToArray();
foreach (var v in _allVarietyIds)
{
clientVarietyidSet.Add(v);
}
foreach (var v in VarietyIds)
{
clientVarietyidSet.Remove(v);
}
}
}
}
_checkDic["client:" + clientId.Value] = clientVarietyidSet;
}
if (clientVarietyidSet != null && !clientVarietyidSet.Contains(varietyId))
{
throw new ServiceException("该客户没有该品种的交易权限,不能添加该品种交易");
}
if (varietyId2 != 0)
{
if (clientVarietyidSet != null && !clientVarietyidSet.Contains(varietyId2))
{
throw new ServiceException("该客户没有该品种的交易权限,不能添加该品种交易");
}
}
}
}
///
/// 检查行权方式
///
static void CheckExerciseMode(trade req)
{
if (req.TradeType == "远期" || req.TradeType == "现金流交易")
{
req.ExerciseMode = string.Empty;
}
else
{
switch (req.ExerciseMode)
{
case "欧式":
req.ExerciseMode = "European";
break;
case "美式":
req.ExerciseMode = "American";
break;
case "European":
case "American":
break;
default:
if (!string.IsNullOrWhiteSpace(req.ExerciseMode))
{
throw new ServiceException("行权方式 填写错误:" + req.ExerciseMode);
}
if (req.TradeType != "自定义交易")
{
throw new ServiceException("行权方式 必须填写");
}
break;
}
if (req.ExerciseMode == "American")
{
switch (req.TradeType)
{
case "双鲨期权":
case "凤凰期权":
case "雪球期权":
case "合成价差期权":
case "区间累积期权":
case "Risky期权":
throw new ServiceException($"行权方式 填写错误:{req.TradeType}不支持美式行权");
case "亚式期权":
if (req.trade_asian_option?.StrikeType == "Floating")
{
throw new ServiceException("行权方式 填写错误:亚式期权美式行权时不支持浮动行权价方式");
}
else if (req.trade_asian_option?.StrikeType == "Segmented")
{
throw new ServiceException("行权方式 填写错误:亚式期权美式行权时不支持分段式行权价方式");
}
break;
case "二元期权":
if (req.trade_binary_option?.RebateType == "AtHit" && req.trade_binary_option?.PayoffType == "DoubleNoTouch")
{
throw new ServiceException("行权方式 填写错误:二元美式行权并且二元类型为'DoubleNoTouch'时不支持'立即'补偿支付");
}
break;
}
}
else
{
switch (req.TradeType)
{
case "亚式期权":
if (req.trade_asian_option?.StrikeType == "Segmented" && req.trade_asian_option.PayoffType != "EnhancedArithmeticAverage")
{
throw new ServiceException("行权价类型为'分段式'时,均价计算类型只支持'增强算术平均'");
}
if (req.trade_asian_option?.StrikeType == "Floating" && req.trade_asian_option.PayoffType == "EnhancedArithmeticAverage")
{
throw new ServiceException("均价计算类型为'增强算术平均'时,行权价类型不能为'浮动行权价'");
}
break;
}
}
}
}
///
/// 检查看涨看跌和远期多空方向
///
static void CheckOptionType(OtcTradeBase req)
{
switch (req.OptionType)
{
case "看涨":
case "看跌":
break;
case "Call":
case "多头":
req.OptionType = "看涨";
break;
case "Put":
case "空头":
req.OptionType = "看跌";
break;
default:
if (!string.IsNullOrWhiteSpace(req.OptionType))
{
throw new ServiceException((req.TradeType == "远期" ? "多空方向" : "看涨看跌") + " 填写错误:" + req.OptionType);
}
if (req.TradeType != "自定义交易" && req.TradeType != "现金流交易" && (req.TradeType != "二元期权" || req.ExerciseMode != ConsGlobal.ExerciseMode.American))
{
throw new ServiceException((req.TradeType == "远期" ? "多空方向" : "看涨看跌") + " 必须填写");
}
break;
}
if (req.TradeType == "Risky期权" && req.OptionType != "看涨")
{
throw new ServiceException($"看涨看跌 填写错误:{req.TradeType}不支持看跌");
}
}
}
}