964 lines
40 KiB
C#
964 lines
40 KiB
C#
using Qdp.Foundation.Utilities;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Eod;
|
|
using YLErp.BLL.EodSettlement;
|
|
using YLErp.Commons;
|
|
using YLErp.CustomizedBizLogic;
|
|
using YLErp.DBModels;
|
|
using YLErp.DBModels.Consts;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Model.Enum;
|
|
using YLErp.Modules.ClientModule;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.TradeModule.DealModule
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class TradeCashService : TradeServiceBase
|
|
{
|
|
public TradeCashService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
public TradeCashService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据资金记录Id查找资金记录
|
|
/// </summary>
|
|
/// <param name="tradeCashIds">资金记录Id</param>
|
|
/// <param name="tradeIds">交易Id</param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public List<trade_cash> SearchTradeCashList(List<int> tradeCashIds, List<int> tradeIds)
|
|
{
|
|
var query = DbContext.trade_cash.AsQueryable();
|
|
if (tradeCashIds != null && tradeCashIds.Count > 0)
|
|
{
|
|
query = query.Where(O => tradeCashIds.Contains(O.id));
|
|
}
|
|
if (tradeIds != null && tradeIds.Count > 0)
|
|
{
|
|
query = query.Where(O => tradeIds.Contains(O.TradeId));
|
|
}
|
|
return query.ToList();
|
|
}
|
|
|
|
public List<trade_cash> SearchTradeCashLists()
|
|
{
|
|
var query = DbContext.trade_cash.AsQueryable();
|
|
return query.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据交易计算行权的交割金,新生成
|
|
/// 来自tradeBLL.ExecutionTradeCash
|
|
/// </summary>
|
|
public trade_cash ExecutionTradeCash(trade td, double? finalPrice = null)
|
|
{
|
|
var r = new trade_cash
|
|
{
|
|
TradeId = td.id,
|
|
TradeType = td.BuySell,
|
|
Trade_TradeType = td.TradeType,
|
|
ExceciseType = "现金",
|
|
CallPut = td.CallPut,
|
|
Strike = td.Strike,
|
|
Notional = td.Notional
|
|
};
|
|
|
|
var valueDate = valuedateBLL.ValueDate;
|
|
if (PS.Config.Company == Configuration.CompanyEnum.厦门象屿 && td.SettlementType == SettlementTypeEnum.ReferencePrice)
|
|
{
|
|
valueDate = SpecialModule.XiaMenXiangYuHelper.GetRefernceValueDate(valueDate);
|
|
}
|
|
if (td.ExerciseDate != null && valueDate > td.ExerciseDate)
|
|
{
|
|
valueDate = td.ExerciseDate.Value;
|
|
}
|
|
r.ValueDate = valueDate;
|
|
|
|
r.SpotPrice = EodPriceQueryService.TryGetEodPrice(valueDate, td.UnderlyingCode, out var eodPrice)
|
|
? eodPrice.GetPrice(td.SettlementType) : DataCacheProvider.GetUnderlyingDataSource().GetPrice(r.UnderlyingId ?? 0);
|
|
|
|
var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode);
|
|
r.TradeAmount = r.Notional / CountRatio;
|
|
//r.SpotPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(td.UnderlyingCode);
|
|
r.ClientName = td.ClientName;
|
|
r.IsMoneynessOptionData = td.IsMoneynessOptionData;
|
|
r.IsMoneynessOption = td.IsMoneynessOption == "是";
|
|
r.InitialSpotPrice = (td.SpotPrice ?? 0);
|
|
r.UnderlyingInstrumentType = td.UnderlyingInstrumentType;
|
|
r.ValidStrike = r.IsMoneynessOptionData ? ((td.SpotPrice * td.Strike) ?? 0.0) : (r.Strike ?? 0.0);
|
|
r.ExerciseCostRatio = valuedateBLL.SystemDate.ExerciseCostRatio;
|
|
//修改只收买方行权费
|
|
r.ExtraAmount = Math.Abs(td.StockEqvNotional * (valuedateBLL.SystemDate.ExerciseCostRatioReal ?? 0)) * ("卖出".Equals(td.BuySell) ? 1 : 0);
|
|
tradeBLL.SetFieldsByTradeType(td);
|
|
r.IsPossibleExec = true;
|
|
|
|
if (finalPrice != null)
|
|
{
|
|
r.FinalPrice = finalPrice;
|
|
}
|
|
else
|
|
{
|
|
if (td.ExerciseDate <= valueDate && EodPriceQueryService.TryGetEodPrice(td.ExerciseDate.Value, td.UnderlyingCode, out var ep))
|
|
{
|
|
r.FinalPrice = ep.GetPrice(td.SettlementType);
|
|
}
|
|
else if (UnderlyingPriceProvider.TryGetPrice(td.UnderlyingCode, out var price))
|
|
{
|
|
r.FinalPrice = price;
|
|
}
|
|
}
|
|
|
|
if (td.IsMoneynessOption == "是")
|
|
{
|
|
r.StrikeString = ((r.Strike ?? 0) * 100).ToString("0.00") + "%";
|
|
}
|
|
else
|
|
{
|
|
r.StrikeString = (r.Strike ?? 0).ToString("0.00");
|
|
}
|
|
|
|
var childTrades = new List<trade>();
|
|
if (td.TradeType == "结构化交易")
|
|
{
|
|
childTrades = DbContext.trade.Where(x => x.ParentTradeId == td.id).ToList();
|
|
foreach (var trade in childTrades)
|
|
{
|
|
if (trade.Strike != r.Strike)
|
|
{
|
|
r.StrikeTwo = trade.Strike;
|
|
|
|
if (trade.IsMoneynessOption == "是")
|
|
{
|
|
r.StrikeTwoString = ((r.StrikeTwo ?? 0) * 100).ToString("0.00") + "%";
|
|
}
|
|
else
|
|
{
|
|
r.StrikeTwoString = (r.StrikeTwo ?? 0).ToString("0.00");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (td.TradeStatus == ConsTrade.行权待复核)
|
|
{
|
|
r.IsPossibleExec = true;
|
|
}
|
|
|
|
if (td.TradeType == "结构化交易")
|
|
{
|
|
double amount = 0;
|
|
foreach (var trade in childTrades)
|
|
{
|
|
var tradeCash = new trade_cash();
|
|
tradeBLL.SetFieldsByTradeType(trade);
|
|
SettlementCalcCommons.SetPossibleExec(tradeCash, trade, r.FinalPrice ?? 0, true, finalPriceSettleDate: r.ValueDate);
|
|
|
|
amount += tradeCash.Amount;
|
|
}
|
|
|
|
r.Amount = amount;
|
|
r.InitialAmount = amount;
|
|
}
|
|
else
|
|
{
|
|
SettlementCalcCommons.SetPossibleExec(r, td, r.FinalPrice ?? 0, true, finalPriceSettleDate: r.ValueDate);
|
|
}
|
|
|
|
r.OptionType = td.TradeType;
|
|
r.IsPossibleExec = r.IsPossibleExec && r.Amount != 0;
|
|
r.Amount += r.ExtraAmount ?? 0;
|
|
r.UnderlyingId = td.UnderlyingId;
|
|
r.UnderlyingCode = td.UnderlyingCode;
|
|
r.UnderlyingName = td.UnderlyingAssetName;
|
|
r.DurationDays = td.DurationDays;
|
|
|
|
//四舍五入处理,保留小数点两位
|
|
r.Amount = Math.Round(r.Amount * 100) / 100;
|
|
r.InitialAmount = Math.Round(r.InitialAmount * 100) / 100;
|
|
|
|
r.OptId = UserId;
|
|
r.OptName = UserName;
|
|
r.OptDate = OptDate;
|
|
|
|
return r;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 黑箱交易主交易tradecash保存子交易期权费加和的值
|
|
/// clientcashincashout保存一条0的记录
|
|
/// 主要为了解决我们是按照子交易进行结算,但是客户结算报告,又想以主交易来展示主交易资金
|
|
/// </summary>
|
|
/// <param name="td"></param>
|
|
/// <returns></returns>
|
|
public trade_cash SaveGroupOpenTradeCash(trade td)
|
|
{
|
|
var r = new trade_cash
|
|
{
|
|
TradeId = td.id,
|
|
TradeType = td.BuySell,
|
|
ExceciseType = "现金",
|
|
CallPut = td.CallPut,
|
|
Strike = td.Strike,
|
|
Amount = ((td.BuySell == "买入" ? -1 : 1) * td.TradePrice) ?? 0,
|
|
Notional = td.Notional,
|
|
TradeAmount = td.TradeAmount,
|
|
ValueDate = td.TradeDate.Value,
|
|
Action = "系统操作-期权费",
|
|
OptDate = OptDate,
|
|
OptId = UserId,
|
|
OptName = UserName
|
|
};
|
|
|
|
DbContext.trade_cash.Add(r);
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
#region---存入ClientCashInCashOut---
|
|
|
|
var cl = ClientDataQueryService.GetClient(td.ClientId, true);
|
|
var ee = new ClientCashInCashOut
|
|
{
|
|
Direction = "应收",
|
|
Number = UniqueTimeId.GetStr(),
|
|
ClientId = cl.id,
|
|
ClientNumber = cl.Number,
|
|
ClientName = cl.Name,
|
|
HappenDate = r.ValueDate,
|
|
State = ClientCashInCashOut.已确认,
|
|
OptId = r.OptId,
|
|
OptName = r.OptName,
|
|
OptDate = r.OptDate,
|
|
CreatorId = r.OptId,
|
|
CreatorName = r.OptName,
|
|
CreateDate = r.OptDate,
|
|
TradeId = r.TradeId,
|
|
TradeCashId = r.id,
|
|
Action = r.Action,
|
|
TradeNumber = td.TradeNumber,
|
|
IsGroup = td.IsGroup
|
|
};
|
|
DbContext.ClientCashInCashOut.Add(ee);
|
|
|
|
#endregion
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
return r;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 来自:executionTradeCashJson
|
|
/// </summary>
|
|
public ExecutionTradeCashResult ExecutionTradeCash(TradeCashReq req)
|
|
{
|
|
var isFromRecheck = false;
|
|
var tradeDalService = new TradeDalModule.TradeDalService(OptUser);
|
|
//修改交易主表信息交易状态为已执行
|
|
var td = DbContext.trade.Find(req.TradeId);
|
|
if (td.Warning)
|
|
{
|
|
tradeDalService.RollbackToBeforeSettle(td, valuedateBLL.ValueDate);
|
|
}
|
|
var unwindNotional = td.IsUsePremiumRate == true ? req.UnwindPercentRate / 100 * td.OriginalNotional : req.UnwindNotional;
|
|
if (unwindNotional >= Math.Floor(td.Notional * Math.Pow(10, 8)) / Math.Pow(10, 8))
|
|
{
|
|
req.UnwindType = "全部行权";
|
|
td.UnWindNotional = td.Notional;
|
|
}
|
|
else
|
|
{
|
|
td.UnWindNotional = unwindNotional;
|
|
}
|
|
|
|
//行权复核逻辑验证
|
|
if ((valuedateBLL.SystemDate.CloseReCheck == 1) && td.TradeStatus == ConsTrade.行权待复核)
|
|
{
|
|
//检查行权单价
|
|
//部分行权是按照 Action=系统操作_平仓费 来处理的
|
|
var tcash = DbContext.trade_cash.Where(dd => dd.TradeId == req.TradeId && (dd.Action == ClientCashInCashOut.系统操作_行权费 && (dd.UnwindType == "全部行权" || dd.UnwindType.Contains("到期")) || dd.Action == ClientCashInCashOut.系统操作_平仓费 && dd.UnwindType == "部分行权") && dd.ValidState == ConsGlobal.InValid && !dd.IsDeleted).OrderByDescending(x => x.id).FirstOrDefault();
|
|
if (req.UnwindType != null && req.UnwindType.Contains("全部") && td.OriginalStockEqvNotional > 0)
|
|
{
|
|
req.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional.Value * 100;
|
|
}
|
|
if (td.TradeType == "自定义交易")
|
|
{
|
|
if (req.UnwindPrice == tcash.UnwindPrice && req.FinalPrice == tcash.FinalPrice && req.ExtraAmount == tcash.ExtraAmount && (req.UnwindNotional == tcash.UnwindNotional && td.IsUsePremiumRate != true || Math.Abs(req.UnwindPercentRate / 100 - (tcash.UnwindPercentRate ?? 0)) <= 1e-7 && td.IsUsePremiumRate == true))
|
|
{
|
|
isFromRecheck = true;
|
|
td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfExercise);
|
|
DbContext.SaveChanges();
|
|
}
|
|
else
|
|
{
|
|
//审核失败
|
|
throw new ServiceException("复核信息不一致,复核不通过");
|
|
}
|
|
}
|
|
if (req.FinalPrice == tcash.FinalPrice && req.ExtraAmount == tcash.ExtraAmount && (req.UnwindNotional == tcash.UnwindNotional && td.IsUsePremiumRate != true || Math.Abs(req.UnwindPercentRate / 100 - (tcash.UnwindPercentRate ?? 0)) <= 1e-7 && td.IsUsePremiumRate == true))
|
|
{
|
|
isFromRecheck = true;
|
|
td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfExercise);
|
|
DbContext.SaveChanges();
|
|
}
|
|
else
|
|
{
|
|
//审核失败
|
|
throw new ServiceException("复核信息不一致,复核不通过");
|
|
}
|
|
}
|
|
|
|
//------------------------------------
|
|
// 存在审批流程时,走审批成功流程
|
|
//------------------------------------
|
|
|
|
if (valuedateBLL.SystemDate.CloseReApprove == 1 && HasTradeProcess() && td.ExerciseMode == "American")
|
|
{
|
|
var openResult = new TradeOpenService(this).UpdateTradeProcessLog(new TradeOpenReqModel
|
|
{
|
|
tradeId = td.id,
|
|
status = "pass",
|
|
comments = "行权复核审批成功",
|
|
ignoreMoneyCheck = false,
|
|
notNeedOperationHistory = true,
|
|
isPartialExercise = req.UnwindType == "部分行权",
|
|
isExpire = req.IsExpire
|
|
});
|
|
|
|
if (openResult.RetCode == TradeOpenRetCode.Success || string.IsNullOrWhiteSpace(openResult.ErrorMsg))
|
|
{
|
|
//生成文档
|
|
if (PS.Config.IsAutoGenerateContracts)
|
|
{
|
|
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { td.id });
|
|
}
|
|
|
|
SaveTradeOperationHistory(td, "行权复核");
|
|
|
|
return new ExecutionTradeCashResult(td) { Success = true, Message = "复核审批成功!" };
|
|
}
|
|
|
|
return new ExecutionTradeCashResult(td) { Success = false, Message = openResult.ErrorMsg };
|
|
}
|
|
|
|
//------------------------------------
|
|
// 行权操作处理
|
|
//------------------------------------
|
|
|
|
var result = new ExecutionTradeCashResult(td) { Success = true, Message = "执行成功" };
|
|
|
|
//行权
|
|
if (req.UnwindType == "部分行权")
|
|
{
|
|
result.TradeCash = new TradeUnwindService(this).UnwindTrade(td, req, isFromRecheck, true);
|
|
}
|
|
else
|
|
{
|
|
result.TradeCash = ExecutionTradeCashInner(td, req, isFromRecheck);
|
|
}
|
|
|
|
trade pairTrade = null;
|
|
|
|
//配对 行权(不需要复核且不需要审批的行权操作)
|
|
if (req.IsUnwindPairTrade && !string.IsNullOrEmpty(td.PairTrade))
|
|
{
|
|
var pairTradeId = Convert.ToInt32(td.PairTrade);
|
|
pairTrade = DbContext.trade.Find(pairTradeId);
|
|
if (pairTrade.Warning)
|
|
{
|
|
tradeDalService.RollbackToBeforeSettle(pairTrade, SystemValueDate);
|
|
}
|
|
if (req.UnwindType == "部分行权")
|
|
{
|
|
try
|
|
{
|
|
new TradeUnwindService(this).UnwindTrade(pairTrade, req, isFromRecheck, true);
|
|
result.Message += ",配对交易也执行成功";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Message += ",配对交易执行失败:" + ex.Message;
|
|
LogFactory.GetLogger("ExecutionTradeCash").Error("配对交易部分行权失败", ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == pairTrade.id && !t.IsDeleted && t.Action == ClientCashInCashOut.系统操作_行权费);
|
|
if (tc == null)
|
|
{
|
|
tc = ExecutionTradeCash(pairTrade, req.FinalPrice);
|
|
}
|
|
|
|
req.Amount = tc.Amount;
|
|
req.ExtraAmount = tc.ExtraAmount ?? 0;
|
|
req.TradeId = pairTradeId;
|
|
try
|
|
{
|
|
ExecutionTradeCashInner(pairTrade, req, isFromRecheck);
|
|
result.Message += ",配对交易也执行成功";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Message += ",配对交易执行失败:" + ex.Message;
|
|
LogFactory.GetLogger("ExecutionTradeCash").Error("配对交易行权失败", ex);
|
|
}
|
|
}
|
|
|
|
SaveTradeOperationHistory(pairTrade, "执行合约");
|
|
}
|
|
|
|
//生成文档
|
|
if (PS.Config.IsAutoGenerateContracts)
|
|
{
|
|
var tradeIds = new List<int> { td.id };
|
|
if (pairTrade != null && pairTrade.TradeStatus == ConsTrade.已执行)
|
|
{
|
|
tradeIds.Add(td.id);
|
|
}
|
|
|
|
new TradeContractGenerateService(this).GenerateContractsAsync(tradeIds);
|
|
}
|
|
|
|
SaveTradeOperationHistory(td, "执行合约");
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 行权处理(tradeCashController.executionTradeCashInner)
|
|
/// </summary>
|
|
protected trade_cash ExecutionTradeCashInner(trade td, TradeCashReq req, bool isFromRecheck = false)
|
|
{
|
|
var valueDate = td.ExerciseDate != null && td.ExerciseDate < valuedateBLL.ValueDate ? td.ExerciseDate.Value : valuedateBLL.ValueDate;
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (PS.Config.TradeElement.ExecuteAfterGeneratedConfirmDoc && !td.HasGeneratedConfirmBook() && td.TradeType != "自定义交易")
|
|
{
|
|
throw new ServiceException("请先去生成交易确认书");
|
|
}
|
|
if (td.TradeStatus != ConsTrade.确认成交 && td.TradeStatus != ConsTrade.提前终止拒绝 && td.TradeStatus != ConsTrade.行权待复核)
|
|
{
|
|
throw new ServiceException("行权失败,状态为" + td.TradeStatus + ",该交易状态不能行权");
|
|
}
|
|
|
|
if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && HasTradeProcess()))
|
|
&& td.ExerciseMode == "American" && td.TradeStatus != ConsTrade.行权待复核)
|
|
{
|
|
throw new ServiceException("行权失败, 需要行权复核!");
|
|
}
|
|
|
|
td.OptDate = OptDate;
|
|
td.OptId = UserId;
|
|
td.OptName = UserName;
|
|
|
|
if (req.UnwindType != null && req.UnwindType.Contains("全部") && td.OriginalStockEqvNotional > 0)
|
|
{
|
|
req.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional.Value * 100;
|
|
}
|
|
|
|
//增加现金交割交易
|
|
var tc = ExerciseTrade_TradeCashSave(td, req, valueDate, isFromRecheck);
|
|
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成");
|
|
if (groupAction != null)
|
|
{
|
|
groupAction.Status = "已完成";
|
|
tc.ParentTradeCashId = groupAction.ParentTradeCashId;
|
|
tc.ParentTradeId = groupAction.ParentTradeId;
|
|
}
|
|
tc.ValidState = ConsGlobal.Valid;
|
|
|
|
td.TradeStatus = tc.UnwindType == "到期" ? ConsTrade.已到期 : ConsTrade.已执行;
|
|
td.FinalPrice = tc.FinalPrice;
|
|
|
|
//增加执行日期
|
|
td.UnWindDate = tc.HappenedDate ?? tc.ValueDate;
|
|
td.TradeAmount = 0;
|
|
td.StockEqvNotional = 0;
|
|
td.Notional = 0;
|
|
SaveTradeCashDetail(tc, false);
|
|
|
|
//增加出入金记录
|
|
var cl = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
|
|
var ee = new ClientCashInCashOut
|
|
{
|
|
Direction = "应收",
|
|
Number = UniqueTimeId.GetStr(),
|
|
ClientId = cl.id,
|
|
ClientNumber = cl.Number,
|
|
ClientName = cl.Name,
|
|
//客户和交易员反方向
|
|
Money = tc.Amount * -1,
|
|
HappenDate = tc.ValueDate,
|
|
State = ClientCashInCashOut.已确认,
|
|
//ee.OpenBankId = bc.id + "";
|
|
//ee.OpenBankCard = bc.Card;
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = tc.OptDate,
|
|
CreatorId = tc.OptId,
|
|
CreatorName = tc.OptName,
|
|
CreateDate = tc.OptDate,
|
|
Action = ClientCashInCashOut.系统操作_行权费
|
|
};
|
|
ee.Number = UniqueTimeId.GetStr();
|
|
ee.TradeNumber = td.TradeNumber;
|
|
ee.IsGroup = td.IsGroup;
|
|
DbContext.ClientCashInCashOut.Add(ee);
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
//Utilities.AddLog(td.id, trade.LogClass, "修改", "执行交割完成,修改交易主表状态为已执行");
|
|
//Utilities.AddLog(tc.id, trade_cash.LogClass, "创建", "执行交割增加现金交割交易记录");
|
|
//Utilities.AddLog(ee.id, ClientCashInCashOut.LogClass, "创建", "执行交割增加应收账记录");
|
|
|
|
//删除E/Bod数据
|
|
RemoveEodTradeAndFutureInfo(false, td.id, tc.ValueDate);
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
return tc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 复核的时候不需要重新赋值(这里的行为执行后整笔交易被完全了结)
|
|
/// </summary>
|
|
protected trade_cash ExerciseTrade_TradeCashSave(trade td, TradeCashReq req, DateTime valueDate, bool isFromRecheck = false)
|
|
{
|
|
var tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == td.id && !t.IsDeleted && t.Action == ClientCashInCashOut.系统操作_行权费);
|
|
|
|
//复核审批的交易 复核时不需要再次给trade_cash重新赋值
|
|
if (isFromRecheck && tc != null)
|
|
{
|
|
if (PS.Config.SalesCommissionCalculation == "公式1")
|
|
{
|
|
//销售提成
|
|
new SalesModule.SalesCommissionDetailDataService(UserInfo).CalcuSalesCommissionDetail(tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.招证)
|
|
{
|
|
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(td, tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.物产中大)
|
|
{
|
|
new BizLogicWCZD().GenerateWCZDNumber(DbContext, td, tc.ValueDate, tc.id);
|
|
}
|
|
return tc;
|
|
}
|
|
else if (tc == null)
|
|
{
|
|
tc = new trade_cash();
|
|
DbContext.trade_cash.Add(tc);
|
|
}
|
|
|
|
tc.ValidState = req.ValidState;
|
|
tc.OptId = UserId;
|
|
tc.OptName = UserName;
|
|
tc.OptDate = OptDate;
|
|
tc.Action = ClientCashInCashOut.系统操作_行权费;
|
|
tc.IsLastAction = true;
|
|
|
|
//执行到期时,如果有结算日期,资金记在结算日,发生日期记为到期日
|
|
if ((req.TradeStatus == ConsTrade.已到期 || req.TradeStatus == ConsTrade.已执行) && td.ExerciseDate == valueDate)
|
|
{
|
|
tc.ValueDate = td.SettlementDate ?? td.ExerciseDate.Value;
|
|
tc.HappenedDate = td.ExerciseDate;
|
|
}
|
|
else
|
|
{
|
|
tc.ValueDate = valueDate;
|
|
tc.HappenedDate = valueDate;
|
|
}
|
|
|
|
//------------------------------------------
|
|
//如果到期日为节假日则国君了结日期取节前一交易日其他公司取节后一交易日
|
|
//如果到期日为节假日则国君资金发生的日期是节后第一个交易日
|
|
//------------------------------------------
|
|
tc.ValueDate = QdpCalendarHelper.GetNonHoliday(tc.ValueDate);
|
|
if (PS.Config.IsGuoJun)
|
|
{
|
|
tc.HappenedDate = QdpCalendarHelper.GetNonHolidayDefore(tc.HappenedDate.Value);
|
|
}
|
|
else
|
|
{
|
|
tc.HappenedDate = QdpCalendarHelper.GetNonHoliday(tc.HappenedDate.Value);
|
|
}
|
|
|
|
//------------------------------------------
|
|
|
|
tc.FinalPrice = req.FinalPrice;
|
|
tc.SpotPrice = req.SpotPrice;
|
|
tc.Strike = req.Strike;
|
|
tc.CallPut = req.CallPut;
|
|
tc.Amount = req.Amount ?? 0;
|
|
tc.UnwindPrice = req.UnwindPrice;
|
|
tc.UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
|
|
tc.ExtraAmount = req.ExtraAmount;
|
|
tc.Status = TradeCashStatusEnum.已执行;
|
|
tc.TradeType = td.BuySell;
|
|
tc.TradeId = td.id;
|
|
tc.ParentTradeId = td.ParentTradeId;
|
|
tc.Notional = td.Notional;
|
|
tc.UnwindTradeAmount = td.TradeAmount;
|
|
tc.UnwindType = req.TradeStatus == ConsTrade.已到期 ? "到期" : req.UnwindType;
|
|
tc.UnwindNotional = req.UnwindNotional;
|
|
tc.UnwindPercentRate = req.UnwindPercentRate / 100;
|
|
tc.NotionalPercentRate = tc.UnwindPercentRate;
|
|
tc.UnwindStockEqvNotional = req.UnwindStockEqvNotional;
|
|
tc.ExerciseWay = req.IsExpire ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权;
|
|
tc.TradePremium = req.TradePremium;
|
|
var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode);
|
|
tc.TradeAmount = tc.Notional / CountRatio;
|
|
tc.ExceciseType = req.ExceciseType;
|
|
tc.UnwindVol = 0;
|
|
tc.ParentTradeCashId = req.ParentTradeCashId;
|
|
DbContext.SaveChanges();
|
|
|
|
if (PS.Config.SalesCommissionCalculation == "公式1" && isFromRecheck)
|
|
{
|
|
//销售提成
|
|
new SalesModule.SalesCommissionDetailDataService(UserInfo).CalcuSalesCommissionDetail(tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.招证)
|
|
{
|
|
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(td, tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.物产中大)
|
|
{
|
|
new BizLogicWCZD().GenerateWCZDNumber(DbContext, td, tc.ValueDate, tc.id);
|
|
}
|
|
return tc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 平仓保存trade_cash
|
|
/// </summary>
|
|
protected trade_cash CloseTrade_TradeCashSave(OtcTradeBase td, TradeCashReq req, bool isFromRecheckOrKO = false, bool needMinusHandle = true, bool isLastAction = false, bool saveChanges = true)
|
|
{
|
|
//增加现金交割交易记录,查询待审的现金交割记录(Invalid 这边为无效或是待审核交割记录)
|
|
var tc = DbContext.trade_cash.Where(t => t.TradeId == td.id && (t.Action == ClientCashInCashOut.系统操作_平仓费 || t.Action == ClientCashInCashOut.系统操作_互换) && t.ValidState == ConsGlobal.InValid && !t.IsDeleted).OrderByDescending(x => x.id).FirstOrDefault();
|
|
|
|
//复核审批的交易复核时不需要再次给trade_cash重新赋值
|
|
if (isFromRecheckOrKO && tc != null)
|
|
{
|
|
if (PS.Config.SalesCommissionCalculation == "公式1")
|
|
{
|
|
//销售提成
|
|
new SalesModule.SalesCommissionDetailDataService(this).CalcuSalesCommissionDetail(tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.招证)
|
|
{
|
|
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(td, tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.物产中大)
|
|
{
|
|
new BizLogicWCZD().GenerateWCZDNumber(DbContext, td, tc.ValueDate, tc.id);
|
|
}
|
|
return tc;
|
|
}
|
|
else
|
|
{
|
|
if (tc == null)
|
|
{
|
|
tc = new trade_cash();
|
|
DbContext.trade_cash.Add(tc);
|
|
}
|
|
}
|
|
|
|
tc.ValidState = req.ValidState;
|
|
tc.OptId = UserId;
|
|
tc.OptName = UserName;
|
|
tc.OptDate = OptDate;
|
|
tc.ExceciseType = "现金";
|
|
tc.TradeType = td.BuySell;
|
|
tc.CallPut = td.CallPut;
|
|
tc.Strike = td.IsMoneynessOptionData ? (td.Strike * td.SpotPrice) : td.Strike;
|
|
tc.Notional = (req == null ? td.Notional : req.Notional) ?? 0;
|
|
tc.TradeAmount = req == null ? td.TradeAmount : req.TradeAmount;
|
|
tc.IsLastAction = isLastAction;
|
|
|
|
if (needMinusHandle)
|
|
{
|
|
if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1)
|
|
{
|
|
//添加根据交易方向判断资金方向
|
|
tc.UnwindPrice = req.UnwindPrice * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
tc.UnwindPricePercentRate = req.UnwindPricePercentRate * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
}
|
|
else
|
|
{
|
|
//添加根据交易方向判断资金方向
|
|
tc.UnwindPrice = req.UnwindPrice * (td.TradeType == "远期" || !ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
tc.UnwindPricePercentRate = req.UnwindPricePercentRate * (td.TradeType == "远期" || !ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
}
|
|
|
|
if (valuedateBLL.SystemDate.UnwindAmountAngle == 1)
|
|
{
|
|
tc.Amount = req.UnwindFee;
|
|
tc.QuoteAmount = req.QuoteUnwindFee;
|
|
}
|
|
else
|
|
{
|
|
tc.Amount = req.UnwindFee * (td.TradeType == "远期" ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
tc.QuoteAmount = req.QuoteUnwindFee * (td.TradeType == "远期" ? 1 : EodOperationBase.GetSign(td.BuySell));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tc.Amount = req.UnwindFee;
|
|
tc.QuoteAmount = req.QuoteUnwindFee;
|
|
//部分行权和互换平仓UnwindFee包含了符号
|
|
tc.UnwindPrice = req.UnwindPrice;
|
|
tc.UnwindPricePercentRate = req.UnwindPricePercentRate;
|
|
}
|
|
|
|
tc.UnwindMethod = req.UnwindMethod;
|
|
tc.UnwindStockEqvNotional = req.UnwindStockEqvNotional ?? 0;
|
|
//1.远期存入平仓时算出的预付金占用成本
|
|
//2.部分行权时候的额外金额
|
|
//3.互换交易的平仓费用
|
|
tc.ExtraAmount = req.ExtraAmount;
|
|
|
|
tc.TradeId = td.id;
|
|
tc.FinalPrice = req.FinalPrice;
|
|
tc.SpotPrice = req.SpotPrice;
|
|
tc.UnwindVol = req.UnwindVol;
|
|
tc.HappenedDate = req.HappenedDate;
|
|
tc.BarrierPrice = req.BarrierPrice;
|
|
tc.VolType = req.VolType;
|
|
tc.UnwindType = req.UnwindType;
|
|
tc.UnwindMethod = req.UnwindMethod;
|
|
tc.UnwindStockEqvNotional = req.UnwindStockEqvNotional ?? 0;
|
|
tc.ExerciseWay = td.TradeType == "远期" ? "提前终止行权" : null;//远期了结都是提前终止
|
|
//远期页面操作平仓时,执行到这里,td.Notional已经被赋值为0了,会导致UnwindNotional错误;
|
|
if (tc.UnwindType == "全部平仓" && td.Notional != 0)
|
|
{
|
|
tc.UnwindNotional = td.Notional;
|
|
tc.UnwindTradeAmount = td.TradeAmount;
|
|
}
|
|
else
|
|
{
|
|
tc.UnwindNotional = req.UnwindNotional;
|
|
var underlying = underlying_managerBLL.GetByCode(td.UnderlyingCode);
|
|
if (underlying != null && underlying.UnderlyingInstrumentType != "Stock")
|
|
{
|
|
tc.UnwindTradeAmount = tc.UnwindNotional / underlying.CountRatio;
|
|
}
|
|
else
|
|
{
|
|
tc.UnwindTradeAmount = tc.UnwindNotional;
|
|
}
|
|
}
|
|
if (tc.UnwindMethod == 1)//选中数量平仓
|
|
{
|
|
tc.UnwindTradeAmount = req.UnwindTradeAmount;
|
|
}
|
|
tc.Action = ClientCashInCashOut.系统操作_平仓费;
|
|
tc.Status = TradeCashStatusEnum.已执行;
|
|
if (!req.ValueDate.HasValue)
|
|
{
|
|
tc.ValueDate = valuedateBLL.ValueDate;
|
|
}
|
|
else
|
|
{
|
|
tc.ValueDate = req.ValueDate.Value;
|
|
}
|
|
|
|
if ((req.TradeStatus == ConsTrade.已到期 || req.TradeStatus == ConsTrade.已执行) && td.ExerciseDate == tc.ValueDate)
|
|
{
|
|
tc.ValueDate = td.SettlementDate ?? td.ExerciseDate.Value;
|
|
tc.HappenedDate = td.ExerciseDate;
|
|
tc.HappenedDate = QdpCalendarHelper.GetNonHoliday(tc.HappenedDate.Value);
|
|
}
|
|
|
|
tc.UnwindPercentRate = req.UnwindPercentRate;
|
|
tc.NotionalPercentRate = req.UnwindPercentRate;
|
|
tc.TradePremium = req.TradePremium;
|
|
tc.AdvanceMoney = req.AdvanceMoney ?? 0;
|
|
tc.ParentTradeCashId = req.ParentTradeCashId;
|
|
//if (saveChanges)
|
|
//{
|
|
DbContext.SaveChanges();
|
|
if (PS.Config.SalesCommissionCalculation == "公式1" && isFromRecheckOrKO || (isLastAction && req.ValidState != "InValid"))
|
|
{
|
|
//销售提成
|
|
new SalesModule.SalesCommissionDetailDataService(this).CalcuSalesCommissionDetail(tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.招证)
|
|
{
|
|
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(td, tc);
|
|
}
|
|
if (PS.Config.Company == Configuration.CompanyEnum.物产中大)
|
|
{
|
|
new BizLogicWCZD().GenerateWCZDNumber(DbContext, td, tc.ValueDate, tc.id);
|
|
}
|
|
//}
|
|
return tc;
|
|
}
|
|
|
|
protected void unwindPreHandle(TradeCashReq req, trade td, bool isFromExercise, out bool isPartialClose)
|
|
{
|
|
isPartialClose = true;
|
|
if (req != null)
|
|
{
|
|
if (req.UnwindType == "全部平仓")
|
|
{
|
|
isPartialClose = false;
|
|
req.UnwindNotional = td.TradeType == "现金流交易" ? req.UnwindNotional : td.Notional;
|
|
}
|
|
|
|
req.Notional = td.Notional;
|
|
req.TradeAmount = td.TradeAmount;
|
|
|
|
//对名义本金成交方式的平仓做数据处理
|
|
if (td.IsUsePremiumRate == true)
|
|
{
|
|
req.UnwindPercentRate /= 100;
|
|
|
|
if (td.OriginalStockEqvNotional > 0 && (req.UnwindType != null && req.UnwindType.Contains("全部") || req.UnwindPercentRate >= td.StockEqvNotional / td.OriginalStockEqvNotional))
|
|
{
|
|
req.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional.Value;
|
|
isPartialClose = false;
|
|
}
|
|
|
|
req.UnwindPricePercentRate /= 100;
|
|
req.UnwindNotional = td.TradeType == "现金流交易" ? req.UnwindNotional : td.OriginalNotional.Value * req.UnwindPercentRate;
|
|
}
|
|
else
|
|
{
|
|
if (req.UnwindNotional >= td.Notional)
|
|
{
|
|
if (req.UnwindNotional - td.Notional > 1e-2)
|
|
{
|
|
if (isFromExercise)
|
|
{
|
|
throw new ServiceException("行权份额高于持仓份额,请刷新页面后再做行权处理");
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException("平仓份额高于持仓份额,请刷新页面后再做平仓处理");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
req.UnwindNotional = td.TradeType == "现金流交易" ? req.UnwindNotional : td.Notional;
|
|
}
|
|
isPartialClose = false;
|
|
}
|
|
|
|
req.UnwindPercentRate = td.OriginalNotional > 0 ? req.UnwindNotional / td.OriginalNotional.Value : 0;
|
|
}
|
|
|
|
if (isFromExercise)
|
|
{
|
|
if (td.IsUsePremiumRate == true)
|
|
{
|
|
var unwindPricePercentRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
|
|
req.UnwindPricePercentRate = unwindPricePercentRate;
|
|
}
|
|
|
|
req.UnwindFee = req.Amount ?? 0;
|
|
}
|
|
|
|
if (req.ValueDate.HasValue)
|
|
{
|
|
if (req.ValueDate < td.TradeDate)
|
|
{
|
|
if (isFromExercise)
|
|
{
|
|
throw new ServiceException("行权日期不能小于交易日期");
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException("平仓日期不能小于交易日期");
|
|
}
|
|
}
|
|
if (req.ValueDate > valuedateBLL.ValueDate)
|
|
{
|
|
if (isFromExercise)
|
|
{
|
|
throw new ServiceException("行权日期不能大于系统日期");
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException("平仓日期不能大于系统日期");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取最新的了结记录
|
|
/// 行权平仓时检查未来的了结记录
|
|
/// </summary>
|
|
public trade_cash GetLastSettleInfo(int tradeId)
|
|
{
|
|
var tc = DbContext.trade_cash
|
|
.Where(O => O.TradeId == tradeId && O.Action != "系统操作-期权费" && O.ValidState != ConsGlobal.InValid && !O.IsDeleted)
|
|
.OrderByDescending(O => O.ValueDate)
|
|
.FirstOrDefault();
|
|
return tc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 平仓保存 出入金
|
|
/// </summary>
|
|
public trade_cash_detail SaveTradeCashDetail(trade_cash tc, bool saveChanges = true)
|
|
{
|
|
var tcd = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = tc.Amount,
|
|
ExtraAmount = tc.ExtraAmount,
|
|
SinglePrice = tc.UnwindPrice,
|
|
SinglePricePercentRate = tc.UnwindPricePercentRate,
|
|
ValueDate = tc.ValueDate,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcd);
|
|
if (saveChanges)
|
|
{
|
|
DbContext.SaveChanges();
|
|
}
|
|
return tcd;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 交易了结执行结果
|
|
/// </summary>
|
|
public class ExecutionTradeCashResult
|
|
{
|
|
public bool Success;
|
|
|
|
public string Message;
|
|
|
|
public trade Trade { get; }
|
|
|
|
public trade_cash TradeCash;
|
|
|
|
public ExecutionTradeCashResult(trade trade)
|
|
{
|
|
Trade = trade;
|
|
}
|
|
}
|
|
}
|