713 lines
35 KiB
C#
713 lines
35 KiB
C#
using System.Data;
|
|
using YLErp.BLL;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.EodModule;
|
|
using YLErp.Modules.TradeModule;
|
|
|
|
namespace YLErp.Modules.ClientModule
|
|
{
|
|
/// <summary>
|
|
/// 客户持仓服务
|
|
/// </summary>
|
|
public class ClientPositionService : YLBaseService
|
|
{
|
|
public ClientPositionService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public ClientPositionService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户持仓(返回分页数据)
|
|
/// </summary>
|
|
public IPagedList<ClientPositionModel> GetAllPositionsV1(ClientPositionQueryModel queryModel)
|
|
{
|
|
return InnerGetAllPositionsV1(queryModel);
|
|
}
|
|
|
|
private IPagedList<ClientPositionModel> InnerGetAllPositionsV1(ClientPositionQueryModel queryModel, ApiVersion apiVersion = ApiVersion.v1)
|
|
{
|
|
if (queryModel.ValueDate == null)
|
|
{
|
|
queryModel.ValueDate = valuedateBLL.ValueDate;
|
|
}
|
|
|
|
//日终结算数据
|
|
if (queryModel.ValueDate <= EodDataHelper.GetLastSettleDate(valuedateBLL.ValueDate))
|
|
{
|
|
var query = CreateEodPositionQuery(queryModel);
|
|
|
|
var pagedList = query.ToPagedList(queryModel);
|
|
|
|
ProcessClientPositionModel(apiVersion, pagedList, queryModel.ValueDate.Value, true);
|
|
|
|
LoadTradeMeta(pagedList);
|
|
|
|
return pagedList;
|
|
}
|
|
else //日内实时数据
|
|
{
|
|
var query = CreateIntradayPositionQuery(queryModel);
|
|
var pagedList = query.ToPagedList(queryModel);
|
|
|
|
ProcessClientPositionModel(apiVersion, pagedList, queryModel.ValueDate.Value, false);
|
|
|
|
//读取交易的期权结构信息
|
|
LoadTradeExtend(pagedList);
|
|
|
|
LoadTradeMeta(pagedList);
|
|
|
|
return pagedList;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户持仓V2(返回分页数据)
|
|
/// </summary>
|
|
public IPagedList<ClientPositionModel> GetAllPositionsV2(ClientPositionQueryModel queryModel)
|
|
{
|
|
return InnerGetAllPositionsV2(queryModel);
|
|
}
|
|
|
|
private IPagedList<ClientPositionModel> InnerGetAllPositionsV2(ClientPositionQueryModel queryModel, ApiVersion apiVersion = ApiVersion.v2)
|
|
{
|
|
queryModel.AllStatusIncluded = true;
|
|
|
|
var pagedList = GetAllPositionsV1(queryModel);
|
|
|
|
//获取平仓价和平仓标的价
|
|
|
|
//平仓价和平仓标的价只有交易确认后才有
|
|
var tradeIds = pagedList.Where(n => n.TradeStatus >= EnumTradeStatus.confirmed).Select(n => n.TradeId).ToArray();
|
|
|
|
var tcDic = new Dictionary<int, TradeCashData>(tradeIds.Length);
|
|
|
|
for (var i = 0; i < tradeIds.Length; i += 1000)
|
|
{
|
|
var subTradeIds = tradeIds.Skip(i).Take(1000);
|
|
|
|
var query = from tc in DbContext.trade_cash
|
|
where subTradeIds.Contains(tc.TradeId)
|
|
&& tc.Action != ClientCashInCashOut.系统操作_期权费
|
|
&& tc.ValidState != "InValid" && !tc.IsDeleted
|
|
group tc by tc.TradeId into g
|
|
select new TradeCashData
|
|
{
|
|
tradeId = g.Key,
|
|
UnwindNotional = g.Sum(n => n.UnwindNotional ?? 0),
|
|
UnwindAmount = g.Sum(n => n.Amount),
|
|
FinalPrice = g.Sum(n => (n.FinalPrice ?? 0) * (n.UnwindNotional ?? 0)),
|
|
UnwindDate = g.Max(n => n.ValueDate)
|
|
};
|
|
|
|
foreach (var item in query)
|
|
{
|
|
tcDic.Add(item.tradeId, item);
|
|
}
|
|
}
|
|
|
|
//获取对冲波动率
|
|
//所有交易状态的对冲波动率都需要
|
|
tradeIds = pagedList.Select(n => n.TradeId).ToArray();
|
|
var dic2 = new TradeHisDataService(this).GetLatestHedgeVol(tradeIds);
|
|
|
|
//赋值
|
|
|
|
foreach (var item in pagedList)
|
|
{
|
|
if (tcDic.TryGetValue(item.TradeId, out var data) && data.UnwindNotional > 0)
|
|
{
|
|
//把平仓总额转为正向值(这样平仓单价和界面输入时的符号一致)
|
|
var amount = data.UnwindAmount * TradeCalcHelper.GetBuySellSign(item.BuySell);
|
|
item.UnwindPrice = amount / data.UnwindNotional;
|
|
item.FinalPrice = data.FinalPrice / data.UnwindNotional;
|
|
item.UnwindDate = data.UnwindDate;
|
|
}
|
|
else
|
|
{
|
|
item.UnwindPrice = 0;
|
|
item.FinalPrice = 0;
|
|
}
|
|
|
|
if (dic2.TryGetValue(item.TradeId, out var hedgeVol))
|
|
{
|
|
item.HedgeVol = hedgeVol;
|
|
}
|
|
}
|
|
|
|
return pagedList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户持仓V3(返回分页数据)
|
|
/// </summary>
|
|
public IPagedList<ClientPositionModel> GetAllPositionsV3(ClientPositionQueryModel queryModel)
|
|
{
|
|
if ("v2".Equals(queryModel.DataVersion, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return InnerGetAllPositionsV2(queryModel, ApiVersion.v3);
|
|
}
|
|
|
|
return InnerGetAllPositionsV1(queryModel, ApiVersion.v3);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建客户日内持仓查询语句
|
|
/// </summary>
|
|
/// <param name="clientId">客户ID,为0表示所有客户</param>
|
|
private IQueryable<ClientPositionModel> CreateIntradayPositionQuery(ClientPositionQueryModel queryModel)
|
|
{
|
|
var clientId = queryModel.ClientId;
|
|
var clientNumber = queryModel.ClientNumber.TrimToNull();
|
|
var tradeStatus = queryModel.TradeStatus;
|
|
|
|
if (clientId < 1 && !string.IsNullOrEmpty(clientNumber))
|
|
{
|
|
var cid = DataCacheProvider.GetClientDataSource().AsQueryable()
|
|
.Where(n => clientNumber.Equals(n.Number, StringComparison.OrdinalIgnoreCase))
|
|
.Select(n => (int?)n.id).FirstOrDefault();
|
|
if (!cid.HasValue)
|
|
{
|
|
return Array.Empty<ClientPositionModel>().AsQueryable();
|
|
}
|
|
clientId = cid.Value;
|
|
}
|
|
|
|
var ValueDate = valuedateBLL.ValueDate;
|
|
var predicate = PredicateBuilder.Create<trade>(n => n.ClientId > 0
|
|
&& n.ValidState != ConsGlobal.InValid && (n.TradeType != "结构化交易" || n.IsGroup == 1));
|
|
if (clientId > 0)
|
|
{
|
|
predicate = PredicateBuilder.Create<trade>(n => n.ClientId == clientId).And(predicate);
|
|
}
|
|
|
|
if (queryModel.AllStatusIncluded)
|
|
{
|
|
if (tradeStatus != null && tradeStatus.Any(n => n > 0))
|
|
{
|
|
var status = tradeStatus.Select(n => TradeHelper.GetTradeStatus(n)).ToHashSet();
|
|
predicate = predicate.And(n => status.Contains(n.TradeStatus));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var suspensionUnderlyingIdList = DbContext.underlying_manager.Where(t => t.UnderlyingStatus == underlying_manager.Status_Suspension).Select(t => t.id).ToList();
|
|
predicate = predicate.And(n => ConsTrade.NeedMarginTradeStatusList.Contains(n.TradeStatus)
|
|
&& (n.ExerciseDate >= ValueDate || suspensionUnderlyingIdList.Contains(n.UnderlyingId)));
|
|
}
|
|
|
|
var riskQuery = from tr in DbContext.realtime_trade_risk where tr.ValueDate == ValueDate && tr.VolType == "持仓" select tr;
|
|
var posiQuery = from tr in DbContext.intraday_trade_position where tr.ValueDate == ValueDate select new { tr.TradeId, tr.Margin };
|
|
|
|
var snowballQuery = DbContext.trade_snowball.AsNoTracking();
|
|
var asianQuery = DbContext.trade_asian_option.AsNoTracking();
|
|
var barrierQuery = DbContext.trade_barrier_option.AsNoTracking();
|
|
|
|
var binaryOptionQuery = DbContext.trade_binary_option.AsNoTracking();
|
|
var rainbowOptionQuery = DbContext.trade_rainbow_option.AsNoTracking();
|
|
var spreadOptionQuery = DbContext.trade_spread_option.AsNoTracking();
|
|
var doubleSharkfinOptionQuery = DbContext.trade_double_sharkfin_option.AsNoTracking();
|
|
var rangeaccrualOptionQuery = DbContext.trade_rangeaccrual.AsNoTracking();
|
|
var underlyingEnhanceOptionQuery = DbContext.trade_underlying_enhance.AsNoTracking();
|
|
var airbagOptionQuery = DbContext.trade_airbag.AsNoTracking();
|
|
var autocallOptionQuery = DbContext.trade_autocall.AsNoTracking();
|
|
|
|
var query = from trade in DbContext.trade.Where(predicate)
|
|
join assetbook in DbContext.assetunit on trade.AssetId equals assetbook.id
|
|
join risk in riskQuery on trade.id equals risk.TradeId into risks
|
|
from risk in risks.DefaultIfEmpty()
|
|
join epos in DbContext.eod_trade_position on new { valueDate = ValueDate, id = trade.TraderId } equals new { valueDate = epos.ValueDate, id = epos.TradeId } into positions
|
|
from epos in positions.DefaultIfEmpty()
|
|
join posi in posiQuery on trade.id equals posi.TradeId into posis
|
|
from posi in posis.DefaultIfEmpty()
|
|
join asian in asianQuery on new { TradeId = trade.id, trade.TradeType } equals new { asian.TradeId, TradeType = "亚式期权" } into asian_t
|
|
from asian in asian_t.DefaultIfEmpty()
|
|
join barrier in barrierQuery on new { TradeId = trade.id, trade.TradeType } equals new { barrier.TradeId, TradeType = "障碍期权" } into barrier_t
|
|
from barrier in barrier_t.DefaultIfEmpty()
|
|
join snowball in snowballQuery on new { TradeId = trade.id, trade.TradeType } equals new { snowball.TradeId, TradeType = "雪球期权" } into snowball_t
|
|
from snowball in snowball_t.DefaultIfEmpty()
|
|
join binary in binaryOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { binary.TradeId, TradeType = "二元期权" } into binary_t
|
|
from binary in binary_t.DefaultIfEmpty()
|
|
join rainbow in rainbowOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { rainbow.TradeId, TradeType = "彩虹期权" } into rainbow_t
|
|
from rainbow in rainbow_t.DefaultIfEmpty()
|
|
join spread in spreadOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { spread.TradeId, TradeType = "价差期权" } into spread_t
|
|
from spread in spread_t.DefaultIfEmpty()
|
|
join doublesharkfin in doubleSharkfinOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { doublesharkfin.TradeId, TradeType = "双鲨期权" } into doublesharkfin_t
|
|
from doublesharkfin in doublesharkfin_t.DefaultIfEmpty()
|
|
join rangeaccrual in rangeaccrualOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { rangeaccrual.TradeId, TradeType = "区间累积期权" } into rangeaccrual_t
|
|
from rangeaccrual in rangeaccrual_t.DefaultIfEmpty()
|
|
join underlyingEnhance in underlyingEnhanceOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { underlyingEnhance.TradeId, TradeType = "收益增强结构" } into underlyingEnhance_t
|
|
from underlyingEnhance in underlyingEnhance_t.DefaultIfEmpty()
|
|
join airbag in airbagOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { airbag.TradeId, TradeType = "气囊结构" } into airbag_t
|
|
from airbag in airbag_t.DefaultIfEmpty()
|
|
join autocall in autocallOptionQuery on new { TradeId = trade.id, trade.TradeType } equals new { autocall.TradeId, TradeType = "凤凰期权" } into autocall_t
|
|
from autocall in autocall_t.DefaultIfEmpty()
|
|
orderby trade.id descending
|
|
select new ClientPositionModel
|
|
{
|
|
ClientId = trade.ClientId,
|
|
TradeNumber = trade.TradeNumber,
|
|
UnderlyingCode = trade.UnderlyingCode,
|
|
BuySell = trade.BuySell,//交易员角度提供数据
|
|
RoundedPv = risk == null ? 0 : (double?)risk.RoundedPv.Value,
|
|
RoundedPnl = risk == null ? 0 : (double?)risk.RoundedPositionPnl.Value,
|
|
ExerciseDate = trade.ExerciseDate,
|
|
InitialSpotPrice = trade.SpotPrice ?? 0,
|
|
Strike = trade.Strike ?? 0,
|
|
OriginalStockEqvNotional = trade.OriginalStockEqvNotional ?? 0,
|
|
TradeAmount = trade.TradeAmount,
|
|
TradeDate = trade.TradeDate,
|
|
ExerciseMode = trade.ExerciseMode,
|
|
CallPut = trade.OptionType,
|
|
TradeType = trade.TradeType,
|
|
StructureType = trade.StructureType,
|
|
TradeOpenVolatility = trade.TradeOpenVolatility ?? 0,
|
|
OriginalNotional = trade.OriginalNotional ?? 0,
|
|
TradePrice = trade.TradePrice ?? 0,
|
|
TradeSinglePrice = trade.TradeSinglePrice ?? 0,
|
|
PosiSettlePrice = risk == null ? null : (double?)risk.UnderlyingPrice,
|
|
Vol = risk.Vol ?? 0,
|
|
Delta = risk.Delta ?? 0,
|
|
Gamma = risk.Gamma ?? 0,
|
|
Theta = risk.Theta ?? 0,
|
|
Vega = risk.Vega ?? 0,
|
|
Rho = risk.Rho ?? 0,
|
|
Pv = risk.Pv ?? 0,
|
|
Notional = trade.Notional,
|
|
InstrumentType = trade.UnderlyingInstrumentType,
|
|
TradeId = trade.id,
|
|
|
|
ParticipationRate = trade.ParticipationRate ?? 1,
|
|
PrincipalSum = (trade.OriginalPrincipalSum * trade.StockEqvNotional / trade.OriginalStockEqvNotional) ?? 0,
|
|
AnnualizeFactor = trade.AnnualizeFactor ?? 1,
|
|
AssetBookName = assetbook.Name,
|
|
|
|
DeltaLots = 0,
|
|
GammaLots = 0,
|
|
CurrentPrice = 0,
|
|
|
|
Comments = trade.Comments,
|
|
TradeStatusProxy = trade.TradeStatus,
|
|
TradeCloseVolatility = trade.TradeCloseVolatility ?? 0,
|
|
|
|
ParentTradeId = trade.IsGroup == 2 ? trade.ParentTradeId : 0,
|
|
//下面这行在数据库字段定义为bit null的情况下会报FormatException
|
|
//IsUsePremiumRate = trade.IsUsePremiumRate ?? false,
|
|
IsUsePremiumRate = trade.IsUsePremiumRate == true,
|
|
IsUseTotalPremium = trade.IsTradePricePayType,
|
|
IsMoneynessOption = trade.IsMoneynessOption == "是",
|
|
|
|
AsianOption = asian,
|
|
BarrierOption = barrier,
|
|
SnowballOption = snowball,
|
|
|
|
BinaryOption = binary,
|
|
DoubleSharkfinOption = doublesharkfin,
|
|
RangeaccrualOption = rangeaccrual,
|
|
UnderlyingEnhanceOption = underlyingEnhance,
|
|
AirbagOption = airbag,
|
|
AutocallOption = autocall,
|
|
|
|
Margin = posi != null ? (double?)posi.Margin : default,
|
|
KeepMargin = epos != null ? epos.Margin : default,
|
|
InitMargin = trade.InitialMargin ?? 0
|
|
};
|
|
return query;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建客户日终持仓查询语句
|
|
/// </summary>
|
|
/// <param name="clientId">客户ID,为0表示所有客户</param>
|
|
private IQueryable<ClientPositionModel> CreateEodPositionQuery(ClientPositionQueryModel queryModel)
|
|
{
|
|
var clientId = queryModel.ClientId;
|
|
var clientNumber = queryModel.ClientNumber.TrimToNull();
|
|
var tradeStatus = queryModel.TradeStatus;
|
|
|
|
if (clientId < 1 && !string.IsNullOrEmpty(clientNumber))
|
|
{
|
|
var cid = DataCacheProvider.GetClientDataSource().AsQueryable()
|
|
.Where(n => clientNumber.Equals(n.Number, StringComparison.OrdinalIgnoreCase))
|
|
.Select(n => (int?)n.id).FirstOrDefault();
|
|
if (!cid.HasValue)
|
|
{
|
|
return Array.Empty<ClientPositionModel>().AsQueryable();
|
|
}
|
|
clientId = cid.Value;
|
|
}
|
|
|
|
var valueDate = queryModel.ValueDate.Value;
|
|
var predicate = PredicateBuilder.Create<trade>(n => n.ClientId > 0
|
|
&& n.ValidState != ConsGlobal.InValid && (n.TradeType != "结构化交易" || n.IsGroup == 1));
|
|
if (clientId > 0)
|
|
{
|
|
predicate = PredicateBuilder.Create<trade>(n => n.ClientId == clientId).And(predicate);
|
|
}
|
|
|
|
var riskQuery = DbContext.eod_trade_risk.Where(tr => tr.ValueDate == valueDate);
|
|
var posiQuery = DbContext.eod_trade_position.Where(tr => tr.ValueDate == valueDate);
|
|
|
|
var query = from et in DbContext.eod_trade
|
|
join trade in DbContext.trade.Where(predicate) on et.TradeId equals trade.id
|
|
join assetbook in DbContext.assetunit on trade.AssetId equals assetbook.id
|
|
join risk in riskQuery on trade.id equals risk.TradeId into risks
|
|
from risk in risks.DefaultIfEmpty()
|
|
join posi in posiQuery on trade.id equals posi.TradeId into posis
|
|
from posi in posis.DefaultIfEmpty()
|
|
where et.ValueDate == valueDate
|
|
orderby trade.id descending
|
|
select new ClientPositionModelEod
|
|
{
|
|
ClientId = trade.ClientId,
|
|
TradeNumber = trade.TradeNumber,
|
|
UnderlyingCode = trade.UnderlyingCode,
|
|
BuySell = trade.BuySell,//交易员角度提供数据
|
|
RoundedPv = posi.RoundedPv,
|
|
RoundedPnl = posi.RoundedPositionPnL,
|
|
ExerciseDate = trade.ExerciseDate,
|
|
InitialSpotPrice = trade.SpotPrice ?? 0,
|
|
Strike = trade.Strike ?? 0,
|
|
OriginalStockEqvNotional = trade.OriginalStockEqvNotional ?? 0,
|
|
TradeAmount = posi != null ? posi.Amount : trade.TradeAmount,
|
|
Notional = posi != null ? posi.Amount : trade.Notional,
|
|
TradeDate = trade.TradeDate,
|
|
ExerciseMode = trade.ExerciseMode,
|
|
CallPut = trade.OptionType,
|
|
TradeType = trade.TradeType,
|
|
StructureType = trade.StructureType,
|
|
TradeOpenVolatility = trade.TradeOpenVolatility ?? 0,
|
|
OriginalNotional = trade.OriginalNotional ?? 0,
|
|
TradePrice = trade.TradePrice ?? 0,
|
|
TradeSinglePrice = trade.TradeSinglePrice ?? 0,
|
|
InstrumentType = trade.UnderlyingInstrumentType,
|
|
|
|
PosiSettlePrice = posi.UnderlyingPrice,
|
|
|
|
Vol = risk.Vol,
|
|
Delta = risk.Delta,
|
|
Gamma = risk.Gamma,
|
|
Theta = risk.Theta,
|
|
Vega = risk.Vega,
|
|
Rho = risk.Rho,
|
|
Pv = risk.Pv,
|
|
|
|
TradeId = trade.id,
|
|
|
|
ParticipationRate = trade.ParticipationRate ?? 1,
|
|
PrincipalSum = (trade.OriginalPrincipalSum * trade.StockEqvNotional / trade.OriginalStockEqvNotional) ?? 0,
|
|
AnnualizeFactor = trade.AnnualizeFactor ?? 1,
|
|
AssetBookName = assetbook.Name,
|
|
|
|
DeltaLots = 0,
|
|
GammaLots = 0,
|
|
CurrentPrice = 0,
|
|
|
|
Comments = trade.Comments,
|
|
TradeStatusProxy = trade.TradeStatus,
|
|
TradeCloseVolatility = trade.TradeCloseVolatility ?? 0,
|
|
|
|
ParentTradeId = trade.IsGroup == 2 ? trade.ParentTradeId : 0,
|
|
//下面这行在数据库字段定义为bit null的情况下会报FormatException
|
|
//IsUsePremiumRate = trade.IsUsePremiumRate ?? false,
|
|
IsUsePremiumRate = trade.IsUsePremiumRate == true,
|
|
IsUseTotalPremium = trade.IsTradePricePayType,
|
|
IsMoneynessOption = trade.IsMoneynessOption == "是",
|
|
|
|
Margin = posi.Margin,
|
|
InitMargin = trade.InitialMargin ?? 0,
|
|
TradeJson = et.TradeJson
|
|
};
|
|
|
|
return query;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
private void ProcessClientPositionModel(ApiVersion apiVersion, IEnumerable<ClientPositionModel> clientPositionModels, DateTime valueDate, bool isEodPosition)
|
|
{
|
|
(string tradeType, Action<ClientPositionModel> setNull)[] setNullArr = null;
|
|
|
|
var eodPriceProvider = new Lazy<EodPriceProvider>(() => new EodPriceProvider(valueDate));
|
|
|
|
foreach (var item in clientPositionModels)
|
|
{
|
|
var um = UnderlyingDataProvider.GetUnderlying(item.UnderlyingCode);
|
|
|
|
if (um != null)
|
|
{
|
|
if (item.UnderlyingPrice == null)
|
|
{
|
|
item.UnderlyingPrice = um.Price ?? 0;
|
|
}
|
|
|
|
item.UnderlyingName = um.UnderlyingName;
|
|
|
|
//成交数量
|
|
item.TradeOriginalAmount = item.OriginalNotional / um.CountRatio;
|
|
|
|
if (um.IsSynthetic())
|
|
{
|
|
var synthetic = UnderlyingDataProvider.GetSyntheticUnderlying(um.UnderlyingCode);
|
|
if (synthetic != null)
|
|
{
|
|
item.Synthetic = synthetic.GetSyntheticPriceModel();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
item.TradeOriginalAmount = item.OriginalNotional;
|
|
}
|
|
|
|
if (apiVersion < ApiVersion.v3)
|
|
{
|
|
item.RoundedPv = -item.RoundedPv;
|
|
item.RoundedPnl = -item.RoundedPnl;
|
|
|
|
var pv = PS.Config.IsPVRounded ? item.RoundedPv : item.Pv;
|
|
|
|
//传入的pv是客户角度但是TradeHelper.GetTradeSinglePriceByTradePrice用的是交易员角度所以需要pv再取反
|
|
//RoundedPv用的是客户方向,pv用的是交易员方向,所以CurrentPrice的方向受到了IsPVRounded配置影响,这其实是个bug,但是国君希望原来的数据逻辑不动,所以保留
|
|
item.CurrentPrice = TradeHelper.GetTradeSinglePriceByTradePrice(-pv, item.Notional, !PS.Config.ErpElement.IsPVIncludePrincipal ? 0 : item.PrincipalSum, item.BuySell, item.TradeType, false);
|
|
}
|
|
else
|
|
{
|
|
var pv = PS.Config.IsPVRounded ? item.RoundedPv : item.Pv;
|
|
item.CurrentPrice = TradeHelper.GetTradeSinglePriceByTradePrice(pv, item.Notional, !PS.Config.ErpElement.IsPVIncludePrincipal ? 0 : item.PrincipalSum, item.BuySell, item.TradeType, false);
|
|
}
|
|
|
|
//delta手数&gamma手数
|
|
if (item.Delta == null || double.IsNaN(item.Delta.Value))
|
|
{
|
|
item.Delta = 0;
|
|
}
|
|
|
|
if (item.Gamma == null || double.IsNaN(item.Gamma.Value))
|
|
{
|
|
item.Gamma = 0;
|
|
}
|
|
|
|
var contractSize = um?.ContractSize ?? (ConsGlobal.InstrumentType.IsStock(item.InstrumentType) ? 100 : 1);
|
|
item.DeltaLots = item.Delta.Value / contractSize;
|
|
item.GammaLots = item.Gamma.Value / contractSize;
|
|
|
|
item.ClientNumber = DataCacheProvider.GetClientDataSource().GetData(item.ClientId)?.Number;
|
|
|
|
if (isEodPosition)
|
|
{
|
|
var eodPosi = (ClientPositionModelEod)item;
|
|
var td = TradeHelper2.Deserialize(eodPosi.TradeJson);
|
|
if (td != null)
|
|
{
|
|
item.AsianOption = td.trade_asian_option;
|
|
item.BarrierOption = td.trade_barrier_option;
|
|
item.SnowballOption = td.trade_snowball;
|
|
item.BinaryOption = td.trade_binary_option;
|
|
item.DoubleSharkfinOption = td.trade_double_sharkfin_option;
|
|
item.RangeaccrualOption = td.trade_rangeaccrual;
|
|
item.UnderlyingEnhanceOption = td.trade_underlying_enhance;
|
|
item.AirbagOption = td.trade_airbag;
|
|
item.AutocallOption = td.trade_autocall;
|
|
item.AccumulatorOption = td.trade_accumulator_option;
|
|
item.Forward = td.trade_forward;
|
|
item.Swap = td.trade_swap;
|
|
item.CashFlow = td.trade_cashflow;
|
|
item.CustomOption = td.trade_custom;
|
|
}
|
|
|
|
item.UnderlyingPrice = eodPriceProvider.Value.GetPrice(item.UnderlyingCode, td?.SettlementType ?? SettlementTypeEnum.ClosePrice);
|
|
|
|
if (item.Synthetic?.SuList != null)
|
|
{
|
|
foreach (var su in item.Synthetic.SuList)
|
|
{
|
|
su.Price = eodPriceProvider.Value.GetPrice(su.UnderlyingCode, td?.SettlementType ?? SettlementTypeEnum.ClosePrice);
|
|
}
|
|
|
|
item.Synthetic.Price = item.Synthetic.SuList.Sum(n => n.Coefficient * n.Price) + item.Synthetic.Constant;
|
|
}
|
|
|
|
if (um != null)
|
|
{
|
|
item.TradeAmount = item.Notional / um.CountRatio;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (item.TradeStatus >= EnumTradeStatus.closed)
|
|
{
|
|
item.CurrentPrice = 0;
|
|
item.Pv = item.RoundedPv = item.RoundedPnl = 0;
|
|
item.Delta = item.Gamma = 0;
|
|
item.DeltaLots = item.GammaLots = 0;
|
|
item.TradeAmount = item.Notional = 0;
|
|
}
|
|
|
|
if (item.Synthetic?.SuList != null)
|
|
{
|
|
foreach (var su in item.Synthetic.SuList)
|
|
{
|
|
su.Price = UnderlyingDataProvider.GetUnderlying(su.UnderlyingCode)?.Price ?? 0;
|
|
}
|
|
|
|
item.Synthetic.Price = item.Synthetic.SuList.Sum(n => n.Coefficient * n.Price) + item.Synthetic.Constant;
|
|
}
|
|
}
|
|
|
|
if (item.UnderlyingPrice == null)
|
|
{
|
|
item.UnderlyingPrice = 0;
|
|
}
|
|
|
|
if (item.PosiSettlePrice == null)
|
|
{
|
|
item.PosiSettlePrice = item.UnderlyingPrice;
|
|
}
|
|
|
|
setNullValues(item);
|
|
}
|
|
|
|
void setNullValues(ClientPositionModel model)
|
|
{
|
|
if (setNullArr == null)
|
|
{
|
|
setNullArr = new (string tradeType, Action<ClientPositionModel> setNull)[] {
|
|
("亚式期权", m=> m.AsianOption = null ),
|
|
("障碍期权", m=> m.BarrierOption = null),
|
|
("二元期权", m=> m.BinaryOption = null ),
|
|
("双鲨期权", m=> m.DoubleSharkfinOption = null ),
|
|
("雪球期权", m=> m.SnowballOption = null ),
|
|
("凤凰期权", m=> m.AutocallOption = null ),
|
|
("累计期权", m=> m.AccumulatorOption = null ),
|
|
("区间累积期权", m=> m.RangeaccrualOption = null ),
|
|
("收益增强结构", m=> m.UnderlyingEnhanceOption = null ),
|
|
("气囊结构", m=> m.AirbagOption = null ),
|
|
("远期", m=> m.Forward = null ),
|
|
("收益互换", m=> m.Swap = null ),
|
|
("现金流", m=> m.CashFlow = null ),
|
|
("自定义交易", m=> m.CustomOption = null ),
|
|
};
|
|
}
|
|
|
|
foreach (var item in setNullArr)
|
|
{
|
|
if (model.TradeType == "结构化交易")
|
|
{
|
|
if (item.tradeType != model.StructureType)
|
|
{
|
|
item.setNull(model);
|
|
}
|
|
}
|
|
else if (item.tradeType != model.TradeType)
|
|
{
|
|
item.setNull(model);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadTradeExtend(IEnumerable<ClientPositionModel> clientPositionModels)
|
|
{
|
|
var forwardTradeIds = clientPositionModels.Where(b => b.TradeType == "远期").Select(b => b.TradeId).ToList();
|
|
var forwardOptions = DbContext.trade_forward.Where(b => forwardTradeIds.Contains(b.TradeId))?.ToDictionary(b => b.TradeId, b => b);
|
|
|
|
var swapTradeIds = clientPositionModels.Where(b => b.TradeType == "收益互换").Select(b => b.TradeId).ToList();
|
|
var swapOptions = DbContext.trade_swap.Where(b => swapTradeIds.Contains(b.TradeId))?.ToDictionary(b => b.TradeId, b => b);
|
|
|
|
var cashTradeIds = clientPositionModels.Where(b => b.TradeType == "现金流交易").Select(b => b.TradeId).ToList();
|
|
var cashOptions = DbContext.trade_cashflow.Where(b => swapTradeIds.Contains(b.TradeId))?.ToDictionary(b => b.TradeId, b => b);
|
|
|
|
var accumulatorTradeIds = clientPositionModels.Where(b => b.TradeType == "累计期权").Select(b => b.TradeId).ToList();
|
|
var accumulatorOptions = DbContext.trade_accumulator_option.Where(b => swapTradeIds.Contains(b.TradeId))?.ToDictionary(b => b.TradeId, b => b);
|
|
|
|
var customTradeIds = clientPositionModels.Where(b => b.TradeType == "自定义交易").Select(b => b.TradeId).ToList();
|
|
var customOptions = DbContext.trade_custom.Where(b => swapTradeIds.Contains(b.TradeId))?.ToDictionary(b => b.TradeId, b => b);
|
|
|
|
foreach (var model in clientPositionModels)
|
|
{
|
|
switch (model.TradeType)
|
|
{
|
|
case "远期":
|
|
if (forwardOptions != null && forwardOptions.TryGetValue(model.TradeId, out var forward))
|
|
{
|
|
model.Forward = forward;
|
|
}
|
|
break;
|
|
case "收益互换":
|
|
if (swapOptions != null && swapOptions.TryGetValue(model.TradeId, out var swap))
|
|
{
|
|
model.Swap = swap;
|
|
}
|
|
break;
|
|
case "现金流交易":
|
|
if (cashOptions != null && cashOptions.TryGetValue(model.TradeId, out var cash))
|
|
{
|
|
model.CashFlow = cash;
|
|
}
|
|
break;
|
|
case "累计期权":
|
|
if (accumulatorOptions != null && accumulatorOptions.TryGetValue(model.TradeId, out var accumulator))
|
|
{
|
|
model.AccumulatorOption = accumulator;
|
|
}
|
|
break;
|
|
case "自定义交易":
|
|
if (customOptions != null && customOptions.TryGetValue(model.TradeId, out var custom))
|
|
{
|
|
model.CustomOption = custom;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadTradeMeta(IEnumerable<ClientPositionModel> clientPositionModels)
|
|
{
|
|
var tradeIds = clientPositionModels.Select(b => b.TradeId).ToList();
|
|
|
|
var metaDics = DbContext.TradeMeta.Where(b => tradeIds.Contains(b.TradeId))
|
|
.Select(n => new { n.TradeId, n.MetaKey, n.MetaValue })
|
|
.ToArray().GroupBy(n => n.TradeId)
|
|
.ToDictionary(n => n.Key, n => n.ToDictionary(m => m.MetaKey, m => m.MetaValue));
|
|
|
|
foreach (var posi in clientPositionModels)
|
|
{
|
|
if (metaDics.TryGetValue(posi.TradeId, out var mdic))
|
|
{
|
|
posi.MetaDic = mdic;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// v3全部统一成交易员方向,原来的v1和v2既有交易员方向也有客户方向,太烧脑
|
|
/// </summary>
|
|
enum ApiVersion { v1, v2, v3 }
|
|
|
|
class TradeCashData
|
|
{
|
|
public int tradeId { get; set; }
|
|
|
|
public double UnwindNotional { get; set; }
|
|
|
|
public double UnwindAmount { get; set; }
|
|
|
|
public double FinalPrice { get; set; }
|
|
|
|
public DateTime UnwindDate { get; set; }
|
|
}
|
|
|
|
class ClientPositionModelEod : ClientPositionModel
|
|
{
|
|
internal string TradeJson { get; set; }
|
|
}
|
|
}
|
|
}
|